diff --git a/cmd/drupal_setting.go b/cmd/drupal_setting.go new file mode 100644 index 0000000..53fc0d3 --- /dev/null +++ b/cmd/drupal_setting.go @@ -0,0 +1,78 @@ +package cmd + +import ( + "encoding/json" + + wisski_distillery "github.com/FAU-CDI/wisski-distillery" + "github.com/FAU-CDI/wisski-distillery/internal/core" + "github.com/tkw1536/goprogram/exit" +) + +// DrupalSetting is then 'drupal_setting' command +var DrupalSetting wisski_distillery.Command = setting{} + +type setting struct { + Positionals struct { + Slug string `positional-arg-name:"SLUG" required:"1-1" description:"slug of instance to get or set value for"` + Setting string `positional-arg-name:"SETTING" require:"1-1" description:"name of setting to read or write"` + Value string `positional-arg-name:"VALUE" description:"json serialization of value to write"` + } `positional-args:"true"` +} + +func (setting) Description() wisski_distillery.Description { + return wisski_distillery.Description{ + Requirements: core.Requirements{ + NeedsDistillery: true, + }, + Command: "drupal_setting", + Description: "Get or set a drupal setting", + } +} + +var errSettingGet = exit.Error{ + ExitCode: exit.ExitGeneric, + Message: "Unable to get setting", +} + +var errSettingSet = exit.Error{ + ExitCode: exit.ExitGeneric, + Message: "Unable to set setting", +} + +func (ds setting) Run(context wisski_distillery.Context) error { + instance, err := context.Environment.Instances().WissKI(ds.Positionals.Slug) + if err != nil { + return err + } + + if ds.Positionals.Value == "" { + // get the setting + value, err := instance.GetSettingsPHP(ds.Positionals.Setting) + if err != nil { + return errSettingGet.Wrap(err) + } + + // and print it + if err := json.NewEncoder(context.Stdout).Encode(value); err != nil { + return errSettingGet.Wrap(err) + } + + // finish with a newline + context.Println("") + return nil + } + + // serialize the setting into json + var data any + if err := json.Unmarshal([]byte(ds.Positionals.Value), &data); err != nil { + return errSettingSet.Wrap(err) + } + + // set the serialized value! + if err := instance.SetSettingsPHP(ds.Positionals.Setting, data); err != nil { + return errSettingSet.Wrap(err) + } + + // and we're done + return nil +} diff --git a/cmd/system_update.go b/cmd/system_update.go index a9801b4..722d234 100644 --- a/cmd/system_update.go +++ b/cmd/system_update.go @@ -1,6 +1,9 @@ package cmd import ( + "fmt" + "io" + wisski_distillery "github.com/FAU-CDI/wisski-distillery" "github.com/FAU-CDI/wisski-distillery/internal/component" "github.com/FAU-CDI/wisski-distillery/internal/core" @@ -8,7 +11,9 @@ import ( "github.com/FAU-CDI/wisski-distillery/pkg/fsx" "github.com/FAU-CDI/wisski-distillery/pkg/logging" "github.com/tkw1536/goprogram/exit" + "github.com/tkw1536/goprogram/lib/status" "github.com/tkw1536/goprogram/parser" + "github.com/tkw1536/goprogram/stream" ) // SystemUpdate is the 'system_update' command @@ -106,24 +111,35 @@ func (si systemupdate) Run(context wisski_distillery.Context) error { } if err := logging.LogOperation(func() error { - for _, component := range dis.Installables() { - name := component.Name() - stack := component.Stack(dis.Core.Environment) - ctx := component.Context(ctx) + group := &status.Group[component.Installable]{ + Writer: context.Stdout, + PrefixString: func(item component.Installable, index int) string { + return fmt.Sprintf("[install %q]: ", item.Name()) + }, + PrefixAlign: true, + ErrString: func(item component.Installable, index int, err error) string { + if err == nil { + return "ok" + } + return "failed (" + err.Error() + ")" + }, + Handler: func(item component.Installable, index int, writer io.Writer) error { + io := stream.NewIOStream(writer, writer, stream.Null, 0) + stack := item.Stack(context.Environment.Environment) - if err := logging.LogOperation(func() error { - return stack.Install(context.IOStream, ctx) - }, context.IOStream, "Installing Docker Stack %q", name); err != nil { - return err - } + if err := stack.Install(io, item.Context(ctx)); err != nil { + return err + } - if err := logging.LogOperation(func() error { - return stack.Update(context.IOStream, true) - }, context.IOStream, "Updating Docker Stack: %q", name); err != nil { - return err - } + if err := stack.Update(io, true); err != nil { + return err + } + + return nil + }, } - return nil + + return group.Run(dis.Installables()) }, context.IOStream, "Performing Stack Updates"); err != nil { return err } diff --git a/cmd/wdcli/main.go b/cmd/wdcli/main.go index a612f3d..626e38b 100644 --- a/cmd/wdcli/main.go +++ b/cmd/wdcli/main.go @@ -42,8 +42,10 @@ func init() { wdcli.Register(cmd.Shell) wdcli.Register(cmd.BlindUpdate) wdcli.Register(cmd.UpdatePrefixConfig) // TODO: Move into post-instance configuration + wdcli.Register(cmd.Pathbuilders) wdcli.Register(cmd.Prefixes) + wdcli.Register(cmd.DrupalSetting) // backup & cron wdcli.Register(cmd.Snapshot) diff --git a/go.mod b/go.mod index c54af81..25da022 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/feiin/sqlstring v0.3.0 github.com/go-sql-driver/mysql v1.6.0 github.com/pkg/errors v0.9.1 - github.com/tkw1536/goprogram v0.0.12 + github.com/tkw1536/goprogram v0.0.13 golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 golang.org/x/sync v0.0.0-20220907140024-f12130a52804 gorm.io/driver/mysql v1.3.6 diff --git a/go.sum b/go.sum index f6b00f3..deef222 100644 --- a/go.sum +++ b/go.sum @@ -19,16 +19,26 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/tkw1536/goprogram v0.0.12 h1:CJI79IieP750q9j27OmoB1J/PC4povjk8kdckPVJ1YQ= github.com/tkw1536/goprogram v0.0.12/go.mod h1:rX9MKOpJ9qAu4jHV2+n64SKmm3c2D3Hh1V8zC1H3jB4= +github.com/tkw1536/goprogram v0.0.13 h1:tq36MGZ24T+Xjv8y+bWbMGv2zx5prJ7tmi/bvs3wemA= +github.com/tkw1536/goprogram v0.0.13/go.mod h1:rX9MKOpJ9qAu4jHV2+n64SKmm3c2D3Hh1V8zC1H3jB4= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/exp v0.0.0-20220921164117-439092de6870 h1:j8b6j9gzSigH28O5SjSpQSSh9lFd6f5D/q0aHjNTulc= +golang.org/x/exp v0.0.0-20220921164117-439092de6870/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/sync v0.0.0-20220907140024-f12130a52804 h1:0SH2R3f1b1VmIMG7BXbEZCBUu2dKmHschSmjqGUrW8A= golang.org/x/sync v0.0.0-20220907140024-f12130a52804/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 h1:wM1k/lXfpc5HdkJJyW9GELpd8ERGdnh8sMGL6Gzq3Ho= golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220919170432-7a66f970e087 h1:tPwmk4vmvVCMdr98VgL4JH+qZxPL8fqlUOHnyOM8N3w= +golang.org/x/term v0.0.0-20220919170432-7a66f970e087/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= gorm.io/driver/mysql v1.3.6 h1:BhX1Y/RyALb+T9bZ3t07wLnPZBukt+IRkMn8UZSNbGM= gorm.io/driver/mysql v1.3.6/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c= gorm.io/gorm v1.23.8 h1:h8sGJ+biDgBA1AD1Ha9gFCx7h8npU7AsLdlkX0n2TpE= gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +gorm.io/gorm v1.23.9 h1:NSHG021i+MCznokeXR3udGaNyFyBQJW8MbjrJMVCfGw= +gorm.io/gorm v1.23.9/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= diff --git a/internal/component/control/control.go b/internal/component/control/control.go index a1811fe..8a03695 100644 --- a/internal/component/control/control.go +++ b/internal/component/control/control.go @@ -31,7 +31,7 @@ func (control Control) Path() string { var resources embed.FS func (control *Control) Stack(env environment.Environment) component.StackWithResources { - return component.MakeStack(control, env, component.StackWithResources{ + stt := component.MakeStack(control, env, component.StackWithResources{ Resources: resources, ContextPath: "control", EnvPath: "control.env", @@ -52,6 +52,7 @@ func (control *Control) Stack(env environment.Environment) component.StackWithRe TouchFiles: []string{control.ResolverFile}, CopyContextFiles: []string{core.Executable}, }) + return stt } func (control Control) Context(parent component.InstallationContext) component.InstallationContext { diff --git a/internal/component/instances/php/settings.php b/internal/component/instances/php/settings.php new file mode 100644 index 0000000..3ed992c --- /dev/null +++ b/internal/component/instances/php/settings.php @@ -0,0 +1,29 @@ + $value, + "required" => TRUE, + ]; + + // find the filename + $filename = DRUPAL_ROOT . "/" . \Drupal::service("site.path") . "/settings.php"; + drupal_rewrite_settings($settings, $filename); + + return True; +} \ No newline at end of file diff --git a/internal/component/instances/wisski_pathbuilders.go b/internal/component/instances/wisski_pathbuilders.go index 4cbbcd1..67502e9 100644 --- a/internal/component/instances/wisski_pathbuilders.go +++ b/internal/component/instances/wisski_pathbuilders.go @@ -17,7 +17,7 @@ var exportPathbuilderPHP string // Pathbuilders returns the ids of all pathbuilders in consistent order. func (wisski *WissKI) Pathbuilders() (ids []string, err error) { - err = wisski.ExecPHPScript(stream.FromNil(), &ids, exportPathbuilderPHP, "all_list") + err = wisski.ExecPHPScript(stream.FromDebug(), &ids, exportPathbuilderPHP, "all_list") slices.Sort(ids) return } @@ -25,13 +25,13 @@ func (wisski *WissKI) Pathbuilders() (ids []string, err error) { // Pathbuilder returns a single pathbuilder as xml. // If it does not exist, it returns the empty string and nil error. func (wisski *WissKI) Pathbuilder(id string) (xml string, err error) { - err = wisski.ExecPHPScript(stream.FromNil(), &xml, exportPathbuilderPHP, "one_xml", id) + err = wisski.ExecPHPScript(stream.FromDebug(), &xml, exportPathbuilderPHP, "one_xml", id) return } // AllPathbuilders returns all pathbuilders serialized as xml func (wisski *WissKI) AllPathbuilders() (pathbuilders map[string]string, err error) { - err = wisski.ExecPHPScript(stream.FromNil(), &pathbuilders, exportPathbuilderPHP, "all_xml") + err = wisski.ExecPHPScript(stream.FromDebug(), &pathbuilders, exportPathbuilderPHP, "all_xml") return } diff --git a/internal/component/instances/wisski_php.go b/internal/component/instances/wisski_php.go index 4ebf071..365187f 100644 --- a/internal/component/instances/wisski_php.go +++ b/internal/component/instances/wisski_php.go @@ -7,6 +7,8 @@ import ( "strings" "github.com/tkw1536/goprogram/stream" + + _ "embed" ) var ErrExecInvalidCode = errors.New("invalid code to execute") @@ -123,3 +125,17 @@ func marshalPHP(data any) (string, error) { result := "call_user_func(function(){$x=<<<'" + delim + "'\n" + jstring + "\n" + delim + ";return json_decode(trim($x));})" // press to doubt return result, nil } + +// + +//go:embed php/settings.php +var settingsPHP string + +func (wisski *WissKI) GetSettingsPHP(key string) (value any, err error) { + err = wisski.ExecPHPScript(stream.FromDebug(), &value, settingsPHP, "get_setting", key) + return +} + +func (wisski *WissKI) SetSettingsPHP(key string, value any) error { + return wisski.ExecPHPScript(stream.FromDebug(), nil, settingsPHP, "set_setting", key, value) +} diff --git a/internal/component/instances/wisski_prefix.go b/internal/component/instances/wisski_prefix.go index b1195df..af68220 100644 --- a/internal/component/instances/wisski_prefix.go +++ b/internal/component/instances/wisski_prefix.go @@ -25,7 +25,7 @@ var listURIPrefixesPHP string // Prefixes returns the prefixes func (wisski *WissKI) Prefixes() (prefixes []string, err error) { // get all the ugly prefixes - err = wisski.ExecPHPScript(stream.FromNil(), &prefixes, listURIPrefixesPHP, "list_prefixes") + err = wisski.ExecPHPScript(stream.FromDebug(), &prefixes, listURIPrefixesPHP, "list_prefixes") if err != nil { return nil, err } diff --git a/internal/component/sql/connect.go b/internal/component/sql/connect.go index 9c83136..196064d 100644 --- a/internal/component/sql/connect.go +++ b/internal/component/sql/connect.go @@ -8,6 +8,7 @@ import ( "sync/atomic" mysqldriver "github.com/go-sql-driver/mysql" + "github.com/tkw1536/goprogram/stream" "gorm.io/driver/mysql" "gorm.io/gorm" @@ -43,7 +44,6 @@ func (sql *SQL) Exec(query string, args ...interface{}) error { func (sql *SQL) WaitExec() error { return wait.Wait(func() bool { err := sql.Exec("select 1;") - // log.Printf("[WaitQuery] %s\n", err) // debug return err == nil }, sql.PollInterval, sql.PollContext) } @@ -90,8 +90,10 @@ func (sql *SQL) QueryTable(silent bool, table string) (*gorm.DB, error) { // WaitQueryTable waits for a connection to succeed via QueryTable func (sql *SQL) WaitQueryTable() error { + n := stream.FromDebug() return wait.Wait(func() bool { _, err := sql.QueryTable(true, models.InstanceTable) + n.EPrintf("[SQL.WaitQueryTable]: %s\n", err) return err == nil }, sql.PollInterval, sql.PollContext) } diff --git a/internal/component/sql/update.go b/internal/component/sql/update.go index 0cdbc7c..5ab267e 100644 --- a/internal/component/sql/update.go +++ b/internal/component/sql/update.go @@ -21,10 +21,10 @@ func (sql *SQL) Shell(io stream.IOStream, argv ...string) (int, error) { // unsafeWaitShell waits for a connection via the database shell to succeed func (sql *SQL) unsafeWaitShell() error { - n := stream.FromNil() + n := stream.FromDebug() return wait.Wait(func() bool { code, err := sql.Shell(n, "-e", "select 1;") - // log.Printf("[unsafeWaitShell] %d %s\n", code, err) // debug + n.EPrintf("[SQL.unsafeWaitShell]: %d %s\n", code, err) return err == nil && code == 0 }, sql.PollInterval, sql.PollContext) } diff --git a/internal/component/stack.go b/internal/component/stack.go index 8e0c1d5..931977b 100644 --- a/internal/component/stack.go +++ b/internal/component/stack.go @@ -89,7 +89,7 @@ func (ds Stack) Exec(io stream.IOStream, service, executable string, args ...str return ds.compose(io, compose...) } -// Run executes the provided service with the given executable. +// Run runs a command in a running container with the given executable. // It is equivalent to 'docker compose run [--rm] $service $executable $args...'. // // It returns the exit code of the process. diff --git a/internal/component/triplestore/database.go b/internal/component/triplestore/database.go index 6fae113..1bc52d0 100644 --- a/internal/component/triplestore/database.go +++ b/internal/component/triplestore/database.go @@ -9,6 +9,7 @@ import ( "github.com/FAU-CDI/wisski-distillery/pkg/wait" "github.com/pkg/errors" + "github.com/tkw1536/goprogram/stream" ) type TriplestoreUserPayload struct { @@ -85,8 +86,10 @@ func (ts Triplestore) OpenRaw(method, url string, body interface{}, bodyName str // Wait waits for the connection to the Triplestore to succeed. // This is achieved using a polling strategy. func (ts Triplestore) Wait() error { + n := stream.FromDebug() return wait.Wait(func() bool { res, err := ts.OpenRaw("GET", "/rest/repositories", nil, "", "") + n.EPrintf("[Triplestore.Wait]: %s\n", err) if err != nil { return false } diff --git a/internal/component/web/web.go b/internal/component/web/web.go index 54b1680..7b90a9e 100644 --- a/internal/component/web/web.go +++ b/internal/component/web/web.go @@ -2,7 +2,6 @@ package web import ( "embed" - "fmt" "path/filepath" "github.com/FAU-CDI/wisski-distillery/internal/component" @@ -22,7 +21,6 @@ func (Web) Name() string { func (web Web) Path() string { res := filepath.Join(web.Core.Config.DeployRoot, "core", web.Name()) - fmt.Println("debug====" + res) return res }