control: Move serves into a separate components

This commit is contained in:
Tom Wiesing 2022-10-04 11:36:45 +02:00
parent 6f409be8b2
commit 845e927117
No known key found for this signature in database
12 changed files with 210 additions and 127 deletions

View file

@ -39,7 +39,7 @@ func (upc updateprefixconfig) Run(context wisski_distillery.Context) error {
}
ddis := dis.Control()
target := ddis.ResolverConfigPath()
target := dis.Resolver().ConfigPath()
// print the configuration
config, err := dis.Core.Environment.Create(target, environment.DefaultFilePerm)

View file

@ -5,7 +5,6 @@ import (
"path/filepath"
"github.com/FAU-CDI/wisski-distillery/internal/component"
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
"github.com/FAU-CDI/wisski-distillery/internal/core"
"github.com/FAU-CDI/wisski-distillery/pkg/environment"
)
@ -14,9 +13,9 @@ import (
type Control struct {
component.ComponentBase
Instances *instances.Instances
Servables []component.Servable
ResolverFile string
ResolverFile string // TODO: this shouldn't be needed!
}
func (control Control) Name() string {

View file

@ -8,6 +8,7 @@ import (
"strings"
"time"
"github.com/FAU-CDI/wisski-distillery/internal/component"
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
"github.com/FAU-CDI/wisski-distillery/internal/config"
"github.com/FAU-CDI/wisski-distillery/internal/models"
@ -16,7 +17,17 @@ import (
"golang.org/x/sync/errgroup"
)
func (control *Control) info(io stream.IOStream) (http.Handler, error) {
type Info struct {
component.ComponentBase
Instances *instances.Instances
}
func (Info) Name() string { return "control-info" }
func (*Info) Routes() []string { return []string{"/dis/"} }
func (info *Info) Handler(route string, io stream.IOStream) (http.Handler, error) {
mux := http.NewServeMux()
// handle everything under /dis/!
@ -29,7 +40,7 @@ func (control *Control) info(io stream.IOStream) (http.Handler, error) {
})
// static stuff
static, err := control.disStatic()
static, err := info.disStatic()
if err != nil {
return nil, err
}
@ -37,22 +48,22 @@ func (control *Control) info(io stream.IOStream) (http.Handler, error) {
// render everything
mux.Handle("/dis/index", httpx.HTMLHandler[disIndex]{
Handler: control.disIndex,
Handler: info.disIndex,
Template: indexTemplate,
})
mux.Handle("/dis/instance/", httpx.HTMLHandler[disInstance]{
Handler: control.disInstance,
Handler: info.disInstance,
Template: instanceTemplate,
})
// api -- for future usage
mux.Handle("/dis/api/v1/instance/get/", httpx.JSON(control.getinstance))
mux.Handle("/dis/api/v1/instance/all", httpx.JSON(control.allinstances))
mux.Handle("/dis/api/v1/instance/get/", httpx.JSON(info.getinstance))
mux.Handle("/dis/api/v1/instance/all", httpx.JSON(info.allinstances))
// ensure that everyone is logged in!
return httpx.BasicAuth(mux, "WissKI Distillery Admin", func(user, pass string) bool {
return user == control.Config.DisAdminUser && pass == control.Config.DisAdminPassword
return user == info.Config.DisAdminUser && pass == info.Config.DisAdminPassword
}), nil
}
@ -71,12 +82,12 @@ type disIndex struct {
Backups []models.Snapshot
}
func (dis *Control) disIndex(r *http.Request) (idx disIndex, err error) {
func (info *Info) disIndex(r *http.Request) (idx disIndex, err error) {
var group errgroup.Group
group.Go(func() error {
// load instances
idx.Instances, err = dis.allinstances(r)
idx.Instances, err = info.allinstances(r)
if err != nil {
return err
}
@ -96,12 +107,12 @@ func (dis *Control) disIndex(r *http.Request) (idx disIndex, err error) {
// get the log entries
group.Go(func() (err error) {
idx.Backups, err = dis.Instances.SnapshotLogFor("")
idx.Backups, err = info.Instances.SnapshotLogFor("")
return
})
// get the static properties
idx.Config = dis.Config
idx.Config = info.Config
// current time
idx.Time = time.Now().UTC()
@ -120,13 +131,13 @@ type disInstance struct {
Info instances.WissKIInfo
}
func (dis *Control) disInstance(r *http.Request) (is disInstance, err error) {
func (info *Info) disInstance(r *http.Request) (is disInstance, err error) {
// find the slug as the last component of path!
slug := strings.TrimSuffix(r.URL.Path, "/")
slug = slug[strings.LastIndex(slug, "/")+1:]
// find the instance itself!
instance, err := dis.Instances.WissKI(slug)
instance, err := info.Instances.WissKI(slug)
if err == instances.ErrWissKINotFound {
return is, httpx.ErrNotFound
}
@ -150,7 +161,7 @@ func (dis *Control) disInstance(r *http.Request) (is disInstance, err error) {
//go:embed html/static
var htmlStaticFS embed.FS
func (*Control) disStatic() (http.Handler, error) {
func (*Info) disStatic() (http.Handler, error) {
fs, err := fs.Sub(htmlStaticFS, "html/static")
if err != nil {
return nil, err
@ -167,29 +178,29 @@ var indexTemplate = template.Must(template.New("index.html").Parse(indexTemplate
var instanceTemplateString string
var instanceTemplate = template.Must(template.New("instance.html").Parse(instanceTemplateString))
func (dis *Control) getinstance(r *http.Request) (info instances.WissKIInfo, err error) {
func (info *Info) getinstance(r *http.Request) (iinfo instances.WissKIInfo, err error) {
// find the slug as the last component of path!
slug := strings.TrimSuffix(r.URL.Path, "/")
slug = slug[strings.LastIndex(slug, "/")+1:]
// load the wisski instance!
wisski, err := dis.Instances.WissKI(strings.TrimSuffix(slug, "/"))
wisski, err := info.Instances.WissKI(strings.TrimSuffix(slug, "/"))
if err == instances.ErrWissKINotFound {
return info, httpx.ErrNotFound
return iinfo, httpx.ErrNotFound
}
if err != nil {
return info, err
return iinfo, err
}
// get info about it!
return wisski.Info(false)
}
func (dis *Control) allinstances(*http.Request) (infos []instances.WissKIInfo, err error) {
func (info *Info) allinstances(*http.Request) (infos []instances.WissKIInfo, err error) {
var errgroup errgroup.Group
// list all the instances
all, err := dis.Instances.All()
all, err := info.Instances.All()
if err != nil {
return nil, err
}

View file

@ -6,14 +6,30 @@ import (
"net/http"
"strings"
"github.com/FAU-CDI/wisski-distillery/internal/component"
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
"github.com/tkw1536/goprogram/stream"
)
// self returns the handler for the self overrides
func (control Control) self(io stream.IOStream) (redirect Redirect, err error) {
// SelfHandler implements serving the '/' route
type SelfHandler struct {
component.ComponentBase
Instances *instances.Instances
}
func (SelfHandler) Name() string { return "control-self" }
func (*SelfHandler) Routes() []string { return []string{"/"} }
func (sh *SelfHandler) Handler(route string, io stream.IOStream) (http.Handler, error) {
// create a redirect
var redirect Redirect
var err error
// open the overrides file
overrides, err := control.Environment.Open(control.Config.SelfOverridesFile)
io.Printf("loading overrides from %q\n", control.Config.SelfOverridesFile)
overrides, err := sh.Environment.Open(sh.Config.SelfOverridesFile)
io.Printf("loading overrides from %q\n", sh.Config.SelfOverridesFile)
if err != nil {
return redirect, err
}
@ -21,18 +37,18 @@ func (control Control) self(io stream.IOStream) (redirect Redirect, err error) {
// decode the overrides file
if err := json.NewDecoder(overrides).Decode(&redirect.Overrides); err != nil {
return redirect, err
return nil, err
}
if redirect.Overrides == nil {
redirect.Overrides = make(map[string]string)
}
redirect.Overrides[""] = control.Config.SelfRedirect.String()
redirect.Overrides[""] = sh.Config.SelfRedirect.String()
// create a redirect server
redirect.Fallback, err = control.selfFallback()
redirect.Fallback, err = sh.selfFallback()
if err != nil {
return redirect, err
return nil, err
}
redirect.Absolute = false
redirect.Permanent = false
@ -41,22 +57,22 @@ func (control Control) self(io stream.IOStream) (redirect Redirect, err error) {
return redirect, nil
}
func (control *Control) selfFallback() (http.Handler, error) {
return http.HandlerFunc(control.serveFallback), nil
func (sh *SelfHandler) selfFallback() (http.Handler, error) {
return http.HandlerFunc(sh.serveFallback), nil
}
var notFoundText = []byte("not found")
func (control *Control) serveFallback(w http.ResponseWriter, r *http.Request) {
func (sh *SelfHandler) serveFallback(w http.ResponseWriter, r *http.Request) {
slug := control.Config.SlugFromHost(r.Host)
slug := sh.Config.SlugFromHost(r.Host)
if slug == "" {
w.WriteHeader(http.StatusNotFound)
w.Write(notFoundText)
return
}
if ok, _ := control.Instances.Has(slug); !ok {
if ok, _ := sh.Instances.Has(slug); !ok {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "WissKI %q not found\n", slug)
return

View file

@ -1,60 +0,0 @@
package control
import (
"fmt"
"path/filepath"
"regexp"
"github.com/FAU-CDI/wdresolve"
"github.com/FAU-CDI/wdresolve/resolvers"
"github.com/tkw1536/goprogram/stream"
)
func (control Control) ResolverConfigPath() string {
return filepath.Join(control.Path(), control.ResolverFile)
}
func (control Control) resolver(io stream.IOStream) (p wdresolve.ResolveHandler, err error) {
p.TrustXForwardedProto = true
fallback := &resolvers.Regexp{
Data: map[string]string{},
}
// handle the default domain name!
domainName := control.Config.DefaultDomain
if domainName != "" {
fallback.Data[fmt.Sprintf("^https?://(.*)\\.%s", regexp.QuoteMeta(domainName))] = fmt.Sprintf("https://$1.%s", domainName)
io.Printf("registering default domain %s\n", domainName)
}
// handle the extra domains!
for _, domain := range control.Config.SelfExtraDomains {
fallback.Data[fmt.Sprintf("^https?://(.*)\\.%s", regexp.QuoteMeta(domain))] = fmt.Sprintf("https://$1.%s", domainName)
io.Printf("registering legacy domain %s\n", domain)
}
// open the prefix file
prefixFile := control.ResolverConfigPath()
fs, err := control.Environment.Open(prefixFile)
io.Println("loading prefixes from ", prefixFile)
if err != nil {
return p, err
}
defer fs.Close()
// read the prefixes
// TODO: Do we want to load these without a file?
prefixes, err := resolvers.ReadPrefixes(fs)
if err != nil {
return p, err
}
// and use that as the resolver!
p.Resolver = resolvers.InOrder{
prefixes,
fallback,
}
return p, nil
}

View file

@ -7,33 +7,21 @@ import (
)
// Server returns an http.Mux that implements the main server instance
func (control Control) Server(io stream.IOStream) (http.Handler, error) {
// self server
self, err := control.self(io)
if err != nil {
return nil, err
}
resolver, err := control.resolver(io)
if err != nil {
return nil, err
}
info, err := control.info(io)
if err != nil {
return nil, err
}
// resolver
// Logging messages are directed to io.
func (control *Control) Server(io stream.IOStream) (*http.ServeMux, error) {
// create a new mux
mux := http.NewServeMux()
mux.Handle("/", self)
mux.Handle("/go/", resolver)
mux.Handle("/wisski/get/", resolver)
// TODO: Fix me!
mux.Handle("/dis/", info)
// add all the servable routes!
for _, s := range control.Servables {
for _, route := range s.Routes() {
io.Printf("mounting %s\n", route)
handler, err := s.Handler(route, io)
if err != nil {
return nil, err
}
mux.Handle(route, handler)
}
}
return mux, nil
}

View file

@ -0,0 +1,84 @@
package resolver
import (
"fmt"
"io/fs"
"net/http"
"path/filepath"
"regexp"
"github.com/FAU-CDI/wdresolve"
"github.com/FAU-CDI/wdresolve/resolvers"
"github.com/FAU-CDI/wisski-distillery/internal/component"
"github.com/FAU-CDI/wisski-distillery/internal/component/control"
"github.com/FAU-CDI/wisski-distillery/pkg/lazy"
"github.com/tkw1536/goprogram/stream"
)
type Resolver struct {
component.ComponentBase
Control *control.Control
ResolverFile string
handler lazy.Lazy[wdresolve.ResolveHandler]
}
func (Resolver) Name() string { return "resolver" }
func (resolver *Resolver) Routes() []string { return []string{"/go/", "/wisski/get/"} }
func (resolver *Resolver) Handler(route string, io stream.IOStream) (http.Handler, error) {
var err error
return resolver.handler.Get(func() (p wdresolve.ResolveHandler) {
p.TrustXForwardedProto = true
fallback := &resolvers.Regexp{
Data: map[string]string{},
}
// handle the default domain name!
domainName := resolver.Config.DefaultDomain
if domainName != "" {
fallback.Data[fmt.Sprintf("^https?://(.*)\\.%s", regexp.QuoteMeta(domainName))] = fmt.Sprintf("https://$1.%s", domainName)
io.Printf("registering default domain %s\n", domainName)
}
// handle the extra domains!
for _, domain := range resolver.Config.SelfExtraDomains {
fallback.Data[fmt.Sprintf("^https?://(.*)\\.%s", regexp.QuoteMeta(domain))] = fmt.Sprintf("https://$1.%s", domainName)
io.Printf("registering legacy domain %s\n", domain)
}
configPath := resolver.ConfigPath()
{
// load the prefix path!
var fs fs.File
fs, err = resolver.Environment.Open(configPath)
io.Println("loading prefixes from ", configPath)
if err != nil {
return
}
defer fs.Close()
// read the file
var prefixes resolvers.Prefix
prefixes, err = resolvers.ReadPrefixes(fs)
if err != nil {
return
}
// and use that as the resolver!
p.Resolver = resolvers.InOrder{
prefixes,
fallback,
}
return p
}
}), err
}
func (resolver *Resolver) ConfigPath() string {
return filepath.Join(resolver.Control.Path(), resolver.ResolverFile)
}

View file

@ -0,0 +1,18 @@
package component
import (
"net/http"
"github.com/tkw1536/goprogram/stream"
)
// Servable implements a component with a Serve method
type Servable interface {
Component
// Routes returns the routes served by this servable
Routes() []string
// Handler returns the handler for the requested route
Handler(route string, io stream.IOStream) (http.Handler, error)
}

View file

@ -1,4 +1,4 @@
// Package config provides the distillery configuration
// Package config contains distillery configuration
package config
import (

View file

@ -1,6 +1,8 @@
package config
import "strings"
import (
"strings"
)
// This file contains domain related derived configuration values.

View file

@ -7,6 +7,7 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/component"
"github.com/FAU-CDI/wisski-distillery/internal/component/control"
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
"github.com/FAU-CDI/wisski-distillery/internal/component/resolver"
"github.com/FAU-CDI/wisski-distillery/internal/component/snapshots"
"github.com/FAU-CDI/wisski-distillery/internal/component/sql"
"github.com/FAU-CDI/wisski-distillery/internal/component/ssh"
@ -49,7 +50,7 @@ func (dis *Distillery) cWeb(thread int32) *web.Web {
func (dis *Distillery) cControl(thread int32) *control.Control {
return component.InitComponent(&dis.pool, thread, dis.Core, func(control *control.Control, thread int32) {
control.ResolverFile = core.PrefixConfig
control.Instances = dis.cInstances(thread)
control.Servables = dis.cServables(thread)
})
}
@ -89,6 +90,13 @@ func (dis *Distillery) cSnapshotManager(thread int32) *snapshots.Manager {
})
}
func (dis *Distillery) cResolver(thread int32) *resolver.Resolver {
return component.InitComponent(&dis.pool, thread, dis.Core, func(resolver *resolver.Resolver, thread int32) {
resolver.Control = dis.cControl(thread)
resolver.ResolverFile = core.PrefixConfig
})
}
//
// ALL COMPONENTS
//
@ -96,7 +104,6 @@ func (dis *Distillery) cSnapshotManager(thread int32) *snapshots.Manager {
func (dis *Distillery) cComponents(thread int32) []component.Component {
return []component.Component{
dis.cWeb(thread),
dis.cControl(thread),
dis.cSSH(thread),
dis.cTriplestore(thread),
dis.cSQL(thread),
@ -110,6 +117,16 @@ func (dis *Distillery) cComponents(thread int32) []component.Component {
c(dis, thread, func(pbs *snapshots.Pathbuilders, thread int32) {
pbs.Instances = dis.cInstances(thread)
}),
// Control server
dis.cControl(thread),
c(dis, thread, func(sh *control.SelfHandler, thread int32) {
sh.Instances = dis.cInstances(thread)
}),
dis.cResolver(thread),
c(dis, thread, func(info *control.Info, thread int32) {
info.Instances = dis.cInstances(thread)
}),
}
}
@ -137,6 +154,10 @@ func (dis *Distillery) cSnapshotable(thread int32) []component.Snapshotable {
return getComponentSubtype[component.Snapshotable](dis, thread)
}
func (dis *Distillery) cServables(thread int32) []component.Servable {
return getComponentSubtype[component.Servable](dis, thread)
}
func getComponentSubtype[C component.Component](dis *Distillery, thread int32) (components []C) {
all := dis.cComponents(thread)

View file

@ -7,6 +7,7 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/component"
"github.com/FAU-CDI/wisski-distillery/internal/component/control"
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
"github.com/FAU-CDI/wisski-distillery/internal/component/resolver"
"github.com/FAU-CDI/wisski-distillery/internal/component/snapshots"
"github.com/FAU-CDI/wisski-distillery/internal/component/sql"
"github.com/FAU-CDI/wisski-distillery/internal/component/ssh"
@ -49,6 +50,9 @@ func (dis *Distillery) Context() context.Context {
func (dis *Distillery) Control() *control.Control {
return dis.cControl(dis.thread())
}
func (dis *Distillery) Resolver() *resolver.Resolver {
return dis.cResolver(dis.thread())
}
func (dis *Distillery) SSH() *ssh.SSH {
return dis.cSSH(dis.thread())
}