Require access to Still via method

This commit adds a safeguard to accessing the still from a specific
component by requiring access via the component.GetStill method.
This commit is contained in:
Tom Wiesing 2024-04-08 22:39:32 +02:00
parent 81fa84c244
commit 8235ea9105
No known key found for this signature in database
63 changed files with 288 additions and 197 deletions

View file

@ -36,8 +36,9 @@ type AuthInfo struct {
} }
func (a *API) HandleRoute(ctx context.Context, path string) (http.Handler, error) { func (a *API) HandleRoute(ctx context.Context, path string) (http.Handler, error) {
return &Handler[AuthInfo]{ return &Handler[AuthInfo]{
Config: a.Config, Config: component.GetStill(a).Config,
Auth: a.dependencies.Auth, Auth: a.dependencies.Auth,
Methods: []string{"GET"}, Methods: []string{"GET"},

View file

@ -58,7 +58,7 @@ func (next *Next) getInstance(r *http.Request) (wisski *wisski.WissKI, path stri
} }
// find the slug // find the slug
slug, ok := next.Config.HTTP.SlugFromHost(url.Host) slug, ok := component.GetStill(next).Config.HTTP.SlugFromHost(url.Host)
if slug == "" || !ok { if slug == "" || !ok {
return nil, "", httpx.ErrBadRequest return nil, "", httpx.ErrBadRequest
} }

View file

@ -5,6 +5,7 @@ import (
"errors" "errors"
"net/http" "net/http"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
@ -64,9 +65,10 @@ func (panel *UserPanel) sshRoute(ctx context.Context) http.Handler {
return sc, err return sc, err
} }
sc.Domain = panel.Config.HTTP.PrimaryDomain config := component.GetStill(panel).Config
sc.PanelDomain = panel.Config.HTTP.PanelDomain() sc.Domain = config.HTTP.PrimaryDomain
sc.Port = panel.Config.Listen.SSHPort sc.PanelDomain = config.HTTP.PanelDomain()
sc.Port = config.Listen.SSHPort
// pick the first domain that the user has access to as an example // pick the first domain that the user has access to as an example
grants, err := panel.dependencies.Policy.User(r.Context(), user.User.User) grants, err := panel.dependencies.Policy.User(r.Context(), user.User.User)
@ -75,7 +77,7 @@ func (panel *UserPanel) sshRoute(ctx context.Context) http.Handler {
} else { } else {
sc.Slug = "example" sc.Slug = "example"
} }
sc.Hostname = panel.Config.HTTP.HostFromSlug(sc.Slug) sc.Hostname = config.HTTP.HostFromSlug(sc.Slug)
sc.Keys, err = panel.dependencies.Keys.Keys(r.Context(), user.User.User) sc.Keys, err = panel.dependencies.Keys.Keys(r.Context(), user.User.User)
if err != nil { if err != nil {

View file

@ -5,6 +5,7 @@ import (
"html/template" "html/template"
"net/http" "net/http"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
@ -52,7 +53,7 @@ func (panel *UserPanel) tokensRoute(ctx context.Context) http.Handler {
return tc, err return tc, err
} }
tc.Domain = template.URL(panel.Config.HTTP.JoinPath().String()) tc.Domain = template.URL(component.GetStill(panel).Config.HTTP.JoinPath().String())
// get the tokens // get the tokens
tc.Tokens, err = panel.dependencies.Tokens.Tokens(r.Context(), user.User.User) tc.Tokens, err = panel.dependencies.Tokens.Tokens(r.Context(), user.User.User)
@ -178,7 +179,7 @@ func (panel *UserPanel) tokensAddRoute(ctx context.Context) http.Handler {
// render the created context // render the created context
return panel.dependencies.Handling.WriteHTML( return panel.dependencies.Handling.WriteHTML(
tplDone.Context(r, TokenCreateContext{ tplDone.Context(r, TokenCreateContext{
Domain: template.URL(panel.Config.HTTP.JoinPath().String()), Domain: template.URL(component.GetStill(panel).Config.HTTP.JoinPath().String()),
Token: tok, Token: tok,
}), }),
nil, nil,

View file

@ -46,8 +46,7 @@ func (panel *UserPanel) routeUser(ctx context.Context) http.Handler {
menuTOTPAction, menuTOTPAction,
menuSSH, menuSSH,
} }
if component.GetStill(panel).Config.HTTP.API.Value {
if panel.Config.HTTP.API.Value {
actions = append(actions, menuTokens) actions = append(actions, menuTokens)
} }

View file

@ -107,7 +107,7 @@ func (auth *Auth) checkUser(ctx context.Context, name string) (user *AuthUser, e
// If the session is not set, creates a new session. // If the session is not set, creates a new session.
func (auth *Auth) session(r *http.Request) (*sessions.Session, error) { func (auth *Auth) session(r *http.Request) (*sessions.Session, error) {
return auth.store.Get(func() sessions.Store { return auth.store.Get(func() sessions.Store {
return sessions.NewCookieStore([]byte(auth.Config.SessionSecret)) return sessions.NewCookieStore([]byte(component.GetStill(auth).Config.SessionSecret))
}).Get(r, server.SessionCookie) }).Get(r, server.SessionCookie)
} }

View file

@ -18,7 +18,7 @@ var (
) )
func (binder *Binder) Path() string { func (binder *Binder) Path() string {
return filepath.Join(binder.Still.Config.Paths.Root, "core", "binder") return filepath.Join(component.GetStill(binder).Config.Paths.Root, "core", "binder")
} }
func (binder *Binder) Context(parent component.InstallationContext) component.InstallationContext { func (binder *Binder) Context(parent component.InstallationContext) component.InstallationContext {
@ -29,17 +29,20 @@ func (binder *Binder) Context(parent component.InstallationContext) component.In
var composeTemplate embed.FS var composeTemplate embed.FS
func (binder *Binder) Stack() component.StackWithResources { func (binder *Binder) Stack() component.StackWithResources {
config := component.GetStill(binder).Config
return component.MakeStack(binder, component.StackWithResources{ return component.MakeStack(binder, component.StackWithResources{
ContextPath: ".", ContextPath: ".",
Resources: composeTemplate, Resources: composeTemplate,
ComposerYML: func(root *yaml.Node) (*yaml.Node, error) { ComposerYML: func(root *yaml.Node) (*yaml.Node, error) {
ports := binder.Config.Listen.ComposePorts("8000")
ports := config.Listen.ComposePorts("8000")
if err := yamlx.ReplaceWith(root, ports, "services", "binder", "ports"); err != nil { if err := yamlx.ReplaceWith(root, ports, "services", "binder", "ports"); err != nil {
return nil, err return nil, err
} }
command := binder.Config.HTTP.TCPMuxCommand("0.0.0.0:8000", "http:80", "http:443", "ssh:2222") command := config.HTTP.TCPMuxCommand("0.0.0.0:8000", "http:80", "http:443", "ssh:2222")
if err := yamlx.ReplaceWith(root, command, "services", "binder", "command"); err != nil { if err := yamlx.ReplaceWith(root, command, "services", "binder", "command"); err != nil {
return nil, err return nil, err
} }
@ -48,7 +51,7 @@ func (binder *Binder) Stack() component.StackWithResources {
}, },
EnvContext: map[string]string{ EnvContext: map[string]string{
"DOCKER_NETWORK_NAME": binder.Config.Docker.Network(), "DOCKER_NETWORK_NAME": config.Docker.Network(),
}, },
}) })
} }

View file

@ -33,7 +33,7 @@ type Component interface {
// Base is embedded into every Component // Base is embedded into every Component
type Base struct { type Base struct {
name, id string // name and id of this component name, id string // name and id of this component
Still // the underlying still of the distillery still Still // the underlying still of the distillery
} }
//lint:ignore U1000 used to implement the private methods of [Component] //lint:ignore U1000 used to implement the private methods of [Component]
@ -45,7 +45,7 @@ func (cb *Base) getBase() *Base {
// Init is only initended to be used within a lifetime.Lifetime[Component,Still]. // Init is only initended to be used within a lifetime.Lifetime[Component,Still].
func Init(component Component, core Still) { func Init(component Component, core Still) {
base := component.getBase() // pointer to a struct base := component.getBase() // pointer to a struct
base.Still = core base.still = core
tp := reflect.TypeOf(component).Elem() tp := reflect.TypeOf(component).Elem()
base.name = strings.ToLower(tp.Name()) base.name = strings.ToLower(tp.Name())
@ -60,8 +60,13 @@ func (cb Base) ID() string {
return cb.id return cb.id
} }
// GetStill returns the still underlying the provided component.
func GetStill(c Component) Still {
return c.getBase().still
}
// Still represents the central part of a distillery. // Still represents the central part of a distillery.
// It is used inside the main distillery struct, as well as every component via [ComponentBase]. // It holds configuration of the distillery.
type Still struct { type Still struct {
Config *config.Config // the configuration of the distillery Config *config.Config // the configuration of the distillery
Upstream Upstream Upstream Upstream

View file

@ -33,7 +33,7 @@ type Exporter struct {
// Path returns the path that contains all snapshot related data. // Path returns the path that contains all snapshot related data.
func (dis *Exporter) Path() string { func (dis *Exporter) Path() string {
return filepath.Join(dis.Config.Paths.Root, "snapshots") return filepath.Join(component.GetStill(dis).Config.Paths.Root, "snapshots")
} }
// StagingPath returns the path to the directory containing a temporary staging area for snapshots. // StagingPath returns the path to the directory containing a temporary staging area for snapshots.

View file

@ -36,10 +36,11 @@ func (control *Config) Backup(scontext *component.StagingContext) error {
// backupfiles lists the files to be backed up. // backupfiles lists the files to be backed up.
func (control *Config) backupFiles() []string { func (control *Config) backupFiles() []string {
config := component.GetStill(control).Config
return []string{ return []string{
control.Config.ConfigPath, config.ConfigPath,
control.Config.Paths.ExecutablePath(), config.Paths.ExecutablePath(),
control.Config.Paths.OverridesJSON, config.Paths.OverridesJSON,
control.Config.Paths.ResolverBlocks, config.Paths.ResolverBlocks,
} }
} }

View file

@ -7,12 +7,14 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"time" "time"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
) )
// ShouldPrune determines if a file with the provided modification time should be // ShouldPrune determines if a file with the provided modification time should be
// removed from the export log. // removed from the export log.
func (exporter *Exporter) ShouldPrune(modtime time.Time) bool { func (exporter *Exporter) ShouldPrune(modtime time.Time) bool {
return time.Since(modtime) > exporter.Config.MaxBackupAge return time.Since(modtime) > component.GetStill(exporter).Config.MaxBackupAge
} }
// Prune prunes all old exports // Prune prunes all old exports
@ -44,7 +46,7 @@ func (exporter *Exporter) PruneExports(ctx context.Context, progress io.Writer)
// assemble path, and then remove the file! // assemble path, and then remove the file!
path := filepath.Join(sPath, entry.Name()) path := filepath.Join(sPath, entry.Name())
fmt.Fprintf(progress, "Removing %s cause it is older than %d days\n", path, exporter.Config.MaxBackupAge) fmt.Fprintf(progress, "Removing %s cause it is older than %d days\n", path, component.GetStill(exporter).Config.MaxBackupAge)
if err := os.Remove(path); err != nil { if err := os.Remove(path); err != nil {
return err return err

View file

@ -7,6 +7,7 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/config" "github.com/FAU-CDI/wisski-distillery/internal/config"
"github.com/FAU-CDI/wisski-distillery/internal/config/validators" "github.com/FAU-CDI/wisski-distillery/internal/config/validators"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
"github.com/FAU-CDI/wisski-distillery/internal/models" "github.com/FAU-CDI/wisski-distillery/internal/models"
"github.com/FAU-CDI/wisski-distillery/internal/wisski" "github.com/FAU-CDI/wisski-distillery/internal/wisski"
) )
@ -37,22 +38,24 @@ func (instances *Instances) Create(slug string, system models.System) (wissKI *w
wissKI.Liquid.Instance.OwnerEmail = "" wissKI.Liquid.Instance.OwnerEmail = ""
wissKI.Liquid.Instance.AutoBlindUpdateEnabled = true wissKI.Liquid.Instance.AutoBlindUpdateEnabled = true
config := component.GetStill(instances).Config
// sql // sql
wissKI.Liquid.Instance.SqlDatabase = instances.Config.SQL.DataPrefix + slug wissKI.Liquid.Instance.SqlDatabase = config.SQL.DataPrefix + slug
wissKI.Liquid.Instance.SqlUsername = instances.Config.SQL.UserPrefix + slug wissKI.Liquid.Instance.SqlUsername = config.SQL.UserPrefix + slug
wissKI.Liquid.Instance.SqlPassword, err = instances.Config.NewPassword() wissKI.Liquid.Instance.SqlPassword, err = config.NewPassword()
if err != nil { if err != nil {
return nil, err return nil, err
} }
// triplestore // triplestore
wissKI.Liquid.Instance.GraphDBRepository = instances.Config.TS.DataPrefix + slug wissKI.Liquid.Instance.GraphDBRepository = config.TS.DataPrefix + slug
wissKI.Liquid.Instance.GraphDBUsername = instances.Config.TS.UserPrefix + slug wissKI.Liquid.Instance.GraphDBUsername = config.TS.UserPrefix + slug
wissKI.Liquid.Instance.GraphDBPassword, err = instances.Config.NewPassword() wissKI.Liquid.Instance.GraphDBPassword, err = config.NewPassword()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -61,7 +64,7 @@ func (instances *Instances) Create(slug string, system models.System) (wissKI *w
wissKI.Liquid.DrupalUsername = "admin" // TODO: Change this! wissKI.Liquid.DrupalUsername = "admin" // TODO: Change this!
wissKI.Liquid.DrupalPassword, err = instances.Config.NewPassword() wissKI.Liquid.DrupalPassword, err = config.NewPassword()
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -28,7 +28,7 @@ type Instances struct {
} }
func (instances *Instances) Path() string { func (instances *Instances) Path() string {
return filepath.Join(instances.Still.Config.Paths.Root, "instances") return filepath.Join(component.GetStill(instances).Config.Paths.Root, "instances")
} }
// ErrWissKINotFound is returned when a WissKI is not found // ErrWissKINotFound is returned when a WissKI is not found

View file

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"io" "io"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
"github.com/FAU-CDI/wisski-distillery/pkg/unpack" "github.com/FAU-CDI/wisski-distillery/pkg/unpack"
"github.com/tkw1536/goprogram/exit" "github.com/tkw1536/goprogram/exit"
) )
@ -22,7 +23,7 @@ var runtimeResources embed.FS
// Update installs or updates runtime components needed by this component. // Update installs or updates runtime components needed by this component.
func (instances *Instances) Update(ctx context.Context, progress io.Writer) error { func (instances *Instances) Update(ctx context.Context, progress io.Writer) error {
err := unpack.InstallDir(instances.Config.Paths.RuntimeDir(), "runtime", runtimeResources, func(dst, src string) { err := unpack.InstallDir(component.GetStill(instances).Config.Paths.RuntimeDir(), "runtime", runtimeResources, func(dst, src string) {
fmt.Fprintf(progress, "[copy] %s\n", dst) fmt.Fprintf(progress, "[copy] %s\n", dst)
}) })
if err != nil { if err != nil {

View file

@ -32,7 +32,7 @@ func (api *API) Routes() component.Routes {
func (a *API) HandleRoute(ctx context.Context, path string) (http.Handler, error) { func (a *API) HandleRoute(ctx context.Context, path string) (http.Handler, error) {
return &api.Handler[string]{ return &api.Handler[string]{
Config: a.Config, Config: component.GetStill(a).Config,
Auth: a.dependencies.Auth, Auth: a.dependencies.Auth,
Methods: []string{"GET"}, Methods: []string{"GET"},

View file

@ -85,15 +85,17 @@ func (resolver *Resolver) HandleRoute(ctx context.Context, route string) (http.H
Data: map[string]string{}, Data: map[string]string{},
} }
config := component.GetStill(resolver).Config
// handle the default domain name! // handle the default domain name!
domainName := resolver.Config.HTTP.PrimaryDomain domainName := config.HTTP.PrimaryDomain
if domainName != "" { if domainName != "" {
fallback.Data[fmt.Sprintf("^https?://(.*)\\.%s", regexp.QuoteMeta(domainName))] = fmt.Sprintf("https://$1.%s", domainName) fallback.Data[fmt.Sprintf("^https?://(.*)\\.%s", regexp.QuoteMeta(domainName))] = fmt.Sprintf("https://$1.%s", domainName)
logger.Info().Str("name", domainName).Msg("registering default domain") logger.Info().Str("name", domainName).Msg("registering default domain")
} }
// handle the extra domains! // handle the extra domains!
for _, domain := range resolver.Config.HTTP.ExtraDomains { for _, domain := range config.HTTP.ExtraDomains {
fallback.Data[fmt.Sprintf("^https?://(.*)\\.%s", regexp.QuoteMeta(domain))] = fmt.Sprintf("https://$1.%s", domainName) fallback.Data[fmt.Sprintf("^https?://(.*)\\.%s", regexp.QuoteMeta(domain))] = fmt.Sprintf("https://$1.%s", domainName)
logger.Info().Str("name", domainName).Msg("registering legacy domain") logger.Info().Str("name", domainName).Msg("registering legacy domain")
} }

View file

@ -78,7 +78,7 @@ func (admin *Admin) Status(ctx context.Context, QuickInformation bool) (target s
func (admin *Admin) Fetch(flags component.FetcherFlags, target *status.Distillery) error { func (admin *Admin) Fetch(flags component.FetcherFlags, target *status.Distillery) error {
target.Time = time.Now().UTC() target.Time = time.Now().UTC()
target.Config = admin.Config target.Config = component.GetStill(admin).Config
return nil return nil
} }

View file

@ -56,9 +56,10 @@ func (admin *Admin) instanceSSH(ctx context.Context) http.Handler {
return ctx, nil, httpx.ErrNotFound return ctx, nil, httpx.ErrNotFound
} }
config := component.GetStill(admin).Config
ctx.Hostname = ctx.Instance.Domain() ctx.Hostname = ctx.Instance.Domain()
ctx.PanelDomain = admin.Config.HTTP.PanelDomain() ctx.PanelDomain = config.HTTP.PanelDomain()
ctx.Port = admin.Config.Listen.SSHPort ctx.Port = config.Listen.SSHPort
keys, err := ctx.Instance.SSH().Keys(r.Context()) keys, err := ctx.Instance.SSH().Keys(r.Context())
if err != nil { if err != nil {

View file

@ -99,11 +99,12 @@ func (control *Cron) Once(ctx context.Context) {
// //
// The returned channel is closed once no more cron tasks are active. // The returned channel is closed once no more cron tasks are active.
func (control *Cron) Start(ctx context.Context, signal <-chan struct{}) <-chan struct{} { func (control *Cron) Start(ctx context.Context, signal <-chan struct{}) <-chan struct{} {
zerolog.Ctx(ctx).Info().Dur("interval", control.Config.CronInterval).Msg("Scheduling Cron() tasks") interval := component.GetStill(control).Config.CronInterval
zerolog.Ctx(ctx).Info().Dur("interval", interval).Msg("Scheduling Cron() tasks")
// run runs cron tasks with the configured timeout // run runs cron tasks with the configured timeout
run := func() { run := func() {
ctx, done := context.WithTimeout(ctx, control.Config.CronInterval) ctx, done := context.WithTimeout(ctx, interval)
defer done() defer done()
control.Once(ctx) control.Once(ctx)
@ -123,7 +124,7 @@ func (control *Cron) Start(ctx context.Context, signal <-chan struct{}) <-chan s
defer timex.ReleaseTimer(t) defer timex.ReleaseTimer(t)
for { for {
timex.StopTimer(t) timex.StopTimer(t)
t.Reset(control.Config.CronInterval) t.Reset(interval)
select { select {
case <-t.C: case <-t.C:

View file

@ -37,7 +37,8 @@ func (h *Handling) interceptor(parent httpx.ErrInterceptor) httpx.ErrInterceptor
pf = func(r *http.Request, err error) {} pf = func(r *http.Request, err error) {}
} }
parent.RenderError = h.Config.HTTP.Debug.Set && h.Config.HTTP.Debug.Value config := component.GetStill(h).Config
parent.RenderError = config.HTTP.Debug.Set && config.HTTP.Debug.Value
parent.OnFallback = func(r *http.Request, err error) { parent.OnFallback = func(r *http.Request, err error) {
pf(r, err) pf(r, err)

View file

@ -30,7 +30,7 @@ func (home *Home) Routes() component.Routes {
MatchAllDomains: true, MatchAllDomains: true,
CSRF: false, CSRF: false,
MenuTitle: home.Config.Home.Title, MenuTitle: component.GetStill(home).Config.Home.Title,
MenuSticky: true, MenuSticky: true,
MenuPriority: component.MenuHome, MenuPriority: component.MenuHome,
} }
@ -45,7 +45,7 @@ func (home *Home) HandleRoute(ctx context.Context, route string) (http.Handler,
dflt.Fallback = home.publicHandler(ctx) dflt.Fallback = home.publicHandler(ctx)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
slug, ok := home.Config.HTTP.NormSlugFromHost(r.Host) slug, ok := component.GetStill(home).Config.HTTP.NormSlugFromHost(r.Host)
switch { switch {
case !ok: case !ok:
http.NotFound(w, r) http.NotFound(w, r)

View file

@ -48,14 +48,14 @@ type publicContext struct {
const logoHTML = template.HTML(`<img src="/logo.svg" alt="WissKI Distillery Logo" class="biglogo">`) const logoHTML = template.HTML(`<img src="/logo.svg" alt="WissKI Distillery Logo" class="biglogo">`)
func (home *Home) publicHandler(ctx context.Context) http.Handler { func (home *Home) publicHandler(ctx context.Context) http.Handler {
title := home.Config.Home.Title config := component.GetStill(home).Config.Home
tpl := publicTemplate.Prepare( tpl := publicTemplate.Prepare(
home.dependencies.Templating, home.dependencies.Templating,
// set title and menu item // set title and menu item
templating.Title(title), templating.Title(config.Title),
templating.Crumbs( templating.Crumbs(
component.MenuItem{Title: title, Path: "/"}, component.MenuItem{Title: config.Title, Path: "/"},
), ),
) )
@ -73,7 +73,7 @@ func (home *Home) publicHandler(ctx context.Context) http.Handler {
// prepare about // prepare about
pc.aboutContext.Logo = logoHTML pc.aboutContext.Logo = logoHTML
pc.aboutContext.Instances = home.dependencies.ListInstances.Infos() pc.aboutContext.Instances = home.dependencies.ListInstances.Infos()
pc.aboutContext.SelfRedirect = home.Config.Home.SelfRedirect.String() pc.aboutContext.SelfRedirect = config.SelfRedirect.String()
// render the about template // render the about template
@ -88,7 +88,7 @@ func (home *Home) publicHandler(ctx context.Context) http.Handler {
pc.ListEnabled = home.dependencies.ListInstances.ShouldShowList(r) pc.ListEnabled = home.dependencies.ListInstances.ShouldShowList(r)
// title of the list // title of the list
pc.ListTitle = home.Config.Home.List.Title pc.ListTitle = config.List.Title
return return
}) })

View file

@ -6,6 +6,8 @@ import (
"net/http" "net/http"
"os" "os"
"strings" "strings"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
) )
func (home *Home) loadRedirect(ctx context.Context) (redirect Redirect, err error) { func (home *Home) loadRedirect(ctx context.Context) (redirect Redirect, err error) {
@ -19,7 +21,7 @@ func (home *Home) loadRedirect(ctx context.Context) (redirect Redirect, err erro
redirect.Permanent = false redirect.Permanent = false
// load the overrides file // load the overrides file
overrides, err := os.Open(home.Config.Paths.OverridesJSON) overrides, err := os.Open(component.GetStill(home).Config.Paths.OverridesJSON)
if err != nil { if err != nil {
return redirect, err return redirect, err
} }

View file

@ -41,7 +41,7 @@ type APISystem struct {
func (a *API) HandleRoute(ctx context.Context, path string) (http.Handler, error) { func (a *API) HandleRoute(ctx context.Context, path string) (http.Handler, error) {
return &api.Handler[[]APISystem]{ return &api.Handler[[]APISystem]{
Config: a.Config, Config: component.GetStill(a).Config,
Auth: a.dependencies.Auth, Auth: a.dependencies.Auth,
Methods: []string{"GET"}, Methods: []string{"GET"},

View file

@ -35,8 +35,9 @@ func (li *ListInstances) Infos() []status.WissKI {
// ShouldShowList determines if a list should be shown for the given request // ShouldShowList determines if a list should be shown for the given request
func (li *ListInstances) ShouldShowList(r *http.Request) bool { func (li *ListInstances) ShouldShowList(r *http.Request) bool {
allowPrivate := li.Config.Home.List.Private.Value config := component.GetStill(li).Config.Home.List
allowPublic := li.Config.Home.List.Public.Value allowPrivate := config.Private.Value
allowPublic := config.Public.Value
if allowPrivate == allowPublic { if allowPrivate == allowPublic {
return allowPrivate return allowPrivate

View file

@ -30,7 +30,7 @@ func (api *API) Routes() component.Routes {
func (a *API) HandleRoute(ctx context.Context, path string) (http.Handler, error) { func (a *API) HandleRoute(ctx context.Context, path string) (http.Handler, error) {
return &api.Handler[[]Item]{ return &api.Handler[[]Item]{
Config: a.Config, Config: component.GetStill(a).Config,
Auth: a.dependencies.Auth, Auth: a.dependencies.Auth,
Methods: []string{"GET"}, Methods: []string{"GET"},

View file

@ -53,7 +53,7 @@ func (server *Server) Server(ctx context.Context, progress io.Writer) (public ht
}() }()
// determine if we are on a slug from a host // determine if we are on a slug from a host
slug, ok := server.Config.HTTP.NormSlugFromHost(r.Host) slug, ok := component.GetStill(server).Config.HTTP.NormSlugFromHost(r.Host)
rctx := component.WithRouteContext(r.Context(), component.RouteContext{ rctx := component.WithRouteContext(r.Context(), component.RouteContext{
DefaultDomain: slug == "" && ok, DefaultDomain: slug == "" && ok,
@ -126,13 +126,15 @@ func (server *Server) Server(ctx context.Context, progress io.Writer) (public ht
// CSRF returns a CSRF handler for the given function // CSRF returns a CSRF handler for the given function
func (server *Server) csrf() func(http.Handler) http.Handler { func (server *Server) csrf() func(http.Handler) http.Handler {
config := component.GetStill(server).Config
var opts []csrf.Option var opts []csrf.Option
opts = append(opts, csrf.Secure(server.Config.HTTP.HTTPSEnabled())) opts = append(opts, csrf.Secure(config.HTTP.HTTPSEnabled()))
opts = append(opts, csrf.SameSite(csrf.SameSiteStrictMode)) opts = append(opts, csrf.SameSite(csrf.SameSiteStrictMode))
opts = append(opts, csrf.Path("/")) opts = append(opts, csrf.Path("/"))
opts = append(opts, csrf.CookieName(CSRFCookie)) opts = append(opts, csrf.CookieName(CSRFCookie))
opts = append(opts, csrf.FieldName(CSRFCookieField)) opts = append(opts, csrf.FieldName(CSRFCookieField))
return csrf.Protect(server.Config.CSRFSecret(), opts...) return csrf.Protect(config.CSRFSecret(), opts...)
} }
// WithCSP adds a Content-Security-Policy header to every response // WithCSP adds a Content-Security-Policy header to every response

View file

@ -11,28 +11,30 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/dis/component" "github.com/FAU-CDI/wisski-distillery/internal/dis/component"
) )
func (control Server) Path() string { func (server *Server) Path() string {
return filepath.Join(control.Still.Config.Paths.Root, "core", "dis") return filepath.Join(component.GetStill(server).Config.Paths.Root, "core", "dis")
} }
//go:embed all:server //go:embed all:server
var resources embed.FS var resources embed.FS
func (server *Server) Stack() component.StackWithResources { func (server *Server) Stack() component.StackWithResources {
config := component.GetStill(server).Config
return component.MakeStack(server, component.StackWithResources{ return component.MakeStack(server, component.StackWithResources{
Resources: resources, Resources: resources,
ContextPath: "server", ContextPath: "server",
EnvContext: map[string]string{ EnvContext: map[string]string{
"DOCKER_NETWORK_NAME": server.Config.Docker.Network(), "DOCKER_NETWORK_NAME": config.Docker.Network(),
"HOST_RULE": server.Config.HTTP.PanelHostRule(), "HOST_RULE": config.HTTP.PanelHostRule(),
"HTTPS_ENABLED": server.Config.HTTP.HTTPSEnabledEnv(), "HTTPS_ENABLED": config.HTTP.HTTPSEnabledEnv(),
"CONFIG_PATH": server.Config.ConfigPath, "CONFIG_PATH": config.ConfigPath,
"DEPLOY_ROOT": server.Config.Paths.Root, "DEPLOY_ROOT": config.Paths.Root,
"SELF_OVERRIDES_FILE": server.Config.Paths.OverridesJSON, "SELF_OVERRIDES_FILE": config.Paths.OverridesJSON,
"SELF_RESOLVER_BLOCK_FILE": server.Config.Paths.ResolverBlocks, "SELF_RESOLVER_BLOCK_FILE": config.Paths.ResolverBlocks,
"CUSTOM_ASSETS_PATH": server.dependencies.Templating.CustomAssetsPath(), "CUSTOM_ASSETS_PATH": server.dependencies.Templating.CustomAssetsPath(),
}, },
@ -48,6 +50,6 @@ func (server *Server) Trigger(ctx context.Context) error {
func (server *Server) Context(parent component.InstallationContext) component.InstallationContext { func (server *Server) Context(parent component.InstallationContext) component.InstallationContext {
return component.InstallationContext{ return component.InstallationContext{
bootstrap.Executable: server.Config.Paths.CurrentExecutable(), // TODO: Does this make sense? bootstrap.Executable: component.GetStill(server).Config.Paths.CurrentExecutable(), // TODO: Does this make sense?
} }
} }

View file

@ -8,7 +8,7 @@ import (
// CustomAssetsPath is the path custom assets are stored at // CustomAssetsPath is the path custom assets are stored at
func (tpl *Templating) CustomAssetsPath() string { func (tpl *Templating) CustomAssetsPath() string {
return filepath.Join(tpl.Config.Paths.Root, "core", "assets") return filepath.Join(component.GetStill(tpl).Config.Paths.Root, "core", "assets")
} }
func (tpl *Templating) CustomAssetPath(name string) string { func (tpl *Templating) CustomAssetPath(name string) string {

View file

@ -21,7 +21,7 @@ var (
) )
func (s *Solr) Path() string { func (s *Solr) Path() string {
return filepath.Join(s.Still.Config.Paths.Root, "core", "solr") return filepath.Join(component.GetStill(s).Config.Paths.Root, "core", "solr")
} }
func (*Solr) Context(parent component.InstallationContext) component.InstallationContext { func (*Solr) Context(parent component.InstallationContext) component.InstallationContext {
@ -37,7 +37,7 @@ func (solr *Solr) Stack() component.StackWithResources {
ContextPath: "solr", ContextPath: "solr",
EnvContext: map[string]string{ EnvContext: map[string]string{
"DOCKER_NETWORK_NAME": solr.Config.Docker.Network(), "DOCKER_NETWORK_NAME": component.GetStill(solr).Config.Docker.Network(),
}, },
MakeDirs: []string{ MakeDirs: []string{

View file

@ -48,7 +48,7 @@ func (sql *SQL) QueryTable(ctx context.Context, table component.Table) (*gorm.DB
// queryTable returns a gorm.DB to connect to the provided distillery database table // queryTable returns a gorm.DB to connect to the provided distillery database table
func (sql *SQL) queryTable(ctx context.Context, silent bool, table string) (*gorm.DB, error) { func (sql *SQL) queryTable(ctx context.Context, silent bool, table string) (*gorm.DB, error) {
conn, err := sql.connect(sql.Config.SQL.Database) conn, err := sql.connect(component.GetStill(sql).Config.SQL.Database)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -113,8 +113,9 @@ func (ssql *SQL) connect(database string) (*sql.DB, error) {
// dsn returns a dsn fof connecting to the database // dsn returns a dsn fof connecting to the database
func (sql *SQL) dsn(database string) string { func (sql *SQL) dsn(database string) string {
user := sql.Config.SQL.AdminUsername config := component.GetStill(sql).Config.SQL
pass := sql.Config.SQL.AdminPassword user := config.AdminUsername
pass := config.AdminPassword
network := "tcp" network := "tcp"
server := sql.ServerURL server := sql.ServerURL

View file

@ -5,7 +5,7 @@ import (
"path/filepath" "path/filepath"
"time" "time"
"github.com/FAU-CDI/wisski-distillery/internal/config" config_package "github.com/FAU-CDI/wisski-distillery/internal/config"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component" "github.com/FAU-CDI/wisski-distillery/internal/dis/component"
"github.com/tkw1536/pkglib/fsx/umaskfree" "github.com/tkw1536/pkglib/fsx/umaskfree"
"github.com/tkw1536/pkglib/yamlx" "github.com/tkw1536/pkglib/yamlx"
@ -32,7 +32,7 @@ var (
) )
func (sql *SQL) Path() string { func (sql *SQL) Path() string {
return filepath.Join(sql.Still.Config.Paths.Root, "core", "sql") return filepath.Join(component.GetStill(sql).Config.Paths.Root, "core", "sql")
} }
func (*SQL) Context(parent component.InstallationContext) component.InstallationContext { func (*SQL) Context(parent component.InstallationContext) component.InstallationContext {
@ -43,19 +43,20 @@ func (*SQL) Context(parent component.InstallationContext) component.Installation
var resources embed.FS var resources embed.FS
func (sql *SQL) Stack() component.StackWithResources { func (sql *SQL) Stack() component.StackWithResources {
config := component.GetStill(sql).Config
return component.MakeStack(sql, component.StackWithResources{ return component.MakeStack(sql, component.StackWithResources{
Resources: resources, Resources: resources,
ContextPath: "sql", ContextPath: "sql",
EnvContext: map[string]string{ EnvContext: map[string]string{
"DOCKER_NETWORK_NAME": sql.Config.Docker.Network(), "DOCKER_NETWORK_NAME": config.Docker.Network(),
"HTTPS_ENABLED": sql.Config.HTTP.HTTPSEnabledEnv(), "HTTPS_ENABLED": config.HTTP.HTTPSEnabledEnv(),
"HOST_RULE": sql.Config.HTTP.HostRule(config.PHPMyAdminDomain.Domain()), "HOST_RULE": config.HTTP.HostRule(config_package.PHPMyAdminDomain.Domain()),
}, },
ComposerYML: func(root *yaml.Node) (*yaml.Node, error) { ComposerYML: func(root *yaml.Node) (*yaml.Node, error) {
// phpmyadmin is exposed => everything is fine // phpmyadmin is exposed => everything is fine
if sql.Config.HTTP.PhpMyAdmin.Set && sql.Config.HTTP.PhpMyAdmin.Value { if config.HTTP.PhpMyAdmin.Set && config.HTTP.PhpMyAdmin.Value {
return root, nil return root, nil
} }

View file

@ -8,6 +8,7 @@ import (
"reflect" "reflect"
"time" "time"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
"github.com/FAU-CDI/wisski-distillery/pkg/logging" "github.com/FAU-CDI/wisski-distillery/pkg/logging"
"github.com/tkw1536/goprogram/exit" "github.com/tkw1536/goprogram/exit"
"github.com/tkw1536/pkglib/sqlx" "github.com/tkw1536/pkglib/sqlx"
@ -68,6 +69,7 @@ var errSQLUnableToMigrate = exit.Error{
// Update initializes or updates the SQL database. // Update initializes or updates the SQL database.
func (sql *SQL) Update(ctx context.Context, progress io.Writer) error { func (sql *SQL) Update(ctx context.Context, progress io.Writer) error {
config := component.GetStill(sql).Config.SQL
// unsafely create the admin user! // unsafely create the admin user!
{ {
@ -76,8 +78,8 @@ func (sql *SQL) Update(ctx context.Context, progress io.Writer) error {
} }
logging.LogMessage(progress, "Creating administrative user") logging.LogMessage(progress, "Creating administrative user")
{ {
username := sql.Config.SQL.AdminUsername username := config.AdminUsername
password := sql.Config.SQL.AdminPassword password := config.AdminPassword
if err := sql.CreateSuperuser(ctx, username, password, true); err != nil { if err := sql.CreateSuperuser(ctx, username, password, true); err != nil {
return errSQLUnableToCreateUser return errSQLUnableToCreateUser
} }
@ -87,10 +89,10 @@ func (sql *SQL) Update(ctx context.Context, progress io.Writer) error {
// create the admin user // create the admin user
logging.LogMessage(progress, "Creating sql database") logging.LogMessage(progress, "Creating sql database")
{ {
if !sqlx.IsSafeDatabaseLiteral(sql.Config.SQL.Database) { if !sqlx.IsSafeDatabaseLiteral(config.Database) {
return errSQLUnsafeDatabaseName return errSQLUnsafeDatabaseName
} }
createDBSQL := fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s`;", sql.Config.SQL.Database) createDBSQL := fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s`;", config.Database)
if err := sql.Exec(createDBSQL); err != nil { if err := sql.Exec(createDBSQL); err != nil {
return err return err
} }

View file

@ -26,7 +26,7 @@ func (ssh2 *SSH2) HandleRoute(ctx context.Context, path string) (http.Handler, e
} }
// find the host // find the host
slug, ok := ssh2.Config.HTTP.SlugFromHost(r.Host) slug, ok := component.GetStill(ssh2).Config.HTTP.SlugFromHost(r.Host)
if slug == "" || !ok { if slug == "" || !ok {
httpx.TextInterceptor.Intercept(w, r, httpx.ErrNotFound) httpx.TextInterceptor.Intercept(w, r, httpx.ErrNotFound)
return return

View file

@ -58,10 +58,11 @@ func (i Intercept) Intercept(req component.HostPort) (intercepted bool, ok bool,
} }
func (ssh2 *SSH2) Intercepts() []Intercept { func (ssh2 *SSH2) Intercepts() []Intercept {
upstream := component.GetStill(ssh2).Upstream
return ssh2.interceptsC.Get(func() []Intercept { return ssh2.interceptsC.Get(func() []Intercept {
return []Intercept{ return []Intercept{
{Description: "Triplestore", Match: component.HostPort{Host: "triplestore", Port: 7200}, Dest: ssh2.Upstream.Triplestore}, {Description: "Triplestore", Match: component.HostPort{Host: "triplestore", Port: 7200}, Dest: upstream.Triplestore},
{Description: "SQL", Match: component.HostPort{Host: "sql", Port: 3306}, Dest: ssh2.Upstream.SQL}, {Description: "SQL", Match: component.HostPort{Host: "sql", Port: 3306}, Dest: upstream.SQL},
{Description: "PHPMyAdmin", Match: component.HostPort{Host: "phpmyadmin", Port: 80}, Dest: component.HostPort{Host: "phpmyadmin", Port: 80}}, {Description: "PHPMyAdmin", Match: component.HostPort{Host: "phpmyadmin", Port: 80}, Dest: component.HostPort{Host: "phpmyadmin", Port: 80}},
} }
}) })
@ -77,13 +78,15 @@ func (ssh2 *SSH2) getForwardDest(req component.HostPort, ctx ssh.Context) (ok bo
return ok, dest, rejectReason return ok, dest, rejectReason
} }
config := component.GetStill(ssh2).Config
// then check the instances // then check the instances
slug, ok := ssh2.Config.HTTP.SlugFromHost(req.Host) slug, ok := config.HTTP.SlugFromHost(req.Host)
if !ok || req.Port != 22 || !hasPermission(ctx, slug) { if !ok || req.Port != 22 || !hasPermission(ctx, slug) {
return false, dest, "permission denied" return false, dest, "permission denied"
} }
return true, component.HostPort{Host: slug + "." + ssh2.Config.HTTP.PrimaryDomain + ".wisski", Port: 22}, "" return true, component.HostPort{Host: slug + "." + config.HTTP.PrimaryDomain + ".wisski", Port: 22}, ""
} }
// handleDirectTCP handles a direct tcp connection for the server // handleDirectTCP handles a direct tcp connection for the server

View file

@ -6,6 +6,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
"github.com/gliderlabs/ssh" "github.com/gliderlabs/ssh"
) )
@ -38,17 +39,18 @@ Press CTRL-C to close this connection.
` `
func (ssh2 *SSH2) handleConnection(session ssh.Session) { func (ssh2 *SSH2) handleConnection(session ssh.Session) {
config := component.GetStill(ssh2).Config
slug, _ := getAnyPermission(session.Context()) slug, _ := getAnyPermission(session.Context())
banner := welcomeMessage banner := welcomeMessage
for _, oldnew := range [][2]string{ for _, oldnew := range [][2]string{
{"${SLUG}", slug}, {"${SLUG}", slug},
{"${HOSTNAME}", slug + "." + ssh2.Config.HTTP.PrimaryDomain}, {"${HOSTNAME}", slug + "." + config.HTTP.PrimaryDomain},
{"${DOMAIN}", ssh2.Config.HTTP.PanelDomain()}, {"${DOMAIN}", config.HTTP.PanelDomain()},
{"${PORT}", strconv.FormatUint(uint64(ssh2.Config.Listen.SSHPort), 10)}, {"${PORT}", strconv.FormatUint(uint64(config.Listen.SSHPort), 10)},
{"${HELP_URL}", ssh2.Config.HTTP.JoinPath("user", "ssh").String()}, {"${HELP_URL}", config.HTTP.JoinPath("user", "ssh").String()},
} { } {
banner = strings.ReplaceAll(banner, oldnew[0], oldnew[1]) banner = strings.ReplaceAll(banner, oldnew[0], oldnew[1])
} }

View file

@ -9,25 +9,26 @@ import (
) )
func (ssh *SSH2) Path() string { func (ssh *SSH2) Path() string {
return filepath.Join(ssh.Still.Config.Paths.Root, "core", "ssh2") return filepath.Join(component.GetStill(ssh).Config.Paths.Root, "core", "ssh2")
} }
//go:embed all:ssh2 //go:embed all:ssh2
var resources embed.FS var resources embed.FS
func (ssh *SSH2) Stack() component.StackWithResources { func (ssh *SSH2) Stack() component.StackWithResources {
config := component.GetStill(ssh).Config
return component.MakeStack(ssh, component.StackWithResources{ return component.MakeStack(ssh, component.StackWithResources{
Resources: resources, Resources: resources,
ContextPath: "ssh2", ContextPath: "ssh2",
EnvContext: map[string]string{ EnvContext: map[string]string{
"DOCKER_NETWORK_NAME": ssh.Config.Docker.Network(), "DOCKER_NETWORK_NAME": config.Docker.Network(),
"CONFIG_PATH": ssh.Config.ConfigPath, "CONFIG_PATH": config.ConfigPath,
"DEPLOY_ROOT": ssh.Config.Paths.Root, "DEPLOY_ROOT": config.Paths.Root,
"SELF_OVERRIDES_FILE": ssh.Config.Paths.OverridesJSON, "SELF_OVERRIDES_FILE": config.Paths.OverridesJSON,
"SELF_RESOLVER_BLOCK_FILE": ssh.Config.Paths.ResolverBlocks, "SELF_RESOLVER_BLOCK_FILE": config.Paths.ResolverBlocks,
}, },
CopyContextFiles: []string{bootstrap.Executable}, CopyContextFiles: []string{bootstrap.Executable},
@ -36,6 +37,6 @@ func (ssh *SSH2) Stack() component.StackWithResources {
func (ssh *SSH2) Context(parent component.InstallationContext) component.InstallationContext { func (ssh *SSH2) Context(parent component.InstallationContext) component.InstallationContext {
return component.InstallationContext{ return component.InstallationContext{
bootstrap.Executable: ssh.Config.Paths.CurrentExecutable(), // TODO: Does this make sense? bootstrap.Executable: component.GetStill(ssh).Config.Paths.CurrentExecutable(), // TODO: Does this make sense?
} }
} }

View file

@ -9,6 +9,7 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/tkw1536/pkglib/timex" "github.com/tkw1536/pkglib/timex"
@ -58,12 +59,12 @@ func (rh *RequestHeaders) With(headers RequestHeaders) *RequestHeaders {
} }
// DoRest performs a (raw) http request to the without a body. // DoRest performs a (raw) http request to the without a body.
func (ts Triplestore) DoRest(ctx context.Context, timeout time.Duration, method, url string, headers *RequestHeaders) (*http.Response, error) { func (ts *Triplestore) DoRest(ctx context.Context, timeout time.Duration, method, url string, headers *RequestHeaders) (*http.Response, error) {
return ts.DoRestWithReader(ctx, timeout, method, url, headers, nil) return ts.DoRestWithReader(ctx, timeout, method, url, headers, nil)
} }
// DoRestWithForm performs a http request where the body are all bytes read from fieldvalue. // DoRestWithForm performs a http request where the body are all bytes read from fieldvalue.
func (ts Triplestore) DoRestWithForm(ctx context.Context, timeout time.Duration, method, url string, headers *RequestHeaders, fieldname string, fieldvalue io.Reader) (*http.Response, error) { func (ts *Triplestore) DoRestWithForm(ctx context.Context, timeout time.Duration, method, url string, headers *RequestHeaders, fieldname string, fieldvalue io.Reader) (*http.Response, error) {
var buffer bytes.Buffer var buffer bytes.Buffer
// write the file to it // write the file to it
@ -83,7 +84,7 @@ func (ts Triplestore) DoRestWithForm(ctx context.Context, timeout time.Duration,
// DoRestWithReader performs a http request where the body is copied from the given io.Reader. // DoRestWithReader performs a http request where the body is copied from the given io.Reader.
// The caller must ensure the reader is closed. // The caller must ensure the reader is closed.
func (ts Triplestore) DoRestWithMarshal(ctx context.Context, timeout time.Duration, method, url string, headers *RequestHeaders, body any) (*http.Response, error) { func (ts *Triplestore) DoRestWithMarshal(ctx context.Context, timeout time.Duration, method, url string, headers *RequestHeaders, body any) (*http.Response, error) {
// encode into a buffer // encode into a buffer
var buffer bytes.Buffer var buffer bytes.Buffer
if err := json.NewEncoder(&buffer).Encode(body); err != nil { if err := json.NewEncoder(&buffer).Encode(body); err != nil {
@ -95,7 +96,7 @@ func (ts Triplestore) DoRestWithMarshal(ctx context.Context, timeout time.Durati
// DoRestWithReader performs a http request where the body is copied from the given io.Reader. // DoRestWithReader performs a http request where the body is copied from the given io.Reader.
// The caller must ensure the reader is closed. // The caller must ensure the reader is closed.
func (ts Triplestore) DoRestWithReader(ctx context.Context, timeout time.Duration, method string, url string, headers *RequestHeaders, body io.Reader) (*http.Response, error) { func (ts *Triplestore) DoRestWithReader(ctx context.Context, timeout time.Duration, method string, url string, headers *RequestHeaders, body io.Reader) (*http.Response, error) {
// create the request object // create the request object
client := &http.Client{ client := &http.Client{
Timeout: timeout, Timeout: timeout,
@ -104,12 +105,14 @@ func (ts Triplestore) DoRestWithReader(ctx context.Context, timeout time.Duratio
}, },
} }
config := component.GetStill(ts).Config.TS
// create the request and authentication // create the request and authentication
req, err := http.NewRequestWithContext(ctx, method, ts.BaseURL+url, body) req, err := http.NewRequestWithContext(ctx, method, ts.BaseURL+url, body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
req.SetBasicAuth(ts.Config.TS.AdminUsername, ts.Config.TS.AdminPassword) req.SetBasicAuth(config.AdminUsername, config.AdminPassword)
// add extra headers // add extra headers
if headers != nil && headers.Accept != "" { if headers != nil && headers.Accept != "" {

View file

@ -5,7 +5,7 @@ import (
"path/filepath" "path/filepath"
"time" "time"
"github.com/FAU-CDI/wisski-distillery/internal/config" config_package "github.com/FAU-CDI/wisski-distillery/internal/config"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component" "github.com/FAU-CDI/wisski-distillery/internal/dis/component"
"github.com/tkw1536/pkglib/yamlx" "github.com/tkw1536/pkglib/yamlx"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
@ -27,7 +27,7 @@ var (
) )
func (ts *Triplestore) Path() string { func (ts *Triplestore) Path() string {
return filepath.Join(ts.Still.Config.Paths.Root, "core", "triplestore") return filepath.Join(component.GetStill(ts).Config.Paths.Root, "core", "triplestore")
} }
func (Triplestore) Context(parent component.InstallationContext) component.InstallationContext { func (Triplestore) Context(parent component.InstallationContext) component.InstallationContext {
@ -38,6 +38,7 @@ func (Triplestore) Context(parent component.InstallationContext) component.Insta
var resources embed.FS var resources embed.FS
func (ts *Triplestore) Stack() component.StackWithResources { func (ts *Triplestore) Stack() component.StackWithResources {
config := component.GetStill(ts).Config
return component.MakeStack(ts, component.StackWithResources{ return component.MakeStack(ts, component.StackWithResources{
Resources: resources, Resources: resources,
ContextPath: "triplestore", ContextPath: "triplestore",
@ -45,14 +46,14 @@ func (ts *Triplestore) Stack() component.StackWithResources {
CopyContextFiles: []string{"graphdb.zip"}, // TODO: Move into constant? CopyContextFiles: []string{"graphdb.zip"}, // TODO: Move into constant?
EnvContext: map[string]string{ EnvContext: map[string]string{
"DOCKER_NETWORK_NAME": ts.Config.Docker.Network(), "DOCKER_NETWORK_NAME": config.Docker.Network(),
"HOST_RULE": ts.Config.HTTP.HostRule(config.TriplestoreDomain.Domain()), "HOST_RULE": config.HTTP.HostRule(config_package.TriplestoreDomain.Domain()),
"HTTPS_ENABLED": ts.Config.HTTP.HTTPSEnabledEnv(), "HTTPS_ENABLED": config.HTTP.HTTPSEnabledEnv(),
}, },
ComposerYML: func(root *yaml.Node) (*yaml.Node, error) { ComposerYML: func(root *yaml.Node) (*yaml.Node, error) {
// ts is exposed => everything is fine // ts is exposed => everything is fine
if ts.Config.HTTP.TS.Set && ts.Config.HTTP.TS.Value { if config.HTTP.TS.Set && config.HTTP.TS.Value {
return root, nil return root, nil
} }

View file

@ -6,13 +6,14 @@ import (
"io" "io"
"net/http" "net/http"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
"github.com/FAU-CDI/wisski-distillery/pkg/logging" "github.com/FAU-CDI/wisski-distillery/pkg/logging"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
var errTriplestoreFailedSecurity = errors.New("failed to enable triplestore security: request did not succeed with HTTP 200 OK") var errTriplestoreFailedSecurity = errors.New("failed to enable triplestore security: request did not succeed with HTTP 200 OK")
func (ts Triplestore) Update(ctx context.Context, progress io.Writer) error { func (ts *Triplestore) Update(ctx context.Context, progress io.Writer) error {
logging.LogMessage(progress, "Waiting for Triplestore") logging.LogMessage(progress, "Waiting for Triplestore")
if err := ts.Wait(ctx); err != nil { if err := ts.Wait(ctx); err != nil {
return err return err
@ -20,8 +21,10 @@ func (ts Triplestore) Update(ctx context.Context, progress io.Writer) error {
logging.LogMessage(progress, "Resetting admin user password") logging.LogMessage(progress, "Resetting admin user password")
{ {
res, err := ts.DoRestWithMarshal(ctx, tsTrivialTimeout, http.MethodPut, "/rest/security/users/"+ts.Config.TS.AdminUsername, nil, TriplestoreUserPayload{ config := component.GetStill(ts).Config.TS
Password: ts.Config.TS.AdminPassword,
res, err := ts.DoRestWithMarshal(ctx, tsTrivialTimeout, http.MethodPut, "/rest/security/users/"+config.AdminUsername, nil, TriplestoreUserPayload{
Password: config.AdminPassword,
AppSettings: TriplestoreUserAppSettings{ AppSettings: TriplestoreUserAppSettings{
DefaultInference: true, DefaultInference: true,
DefaultVisGraphSchema: true, DefaultVisGraphSchema: true,

View file

@ -21,7 +21,7 @@ var (
) )
func (web *Web) Path() string { func (web *Web) Path() string {
return filepath.Join(web.Still.Config.Paths.Root, "core", "web") return filepath.Join(component.GetStill(web).Config.Paths.Root, "core", "web")
} }
func (*Web) Context(parent component.InstallationContext) component.InstallationContext { func (*Web) Context(parent component.InstallationContext) component.InstallationContext {
@ -37,12 +37,13 @@ var dockerComposeHTTPS []byte
func (web *Web) Stack() component.StackWithResources { func (web *Web) Stack() component.StackWithResources {
var stack component.StackWithResources var stack component.StackWithResources
config := component.GetStill(web).Config
stack.EnvContext = map[string]string{ stack.EnvContext = map[string]string{
"DOCKER_NETWORK_NAME": web.Config.Docker.Network(), "DOCKER_NETWORK_NAME": config.Docker.Network(),
"CERT_EMAIL": web.Config.HTTP.CertbotEmail, "CERT_EMAIL": config.HTTP.CertbotEmail,
} }
if web.Config.HTTP.HTTPSEnabled() { if config.HTTP.HTTPSEnabled() {
stack.ComposerYML = readYaml(dockerComposeHTTPS) stack.ComposerYML = readYaml(dockerComposeHTTPS)
stack.TouchFilesPerm = 0600 stack.TouchFilesPerm = 0600
stack.TouchFiles = []string{"acme.json"} stack.TouchFiles = []string{"acme.json"}

View file

@ -20,7 +20,8 @@ var errBlindUpdateFailed = exit.Error{
// Update performs a blind drush update // Update performs a blind drush update
func (composer *Composer) Update(ctx context.Context, progress io.Writer) (err error) { func (composer *Composer) Update(ctx context.Context, progress io.Writer) (err error) {
defer errBlindUpdateFailed.WithMessageF(composer.Slug).DeferWrap(&err)
defer errBlindUpdateFailed.WithMessageF(ingredient.GetLiquid(composer).Slug).DeferWrap(&err)
if err := composer.FixPermission(ctx, progress); err != nil { if err := composer.FixPermission(ctx, progress); err != nil {
return err return err

View file

@ -24,7 +24,7 @@ func (drush *Drush) Cron(ctx context.Context, progress io.Writer) error {
if err != nil { if err != nil {
code := err.(barrel.ExitError).Code code := err.(barrel.ExitError).Code
// keep going, because we want to run as many crons as possible // keep going, because we want to run as many crons as possible
fmt.Fprintf(progress, "%v", errCronFailed.WithMessageF(drush.Slug, code)) fmt.Fprintf(progress, "%v", errCronFailed.WithMessageF(ingredient.GetLiquid(drush).Slug, code))
} }
return nil return nil

View file

@ -8,6 +8,7 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/dis/component" "github.com/FAU-CDI/wisski-distillery/internal/dis/component"
"github.com/FAU-CDI/wisski-distillery/internal/models" "github.com/FAU-CDI/wisski-distillery/internal/models"
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel" "github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel"
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel/composer" "github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel/composer"
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php/extras" "github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php/extras"
@ -106,7 +107,9 @@ func (provision *Manager) bootstrap(ctx context.Context, progress io.Writer, fla
} }
} }
var sqlDBURL = "mysql://" + provision.SqlUsername + ":" + provision.SqlPassword + "@sql/" + provision.SqlDatabase liquid := ingredient.GetLiquid(provision)
var sqlDBURL = "mysql://" + liquid.SqlUsername + ":" + liquid.SqlPassword + "@sql/" + liquid.SqlDatabase
// Use 'drush' to run the site-installation. // Use 'drush' to run the site-installation.
// Here we need to use the username, password and database creds we made above. // Here we need to use the username, password and database creds we made above.
@ -115,8 +118,8 @@ func (provision *Manager) bootstrap(ctx context.Context, progress io.Writer, fla
if err := provision.dependencies.Drush.Exec( if err := provision.dependencies.Drush.Exec(
ctx, progress, ctx, progress,
"site-install", "site-install",
"standard", "--yes", "--site-name="+provision.Domain(), "standard", "--yes", "--site-name="+liquid.Domain(),
"--account-name="+provision.DrupalUsername, "--account-pass="+provision.DrupalPassword, "--account-name="+liquid.DrupalUsername, "--account-pass="+liquid.DrupalPassword,
"--db-url="+sqlDBURL, "--db-url="+sqlDBURL,
); err != nil { ); err != nil {
return err return err
@ -166,11 +169,11 @@ func (provision *Manager) bootstrap(ctx context.Context, progress io.Writer, fla
if err := provision.dependencies.Adapters.CreateDistilleryAdapter(ctx, nil, extras.DistilleryAdapter{ if err := provision.dependencies.Adapters.CreateDistilleryAdapter(ctx, nil, extras.DistilleryAdapter{
Label: "Default WissKI Distillery Adapter", Label: "Default WissKI Distillery Adapter",
MachineName: "default", MachineName: "default",
Description: "Default Adapter for " + provision.Domain(), Description: "Default Adapter for " + liquid.Domain(),
InstanceDomain: provision.Domain(), InstanceDomain: liquid.Domain(),
GraphDBRepository: provision.GraphDBRepository, GraphDBRepository: liquid.GraphDBRepository,
GraphDBUsername: provision.GraphDBUsername, GraphDBUsername: liquid.GraphDBUsername,
GraphDBPassword: provision.GraphDBPassword, GraphDBPassword: liquid.GraphDBPassword,
}); err != nil { }); err != nil {
return err return err
} }
@ -185,9 +188,9 @@ func (provision *Manager) bootstrap(ctx context.Context, progress io.Writer, fla
logging.LogMessage(progress, "Provisioning is now complete") logging.LogMessage(progress, "Provisioning is now complete")
{ {
fmt.Fprintf(progress, "URL: %s\n", provision.URL()) fmt.Fprintf(progress, "URL: %s\n", liquid.URL())
fmt.Fprintf(progress, "Username: %s\n", provision.DrupalUsername) fmt.Fprintf(progress, "Username: %s\n", liquid.DrupalUsername)
fmt.Fprintf(progress, "Password: %s\n", provision.DrupalPassword) fmt.Fprintf(progress, "Password: %s\n", liquid.DrupalPassword)
} }
return nil return nil

View file

@ -10,7 +10,7 @@ import (
// Running checks if this WissKI is currently running. // Running checks if this WissKI is currently running.
func (barrel *Barrel) Running(ctx context.Context) (bool, error) { func (barrel *Barrel) Running(ctx context.Context) (bool, error) {
containers, err := barrel.Docker.Containers(ctx, barrel.Stack().Dir) containers, err := ingredient.GetLiquid(barrel).Docker.Containers(ctx, barrel.Stack().Dir)
if err != nil { if err != nil {
// The compose file is gone => the stack doesn't exist. // The compose file is gone => the stack doesn't exist.
// Probably means some purging got interrupted. // Probably means some purging got interrupted.

View file

@ -18,7 +18,8 @@ var (
) )
func (ssh *SSH) Keys(ctx context.Context) (keys []ssh.PublicKey, err error) { func (ssh *SSH) Keys(ctx context.Context) (keys []ssh.PublicKey, err error) {
grants, err := ssh.Liquid.Policy.Instance(ctx, ssh.Slug) liquid := ingredient.GetLiquid(ssh)
grants, err := liquid.Policy.Instance(ctx, liquid.Slug)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -28,7 +29,7 @@ func (ssh *SSH) Keys(ctx context.Context) (keys []ssh.PublicKey, err error) {
if !grant.DrupalAdminRole { if !grant.DrupalAdminRole {
continue continue
} }
ukeys, err := ssh.Liquid.Keys.Keys(ctx, grant.User) ukeys, err := liquid.Keys.Keys(ctx, grant.User)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -50,7 +51,7 @@ func (ssh *SSH) AllKeys(ctx context.Context) (keys []ssh.PublicKey, err error) {
return nil, err return nil, err
} }
gkeys, err := ssh.Liquid.Keys.Admin(ctx) gkeys, err := ingredient.GetLiquid(ssh).Keys.Admin(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -5,6 +5,7 @@ import (
"path/filepath" "path/filepath"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component" "github.com/FAU-CDI/wisski-distillery/internal/dis/component"
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
) )
//go:embed all:barrel //go:embed all:barrel
@ -17,9 +18,12 @@ var localSettingsTemplate string
// Barrel returns a stack representing the running WissKI Instance // Barrel returns a stack representing the running WissKI Instance
func (barrel *Barrel) Stack() component.StackWithResources { func (barrel *Barrel) Stack() component.StackWithResources {
liquid := ingredient.GetLiquid(barrel)
config := ingredient.GetStill(barrel).Config
return component.StackWithResources{ return component.StackWithResources{
Stack: component.Stack{ Stack: component.Stack{
Dir: barrel.FilesystemBase, Dir: liquid.FilesystemBase,
}, },
Resources: barrelResources, Resources: barrelResources,
@ -30,23 +34,23 @@ func (barrel *Barrel) Stack() component.StackWithResources {
}, },
EnvContext: map[string]string{ EnvContext: map[string]string{
"DOCKER_NETWORK_NAME": barrel.Malt.Config.Docker.Network(), "DOCKER_NETWORK_NAME": config.Docker.Network(),
"SLUG": barrel.Slug, "SLUG": liquid.Slug,
"HOST_RULE": barrel.HostRule(), "HOST_RULE": liquid.HostRule(),
"WISSKI_HOSTNAME": barrel.Hostname(), "WISSKI_HOSTNAME": liquid.Hostname(),
"HTTPS_ENABLED": barrel.Malt.Config.HTTP.HTTPSEnabledEnv(), "HTTPS_ENABLED": config.HTTP.HTTPSEnabledEnv(),
"DATA_PATH": filepath.Join(barrel.FilesystemBase, "data"), "DATA_PATH": filepath.Join(liquid.FilesystemBase, "data"),
"RUNTIME_DIR": barrel.Malt.Config.Paths.RuntimeDir(), "RUNTIME_DIR": config.Paths.RuntimeDir(),
"LOCAL_SETTINGS_PATH": filepath.Join(barrel.FilesystemBase, localSettingsName), "LOCAL_SETTINGS_PATH": filepath.Join(liquid.FilesystemBase, localSettingsName),
"LOCAL_SETTINGS_MOUNT": LocalSettingsPath, "LOCAL_SETTINGS_MOUNT": LocalSettingsPath,
"BARREL_BASE_IMAGE": barrel.GetDockerBaseImage(), "BARREL_BASE_IMAGE": liquid.GetDockerBaseImage(),
"IIP_SERVER_ENABLED": barrel.GetIIPServerEnabled(), "IIP_SERVER_ENABLED": liquid.GetIIPServerEnabled(),
"OPCACHE_MODE": barrel.OpCacheMode(), "OPCACHE_MODE": liquid.OpCacheMode(),
"CONTENT_SECURITY_POLICY": barrel.ContentSecurityPolicy, "CONTENT_SECURITY_POLICY": liquid.ContentSecurityPolicy,
}, },
MakeDirs: []string{"data", ".composer"}, MakeDirs: []string{"data", ".composer"},

View file

@ -4,6 +4,7 @@ import (
"context" "context"
"io" "io"
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
"github.com/FAU-CDI/wisski-distillery/pkg/logging" "github.com/FAU-CDI/wisski-distillery/pkg/logging"
) )
@ -12,7 +13,7 @@ import (
func (smanager *SystemManager) BuildSettings(ctx context.Context, progress io.Writer) (err error) { func (smanager *SystemManager) BuildSettings(ctx context.Context, progress io.Writer) (err error) {
logging.LogMessage(progress, "Updating TRUSTED_HOST_PATTERNS in settings.php") logging.LogMessage(progress, "Updating TRUSTED_HOST_PATTERNS in settings.php")
{ {
if err := smanager.dependencies.Settings.SetTrustedDomain(ctx, nil, smanager.Domain()); err != nil { if err := smanager.dependencies.Settings.SetTrustedDomain(ctx, nil, ingredient.GetLiquid(smanager).Domain()); err != nil {
return err return err
} }
} }

View file

@ -43,7 +43,7 @@ func (smanager *SystemManager) ApplyInitial(ctx context.Context, progress io.Wri
// start inidicates if the image should be started afterwards // start inidicates if the image should be started afterwards
func (smanager *SystemManager) apply(ctx context.Context, progress io.Writer, system models.System, start bool) error { func (smanager *SystemManager) apply(ctx context.Context, progress io.Writer, system models.System, start bool) error {
// store the new system configuration // store the new system configuration
smanager.Instance.System = system ingredient.GetLiquid(smanager).Instance.System = system
if err := smanager.dependencies.Bookkeeping.Save(ctx); err != nil { if err := smanager.dependencies.Bookkeeping.Save(ctx); err != nil {
return err return err
} }

View file

@ -13,32 +13,34 @@ type Bookkeeping struct {
// Save saves this instance in the bookkeeping table // Save saves this instance in the bookkeeping table
func (bk *Bookkeeping) Save(ctx context.Context) error { func (bk *Bookkeeping) Save(ctx context.Context) error {
sdb, err := bk.Malt.SQL.QueryTable(ctx, bk.Malt.InstanceTable) liquid := ingredient.GetLiquid(bk)
sdb, err := ingredient.GetLiquid(bk).Malt.SQL.QueryTable(ctx, liquid.Malt.InstanceTable)
if err != nil { if err != nil {
return err return err
} }
// it has never been created => we need to create it in the database // it has never been created => we need to create it in the database
if bk.Instance.Created.IsZero() { if liquid.Instance.Created.IsZero() {
return sdb.Create(&bk.Instance).Error return sdb.Create(&liquid.Instance).Error
} }
// Update based on the primary key! // Update based on the primary key!
return sdb.Select("*").Save(&bk.Instance).Error return sdb.Select("*").Save(&liquid.Instance).Error
} }
// Delete deletes this instance from the bookkeeping table // Delete deletes this instance from the bookkeeping table
func (bk *Bookkeeping) Delete(ctx context.Context) error { func (bk *Bookkeeping) Delete(ctx context.Context) error {
sdb, err := bk.Malt.SQL.QueryTable(ctx, bk.Malt.InstanceTable) liquid := ingredient.GetLiquid(bk)
sdb, err := liquid.SQL.QueryTable(ctx, liquid.InstanceTable)
if err != nil { if err != nil {
return err return err
} }
// doesn't exist => nothing to delete // doesn't exist => nothing to delete
if bk.Instance.Created.IsZero() { if liquid.Instance.Created.IsZero() {
return nil return nil
} }
// delete it directly // delete it directly
return sdb.Delete(&bk.Instance).Error return sdb.Delete(&liquid.Instance).Error
} }

View file

@ -29,7 +29,7 @@ var (
) )
// Information fetches information about this WissKI. // Information fetches information about this WissKI.
func (wisski *Info) Information(ctx context.Context, quick bool) (info status.WissKI, err error) { func (nfo *Info) Information(ctx context.Context, quick bool) (info status.WissKI, err error) {
// setup flags // setup flags
flags := ingredient.FetcherFlags{ flags := ingredient.FetcherFlags{
Quick: quick, Quick: quick,
@ -43,7 +43,7 @@ func (wisski *Info) Information(ctx context.Context, quick bool) (info status.Wi
Limit: 5, Limit: 5,
New: func() *phpx.Server { New: func() *phpx.Server {
atomic.AddUint64(&serversUsed, 1) atomic.AddUint64(&serversUsed, 1)
return wisski.dependencies.PHP.NewServer() return nfo.dependencies.PHP.NewServer()
}, },
Discard: func(s *phpx.Server) { Discard: func(s *phpx.Server) {
s.Close() s.Close()
@ -53,7 +53,7 @@ func (wisski *Info) Information(ctx context.Context, quick bool) (info status.Wi
// setup a dictionary to record data about how long each operation took. // setup a dictionary to record data about how long each operation took.
// we use a slice as opposed to a map to avoid having to mutex! // we use a slice as opposed to a map to avoid having to mutex!
fetcherTimes := make([]time.Duration, len(wisski.dependencies.Fetchers)) fetcherTimes := make([]time.Duration, len(nfo.dependencies.Fetchers))
recordTime := func(i int) func() { recordTime := func(i int) func() {
start := time.Now() start := time.Now()
return func() { return func() {
@ -64,7 +64,7 @@ func (wisski *Info) Information(ctx context.Context, quick bool) (info status.Wi
start := time.Now() start := time.Now()
{ {
var group errgroup.Group var group errgroup.Group
for i, fetcher := range wisski.dependencies.Fetchers { for i, fetcher := range nfo.dependencies.Fetchers {
fetcher, flags, i := fetcher, flags, i fetcher, flags, i := fetcher, flags, i
group.Go(func() error { group.Go(func() error {
// quick: don't need to create servers // quick: don't need to create servers
@ -101,7 +101,7 @@ func (wisski *Info) Information(ctx context.Context, quick bool) (info status.Wi
// get a map of how long each fetcher took // get a map of how long each fetcher took
times := zerolog.Dict() times := zerolog.Dict()
for i, fetcher := range wisski.dependencies.Fetchers { for i, fetcher := range nfo.dependencies.Fetchers {
tookSum += fetcherTimes[i] tookSum += fetcherTimes[i]
times = times.Dur(fetcher.Name(), fetcherTimes[i]) times = times.Dur(fetcher.Name(), fetcherTimes[i])
} }
@ -115,9 +115,11 @@ func (wisski *Info) Information(ctx context.Context, quick bool) (info status.Wi
return return
} }
func (wisski *Info) Fetch(flags ingredient.FetcherFlags, info *status.WissKI) error { func (nfo *Info) Fetch(flags ingredient.FetcherFlags, info *status.WissKI) error {
liquid := ingredient.GetLiquid(nfo)
info.Time = time.Now().UTC() info.Time = time.Now().UTC()
info.Slug = wisski.Slug info.Slug = liquid.Slug
info.URL = wisski.URL().String() info.URL = liquid.URL().String()
return nil return nil
} }

View file

@ -18,6 +18,6 @@ func (lbr *SnapshotsFetcher) Fetch(flags ingredient.FetcherFlags, info *status.W
return return
} }
info.Snapshots, _ = lbr.Snapshots(flags.Context) info.Snapshots, _ = ingredient.GetLiquid(lbr).Snapshots(flags.Context)
return return
} }

View file

@ -4,6 +4,7 @@ import (
"reflect" "reflect"
"strings" "strings"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
"github.com/FAU-CDI/wisski-distillery/internal/wisski/liquid" "github.com/FAU-CDI/wisski-distillery/internal/wisski/liquid"
) )
@ -25,8 +26,18 @@ type Ingredient interface {
// Base is embedded into every Ingredient // Base is embedded into every Ingredient
type Base struct { type Base struct {
name string // name is the name of this ingredient name string // name is the name of this ingredient
*liquid.Liquid // the underlying liquid liquid *liquid.Liquid // the underlying liquid
}
// GetLiquid gets the liquid of this Ingredient
func GetLiquid(i Ingredient) *liquid.Liquid {
return i.getBase().liquid
}
// GetStill returns the still of the distillery associated with the provided ingredient.
func GetStill(i Ingredient) component.Still {
return component.GetStill(GetLiquid(i).Malt)
} }
//lint:ignore U1000 used to implement the private methods of [Component] //lint:ignore U1000 used to implement the private methods of [Component]
@ -38,7 +49,7 @@ func (cb *Base) getBase() *Base {
// Init is only intended to be used within a lifetime.Lifetime[Ingredient,*Liquid]. // Init is only intended to be used within a lifetime.Lifetime[Ingredient,*Liquid].
func Init(ingredient Ingredient, liquid *liquid.Liquid) { func Init(ingredient Ingredient, liquid *liquid.Liquid) {
base := ingredient.getBase() // pointer to a struct base := ingredient.getBase() // pointer to a struct
base.Liquid = liquid base.liquid = liquid
base.name = strings.ToLower(reflect.TypeOf(ingredient).Elem().Name()) base.name = strings.ToLower(reflect.TypeOf(ingredient).Elem().Name())
} }

View file

@ -26,26 +26,30 @@ var Locked = exit.Error{
// TryLock attemps to lock this WissKI and returns if it suceeded // TryLock attemps to lock this WissKI and returns if it suceeded
func (lock *Locker) TryLock(ctx context.Context) bool { func (lock *Locker) TryLock(ctx context.Context) bool {
table, err := lock.Malt.SQL.QueryTable(ctx, lock.Malt.LockTable) liquid := ingredient.GetLiquid(lock)
table, err := liquid.SQL.QueryTable(ctx, liquid.LockTable)
if err != nil { if err != nil {
return false return false
} }
result := table.FirstOrCreate(&models.Lock{}, models.Lock{Slug: lock.Slug}) result := table.FirstOrCreate(&models.Lock{}, models.Lock{Slug: liquid.Slug})
return result.Error == nil && result.RowsAffected == 1 return result.Error == nil && result.RowsAffected == 1
} }
// TryUnlock attempts to unlock this WissKI and reports if it succeeded. // TryUnlock attempts to unlock this WissKI and reports if it succeeded.
// An Unlock is also attempted when ctx is cancelled. // An Unlock is also attempted when ctx is cancelled.
func (lock *Locker) TryUnlock(ctx context.Context) bool { func (lock *Locker) TryUnlock(ctx context.Context) bool {
liquid := ingredient.GetLiquid(lock)
ctx, close := contextx.Anyways(ctx, time.Second) ctx, close := contextx.Anyways(ctx, time.Second)
defer close() defer close()
table, err := lock.Malt.SQL.QueryTable(ctx, lock.Malt.LockTable) table, err := liquid.SQL.QueryTable(ctx, liquid.LockTable)
if err != nil { if err != nil {
return false return false
} }
result := table.Where("slug = ?", lock.Slug).Delete(&models.Lock{}) result := table.Where("slug = ?", liquid.Slug).Delete(&models.Lock{})
return result.Error == nil && result.RowsAffected == 1 return result.Error == nil && result.RowsAffected == 1
} }

View file

@ -10,13 +10,15 @@ import (
// Locked checks if this WissKI is currently locked. // Locked checks if this WissKI is currently locked.
// If an error occurs, the instance is considered not locked. // If an error occurs, the instance is considered not locked.
func (lock *Locker) Locked(ctx context.Context) (locked bool) { func (lock *Locker) Locked(ctx context.Context) (locked bool) {
table, err := lock.Malt.SQL.QueryTable(ctx, lock.Malt.LockTable) liquid := ingredient.GetLiquid(lock)
table, err := liquid.SQL.QueryTable(ctx, liquid.LockTable)
if err != nil { if err != nil {
return false return false
} }
// check if this instance is locked // check if this instance is locked
table.Select("count(*) > 0").Where("slug = ?", lock.Slug).Find(&locked) table.Select("count(*) > 0").Where("slug = ?", liquid.Slug).Find(&locked)
return return
} }

View file

@ -35,8 +35,10 @@ var (
// NoPrefix checks if this WissKI instance is excluded from generating prefixes. // NoPrefix checks if this WissKI instance is excluded from generating prefixes.
// TODO: Move this to the database! // TODO: Move this to the database!
func (prefixes *Prefixes) NoPrefix() bool { func (prefixes *Prefixes) NoPrefix() bool {
liquid := ingredient.GetLiquid(prefixes)
// FIXME: Ignoring error here! // FIXME: Ignoring error here!
exists, _ := fsx.IsRegular(filepath.Join(prefixes.FilesystemBase, "prefixes.skip"), false) exists, _ := fsx.IsRegular(filepath.Join(liquid.FilesystemBase, "prefixes.skip"), false)
return exists return exists
} }
@ -63,9 +65,8 @@ func (prefixes *Prefixes) All(ctx context.Context, server *phpx.Server) ([]strin
// getLivePrefixes get the list of prefixes found within the live system // getLivePrefixes get the list of prefixes found within the live system
func (prefixes *Prefixes) getLivePrefixes(ctx context.Context, server *phpx.Server) (pfs []string, err error) { func (prefixes *Prefixes) getLivePrefixes(ctx context.Context, server *phpx.Server) (pfs []string, err error) {
useTS := !(prefixes.Config.TS.DangerouslyUseAdapterPrefixes.Set && prefixes.Config.TS.DangerouslyUseAdapterPrefixes.Value) danger := ingredient.GetStill(prefixes).Config.TS.DangerouslyUseAdapterPrefixes
if !(danger.Set && danger.Value) {
if useTS {
pfs, err = prefixes.getTSPrefixes(ctx, server) pfs, err = prefixes.getTSPrefixes(ctx, server)
} else { } else {
// danger danger danger: Use the adapter prefixes // danger danger danger: Use the adapter prefixes
@ -107,9 +108,11 @@ func (wisski *Prefixes) getTSPrefixes(ctx context.Context, server *phpx.Server)
} }
func (prefixes *Prefixes) blocked() ([]string, error) { func (prefixes *Prefixes) blocked() ([]string, error) {
config := ingredient.GetStill(prefixes).Config
// open the resolver block file // open the resolver block file
// TODO: move this to the distillery // TODO: move this to the distillery
file, err := os.Open(prefixes.Malt.Config.Paths.ResolverBlocks) file, err := os.Open(config.Paths.ResolverBlocks)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -145,7 +148,7 @@ func hasAnyPrefix(candidate string, prefixes []string) bool {
} }
func (wisski *Prefixes) filePrefixes() (prefixes []string, err error) { func (wisski *Prefixes) filePrefixes() (prefixes []string, err error) {
path := filepath.Join(wisski.FilesystemBase, "prefixes") path := filepath.Join(ingredient.GetLiquid(wisski).FilesystemBase, "prefixes")
// check that the prefixes path exists // check that the prefixes path exists
{ {

View file

@ -29,7 +29,7 @@ var requirementsPHP string
// Create creates a new block with the given title and html content // Create creates a new block with the given title and html content
func (requirements *Requirements) Get(ctx context.Context, server *phpx.Server) (data []status.Requirement, err error) { func (requirements *Requirements) Get(ctx context.Context, server *phpx.Server) (data []status.Requirement, err error) {
err = requirements.dependencies.PHP.ExecScript(ctx, server, &data, requirementsPHP, "get_requirements", requirements.URL().String()) err = requirements.dependencies.PHP.ExecScript(ctx, server, &data, requirementsPHP, "get_requirements", ingredient.GetLiquid(requirements).URL().String())
if err == nil { if err == nil {
// sort first by weight, then by id! // sort first by weight, then by id!
slices.SortFunc(data, func(a, b status.Requirement) int { slices.SortFunc(data, func(a, b status.Requirement) int {

View file

@ -19,6 +19,7 @@ func (up *UserPolicy) Fetch(flags ingredient.FetcherFlags, target *status.WissKI
} }
// read the grants into the info struct // read the grants into the info struct
target.Grants, err = up.Malt.Policy.Instance(flags.Context, up.Slug) liquid := ingredient.GetLiquid(up)
target.Grants, err = liquid.Policy.Instance(flags.Context, liquid.Slug)
return err return err
} }

View file

@ -71,7 +71,7 @@ func (u *Users) LoginWithOpt(ctx context.Context, server *phpx.Server, username
} }
// and resolve the (possibly relative) reference // and resolve the (possibly relative) reference
dest = u.URL().ResolveReference(dest) dest = ingredient.GetLiquid(u).URL().ResolveReference(dest)
return return
} }

View file

@ -19,20 +19,22 @@ var reserveResources embed.FS
// Stack returns a stack representing the reserve instance // Stack returns a stack representing the reserve instance
func (reserve *Reserve) Stack() component.StackWithResources { func (reserve *Reserve) Stack() component.StackWithResources {
liquid := ingredient.GetLiquid(reserve)
config := ingredient.GetStill(reserve).Config
return component.StackWithResources{ return component.StackWithResources{
Stack: component.Stack{ Stack: component.Stack{
Dir: reserve.FilesystemBase, Dir: liquid.FilesystemBase,
}, },
Resources: reserveResources, Resources: reserveResources,
ContextPath: filepath.Join("reserve"), ContextPath: filepath.Join("reserve"),
EnvContext: map[string]string{ EnvContext: map[string]string{
"DOCKER_NETWORK_NAME": reserve.Malt.Config.Docker.Network(), "DOCKER_NETWORK_NAME": config.Docker.Network(),
"SLUG": reserve.Slug, "SLUG": liquid.Slug,
"HOST_RULE": reserve.HostRule(), "HOST_RULE": liquid.HostRule(),
"HTTPS_ENABLED": reserve.Malt.Config.HTTP.HTTPSEnabledEnv(), "HTTPS_ENABLED": config.HTTP.HTTPSEnabledEnv(),
}, },
} }
} }

View file

@ -46,13 +46,15 @@ func (trb *TRB) RebuildTriplestore(ctx context.Context, out io.Writer, allowEmpt
} }
fmt.Printf("Wrote %q\n", dumpPath) fmt.Printf("Wrote %q\n", dumpPath)
liquid := ingredient.GetLiquid(trb)
logging.LogMessage(out, "Purging triplestore") logging.LogMessage(out, "Purging triplestore")
if err := trb.Malt.TS.Purge(ctx, trb.Instance, trb.Domain()); err != nil { if err := liquid.TS.Purge(ctx, liquid.Instance, liquid.Domain()); err != nil {
return err return err
} }
logging.LogMessage(out, "Provising triplestore") logging.LogMessage(out, "Provising triplestore")
if err := trb.Malt.TS.Provision(ctx, trb.Instance, trb.Domain()); err != nil { if err := liquid.TS.Provision(ctx, liquid.Instance, liquid.Domain()); err != nil {
return err return err
} }
@ -82,7 +84,9 @@ func (trb *TRB) makeBackup(ctx context.Context, allowEmptyRepository bool) (path
zippedFile := gzip.NewWriter(file) zippedFile := gzip.NewWriter(file)
defer zippedFile.Close() defer zippedFile.Close()
count, err := trb.Malt.TS.SnapshotDB(ctx, zippedFile, trb.GraphDBRepository) liquid := ingredient.GetLiquid(trb)
count, err := liquid.TS.SnapshotDB(ctx, zippedFile, liquid.GraphDBRepository)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -107,7 +111,8 @@ func (trb *TRB) restoreBackup(ctx context.Context, path string) (err error) {
} }
defer decompressedReader.Close() defer decompressedReader.Close()
if err := trb.Malt.TS.RestoreDB(ctx, trb.GraphDBRepository, decompressedReader); err != nil { liquid := ingredient.GetLiquid(trb)
if err := liquid.TS.RestoreDB(ctx, liquid.GraphDBRepository, decompressedReader); err != nil {
return err return err
} }
return nil return nil

View file

@ -4,11 +4,12 @@ import (
"net/url" "net/url"
"github.com/FAU-CDI/wisski-distillery/internal/config" "github.com/FAU-CDI/wisski-distillery/internal/config"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
) )
// Domain returns the full domain name of this WissKI // Domain returns the full domain name of this WissKI
func (liquid *Liquid) Domain() string { func (liquid *Liquid) Domain() string {
return liquid.Config.HTTP.HostFromSlug(liquid.Slug) return component.GetStill(liquid).Config.HTTP.HostFromSlug(liquid.Slug)
} }
func (liquid *Liquid) Hostname() string { func (liquid *Liquid) Hostname() string {
@ -29,7 +30,7 @@ func (liquid *Liquid) URL() *url.URL {
} }
// use http or https scheme depending on if the distillery has it enabled // use http or https scheme depending on if the distillery has it enabled
if liquid.Malt.Config.HTTP.HTTPSEnabled() { if component.GetStill(liquid.Malt).Config.HTTP.HTTPSEnabled() {
url.Scheme = "https" url.Scheme = "https"
} else { } else {
url.Scheme = "http" url.Scheme = "http"

View file

@ -9,7 +9,7 @@ import (
// Liquid is the core of a WissKI Instance and used in every ingredient. // Liquid is the core of a WissKI Instance and used in every ingredient.
type Liquid struct { type Liquid struct {
*malt.Malt *malt.Malt
models.Instance models.Instance // TODO: move this into an explicit field
DrupalUsername string DrupalUsername string
DrupalPassword string DrupalPassword string