diff --git a/api/v1beta1/clustersummary_types.go b/api/v1beta1/clustersummary_types.go index 061c1160..6e096111 100644 --- a/api/v1beta1/clustersummary_types.go +++ b/api/v1beta1/clustersummary_types.go @@ -212,6 +212,7 @@ type ClusterSummaryStatus struct { // +kubebuilder:resource:path=clustersummaries,scope=Namespaced // +kubebuilder:subresource:status // +kubebuilder:storageversion +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of ClusterSummary" // +kubebuilder:printcolumn:name="HelmCharts",type="string",JSONPath=".status.featureSummaries[?(@.featureID==\"Helm\")].status",description="Indicates whether HelmCharts are all provisioned",priority=2 // +kubebuilder:printcolumn:name="KustomizeRefs",type="string",JSONPath=".status.featureSummaries[?(@.featureID==\"Kustomize\")].status",description="Indicates whether KustomizeRefs are all provisioned",priority=2 // +kubebuilder:printcolumn:name="PolicyRefs",type="string",JSONPath=".status.featureSummaries[?(@.featureID==\"Resources\")].status",description="Indicates whether PolicyRefs are all provisioned",priority=2 diff --git a/config/crd/bases/config.projectsveltos.io_clustersummaries.yaml b/config/crd/bases/config.projectsveltos.io_clustersummaries.yaml index 5e789781..328f4999 100644 --- a/config/crd/bases/config.projectsveltos.io_clustersummaries.yaml +++ b/config/crd/bases/config.projectsveltos.io_clustersummaries.yaml @@ -936,6 +936,10 @@ spec: subresources: status: {} - additionalPrinterColumns: + - description: Time duration since creation of ClusterSummary + jsonPath: .metadata.creationTimestamp + name: Age + type: date - description: Indicates whether HelmCharts are all provisioned jsonPath: .status.featureSummaries[?(@.featureID=="Helm")].status name: HelmCharts diff --git a/controllers/clustercache/cluster_cache_test.go b/controllers/clustercache/cluster_cache_test.go index 9da354be..206926a9 100644 --- a/controllers/clustercache/cluster_cache_test.go +++ b/controllers/clustercache/cluster_cache_test.go @@ -86,7 +86,6 @@ var _ = Describe("Clustercache", func() { Expect(cacheMgr.GetConfigFromMap(clusterObj)).To(BeNil()) Expect(cacheMgr.GetSecretForCluster(clusterObj)).To(BeNil()) Expect(cacheMgr.GetClusterFromSecret(secretObj)).To(BeNil()) - }) It("RemoveSecret removes entries for all clusters using the modified secret", func() { @@ -136,9 +135,12 @@ func createClusterResources(cluster *libsveltosv1beta1.SveltosCluster) *corev1.S } Expect(testEnv.Create(context.TODO(), ns)).To(Succeed()) + Expect(waitForObject(context.TODO(), testEnv.Client, ns)).To(Succeed()) + Expect(testEnv.Create(context.TODO(), cluster)).To(Succeed()) Expect(testEnv.Create(context.TODO(), secret)).To(Succeed()) + Expect(waitForObject(context.TODO(), testEnv.Client, cluster)).To(Succeed()) Expect(waitForObject(context.TODO(), testEnv.Client, secret)).To(Succeed()) return secret } diff --git a/controllers/clustercache/clustercache_suite_test.go b/controllers/clustercache/clustercache_suite_test.go index 93326ff2..0cfd7485 100644 --- a/controllers/clustercache/clustercache_suite_test.go +++ b/controllers/clustercache/clustercache_suite_test.go @@ -41,7 +41,7 @@ import ( "github.com/projectsveltos/addon-controller/internal/test/helpers" libsveltosv1beta1 "github.com/projectsveltos/libsveltos/api/v1beta1" libsveltoscrd "github.com/projectsveltos/libsveltos/lib/crd" - "github.com/projectsveltos/libsveltos/lib/utils" + "github.com/projectsveltos/libsveltos/lib/k8s_utils" ) var ( @@ -95,7 +95,7 @@ var _ = BeforeSuite(func() { }() var sveltosCRD *unstructured.Unstructured - sveltosCRD, err = utils.GetUnstructured(libsveltoscrd.GetSveltosClusterCRDYAML()) + sveltosCRD, err = k8s_utils.GetUnstructured(libsveltoscrd.GetSveltosClusterCRDYAML()) Expect(err).To(BeNil()) Expect(testEnv.Create(context.TODO(), sveltosCRD)).To(Succeed()) Expect(waitForObject(context.TODO(), testEnv, sveltosCRD)).To(Succeed()) diff --git a/controllers/clustersummary_controller.go b/controllers/clustersummary_controller.go index 980fa6f2..b6ed36cb 100644 --- a/controllers/clustersummary_controller.go +++ b/controllers/clustersummary_controller.go @@ -366,7 +366,7 @@ func (r *ClusterSummaryReconciler) reconcileNormal( err = r.removeResourceSummary(ctx, clusterSummaryScope, logger) if err != nil { logger.V(logs.LogInfo).Error(err, "failed to remove ResourceSummary.") - return reconcile.Result{Requeue: true, RequeueAfter: deleteRequeueAfter}, nil + return reconcile.Result{Requeue: true, RequeueAfter: normalRequeueAfter}, nil } } diff --git a/controllers/controllers_suite_test.go b/controllers/controllers_suite_test.go index 9633c87b..57929854 100644 --- a/controllers/controllers_suite_test.go +++ b/controllers/controllers_suite_test.go @@ -43,8 +43,8 @@ import ( "github.com/projectsveltos/libsveltos/lib/clusterproxy" libsveltoscrd "github.com/projectsveltos/libsveltos/lib/crd" "github.com/projectsveltos/libsveltos/lib/deployer" + "github.com/projectsveltos/libsveltos/lib/k8s_utils" libsveltosset "github.com/projectsveltos/libsveltos/lib/set" - "github.com/projectsveltos/libsveltos/lib/utils" ) var ( @@ -92,37 +92,37 @@ var _ = BeforeSuite(func() { }() var sveltosCRD *unstructured.Unstructured - sveltosCRD, err = utils.GetUnstructured(libsveltoscrd.GetSveltosClusterCRDYAML()) + sveltosCRD, err = k8s_utils.GetUnstructured(libsveltoscrd.GetSveltosClusterCRDYAML()) Expect(err).To(BeNil()) Expect(testEnv.Create(context.TODO(), sveltosCRD)).To(Succeed()) Expect(waitForObject(context.TODO(), testEnv, sveltosCRD)).To(Succeed()) var resourceSummaryCRD *unstructured.Unstructured - resourceSummaryCRD, err = utils.GetUnstructured(libsveltoscrd.GetResourceSummaryCRDYAML()) + resourceSummaryCRD, err = k8s_utils.GetUnstructured(libsveltoscrd.GetResourceSummaryCRDYAML()) Expect(err).To(BeNil()) Expect(testEnv.Create(context.TODO(), resourceSummaryCRD)).To(Succeed()) Expect(waitForObject(context.TODO(), testEnv, resourceSummaryCRD)).To(Succeed()) var dcCRD *unstructured.Unstructured - dcCRD, err = utils.GetUnstructured(libsveltoscrd.GetDebuggingConfigurationCRDYAML()) + dcCRD, err = k8s_utils.GetUnstructured(libsveltoscrd.GetDebuggingConfigurationCRDYAML()) Expect(err).To(BeNil()) Expect(testEnv.Create(context.TODO(), dcCRD)).To(Succeed()) Expect(waitForObject(context.TODO(), testEnv, dcCRD)).To(Succeed()) var reloaderCRD *unstructured.Unstructured - reloaderCRD, err = utils.GetUnstructured(libsveltoscrd.GetReloaderCRDYAML()) + reloaderCRD, err = k8s_utils.GetUnstructured(libsveltoscrd.GetReloaderCRDYAML()) Expect(err).To(BeNil()) Expect(testEnv.Create(context.TODO(), reloaderCRD)).To(Succeed()) Expect(waitForObject(context.TODO(), testEnv, reloaderCRD)).To(Succeed()) var setCRD *unstructured.Unstructured - setCRD, err = utils.GetUnstructured(libsveltoscrd.GetSetCRDYAML()) + setCRD, err = k8s_utils.GetUnstructured(libsveltoscrd.GetSetCRDYAML()) Expect(err).To(BeNil()) Expect(testEnv.Create(context.TODO(), setCRD)).To(Succeed()) Expect(waitForObject(context.TODO(), testEnv, setCRD)).To(Succeed()) var clusterSetCRD *unstructured.Unstructured - clusterSetCRD, err = utils.GetUnstructured(libsveltoscrd.GetClusterSetCRDYAML()) + clusterSetCRD, err = k8s_utils.GetUnstructured(libsveltoscrd.GetClusterSetCRDYAML()) Expect(err).To(BeNil()) Expect(testEnv.Create(context.TODO(), clusterSetCRD)).To(Succeed()) Expect(waitForObject(context.TODO(), testEnv, clusterSetCRD)).To(Succeed()) diff --git a/controllers/handlers_helm.go b/controllers/handlers_helm.go index dd6d1145..adb14259 100644 --- a/controllers/handlers_helm.go +++ b/controllers/handlers_helm.go @@ -67,10 +67,10 @@ import ( libsveltosv1beta1 "github.com/projectsveltos/libsveltos/api/v1beta1" "github.com/projectsveltos/libsveltos/lib/clusterproxy" "github.com/projectsveltos/libsveltos/lib/deployer" + "github.com/projectsveltos/libsveltos/lib/k8s_utils" logs "github.com/projectsveltos/libsveltos/lib/logsettings" "github.com/projectsveltos/libsveltos/lib/patcher" libsveltostemplate "github.com/projectsveltos/libsveltos/lib/template" - "github.com/projectsveltos/libsveltos/lib/utils" ) var ( @@ -1186,7 +1186,7 @@ func upgradeCRDs(ctx context.Context, requestedChart *configv1beta1.HelmChart, k return err } - dr, err := utils.GetDynamicResourceInterface(destConfig, apiextensionsv1.SchemeGroupVersion.WithKind("CustomResourceDefinition"), "") + dr, err := k8s_utils.GetDynamicResourceInterface(destConfig, apiextensionsv1.SchemeGroupVersion.WithKind("CustomResourceDefinition"), "") if err != nil { return err } @@ -1933,7 +1933,7 @@ func collectHelmContent(manifest string, logger logr.Logger) ([]*unstructured.Un resources := make([]*unstructured.Unstructured, 0, len(elements)) for i := range elements { - policy, err := utils.GetUnstructured([]byte(elements[i])) + policy, err := k8s_utils.GetUnstructured([]byte(elements[i])) if err != nil { logger.Error(err, fmt.Sprintf("failed to get policy from Data %.100s", elements[i])) return nil, err @@ -2350,7 +2350,7 @@ func addExtraMetadata(ctx context.Context, requestedChart *configv1beta1.HelmCha } var dr dynamic.ResourceInterface - dr, err = utils.GetDynamicResourceInterface(config, r.GroupVersionKind(), namespace) + dr, err = k8s_utils.GetDynamicResourceInterface(config, r.GroupVersionKind(), namespace) if err != nil { return err } @@ -2358,7 +2358,7 @@ func addExtraMetadata(ctx context.Context, requestedChart *configv1beta1.HelmCha addExtraLabels(r, clusterSummary.Spec.ClusterProfileSpec.ExtraLabels) addExtraAnnotations(r, clusterSummary.Spec.ClusterProfileSpec.ExtraAnnotations) - err = updateResource(ctx, dr, clusterSummary, r, []string{}, logger) + _, err = updateResource(ctx, dr, clusterSummary, r, []string{}, logger) if err != nil { logger.V(logs.LogInfo).Info(fmt.Sprintf("failed to update resource %s %s/%s: %v", r.GetKind(), r.GetNamespace(), r.GetName(), err)) diff --git a/controllers/handlers_kustomize.go b/controllers/handlers_kustomize.go index c5d3f339..6bcb8b24 100644 --- a/controllers/handlers_kustomize.go +++ b/controllers/handlers_kustomize.go @@ -53,9 +53,9 @@ import ( "github.com/projectsveltos/libsveltos/lib/clusterproxy" "github.com/projectsveltos/libsveltos/lib/deployer" "github.com/projectsveltos/libsveltos/lib/funcmap" + "github.com/projectsveltos/libsveltos/lib/k8s_utils" logs "github.com/projectsveltos/libsveltos/lib/logsettings" libsveltostemplate "github.com/projectsveltos/libsveltos/lib/template" - "github.com/projectsveltos/libsveltos/lib/utils" ) const ( @@ -663,7 +663,7 @@ func getKustomizedResources(ctx context.Context, c client.Client, clusterSummary } var u *unstructured.Unstructured - u, err = utils.GetUnstructured(yaml) + u, err = k8s_utils.GetUnstructured(yaml) if err != nil { logger.V(logs.LogInfo).Info(fmt.Sprintf("failed to get unstructured %v", err)) return nil, nil, nil, err diff --git a/controllers/handlers_resources.go b/controllers/handlers_resources.go index c7252111..b425e822 100644 --- a/controllers/handlers_resources.go +++ b/controllers/handlers_resources.go @@ -453,7 +453,6 @@ func updateClusterReportWithResourceReports(ctx context.Context, c client.Client } else if featureID == configv1beta1.FeatureKustomize { clusterReport.Status.KustomizeResourceReports = resourceReports } - return c.Status().Update(ctx, clusterReport) }) return err diff --git a/controllers/handlers_utils.go b/controllers/handlers_utils.go index 862047a8..1aa682cb 100644 --- a/controllers/handlers_utils.go +++ b/controllers/handlers_utils.go @@ -31,6 +31,9 @@ import ( sourcev1 "github.com/fluxcd/source-controller/api/v1" "github.com/gdexlab/go-render/render" "github.com/go-logr/logr" + "github.com/hexops/gotextdiff" + "github.com/hexops/gotextdiff/myers" + "github.com/hexops/gotextdiff/span" "github.com/pkg/errors" "gopkg.in/yaml.v3" corev1 "k8s.io/api/core/v1" @@ -55,10 +58,10 @@ import ( libsveltosv1beta1 "github.com/projectsveltos/libsveltos/api/v1beta1" "github.com/projectsveltos/libsveltos/lib/clusterproxy" "github.com/projectsveltos/libsveltos/lib/deployer" + "github.com/projectsveltos/libsveltos/lib/k8s_utils" logs "github.com/projectsveltos/libsveltos/lib/logsettings" "github.com/projectsveltos/libsveltos/lib/patcher" libsveltostemplate "github.com/projectsveltos/libsveltos/lib/template" - "github.com/projectsveltos/libsveltos/lib/utils" ) const ( @@ -208,6 +211,7 @@ func applySubresources(ctx context.Context, dr dynamic.ResourceInterface, return nil } + object.SetManagedFields(nil) object.SetResourceVersion("") data, err := runtime.Encode(unstructured.UnstructuredJSONScheme, object) if err != nil { @@ -218,69 +222,98 @@ func applySubresources(ctx context.Context, dr dynamic.ResourceInterface, return err } +func removeDriftExclusionsFields(ctx context.Context, dr dynamic.ResourceInterface, + clusterSummary *configv1beta1.ClusterSummary, object *unstructured.Unstructured) (bool, error) { + + // When operating in SyncModeContinuousWithDriftDetection mode and DriftExclusions are specified, + // avoid resetting certain object fields if the object is being redeployed (i.e, object already exists) + // For example, consider a Deployment with an Autoscaler. Since the Autoscaler manages the spec.replicas + // field, Sveltos is requested to deploy the Deployment and spec.replicas is specified as a field to ignore during + // configuration drift evaluation. + // If Sveltos is redeploying the deployment (for instance deployment image tag was changed), Sveltos must not + // override spec.replicas. + if clusterSummary.Spec.ClusterProfileSpec.SyncMode == configv1beta1.SyncModeContinuousWithDriftDetection { + if clusterSummary.Spec.ClusterProfileSpec.DriftExclusions != nil { + _, err := dr.Get(ctx, object.GetName(), metav1.GetOptions{}) + if err == nil { + // Resource exist. We are in drift detection mode and with driftExclusions. + // Remove fields in driftExclusions before applying an update + return true, nil + } else if apierrors.IsNotFound(err) { + // Object does not exist. We can apply it as it is. Since the object does + // not exist, nothing will be overridden + return false, nil + } else { + return false, err + } + } + } + + if clusterSummary.Spec.ClusterProfileSpec.SyncMode == configv1beta1.SyncModeDryRun && + clusterSummary.Spec.ClusterProfileSpec.DriftExclusions != nil { + // When evaluating diff in DryRun mode, exclude fields + return true, nil + } + + return false, nil +} + // updateResource creates or updates a resource in a Cluster. // No action in DryRun mode. func updateResource(ctx context.Context, dr dynamic.ResourceInterface, clusterSummary *configv1beta1.ClusterSummary, object *unstructured.Unstructured, subresources []string, - logger logr.Logger) error { + logger logr.Logger) (*unstructured.Unstructured, error) { + + forceConflict := true + options := metav1.PatchOptions{ + FieldManager: "application/apply-patch", + Force: &forceConflict, + } // No-op in DryRun mode if clusterSummary.Spec.ClusterProfileSpec.SyncMode == configv1beta1.SyncModeDryRun { - return nil + // Set dryRun option. Still proceed further so diff can be properly evaluated + options.DryRun = []string{metav1.DryRunAll} } l := logger.WithValues("resourceNamespace", object.GetNamespace(), "resourceName", object.GetName(), "resourceGVK", object.GetObjectKind().GroupVersionKind(), "subresources", subresources) l.V(logs.LogDebug).Info("deploying policy") - forceConflict := true - options := metav1.PatchOptions{ - FieldManager: "application/apply-patch", - Force: &forceConflict, + removeFields, err := removeDriftExclusionsFields(ctx, dr, clusterSummary, object) + if err != nil { + return nil, err } - // When operating in SyncModeContinuousWithDriftDetection mode and DriftExclusions are specified, - // avoid resetting certain object fields if the object is being redeployed. - // For example, consider a Deployment with an Autoscaler. Since the Autoscaler manages the spec.replicas - // field, Sveltos is requested to deploy the Deployment and spec.replicas is specified as a field to ignore during - // configuration drift evaluation. - // If Sveltos is redeploying the deployment (for instance deployment image tag was changed), Sveltos must not - // override spec.replicas. So code first tries to create resource and if already existing, before applying a patch - // the spec.replicas is removed. - if clusterSummary.Spec.ClusterProfileSpec.SyncMode == configv1beta1.SyncModeContinuousWithDriftDetection { - if clusterSummary.Spec.ClusterProfileSpec.DriftExclusions != nil { - _, err := dr.Create(ctx, object, metav1.CreateOptions{}) - if err != nil { - if !apierrors.IsAlreadyExists(err) { - return err - } - // The resource already exist. Apply Patches to avoid resetting fields that should be ignored for - // drift evaluation - patches := transformDriftExclusionsToPatches(clusterSummary.Spec.ClusterProfileSpec.DriftExclusions) - p := &patcher.CustomPatchPostRenderer{Patches: patches} - var patchedObjects []*unstructured.Unstructured - patchedObjects, err = p.RunUnstructured([]*unstructured.Unstructured{object}) - if err != nil { - return err - } - object = patchedObjects[0] - } else { - return applySubresources(ctx, dr, object, subresources, &options) - } + if removeFields { + patches := transformDriftExclusionsToPatches(clusterSummary.Spec.ClusterProfileSpec.DriftExclusions) + p := &patcher.CustomPatchPostRenderer{Patches: patches} + var patchedObjects []*unstructured.Unstructured + patchedObjects, err = p.RunUnstructured([]*unstructured.Unstructured{object}) + if err != nil { + return nil, err } + object = patchedObjects[0] } data, err := runtime.Encode(unstructured.UnstructuredJSONScheme, object) if err != nil { - return err + return nil, err } - _, err = dr.Patch(ctx, object.GetName(), types.ApplyPatchType, data, options) + updatedObject, err := dr.Patch(ctx, object.GetName(), types.ApplyPatchType, data, options) if err != nil { - return err + if clusterSummary.Spec.ClusterProfileSpec.SyncMode == configv1beta1.SyncModeDryRun && + apierrors.IsNotFound(err) { + // In DryRun mode, if resource is namespaced and namespace is not present, + // patch will fail with namespace not found. Treat this error as resoruce + // would be created + return object, nil + } + return nil, err } - return applySubresources(ctx, dr, object, subresources, &options) + return updatedObject, applySubresources(ctx, dr, object, subresources, &options) } func instantiateTemplate(referencedObject client.Object, logger logr.Logger) bool { @@ -427,7 +460,7 @@ func deployUnstructured(ctx context.Context, deployingToMgmtCluster bool, destCo return nil, err } - dr, err := utils.GetDynamicResourceInterface(destConfig, policy.GroupVersionKind(), policy.GetNamespace()) + dr, err := k8s_utils.GetDynamicResourceInterface(destConfig, policy.GroupVersionKind(), policy.GetNamespace()) if err != nil { return nil, err } @@ -454,7 +487,7 @@ func deployUnstructured(ctx context.Context, deployingToMgmtCluster bool, destCo return reports, err } - addMetadata(policy, resourceInfo.ResourceVersion, profile, + addMetadata(policy, resourceInfo.GetResourceVersion(), profile, clusterSummary.Spec.ClusterProfileSpec.ExtraLabels, clusterSummary.Spec.ClusterProfileSpec.ExtraAnnotations) if deployingToMgmtCluster { @@ -468,19 +501,19 @@ func deployUnstructured(ctx context.Context, deployingToMgmtCluster bool, destCo } if requeue { - err = requeueAllOldOwners(ctx, resourceInfo.OwnerReferences, featureID, clusterSummary, logger) + err = requeueAllOldOwners(ctx, resourceInfo.GetOwnerReferences(), featureID, clusterSummary, logger) if err != nil { return reports, err } } - err = updateResource(ctx, dr, clusterSummary, policy, subresources, logger) + policy, err = updateResource(ctx, dr, clusterSummary, policy, subresources, logger) if err != nil { return reports, err } resource.LastAppliedTime = &metav1.Time{Time: time.Now()} - reports = append(reports, *generateResourceReport(policyHash, resourceInfo, resource)) + reports = append(reports, *generateResourceReport(policyHash, resourceInfo, policy, resource)) } if conflictErrorMsg != "" { @@ -506,7 +539,7 @@ func addMetadata(policy *unstructured.Unstructured, resourceVersion string, prof // This approach ensures that conflict resolution decisions made by canDeployResource remain valid during the policy update. policy.SetResourceVersion(resourceVersion) - deployer.AddOwnerReference(policy, profile) + k8s_utils.AddOwnerReference(policy, profile) addExtraLabels(policy, extraLabels) addExtraAnnotations(policy, extraAnnotations) @@ -605,13 +638,17 @@ func canDeployResource(ctx context.Context, dr dynamic.ResourceInterface, policy } func generateResourceReport(policyHash string, resourceInfo *deployer.ResourceInfo, - resource *configv1beta1.Resource) *configv1beta1.ResourceReport { + policy *unstructured.Unstructured, resource *configv1beta1.Resource) *configv1beta1.ResourceReport { resourceReport := &configv1beta1.ResourceReport{Resource: *resource} - if resourceInfo.ResourceVersion == "" { + if resourceInfo == nil { resourceReport.Action = string(configv1beta1.CreateResourceAction) } else if policyHash != resourceInfo.Hash { resourceReport.Action = string(configv1beta1.UpdateResourceAction) + diff, err := evaluateResourceDiff(resourceInfo.CurrentResource, policy) + if err == nil { + resourceReport.Message = diff + } } else { resourceReport.Action = string(configv1beta1.NoResourceAction) resourceReport.Message = "Object already deployed. And policy referenced by ClusterProfile has not changed since last deployment." @@ -620,6 +657,112 @@ func generateResourceReport(policyHash string, resourceInfo *deployer.ResourceIn return resourceReport } +// evaluateResourceDiff evaluates and returns diff +func evaluateResourceDiff(from, to *unstructured.Unstructured) (string, error) { + objectInfo := fmt.Sprintf("%s %s", from.GroupVersionKind().Kind, from.GetName()) + + // Remove managedFields, status and the hash annotation added by Sveltos. + from = omitManagedFields(from) + from = omitGeneratation(from) + from = omitStatus(from) + from = omitHashAnnotation(from) + + to = omitManagedFields(to) + to = omitGeneratation(to) + to = omitStatus(to) + to = omitHashAnnotation(to) + + fromTempFile, err := os.CreateTemp("", "from-temp-file-") + if err != nil { + return "", err + } + defer os.Remove(fromTempFile.Name()) // Clean up the file after use + fromWriter := io.Writer(fromTempFile) + err = printUnstructured(from, fromWriter) + if err != nil { + return "", err + } + + toTempFile, err := os.CreateTemp("", "to-temp-file-") + if err != nil { + return "", err + } + defer os.Remove(toTempFile.Name()) // Clean up the file after use + toWriter := io.Writer(toTempFile) + err = printUnstructured(to, toWriter) + if err != nil { + return "", err + } + + fromContent, err := os.ReadFile(fromTempFile.Name()) + if err != nil { + return "", err + } + + toContent, err := os.ReadFile(toTempFile.Name()) + if err != nil { + return "", err + } + + edits := myers.ComputeEdits(span.URIFromPath(objectInfo), string(fromContent), string(toContent)) + + diff := fmt.Sprint(gotextdiff.ToUnified(fmt.Sprintf("deployed: %s", objectInfo), + fmt.Sprintf("proposed: %s", objectInfo), string(fromContent), edits)) + + return diff, nil +} + +func omitManagedFields(u *unstructured.Unstructured) *unstructured.Unstructured { + a, err := meta.Accessor(u) + if err != nil { + // The object is not a `metav1.Object`, ignore it. + return u + } + a.SetManagedFields(nil) + return u +} + +func omitGeneratation(u *unstructured.Unstructured) *unstructured.Unstructured { + a, err := meta.Accessor(u) + if err != nil { + // The object is not a `metav1.Object`, ignore it. + return u + } + a.SetGeneration(0) + return u +} + +func omitStatus(u *unstructured.Unstructured) *unstructured.Unstructured { + content := u.UnstructuredContent() + if _, ok := content["status"]; ok { + content["status"] = "" + } + u.SetUnstructuredContent(content) + return u +} + +func omitHashAnnotation(u *unstructured.Unstructured) *unstructured.Unstructured { + annotations := u.GetAnnotations() + if annotations != nil { + delete(annotations, deployer.PolicyHash) + } + u.SetAnnotations(annotations) + return u +} + +// Print the object inside the writer w. +func printUnstructured(obj runtime.Object, w io.Writer) error { + if obj == nil { + return nil + } + data, err := yaml.Marshal(obj) + if err != nil { + return err + } + _, err = w.Write(data) + return err +} + // addExtraLabels adds ExtraLabels to policy. // If policy already has a label with a key present in `ExtraLabels`, the value from `ExtraLabels` will // override the existing value. @@ -811,7 +954,7 @@ func getUnstructured(section []byte, logger logr.Logger) ([]*unstructured.Unstru } policies := make([]*unstructured.Unstructured, 0, len(elements)) for i := range elements { - policy, err := utils.GetUnstructured([]byte(elements[i])) + policy, err := k8s_utils.GetUnstructured([]byte(elements[i])) if err != nil { logger.Error(err, fmt.Sprintf("failed to get policy from Data %.100s", elements[i])) return nil, err @@ -1173,7 +1316,7 @@ func undeployStaleResource(ctx context.Context, isMgmtCluster bool, remoteClient // If this ClusterSummary is the only OwnerReference and it is not deploying this policy anymore, // policy would be withdrawn if clusterSummary.Spec.ClusterProfileSpec.SyncMode == configv1beta1.SyncModeDryRun { - if canDelete(&r, currentPolicies) && deployer.IsOnlyOwnerReference(&r, profile) && + if canDelete(&r, currentPolicies) && k8s_utils.IsOnlyOwnerReference(&r, profile) && !isLeavePolicies(clusterSummary, logger) { resourceReport = &configv1beta1.ResourceReport{ @@ -1187,7 +1330,7 @@ func undeployStaleResource(ctx context.Context, isMgmtCluster bool, remoteClient } else if canDelete(&r, currentPolicies) { logger.V(logs.LogVerbose).Info(fmt.Sprintf("remove owner reference %s/%s", r.GetNamespace(), r.GetName())) - deployer.RemoveOwnerReference(&r, profile) + k8s_utils.RemoveOwnerReference(&r, profile) if len(r.GetOwnerReferences()) != 0 { // Other ClusterSummary are still deploying this very same policy diff --git a/controllers/handlers_utils_test.go b/controllers/handlers_utils_test.go index 5a1fa8bb..f69f8080 100644 --- a/controllers/handlers_utils_test.go +++ b/controllers/handlers_utils_test.go @@ -45,7 +45,7 @@ import ( "github.com/projectsveltos/addon-controller/controllers" libsveltosv1beta1 "github.com/projectsveltos/libsveltos/api/v1beta1" "github.com/projectsveltos/libsveltos/lib/deployer" - "github.com/projectsveltos/libsveltos/lib/utils" + "github.com/projectsveltos/libsveltos/lib/k8s_utils" ) const ( @@ -615,15 +615,16 @@ var _ = Describe("HandlersUtils", func() { It("updateResource does not reset paths in DriftExclusions", func() { depl := fmt.Sprintf(deplTemplate, namespace) - u, err := utils.GetUnstructured([]byte(depl)) + u, err := k8s_utils.GetUnstructured([]byte(depl)) Expect(err).To(BeNil()) - dr, err := utils.GetDynamicResourceInterface(testEnv.Config, u.GroupVersionKind(), u.GetNamespace()) + dr, err := k8s_utils.GetDynamicResourceInterface(testEnv.Config, u.GroupVersionKind(), u.GetNamespace()) Expect(err).To(BeNil()) // following will successfully create deployment - Expect(controllers.UpdateResource(context.TODO(), dr, clusterSummary, u, nil, - textlogger.NewLogger(textlogger.NewConfig()))).To(Succeed()) + _, err = controllers.UpdateResource(context.TODO(), dr, clusterSummary, u, nil, + textlogger.NewLogger(textlogger.NewConfig())) + Expect(err).To(BeNil()) currentDeployment := &appsv1.Deployment{} Eventually(func() bool { @@ -663,8 +664,9 @@ var _ = Describe("HandlersUtils", func() { }, timeout, pollingInterval).Should(BeTrue()) // New deploy will not override replicas - Expect(controllers.UpdateResource(context.TODO(), dr, clusterSummary, u, nil, - textlogger.NewLogger(textlogger.NewConfig()))).To(Succeed()) + _, err = controllers.UpdateResource(context.TODO(), dr, clusterSummary, u, nil, + textlogger.NewLogger(textlogger.NewConfig())) + Expect(err).To(BeNil()) Consistently(func() bool { err := testEnv.Get(context.TODO(), @@ -677,15 +679,16 @@ var _ = Describe("HandlersUtils", func() { It("updateResource: subresources and driftExclusions", func() { depl := fmt.Sprintf(deplTemplate, namespace) - u, err := utils.GetUnstructured([]byte(depl)) + u, err := k8s_utils.GetUnstructured([]byte(depl)) Expect(err).To(BeNil()) - dr, err := utils.GetDynamicResourceInterface(testEnv.Config, u.GroupVersionKind(), u.GetNamespace()) + dr, err := k8s_utils.GetDynamicResourceInterface(testEnv.Config, u.GroupVersionKind(), u.GetNamespace()) Expect(err).To(BeNil()) // following will successfully create deployment - Expect(controllers.UpdateResource(context.TODO(), dr, clusterSummary, u, nil, - textlogger.NewLogger(textlogger.NewConfig()))).To(Succeed()) + _, err = controllers.UpdateResource(context.TODO(), dr, clusterSummary, u, nil, + textlogger.NewLogger(textlogger.NewConfig())) + Expect(err).To(BeNil()) currentDeployment := &appsv1.Deployment{} Eventually(func() bool { @@ -729,12 +732,13 @@ var _ = Describe("HandlersUtils", func() { unavailableReplicas := 2 depl = fmt.Sprintf(deplTemplateWithStatus, namespace, newReplicas, unavailableReplicas, readyReplicas, availableReplicas) - u, err = utils.GetUnstructured([]byte(depl)) + u, err = k8s_utils.GetUnstructured([]byte(depl)) Expect(err).To(BeNil()) // New deploy will not override replicas - Expect(controllers.UpdateResource(context.TODO(), dr, clusterSummary, u, []string{"status"}, - textlogger.NewLogger(textlogger.NewConfig()))).To(Succeed()) + _, err = controllers.UpdateResource(context.TODO(), dr, clusterSummary, u, []string{"status"}, + textlogger.NewLogger(textlogger.NewConfig())) + Expect(err).To(BeNil()) Consistently(func() bool { err := testEnv.Get(context.TODO(), @@ -809,7 +813,7 @@ var _ = Describe("HandlersUtils", func() { elements := strings.Split(services, "---") for i := range elements { var policy *unstructured.Unstructured - policy, err = utils.GetUnstructured([]byte(elements[i])) + policy, err = k8s_utils.GetUnstructured([]byte(elements[i])) Expect(err).To(BeNil()) var policyHash string policyHash, err = controllers.ComputePolicyHash(policy) @@ -839,7 +843,7 @@ var _ = Describe("HandlersUtils", func() { newContent := "" for i := range elements { var policy *unstructured.Unstructured - policy, err = utils.GetUnstructured([]byte(elements[i])) + policy, err = k8s_utils.GetUnstructured([]byte(elements[i])) Expect(err).To(BeNil()) Expect(addTypeInformationToObject(scheme, policy)).To(Succeed()) labels := policy.GetLabels() @@ -969,7 +973,7 @@ var _ = Describe("HandlersUtils", func() { // Create ClusterRole policy in the cluster, pretending it was created because of this ConfigMap and because // of this ClusterSummary (owner is ClusterProfile owning the ClusterSummary) - clusterRole, err := utils.GetUnstructured([]byte(fmt.Sprintf(viewClusterRole, viewClusterRoleName))) + clusterRole, err := k8s_utils.GetUnstructured([]byte(fmt.Sprintf(viewClusterRole, viewClusterRoleName))) Expect(err).To(BeNil()) clusterRole.SetLabels(map[string]string{ deployer.ReferenceKindLabel: string(libsveltosv1beta1.ConfigMapReferencedResourceKind), @@ -1291,7 +1295,7 @@ kind: Deployment metadata: name: test` - u, err := utils.GetUnstructured([]byte(deployment)) + u, err := k8s_utils.GetUnstructured([]byte(deployment)) Expect(err).To(BeNil()) Expect(controllers.AdjustNamespace(u, testEnv.Config)).To(BeNil()) @@ -1304,7 +1308,7 @@ metadata: name: view namespace: cert-manager` - u, err = utils.GetUnstructured([]byte(clusterIssuer)) + u, err = k8s_utils.GetUnstructured([]byte(clusterIssuer)) Expect(err).To(BeNil()) Expect(controllers.AdjustNamespace(u, testEnv.Config)).To(BeNil()) diff --git a/controllers/profile_utils.go b/controllers/profile_utils.go index 27629be9..6eb686b3 100644 --- a/controllers/profile_utils.go +++ b/controllers/profile_utils.go @@ -816,6 +816,10 @@ func createClusterReport(ctx context.Context, c client.Client, profile client.Ob func cleanClusterReports(ctx context.Context, c client.Client, profileScope *scope.ProfileScope) error { listOptions := []client.ListOption{} + if profileScope.IsDryRunSync() { + return nil + } + if profileScope.GetKind() == configv1beta1.ClusterProfileKind { listOptions = append(listOptions, client.MatchingLabels{ClusterProfileLabelName: profileScope.Name()}) } else { @@ -824,18 +828,6 @@ func cleanClusterReports(ctx context.Context, c client.Client, profileScope *sco client.InNamespace(profileScope.Namespace())) } - matchingClusterMap := make(map[string]bool) - - info := func(namespace, clusterReportName string) string { - return fmt.Sprintf("%s--%s", namespace, clusterReportName) - } - - for i := range profileScope.GetStatus().MatchingClusterRefs { - ref := &profileScope.GetStatus().MatchingClusterRefs[i] - matchingClusterMap[info(ref.Namespace, getClusterReportName(profileScope.GetKind(), - profileScope.Name(), ref.Name, clusterproxy.GetClusterType(ref)))] = true - } - clusterReportList := &configv1beta1.ClusterReportList{} err := c.List(ctx, clusterReportList, listOptions...) if err != nil { @@ -845,10 +837,6 @@ func cleanClusterReports(ctx context.Context, c client.Client, profileScope *sco for i := range clusterReportList.Items { cr := &clusterReportList.Items[i] - if _, ok := matchingClusterMap[info(cr.Namespace, cr.Name)]; ok { - continue - } - err = c.Delete(ctx, cr) if err != nil { if !apierrors.IsNotFound(err) { @@ -1071,12 +1059,6 @@ func reconcileNormalCommon(ctx context.Context, c client.Client, profileScope *s return err } - // For Sveltos/Cluster not matching, remove ClusterReports - if err := cleanClusterReports(ctx, c, profileScope); err != nil { - logger.V(logs.LogInfo).Error(err, "failed to clean ClusterConfigurations") - return err - } - return nil } diff --git a/controllers/reloader_utils_test.go b/controllers/reloader_utils_test.go index 90324bcc..099ddb5d 100644 --- a/controllers/reloader_utils_test.go +++ b/controllers/reloader_utils_test.go @@ -35,7 +35,7 @@ import ( "github.com/projectsveltos/addon-controller/controllers" libsveltosv1beta1 "github.com/projectsveltos/libsveltos/api/v1beta1" libsveltoscrd "github.com/projectsveltos/libsveltos/lib/crd" - "github.com/projectsveltos/libsveltos/lib/utils" + "github.com/projectsveltos/libsveltos/lib/k8s_utils" ) var _ = Describe("Reloader utils", func() { @@ -162,7 +162,7 @@ var _ = Describe("Reloader utils", func() { c := fake.NewClientBuilder().WithScheme(scheme).Build() var reloaderCRD *unstructured.Unstructured - reloaderCRD, err := utils.GetUnstructured(libsveltoscrd.GetReloaderCRDYAML()) + reloaderCRD, err := k8s_utils.GetUnstructured(libsveltoscrd.GetReloaderCRDYAML()) Expect(err).To(BeNil()) Expect(c.Create(context.TODO(), reloaderCRD)).To(Succeed()) diff --git a/controllers/resourcesummary.go b/controllers/resourcesummary.go index b4a26d96..409cb578 100644 --- a/controllers/resourcesummary.go +++ b/controllers/resourcesummary.go @@ -37,10 +37,10 @@ import ( libsveltosv1beta1 "github.com/projectsveltos/libsveltos/api/v1beta1" "github.com/projectsveltos/libsveltos/lib/clusterproxy" "github.com/projectsveltos/libsveltos/lib/crd" + "github.com/projectsveltos/libsveltos/lib/k8s_utils" "github.com/projectsveltos/libsveltos/lib/logsettings" logs "github.com/projectsveltos/libsveltos/lib/logsettings" "github.com/projectsveltos/libsveltos/lib/patcher" - "github.com/projectsveltos/libsveltos/lib/utils" ) const ( @@ -149,14 +149,14 @@ func deployDriftDetectionCRDs(ctx context.Context, remoteRestConfig *rest.Config func deployDebuggingConfigurationCRD(ctx context.Context, remoteRestConfig *rest.Config, logger logr.Logger) error { - u, err := utils.GetUnstructured(crd.GetDebuggingConfigurationCRDYAML()) + u, err := k8s_utils.GetUnstructured(crd.GetDebuggingConfigurationCRDYAML()) if err != nil { logger.V(logs.LogInfo).Info( fmt.Sprintf("failed to get DebuggingConfiguration CRD unstructured: %v", err)) return err } - dr, err := utils.GetDynamicResourceInterface(remoteRestConfig, u.GroupVersionKind(), "") + dr, err := k8s_utils.GetDynamicResourceInterface(remoteRestConfig, u.GroupVersionKind(), "") if err != nil { logger.V(logs.LogInfo).Info(fmt.Sprintf("failed to get dynamic client: %v", err)) return err @@ -178,14 +178,14 @@ func deployDebuggingConfigurationCRD(ctx context.Context, remoteRestConfig *rest func deployResourceSummaryCRD(ctx context.Context, remoteRestConfig *rest.Config, logger logr.Logger) error { - rsCRD, err := utils.GetUnstructured(crd.GetResourceSummaryCRDYAML()) + rsCRD, err := k8s_utils.GetUnstructured(crd.GetResourceSummaryCRDYAML()) if err != nil { logger.V(logs.LogInfo).Info( fmt.Sprintf("failed to get ResourceSummary CRD unstructured: %v", err)) return err } - dr, err := utils.GetDynamicResourceInterface(remoteRestConfig, rsCRD.GroupVersionKind(), "") + dr, err := k8s_utils.GetDynamicResourceInterface(remoteRestConfig, rsCRD.GroupVersionKind(), "") if err != nil { logger.V(logs.LogInfo).Info(fmt.Sprintf("failed to get dynamic client: %v", err)) return err @@ -272,7 +272,7 @@ func deployDriftDetectionManagerResources(ctx context.Context, restConfig *rest. return err } for i := range elements { - policy, err := utils.GetUnstructured([]byte(elements[i])) + policy, err := k8s_utils.GetUnstructured([]byte(elements[i])) if err != nil { logger.V(logs.LogInfo).Info(fmt.Sprintf("failed to parse drift detection manager yaml: %v", err)) return err @@ -318,7 +318,7 @@ func deployDriftDetectionManagerPatchedResources(ctx context.Context, restConfig for i := range referencedUnstructured { policy := referencedUnstructured[i] - dr, err := utils.GetDynamicResourceInterface(restConfig, policy.GroupVersionKind(), policy.GetNamespace()) + dr, err := k8s_utils.GetDynamicResourceInterface(restConfig, policy.GroupVersionKind(), policy.GetNamespace()) if err != nil { logger.V(logsettings.LogInfo).Info(fmt.Sprintf("failed to get dynamic client: %v", err)) return err @@ -586,13 +586,13 @@ func removeDriftDetectionManagerFromManagementCluster(ctx context.Context, return err } for i := range elements { - policy, err := utils.GetUnstructured([]byte(elements[i])) + policy, err := k8s_utils.GetUnstructured([]byte(elements[i])) if err != nil { logger.V(logs.LogInfo).Info(fmt.Sprintf("failed to parse drift detection manager yaml: %v", err)) return err } - dr, err := utils.GetDynamicResourceInterface(restConfig, policy.GroupVersionKind(), policy.GetNamespace()) + dr, err := k8s_utils.GetDynamicResourceInterface(restConfig, policy.GroupVersionKind(), policy.GetNamespace()) if err != nil { logger.V(logsettings.LogInfo).Info(fmt.Sprintf("failed to get dynamic client: %v", err)) return err diff --git a/controllers/template_instantiation.go b/controllers/template_instantiation.go index e0305938..b303202a 100644 --- a/controllers/template_instantiation.go +++ b/controllers/template_instantiation.go @@ -34,8 +34,8 @@ import ( libsveltosv1beta1 "github.com/projectsveltos/libsveltos/api/v1beta1" "github.com/projectsveltos/libsveltos/lib/clusterproxy" "github.com/projectsveltos/libsveltos/lib/funcmap" + "github.com/projectsveltos/libsveltos/lib/k8s_utils" logs "github.com/projectsveltos/libsveltos/lib/logsettings" - "github.com/projectsveltos/libsveltos/lib/utils" ) type currentClusterObjects struct { @@ -59,7 +59,7 @@ func fetchResource(ctx context.Context, config *rest.Config, namespace, name, ap Kind: kind, } var dr dynamic.ResourceInterface - dr, err = utils.GetDynamicResourceInterface(config, gvk, namespace) + dr, err = k8s_utils.GetDynamicResourceInterface(config, gvk, namespace) if err != nil { logger.V(logs.LogInfo).Info(fmt.Sprintf("failed to fetch %s: %v", kind, err)) return nil, err diff --git a/controllers/template_instantiation_test.go b/controllers/template_instantiation_test.go index 987e100d..e4b0a753 100644 --- a/controllers/template_instantiation_test.go +++ b/controllers/template_instantiation_test.go @@ -35,7 +35,7 @@ import ( "github.com/projectsveltos/addon-controller/controllers" libsveltosv1beta1 "github.com/projectsveltos/libsveltos/api/v1beta1" - "github.com/projectsveltos/libsveltos/lib/utils" + "github.com/projectsveltos/libsveltos/lib/k8s_utils" ) var _ = Describe("Template instantiation", func() { @@ -181,7 +181,7 @@ var _ = Describe("Template instantiation", func() { Expect(err).To(BeNil()) Expect(result).To(ContainSubstring(fmt.Sprintf("namespace: %s", namespace))) - policy, err := utils.GetUnstructured([]byte(result)) + policy, err := k8s_utils.GetUnstructured([]byte(result)) Expect(err).To(BeNil()) Expect(policy.GetNamespace()).To(Equal(namespace)) }) @@ -370,7 +370,7 @@ var _ = Describe("Template instantiation", func() { Expect(*modifiedDepl.Spec.Replicas).To(Equal(int32(5))) Expect(modifiedDepl.Spec.Paused).To(BeTrue()) - policy, err := utils.GetUnstructured([]byte(result)) + policy, err := k8s_utils.GetUnstructured([]byte(result)) Expect(err).To(BeNil()) Expect(policy.GetNamespace()).To(Equal(namespace)) }) diff --git a/controllers/templateresourcedef_utils.go b/controllers/templateresourcedef_utils.go index 7961b058..92ce0503 100644 --- a/controllers/templateresourcedef_utils.go +++ b/controllers/templateresourcedef_utils.go @@ -28,7 +28,7 @@ import ( configv1beta1 "github.com/projectsveltos/addon-controller/api/v1beta1" "github.com/projectsveltos/libsveltos/lib/funcmap" - "github.com/projectsveltos/libsveltos/lib/utils" + "github.com/projectsveltos/libsveltos/lib/k8s_utils" ) // The TemplateResource namespace can be specified or it will inherit the cluster namespace @@ -104,7 +104,7 @@ func collectTemplateResourceRefs(ctx context.Context, clusterSummary *configv1be return nil, err } - dr, err := utils.GetDynamicResourceInterface(restConfig, ref.Resource.GroupVersionKind(), ref.Resource.Namespace) + dr, err := k8s_utils.GetDynamicResourceInterface(restConfig, ref.Resource.GroupVersionKind(), ref.Resource.Namespace) if err != nil { return nil, err } diff --git a/controllers/utils_test.go b/controllers/utils_test.go index 7c065e4d..1006d612 100644 --- a/controllers/utils_test.go +++ b/controllers/utils_test.go @@ -38,7 +38,7 @@ import ( configv1beta1 "github.com/projectsveltos/addon-controller/api/v1beta1" "github.com/projectsveltos/addon-controller/controllers" libsveltosv1beta1 "github.com/projectsveltos/libsveltos/api/v1beta1" - "github.com/projectsveltos/libsveltos/lib/utils" + "github.com/projectsveltos/libsveltos/lib/k8s_utils" ) const ( @@ -304,13 +304,13 @@ var _ = Describe("getClusterProfileOwner ", func() { }) It("isNamespaced returns true for namespaced resources", func() { - clusterRole, err := utils.GetUnstructured([]byte(fmt.Sprintf(viewClusterRole, randomString()))) + clusterRole, err := k8s_utils.GetUnstructured([]byte(fmt.Sprintf(viewClusterRole, randomString()))) Expect(err).To(BeNil()) isNamespaced, err := controllers.IsNamespaced(clusterRole, testEnv.Config) Expect(err).To(BeNil()) Expect(isNamespaced).To(BeFalse()) - deployment, err := utils.GetUnstructured([]byte(fmt.Sprintf(deplTemplate, randomString()))) + deployment, err := k8s_utils.GetUnstructured([]byte(fmt.Sprintf(deplTemplate, randomString()))) Expect(err).To(BeNil()) isNamespaced, err = controllers.IsNamespaced(deployment, testEnv.Config) Expect(err).To(BeNil()) diff --git a/controllers/validate_health_test.go b/controllers/validate_health_test.go index 85994af8..d8e2dc91 100644 --- a/controllers/validate_health_test.go +++ b/controllers/validate_health_test.go @@ -33,7 +33,7 @@ import ( configv1beta1 "github.com/projectsveltos/addon-controller/api/v1beta1" "github.com/projectsveltos/addon-controller/controllers" libsveltosv1beta1 "github.com/projectsveltos/libsveltos/api/v1beta1" - libsveltosutils "github.com/projectsveltos/libsveltos/lib/utils" + libsveltosutils "github.com/projectsveltos/libsveltos/lib/k8s_utils" ) const ( diff --git a/go.mod b/go.mod index 6f5c11b2..04f7dfe8 100644 --- a/go.mod +++ b/go.mod @@ -13,10 +13,11 @@ require ( github.com/gdexlab/go-render v1.0.1 github.com/go-logr/logr v1.4.2 github.com/google/gofuzz v1.2.0 + github.com/hexops/gotextdiff v1.0.3 github.com/onsi/ginkgo/v2 v2.22.0 github.com/onsi/gomega v1.36.0 github.com/pkg/errors v0.9.1 - github.com/projectsveltos/libsveltos v0.42.1 + github.com/projectsveltos/libsveltos v0.42.1-0.20241129122707-e7c51baedfbd github.com/prometheus/client_golang v1.20.5 github.com/spf13/pflag v1.0.5 github.com/yuin/gopher-lua v1.1.1 diff --git a/go.sum b/go.sum index 10a941a5..7d37c435 100644 --- a/go.sum +++ b/go.sum @@ -221,6 +221,8 @@ github.com/hashicorp/golang-lru/arc/v2 v2.0.5 h1:l2zaLDubNhW4XO3LnliVj0GXO3+/CGN github.com/hashicorp/golang-lru/arc/v2 v2.0.5/go.mod h1:ny6zBSQZi2JxIeYcv7kt2sH2PXJtirBN7RDhRpxPkxU= github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4= github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= @@ -304,8 +306,6 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= -github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/onsi/gomega v1.36.0 h1:Pb12RlruUtj4XUuPUqeEWc6j5DkVVVA49Uf6YLfC95Y= github.com/onsi/gomega v1.36.0/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/opencontainers/go-digest v1.0.1-0.20240426182413-22b78e47854a h1:JgnDqvmVl/kOyC4pEpn2Ra2QtisfpG27Hp+xFKF26AE= @@ -326,8 +326,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 v0.42.1 h1:B7c8cF+vhR5AZJT8D4h19PSJfZqWUCR3CKEDUf032JI= -github.com/projectsveltos/libsveltos v0.42.1/go.mod h1:XPev2TKsMxVG5LwhbbMkcCs/U0730ZMmOXIvu2HEtWo= +github.com/projectsveltos/libsveltos v0.42.1-0.20241129122707-e7c51baedfbd h1:csSkEGSZS8DJ+EioxTdJDC+dDcyvVWzWo8yaWlgBOoc= +github.com/projectsveltos/libsveltos v0.42.1-0.20241129122707-e7c51baedfbd/go.mod h1:4hqQRjwZ/DZ6u0haL3zhdARZ3lpspLv57rkhpXIzbsg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= diff --git a/manifest/manifest.yaml b/manifest/manifest.yaml index aeef13f5..e250b82d 100644 --- a/manifest/manifest.yaml +++ b/manifest/manifest.yaml @@ -4518,6 +4518,10 @@ spec: subresources: status: {} - additionalPrinterColumns: + - description: Time duration since creation of ClusterSummary + jsonPath: .metadata.creationTimestamp + name: Age + type: date - description: Indicates whether HelmCharts are all provisioned jsonPath: .status.featureSummaries[?(@.featureID=="Helm")].status name: HelmCharts