diff --git a/api/v1beta1/spec.go b/api/v1beta1/spec.go index 17ac56a0..faebd88c 100644 --- a/api/v1beta1/spec.go +++ b/api/v1beta1/spec.go @@ -549,6 +549,16 @@ type KustomizationRef struct { // +kubebuilder:validation:Minimum=1 // +optional Tier int32 `json:"tier,omitempty"` + + // SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + // for namespaced resources defined in this KustomizationRef. + // This field is ignored for cluster-scoped resources. + // By default, Sveltos attempts to get or create the target namespace if it does not exist. + // Setting this to true avoids those calls, which is necessary when Sveltos lacks + // permissions to manage namespaces at the cluster level. + // +kubebuilder:default:=false + // +optional + SkipNamespaceCreation bool `json:"skipNamespaceCreation,omitempty"` } // StopMatchingBehavior indicates what will happen when Cluster stops matching @@ -641,6 +651,16 @@ type PolicyRef struct { // +kubebuilder:validation:Minimum=1 // +optional Tier int32 `json:"tier,omitempty"` + + // SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + // for namespaced resources defined in this PolicyRef. + // This field is ignored for cluster-scoped resources. + // By default, Sveltos attempts to get or create the target namespace if it does not exist. + // Setting this to true avoids those calls, which is necessary when Sveltos lacks + // permissions to manage namespaces at the cluster level. + // +kubebuilder:default:=false + // +optional + SkipNamespaceCreation bool `json:"skipNamespaceCreation,omitempty"` } type Clusters struct { diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 17bbd59d..3504036e 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -21,11 +21,12 @@ limitations under the License. package v1beta1 import ( - apiv1beta1 "github.com/projectsveltos/libsveltos/api/v1beta1" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/intstr" + + apiv1beta1 "github.com/projectsveltos/libsveltos/api/v1beta1" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. diff --git a/config/crd/bases/config.projectsveltos.io_clusterprofiles.yaml b/config/crd/bases/config.projectsveltos.io_clusterprofiles.yaml index ad586f21..6dfa1a44 100644 --- a/config/crd/bases/config.projectsveltos.io_clusterprofiles.yaml +++ b/config/crd/bases/config.projectsveltos.io_clusterprofiles.yaml @@ -707,6 +707,16 @@ spec: When expressed as templates, the values are filled in using information from resources within the management cluster before deployment (Cluster) type: string + skipNamespaceCreation: + default: false + description: |- + SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + for namespaced resources defined in this KustomizationRef. + This field is ignored for cluster-scoped resources. + By default, Sveltos attempts to get or create the target namespace if it does not exist. + Setting this to true avoids those calls, which is necessary when Sveltos lacks + permissions to manage namespaces at the cluster level. + type: boolean targetNamespace: description: |- TargetNamespace sets or overrides the namespace in the @@ -991,6 +1001,16 @@ spec: Defaults to 'None', which translates to the root path of the SourceRef. Used only for GitRepository;OCIRepository;Bucket type: string + skipNamespaceCreation: + default: false + description: |- + SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + for namespaced resources defined in this PolicyRef. + This field is ignored for cluster-scoped resources. + By default, Sveltos attempts to get or create the target namespace if it does not exist. + Setting this to true avoids those calls, which is necessary when Sveltos lacks + permissions to manage namespaces at the cluster level. + type: boolean tier: default: 100 description: |- diff --git a/config/crd/bases/config.projectsveltos.io_clusterpromotions.yaml b/config/crd/bases/config.projectsveltos.io_clusterpromotions.yaml index 11c93394..825f6fa5 100644 --- a/config/crd/bases/config.projectsveltos.io_clusterpromotions.yaml +++ b/config/crd/bases/config.projectsveltos.io_clusterpromotions.yaml @@ -608,6 +608,16 @@ spec: When expressed as templates, the values are filled in using information from resources within the management cluster before deployment (Cluster) type: string + skipNamespaceCreation: + default: false + description: |- + SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + for namespaced resources defined in this KustomizationRef. + This field is ignored for cluster-scoped resources. + By default, Sveltos attempts to get or create the target namespace if it does not exist. + Setting this to true avoids those calls, which is necessary when Sveltos lacks + permissions to manage namespaces at the cluster level. + type: boolean targetNamespace: description: |- TargetNamespace sets or overrides the namespace in the @@ -892,6 +902,16 @@ spec: Defaults to 'None', which translates to the root path of the SourceRef. Used only for GitRepository;OCIRepository;Bucket type: string + skipNamespaceCreation: + default: false + description: |- + SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + for namespaced resources defined in this PolicyRef. + This field is ignored for cluster-scoped resources. + By default, Sveltos attempts to get or create the target namespace if it does not exist. + Setting this to true avoids those calls, which is necessary when Sveltos lacks + permissions to manage namespaces at the cluster level. + type: boolean tier: default: 100 description: |- @@ -1613,6 +1633,16 @@ spec: Defaults to 'None', which translates to the root path of the SourceRef. Used only for GitRepository;OCIRepository;Bucket type: string + skipNamespaceCreation: + default: false + description: |- + SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + for namespaced resources defined in this PolicyRef. + This field is ignored for cluster-scoped resources. + By default, Sveltos attempts to get or create the target namespace if it does not exist. + Setting this to true avoids those calls, which is necessary when Sveltos lacks + permissions to manage namespaces at the cluster level. + type: boolean tier: default: 100 description: |- @@ -1844,6 +1874,16 @@ spec: Defaults to 'None', which translates to the root path of the SourceRef. Used only for GitRepository;OCIRepository;Bucket type: string + skipNamespaceCreation: + default: false + description: |- + SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + for namespaced resources defined in this PolicyRef. + This field is ignored for cluster-scoped resources. + By default, Sveltos attempts to get or create the target namespace if it does not exist. + Setting this to true avoids those calls, which is necessary when Sveltos lacks + permissions to manage namespaces at the cluster level. + type: boolean tier: default: 100 description: |- diff --git a/config/crd/bases/config.projectsveltos.io_clustersummaries.yaml b/config/crd/bases/config.projectsveltos.io_clustersummaries.yaml index 3afc53b9..e1d2db8b 100644 --- a/config/crd/bases/config.projectsveltos.io_clustersummaries.yaml +++ b/config/crd/bases/config.projectsveltos.io_clustersummaries.yaml @@ -745,6 +745,16 @@ spec: When expressed as templates, the values are filled in using information from resources within the management cluster before deployment (Cluster) type: string + skipNamespaceCreation: + default: false + description: |- + SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + for namespaced resources defined in this KustomizationRef. + This field is ignored for cluster-scoped resources. + By default, Sveltos attempts to get or create the target namespace if it does not exist. + Setting this to true avoids those calls, which is necessary when Sveltos lacks + permissions to manage namespaces at the cluster level. + type: boolean targetNamespace: description: |- TargetNamespace sets or overrides the namespace in the @@ -1029,6 +1039,16 @@ spec: Defaults to 'None', which translates to the root path of the SourceRef. Used only for GitRepository;OCIRepository;Bucket type: string + skipNamespaceCreation: + default: false + description: |- + SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + for namespaced resources defined in this PolicyRef. + This field is ignored for cluster-scoped resources. + By default, Sveltos attempts to get or create the target namespace if it does not exist. + Setting this to true avoids those calls, which is necessary when Sveltos lacks + permissions to manage namespaces at the cluster level. + type: boolean tier: default: 100 description: |- diff --git a/config/crd/bases/config.projectsveltos.io_profiles.yaml b/config/crd/bases/config.projectsveltos.io_profiles.yaml index 21a578d7..ba30e726 100644 --- a/config/crd/bases/config.projectsveltos.io_profiles.yaml +++ b/config/crd/bases/config.projectsveltos.io_profiles.yaml @@ -707,6 +707,16 @@ spec: When expressed as templates, the values are filled in using information from resources within the management cluster before deployment (Cluster) type: string + skipNamespaceCreation: + default: false + description: |- + SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + for namespaced resources defined in this KustomizationRef. + This field is ignored for cluster-scoped resources. + By default, Sveltos attempts to get or create the target namespace if it does not exist. + Setting this to true avoids those calls, which is necessary when Sveltos lacks + permissions to manage namespaces at the cluster level. + type: boolean targetNamespace: description: |- TargetNamespace sets or overrides the namespace in the @@ -991,6 +1001,16 @@ spec: Defaults to 'None', which translates to the root path of the SourceRef. Used only for GitRepository;OCIRepository;Bucket type: string + skipNamespaceCreation: + default: false + description: |- + SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + for namespaced resources defined in this PolicyRef. + This field is ignored for cluster-scoped resources. + By default, Sveltos attempts to get or create the target namespace if it does not exist. + Setting this to true avoids those calls, which is necessary when Sveltos lacks + permissions to manage namespaces at the cluster level. + type: boolean tier: default: 100 description: |- diff --git a/controllers/export_test.go b/controllers/export_test.go index c81f85c3..0bec8fed 100644 --- a/controllers/export_test.go +++ b/controllers/export_test.go @@ -210,3 +210,7 @@ var ( var ( GetSortedKeys = getSortedKeys ) + +type ( + ReferencedObject = referencedObject +) diff --git a/controllers/handlers_kustomize.go b/controllers/handlers_kustomize.go index 6272ffa2..60a88182 100644 --- a/controllers/handlers_kustomize.go +++ b/controllers/handlers_kustomize.go @@ -995,7 +995,8 @@ func deployKustomizeResources(ctx context.Context, c client.Client, remoteRestCo Name: kustomizationRef.Name, } localReports, err = deployUnstructured(ctx, true, localConfig, c, objectsToDeployLocally, - ref, kustomizationRef.Tier, libsveltosv1beta1.FeatureKustomize, clusterSummary, []string{}, logger) + ref, kustomizationRef.Tier, kustomizationRef.SkipNamespaceCreation, libsveltosv1beta1.FeatureKustomize, + clusterSummary, []string{}, logger) if err != nil { logger.V(logs.LogInfo).Info(fmt.Sprintf("failed to deploy to management cluster %v", err)) return localReports, nil, err @@ -1020,7 +1021,8 @@ func deployKustomizeResources(ctx context.Context, c client.Client, remoteRestCo } remoteReports, err = deployUnstructured(ctx, false, remoteRestConfig, remoteClient, objectsToDeployRemotely, - ref, kustomizationRef.Tier, libsveltosv1beta1.FeatureKustomize, clusterSummary, []string{}, logger) + ref, kustomizationRef.Tier, kustomizationRef.SkipNamespaceCreation, libsveltosv1beta1.FeatureKustomize, + clusterSummary, []string{}, logger) if err != nil { logger.V(logs.LogInfo).Info(fmt.Sprintf("failed to deploy to remote cluster %v", err)) return localReports, remoteReports, err diff --git a/controllers/handlers_utils.go b/controllers/handlers_utils.go index 97f78464..5d1d449d 100644 --- a/controllers/handlers_utils.go +++ b/controllers/handlers_utils.go @@ -70,9 +70,10 @@ const ( type referencedObject struct { corev1.ObjectReference - Tier int32 - Optional bool - Path string + Tier int32 + SkipNamespaceCreation bool + Optional bool + Path string } func getClusterSummaryAnnotationValue(clusterSummary *configv1beta1.ClusterSummary) string { @@ -86,12 +87,12 @@ func getClusterSummaryAnnotationValue(clusterSummary *configv1beta1.ClusterSumma // the policies deployed in the form of kind.group:namespace:name for namespaced policies // and kind.group::name for cluster wide policies. func deployContentOfConfigMap(ctx context.Context, deployingToMgmtCluster bool, destConfig *rest.Config, - destClient client.Client, configMap *corev1.ConfigMap, referenceTier int32, clusterSummary *configv1beta1.ClusterSummary, + destClient client.Client, configMap *corev1.ConfigMap, reference *referencedObject, clusterSummary *configv1beta1.ClusterSummary, mgmtResources map[string]*unstructured.Unstructured, logger logr.Logger, ) ([]libsveltosv1beta1.ResourceReport, error) { resourceReports, err := deployContent(ctx, deployingToMgmtCluster, destConfig, destClient, configMap, configMap.Data, - referenceTier, clusterSummary, mgmtResources, logger) + reference.Tier, reference.SkipNamespaceCreation, clusterSummary, mgmtResources, logger) if err != nil { return resourceReports, fmt.Errorf("processing ConfigMap %s/%s: %w", configMap.Namespace, configMap.Name, err) } @@ -104,7 +105,7 @@ func deployContentOfConfigMap(ctx context.Context, deployingToMgmtCluster bool, // the policies deployed in the form of kind.group:namespace:name for namespaced policies // and kind.group::name for cluster wide policies. func deployContentOfSecret(ctx context.Context, deployingToMgmtCluster bool, destConfig *rest.Config, - destClient client.Client, secret *corev1.Secret, referenceTier int32, clusterSummary *configv1beta1.ClusterSummary, + destClient client.Client, secret *corev1.Secret, reference *referencedObject, clusterSummary *configv1beta1.ClusterSummary, mgmtResources map[string]*unstructured.Unstructured, logger logr.Logger, ) ([]libsveltosv1beta1.ResourceReport, error) { @@ -114,7 +115,7 @@ func deployContentOfSecret(ctx context.Context, deployingToMgmtCluster bool, des } resourceReports, err := deployContent(ctx, deployingToMgmtCluster, destConfig, destClient, secret, data, - referenceTier, clusterSummary, mgmtResources, logger) + reference.Tier, reference.SkipNamespaceCreation, clusterSummary, mgmtResources, logger) if err != nil { return resourceReports, fmt.Errorf("processing Secret %s/%s: %w", secret.Namespace, secret.Name, err) } @@ -123,7 +124,7 @@ func deployContentOfSecret(ctx context.Context, deployingToMgmtCluster bool, des } func deployContentOfSource(ctx context.Context, deployingToMgmtCluster bool, destConfig *rest.Config, - destClient client.Client, source client.Object, referenceTier int32, path string, + destClient client.Client, source client.Object, reference *referencedObject, path string, clusterSummary *configv1beta1.ClusterSummary, mgmtResources map[string]*unstructured.Unstructured, logger logr.Logger) ([]libsveltosv1beta1.ResourceReport, error) { @@ -171,7 +172,7 @@ func deployContentOfSource(ctx context.Context, deployingToMgmtCluster bool, des } return deployContent(ctx, deployingToMgmtCluster, destConfig, destClient, source, content, - referenceTier, clusterSummary, mgmtResources, logger) + reference.Tier, reference.SkipNamespaceCreation, clusterSummary, mgmtResources, logger) } func readFiles(dir string) (map[string]string, error) { @@ -281,8 +282,8 @@ func prepareReports(resources []*unstructured.Unstructured) []libsveltosv1beta1. // the policies deployed in the form of kind.group:namespace:name for namespaced policies // and kind.group::name for cluster wide policies. func deployContent(ctx context.Context, deployingToMgmtCluster bool, destConfig *rest.Config, destClient client.Client, - referencedObject client.Object, data map[string]string, referenceTier int32, clusterSummary *configv1beta1.ClusterSummary, - mgmtResources map[string]*unstructured.Unstructured, logger logr.Logger, + referencedObject client.Object, data map[string]string, referenceTier int32, skipNamespaceCreation bool, + clusterSummary *configv1beta1.ClusterSummary, mgmtResources map[string]*unstructured.Unstructured, logger logr.Logger, ) (reports []libsveltosv1beta1.ResourceReport, err error) { subresources := getSubresources(referencedObject) @@ -324,7 +325,7 @@ func deployContent(ctx context.Context, deployingToMgmtCluster bool, destConfig } return deployUnstructured(ctx, deployingToMgmtCluster, destConfig, destClient, resources, ref, referenceTier, - libsveltosv1beta1.FeatureResources, clusterSummary, subresources, logger) + skipNamespaceCreation, libsveltosv1beta1.FeatureResources, clusterSummary, subresources, logger) } // adjustNamespace fixes namespace. @@ -396,7 +397,7 @@ func applyPatches(ctx context.Context, clusterSummary *configv1beta1.ClusterSumm //nolint:funlen // requires a lot of arguments because kustomize and plain resources are using this function func deployUnstructured(ctx context.Context, deployingToMgmtCluster bool, destConfig *rest.Config, destClient client.Client, referencedUnstructured []*unstructured.Unstructured, referencedObject *corev1.ObjectReference, - referenceTier int32, featureID libsveltosv1beta1.FeatureID, clusterSummary *configv1beta1.ClusterSummary, + referenceTier int32, skipNamespaceCreation bool, featureID libsveltosv1beta1.FeatureID, clusterSummary *configv1beta1.ClusterSummary, subresources []string, logger logr.Logger) (reports []libsveltosv1beta1.ResourceReport, err error) { profile, profileTier, err := configv1beta1.GetProfileOwnerAndTier(ctx, getManagementClusterClient(), clusterSummary) @@ -433,11 +434,13 @@ func deployUnstructured(ctx context.Context, deployingToMgmtCluster bool, destCo resource, policyHash := deployer.GetResource(policy, deployer.HasIgnoreConfigurationDriftAnnotation(policy), referencedObject, profile, profileTier, referenceTier, string(featureID), logger) - // If policy is namespaced, create namespace if not already existing - err = deployer.CreateNamespace(ctx, destClient, clusterSummary.Spec.ClusterProfileSpec.SyncMode == configv1beta1.SyncModeDryRun, - policy.GetNamespace()) - if err != nil { - return nil, fmt.Errorf("%s: %w", errorPrefix, err) + if !skipNamespaceCreation { + // If policy is namespaced, create namespace if not already existing + err = deployer.CreateNamespace(ctx, destClient, clusterSummary.Spec.ClusterProfileSpec.SyncMode == configv1beta1.SyncModeDryRun, + policy.GetNamespace()) + if err != nil { + return nil, fmt.Errorf("%s: %w", errorPrefix, err) + } } dr, err := k8s_utils.GetDynamicResourceInterface(destConfig, policy.GroupVersionKind(), policy.GetNamespace()) @@ -752,6 +755,7 @@ func collectReferencedObjects(references []configv1beta1.PolicyRef) (local, remo } object.Tier = reference.Tier object.Optional = reference.Optional + object.SkipNamespaceCreation = reference.SkipNamespaceCreation case string(libsveltosv1beta1.SecretReferencedResourceKind): object.ObjectReference = corev1.ObjectReference{ APIVersion: "v1", @@ -761,6 +765,7 @@ func collectReferencedObjects(references []configv1beta1.PolicyRef) (local, remo } object.Tier = reference.Tier object.Optional = reference.Optional + object.SkipNamespaceCreation = reference.SkipNamespaceCreation case sourcev1.GitRepositoryKind: object.ObjectReference = corev1.ObjectReference{ APIVersion: sourcev1.GroupVersion.String(), @@ -770,6 +775,7 @@ func collectReferencedObjects(references []configv1beta1.PolicyRef) (local, remo } object.Tier = reference.Tier object.Optional = reference.Optional + object.SkipNamespaceCreation = reference.SkipNamespaceCreation object.Path = reference.Path case sourcev1.OCIRepositoryKind: object.ObjectReference = corev1.ObjectReference{ @@ -780,6 +786,7 @@ func collectReferencedObjects(references []configv1beta1.PolicyRef) (local, remo } object.Tier = reference.Tier object.Optional = reference.Optional + object.SkipNamespaceCreation = reference.SkipNamespaceCreation object.Path = reference.Path case sourcev1.BucketKind: object.ObjectReference = corev1.ObjectReference{ @@ -790,6 +797,7 @@ func collectReferencedObjects(references []configv1beta1.PolicyRef) (local, remo } object.Tier = reference.Tier object.Optional = reference.Optional + object.SkipNamespaceCreation = reference.SkipNamespaceCreation object.Path = reference.Path } @@ -923,14 +931,14 @@ func deployObjects(ctx context.Context, deployingToMgmtCluster bool, destClient l := logger.WithValues("configMapNamespace", configMap.Namespace, "configMapName", configMap.Name) l.V(logs.LogDebug).Info("deploying ConfigMap content") tmpResourceReports, err = - deployContentOfConfigMap(ctx, deployingToMgmtCluster, destConfig, destClient, configMap, referencedObjects[i].Tier, + deployContentOfConfigMap(ctx, deployingToMgmtCluster, destConfig, destClient, configMap, &referencedObjects[i], clusterSummary, mgmtResources, l) } else if referencedObjects[i].GetObjectKind().GroupVersionKind().Kind == string(libsveltosv1beta1.SecretReferencedResourceKind) { secret := referencedObject.(*corev1.Secret) l := logger.WithValues("secretNamespace", secret.Namespace, "secretName", secret.Name) l.V(logs.LogDebug).Info("deploying Secret content") tmpResourceReports, err = - deployContentOfSecret(ctx, deployingToMgmtCluster, destConfig, destClient, secret, referencedObjects[i].Tier, + deployContentOfSecret(ctx, deployingToMgmtCluster, destConfig, destClient, secret, &referencedObjects[i], clusterSummary, mgmtResources, l) } else { source := referencedObject @@ -938,7 +946,7 @@ func deployObjects(ctx context.Context, deployingToMgmtCluster bool, destClient annotations := source.GetAnnotations() path := annotations[pathAnnotation] tmpResourceReports, err = - deployContentOfSource(ctx, deployingToMgmtCluster, destConfig, destClient, source, referencedObjects[i].Tier, path, + deployContentOfSource(ctx, deployingToMgmtCluster, destConfig, destClient, source, &referencedObjects[i], path, clusterSummary, mgmtResources, logger) } diff --git a/controllers/handlers_utils_test.go b/controllers/handlers_utils_test.go index 5a0d0ee5..4fefde97 100644 --- a/controllers/handlers_utils_test.go +++ b/controllers/handlers_utils_test.go @@ -556,7 +556,7 @@ var _ = Describe("HandlersUtils", func() { // created) resourceReports, err := controllers.DeployContent(context.TODO(), false, testEnv.Config, testEnv.Client, secret, map[string]string{"service": services}, - defaultTier, clusterSummary, nil, textlogger.NewLogger(textlogger.NewConfig())) + defaultTier, false, clusterSummary, nil, textlogger.NewLogger(textlogger.NewConfig())) Expect(err).To(BeNil()) By("Validating action for all resourceReports is Create") validateResourceReports(resourceReports, 2, 0, 0, 0) @@ -587,7 +587,7 @@ var _ = Describe("HandlersUtils", func() { // ( if the ClusterProfile were to be changed from DryRun, nothing would happen). resourceReports, err = controllers.DeployContent(context.TODO(), false, testEnv.Config, testEnv.Client, secret, map[string]string{"service": services}, - defaultTier, clusterSummary, nil, textlogger.NewLogger(textlogger.NewConfig())) + defaultTier, false, clusterSummary, nil, textlogger.NewLogger(textlogger.NewConfig())) Expect(err).To(BeNil()) By("Validating action for all resourceReports is NoAction") validateResourceReports(resourceReports, 0, 0, 2, 0) @@ -624,7 +624,7 @@ var _ = Describe("HandlersUtils", func() { // (if the ClusterProfile were to be changed from DryRun, both service would be updated). resourceReports, err = controllers.DeployContent(context.TODO(), false, testEnv.Config, testEnv.Client, secret, map[string]string{"service": newContent}, - defaultTier, clusterSummary, nil, textlogger.NewLogger(textlogger.NewConfig())) + defaultTier, false, clusterSummary, nil, textlogger.NewLogger(textlogger.NewConfig())) Expect(err).To(BeNil()) By("Validating action for all resourceReports is Update") validateResourceReports(resourceReports, 0, 2, 0, 0) @@ -634,7 +634,7 @@ var _ = Describe("HandlersUtils", func() { tmpSecret := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Namespace: randomString(), Name: randomString()}} resourceReports, err = controllers.DeployContent(context.TODO(), false, testEnv.Config, testEnv.Client, tmpSecret, map[string]string{"service": services}, - defaultTier, clusterSummary, nil, textlogger.NewLogger(textlogger.NewConfig())) + defaultTier, false, clusterSummary, nil, textlogger.NewLogger(textlogger.NewConfig())) Expect(err).To(BeNil()) By("Validating action for all resourceReports is Conflict") validateResourceReports(resourceReports, 0, 0, 0, 2) @@ -660,27 +660,37 @@ var _ = Describe("HandlersUtils", func() { Expect(addTypeInformationToObject(testEnv.Scheme(), clusterSummary)).To(Succeed()) + reference := &controllers.ReferencedObject{Tier: defaultTier, SkipNamespaceCreation: false} resourceReports, err := controllers.DeployContentOfSecret(context.TODO(), false, - testEnv.Config, testEnv.Client, secret, defaultTier, clusterSummary, nil, + testEnv.Config, testEnv.Client, secret, reference, clusterSummary, nil, textlogger.NewLogger(textlogger.NewConfig())) Expect(err).To(BeNil()) Expect(len(resourceReports)).To(Equal(3)) }) - It("deployContentOfConfigMap deploys all policies contained in a Secret", func() { - services := fmt.Sprintf(serviceTemplate, namespace, namespace) - depl := fmt.Sprintf(deplTemplate, namespace) + It("deployContentOfConfigMap deploys all policies contained in a ConfigMap", func() { + ns := randomString() + services := fmt.Sprintf(serviceTemplate, ns, namespace) + depl := fmt.Sprintf(deplTemplate, ns) configMap := createConfigMapWithPolicy(namespace, randomString(), depl, services) - Expect(testEnv.Create(context.TODO(), configMap)).To(Succeed()) Expect(waitForObject(ctx, testEnv.Client, configMap)).To(Succeed()) Expect(addTypeInformationToObject(testEnv.Scheme(), clusterSummary)).To(Succeed()) + reference := &controllers.ReferencedObject{Tier: defaultTier, SkipNamespaceCreation: true} + _, err := controllers.DeployContentOfConfigMap(context.TODO(), false, + testEnv.Config, testEnv.Client, configMap, reference, clusterSummary, nil, + textlogger.NewLogger(textlogger.NewConfig())) + // SkipNamespaceCreation is set to true. Since Service namespace is missing this will fail + Expect(err).ToNot(BeNil()) + Expect(err.Error()).To(ContainSubstring(fmt.Sprintf("namespaces %q not found", ns))) + + reference.SkipNamespaceCreation = false resourceReports, err := controllers.DeployContentOfConfigMap(context.TODO(), false, - testEnv.Config, testEnv.Client, configMap, defaultTier, clusterSummary, nil, + testEnv.Config, testEnv.Client, configMap, reference, clusterSummary, nil, textlogger.NewLogger(textlogger.NewConfig())) Expect(err).To(BeNil()) Expect(len(resourceReports)).To(Equal(3)) @@ -1174,6 +1184,7 @@ stringData: }) It("patchRessource with subresources correctly update instance", func() { + namespace := randomString() serviceName := randomString() key := randomString() value := randomString() @@ -1181,7 +1192,7 @@ stringData: kind: Service metadata: name: %s - namespace: default + namespace: %s labels: %s: %s spec: @@ -1192,10 +1203,18 @@ status: ingress: - ip: 1.1.1.1` + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: namespace, + }, + } + Expect(testEnv.Create(context.TODO(), ns)).To(Succeed()) + Expect(waitForObject(ctx, testEnv.Client, ns)).To(Succeed()) + service := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: serviceName, - Namespace: "default", + Namespace: namespace, }, Spec: corev1.ServiceSpec{ Type: corev1.ServiceTypeLoadBalancer, @@ -1217,25 +1236,27 @@ status: Expect(addTypeInformationToObject(testEnv.Scheme(), clusterSummary)).To(Succeed()) configMap := createConfigMapWithPolicy(namespace, randomString(), fmt.Sprintf(servicePatch, - serviceName, key, value, key, value)) + serviceName, namespace, key, value, key, value)) configMap.Annotations = map[string]string{ "projectsveltos.io/subresources": "status"} + + reference := &controllers.ReferencedObject{Tier: defaultTier, SkipNamespaceCreation: false} _, err := controllers.DeployContentOfConfigMap(context.TODO(), false, testEnv.Config, testEnv.Client, - configMap, defaultTier, clusterSummary, nil, textlogger.NewLogger(textlogger.NewConfig())) + configMap, reference, clusterSummary, nil, textlogger.NewLogger(textlogger.NewConfig())) Expect(err).To(BeNil()) serviceOut := corev1.Service{} // wait for cache to sync Eventually(func() bool { err := testEnv.Get(context.TODO(), - types.NamespacedName{Namespace: "default", Name: serviceName}, + types.NamespacedName{Namespace: namespace, Name: serviceName}, &serviceOut) return err == nil && serviceOut.Status.LoadBalancer.Ingress != nil }, timeout, pollingInterval).Should(BeTrue()) Expect(testEnv.Get(context.TODO(), - types.NamespacedName{Namespace: "default", Name: serviceName}, &serviceOut)).To(Succeed()) + types.NamespacedName{Namespace: namespace, Name: serviceName}, &serviceOut)).To(Succeed()) // verify status has been updated Expect(serviceOut.Status.LoadBalancer.Ingress).ToNot(BeNil()) diff --git a/manifest/manifest.yaml b/manifest/manifest.yaml index eac10584..0db0b06e 100644 --- a/manifest/manifest.yaml +++ b/manifest/manifest.yaml @@ -1016,6 +1016,16 @@ spec: When expressed as templates, the values are filled in using information from resources within the management cluster before deployment (Cluster) type: string + skipNamespaceCreation: + default: false + description: |- + SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + for namespaced resources defined in this KustomizationRef. + This field is ignored for cluster-scoped resources. + By default, Sveltos attempts to get or create the target namespace if it does not exist. + Setting this to true avoids those calls, which is necessary when Sveltos lacks + permissions to manage namespaces at the cluster level. + type: boolean targetNamespace: description: |- TargetNamespace sets or overrides the namespace in the @@ -1300,6 +1310,16 @@ spec: Defaults to 'None', which translates to the root path of the SourceRef. Used only for GitRepository;OCIRepository;Bucket type: string + skipNamespaceCreation: + default: false + description: |- + SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + for namespaced resources defined in this PolicyRef. + This field is ignored for cluster-scoped resources. + By default, Sveltos attempts to get or create the target namespace if it does not exist. + Setting this to true avoids those calls, which is necessary when Sveltos lacks + permissions to manage namespaces at the cluster level. + type: boolean tier: default: 100 description: |- @@ -2660,6 +2680,16 @@ spec: When expressed as templates, the values are filled in using information from resources within the management cluster before deployment (Cluster) type: string + skipNamespaceCreation: + default: false + description: |- + SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + for namespaced resources defined in this KustomizationRef. + This field is ignored for cluster-scoped resources. + By default, Sveltos attempts to get or create the target namespace if it does not exist. + Setting this to true avoids those calls, which is necessary when Sveltos lacks + permissions to manage namespaces at the cluster level. + type: boolean targetNamespace: description: |- TargetNamespace sets or overrides the namespace in the @@ -2944,6 +2974,16 @@ spec: Defaults to 'None', which translates to the root path of the SourceRef. Used only for GitRepository;OCIRepository;Bucket type: string + skipNamespaceCreation: + default: false + description: |- + SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + for namespaced resources defined in this PolicyRef. + This field is ignored for cluster-scoped resources. + By default, Sveltos attempts to get or create the target namespace if it does not exist. + Setting this to true avoids those calls, which is necessary when Sveltos lacks + permissions to manage namespaces at the cluster level. + type: boolean tier: default: 100 description: |- @@ -3665,6 +3705,16 @@ spec: Defaults to 'None', which translates to the root path of the SourceRef. Used only for GitRepository;OCIRepository;Bucket type: string + skipNamespaceCreation: + default: false + description: |- + SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + for namespaced resources defined in this PolicyRef. + This field is ignored for cluster-scoped resources. + By default, Sveltos attempts to get or create the target namespace if it does not exist. + Setting this to true avoids those calls, which is necessary when Sveltos lacks + permissions to manage namespaces at the cluster level. + type: boolean tier: default: 100 description: |- @@ -3896,6 +3946,16 @@ spec: Defaults to 'None', which translates to the root path of the SourceRef. Used only for GitRepository;OCIRepository;Bucket type: string + skipNamespaceCreation: + default: false + description: |- + SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + for namespaced resources defined in this PolicyRef. + This field is ignored for cluster-scoped resources. + By default, Sveltos attempts to get or create the target namespace if it does not exist. + Setting this to true avoids those calls, which is necessary when Sveltos lacks + permissions to manage namespaces at the cluster level. + type: boolean tier: default: 100 description: |- @@ -5065,6 +5125,16 @@ spec: When expressed as templates, the values are filled in using information from resources within the management cluster before deployment (Cluster) type: string + skipNamespaceCreation: + default: false + description: |- + SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + for namespaced resources defined in this KustomizationRef. + This field is ignored for cluster-scoped resources. + By default, Sveltos attempts to get or create the target namespace if it does not exist. + Setting this to true avoids those calls, which is necessary when Sveltos lacks + permissions to manage namespaces at the cluster level. + type: boolean targetNamespace: description: |- TargetNamespace sets or overrides the namespace in the @@ -5349,6 +5419,16 @@ spec: Defaults to 'None', which translates to the root path of the SourceRef. Used only for GitRepository;OCIRepository;Bucket type: string + skipNamespaceCreation: + default: false + description: |- + SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + for namespaced resources defined in this PolicyRef. + This field is ignored for cluster-scoped resources. + By default, Sveltos attempts to get or create the target namespace if it does not exist. + Setting this to true avoids those calls, which is necessary when Sveltos lacks + permissions to manage namespaces at the cluster level. + type: boolean tier: default: 100 description: |- @@ -6735,6 +6815,16 @@ spec: When expressed as templates, the values are filled in using information from resources within the management cluster before deployment (Cluster) type: string + skipNamespaceCreation: + default: false + description: |- + SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + for namespaced resources defined in this KustomizationRef. + This field is ignored for cluster-scoped resources. + By default, Sveltos attempts to get or create the target namespace if it does not exist. + Setting this to true avoids those calls, which is necessary when Sveltos lacks + permissions to manage namespaces at the cluster level. + type: boolean targetNamespace: description: |- TargetNamespace sets or overrides the namespace in the @@ -7019,6 +7109,16 @@ spec: Defaults to 'None', which translates to the root path of the SourceRef. Used only for GitRepository;OCIRepository;Bucket type: string + skipNamespaceCreation: + default: false + description: |- + SkipNamespaceCreation indicates whether Sveltos should skip creating the namespace + for namespaced resources defined in this PolicyRef. + This field is ignored for cluster-scoped resources. + By default, Sveltos attempts to get or create the target namespace if it does not exist. + Setting this to true avoids those calls, which is necessary when Sveltos lacks + permissions to manage namespaces at the cluster level. + type: boolean tier: default: 100 description: |-