diff --git a/pkg/addon-operator/operator.go b/pkg/addon-operator/operator.go index d0bb98417..7fb86fb2e 100644 --- a/pkg/addon-operator/operator.go +++ b/pkg/addon-operator/operator.go @@ -3,6 +3,7 @@ package addon_operator import ( "context" "fmt" + "github.com/flant/shell-operator/pkg/debug" "path" "runtime/trace" "strings" @@ -30,7 +31,6 @@ import ( "github.com/flant/kube-client/client" sh_app "github.com/flant/shell-operator/pkg/app" runtimeConfig "github.com/flant/shell-operator/pkg/config" - "github.com/flant/shell-operator/pkg/debug" bc "github.com/flant/shell-operator/pkg/hook/binding_context" "github.com/flant/shell-operator/pkg/hook/controller" htypes "github.com/flant/shell-operator/pkg/hook/types" @@ -1291,7 +1291,8 @@ func (op *AddonOperator) HandleModulePurge(t sh_task.Task, labels map[string]str logEntry.Debugf("Module purge start") hm := task.HookMetadataAccessor(t) - err := op.Helm.NewClient(t.GetLogLabels()).DeleteRelease(hm.ModuleName) + namespace, _ := op.ModuleManager.GetModule(hm.ModuleName).GetDefaultOrDefinedNamespace() + err := op.Helm.NewClient(t.GetLogLabels()).DeleteRelease(namespace, hm.ModuleName) if err != nil { // Purge is for unknown modules, just print warning. logEntry.Warnf("Module purge failed, no retry. Error: %s", err) diff --git a/pkg/helm/client/client.go b/pkg/helm/client/client.go index 0bfcd65cc..6a498e21d 100644 --- a/pkg/helm/client/client.go +++ b/pkg/helm/client/client.go @@ -3,11 +3,11 @@ package client import "github.com/flant/addon-operator/pkg/utils" type HelmClient interface { - LastReleaseStatus(releaseName string) (string, string, error) + LastReleaseStatus(releaseNamespace string, releaseName string) (string, string, error) UpgradeRelease(releaseName string, chart string, valuesPaths []string, setValues []string, namespace string) error Render(releaseName string, chart string, valuesPaths []string, setValues []string, namespace string, debug bool) (string, error) - GetReleaseValues(releaseName string) (utils.Values, error) - DeleteRelease(releaseName string) error - ListReleasesNames() ([]string, error) - IsReleaseExists(releaseName string) (bool, error) + GetReleaseValues(releaseNamespace string, releaseName string) (utils.Values, error) + DeleteRelease(releaseNamespace string, releaseName string) error + ListReleasesNames(releaseNamespace string) ([]string, error) + IsReleaseExists(releaseNamespace string, releaseName string) (bool, error) } diff --git a/pkg/helm/helm3/helm3.go b/pkg/helm/helm3/helm3.go index 3a19eeb6f..e90dbdec8 100644 --- a/pkg/helm/helm3/helm3.go +++ b/pkg/helm/helm3/helm3.go @@ -3,6 +3,7 @@ package helm3 import ( "bytes" "fmt" + "github.com/flant/addon-operator/pkg/app" "os/exec" "sort" "strings" @@ -11,7 +12,6 @@ import ( log "github.com/sirupsen/logrus" k8syaml "sigs.k8s.io/yaml" - "github.com/flant/addon-operator/pkg/app" "github.com/flant/addon-operator/pkg/helm/client" "github.com/flant/addon-operator/pkg/utils" "github.com/flant/shell-operator/pkg/executor" @@ -93,10 +93,10 @@ func (h *Helm3Client) initAndVersion() error { // Example helm history output: // REVISION UPDATED STATUS CHART DESCRIPTION // 1 Fri Jul 14 18:25:00 2017 SUPERSEDED symfony-demo-0.1.0 Install complete -func (h *Helm3Client) LastReleaseStatus(releaseName string) (revision string, status string, err error) { +func (h *Helm3Client) LastReleaseStatus(releaseNamespace string, releaseName string) (revision string, status string, err error) { stdout, stderr, err := h.cmd( "history", releaseName, - "--namespace", h.Namespace, + "--namespace", releaseNamespace, "--max", "1", "--output", "yaml", ) @@ -134,18 +134,17 @@ func (h *Helm3Client) LastReleaseStatus(releaseName string) (revision string, st } func (h *Helm3Client) UpgradeRelease(releaseName string, chart string, valuesPaths []string, setValues []string, namespace string) error { + if namespace == "" { + namespace = h.Namespace + } args := []string{ "upgrade", releaseName, chart, "--install", + "--namespace", namespace, "--history-max", fmt.Sprintf("%d", Options.HistoryMax), "--timeout", Options.Timeout.String(), } - if namespace != "" { - args = append(args, "--namespace") - args = append(args, namespace) - } - for _, valuesPath := range valuesPaths { args = append(args, "--values") args = append(args, valuesPath) @@ -166,12 +165,18 @@ func (h *Helm3Client) UpgradeRelease(releaseName string, chart string, valuesPat return nil } -func (h *Helm3Client) GetReleaseValues(releaseName string) (utils.Values, error) { +func (h *Helm3Client) GetReleaseValues(releaseNamespace string, releaseName string) (utils.Values, error) { + namespace := h.Namespace + if releaseNamespace != "" { + namespace = releaseNamespace + } + args := []string{ "get", "values", releaseName, - "--namespace", h.Namespace, + "--namespace", namespace, "--output", "yaml", } + stdout, stderr, err := h.cmd(args...) if err != nil { return nil, fmt.Errorf("cannot get values of helm release %s: %s\n%s %s", releaseName, err, stdout, stderr) @@ -185,13 +190,19 @@ func (h *Helm3Client) GetReleaseValues(releaseName string) (utils.Values, error) return values, nil } -func (h *Helm3Client) DeleteRelease(releaseName string) (err error) { +func (h *Helm3Client) DeleteRelease(releaseNamespace string, releaseName string) (err error) { h.LogEntry.Debugf("helm release '%s': execute helm uninstall", releaseName) + namespace := h.Namespace + if releaseNamespace != "" { + namespace = releaseNamespace + } + args := []string{ "uninstall", releaseName, - "--namespace", h.Namespace, + "--namespace", namespace, } + stdout, stderr, err := h.cmd(args...) if err != nil { return fmt.Errorf("helm uninstall %s invocation error: %v\n%v %v", releaseName, err, stdout, stderr) @@ -200,8 +211,8 @@ func (h *Helm3Client) DeleteRelease(releaseName string) (err error) { return } -func (h *Helm3Client) IsReleaseExists(releaseName string) (bool, error) { - revision, _, err := h.LastReleaseStatus(releaseName) +func (h *Helm3Client) IsReleaseExists(releaseNamespace string, releaseName string) (bool, error) { + revision, _, err := h.LastReleaseStatus(releaseNamespace, releaseName) if err != nil && revision == "0" { return false, nil } else if err != nil { @@ -212,10 +223,15 @@ func (h *Helm3Client) IsReleaseExists(releaseName string) (bool, error) { } // ListReleasesNames returns list of release names. -func (h *Helm3Client) ListReleasesNames() ([]string, error) { +func (h *Helm3Client) ListReleasesNames(releaseNamespace string) ([]string, error) { + namespace := h.Namespace + if releaseNamespace != "" { + namespace = releaseNamespace + } + args := []string{ "list", "--all", - "--namespace", h.Namespace, + "--namespace", namespace, "--output", "yaml", } @@ -231,14 +247,12 @@ func (h *Helm3Client) ListReleasesNames() ([]string, error) { if err := k8syaml.Unmarshal([]byte(stdout), &list); err != nil { return nil, fmt.Errorf("helm list returned invalid json: %v", err) } - releases := make([]string, 0, len(list)) for _, release := range list { // Do not return ignored release or empty string. if release.Name == app.HelmIgnoreRelease || release.Name == "" { continue } - releases = append(releases, release.Name) } @@ -248,17 +262,17 @@ func (h *Helm3Client) ListReleasesNames() ([]string, error) { // Render renders helm templates for chart func (h *Helm3Client) Render(releaseName string, chart string, valuesPaths []string, setValues []string, namespace string, debug bool) (string, error) { - args := []string{"template", releaseName, chart} + helmNamespace := h.Namespace + if namespace != "" { + helmNamespace = namespace + } + + args := []string{"template", releaseName, chart, "--namespace", helmNamespace} if debug { args = append(args, "--debug") } - if namespace != "" { - args = append(args, "--namespace") - args = append(args, namespace) - } - for _, valuesPath := range valuesPaths { args = append(args, "--values") args = append(args, valuesPath) diff --git a/pkg/module_manager/models/modules/basic.go b/pkg/module_manager/models/modules/basic.go index d3f471508..7a817c3f5 100644 --- a/pkg/module_manager/models/modules/basic.go +++ b/pkg/module_manager/models/modules/basic.go @@ -3,6 +3,7 @@ package modules import ( "context" "fmt" + "github.com/flant/addon-operator/pkg/app" "os" "path/filepath" "sort" @@ -862,6 +863,26 @@ func (bm *BasicModule) GetModuleError() error { return bm.state.lastModuleErr } +// Read namespace from .namespace file in module dir +// Return addon-operator namespace by default +func (bm *BasicModule) GetDefaultOrDefinedNamespace() (string, error) { + content, err := os.ReadFile(filepath.Join(bm.Path, ".namespace")) + if err != nil { + if err != os.ErrNotExist { + return app.Namespace, fmt.Errorf("%s: %s", bm.Name, err) + } else { + log.Warnf("%s: .namespace not found, ignoring", bm.Name) + return app.Namespace, nil + } + } + namespace := strings.TrimRight(string(content), " \t\n") + if namespace != "" { + return namespace, nil + } else { + return app.Namespace, nil + } +} + type ModuleRunPhase string const ( diff --git a/pkg/module_manager/models/modules/helm.go b/pkg/module_manager/models/modules/helm.go index 27bb22422..459d59564 100644 --- a/pkg/module_manager/models/modules/helm.go +++ b/pkg/module_manager/models/modules/helm.go @@ -13,7 +13,6 @@ import ( "github.com/kennygrant/sanitize" log "github.com/sirupsen/logrus" - "github.com/flant/addon-operator/pkg/app" "github.com/flant/addon-operator/pkg/helm" "github.com/flant/addon-operator/pkg/helm/client" "github.com/flant/addon-operator/pkg/utils" @@ -25,6 +24,8 @@ import ( type HelmModule struct { // Name of the module name string + // Name of the kubernetes namespace where helm module will be installed + namespace string // Path of the module on the fs path string @@ -66,8 +67,14 @@ func NewHelmModule(bm *BasicModule, tmpDir string, deps *HelmModuleDependencies, utils.ModuleNameToValuesKey(bm.GetName()): moduleValues, } + chartNamespace, err := bm.GetDefaultOrDefinedNamespace() + if err != nil { + log.Warnf("module %q has error on reading namespace, using default: %s", bm.Name, err) + } + hm := &HelmModule{ name: bm.Name, + namespace: chartNamespace, path: bm.Path, values: chartValues, tmpDir: tmpDir, @@ -177,7 +184,7 @@ func (hm *HelmModule) RunHelmInstall(logLabels map[string]string) error { hm.path, []string{valuesPath}, []string{}, - app.Namespace, + hm.namespace, false, ) }() @@ -206,7 +213,7 @@ func (hm *HelmModule) RunHelmInstall(logLabels map[string]string) error { hm.dependencies.MetricsStorage.HistogramObserve("{PREFIX}helm_operation_seconds", d.Seconds(), metricLabels, nil) })() - runUpgradeRelease, err = hm.shouldRunHelmUpgrade(helmClient, helmReleaseName, checksum, manifests, logLabels) + runUpgradeRelease, err = hm.shouldRunHelmUpgrade(helmClient, helmReleaseName, hm.namespace, checksum, manifests, logLabels) }() if err != nil { return err @@ -215,7 +222,7 @@ func (hm *HelmModule) RunHelmInstall(logLabels map[string]string) error { if !runUpgradeRelease { // Start resources monitor if release is not changed if !hm.dependencies.HelmResourceManager.HasMonitor(hm.name) { - hm.dependencies.HelmResourceManager.StartMonitor(hm.name, manifests, app.Namespace) + hm.dependencies.HelmResourceManager.StartMonitor(hm.name, manifests, hm.namespace) } return nil } @@ -238,7 +245,7 @@ func (hm *HelmModule) RunHelmInstall(logLabels map[string]string) error { hm.path, []string{valuesPath}, []string{fmt.Sprintf("_addonOperatorModuleChecksum=%s", checksum)}, - app.Namespace, + hm.namespace, ) }() @@ -247,16 +254,16 @@ func (hm *HelmModule) RunHelmInstall(logLabels map[string]string) error { } // Start monitor resources if release was successful - hm.dependencies.HelmResourceManager.StartMonitor(hm.name, manifests, app.Namespace) + hm.dependencies.HelmResourceManager.StartMonitor(hm.name, manifests, hm.namespace) return nil } // If all these conditions aren't met, helm upgrade can be skipped. -func (hm *HelmModule) shouldRunHelmUpgrade(helmClient client.HelmClient, releaseName string, checksum string, manifests []manifest.Manifest, logLabels map[string]string) (bool, error) { +func (hm *HelmModule) shouldRunHelmUpgrade(helmClient client.HelmClient, releaseName string, releaseNamespace string, checksum string, manifests []manifest.Manifest, logLabels map[string]string) (bool, error) { logEntry := log.WithFields(utils.LabelsToLogFields(logLabels)) - revision, status, err := helmClient.LastReleaseStatus(releaseName) + revision, status, err := helmClient.LastReleaseStatus(releaseNamespace, releaseName) if revision == "0" { logEntry.Debugf("helm release '%s' not exists: should run upgrade", releaseName) @@ -274,7 +281,7 @@ func (hm *HelmModule) shouldRunHelmUpgrade(helmClient client.HelmClient, release } // Get values for a non-failed release. - releaseValues, err := helmClient.GetReleaseValues(releaseName) + releaseValues, err := helmClient.GetReleaseValues(releaseNamespace, releaseName) if err != nil { logEntry.Debugf("helm release '%s' get values error, no upgrade: %v", releaseName, err) return false, err @@ -297,7 +304,7 @@ func (hm *HelmModule) shouldRunHelmUpgrade(helmClient client.HelmClient, release } // Check if there are absent resources - absent, err := hm.dependencies.HelmResourceManager.GetAbsentResources(manifests, app.Namespace) + absent, err := hm.dependencies.HelmResourceManager.GetAbsentResources(manifests, hm.namespace) if err != nil { return false, err } @@ -333,6 +340,10 @@ func (hm *HelmModule) safeName() string { return sanitize.BaseName(hm.name) } +func (hm *HelmModule) GetNamespace() string { + return hm.namespace +} + func (hm *HelmModule) Render(namespace string, debug bool) (string, error) { if namespace == "" { namespace = "default" diff --git a/pkg/module_manager/models/modules/values_storage.go b/pkg/module_manager/models/modules/values_storage.go index 2729b4986..33c70ca76 100644 --- a/pkg/module_manager/models/modules/values_storage.go +++ b/pkg/module_manager/models/modules/values_storage.go @@ -48,6 +48,7 @@ type ValuesStorage struct { // /modules/001-module/values.yaml // are set only on module init phase staticConfigValues utils.Values + // configValues are user defined values from KubeConfigManager (ConfigMap or ModuleConfig) // without merge with static and openapi values configValues utils.Values diff --git a/pkg/module_manager/module_manager.go b/pkg/module_manager/module_manager.go index 07e2e369d..6d221a835 100644 --- a/pkg/module_manager/module_manager.go +++ b/pkg/module_manager/module_manager.go @@ -532,7 +532,7 @@ func (mm *ModuleManager) RefreshStateFromHelmReleases(logLabels map[string]strin if mm.dependencies.Helm == nil { return &ModulesState{}, nil } - releasedModules, err := mm.dependencies.Helm.NewClient(logLabels).ListReleasesNames() + releasedModules, err := mm.dependencies.Helm.NewClient(logLabels).ListReleasesNames("", nil) if err != nil { return nil, err } @@ -728,7 +728,7 @@ func (mm *ModuleManager) DeleteModule(moduleName string, logLabels map[string]st } helmModule, _ := modules.NewHelmModule(ml, mm.TempDir, &hmdeps, mm.ValuesValidator) if helmModule != nil { - releaseExists, err := mm.dependencies.Helm.NewClient(deleteLogLabels).IsReleaseExists(ml.GetName()) + releaseExists, err := mm.dependencies.Helm.NewClient(deleteLogLabels).IsReleaseExists(helmModule.GetNamespace(), ml.GetName()) if !releaseExists { if err != nil { logEntry.Warnf("Cannot find helm release '%s' for module '%s'. Helm error: %s", ml.GetName(), ml.GetName(), err) @@ -737,7 +737,7 @@ func (mm *ModuleManager) DeleteModule(moduleName string, logLabels map[string]st } } else { // Chart and release are existed, so run helm delete command - err := mm.dependencies.Helm.NewClient(deleteLogLabels).DeleteRelease(ml.GetName()) + err := mm.dependencies.Helm.NewClient(deleteLogLabels).DeleteRelease(helmModule.GetNamespace(), ml.GetName()) if err != nil { return err }