diff --git a/api/v1beta1/spec.go b/api/v1beta1/spec.go index faebd88c..42d860d6 100644 --- a/api/v1beta1/spec.go +++ b/api/v1beta1/spec.go @@ -223,6 +223,16 @@ type HelmOptions struct { // +optional PassCredentialsAll bool `json:"passCredentialsAll,omitempty"` + // RunTests if set to true, Sveltos will run helm test after each successful install or upgrade + // operation. The tests are the test hooks defined in the chart (annotated with + // "helm.sh/hook: test"). If any test fails the deployment is considered failed and the + // error is surfaced in the ClusterSummary status, providing operational gating. + // Has no effect in DryRun mode. + // Default to false + // +kubebuilder:default:=false + // +optional + RunTests bool `json:"runTests,omitempty"` + // HelmInstallOptions are options specific to helm install // +optional InstallOptions HelmInstallOptions `json:"installOptions,omitempty"` diff --git a/config/crd/bases/config.projectsveltos.io_clusterprofiles.yaml b/config/crd/bases/config.projectsveltos.io_clusterprofiles.yaml index 6dfa1a44..40b05275 100644 --- a/config/crd/bases/config.projectsveltos.io_clusterprofiles.yaml +++ b/config/crd/bases/config.projectsveltos.io_clusterprofiles.yaml @@ -339,6 +339,16 @@ spec: description: PassCredentialsAll is the flag to pass credentials to all domains type: boolean + runTests: + default: false + description: |- + RunTests if set to true, Sveltos will run helm test after each successful install or upgrade + operation. The tests are the test hooks defined in the chart (annotated with + "helm.sh/hook: test"). If any test fails the deployment is considered failed and the + error is surfaced in the ClusterSummary status, providing operational gating. + Has no effect in DryRun mode. + Default to false + type: boolean skipCRDs: default: false description: |- diff --git a/config/crd/bases/config.projectsveltos.io_clusterpromotions.yaml b/config/crd/bases/config.projectsveltos.io_clusterpromotions.yaml index 825f6fa5..bf61cfda 100644 --- a/config/crd/bases/config.projectsveltos.io_clusterpromotions.yaml +++ b/config/crd/bases/config.projectsveltos.io_clusterpromotions.yaml @@ -239,6 +239,16 @@ spec: description: PassCredentialsAll is the flag to pass credentials to all domains type: boolean + runTests: + default: false + description: |- + RunTests if set to true, Sveltos will run helm test after each successful install or upgrade + operation. The tests are the test hooks defined in the chart (annotated with + "helm.sh/hook: test"). If any test fails the deployment is considered failed and the + error is surfaced in the ClusterSummary status, providing operational gating. + Has no effect in DryRun mode. + Default to false + type: boolean skipCRDs: default: false description: |- diff --git a/config/crd/bases/config.projectsveltos.io_clustersummaries.yaml b/config/crd/bases/config.projectsveltos.io_clustersummaries.yaml index 8f3cd29f..450c3c40 100644 --- a/config/crd/bases/config.projectsveltos.io_clustersummaries.yaml +++ b/config/crd/bases/config.projectsveltos.io_clustersummaries.yaml @@ -376,6 +376,16 @@ spec: description: PassCredentialsAll is the flag to pass credentials to all domains type: boolean + runTests: + default: false + description: |- + RunTests if set to true, Sveltos will run helm test after each successful install or upgrade + operation. The tests are the test hooks defined in the chart (annotated with + "helm.sh/hook: test"). If any test fails the deployment is considered failed and the + error is surfaced in the ClusterSummary status, providing operational gating. + Has no effect in DryRun mode. + Default to false + type: boolean skipCRDs: default: false description: |- diff --git a/config/crd/bases/config.projectsveltos.io_profiles.yaml b/config/crd/bases/config.projectsveltos.io_profiles.yaml index ba30e726..2de68f48 100644 --- a/config/crd/bases/config.projectsveltos.io_profiles.yaml +++ b/config/crd/bases/config.projectsveltos.io_profiles.yaml @@ -339,6 +339,16 @@ spec: description: PassCredentialsAll is the flag to pass credentials to all domains type: boolean + runTests: + default: false + description: |- + RunTests if set to true, Sveltos will run helm test after each successful install or upgrade + operation. The tests are the test hooks defined in the chart (annotated with + "helm.sh/hook: test"). If any test fails the deployment is considered failed and the + error is surfaced in the ClusterSummary status, providing operational gating. + Has no effect in DryRun mode. + Default to false + type: boolean skipCRDs: default: false description: |- diff --git a/controllers/handlers_helm.go b/controllers/handlers_helm.go index 51efe17a..42879eea 100644 --- a/controllers/handlers_helm.go +++ b/controllers/handlers_helm.go @@ -1466,6 +1466,7 @@ func deployHelmChart(ctx context.Context, clusterSummary *configv1beta1.ClusterS return nil, nil, err } var report *configv1beta1.ReleaseReport + deployed := false if shouldInstall(currentRelease, instantiatedChart) { _, report, err = handleInstall(ctx, clusterSummary, mgmtResources, instantiatedChart, kubeconfig, @@ -1473,12 +1474,14 @@ func deployHelmChart(ctx context.Context, clusterSummary *configv1beta1.ClusterS if err != nil { return nil, nil, err } + deployed = true } else if shouldUpgrade(ctx, currentRelease, instantiatedChart, clusterSummary, mgmtResources, logger) { _, report, err = handleUpgrade(ctx, clusterSummary, mgmtResources, instantiatedChart, currentRelease, kubeconfig, registryOptions, logger) if err != nil { return nil, nil, err } + deployed = true } else if shouldUninstall(currentRelease, instantiatedChart) { report, err = handleUninstall(ctx, clusterSummary, instantiatedChart, kubeconfig, registryOptions, logger) if err != nil { @@ -1502,6 +1505,15 @@ func deployHelmChart(ctx context.Context, clusterSummary *configv1beta1.ClusterS } } + if deployed && getRunTestsValue(instantiatedChart.Options) && + clusterSummary.Spec.ClusterProfileSpec.SyncMode != configv1beta1.SyncModeDryRun { + + err = runHelmTests(instantiatedChart, kubeconfig, registryOptions, logger) + if err != nil { + return nil, nil, err + } + } + if currentRelease != nil { err = addExtraMetadata(ctx, instantiatedChart, clusterSummary, kubeconfig, registryOptions, logger) if err != nil { @@ -2944,7 +2956,7 @@ func collectResourcesFromManagedHelmChartsForDriftDetection(ctx context.Context, return nil, fmt.Errorf("unexpected release type %T", rawResults) } - resources, err := collectHelmContent(results, install, false, logger) + resources, err := collectHelmContent(results, install, false, false, logger) if err != nil { return nil, err } @@ -3140,7 +3152,7 @@ func hasHookDeleteAnnotation(obj *unstructured.Unstructured) bool { } func collectHelmContent(result *releasev1.Release, helmActionVar helmAction, includeHookResources bool, - logger logr.Logger) ([]*unstructured.Unstructured, error) { + includeTestResources bool, logger logr.Logger) ([]*unstructured.Unstructured, error) { resources := make([]*unstructured.Unstructured, 0) if helmActionVar == uninstall { @@ -3201,6 +3213,14 @@ func collectHelmContent(result *releasev1.Release, helmActionVar helmAction, inc resources = append(resources, postHookResources...) } + if includeTestResources && helmActionVar != uninstall { + testHookResources, err := collectTestHooks(result, logger) + if err != nil { + return nil, err + } + resources = append(resources, testHookResources...) + } + return resources, nil } @@ -3278,6 +3298,32 @@ func collectPostHooks(result *releasev1.Release, helmActionVar helmAction, logge return resources, nil } +func collectTestHooks(result *releasev1.Release, logger logr.Logger) ([]*unstructured.Unstructured, error) { + resources := make([]*unstructured.Unstructured, 0) + for _, hook := range result.Hooks { + isTest := false + for _, event := range hook.Events { + if event == releasev1.HookTest { + isTest = true + break + } + } + if !isTest { + continue + } + + logger.V(logs.LogDebug).Info(fmt.Sprintf("Test Hook Kind: %s", hook.Kind)) + + hookResources, err := parseAndAppendResources(hook.Manifest, logger) + if err != nil { + logger.Error(err, "failed to parse test hook manifest") + return nil, err + } + resources = append(resources, hookResources...) + } + return resources, nil +} + func collectHelmDeleteHooks(result *releasev1.Release, logger logr.Logger) ([]*unstructured.Unstructured, error) { resources := make([]*unstructured.Unstructured, 0) @@ -3625,6 +3671,76 @@ func getSubNotesValue(options *configv1beta1.HelmOptions) bool { return false } +func getRunTestsValue(options *configv1beta1.HelmOptions) bool { + if options != nil { + return options.RunTests + } + + return false +} + +// runHelmTests executes helm test hooks for the given release using action.ReleaseTesting. +// It is called after a successful install or upgrade when RunTests is enabled in HelmOptions. +// Any test failure is returned as an error, blocking the deployment lifecycle. +func runHelmTests(requestedChart *configv1beta1.HelmChart, kubeconfig string, + registryOptions *registryClientOptions, logger logr.Logger) error { + + logger.V(logs.LogDebug).Info("running helm tests", + "release", requestedChart.ReleaseName, + "namespace", requestedChart.ReleaseNamespace) + + actionConfig, err := actionConfigInit(requestedChart.ReleaseNamespace, kubeconfig, registryOptions, + getEnableClientCacheValue(requestedChart.Options)) + if err != nil { + return err + } + + // Log which test hooks will be executed so operators can observe what is about to run. + // Note: test pods with hook-delete-policy "hook-succeeded" are removed immediately on + // success, so they may never be visible on the cluster even when tests pass. + statusObject := action.NewStatus(actionConfig) + rawRelease, statusErr := statusObject.Run(requestedChart.ReleaseName) + if statusErr == nil { + if rel, ok := rawRelease.(*releasev1.Release); ok { + var testHookNames []string + for _, hook := range rel.Hooks { + for _, event := range hook.Events { + if event == releasev1.HookTest { + testHookNames = append(testHookNames, hook.Name) + break + } + } + } + logger.V(logs.LogDebug).Info(fmt.Sprintf("found %d test hook(s) to execute: %v", + len(testHookNames), testHookNames)) + } + } + + testClient := action.NewReleaseTesting(actionConfig) + testClient.Namespace = requestedChart.ReleaseNamespace + testClient.Timeout = getTimeoutValue(requestedChart.Options).Duration + + _, shutdown, err := testClient.Run(requestedChart.ReleaseName) + if shutdown != nil { + defer func() { + if shutdownErr := shutdown(); shutdownErr != nil { + logger.V(logs.LogDebug).Info(fmt.Sprintf("helm test shutdown error for release %s/%s: %v", + requestedChart.ReleaseNamespace, requestedChart.ReleaseName, shutdownErr)) + } + }() + } + if err != nil { + logger.V(logs.LogInfo).Info(fmt.Sprintf("helm tests failed for release %s/%s: %v", + requestedChart.ReleaseNamespace, requestedChart.ReleaseName, err)) + return fmt.Errorf("helm test failed for release %s/%s: %w", + requestedChart.ReleaseNamespace, requestedChart.ReleaseName, err) + } + + logger.V(logs.LogInfo).Info(fmt.Sprintf("helm tests passed for release %s/%s", + requestedChart.ReleaseNamespace, requestedChart.ReleaseName)) + return nil +} + func getHelmInstallClient(ctx context.Context, requestedChart *configv1beta1.HelmChart, kubeconfig string, registryOptions *registryClientOptions, patches []libsveltosv1beta1.Patch, templateOnly bool, logger logr.Logger, ) (*action.Install, error) { @@ -3785,7 +3901,7 @@ func addExtraMetadata(ctx context.Context, requestedChart *configv1beta1.HelmCha return fmt.Errorf("unexpected release type %T", rawResults) } - resources, err := collectHelmContent(results, install, true, logger) + resources, err := collectHelmContent(results, install, true, false, logger) if err != nil { return err } @@ -4517,7 +4633,7 @@ func prepareChartForAgent(ctx context.Context, clusterSummary *configv1beta1.Clu if helmActionVar == uninstall && clusterSummary.Spec.ClusterProfileSpec.SyncMode == configv1beta1.SyncModeDryRun { // if action is install=false and syncMode is dryRun, return empty resources. } else { - resources, err = collectHelmContent(helmRelease, helmActionVar, true, logger) + resources, err = collectHelmContent(helmRelease, helmActionVar, true, getRunTestsValue(instantiatedChart.Options), logger) if err != nil { return nil, nil, err } @@ -4608,6 +4724,7 @@ func splitResources(resources []*unstructured.Unstructured, releaseNamespace str postRollbackHooks []*unstructured.Unstructured preDeleteHooks []*unstructured.Unstructured postDeleteHooks []*unstructured.Unstructured + testHooks []*unstructured.Unstructured hookDeleteAnnotations []*unstructured.Unstructured otherResources []*unstructured.Unstructured ) @@ -4642,6 +4759,9 @@ func splitResources(resources []*unstructured.Unstructured, releaseNamespace str } else if isHookResource(resource, "post-delete") { resource = setNamespace(resource, releaseNamespace) postDeleteHooks = append(postDeleteHooks, resource) + } else if isHookResource(resource, "test") { + resource = setNamespace(resource, releaseNamespace) + testHooks = append(testHooks, resource) } else if hasHookDeleteAnnotation(resource) { resource = setNamespace(resource, releaseNamespace) hookDeleteAnnotations = append(hookDeleteAnnotations, resource) @@ -4682,6 +4802,9 @@ func splitResources(resources []*unstructured.Unstructured, releaseNamespace str // Put post delete instances in subgroups of no more than 15 result = appendResources(result, postDeleteHooks) + // Put test hook instances in subgroups of no more than 15 (after all other resources) + result = appendResources(result, testHooks) + return result } diff --git a/controllers/handlers_kustomize.go b/controllers/handlers_kustomize.go index 60a88182..13dc33bc 100644 --- a/controllers/handlers_kustomize.go +++ b/controllers/handlers_kustomize.go @@ -1008,7 +1008,8 @@ func deployKustomizeResources(ctx context.Context, c client.Client, remoteRestCo key := fmt.Sprintf("%s-%s-%s", ref.Kind, ref.Namespace, ref.Name) bundleResources[key] = convertPointerSliceToValueSlice(objectsToDeployRemotely) - setters := prepareBundleSettersWithResourceInfo(ref.Kind, ref.Namespace, ref.Name, kustomizationRef.Tier) + setters := prepareBundleSettersWithResourceInfo(ref.Kind, ref.Namespace, ref.Name, kustomizationRef.Tier, + kustomizationRef.SkipNamespaceCreation) return localReports, nil, pullmode.StageResourcesForDeployment(ctx, getManagementClusterClient(), clusterSummary.Spec.ClusterNamespace, clusterSummary.Spec.ClusterName, configv1beta1.ClusterSummaryKind, diff --git a/controllers/handlers_utils.go b/controllers/handlers_utils.go index 5d1d449d..9981969f 100644 --- a/controllers/handlers_utils.go +++ b/controllers/handlers_utils.go @@ -315,7 +315,7 @@ func deployContent(ctx context.Context, deployingToMgmtCluster bool, destConfig // In pull mode we return reports with action Create. Those will only be used to update deployed GVK. // sveltos-applier will take care of sending proper reports - setters := prepareBundleSettersWithResourceInfo(ref.Kind, ref.Namespace, ref.Name, referenceTier) + setters := prepareBundleSettersWithResourceInfo(ref.Kind, ref.Namespace, ref.Name, referenceTier, skipNamespaceCreation) return prepareReports(resources), pullmode.StageResourcesForDeployment(ctx, getManagementClusterClient(), @@ -1651,12 +1651,12 @@ func getFileWithKubeconfig(ctx context.Context, c client.Client, clusterSummary } func prepareBundleSettersWithResourceInfo(referenceKind, referenceNamespace, referenceName string, - tier int32) []pullmode.BundleOption { + tier int32, skipNamespaceCreation bool) []pullmode.BundleOption { setters := make([]pullmode.BundleOption, 0) setters = append(setters, - pullmode.WithResourceInfo(referenceKind, referenceNamespace, referenceName, tier)) + pullmode.WithResourceInfo(referenceKind, referenceNamespace, referenceName, tier, skipNamespaceCreation)) return setters } diff --git a/examples/prometheus-grafana.yaml b/examples/prometheus-grafana.yaml index f5235f3b..e3d92fb9 100644 --- a/examples/prometheus-grafana.yaml +++ b/examples/prometheus-grafana.yaml @@ -15,10 +15,10 @@ spec: releaseName: prometheus releaseNamespace: prometheus helmChartAction: Install - - repositoryURL: https://grafana.github.io/helm-charts + - repositoryURL: https://grafana-community.github.io/helm-charts repositoryName: grafana chartName: grafana/grafana - chartVersion: 10.5.15 + chartVersion: 11.3.6 releaseName: grafana releaseNamespace: grafana helmChartAction: Install diff --git a/go.mod b/go.mod index 15d1fd23..8927cb60 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/onsi/ginkgo/v2 v2.28.1 github.com/onsi/gomega v1.39.1 github.com/pkg/errors v0.9.1 - github.com/projectsveltos/libsveltos v1.7.0 + github.com/projectsveltos/libsveltos v1.7.1-0.20260402195729-75fb826e8eb9 github.com/prometheus/client_golang v1.23.2 github.com/robfig/cron v1.2.0 github.com/spf13/pflag v1.0.10 diff --git a/go.sum b/go.sum index 51b5038a..243b6503 100644 --- a/go.sum +++ b/go.sum @@ -70,8 +70,6 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/cli v29.3.0+incompatible h1:z3iWveU7h19Pqx7alZES8j+IeFQZ1lhTwb2F+V9SVvk= -github.com/docker/cli v29.3.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v29.3.1+incompatible h1:M04FDj2TRehDacrosh7Vlkgc7AuQoWloQkf1PA5hmoI= github.com/docker/cli v29.3.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= @@ -265,8 +263,6 @@ github.com/onsi/ginkgo/v2 v2.28.1 h1:S4hj+HbZp40fNKuLUQOYLDgZLwNUVn19N3Atb98NCyI github.com/onsi/ginkgo/v2 v2.28.1/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE= github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28= github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg= -github.com/opencontainers/go-digest v1.0.1-0.20250813155314-89707e38ad1a h1:K26ONn9WVq80kytPgy+GEKVF2NBKqRHCLRDAA9i/gO0= -github.com/opencontainers/go-digest v1.0.1-0.20250813155314-89707e38ad1a/go.mod h1:RqnyioA3pIEZMkSbOIcrw32YSgETfn/VrLuEikEdPNU= github.com/opencontainers/go-digest v1.0.1-0.20260318163012-137fd2bb009d h1:QZUC8fu3Kn4FhwIPx+9tnSZWtfcoNCXAKPX6yyJCoss= github.com/opencontainers/go-digest v1.0.1-0.20260318163012-137fd2bb009d/go.mod h1:RqnyioA3pIEZMkSbOIcrw32YSgETfn/VrLuEikEdPNU= github.com/opencontainers/go-digest/blake3 v0.0.0-20250116041648-1e56c6daea3b h1:nAiL9bmUK4IzFrKoVMRykv0iYGdoit5vpbPaVCZ+fI4= @@ -282,8 +278,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= -github.com/projectsveltos/libsveltos v1.7.0 h1:9evke2LOtFImZ0Me9hkheFVORWXhkdqw3clpec/J2XY= -github.com/projectsveltos/libsveltos v1.7.0/go.mod h1:j3RWlB9jVFbZasy89M2xkp7wqyy6p46oea++VbP3E3Y= +github.com/projectsveltos/libsveltos v1.7.1-0.20260402195729-75fb826e8eb9 h1:bex7YeSlyOVwr3hOwZiCWOyVoLZ9l7nQR+MoTYQRaF4= +github.com/projectsveltos/libsveltos v1.7.1-0.20260402195729-75fb826e8eb9/go.mod h1:j3RWlB9jVFbZasy89M2xkp7wqyy6p46oea++VbP3E3Y= github.com/projectsveltos/lua-utils/glua-json v0.0.0-20251212200258-2b3cdcb7c0f5 h1:khnc+994UszxZYu69J+R5FKiLA/Nk1JQj0EYAkwTWz0= github.com/projectsveltos/lua-utils/glua-json v0.0.0-20251212200258-2b3cdcb7c0f5/go.mod h1:yVL8KQFa9tmcxgwl9nwIMtKgtmIVC1zaFRSCfOwYvPY= github.com/projectsveltos/lua-utils/glua-runes v0.0.0-20251212200258-2b3cdcb7c0f5 h1:YbsebwRwTRhV8QacvEAdFqxcxHdeu7JTVtsBovbkgos= @@ -365,8 +361,6 @@ github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcY github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= -github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= github.com/yuin/gopher-lua v1.1.2 h1:yF/FjE3hD65tBbt0VXLE13HWS9h34fdzJmrWRXwobGA= github.com/yuin/gopher-lua v1.1.2/go.mod h1:7aRmXIWl37SqRf0koeyylBEzJ+aPt8A+mmkQ4f1ntR8= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= diff --git a/manifest/manifest.yaml b/manifest/manifest.yaml index 19ed2f94..23585947 100644 --- a/manifest/manifest.yaml +++ b/manifest/manifest.yaml @@ -648,6 +648,16 @@ spec: description: PassCredentialsAll is the flag to pass credentials to all domains type: boolean + runTests: + default: false + description: |- + RunTests if set to true, Sveltos will run helm test after each successful install or upgrade + operation. The tests are the test hooks defined in the chart (annotated with + "helm.sh/hook: test"). If any test fails the deployment is considered failed and the + error is surfaced in the ClusterSummary status, providing operational gating. + Has no effect in DryRun mode. + Default to false + type: boolean skipCRDs: default: false description: |- @@ -2311,6 +2321,16 @@ spec: description: PassCredentialsAll is the flag to pass credentials to all domains type: boolean + runTests: + default: false + description: |- + RunTests if set to true, Sveltos will run helm test after each successful install or upgrade + operation. The tests are the test hooks defined in the chart (annotated with + "helm.sh/hook: test"). If any test fails the deployment is considered failed and the + error is surfaced in the ClusterSummary status, providing operational gating. + Has no effect in DryRun mode. + Default to false + type: boolean skipCRDs: default: false description: |- @@ -4756,6 +4776,16 @@ spec: description: PassCredentialsAll is the flag to pass credentials to all domains type: boolean + runTests: + default: false + description: |- + RunTests if set to true, Sveltos will run helm test after each successful install or upgrade + operation. The tests are the test hooks defined in the chart (annotated with + "helm.sh/hook: test"). If any test fails the deployment is considered failed and the + error is surfaced in the ClusterSummary status, providing operational gating. + Has no effect in DryRun mode. + Default to false + type: boolean skipCRDs: default: false description: |- @@ -6452,6 +6482,16 @@ spec: description: PassCredentialsAll is the flag to pass credentials to all domains type: boolean + runTests: + default: false + description: |- + RunTests if set to true, Sveltos will run helm test after each successful install or upgrade + operation. The tests are the test hooks defined in the chart (annotated with + "helm.sh/hook: test"). If any test fails the deployment is considered failed and the + error is surfaced in the ClusterSummary status, providing operational gating. + Has no effect in DryRun mode. + Default to false + type: boolean skipCRDs: default: false description: |- diff --git a/test/fv/capi_onboard_annotation_test.go b/test/fv/capi_onboard_annotation_test.go index c0f1efa0..df64c22a 100644 --- a/test/fv/capi_onboard_annotation_test.go +++ b/test/fv/capi_onboard_annotation_test.go @@ -81,13 +81,16 @@ var _ = Describe("Helm", Serial, func() { HelmChartAction: configv1beta1.HelmChartActionInstall, }, { - RepositoryURL: "https://grafana.github.io/helm-charts", + RepositoryURL: "https://grafana-community.github.io/helm-charts", RepositoryName: "grafana", ChartName: "grafana/grafana", - ChartVersion: "8.3.4", + ChartVersion: "11.3.6", ReleaseName: "grafana", ReleaseNamespace: "grafana", HelmChartAction: configv1beta1.HelmChartActionInstall, + Options: &configv1beta1.HelmOptions{ + RunTests: true, + }, }, } return k8sClient.Update(context.TODO(), currentClusterProfile) @@ -145,7 +148,7 @@ var _ = Describe("Helm", Serial, func() { libsveltosv1beta1.FeatureHelm) charts := []configv1beta1.Chart{ - {ReleaseName: "grafana", ChartVersion: "8.3.4", Namespace: "grafana"}, + {ReleaseName: "grafana", ChartVersion: "11.3.6", Namespace: "grafana"}, {ReleaseName: "prometheus", ChartVersion: "25.24.0", Namespace: "prometheus"}, } diff --git a/test/fv/helm_test.go b/test/fv/helm_test.go index d815c530..6ec1b7e3 100644 --- a/test/fv/helm_test.go +++ b/test/fv/helm_test.go @@ -78,10 +78,13 @@ var _ = Describe("Helm", func() { RepositoryURL: "https://kyverno.github.io/kyverno/", RepositoryName: "kyverno", ChartName: "kyverno/kyverno", - ChartVersion: "v3.5.2", + ChartVersion: "v3.7.1", ReleaseName: "kyverno-latest", ReleaseNamespace: "kyverno", HelmChartAction: configv1beta1.HelmChartActionInstall, + Options: &configv1beta1.HelmOptions{ + RunTests: true, + }, }, { RepositoryURL: "https://docs.wildfly.org/wildfly-charts/", @@ -149,7 +152,7 @@ var _ = Describe("Helm", func() { verifyFeatureStatusIsProvisioned(kindWorkloadCluster.GetNamespace(), clusterSummary.Name, libsveltosv1beta1.FeatureHelm) charts := []configv1beta1.Chart{ - {ReleaseName: "kyverno-latest", ChartVersion: "3.5.2", Namespace: "kyverno"}, + {ReleaseName: "kyverno-latest", ChartVersion: "3.7.1", Namespace: "kyverno"}, {ReleaseName: "wildfly", ChartVersion: "2.4.0", Namespace: "wildfly"}, } @@ -164,7 +167,7 @@ var _ = Describe("Helm", func() { RepositoryURL: "https://kyverno.github.io/kyverno/", RepositoryName: "kyverno", ChartName: "kyverno/kyverno", - ChartVersion: "v3.5.1", + ChartVersion: "v3.7.0", ReleaseName: "kyverno-latest", ReleaseNamespace: "kyverno", HelmChartAction: configv1beta1.HelmChartActionInstall, @@ -219,7 +222,7 @@ var _ = Describe("Helm", func() { verifyFeatureStatusIsProvisioned(kindWorkloadCluster.GetNamespace(), clusterSummary.Name, libsveltosv1beta1.FeatureHelm) charts = []configv1beta1.Chart{ - {ReleaseName: "kyverno-latest", ChartVersion: "3.5.1", Namespace: "kyverno"}, + {ReleaseName: "kyverno-latest", ChartVersion: "3.7.0", Namespace: "kyverno"}, {ReleaseName: "wildfly", ChartVersion: "2.4.0", Namespace: "wildfly"}, } @@ -245,7 +248,7 @@ var _ = Describe("Helm", func() { RepositoryURL: "https://kyverno.github.io/kyverno/", RepositoryName: "kyverno", ChartName: "kyverno/kyverno", - ChartVersion: "v3.5.1", + ChartVersion: "v3.7.0", ReleaseName: "kyverno-latest", ReleaseNamespace: "kyverno", HelmChartAction: configv1beta1.HelmChartActionInstall, @@ -273,7 +276,7 @@ var _ = Describe("Helm", func() { }, timeout, pollingInterval).Should(BeTrue()) charts = []configv1beta1.Chart{ - {ReleaseName: "kyverno-latest", ChartVersion: "3.5.1", Namespace: "kyverno"}, + {ReleaseName: "kyverno-latest", ChartVersion: "3.7.0", Namespace: "kyverno"}, } verifyClusterConfiguration(configv1beta1.ClusterProfileKind, clusterProfile.Name, diff --git a/test/pullmode-sveltosapplier.yaml b/test/pullmode-sveltosapplier.yaml index 45a81b66..d8afc977 100644 --- a/test/pullmode-sveltosapplier.yaml +++ b/test/pullmode-sveltosapplier.yaml @@ -99,7 +99,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: docker.io/projectsveltos/sveltos-applier@sha256:3dbe2b2df2992f3f0c6d61ec74276764621bdf82f7582b902fc4ab429034dc91 + image: docker.io/projectsveltos/sveltos-applier@sha256:0b82d595a587215e4d425eb653725adcb85cd2926b89b2ac16081ea4745471fc livenessProbe: failureThreshold: 3 httpGet: