diff --git a/go.mod b/go.mod index 5a9095d1..78083737 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/deckhouse/deckhouse/pkg/log v0.1.0 github.com/deckhouse/deckhouse/pkg/metrics-storage v0.3.0 - github.com/deckhouse/module-sdk v0.5.0 + github.com/deckhouse/module-sdk v0.6.0 github.com/dominikbraun/graph v0.23.0 github.com/ettle/strcase v0.2.0 github.com/flant/kube-client v1.5.0 @@ -140,7 +140,7 @@ require ( github.com/google/go-containerregistry v0.20.6 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.6.0 // indirect + github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/gosuri/uitable v0.0.4 // indirect diff --git a/go.sum b/go.sum index 751b567a..08a8cc26 100644 --- a/go.sum +++ b/go.sum @@ -102,8 +102,8 @@ github.com/deckhouse/deckhouse/pkg/log v0.1.0 h1:2aPfyiHHSIJlX4x7ysyPOaIb7CLmyY+ github.com/deckhouse/deckhouse/pkg/log v0.1.0/go.mod h1:pbAxTSDcPmwyl3wwKDcEB3qdxHnRxqTV+J0K+sha8bw= github.com/deckhouse/deckhouse/pkg/metrics-storage v0.3.0 h1:xZvbKuexrSQGEw6CB4n3UC7XbOb9QNLbm8UhcGZ2R1I= github.com/deckhouse/deckhouse/pkg/metrics-storage v0.3.0/go.mod h1:Rz++SzCLkFW03WGgftnn91TimGU2shiKb5S/YuxcBuE= -github.com/deckhouse/module-sdk v0.5.0 h1:b2GJUzMKQLr7oJVJy5lXHvyymNyvNiFXpBie7MwEWwE= -github.com/deckhouse/module-sdk v0.5.0/go.mod h1:+EbBnP8z+poIihgL4l1oxHng5ePqDUK44c39u7sEBss= +github.com/deckhouse/module-sdk v0.6.0 h1:CuyiLEkPr/3dRYzvvvzR0pInTMSEeThrURuFesIehHE= +github.com/deckhouse/module-sdk v0.6.0/go.mod h1:+EbBnP8z+poIihgL4l1oxHng5ePqDUK44c39u7sEBss= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= diff --git a/pkg/module_manager/models/hooks/kind/batch_hook.go b/pkg/module_manager/models/hooks/kind/batch_hook.go index 981b3fc8..aff57e04 100644 --- a/pkg/module_manager/models/hooks/kind/batch_hook.go +++ b/pkg/module_manager/models/hooks/kind/batch_hook.go @@ -303,6 +303,10 @@ func remapSDKConfigToConfig(input *sdkhook.BatchHookConfig) (*BatchHookConfig, e cfg.Readiness = input.Readiness cfg.Hooks[BatchHookReadyKey] = input.Readiness } + + if input.HasSettingsCheck { + cfg.HasSettingsCheck = true + } default: return nil, fmt.Errorf("unknown version '%s'", input.Version) } diff --git a/pkg/module_manager/models/hooks/kind/check.go b/pkg/module_manager/models/hooks/kind/check.go new file mode 100644 index 00000000..1b3e270d --- /dev/null +++ b/pkg/module_manager/models/hooks/kind/check.go @@ -0,0 +1,96 @@ +// Copyright 2025 Flant JSC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kind + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "log/slog" + "os" + "path/filepath" + "strings" + + "github.com/deckhouse/deckhouse/pkg/log" + "github.com/deckhouse/module-sdk/pkg/settingscheck" + "github.com/google/uuid" + + "github.com/flant/addon-operator/pkg/utils" + "github.com/flant/shell-operator/pkg/executor" +) + +type SettingsCheck struct { + path string + tmp string + logger *log.Logger +} + +func NewSettingsCheck(path, tmpPath string, logger *log.Logger) *SettingsCheck { + return &SettingsCheck{ + path: path, + tmp: tmpPath, + logger: logger, + } +} + +// Check runs the setting check via the OS interpreter and returns the result of the execution +func (c *SettingsCheck) Check(ctx context.Context, settings utils.Values) (settingscheck.Result, error) { + // tmp files has uuid in name and create only in tmp folder (because of RO filesystem) + tmp, err := c.prepareTmpFile(settings) + if err != nil { + return settingscheck.Result{}, err + } + + // Remove tmp files after execution + defer func() { + if err = os.Remove(tmp); err != nil { + c.logger.Error("remove tmp file", slog.String("file", tmp), log.Err(err)) + } + }() + + envs := os.Environ() + envs = append(envs, fmt.Sprintf("%s=%s", settingscheck.EnvSettingsPath, tmp)) + + result := settingscheck.Result{ + Valid: true, + } + + cmd := executor.NewExecutor("", c.path, []string{"hook", "check"}, envs).WithLogger(c.logger.Named("executor")) + if _, err = cmd.RunAndLogLines(ctx, make(map[string]string)); err != nil { + trimmed := bytes.NewBufferString(strings.TrimPrefix(err.Error(), "stderr:")) + + if err = json.NewDecoder(trimmed).Decode(&result); err != nil { + return settingscheck.Result{}, fmt.Errorf("parse output: %s", err) + } + } + + return result, nil +} + +// prepareTmpFile creates temporary files for hook and returns environment variables with paths +func (c *SettingsCheck) prepareTmpFile(settings utils.Values) (string, error) { + data, err := settings.JsonBytes() + if err != nil { + return "", err + } + + path := filepath.Join(c.tmp, fmt.Sprintf("%s.json", uuid.New().String())) + if err = utils.DumpData(path, data); err != nil { + return "", err + } + + return path, err +} diff --git a/pkg/module_manager/models/hooks/kind/config.go b/pkg/module_manager/models/hooks/kind/config.go index b157838e..846edd2c 100644 --- a/pkg/module_manager/models/hooks/kind/config.go +++ b/pkg/module_manager/models/hooks/kind/config.go @@ -5,6 +5,7 @@ import sdkhook "github.com/deckhouse/module-sdk/pkg/hook" const BatchHookReadyKey = "ready" type BatchHookConfig struct { - Hooks map[string]*sdkhook.HookConfig - Readiness *sdkhook.HookConfig + Hooks map[string]*sdkhook.HookConfig + Readiness *sdkhook.HookConfig + HasSettingsCheck bool }