Skip to content

Commit

Permalink
Add owner labels before apply
Browse files Browse the repository at this point in the history
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
  • Loading branch information
stefanprodan committed Sep 3, 2021
1 parent 9d50a5c commit 2ce8667
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 68 deletions.
2 changes: 2 additions & 0 deletions cmd/kustomizer/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
4 changes: 4 additions & 0 deletions cmd/kustomizer/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
26 changes: 15 additions & 11 deletions pkg/manager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
// <owner.group>/name: <name>
// <owner.group>/namespace: <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,
})
}
}
64 changes: 32 additions & 32 deletions pkg/manager/manager_apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand All @@ -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
Expand All @@ -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
}
Expand All @@ -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...)
}
2 changes: 2 additions & 0 deletions pkg/manager/manager_apply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
12 changes: 6 additions & 6 deletions pkg/manager/manager_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
20 changes: 10 additions & 10 deletions pkg/manager/manager_diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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
}
Expand Down Expand Up @@ -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)
}
Expand Down
12 changes: 6 additions & 6 deletions pkg/manager/manager_wait.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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)

Expand Down Expand Up @@ -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
}
Expand Down
3 changes: 0 additions & 3 deletions pkg/manager/owner.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
// <Group>/name: <Name>
// <Group>/namespace: <Namespace>
type Owner struct {
// Field sets the field manager name for the given server-side apply patch.
Field string
Expand Down

0 comments on commit 2ce8667

Please sign in to comment.