diff --git a/controllers/kustomization_controller.go b/controllers/kustomization_controller.go index 86f8ed74..df7b7f73 100644 --- a/controllers/kustomization_controller.go +++ b/controllers/kustomization_controller.go @@ -63,6 +63,7 @@ import ( kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2" "github.com/fluxcd/kustomize-controller/internal/decryptor" "github.com/fluxcd/kustomize-controller/internal/generator" + "github.com/fluxcd/kustomize-controller/internal/inventory" ) // +kubebuilder:rbac:groups=kustomize.toolkit.fluxcd.io,resources=kustomizations,verbs=get;list;watch;create;update;patch;delete @@ -440,8 +441,8 @@ func (r *KustomizationReconciler) reconcile( } // create an inventory of objects to be reconciled - newInventory := NewInventory() - err = AddObjectsToInventory(newInventory, changeSet) + newInventory := inventory.New() + err = inventory.AddChangeSet(newInventory, changeSet) if err != nil { return kustomizev1.KustomizationNotReady( kustomization, @@ -454,7 +455,7 @@ func (r *KustomizationReconciler) reconcile( // detect stale objects which are subject to garbage collection var staleObjects []*unstructured.Unstructured if oldStatus.Inventory != nil { - diffObjects, err := DiffInventory(oldStatus.Inventory, newInventory) + diffObjects, err := inventory.Diff(oldStatus.Inventory, newInventory) if err != nil { return kustomizev1.KustomizationNotReady( kustomization, @@ -467,7 +468,7 @@ func (r *KustomizationReconciler) reconcile( // TODO: remove this workaround after kustomize-controller 0.18 release // skip objects that were wrongly marked as namespaced // https://github.com/fluxcd/kustomize-controller/issues/466 - newObjects, _ := ListObjectsInInventory(newInventory) + newObjects, _ := inventory.List(newInventory) for _, obj := range diffObjects { preserve := false if obj.GetNamespace() != "" { @@ -846,7 +847,7 @@ func (r *KustomizationReconciler) checkHealth(ctx context.Context, manager *ssa. checkStart := time.Now() var err error if !kustomization.Spec.Wait { - objects, err = referenceToObjMetadataSet(kustomization.Spec.HealthChecks) + objects, err = inventory.ReferenceToObjMetadataSet(kustomization.Spec.HealthChecks) if err != nil { return err } @@ -932,7 +933,7 @@ func (r *KustomizationReconciler) finalize(ctx context.Context, kustomization ku !kustomization.Spec.Suspend && kustomization.Status.Inventory != nil && kustomization.Status.Inventory.Entries != nil { - objects, _ := ListObjectsInInventory(kustomization.Status.Inventory) + objects, _ := inventory.List(kustomization.Status.Inventory) impersonation := runtimeClient.NewImpersonator( r.Client, diff --git a/controllers/kustomization_inventory.go b/internal/inventory/inventory.go similarity index 77% rename from controllers/kustomization_inventory.go rename to internal/inventory/inventory.go index 0978d145..2a01820e 100644 --- a/controllers/kustomization_inventory.go +++ b/internal/inventory/inventory.go @@ -14,28 +14,29 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controllers +package inventory import ( "sort" - "github.com/fluxcd/pkg/apis/meta" - "github.com/fluxcd/pkg/ssa" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/cli-utils/pkg/object" + "github.com/fluxcd/pkg/apis/meta" + "github.com/fluxcd/pkg/ssa" + kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2" ) -func NewInventory() *kustomizev1.ResourceInventory { +func New() *kustomizev1.ResourceInventory { return &kustomizev1.ResourceInventory{ Entries: []kustomizev1.ResourceRef{}, } } -// AddObjectsToInventory extracts the metadata from the given objects and adds it to the inventory. -func AddObjectsToInventory(inv *kustomizev1.ResourceInventory, set *ssa.ChangeSet) error { +// AddChangeSet extracts the metadata from the given objects and adds it to the inventory. +func AddChangeSet(inv *kustomizev1.ResourceInventory, set *ssa.ChangeSet) error { if set == nil { return nil } @@ -50,8 +51,8 @@ func AddObjectsToInventory(inv *kustomizev1.ResourceInventory, set *ssa.ChangeSe return nil } -// ListObjectsInInventory returns the inventory entries as unstructured.Unstructured objects. -func ListObjectsInInventory(inv *kustomizev1.ResourceInventory) ([]*unstructured.Unstructured, error) { +// List returns the inventory entries as unstructured.Unstructured objects. +func List(inv *kustomizev1.ResourceInventory) ([]*unstructured.Unstructured, error) { objects := make([]*unstructured.Unstructured, 0) if inv.Entries == nil { @@ -79,8 +80,8 @@ func ListObjectsInInventory(inv *kustomizev1.ResourceInventory) ([]*unstructured return objects, nil } -// ListMetaInInventory returns the inventory entries as object.ObjMetadata objects. -func ListMetaInInventory(inv *kustomizev1.ResourceInventory) (object.ObjMetadataSet, error) { +// ListMetadata returns the inventory entries as object.ObjMetadata objects. +func ListMetadata(inv *kustomizev1.ResourceInventory) (object.ObjMetadataSet, error) { var metas []object.ObjMetadata for _, e := range inv.Entries { m, err := object.ParseObjMetadata(e.ID) @@ -93,8 +94,8 @@ func ListMetaInInventory(inv *kustomizev1.ResourceInventory) (object.ObjMetadata return metas, nil } -// DiffInventory returns the slice of objects that do not exist in the target inventory. -func DiffInventory(inv *kustomizev1.ResourceInventory, target *kustomizev1.ResourceInventory) ([]*unstructured.Unstructured, error) { +// Diff returns the slice of objects that do not exist in the target inventory. +func Diff(inv *kustomizev1.ResourceInventory, target *kustomizev1.ResourceInventory) ([]*unstructured.Unstructured, error) { versionOf := func(i *kustomizev1.ResourceInventory, objMetadata object.ObjMetadata) string { for _, entry := range i.Entries { if entry.ID == objMetadata.String() { @@ -105,12 +106,12 @@ func DiffInventory(inv *kustomizev1.ResourceInventory, target *kustomizev1.Resou } objects := make([]*unstructured.Unstructured, 0) - aList, err := ListMetaInInventory(inv) + aList, err := ListMetadata(inv) if err != nil { return nil, err } - bList, err := ListMetaInInventory(target) + bList, err := ListMetadata(target) if err != nil { return nil, err } @@ -136,7 +137,8 @@ func DiffInventory(inv *kustomizev1.ResourceInventory, target *kustomizev1.Resou return objects, nil } -func referenceToObjMetadataSet(cr []meta.NamespacedObjectKindReference) (object.ObjMetadataSet, error) { +// ReferenceToObjMetadataSet transforms a NamespacedObjectKindReference to an ObjMetadataSet. +func ReferenceToObjMetadataSet(cr []meta.NamespacedObjectKindReference) (object.ObjMetadataSet, error) { var objects []object.ObjMetadata for _, c := range cr { diff --git a/internal/inventory/inventory_test.go b/internal/inventory/inventory_test.go new file mode 100644 index 00000000..85861317 --- /dev/null +++ b/internal/inventory/inventory_test.go @@ -0,0 +1,93 @@ +/* +Copyright 2022 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package inventory + +import ( + "os" + "strings" + "testing" + + . "github.com/onsi/gomega" + "sigs.k8s.io/cli-utils/pkg/object" + + "github.com/fluxcd/pkg/ssa" +) + +func Test_Inventory(t *testing.T) { + g := NewWithT(t) + + set1, err := readManifest("testdata/inventory1.yaml") + if err != nil { + t.Fatal(err) + } + + inv1 := New() + err = AddChangeSet(inv1, set1) + g.Expect(err).ToNot(HaveOccurred()) + + set2, err := readManifest("testdata/inventory2.yaml") + if err != nil { + t.Fatal(err) + } + + inv2 := New() + err = AddChangeSet(inv2, set2) + g.Expect(err).ToNot(HaveOccurred()) + + t.Run("lists objects in inventory", func(t *testing.T) { + unList, err := List(inv1) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(len(unList)).To(BeIdenticalTo(len(inv1.Entries))) + + mList, err := ListMetadata(inv1) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(len(mList)).To(BeIdenticalTo(len(inv1.Entries))) + }) + + t.Run("diff objects in inventory", func(t *testing.T) { + unList, err := Diff(inv2, inv1) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(len(unList)).To(BeIdenticalTo(1)) + g.Expect(unList[0].GetName()).To(BeIdenticalTo("test2")) + }) +} + +func readManifest(manifest string) (*ssa.ChangeSet, error) { + data, err := os.ReadFile(manifest) + if err != nil { + return nil, err + } + + objects, err := ssa.ReadObjects(strings.NewReader(string(data))) + if err != nil { + return nil, err + } + + cs := ssa.NewChangeSet() + + for _, o := range objects { + cse := ssa.ChangeSetEntry{ + ObjMetadata: object.UnstructuredToObjMetadata(o), + GroupVersion: o.GroupVersionKind().Version, + Subject: ssa.FmtUnstructured(o), + Action: string(ssa.CreatedAction), + } + cs.Add(cse) + } + + return cs, nil +} diff --git a/internal/inventory/testdata/inventory1.yaml b/internal/inventory/testdata/inventory1.yaml new file mode 100644 index 00000000..da27cacc --- /dev/null +++ b/internal/inventory/testdata/inventory1.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: test +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test1 + namespace: test +data: + key: value1 diff --git a/internal/inventory/testdata/inventory2.yaml b/internal/inventory/testdata/inventory2.yaml new file mode 100644 index 00000000..9289d022 --- /dev/null +++ b/internal/inventory/testdata/inventory2.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: test +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test1 + namespace: test +data: + key: value1 +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test2 + namespace: test +data: + key: value2