diff --git a/cmd/kustomizer/apply.go b/cmd/kustomizer/apply.go index 52cbc70..9385b05 100644 --- a/cmd/kustomizer/apply.go +++ b/cmd/kustomizer/apply.go @@ -99,6 +99,8 @@ func runApplyCmd(cmd *cobra.Command, args []string) error { Group: PROJECT + ".dev", }) + resMgr.SetOwnerLabels(objects, applyArgs.inventoryName, applyArgs.inventoryNamespace) + ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) defer cancel() diff --git a/cmd/kustomizer/diff.go b/cmd/kustomizer/diff.go index 6e0c80c..f0a1ecb 100644 --- a/cmd/kustomizer/diff.go +++ b/cmd/kustomizer/diff.go @@ -89,6 +89,10 @@ func runDiffCmd(cmd *cobra.Command, args []string) error { ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) defer cancel() + if diffArgs.inventoryName != "" { + resMgr.SetOwnerLabels(objects, diffArgs.inventoryName, diffArgs.inventoryNamespace) + } + invalid := false for _, object := range objects { change, err := resMgr.Diff(ctx, object) diff --git a/pkg/manager/manager.go b/pkg/manager/manager.go index f8f8191..a5b350a 100644 --- a/pkg/manager/manager.go +++ b/pkg/manager/manager.go @@ -42,19 +42,23 @@ func NewResourceManager(client client.Client, poller *polling.StatusPoller, owne } // KubeClient returns the underlying controller-runtime client. -func (kc *ResourceManager) KubeClient() client.Client { - return kc.client +func (m *ResourceManager) KubeClient() client.Client { + return m.client } -func (kc *ResourceManager) changeSetEntry(object *unstructured.Unstructured, action Action) *ChangeSetEntry { +func (m *ResourceManager) changeSetEntry(object *unstructured.Unstructured, action Action) *ChangeSetEntry { return &ChangeSetEntry{Subject: objectutil.FmtUnstructured(object), Action: string(action)} } -//func (kc *ResourceManager) SetOwnerLabels(objects []*unstructured.Unstructured, name, namespace string) { -// for _, object := range objects { -// object.SetLabels(map[string]string{ -// kc.fieldOwner + "/name": name, -// kc.fieldOwner + "/namespace": namespace, -// }) -// } -//} +// SetOwnerLabels adds the ownership labels to the given objects. +// The ownership labels are in the format: +// /name: +// /namespace: +func (m *ResourceManager) SetOwnerLabels(objects []*unstructured.Unstructured, name, namespace string) { + for _, object := range objects { + object.SetLabels(map[string]string{ + m.owner.Group + "/name": name, + m.owner.Group + "/namespace": namespace, + }) + } +} diff --git a/pkg/manager/manager_apply.go b/pkg/manager/manager_apply.go index 7b7c174..7636d59 100644 --- a/pkg/manager/manager_apply.go +++ b/pkg/manager/manager_apply.go @@ -32,78 +32,78 @@ import ( // Apply performs a server-side apply of the given object if the matching in-cluster object is different or if it doesn't exist. // Drift detection is performed by comparing the server-side dry-run result with the existing object. // When immutable field changes are detected, the object is recreated if 'force' is set to 'true'. -func (kc *ResourceManager) Apply(ctx context.Context, object *unstructured.Unstructured, force bool) (*ChangeSetEntry, error) { +func (m *ResourceManager) Apply(ctx context.Context, object *unstructured.Unstructured, force bool) (*ChangeSetEntry, error) { existingObject := object.DeepCopy() - _ = kc.client.Get(ctx, client.ObjectKeyFromObject(object), existingObject) + _ = m.client.Get(ctx, client.ObjectKeyFromObject(object), existingObject) dryRunObject := object.DeepCopy() - if err := kc.dryRunApply(ctx, dryRunObject); err != nil { + if err := m.dryRunApply(ctx, dryRunObject); err != nil { if force && strings.Contains(err.Error(), "immutable") { - if err := kc.client.Delete(ctx, existingObject); err != nil { + if err := m.client.Delete(ctx, existingObject); err != nil { return nil, fmt.Errorf("%s immutable field detected, failed to delete object, error: %w", objectutil.FmtUnstructured(dryRunObject), err) } - return kc.Apply(ctx, object, force) + return m.Apply(ctx, object, force) } - return nil, kc.validationError(dryRunObject, err) + return nil, m.validationError(dryRunObject, err) } // do not apply objects that have not drifted to avoid bumping the resource version - if !kc.hasDrifted(existingObject, dryRunObject) { - return kc.changeSetEntry(object, UnchangedAction), nil + if !m.hasDrifted(existingObject, dryRunObject) { + return m.changeSetEntry(object, UnchangedAction), nil } appliedObject := object.DeepCopy() - if err := kc.apply(ctx, appliedObject); err != nil { + if err := m.apply(ctx, appliedObject); err != nil { return nil, fmt.Errorf("%s apply failed, error: %w", objectutil.FmtUnstructured(appliedObject), err) } if dryRunObject.GetResourceVersion() == "" { - return kc.changeSetEntry(appliedObject, CreatedAction), nil + return m.changeSetEntry(appliedObject, CreatedAction), nil } - return kc.changeSetEntry(appliedObject, ConfiguredAction), nil + return m.changeSetEntry(appliedObject, ConfiguredAction), nil } // ApplyAll performs a server-side dry-run of the given objects, and based on the diff result, // it applies the objects that are new or modified. -func (kc *ResourceManager) ApplyAll(ctx context.Context, objects []*unstructured.Unstructured, force bool) (*ChangeSet, error) { +func (m *ResourceManager) ApplyAll(ctx context.Context, objects []*unstructured.Unstructured, force bool) (*ChangeSet, error) { sort.Sort(objectutil.ApplyOrder(objects)) changeSet := NewChangeSet() var toApply []*unstructured.Unstructured for _, object := range objects { existingObject := object.DeepCopy() - _ = kc.client.Get(ctx, client.ObjectKeyFromObject(object), existingObject) + _ = m.client.Get(ctx, client.ObjectKeyFromObject(object), existingObject) dryRunObject := object.DeepCopy() - if err := kc.dryRunApply(ctx, dryRunObject); err != nil { + if err := m.dryRunApply(ctx, dryRunObject); err != nil { if force && strings.Contains(err.Error(), "immutable") { - if err := kc.client.Delete(ctx, existingObject); err != nil { + if err := m.client.Delete(ctx, existingObject); err != nil { return nil, fmt.Errorf("%s immutable field detected, failed to delete object, error: %w", objectutil.FmtUnstructured(dryRunObject), err) } - return kc.ApplyAll(ctx, objects, force) + return m.ApplyAll(ctx, objects, force) } - return nil, kc.validationError(dryRunObject, err) + return nil, m.validationError(dryRunObject, err) } - if kc.hasDrifted(existingObject, dryRunObject) { + if m.hasDrifted(existingObject, dryRunObject) { toApply = append(toApply, object) if dryRunObject.GetResourceVersion() == "" { - changeSet.Add(*kc.changeSetEntry(dryRunObject, CreatedAction)) + changeSet.Add(*m.changeSetEntry(dryRunObject, CreatedAction)) } else { - changeSet.Add(*kc.changeSetEntry(dryRunObject, ConfiguredAction)) + changeSet.Add(*m.changeSetEntry(dryRunObject, ConfiguredAction)) } } else { - changeSet.Add(*kc.changeSetEntry(dryRunObject, UnchangedAction)) + changeSet.Add(*m.changeSetEntry(dryRunObject, UnchangedAction)) } } for _, object := range toApply { appliedObject := object.DeepCopy() - if err := kc.apply(ctx, appliedObject); err != nil { + if err := m.apply(ctx, appliedObject); err != nil { return nil, fmt.Errorf("%s apply failed, error: %w", objectutil.FmtUnstructured(appliedObject), err) } } @@ -115,7 +115,7 @@ func (kc *ResourceManager) ApplyAll(ctx context.Context, objects []*unstructured // waits for CRDs and Namespaces to become ready, then is applies all the other objects. // This function should be used when the given objects have a mix of custom resource definition and custom resources, // or a mix of namespace definitions with namespaced objects. -func (kc *ResourceManager) ApplyAllStaged(ctx context.Context, objects []*unstructured.Unstructured, force bool, wait time.Duration) (*ChangeSet, error) { +func (m *ResourceManager) ApplyAllStaged(ctx context.Context, objects []*unstructured.Unstructured, force bool, wait time.Duration) (*ChangeSet, error) { changeSet := NewChangeSet() // contains only CRDs and Namespaces @@ -133,18 +133,18 @@ func (kc *ResourceManager) ApplyAllStaged(ctx context.Context, objects []*unstru } if len(stageOne) > 0 { - cs, err := kc.ApplyAll(ctx, stageOne, force) + cs, err := m.ApplyAll(ctx, stageOne, force) if err != nil { return nil, err } changeSet.Append(cs.Entries) - if err := kc.Wait(stageOne, 2*time.Second, wait); err != nil { + if err := m.Wait(stageOne, 2*time.Second, wait); err != nil { return nil, err } } - cs, err := kc.ApplyAll(ctx, stageTwo, force) + cs, err := m.ApplyAll(ctx, stageTwo, force) if err != nil { return nil, err } @@ -153,19 +153,19 @@ func (kc *ResourceManager) ApplyAllStaged(ctx context.Context, objects []*unstru return changeSet, nil } -func (kc *ResourceManager) dryRunApply(ctx context.Context, object *unstructured.Unstructured) error { +func (m *ResourceManager) dryRunApply(ctx context.Context, object *unstructured.Unstructured) error { opts := []client.PatchOption{ client.DryRunAll, client.ForceOwnership, - client.FieldOwner(kc.owner.Field), + client.FieldOwner(m.owner.Field), } - return kc.client.Patch(ctx, object, client.Apply, opts...) + return m.client.Patch(ctx, object, client.Apply, opts...) } -func (kc *ResourceManager) apply(ctx context.Context, object *unstructured.Unstructured) error { +func (m *ResourceManager) apply(ctx context.Context, object *unstructured.Unstructured) error { opts := []client.PatchOption{ client.ForceOwnership, - client.FieldOwner(kc.owner.Field), + client.FieldOwner(m.owner.Field), } - return kc.client.Patch(ctx, object, client.Apply, opts...) + return m.client.Patch(ctx, object, client.Apply, opts...) } diff --git a/pkg/manager/manager_apply_test.go b/pkg/manager/manager_apply_test.go index 47aa63d..7809fbb 100644 --- a/pkg/manager/manager_apply_test.go +++ b/pkg/manager/manager_apply_test.go @@ -25,6 +25,8 @@ func TestApply(t *testing.T) { t.Fatal(err) } + manager.SetOwnerLabels(objects, "app1", "default") + configMapName, configMap := getObjectFrom(objects, "ConfigMap", id) secretName, secret := getObjectFrom(objects, "Secret", id) diff --git a/pkg/manager/manager_delete.go b/pkg/manager/manager_delete.go index c4aa4f6..1f0459d 100644 --- a/pkg/manager/manager_delete.go +++ b/pkg/manager/manager_delete.go @@ -28,29 +28,29 @@ import ( ) // Delete deletes the given object (not found errors are ignored). -func (kc *ResourceManager) Delete(ctx context.Context, object *unstructured.Unstructured) (*ChangeSetEntry, error) { +func (m *ResourceManager) Delete(ctx context.Context, object *unstructured.Unstructured) (*ChangeSetEntry, error) { existingObject := object.DeepCopy() - err := kc.client.Get(ctx, client.ObjectKeyFromObject(object), existingObject) + err := m.client.Get(ctx, client.ObjectKeyFromObject(object), existingObject) if err != nil { if !apierrors.IsNotFound(err) { return nil, fmt.Errorf("%s query failed, error: %w", objectutil.FmtUnstructured(object), err) } } else { - if err := kc.client.Delete(ctx, existingObject); err != nil { + if err := m.client.Delete(ctx, existingObject); err != nil { return nil, fmt.Errorf("%s delete failed, error: %w", objectutil.FmtUnstructured(object), err) } } - return kc.changeSetEntry(object, DeletedAction), nil + return m.changeSetEntry(object, DeletedAction), nil } // DeleteAll deletes the given set of objects (not found errors are ignored).. -func (kc *ResourceManager) DeleteAll(ctx context.Context, objects []*unstructured.Unstructured) (*ChangeSet, error) { +func (m *ResourceManager) DeleteAll(ctx context.Context, objects []*unstructured.Unstructured) (*ChangeSet, error) { sort.Sort(sort.Reverse(objectutil.ApplyOrder(objects))) changeSet := NewChangeSet() for _, object := range objects { - cse, err := kc.Delete(ctx, object) + cse, err := m.Delete(ctx, object) if err != nil { return nil, err } diff --git a/pkg/manager/manager_diff.go b/pkg/manager/manager_diff.go index 680cd4c..43d06bc 100644 --- a/pkg/manager/manager_diff.go +++ b/pkg/manager/manager_diff.go @@ -33,21 +33,21 @@ import ( // Diff performs a server-side apply dry-un and returns the fields that changed in YAML format. // If the diff contains Kubernetes Secrets, the data values are masked. -func (kc *ResourceManager) Diff(ctx context.Context, object *unstructured.Unstructured) (*ChangeSetEntry, error) { +func (m *ResourceManager) Diff(ctx context.Context, object *unstructured.Unstructured) (*ChangeSetEntry, error) { existingObject := object.DeepCopy() - _ = kc.client.Get(ctx, client.ObjectKeyFromObject(object), existingObject) + _ = m.client.Get(ctx, client.ObjectKeyFromObject(object), existingObject) dryRunObject := object.DeepCopy() - if err := kc.dryRunApply(ctx, dryRunObject); err != nil { - return nil, kc.validationError(dryRunObject, err) + if err := m.dryRunApply(ctx, dryRunObject); err != nil { + return nil, m.validationError(dryRunObject, err) } if dryRunObject.GetResourceVersion() == "" { - return kc.changeSetEntry(dryRunObject, CreatedAction), nil + return m.changeSetEntry(dryRunObject, CreatedAction), nil } - if kc.hasDrifted(existingObject, dryRunObject) { - cse := kc.changeSetEntry(object, ConfiguredAction) + if m.hasDrifted(existingObject, dryRunObject) { + cse := m.changeSetEntry(object, ConfiguredAction) unstructured.RemoveNestedField(dryRunObject.Object, "metadata", "managedFields") unstructured.RemoveNestedField(existingObject.Object, "metadata", "managedFields") @@ -72,11 +72,11 @@ func (kc *ResourceManager) Diff(ctx context.Context, object *unstructured.Unstru return cse, nil } - return kc.changeSetEntry(dryRunObject, UnchangedAction), nil + return m.changeSetEntry(dryRunObject, UnchangedAction), nil } // hasDrifted detects changes to metadata labels, metadata annotations, spec and webhooks. -func (kc *ResourceManager) hasDrifted(existingObject, dryRunObject *unstructured.Unstructured) bool { +func (m *ResourceManager) hasDrifted(existingObject, dryRunObject *unstructured.Unstructured) bool { if dryRunObject.GetResourceVersion() == "" { return true } @@ -109,7 +109,7 @@ func (kc *ResourceManager) hasDrifted(existingObject, dryRunObject *unstructured // validationError formats the given error and hides sensitive data // if the error was caused by an invalid Kubernetes secrets. -func (kc *ResourceManager) validationError(object *unstructured.Unstructured, err error) error { +func (m *ResourceManager) validationError(object *unstructured.Unstructured, err error) error { if apierrors.IsNotFound(err) { return fmt.Errorf("%s namespace not specified, error: %w", objectutil.FmtUnstructured(object), err) } diff --git a/pkg/manager/manager_wait.go b/pkg/manager/manager_wait.go index e074874..c21bf3a 100644 --- a/pkg/manager/manager_wait.go +++ b/pkg/manager/manager_wait.go @@ -37,7 +37,7 @@ import ( ) // Wait checks if the given set of objects has been fully reconciled. -func (kc *ResourceManager) Wait(objects []*unstructured.Unstructured, interval, timeout time.Duration) error { +func (m *ResourceManager) Wait(objects []*unstructured.Unstructured, interval, timeout time.Duration) error { objectsMeta := object.UnstructuredsToObjMetas(objects) statusCollector := collector.NewResourceStatusCollector(objectsMeta) @@ -48,7 +48,7 @@ func (kc *ResourceManager) Wait(objects []*unstructured.Unstructured, interval, PollInterval: interval, UseCache: true, } - eventsChan := kc.poller.Poll(ctx, objectsMeta, opts) + eventsChan := m.poller.Poll(ctx, objectsMeta, opts) lastStatus := make(map[object.ObjMetadata]*event.ResourceStatus) @@ -103,22 +103,22 @@ func (kc *ResourceManager) Wait(objects []*unstructured.Unstructured, interval, } // WaitForTermination waits for the given objects to be deleted from the cluster. -func (kc *ResourceManager) WaitForTermination(objects []*unstructured.Unstructured, interval, timeout time.Duration) error { +func (m *ResourceManager) WaitForTermination(objects []*unstructured.Unstructured, interval, timeout time.Duration) error { ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() for _, object := range objects { - if err := wait.PollImmediate(interval, timeout, kc.isDeleted(ctx, object)); err != nil { + if err := wait.PollImmediate(interval, timeout, m.isDeleted(ctx, object)); err != nil { return err } } return nil } -func (kc *ResourceManager) isDeleted(ctx context.Context, object *unstructured.Unstructured) wait.ConditionFunc { +func (m *ResourceManager) isDeleted(ctx context.Context, object *unstructured.Unstructured) wait.ConditionFunc { return func() (bool, error) { obj := object.DeepCopy() - err := kc.client.Get(ctx, client.ObjectKeyFromObject(obj), obj) + err := m.client.Get(ctx, client.ObjectKeyFromObject(obj), obj) if apierrors.IsNotFound(err) { return true, nil } diff --git a/pkg/manager/owner.go b/pkg/manager/owner.go index c095962..450b700 100644 --- a/pkg/manager/owner.go +++ b/pkg/manager/owner.go @@ -18,9 +18,6 @@ limitations under the License. package manager // Owner contains options for setting the field manager and ownership labels group. -// The ownership labels are in the format: -// /name: -// /namespace: type Owner struct { // Field sets the field manager name for the given server-side apply patch. Field string