diff --git a/pkg/controllers/deliverable_reconciler.go b/pkg/controllers/deliverable_reconciler.go index 2475bcbed..026cb4964 100644 --- a/pkg/controllers/deliverable_reconciler.go +++ b/pkg/controllers/deliverable_reconciler.go @@ -37,10 +37,11 @@ import ( cerrors "github.com/vmware-tanzu/cartographer/pkg/errors" "github.com/vmware-tanzu/cartographer/pkg/logger" "github.com/vmware-tanzu/cartographer/pkg/mapper" + "github.com/vmware-tanzu/cartographer/pkg/realizer" realizerclient "github.com/vmware-tanzu/cartographer/pkg/realizer/client" - realizer "github.com/vmware-tanzu/cartographer/pkg/realizer/deliverable" "github.com/vmware-tanzu/cartographer/pkg/realizer/statuses" "github.com/vmware-tanzu/cartographer/pkg/repository" + "github.com/vmware-tanzu/cartographer/pkg/templates" "github.com/vmware-tanzu/cartographer/pkg/tracker/dependency" "github.com/vmware-tanzu/cartographer/pkg/tracker/stamped" "github.com/vmware-tanzu/cartographer/pkg/utils" @@ -115,14 +116,15 @@ func (r *DeliverableReconciler) Reconcile(ctx context.Context, req ctrl.Request) return r.completeReconciliation(ctx, deliverable, nil, fmt.Errorf("failed to get secret for service account [%s]: %w", fmt.Sprintf("%s/%s", serviceAccountNS, serviceAccountName), err)) } - resourceRealizer, err := r.ResourceRealizerBuilder(secret, deliverable, r.Repo, delivery.Spec.Params) + resourceRealizer, err := r.ResourceRealizerBuilder(secret, deliverable, deliverable.Spec.Params, r.Repo, delivery.Spec.Params, buildDeliverableResourceLabeler(deliverable, delivery)) + if err != nil { r.conditionManager.AddPositive(conditions.ResourceRealizerBuilderErrorCondition(err)) return r.completeReconciliation(ctx, deliverable, nil, cerrors.NewUnhandledError(fmt.Errorf("failed to build resource realizer: %w", err))) } resourceStatuses := statuses.NewResourceStatuses(deliverable.Status.Resources, conditions.AddConditionForResourceSubmittedDeliverable) - err = r.Realizer.Realize(ctx, resourceRealizer, delivery, resourceStatuses) + err = r.Realizer.Realize(ctx, resourceRealizer, delivery.Name, realizer.MakeDeliveryOwnerResources(delivery), resourceStatuses) if err != nil { conditions.AddConditionForResourceSubmittedDeliverable(&r.conditionManager, true, err) @@ -208,6 +210,19 @@ func (r *DeliverableReconciler) isDeliveryReady(delivery *v1alpha1.ClusterDelive return readyCondition.Status == "True" } +func buildDeliverableResourceLabeler(owner, blueprint client.Object) realizer.ResourceLabeler { + return func(resource realizer.OwnerResource) templates.Labels { + return templates.Labels{ + "carto.run/deliverable-name": owner.GetName(), + "carto.run/deliverable-namespace": owner.GetNamespace(), + "carto.run/delivery-name": blueprint.GetName(), + "carto.run/resource-name": resource.Name, + "carto.run/template-kind": resource.TemplateRef.Kind, + "carto.run/cluster-template-name": resource.TemplateRef.Name, + } + } +} + func (r *DeliverableReconciler) trackDependencies(deliverable *v1alpha1.Deliverable, realizedResources []v1alpha1.ResourceStatus, serviceAccountName, serviceAccountNS string) { r.DependencyTracker.ClearTracked(types.NamespacedName{ Namespace: deliverable.Namespace, diff --git a/pkg/controllers/deliverable_reconciler_test.go b/pkg/controllers/deliverable_reconciler_test.go index e77b0d2dc..b1cdd9d0b 100644 --- a/pkg/controllers/deliverable_reconciler_test.go +++ b/pkg/controllers/deliverable_reconciler_test.go @@ -33,6 +33,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log/zap" @@ -41,8 +42,8 @@ import ( "github.com/vmware-tanzu/cartographer/pkg/conditions/conditionsfakes" "github.com/vmware-tanzu/cartographer/pkg/controllers" cerrors "github.com/vmware-tanzu/cartographer/pkg/errors" - realizer "github.com/vmware-tanzu/cartographer/pkg/realizer/deliverable" - "github.com/vmware-tanzu/cartographer/pkg/realizer/deliverable/deliverablefakes" + "github.com/vmware-tanzu/cartographer/pkg/realizer" + "github.com/vmware-tanzu/cartographer/pkg/realizer/realizerfakes" "github.com/vmware-tanzu/cartographer/pkg/realizer/statuses" "github.com/vmware-tanzu/cartographer/pkg/repository" "github.com/vmware-tanzu/cartographer/pkg/repository/repositoryfakes" @@ -60,17 +61,18 @@ var _ = Describe("DeliverableReconciler", func() { req ctrl.Request repo *repositoryfakes.FakeRepository conditionManager *conditionsfakes.FakeConditionManager - rlzr *deliverablefakes.FakeRealizer + rlzr *realizerfakes.FakeRealizer dl *v1alpha1.Deliverable deliverableLabels map[string]string stampedTracker *stampedfakes.FakeStampedTracker dependencyTracker *dependencyfakes.FakeDependencyTracker - builtResourceRealizer *deliverablefakes.FakeResourceRealizer - resourceRealizerSecret *corev1.Secret - serviceAccountSecret *corev1.Secret - serviceAccountName string - resourceRealizerBuilderError error + builtResourceRealizer *realizerfakes.FakeResourceRealizer + labelerForBuiltResourceRealizer realizer.ResourceLabeler + resourceRealizerSecret *corev1.Secret + serviceAccountSecret *corev1.Secret + serviceAccountName string + resourceRealizerBuilderError error ) BeforeEach(func() { @@ -84,7 +86,7 @@ var _ = Describe("DeliverableReconciler", func() { return conditionManager } - rlzr = &deliverablefakes.FakeRealizer{} + rlzr = &realizerfakes.FakeRealizer{} rlzr.RealizeReturns(nil) stampedTracker = &stampedfakes.FakeStampedTracker{} @@ -104,12 +106,13 @@ var _ = Describe("DeliverableReconciler", func() { repo.GetServiceAccountSecretReturns(serviceAccountSecret, nil) resourceRealizerBuilderError = nil - resourceRealizerBuilder := func(secret *corev1.Secret, deliverable *v1alpha1.Deliverable, systemRepo repository.Repository, deliveryParams []v1alpha1.BlueprintParam) (realizer.ResourceRealizer, error) { + + resourceRealizerBuilder := func(secret *corev1.Secret, owner client.Object, ownerParams []v1alpha1.OwnerParam, systemRepo repository.Repository, blueprintParams []v1alpha1.BlueprintParam, resourceLabeler realizer.ResourceLabeler) (realizer.ResourceRealizer, error) { + labelerForBuiltResourceRealizer = resourceLabeler if resourceRealizerBuilderError != nil { return nil, resourceRealizerBuilderError } resourceRealizerSecret = secret - builtResourceRealizer = &deliverablefakes.FakeResourceRealizer{} return builtResourceRealizer, nil } @@ -268,7 +271,7 @@ var _ = Describe("DeliverableReconciler", func() { }, }, nil) - rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, delivery *v1alpha1.ClusterDelivery, statuses statuses.ResourceStatuses) error { + rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, deliveryName string, resources []realizer.OwnerResource, statuses statuses.ResourceStatuses) error { statusesVal := reflect.ValueOf(statuses) existingVal := reflect.ValueOf(resourceStatuses) @@ -277,7 +280,30 @@ var _ = Describe("DeliverableReconciler", func() { } }) - It("updates the status of the deliverable with the realizedResources", func() { + It("labels owner resources", func() { + _, _ = reconciler.Reconcile(ctx, req) + Expect(labelerForBuiltResourceRealizer).To(Not(BeNil())) + + resource := realizer.OwnerResource{ + TemplateRef: v1alpha1.TemplateReference{ + Kind: "be-kind", + Name: "no-names", + }, + Name: "fine-i-have-a-name", + } + + labels := labelerForBuiltResourceRealizer(resource) + Expect(labels).To(Equal(templates.Labels{ + "carto.run/deliverable-name": "my-deliverable", + "carto.run/deliverable-namespace": "my-namespace", + "carto.run/delivery-name": "some-delivery", + "carto.run/resource-name": resource.Name, + "carto.run/template-kind": resource.TemplateRef.Kind, + "carto.run/cluster-template-name": resource.TemplateRef.Name, + })) + }) + + It("updates the status of the owner with the realizedResources", func() { _, _ = reconciler.Reconcile(ctx, req) Expect(repo.StatusUpdateCallCount()).To(Equal(1)) @@ -317,7 +343,7 @@ var _ = Describe("DeliverableReconciler", func() { _, _ = reconciler.Reconcile(ctx, req) Expect(rlzr.RealizeCallCount()).To(Equal(1)) - _, resourceRealizer, _, _ := rlzr.RealizeArgsForCall(0) + _, resourceRealizer, _, _, _ := rlzr.RealizeArgsForCall(0) Expect(resourceRealizer).To(Equal(builtResourceRealizer)) }) @@ -585,7 +611,7 @@ var _ = Describe("DeliverableReconciler", func() { }, nil, ) - rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, delivery *v1alpha1.ClusterDelivery, statuses statuses.ResourceStatuses) error { + rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, deliveryName string, resources []realizer.OwnerResource, statuses statuses.ResourceStatuses) error { statusesVal := reflect.ValueOf(statuses) existingVal := reflect.ValueOf(resourceStatuses) @@ -644,7 +670,7 @@ var _ = Describe("DeliverableReconciler", func() { StampedObject: &unstructured.Unstructured{}, ResourceName: "some-name", } - rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, delivery *v1alpha1.ClusterDelivery, statuses statuses.ResourceStatuses) error { + rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, deliveryName string, resources []realizer.OwnerResource, statuses statuses.ResourceStatuses) error { statusesVal := reflect.ValueOf(statuses) existingVal := reflect.ValueOf(resourceStatuses) @@ -699,7 +725,7 @@ var _ = Describe("DeliverableReconciler", func() { BlueprintType: cerrors.Delivery, } - rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, delivery *v1alpha1.ClusterDelivery, statuses statuses.ResourceStatuses) error { + rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, deliveryName string, resources []realizer.OwnerResource, statuses statuses.ResourceStatuses) error { statusesVal := reflect.ValueOf(statuses) existingVal := reflect.ValueOf(resourceStatuses) @@ -759,7 +785,7 @@ var _ = Describe("DeliverableReconciler", func() { StampedObject: stampedObject, } - rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, delivery *v1alpha1.ClusterDelivery, statuses statuses.ResourceStatuses) error { + rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, deliveryName string, resources []realizer.OwnerResource, statuses statuses.ResourceStatuses) error { statusesVal := reflect.ValueOf(statuses) existingVal := reflect.ValueOf(resourceStatuses) @@ -1076,7 +1102,7 @@ var _ = Describe("DeliverableReconciler", func() { var realizerError error BeforeEach(func() { realizerError = errors.New("some error") - rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, delivery *v1alpha1.ClusterDelivery, statuses statuses.ResourceStatuses) error { + rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, deliveryName string, resources []realizer.OwnerResource, statuses statuses.ResourceStatuses) error { statusesVal := reflect.ValueOf(statuses) existingVal := reflect.ValueOf(resourceStatuses) @@ -1341,7 +1367,7 @@ var _ = Describe("DeliverableReconciler", func() { }, }, nil, ) - rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, delivery *v1alpha1.ClusterDelivery, statuses statuses.ResourceStatuses) error { + rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, deliveryName string, resources []realizer.OwnerResource, statuses statuses.ResourceStatuses) error { statusesVal := reflect.ValueOf(statuses) existingVal := reflect.ValueOf(resourceStatuses) diff --git a/pkg/controllers/workload_reconciler.go b/pkg/controllers/workload_reconciler.go index d0b4a2c6e..ec30f0de1 100644 --- a/pkg/controllers/workload_reconciler.go +++ b/pkg/controllers/workload_reconciler.go @@ -37,10 +37,11 @@ import ( cerrors "github.com/vmware-tanzu/cartographer/pkg/errors" "github.com/vmware-tanzu/cartographer/pkg/logger" "github.com/vmware-tanzu/cartographer/pkg/mapper" + "github.com/vmware-tanzu/cartographer/pkg/realizer" realizerclient "github.com/vmware-tanzu/cartographer/pkg/realizer/client" "github.com/vmware-tanzu/cartographer/pkg/realizer/statuses" - realizer "github.com/vmware-tanzu/cartographer/pkg/realizer/workload" "github.com/vmware-tanzu/cartographer/pkg/repository" + "github.com/vmware-tanzu/cartographer/pkg/templates" "github.com/vmware-tanzu/cartographer/pkg/tracker/dependency" "github.com/vmware-tanzu/cartographer/pkg/tracker/stamped" "github.com/vmware-tanzu/cartographer/pkg/utils" @@ -117,7 +118,8 @@ func (r *WorkloadReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c return r.completeReconciliation(ctx, workload, nil, fmt.Errorf("failed to get service account secret [%s]: %w", fmt.Sprintf("%s/%s", serviceAccountNS, serviceAccountName), err)) } - resourceRealizer, err := r.ResourceRealizerBuilder(secret, workload, r.Repo, supplyChain.Spec.Params) + resourceRealizer, err := r.ResourceRealizerBuilder(secret, workload, workload.Spec.Params, r.Repo, supplyChain.Spec.Params, buildWorkloadResourceLabeler(workload, supplyChain)) + if err != nil { r.conditionManager.AddPositive(conditions.ResourceRealizerBuilderErrorCondition(err)) log.Error(err, "failed to build resource realizer") @@ -126,7 +128,7 @@ func (r *WorkloadReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c } resourceStatuses := statuses.NewResourceStatuses(workload.Status.Resources, conditions.AddConditionForResourceSubmittedWorkload) - err = r.Realizer.Realize(ctx, resourceRealizer, supplyChain, resourceStatuses) + err = r.Realizer.Realize(ctx, resourceRealizer, supplyChain.Name, realizer.MakeSupplychainOwnerResources(supplyChain), resourceStatuses) if err != nil { conditions.AddConditionForResourceSubmittedWorkload(&r.conditionManager, true, err) @@ -212,6 +214,19 @@ func (r *WorkloadReconciler) isSupplyChainReady(supplyChain *v1alpha1.ClusterSup return supplyChainReadyCondition.Status == "True" } +func buildWorkloadResourceLabeler(owner, blueprint client.Object) realizer.ResourceLabeler { + return func(resource realizer.OwnerResource) templates.Labels { + return templates.Labels{ + "carto.run/workload-name": owner.GetName(), + "carto.run/workload-namespace": owner.GetNamespace(), + "carto.run/supply-chain-name": blueprint.GetName(), + "carto.run/resource-name": resource.Name, + "carto.run/template-kind": resource.TemplateRef.Kind, + "carto.run/cluster-template-name": resource.TemplateRef.Name, + } + } +} + func getSupplyChainReadyCondition(supplyChain *v1alpha1.ClusterSupplyChain) metav1.Condition { for _, condition := range supplyChain.Status.Conditions { if condition.Type == "Ready" { diff --git a/pkg/controllers/workload_reconciler_test.go b/pkg/controllers/workload_reconciler_test.go index 5becbff5e..83366ea55 100644 --- a/pkg/controllers/workload_reconciler_test.go +++ b/pkg/controllers/workload_reconciler_test.go @@ -33,6 +33,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log/zap" @@ -41,9 +42,9 @@ import ( "github.com/vmware-tanzu/cartographer/pkg/conditions/conditionsfakes" "github.com/vmware-tanzu/cartographer/pkg/controllers" cerrors "github.com/vmware-tanzu/cartographer/pkg/errors" + "github.com/vmware-tanzu/cartographer/pkg/realizer" + "github.com/vmware-tanzu/cartographer/pkg/realizer/realizerfakes" "github.com/vmware-tanzu/cartographer/pkg/realizer/statuses" - realizer "github.com/vmware-tanzu/cartographer/pkg/realizer/workload" - "github.com/vmware-tanzu/cartographer/pkg/realizer/workload/workloadfakes" "github.com/vmware-tanzu/cartographer/pkg/repository" "github.com/vmware-tanzu/cartographer/pkg/repository/repositoryfakes" "github.com/vmware-tanzu/cartographer/pkg/templates" @@ -54,22 +55,23 @@ import ( var _ = Describe("WorkloadReconciler", func() { var ( - out *Buffer - reconciler controllers.WorkloadReconciler - ctx context.Context - req ctrl.Request - repo *repositoryfakes.FakeRepository - conditionManager *conditionsfakes.FakeConditionManager - rlzr *workloadfakes.FakeRealizer - wl *v1alpha1.Workload - workloadLabels map[string]string - stampedTracker *stampedfakes.FakeStampedTracker - dependencyTracker *dependencyfakes.FakeDependencyTracker - builtResourceRealizer *workloadfakes.FakeResourceRealizer - resourceRealizerSecret *corev1.Secret - serviceAccountSecret *corev1.Secret - serviceAccountName string - resourceRealizerBuilderError error + out *Buffer + reconciler controllers.WorkloadReconciler + ctx context.Context + req ctrl.Request + repo *repositoryfakes.FakeRepository + conditionManager *conditionsfakes.FakeConditionManager + rlzr *realizerfakes.FakeRealizer + wl *v1alpha1.Workload + workloadLabels map[string]string + stampedTracker *stampedfakes.FakeStampedTracker + dependencyTracker *dependencyfakes.FakeDependencyTracker + builtResourceRealizer *realizerfakes.FakeResourceRealizer + labelerForBuiltResourceRealizer realizer.ResourceLabeler + resourceRealizerSecret *corev1.Secret + serviceAccountSecret *corev1.Secret + serviceAccountName string + resourceRealizerBuilderError error ) BeforeEach(func() { @@ -83,7 +85,7 @@ var _ = Describe("WorkloadReconciler", func() { return conditionManager } - rlzr = &workloadfakes.FakeRealizer{} + rlzr = &realizerfakes.FakeRealizer{} rlzr.RealizeReturns(nil) stampedTracker = &stampedfakes.FakeStampedTracker{} @@ -101,12 +103,14 @@ var _ = Describe("WorkloadReconciler", func() { repo.GetServiceAccountSecretReturns(serviceAccountSecret, nil) resourceRealizerBuilderError = nil - resourceRealizerBuilder := func(secret *corev1.Secret, workload *v1alpha1.Workload, systemRepo repository.Repository, supplyChainParams []v1alpha1.BlueprintParam) (realizer.ResourceRealizer, error) { + + resourceRealizerBuilder := func(secret *corev1.Secret, owner client.Object, ownerParams []v1alpha1.OwnerParam, systemRepo repository.Repository, blueprintParams []v1alpha1.BlueprintParam, resourceLabeler realizer.ResourceLabeler) (realizer.ResourceRealizer, error) { + labelerForBuiltResourceRealizer = resourceLabeler if resourceRealizerBuilderError != nil { return nil, resourceRealizerBuilderError } resourceRealizerSecret = secret - builtResourceRealizer = &workloadfakes.FakeResourceRealizer{} + builtResourceRealizer = &realizerfakes.FakeResourceRealizer{} return builtResourceRealizer, nil } @@ -265,7 +269,7 @@ var _ = Describe("WorkloadReconciler", func() { }, nil, ) - rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, chain *v1alpha1.ClusterSupplyChain, statuses statuses.ResourceStatuses) error { + rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, deliveryName string, resources []realizer.OwnerResource, statuses statuses.ResourceStatuses) error { statusesVal := reflect.ValueOf(statuses) existingVal := reflect.ValueOf(resourceStatuses) @@ -274,6 +278,29 @@ var _ = Describe("WorkloadReconciler", func() { } }) + It("labels owner resources", func() { + _, _ = reconciler.Reconcile(ctx, req) + Expect(labelerForBuiltResourceRealizer).To(Not(BeNil())) + + resource := realizer.OwnerResource{ + TemplateRef: v1alpha1.TemplateReference{ + Kind: "be-kind", + Name: "no-names", + }, + Name: "fine-i-have-a-name", + } + + labels := labelerForBuiltResourceRealizer(resource) + Expect(labels).To(Equal(templates.Labels{ + "carto.run/workload-name": "my-workload-name", + "carto.run/workload-namespace": "my-namespace", + "carto.run/supply-chain-name": "some-supply-chain", + "carto.run/resource-name": resource.Name, + "carto.run/template-kind": resource.TemplateRef.Kind, + "carto.run/cluster-template-name": resource.TemplateRef.Name, + })) + }) + It("updates the status of the workload with the realizedResources", func() { _, _ = reconciler.Reconcile(ctx, req) @@ -314,7 +341,7 @@ var _ = Describe("WorkloadReconciler", func() { _, _ = reconciler.Reconcile(ctx, req) Expect(rlzr.RealizeCallCount()).To(Equal(1)) - _, resourceRealizer, _, _ := rlzr.RealizeArgsForCall(0) + _, resourceRealizer, _, _, _ := rlzr.RealizeArgsForCall(0) Expect(resourceRealizer).To(Equal(builtResourceRealizer)) }) @@ -581,7 +608,7 @@ var _ = Describe("WorkloadReconciler", func() { }, nil, ) - rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, chain *v1alpha1.ClusterSupplyChain, statuses statuses.ResourceStatuses) error { + rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, deliveryName string, resources []realizer.OwnerResource, statuses statuses.ResourceStatuses) error { statusesVal := reflect.ValueOf(statuses) existingVal := reflect.ValueOf(resourceStatuses) @@ -647,7 +674,7 @@ var _ = Describe("WorkloadReconciler", func() { StampedObject: &unstructured.Unstructured{}, ResourceName: "some-name", } - rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, chain *v1alpha1.ClusterSupplyChain, statuses statuses.ResourceStatuses) error { + rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, deliveryName string, resources []realizer.OwnerResource, statuses statuses.ResourceStatuses) error { statusesVal := reflect.ValueOf(statuses) existingVal := reflect.ValueOf(resourceStatuses) @@ -711,7 +738,7 @@ var _ = Describe("WorkloadReconciler", func() { BlueprintType: cerrors.SupplyChain, } - rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, chain *v1alpha1.ClusterSupplyChain, statuses statuses.ResourceStatuses) error { + rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, deliveryName string, resources []realizer.OwnerResource, statuses statuses.ResourceStatuses) error { statusesVal := reflect.ValueOf(statuses) existingVal := reflect.ValueOf(resourceStatuses) @@ -777,7 +804,7 @@ var _ = Describe("WorkloadReconciler", func() { BlueprintName: supplyChainName, BlueprintType: cerrors.SupplyChain, } - rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, chain *v1alpha1.ClusterSupplyChain, statuses statuses.ResourceStatuses) error { + rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, deliveryName string, resources []realizer.OwnerResource, statuses statuses.ResourceStatuses) error { statusesVal := reflect.ValueOf(statuses) existingVal := reflect.ValueOf(resourceStatuses) @@ -936,7 +963,7 @@ var _ = Describe("WorkloadReconciler", func() { var realizerError error BeforeEach(func() { realizerError = errors.New("some error") - rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, chain *v1alpha1.ClusterSupplyChain, statuses statuses.ResourceStatuses) error { + rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, deliveryName string, resources []realizer.OwnerResource, statuses statuses.ResourceStatuses) error { statusesVal := reflect.ValueOf(statuses) existingVal := reflect.ValueOf(resourceStatuses) @@ -1196,7 +1223,7 @@ var _ = Describe("WorkloadReconciler", func() { }, }, nil, ) - rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, chain *v1alpha1.ClusterSupplyChain, statuses statuses.ResourceStatuses) error { + rlzr.RealizeStub = func(ctx context.Context, resourceRealizer realizer.ResourceRealizer, deliveryName string, resources []realizer.OwnerResource, statuses statuses.ResourceStatuses) error { statusesVal := reflect.ValueOf(statuses) existingVal := reflect.ValueOf(resourceStatuses) diff --git a/pkg/realizer/workload/component.go b/pkg/realizer/component.go similarity index 56% rename from pkg/realizer/workload/component.go rename to pkg/realizer/component.go index 16e2a5ed3..c7ecf8ac3 100644 --- a/pkg/realizer/workload/component.go +++ b/pkg/realizer/component.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package workload +package realizer import ( "context" @@ -21,6 +21,7 @@ import ( "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/controller-runtime/pkg/client" "github.com/vmware-tanzu/cartographer/pkg/apis/v1alpha1" "github.com/vmware-tanzu/cartographer/pkg/errors" @@ -31,49 +32,64 @@ import ( "github.com/vmware-tanzu/cartographer/pkg/templates" ) -//go:generate go run -modfile ../../../hack/tools/go.mod github.com/maxbrunsfeld/counterfeiter/v6 -generate +//go:generate go run -modfile ../../hack/tools/go.mod github.com/maxbrunsfeld/counterfeiter/v6 -generate + +type OwnerResource struct { + TemplateRef v1alpha1.TemplateReference + TemplateOptions []v1alpha1.TemplateOption + Params []v1alpha1.BlueprintParam + Name string + Sources []v1alpha1.ResourceReference + Images []v1alpha1.ResourceReference + Configs []v1alpha1.ResourceReference + Deployment *v1alpha1.DeploymentReference +} //counterfeiter:generate . ResourceRealizer type ResourceRealizer interface { - Do(ctx context.Context, resource *v1alpha1.SupplyChainResource, supplyChainName string, outputs Outputs) (templates.Template, *unstructured.Unstructured, *templates.Output, error) + Do(ctx context.Context, resource OwnerResource, blueprintName string, outputs Outputs) (templates.Template, *unstructured.Unstructured, *templates.Output, error) } type resourceRealizer struct { - workload *v1alpha1.Workload - systemRepo repository.Repository - workloadRepo repository.Repository - supplyChainParams []v1alpha1.BlueprintParam + owner client.Object + ownerParams []v1alpha1.OwnerParam + systemRepo repository.Repository + ownerRepo repository.Repository + blueprintParams []v1alpha1.BlueprintParam + resourceLabeler ResourceLabeler } -type ResourceRealizerBuilder func(secret *corev1.Secret, workload *v1alpha1.Workload, systemRepo repository.Repository, supplyChainParams []v1alpha1.BlueprintParam) (ResourceRealizer, error) +type ResourceRealizerBuilder func(secret *corev1.Secret, owner client.Object, ownerParams []v1alpha1.OwnerParam, systemRepo repository.Repository, blueprintParams []v1alpha1.BlueprintParam, resourceLabeler ResourceLabeler) (ResourceRealizer, error) //counterfeiter:generate sigs.k8s.io/controller-runtime/pkg/client.Client func NewResourceRealizerBuilder(repositoryBuilder repository.RepositoryBuilder, clientBuilder realizerclient.ClientBuilder, cache repository.RepoCache) ResourceRealizerBuilder { - return func(secret *corev1.Secret, workload *v1alpha1.Workload, systemRepo repository.Repository, supplyChainParams []v1alpha1.BlueprintParam) (ResourceRealizer, error) { - workloadClient, _, err := clientBuilder(secret, false) + return func(secret *corev1.Secret, owner client.Object, ownerParams []v1alpha1.OwnerParam, systemRepo repository.Repository, supplyChainParams []v1alpha1.BlueprintParam, resourceLabeler ResourceLabeler) (ResourceRealizer, error) { + ownerClient, _, err := clientBuilder(secret, false) if err != nil { return nil, fmt.Errorf("can't build client: %w", err) } - workloadRepo := repositoryBuilder(workloadClient, cache) + ownerRepo := repositoryBuilder(ownerClient, cache) return &resourceRealizer{ - workload: workload, - systemRepo: systemRepo, - workloadRepo: workloadRepo, - supplyChainParams: supplyChainParams, + owner: owner, + ownerParams: ownerParams, + systemRepo: systemRepo, + ownerRepo: ownerRepo, + blueprintParams: supplyChainParams, + resourceLabeler: resourceLabeler, }, nil } } -func (r *resourceRealizer) Do(ctx context.Context, resource *v1alpha1.SupplyChainResource, supplyChainName string, outputs Outputs) (templates.Template, *unstructured.Unstructured, *templates.Output, error) { +func (r *resourceRealizer) Do(ctx context.Context, resource OwnerResource, blueprintName string, outputs Outputs) (templates.Template, *unstructured.Unstructured, *templates.Output, error) { log := logr.FromContextOrDiscard(ctx).WithValues("template", resource.TemplateRef) ctx = logr.NewContext(ctx, log) var templateName string var err error - if len(resource.TemplateRef.Options) > 0 { - templateName, err = r.findMatchingTemplateName(resource, supplyChainName) + if len(resource.TemplateOptions) > 0 { + templateName, err = r.findMatchingTemplateName(resource, blueprintName) if err != nil { return nil, nil, nil, err } @@ -90,7 +106,7 @@ func (r *resourceRealizer) Do(ctx context.Context, resource *v1alpha1.SupplyChai Err: err, ResourceName: resource.Name, TemplateName: templateName, - BlueprintName: supplyChainName, + BlueprintName: blueprintName, BlueprintType: errors.SupplyChain, } } @@ -101,59 +117,55 @@ func (r *resourceRealizer) Do(ctx context.Context, resource *v1alpha1.SupplyChai return nil, nil, nil, fmt.Errorf("failed to get cluster template [%+v]: %w", resource.TemplateRef, err) } - labels := map[string]string{ - "carto.run/workload-name": r.workload.Name, - "carto.run/workload-namespace": r.workload.Namespace, - "carto.run/supply-chain-name": supplyChainName, - "carto.run/resource-name": resource.Name, - "carto.run/template-kind": template.GetKind(), - "carto.run/cluster-template-name": template.GetName(), - } + labels := r.resourceLabeler(resource) inputs := outputs.GenerateInputs(resource) - workloadTemplatingContext := map[string]interface{}{ - "workload": r.workload, - "params": templates.ParamsBuilder(template.GetDefaultParams(), r.supplyChainParams, resource.Params, r.workload.Spec.Params), - "sources": inputs.Sources, - "images": inputs.Images, - "configs": inputs.Configs, + + ownerTemplatingContext := map[string]interface{}{ + "workload": r.owner, + "deliverable": r.owner, + "params": templates.ParamsBuilder(template.GetDefaultParams(), r.blueprintParams, resource.Params, r.ownerParams), + "sources": inputs.Sources, + "images": inputs.Images, + "deployment": inputs.Deployment, + "configs": inputs.Configs, } - // Todo: this belongs in Stamp. if inputs.OnlyConfig() != nil { - workloadTemplatingContext["config"] = inputs.OnlyConfig() + ownerTemplatingContext["config"] = inputs.OnlyConfig() } if inputs.OnlyImage() != nil { - workloadTemplatingContext["image"] = inputs.OnlyImage() + ownerTemplatingContext["image"] = inputs.OnlyImage() } if inputs.OnlySource() != nil { - workloadTemplatingContext["source"] = inputs.OnlySource() + ownerTemplatingContext["source"] = inputs.OnlySource() } - stampContext := templates.StamperBuilder(r.workload, workloadTemplatingContext, labels) + stampContext := templates.StamperBuilder(r.owner, ownerTemplatingContext, labels) stampedObject, err := stampContext.Stamp(ctx, template.GetResourceTemplate()) if err != nil { log.Error(err, "failed to stamp resource") return template, nil, nil, errors.StampError{ Err: err, ResourceName: resource.Name, - BlueprintName: supplyChainName, + BlueprintName: blueprintName, BlueprintType: errors.SupplyChain, } } - err = r.workloadRepo.EnsureMutableObjectExistsOnCluster(ctx, stampedObject) + err = r.ownerRepo.EnsureMutableObjectExistsOnCluster(ctx, stampedObject) if err != nil { log.Error(err, "failed to ensure object exists on cluster", "object", stampedObject) return template, nil, nil, errors.ApplyStampedObjectError{ Err: err, StampedObject: stampedObject, ResourceName: resource.Name, - BlueprintName: supplyChainName, + BlueprintName: blueprintName, BlueprintType: errors.SupplyChain, } } + template.SetInputs(inputs) template.SetStampedObject(stampedObject) output, err := template.GetOutput() @@ -163,7 +175,7 @@ func (r *resourceRealizer) Do(ctx context.Context, resource *v1alpha1.SupplyChai Err: err, ResourceName: resource.Name, StampedObject: stampedObject, - BlueprintName: supplyChainName, + BlueprintName: blueprintName, BlueprintType: errors.SupplyChain, } } @@ -171,14 +183,14 @@ func (r *resourceRealizer) Do(ctx context.Context, resource *v1alpha1.SupplyChai return template, stampedObject, output, nil } -func (r *resourceRealizer) findMatchingTemplateName(resource *v1alpha1.SupplyChainResource, supplyChainName string) (string, error) { - bestMatchingTemplateOptionsIndices, err := selector.BestSelectorMatchIndices(r.workload, v1alpha1.TemplateOptionSelectors(resource.TemplateRef.Options)) +func (r *resourceRealizer) findMatchingTemplateName(resource OwnerResource, supplyChainName string) (string, error) { + bestMatchingTemplateOptionsIndices, err := selector.BestSelectorMatchIndices(r.owner, v1alpha1.TemplateOptionSelectors(resource.TemplateOptions)) if err != nil { return "", errors.ResolveTemplateOptionError{ Err: err, ResourceName: resource.Name, - OptionName: resource.TemplateRef.Options[err.SelectorIndex()].Name, + OptionName: resource.TemplateOptions[err.SelectorIndex()].Name, BlueprintName: supplyChainName, BlueprintType: errors.SupplyChain, } @@ -187,7 +199,7 @@ func (r *resourceRealizer) findMatchingTemplateName(resource *v1alpha1.SupplyCha if len(bestMatchingTemplateOptionsIndices) != 1 { var optionNames []string for _, optionIndex := range bestMatchingTemplateOptionsIndices { - optionNames = append(optionNames, resource.TemplateRef.Options[optionIndex].Name) + optionNames = append(optionNames, resource.TemplateOptions[optionIndex].Name) } return "", errors.TemplateOptionsMatchError{ @@ -198,5 +210,5 @@ func (r *resourceRealizer) findMatchingTemplateName(resource *v1alpha1.SupplyCha } } - return resource.TemplateRef.Options[bestMatchingTemplateOptionsIndices[0]].Name, nil + return resource.TemplateOptions[bestMatchingTemplateOptionsIndices[0]].Name, nil } diff --git a/pkg/realizer/workload/component_test.go b/pkg/realizer/component_test.go similarity index 79% rename from pkg/realizer/workload/component_test.go rename to pkg/realizer/component_test.go index 390bc5f8d..416f89f0a 100644 --- a/pkg/realizer/workload/component_test.go +++ b/pkg/realizer/component_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package workload_test +package realizer_test import ( "context" @@ -31,7 +31,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" "github.com/vmware-tanzu/cartographer/pkg/apis/v1alpha1" - realizer "github.com/vmware-tanzu/cartographer/pkg/realizer/workload" + "github.com/vmware-tanzu/cartographer/pkg/realizer" "github.com/vmware-tanzu/cartographer/pkg/repository" "github.com/vmware-tanzu/cartographer/pkg/repository/repositoryfakes" "github.com/vmware-tanzu/cartographer/pkg/templates" @@ -41,12 +41,12 @@ var _ = Describe("Resource", func() { var ( ctx context.Context - resource v1alpha1.SupplyChainResource + resource realizer.OwnerResource workload v1alpha1.Workload outputs realizer.Outputs - supplyChainName string + blueprintName string fakeSystemRepo repositoryfakes.FakeRepository - fakeWorkloadRepo repositoryfakes.FakeRepository + fakeOwnerRepo repositoryfakes.FakeRepository clientForBuiltRepository client.Client cacheForBuiltRepository repository.RepoCache theSecret, secretForBuiltClient *corev1.Secret @@ -60,27 +60,27 @@ var _ = Describe("Resource", func() { var err error ctx = context.Background() - resource = v1alpha1.SupplyChainResource{ + resource = realizer.OwnerResource{ Name: "resource-1", - TemplateRef: v1alpha1.SupplyChainTemplateReference{ + TemplateRef: v1alpha1.TemplateReference{ Kind: "ClusterImageTemplate", Name: "image-template-1", }, } - supplyChainName = "supply-chain-name" + blueprintName = "supply-chain-name" supplyChainParams = []v1alpha1.BlueprintParam{} outputs = realizer.NewOutputs() fakeSystemRepo = repositoryfakes.FakeRepository{} - fakeWorkloadRepo = repositoryfakes.FakeRepository{} + fakeOwnerRepo = repositoryfakes.FakeRepository{} workload = v1alpha1.Workload{} repositoryBuilder := func(client client.Client, repoCache repository.RepoCache) repository.Repository { clientForBuiltRepository = client cacheForBuiltRepository = repoCache - return &fakeWorkloadRepo + return &fakeOwnerRepo } builtClient := &repositoryfakes.FakeClient{} @@ -96,7 +96,10 @@ var _ = Describe("Resource", func() { theSecret = &corev1.Secret{StringData: map[string]string{"blah": "blah"}} - r, err = resourceRealizerBuilder(theSecret, &workload, &fakeSystemRepo, supplyChainParams) + dummyLabeler := func(resource realizer.OwnerResource) templates.Labels { + return templates.Labels{"expected-labels-from-labeller-dummy": "labeller"} + } + r, err = resourceRealizerBuilder(theSecret, &workload, []v1alpha1.OwnerParam{}, &fakeSystemRepo, supplyChainParams, dummyLabeler) Expect(err).NotTo(HaveOccurred()) }) @@ -111,7 +114,7 @@ var _ = Describe("Resource", func() { }) Describe("Do", func() { - When("passed a workload with outputs", func() { + When("passed a owner with outputs", func() { BeforeEach(func() { resource.Sources = []v1alpha1.ResourceReference{ { @@ -142,35 +145,37 @@ var _ = Describe("Resource", func() { dbytes, err := json.Marshal(configMap) Expect(err).ToNot(HaveOccurred()) - templateAPI := &v1alpha1.ClusterImageTemplate{ + templateAPI := &v1alpha1.ClusterSourceTemplate{ TypeMeta: metav1.TypeMeta{ - Kind: "ClusterImageTemplate", + Kind: "ClusterSourceTemplate", APIVersion: "carto.run/v1alpha1", }, ObjectMeta: metav1.ObjectMeta{ - Name: "image-template-1", + Name: "source-template-1", Namespace: "some-namespace", }, - Spec: v1alpha1.ImageTemplateSpec{ + Spec: v1alpha1.SourceTemplateSpec{ TemplateSpec: v1alpha1.TemplateSpec{ Template: &runtime.RawExtension{Raw: dbytes}, }, - ImagePath: "data.some_other_info", + URLPath: "data.player_current_lives", + RevisionPath: "data.some_other_info", }, } fakeSystemRepo.GetTemplateReturns(templateAPI, nil) - fakeWorkloadRepo.EnsureMutableObjectExistsOnClusterReturns(nil) + fakeOwnerRepo.EnsureMutableObjectExistsOnClusterReturns(nil) }) - It("creates a stamped object using the workload repository and returns the outputs and stampedObjects and template", func() { - template, returnedStampedObject, out, err := r.Do(ctx, &resource, supplyChainName, outputs) + It("creates a stamped object and returns the outputs and stampedObjects", func() { + template, returnedStampedObject, out, err := r.Do(ctx, resource, blueprintName, outputs) Expect(err).ToNot(HaveOccurred()) - Expect(template.GetName()).To(Equal("image-template-1")) - Expect(template.GetKind()).To(Equal("ClusterImageTemplate")) + Expect(template.GetName()).To(Equal("source-template-1")) + Expect(template.GetKind()).To(Equal("ClusterSourceTemplate")) + + _, stampedObject := fakeOwnerRepo.EnsureMutableObjectExistsOnClusterArgsForCall(0) - _, stampedObject := fakeWorkloadRepo.EnsureMutableObjectExistsOnClusterArgsForCall(0) Expect(returnedStampedObject).To(Equal(stampedObject)) metadata := stampedObject.Object["metadata"] @@ -188,16 +193,10 @@ var _ = Describe("Resource", func() { }, })) Expect(stampedObject.Object["data"]).To(Equal(map[string]interface{}{"player_current_lives": "some-url", "some_other_info": "some-revision"})) - Expect(metadataValues["labels"]).To(Equal(map[string]interface{}{ - "carto.run/supply-chain-name": "supply-chain-name", - "carto.run/resource-name": "resource-1", - "carto.run/cluster-template-name": "image-template-1", - "carto.run/workload-name": "", - "carto.run/workload-namespace": "", - "carto.run/template-kind": "ClusterImageTemplate", - })) + Expect(metadataValues["labels"]).To(Equal(map[string]interface{}{"expected-labels-from-labeller-dummy": "labeller"})) - Expect(out.Image).To(Equal("some-revision")) + Expect(out.Source.Revision).To(Equal("some-revision")) + Expect(out.Source.URL).To(Equal("some-url")) }) }) @@ -207,7 +206,7 @@ var _ = Describe("Resource", func() { }) It("returns GetTemplateError", func() { - template, _, _, err := r.Do(ctx, &resource, supplyChainName, outputs) + template, _, _, err := r.Do(ctx, resource, blueprintName, outputs) Expect(err).To(HaveOccurred()) Expect(template).To(BeNil()) @@ -234,12 +233,12 @@ var _ = Describe("Resource", func() { }) It("returns a helpful error", func() { - template, _, _, err := r.Do(ctx, &resource, supplyChainName, outputs) + template, _, _, err := r.Do(ctx, resource, blueprintName, outputs) Expect(template).To(BeNil()) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("failed to get cluster template [{Kind:ClusterImageTemplate Name:image-template-1 Options:[]}]: resource does not match a known template")) + Expect(err.Error()).To(ContainSubstring("failed to get cluster template [{Kind:ClusterImageTemplate Name:image-template-1}]: resource does not match a known template")) }) }) @@ -265,7 +264,7 @@ var _ = Describe("Resource", func() { }) It("returns StampError", func() { - template, _, _, err := r.Do(ctx, &resource, supplyChainName, outputs) + template, _, _, err := r.Do(ctx, resource, blueprintName, outputs) Expect(template.GetName()).To(Equal("image-template-1")) Expect(template.GetKind()).To(Equal("ClusterImageTemplate")) @@ -313,11 +312,11 @@ var _ = Describe("Resource", func() { } fakeSystemRepo.GetTemplateReturns(templateAPI, nil) - fakeWorkloadRepo.EnsureMutableObjectExistsOnClusterReturns(nil) + fakeOwnerRepo.EnsureMutableObjectExistsOnClusterReturns(nil) }) It("returns RetrieveOutputError", func() { - template, _, _, err := r.Do(ctx, &resource, supplyChainName, outputs) + template, _, _, err := r.Do(ctx, resource, blueprintName, outputs) Expect(template.GetName()).To(Equal("image-template-1")) Expect(template.GetKind()).To(Equal("ClusterImageTemplate")) @@ -377,10 +376,10 @@ var _ = Describe("Resource", func() { } fakeSystemRepo.GetTemplateReturns(templateAPI, nil) - fakeWorkloadRepo.EnsureMutableObjectExistsOnClusterReturns(errors.New("bad object")) + fakeOwnerRepo.EnsureMutableObjectExistsOnClusterReturns(errors.New("bad object")) }) It("returns ApplyStampedObjectError", func() { - template, _, _, err := r.Do(ctx, &resource, supplyChainName, outputs) + template, _, _, err := r.Do(ctx, resource, blueprintName, outputs) Expect(template.GetName()).To(Equal("image-template-1")) Expect(template.GetKind()).To(Equal("ClusterImageTemplate")) @@ -431,8 +430,8 @@ var _ = Describe("Resource", func() { fakeSystemRepo.GetTemplateReturns(templateAPI, nil) }) - It("returns ApplyStampedObjectError", func() { - template, _, _, err := r.Do(ctx, &resource, supplyChainName, outputs) + It("returns StampError", func() { + template, _, _, err := r.Do(ctx, resource, blueprintName, outputs) Expect(template.GetName()).To(Equal("image-template-1")) Expect(template.GetKind()).To(Equal("ClusterImageTemplate")) @@ -473,35 +472,35 @@ var _ = Describe("Resource", func() { }, } - resource = v1alpha1.SupplyChainResource{ + resource = realizer.OwnerResource{ Name: "resource-1", - TemplateRef: v1alpha1.SupplyChainTemplateReference{ - Kind: "ClusterImageTemplate", - Options: []v1alpha1.TemplateOption{ - { - Name: "template-not-chosen", - Selector: v1alpha1.Selector{ - MatchFields: []v1alpha1.FieldSelectorRequirement{ - { - Key: "spec.source.image", - Operator: "Exists", - }, + TemplateOptions: []v1alpha1.TemplateOption{ + { + Name: "template-not-chosen", + Selector: v1alpha1.Selector{ + MatchFields: []v1alpha1.FieldSelectorRequirement{ + { + Key: "spec.source.image", + Operator: "Exists", }, }, }, - { - Name: "template-chosen", - Selector: v1alpha1.Selector{ - MatchFields: []v1alpha1.FieldSelectorRequirement{ - { - Key: "spec.source.git.url", - Operator: "Exists", - }, + }, + { + Name: "template-chosen", + Selector: v1alpha1.Selector{ + MatchFields: []v1alpha1.FieldSelectorRequirement{ + { + Key: "spec.source.git.url", + Operator: "Exists", }, }, }, }, }, + TemplateRef: v1alpha1.TemplateReference{ + Kind: "ClusterImageTemplate", + }, } configMap := &corev1.ConfigMap{ @@ -537,12 +536,12 @@ var _ = Describe("Resource", func() { } fakeSystemRepo.GetTemplateReturns(templateAPI, nil) - fakeWorkloadRepo.EnsureMutableObjectExistsOnClusterReturns(nil) + fakeOwnerRepo.EnsureMutableObjectExistsOnClusterReturns(nil) }) When("one option matches", func() { It("finds the correct template", func() { - template, _, _, err := r.Do(ctx, &resource, supplyChainName, outputs) + template, _, _, err := r.Do(ctx, resource, blueprintName, outputs) Expect(err).NotTo(HaveOccurred()) Expect(template.GetName()).To(Equal("template-chosen")) @@ -556,9 +555,9 @@ var _ = Describe("Resource", func() { When("more than one option matches", func() { It("returns a TemplateOptionsMatchError", func() { - resource.TemplateRef.Options[0].Selector.MatchFields[0].Key = "spec.source.git.ref.branch" + resource.TemplateOptions[0].Selector.MatchFields[0].Key = "spec.source.git.ref.branch" - template, _, _, err := r.Do(ctx, &resource, supplyChainName, outputs) + template, _, _, err := r.Do(ctx, resource, blueprintName, outputs) Expect(template).To(BeNil()) Expect(err).To(HaveOccurred()) @@ -569,10 +568,10 @@ var _ = Describe("Resource", func() { When("zero options match", func() { It("returns a TemplateOptionsMatchError", func() { - resource.TemplateRef.Options[0].Selector.MatchFields[0].Key = "spec.source.image" - resource.TemplateRef.Options[1].Selector.MatchFields[0].Key = "spec.source.subPath" + resource.TemplateOptions[0].Selector.MatchFields[0].Key = "spec.source.image" + resource.TemplateOptions[1].Selector.MatchFields[0].Key = "spec.source.subPath" - template, _, _, err := r.Do(ctx, &resource, supplyChainName, outputs) + template, _, _, err := r.Do(ctx, resource, blueprintName, outputs) Expect(template).To(BeNil()) @@ -583,9 +582,9 @@ var _ = Describe("Resource", func() { When("one option has key that does not exist in the spec", func() { It("does not error", func() { - resource.TemplateRef.Options[0].Selector.MatchFields[0].Key = `spec.env[?(@.name=="some-name")].bad` + resource.TemplateOptions[0].Selector.MatchFields[0].Key = `spec.env[?(@.name=="some-name")].bad` - template, _, _, err := r.Do(ctx, &resource, supplyChainName, outputs) + template, _, _, err := r.Do(ctx, resource, blueprintName, outputs) Expect(template.GetName()).To(Equal("template-chosen")) Expect(template.GetKind()).To(Equal("ClusterImageTemplate")) @@ -599,9 +598,9 @@ var _ = Describe("Resource", func() { When("key is malformed", func() { It("returns a ResolveTemplateOptionError", func() { - resource.TemplateRef.Options[0].Selector.MatchFields[0].Key = `spec.env[` + resource.TemplateOptions[0].Selector.MatchFields[0].Key = `spec.env[` - template, _, _, err := r.Do(ctx, &resource, supplyChainName, outputs) + template, _, _, err := r.Do(ctx, resource, blueprintName, outputs) Expect(template).To(BeNil()) @@ -614,12 +613,12 @@ var _ = Describe("Resource", func() { When("one option matches with multiple fields", func() { It("finds the correct template", func() { - resource.TemplateRef.Options[0].Selector.MatchFields = append(resource.TemplateRef.Options[0].Selector.MatchFields, v1alpha1.FieldSelectorRequirement{ + resource.TemplateOptions[0].Selector.MatchFields = append(resource.TemplateOptions[0].Selector.MatchFields, v1alpha1.FieldSelectorRequirement{ Key: "spec.source.git.ref.branch", Operator: "Exists", }) - template, _, _, err := r.Do(ctx, &resource, supplyChainName, outputs) + template, _, _, err := r.Do(ctx, resource, blueprintName, outputs) Expect(template.GetName()).To(Equal("template-chosen")) Expect(template.GetKind()).To(Equal("ClusterImageTemplate")) diff --git a/pkg/realizer/deliverable/component.go b/pkg/realizer/deliverable/component.go deleted file mode 100644 index d34a2d8bc..000000000 --- a/pkg/realizer/deliverable/component.go +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2021 VMware -// -// 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 deliverable - -import ( - "context" - "fmt" - - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - - "github.com/vmware-tanzu/cartographer/pkg/apis/v1alpha1" - "github.com/vmware-tanzu/cartographer/pkg/errors" - "github.com/vmware-tanzu/cartographer/pkg/logger" - realizerclient "github.com/vmware-tanzu/cartographer/pkg/realizer/client" - "github.com/vmware-tanzu/cartographer/pkg/repository" - "github.com/vmware-tanzu/cartographer/pkg/selector" - "github.com/vmware-tanzu/cartographer/pkg/templates" -) - -//go:generate go run -modfile ../../../hack/tools/go.mod github.com/maxbrunsfeld/counterfeiter/v6 -generate - -//counterfeiter:generate . ResourceRealizer -type ResourceRealizer interface { - Do(ctx context.Context, resource *v1alpha1.DeliveryResource, deliveryName string, outputs Outputs) (templates.Template, *unstructured.Unstructured, *templates.Output, error) -} - -type resourceRealizer struct { - deliverable *v1alpha1.Deliverable - systemRepo repository.Repository - deliverableRepo repository.Repository - deliveryParams []v1alpha1.BlueprintParam -} - -type ResourceRealizerBuilder func(secret *corev1.Secret, deliverable *v1alpha1.Deliverable, repo repository.Repository, deliveryParams []v1alpha1.BlueprintParam) (ResourceRealizer, error) - -func NewResourceRealizerBuilder(repositoryBuilder repository.RepositoryBuilder, clientBuilder realizerclient.ClientBuilder, cache repository.RepoCache) ResourceRealizerBuilder { - return func(secret *corev1.Secret, deliverable *v1alpha1.Deliverable, systemRepo repository.Repository, deliveryParams []v1alpha1.BlueprintParam) (ResourceRealizer, error) { - client, _, err := clientBuilder(secret, false) - if err != nil { - return nil, fmt.Errorf("can't build client: %w", err) - } - deliverableRepo := repositoryBuilder(client, cache) - return &resourceRealizer{ - deliverable: deliverable, - systemRepo: systemRepo, - deliverableRepo: deliverableRepo, - deliveryParams: deliveryParams, - }, nil - } -} - -func (r *resourceRealizer) Do(ctx context.Context, resource *v1alpha1.DeliveryResource, deliveryName string, outputs Outputs) (templates.Template, *unstructured.Unstructured, *templates.Output, error) { - log := logr.FromContextOrDiscard(ctx).WithValues("template", resource.TemplateRef) - ctx = logr.NewContext(ctx, log) - - var templateName string - var err error - if len(resource.TemplateRef.Options) > 0 { - templateName, err = r.findMatchingTemplateName(resource, deliveryName) - if err != nil { - return nil, nil, nil, err - } - } else { - templateName = resource.TemplateRef.Name - } - - log.V(logger.DEBUG).Info("realizing template", "template", fmt.Sprintf("[%s/%s]", resource.TemplateRef.Kind, templateName)) - - apiTemplate, err := r.systemRepo.GetTemplate(ctx, templateName, resource.TemplateRef.Kind) - if err != nil { - log.Error(err, "failed to get delivery cluster template") - return nil, nil, nil, errors.GetTemplateError{ - Err: err, - ResourceName: resource.Name, - TemplateName: templateName, - BlueprintName: deliveryName, - BlueprintType: errors.Delivery, - } - } - - template, err := templates.NewModelFromAPI(apiTemplate) - if err != nil { - log.Error(err, "failed to get delivery cluster template") - return nil, nil, nil, fmt.Errorf("failed to get delivery cluster template [%+v]: %w", resource.TemplateRef, err) - } - - labels := map[string]string{ - "carto.run/deliverable-name": r.deliverable.Name, - "carto.run/deliverable-namespace": r.deliverable.Namespace, - "carto.run/delivery-name": deliveryName, - "carto.run/resource-name": resource.Name, - "carto.run/template-kind": template.GetKind(), - "carto.run/cluster-template-name": template.GetName(), - } - - inputs := outputs.GenerateInputs(resource) - templatingContext := map[string]interface{}{ - "deliverable": r.deliverable, - "params": templates.ParamsBuilder(template.GetDefaultParams(), r.deliveryParams, resource.Params, r.deliverable.Spec.Params), - "sources": inputs.Sources, - "configs": inputs.Configs, - "deployment": inputs.Deployment, - } - - // Todo: this belongs in Stamp. - if inputs.OnlyConfig() != nil { - templatingContext["config"] = inputs.OnlyConfig() - } - if inputs.OnlySource() != nil { - templatingContext["source"] = inputs.OnlySource() - } - - stampContext := templates.StamperBuilder(r.deliverable, templatingContext, labels) - stampedObject, err := stampContext.Stamp(ctx, template.GetResourceTemplate()) - if err != nil { - log.Error(err, "failed to stamp resource") - return template, nil, nil, errors.StampError{ - Err: err, - ResourceName: resource.Name, - BlueprintName: deliveryName, - BlueprintType: errors.Delivery, - } - } - - err = r.deliverableRepo.EnsureMutableObjectExistsOnCluster(ctx, stampedObject) - if err != nil { - log.Error(err, "failed to ensure object exists on cluster", "object", stampedObject) - return template, nil, nil, errors.ApplyStampedObjectError{ - Err: err, - StampedObject: stampedObject, - ResourceName: resource.Name, - BlueprintName: deliveryName, - BlueprintType: errors.Delivery, - } - } - - template.SetInputs(inputs) - template.SetStampedObject(stampedObject) - - output, err := template.GetOutput() - if err != nil { - log.Error(err, "failed to retrieve output from object", "object", stampedObject) - return template, stampedObject, nil, errors.RetrieveOutputError{ - Err: err, - ResourceName: resource.Name, - StampedObject: stampedObject, - BlueprintName: deliveryName, - BlueprintType: errors.Delivery, - } - } - - return template, stampedObject, output, nil -} - -func (r *resourceRealizer) findMatchingTemplateName(resource *v1alpha1.DeliveryResource, deliveryName string) (string, error) { - bestMatchingTemplateOptionsIndices, err := selector.BestSelectorMatchIndices(r.deliverable, v1alpha1.TemplateOptionSelectors(resource.TemplateRef.Options)) - - if err != nil { - return "", errors.ResolveTemplateOptionError{ - Err: err, - ResourceName: resource.Name, - OptionName: resource.TemplateRef.Options[err.SelectorIndex()].Name, - BlueprintName: deliveryName, - BlueprintType: errors.Delivery, - } - } - - if len(bestMatchingTemplateOptionsIndices) != 1 { - var optionNames []string - for _, optionIndex := range bestMatchingTemplateOptionsIndices { - optionNames = append(optionNames, resource.TemplateRef.Options[optionIndex].Name) - } - - return "", errors.TemplateOptionsMatchError{ - ResourceName: resource.Name, - OptionNames: optionNames, - BlueprintName: deliveryName, - BlueprintType: errors.Delivery, - } - } - - return resource.TemplateRef.Options[bestMatchingTemplateOptionsIndices[0]].Name, nil -} diff --git a/pkg/realizer/deliverable/component_test.go b/pkg/realizer/deliverable/component_test.go deleted file mode 100644 index 8706cafd5..000000000 --- a/pkg/realizer/deliverable/component_test.go +++ /dev/null @@ -1,446 +0,0 @@ -// Copyright 2021 VMware -// -// 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 deliverable_test - -import ( - "context" - "encoding/json" - "errors" - "reflect" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/discovery" - "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/vmware-tanzu/cartographer/pkg/apis/v1alpha1" - realizer "github.com/vmware-tanzu/cartographer/pkg/realizer/deliverable" - "github.com/vmware-tanzu/cartographer/pkg/repository" - "github.com/vmware-tanzu/cartographer/pkg/repository/repositoryfakes" - "github.com/vmware-tanzu/cartographer/pkg/templates" -) - -var _ = Describe("Resource", func() { - - var ( - ctx context.Context - resource v1alpha1.DeliveryResource - deliverable v1alpha1.Deliverable - outputs realizer.Outputs - deliveryName string - fakeSystemRepo repositoryfakes.FakeRepository - fakeDeliverableRepo repositoryfakes.FakeRepository - clientForBuiltRepository client.Client - cacheForBuiltRepository repository.RepoCache - repoCache repository.RepoCache - builtClient client.Client - theSecret *corev1.Secret - secretForBuiltClient *corev1.Secret - r realizer.ResourceRealizer - deliveryParams []v1alpha1.BlueprintParam - ) - - BeforeEach(func() { - ctx = context.Background() - resource = v1alpha1.DeliveryResource{ - Name: "resource-1", - TemplateRef: v1alpha1.DeliveryTemplateReference{ - Kind: "ClusterSourceTemplate", - Name: "source-template-1", - }, - } - - deliveryName = "delivery-name" - - deliveryParams = []v1alpha1.BlueprintParam{} - - outputs = realizer.NewOutputs() - - fakeSystemRepo = repositoryfakes.FakeRepository{} - fakeDeliverableRepo = repositoryfakes.FakeRepository{} - - repositoryBuilder := func(client client.Client, repoCache repository.RepoCache) repository.Repository { - clientForBuiltRepository = client - cacheForBuiltRepository = repoCache - return &fakeDeliverableRepo - } - - builtClient = &repositoryfakes.FakeClient{} - clientBuilder := func(secret *corev1.Secret, _ bool) (client.Client, discovery.DiscoveryInterface, error) { - secretForBuiltClient = secret - return builtClient, nil, nil - } - - repoCache = &repositoryfakes.FakeRepoCache{} //TODO: can we verify right cache used? - resourceRealizerBuilder := realizer.NewResourceRealizerBuilder(repositoryBuilder, clientBuilder, repoCache) - - deliverable = v1alpha1.Deliverable{} - - theSecret = &corev1.Secret{StringData: map[string]string{"blah": "blah"}} - - var err error - r, err = resourceRealizerBuilder(theSecret, &deliverable, &fakeSystemRepo, deliveryParams) - Expect(err).NotTo(HaveOccurred()) - }) - - It("creates a resource realizer with the existing client, as well as one with the the supplied secret mixed in", func() { - Expect(secretForBuiltClient).To(Equal(theSecret)) - Expect(clientForBuiltRepository).To(Equal(builtClient)) - }) - - It("creates a resource realizer with the existing cache", func() { - Expect(cacheForBuiltRepository).To(Equal(repoCache)) - }) - - Describe("Do", func() { - When("passed a deliverable with outputs", func() { - BeforeEach(func() { - resource.Sources = []v1alpha1.ResourceReference{ - { - Name: "source-provider", - Resource: "previous-resource", - }, - } - - outputs.AddOutput("previous-resource", &templates.Output{Source: &templates.Source{ - URL: "some-url", - Revision: "some-revision", - }}) - - configMap := &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{ - Kind: "ConfigMap", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "example-config-map", - }, - Data: map[string]string{ - "player_current_lives": `$(source.url)$`, - "some_other_info": `$(sources.source-provider.revision)$`, - }, - } - - dbytes, err := json.Marshal(configMap) - Expect(err).ToNot(HaveOccurred()) - - templateAPI := &v1alpha1.ClusterSourceTemplate{ - TypeMeta: metav1.TypeMeta{ - Kind: "ClusterSourceTemplate", - APIVersion: "carto.run/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "source-template-1", - Namespace: "some-namespace", - }, - Spec: v1alpha1.SourceTemplateSpec{ - TemplateSpec: v1alpha1.TemplateSpec{ - Template: &runtime.RawExtension{Raw: dbytes}, - }, - URLPath: "data.player_current_lives", - RevisionPath: "data.some_other_info", - }, - } - - fakeSystemRepo.GetTemplateReturns(templateAPI, nil) - fakeDeliverableRepo.EnsureMutableObjectExistsOnClusterReturns(nil) - }) - - It("creates a stamped object and returns the outputs and stampedObjects", func() { - template, returnedStampedObject, out, err := r.Do(ctx, &resource, deliveryName, outputs) - Expect(err).ToNot(HaveOccurred()) - - Expect(template.GetName()).To(Equal("source-template-1")) - Expect(template.GetKind()).To(Equal("ClusterSourceTemplate")) - - _, stampedObject := fakeDeliverableRepo.EnsureMutableObjectExistsOnClusterArgsForCall(0) - - Expect(returnedStampedObject).To(Equal(stampedObject)) - - metadata := stampedObject.Object["metadata"] - metadataValues, ok := metadata.(map[string]interface{}) - Expect(ok).To(BeTrue()) - Expect(metadataValues["name"]).To(Equal("example-config-map")) - Expect(metadataValues["ownerReferences"]).To(Equal([]interface{}{ - map[string]interface{}{ - "apiVersion": "", - "kind": "", - "name": "", - "uid": "", - "controller": true, - "blockOwnerDeletion": true, - }, - })) - Expect(stampedObject.Object["data"]).To(Equal(map[string]interface{}{"player_current_lives": "some-url", "some_other_info": "some-revision"})) - Expect(metadataValues["labels"]).To(Equal(map[string]interface{}{ - "carto.run/delivery-name": "delivery-name", - "carto.run/resource-name": "resource-1", - "carto.run/cluster-template-name": "source-template-1", - "carto.run/deliverable-name": "", - "carto.run/deliverable-namespace": "", - "carto.run/template-kind": "ClusterSourceTemplate", - })) - - Expect(out.Source.Revision).To(Equal("some-revision")) - Expect(out.Source.URL).To(Equal("some-url")) - }) - }) - - When("unable to get the template ref from systemRepo", func() { - BeforeEach(func() { - fakeSystemRepo.GetTemplateReturns(nil, errors.New("bad template")) - }) - - It("returns GetTemplateError", func() { - template, _, _, err := r.Do(ctx, &resource, deliveryName, outputs) - Expect(err).To(HaveOccurred()) - - Expect(template).To(BeNil()) - - Expect(err.Error()).To(ContainSubstring("unable to get template [source-template-1]")) - Expect(err.Error()).To(ContainSubstring("bad template")) - Expect(reflect.TypeOf(err).String()).To(Equal("errors.GetTemplateError")) - }) - }) - - When("unable to create a template model from apiTemplate", func() { - BeforeEach(func() { - templateAPI := &v1alpha1.Workload{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "carto.run/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "not-a-template", - Namespace: "some-namespace", - }, - } - - fakeSystemRepo.GetTemplateReturns(templateAPI, nil) - }) - - It("returns a helpful error", func() { - template, _, _, err := r.Do(ctx, &resource, deliveryName, outputs) - Expect(template).To(BeNil()) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("failed to get delivery cluster template [{Kind:ClusterSourceTemplate Name:source-template-1 Options:[]}]: resource does not match a known template")) - }) - }) - - When("unable to Stamp a new template", func() { - BeforeEach(func() { - templateAPI := &v1alpha1.ClusterSourceTemplate{ - TypeMeta: metav1.TypeMeta{ - Kind: "ClusterSourceTemplate", - APIVersion: "carto.run/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "source-template-1", - Namespace: "some-namespace", - }, - Spec: v1alpha1.SourceTemplateSpec{ - TemplateSpec: v1alpha1.TemplateSpec{ - Template: &runtime.RawExtension{}, - }, - }, - } - - fakeSystemRepo.GetTemplateReturns(templateAPI, nil) - }) - - It("returns StampError", func() { - template, _, _, err := r.Do(ctx, &resource, deliveryName, outputs) - - Expect(template.GetName()).To(Equal("source-template-1")) - Expect(template.GetKind()).To(Equal("ClusterSourceTemplate")) - - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("unable to stamp object for resource [resource-1]")) - Expect(reflect.TypeOf(err).String()).To(Equal("errors.StampError")) - }) - }) - - When("unable to retrieve the output from the stamped object", func() { - BeforeEach(func() { - configMap := &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{ - Kind: "ConfigMap", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "example-config-map", - }, - Data: map[string]string{ - "player_current_lives": "9", - "some_other_info": "10", - }, - } - - dbytes, err := json.Marshal(configMap) - Expect(err).ToNot(HaveOccurred()) - - templateAPI := &v1alpha1.ClusterSourceTemplate{ - TypeMeta: metav1.TypeMeta{ - Kind: "ClusterSourceTemplate", - APIVersion: "carto.run/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "source-template-1", - Namespace: "some-namespace", - }, - Spec: v1alpha1.SourceTemplateSpec{ - TemplateSpec: v1alpha1.TemplateSpec{ - Template: &runtime.RawExtension{Raw: dbytes}, - }, - URLPath: "data.does-not-exist", - }, - } - - fakeSystemRepo.GetTemplateReturns(templateAPI, nil) - fakeDeliverableRepo.EnsureMutableObjectExistsOnClusterReturns(nil) - }) - - It("returns RetrieveOutputError", func() { - template, _, _, err := r.Do(ctx, &resource, deliveryName, outputs) - - Expect(template.GetName()).To(Equal("source-template-1")) - Expect(template.GetKind()).To(Equal("ClusterSourceTemplate")) - - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("jsonpath returned empty list: data.does-not-exist")) - Expect(reflect.TypeOf(err).String()).To(Equal("errors.RetrieveOutputError")) - }) - }) - - When("unable to EnsureImmutableObjectExistsOnCluster the stamped object", func() { - BeforeEach(func() { - resource.Sources = []v1alpha1.ResourceReference{ - { - Name: "source-provider", - Resource: "previous-resource", - }, - } - - outputs.AddOutput("previous-resource", &templates.Output{Source: &templates.Source{ - URL: "some-url", - Revision: "some-revision", - }}) - - configMap := &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{ - Kind: "ConfigMap", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "example-config-map", - }, - Data: map[string]string{ - "player_current_lives": `$(sources.source-provider.url)$`, - "some_other_info": `$(sources.source-provider.revision)$`, - }, - } - - dbytes, err := json.Marshal(configMap) - Expect(err).ToNot(HaveOccurred()) - - templateAPI := &v1alpha1.ClusterSourceTemplate{ - TypeMeta: metav1.TypeMeta{ - Kind: "ClusterSourceTemplate", - APIVersion: "carto.run/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "source-template-1", - Namespace: "some-namespace", - }, - Spec: v1alpha1.SourceTemplateSpec{ - TemplateSpec: v1alpha1.TemplateSpec{ - Template: &runtime.RawExtension{Raw: dbytes}, - }, - URLPath: "data.some_other_info", - }, - } - - fakeSystemRepo.GetTemplateReturns(templateAPI, nil) - fakeDeliverableRepo.EnsureMutableObjectExistsOnClusterReturns(errors.New("bad object")) - }) - - It("returns ApplyStampedObjectError", func() { - template, _, _, err := r.Do(ctx, &resource, deliveryName, outputs) - - Expect(template.GetName()).To(Equal("source-template-1")) - Expect(template.GetKind()).To(Equal("ClusterSourceTemplate")) - - Expect(err).To(HaveOccurred()) - - Expect(err.Error()).To(ContainSubstring("bad object")) - Expect(reflect.TypeOf(err).String()).To(Equal("errors.ApplyStampedObjectError")) - }) - }) - - When("resource template has namespace specified", func() { - BeforeEach(func() { - configMap := &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{ - Kind: "ConfigMap", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "example-config-map", - Namespace: "some-namespace", - }, - Data: map[string]string{ - "player_current_lives": "9", - "some_other_info": "10", - }, - } - - dbytes, err := json.Marshal(configMap) - Expect(err).ToNot(HaveOccurred()) - - templateAPI := &v1alpha1.ClusterSourceTemplate{ - TypeMeta: metav1.TypeMeta{ - Kind: "ClusterSourceTemplate", - APIVersion: "carto.run/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "source-template-1", - Namespace: "some-namespace", - }, - Spec: v1alpha1.SourceTemplateSpec{ - TemplateSpec: v1alpha1.TemplateSpec{ - Template: &runtime.RawExtension{Raw: dbytes}, - }, - URLPath: "data.does-not-exist", - }, - } - - fakeSystemRepo.GetTemplateReturns(templateAPI, nil) - }) - - It("returns RetrieveOutputError", func() { - template, _, _, err := r.Do(ctx, &resource, deliveryName, outputs) - - Expect(template.GetName()).To(Equal("source-template-1")) - Expect(template.GetKind()).To(Equal("ClusterSourceTemplate")) - - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("cannot set namespace in resource template")) - Expect(reflect.TypeOf(err).String()).To(Equal("errors.StampError")) - }) - }) - }) -}) diff --git a/pkg/realizer/deliverable/deliverablefakes/fake_realizer.go b/pkg/realizer/deliverable/deliverablefakes/fake_realizer.go deleted file mode 100644 index 104483cea..000000000 --- a/pkg/realizer/deliverable/deliverablefakes/fake_realizer.go +++ /dev/null @@ -1,120 +0,0 @@ -// Code generated by counterfeiter. DO NOT EDIT. -package deliverablefakes - -import ( - "context" - "sync" - - "github.com/vmware-tanzu/cartographer/pkg/apis/v1alpha1" - "github.com/vmware-tanzu/cartographer/pkg/realizer/deliverable" - "github.com/vmware-tanzu/cartographer/pkg/realizer/statuses" -) - -type FakeRealizer struct { - RealizeStub func(context.Context, deliverable.ResourceRealizer, *v1alpha1.ClusterDelivery, statuses.ResourceStatuses) error - realizeMutex sync.RWMutex - realizeArgsForCall []struct { - arg1 context.Context - arg2 deliverable.ResourceRealizer - arg3 *v1alpha1.ClusterDelivery - arg4 statuses.ResourceStatuses - } - realizeReturns struct { - result1 error - } - realizeReturnsOnCall map[int]struct { - result1 error - } - invocations map[string][][]interface{} - invocationsMutex sync.RWMutex -} - -func (fake *FakeRealizer) Realize(arg1 context.Context, arg2 deliverable.ResourceRealizer, arg3 *v1alpha1.ClusterDelivery, arg4 statuses.ResourceStatuses) error { - fake.realizeMutex.Lock() - ret, specificReturn := fake.realizeReturnsOnCall[len(fake.realizeArgsForCall)] - fake.realizeArgsForCall = append(fake.realizeArgsForCall, struct { - arg1 context.Context - arg2 deliverable.ResourceRealizer - arg3 *v1alpha1.ClusterDelivery - arg4 statuses.ResourceStatuses - }{arg1, arg2, arg3, arg4}) - stub := fake.RealizeStub - fakeReturns := fake.realizeReturns - fake.recordInvocation("Realize", []interface{}{arg1, arg2, arg3, arg4}) - fake.realizeMutex.Unlock() - if stub != nil { - return stub(arg1, arg2, arg3, arg4) - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *FakeRealizer) RealizeCallCount() int { - fake.realizeMutex.RLock() - defer fake.realizeMutex.RUnlock() - return len(fake.realizeArgsForCall) -} - -func (fake *FakeRealizer) RealizeCalls(stub func(context.Context, deliverable.ResourceRealizer, *v1alpha1.ClusterDelivery, statuses.ResourceStatuses) error) { - fake.realizeMutex.Lock() - defer fake.realizeMutex.Unlock() - fake.RealizeStub = stub -} - -func (fake *FakeRealizer) RealizeArgsForCall(i int) (context.Context, deliverable.ResourceRealizer, *v1alpha1.ClusterDelivery, statuses.ResourceStatuses) { - fake.realizeMutex.RLock() - defer fake.realizeMutex.RUnlock() - argsForCall := fake.realizeArgsForCall[i] - return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4 -} - -func (fake *FakeRealizer) RealizeReturns(result1 error) { - fake.realizeMutex.Lock() - defer fake.realizeMutex.Unlock() - fake.RealizeStub = nil - fake.realizeReturns = struct { - result1 error - }{result1} -} - -func (fake *FakeRealizer) RealizeReturnsOnCall(i int, result1 error) { - fake.realizeMutex.Lock() - defer fake.realizeMutex.Unlock() - fake.RealizeStub = nil - if fake.realizeReturnsOnCall == nil { - fake.realizeReturnsOnCall = make(map[int]struct { - result1 error - }) - } - fake.realizeReturnsOnCall[i] = struct { - result1 error - }{result1} -} - -func (fake *FakeRealizer) Invocations() map[string][][]interface{} { - fake.invocationsMutex.RLock() - defer fake.invocationsMutex.RUnlock() - fake.realizeMutex.RLock() - defer fake.realizeMutex.RUnlock() - copiedInvocations := map[string][][]interface{}{} - for key, value := range fake.invocations { - copiedInvocations[key] = value - } - return copiedInvocations -} - -func (fake *FakeRealizer) recordInvocation(key string, args []interface{}) { - fake.invocationsMutex.Lock() - defer fake.invocationsMutex.Unlock() - if fake.invocations == nil { - fake.invocations = map[string][][]interface{}{} - } - if fake.invocations[key] == nil { - fake.invocations[key] = [][]interface{}{} - } - fake.invocations[key] = append(fake.invocations[key], args) -} - -var _ deliverable.Realizer = new(FakeRealizer) diff --git a/pkg/realizer/deliverable/deliverablefakes/fake_resource_realizer.go b/pkg/realizer/deliverable/deliverablefakes/fake_resource_realizer.go deleted file mode 100644 index 3c87c753a..000000000 --- a/pkg/realizer/deliverable/deliverablefakes/fake_resource_realizer.go +++ /dev/null @@ -1,136 +0,0 @@ -// Code generated by counterfeiter. DO NOT EDIT. -package deliverablefakes - -import ( - "context" - "sync" - - "github.com/vmware-tanzu/cartographer/pkg/apis/v1alpha1" - "github.com/vmware-tanzu/cartographer/pkg/realizer/deliverable" - "github.com/vmware-tanzu/cartographer/pkg/templates" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" -) - -type FakeResourceRealizer struct { - DoStub func(context.Context, *v1alpha1.DeliveryResource, string, deliverable.Outputs) (templates.Template, *unstructured.Unstructured, *templates.Output, error) - doMutex sync.RWMutex - doArgsForCall []struct { - arg1 context.Context - arg2 *v1alpha1.DeliveryResource - arg3 string - arg4 deliverable.Outputs - } - doReturns struct { - result1 templates.Template - result2 *unstructured.Unstructured - result3 *templates.Output - result4 error - } - doReturnsOnCall map[int]struct { - result1 templates.Template - result2 *unstructured.Unstructured - result3 *templates.Output - result4 error - } - invocations map[string][][]interface{} - invocationsMutex sync.RWMutex -} - -func (fake *FakeResourceRealizer) Do(arg1 context.Context, arg2 *v1alpha1.DeliveryResource, arg3 string, arg4 deliverable.Outputs) (templates.Template, *unstructured.Unstructured, *templates.Output, error) { - fake.doMutex.Lock() - ret, specificReturn := fake.doReturnsOnCall[len(fake.doArgsForCall)] - fake.doArgsForCall = append(fake.doArgsForCall, struct { - arg1 context.Context - arg2 *v1alpha1.DeliveryResource - arg3 string - arg4 deliverable.Outputs - }{arg1, arg2, arg3, arg4}) - stub := fake.DoStub - fakeReturns := fake.doReturns - fake.recordInvocation("Do", []interface{}{arg1, arg2, arg3, arg4}) - fake.doMutex.Unlock() - if stub != nil { - return stub(arg1, arg2, arg3, arg4) - } - if specificReturn { - return ret.result1, ret.result2, ret.result3, ret.result4 - } - return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3, fakeReturns.result4 -} - -func (fake *FakeResourceRealizer) DoCallCount() int { - fake.doMutex.RLock() - defer fake.doMutex.RUnlock() - return len(fake.doArgsForCall) -} - -func (fake *FakeResourceRealizer) DoCalls(stub func(context.Context, *v1alpha1.DeliveryResource, string, deliverable.Outputs) (templates.Template, *unstructured.Unstructured, *templates.Output, error)) { - fake.doMutex.Lock() - defer fake.doMutex.Unlock() - fake.DoStub = stub -} - -func (fake *FakeResourceRealizer) DoArgsForCall(i int) (context.Context, *v1alpha1.DeliveryResource, string, deliverable.Outputs) { - fake.doMutex.RLock() - defer fake.doMutex.RUnlock() - argsForCall := fake.doArgsForCall[i] - return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4 -} - -func (fake *FakeResourceRealizer) DoReturns(result1 templates.Template, result2 *unstructured.Unstructured, result3 *templates.Output, result4 error) { - fake.doMutex.Lock() - defer fake.doMutex.Unlock() - fake.DoStub = nil - fake.doReturns = struct { - result1 templates.Template - result2 *unstructured.Unstructured - result3 *templates.Output - result4 error - }{result1, result2, result3, result4} -} - -func (fake *FakeResourceRealizer) DoReturnsOnCall(i int, result1 templates.Template, result2 *unstructured.Unstructured, result3 *templates.Output, result4 error) { - fake.doMutex.Lock() - defer fake.doMutex.Unlock() - fake.DoStub = nil - if fake.doReturnsOnCall == nil { - fake.doReturnsOnCall = make(map[int]struct { - result1 templates.Template - result2 *unstructured.Unstructured - result3 *templates.Output - result4 error - }) - } - fake.doReturnsOnCall[i] = struct { - result1 templates.Template - result2 *unstructured.Unstructured - result3 *templates.Output - result4 error - }{result1, result2, result3, result4} -} - -func (fake *FakeResourceRealizer) Invocations() map[string][][]interface{} { - fake.invocationsMutex.RLock() - defer fake.invocationsMutex.RUnlock() - fake.doMutex.RLock() - defer fake.doMutex.RUnlock() - copiedInvocations := map[string][][]interface{}{} - for key, value := range fake.invocations { - copiedInvocations[key] = value - } - return copiedInvocations -} - -func (fake *FakeResourceRealizer) recordInvocation(key string, args []interface{}) { - fake.invocationsMutex.Lock() - defer fake.invocationsMutex.Unlock() - if fake.invocations == nil { - fake.invocations = map[string][][]interface{}{} - } - if fake.invocations[key] == nil { - fake.invocations[key] = [][]interface{}{} - } - fake.invocations[key] = append(fake.invocations[key], args) -} - -var _ deliverable.ResourceRealizer = new(FakeResourceRealizer) diff --git a/pkg/realizer/deliverable/outputs.go b/pkg/realizer/deliverable/outputs.go deleted file mode 100644 index d1b2b4d5c..000000000 --- a/pkg/realizer/deliverable/outputs.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2021 VMware -// -// 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 deliverable - -import ( - "github.com/vmware-tanzu/cartographer/pkg/apis/v1alpha1" - "github.com/vmware-tanzu/cartographer/pkg/templates" -) - -type Outputs map[string]*templates.Output - -func NewOutputs() Outputs { - return make(Outputs) -} - -func (o Outputs) AddOutput(name string, output *templates.Output) { - o[name] = output -} - -func (o Outputs) getResourceSource(resourceName string) *templates.Source { - output := o[resourceName] - if output == nil { - return nil - } - - return output.Source -} - -func (o Outputs) getResourceConfig(resourceName string) templates.Config { - output := o[resourceName] - if output == nil { - return nil - } - return output.Config -} - -func (o Outputs) GenerateInputs(resource *v1alpha1.DeliveryResource) *templates.Inputs { - inputs := &templates.Inputs{ - Sources: map[string]templates.SourceInput{}, - Configs: map[string]templates.ConfigInput{}, - Deployment: &templates.SourceInput{}, - } - - for _, referenceSource := range resource.Sources { - source := o.getResourceSource(referenceSource.Resource) - if source != nil { - inputs.Sources[referenceSource.Name] = templates.SourceInput{ - URL: source.URL, - Revision: source.Revision, - Name: referenceSource.Name, - } - } - } - - for _, referenceConfig := range resource.Configs { - config := o.getResourceConfig(referenceConfig.Resource) - if config != nil { - inputs.Configs[referenceConfig.Name] = templates.ConfigInput{ - Config: config, - Name: referenceConfig.Name, - } - } - } - - if resource.Deployment != nil { - deployment := o.getResourceSource(resource.Deployment.Resource) - if deployment != nil { - inputs.Deployment = &templates.SourceInput{ - URL: deployment.URL, - Revision: deployment.Revision, - } - } - } - - return inputs -} diff --git a/pkg/realizer/deliverable/outputs_test.go b/pkg/realizer/deliverable/outputs_test.go deleted file mode 100644 index 0208cf0ac..000000000 --- a/pkg/realizer/deliverable/outputs_test.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2021 VMware -// -// 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 deliverable_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "github.com/vmware-tanzu/cartographer/pkg/apis/v1alpha1" - realizer "github.com/vmware-tanzu/cartographer/pkg/realizer/deliverable" - "github.com/vmware-tanzu/cartographer/pkg/templates" -) - -var _ = Describe("Outputs", func() { - Describe("GenerateInputs", func() { - Context("When resource contains sources", func() { - var outs realizer.Outputs - BeforeEach(func() { - outs = realizer.NewOutputs() - sourceOutput := &templates.Output{ - Source: &templates.Source{ - URL: "source-url", - Revision: "source-revision", - }, - } - outs.AddOutput("source-output", sourceOutput) - }) - - Context("And the sources have a match with the outputs", func() { - It("Adds sources to inputs", func() { - resource := &v1alpha1.DeliveryResource{ - Sources: []v1alpha1.ResourceReference{ - { - Name: "source-ref", - Resource: "source-output", - }, - }, - } - inputs := outs.GenerateInputs(resource) - Expect(inputs.Sources).To(HaveLen(1)) - Expect(inputs.Sources["source-ref"].Name).To(Equal("source-ref")) - Expect(inputs.Sources["source-ref"].URL).To(Equal("source-url")) - Expect(inputs.Sources["source-ref"].Revision).To(Equal("source-revision")) - }) - }) - - Context("And the sources do not have a match with the outputs", func() { - It("Does not add sources to inputs", func() { - resource := &v1alpha1.DeliveryResource{ - Sources: []v1alpha1.ResourceReference{ - { - Name: "source-ref", - Resource: "source-output-does-not-exist", - }, - }, - } - inputs := outs.GenerateInputs(resource) - Expect(len(inputs.Sources)).To(Equal(0)) - }) - }) - }) - - Context("When resource contains configs", func() { - var outs realizer.Outputs - BeforeEach(func() { - outs = realizer.NewOutputs() - configOutput := &templates.Output{ - Config: "config12345", - } - outs.AddOutput("config-output", configOutput) - }) - - Context("And the configs have a match with the outputs", func() { - It("Adds configs to inputs", func() { - resource := &v1alpha1.DeliveryResource{ - Configs: []v1alpha1.ResourceReference{ - { - Name: "config-ref", - Resource: "config-output", - }, - }, - } - inputs := outs.GenerateInputs(resource) - Expect(inputs.Configs).To(HaveLen(1)) - Expect(inputs.Configs["config-ref"].Name).To(Equal("config-ref")) - Expect(inputs.Configs["config-ref"].Config).To(Equal("config12345")) - }) - }) - - Context("And the configs do not have a match with the outputs", func() { - It("Does not add configs to inputs", func() { - resource := &v1alpha1.DeliveryResource{ - Configs: []v1alpha1.ResourceReference{ - { - Name: "config-ref", - Resource: "config-output-does-not-exist", - }, - }, - } - inputs := outs.GenerateInputs(resource) - Expect(inputs.Configs).To(BeEmpty()) - }) - }) - }) - }) -}) diff --git a/pkg/realizer/deliverable/realizer_suite_test.go b/pkg/realizer/deliverable/realizer_suite_test.go deleted file mode 100644 index a96e7b38f..000000000 --- a/pkg/realizer/deliverable/realizer_suite_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2021 VMware -// -// 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 deliverable_test - -import ( - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func TestRealizer(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Deliverable Realizer Suite") -} diff --git a/pkg/realizer/deliverable/realizer_test.go b/pkg/realizer/deliverable/realizer_test.go deleted file mode 100644 index 096a268b1..000000000 --- a/pkg/realizer/deliverable/realizer_test.go +++ /dev/null @@ -1,502 +0,0 @@ -// Copyright 2021 VMware -// -// 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 deliverable_test - -import ( - "context" - "crypto/sha256" - "errors" - "fmt" - "time" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - . "github.com/onsi/gomega/gstruct" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - - "github.com/vmware-tanzu/cartographer/pkg/apis/v1alpha1" - "github.com/vmware-tanzu/cartographer/pkg/conditions" - realizer "github.com/vmware-tanzu/cartographer/pkg/realizer/deliverable" - "github.com/vmware-tanzu/cartographer/pkg/realizer/deliverable/deliverablefakes" - "github.com/vmware-tanzu/cartographer/pkg/realizer/statuses" - "github.com/vmware-tanzu/cartographer/pkg/templates" -) - -var _ = Describe("Realize", func() { - var ( - resourceRealizer *deliverablefakes.FakeResourceRealizer - rlzr realizer.Realizer - ) - BeforeEach(func() { - rlzr = realizer.NewRealizer() - resourceRealizer = &deliverablefakes.FakeResourceRealizer{} - }) - - Context("there are no previous resources", func() { - var ( - delivery *v1alpha1.ClusterDelivery - resource1 v1alpha1.DeliveryResource - resource2 v1alpha1.DeliveryResource - template1 *v1alpha1.ClusterSourceTemplate - template2 *v1alpha1.ClusterTemplate - executedResourceOrder []string - ) - BeforeEach(func() { - resource1 = v1alpha1.DeliveryResource{ - Name: "resource1", - } - resource2 = v1alpha1.DeliveryResource{ - Name: "resource2", - Configs: []v1alpha1.ResourceReference{ - { - Name: "my-config", - Resource: "resource1", - }, - }, - } - template1 = &v1alpha1.ClusterSourceTemplate{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "my-source-template", - }, - } - template2 = &v1alpha1.ClusterTemplate{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "my-config-template", - }, - } - delivery = &v1alpha1.ClusterDelivery{ - ObjectMeta: metav1.ObjectMeta{Name: "greatest-delivery"}, - Spec: v1alpha1.DeliverySpec{ - Resources: []v1alpha1.DeliveryResource{resource1, resource2}, - }, - } - - outputFromFirstResource := &templates.Output{Source: &templates.Source{ - URL: "whatever", - Revision: "whatever-rev", - }} - - resourceRealizer.DoCalls(func(ctx context.Context, resource *v1alpha1.DeliveryResource, deliveryName string, outputs realizer.Outputs) (templates.Template, *unstructured.Unstructured, *templates.Output, error) { - executedResourceOrder = append(executedResourceOrder, resource.Name) - Expect(deliveryName).To(Equal("greatest-delivery")) - if resource.Name == "resource1" { - Expect(outputs).To(Equal(realizer.NewOutputs())) - template, err := templates.NewModelFromAPI(template1) - Expect(err).NotTo(HaveOccurred()) - stampedObj := &unstructured.Unstructured{} - stampedObj.SetName("obj1") - return template, stampedObj, outputFromFirstResource, nil - } - - if resource.Name == "resource2" { - expectedSecondResourceOutputs := realizer.NewOutputs() - expectedSecondResourceOutputs.AddOutput("resource1", outputFromFirstResource) - Expect(outputs).To(Equal(expectedSecondResourceOutputs)) - } - - template, err := templates.NewModelFromAPI(template2) - Expect(err).NotTo(HaveOccurred()) - stampedObj := &unstructured.Unstructured{} - stampedObj.SetName("obj2") - return template, stampedObj, &templates.Output{}, nil - }) - }) - - It("realizes each resource in delivery order, accumulating output for each subsequent resource", func() { - resourceStatuses := statuses.NewResourceStatuses(nil, conditions.AddConditionForResourceSubmittedDeliverable) - err := rlzr.Realize(context.TODO(), resourceRealizer, delivery, resourceStatuses) - Expect(err).To(Succeed()) - - Expect(executedResourceOrder).To(Equal([]string{"resource1", "resource2"})) - - currentStatuses := resourceStatuses.GetCurrent() - Expect(currentStatuses).To(HaveLen(2)) - - Expect(currentStatuses[0].Name).To(Equal(resource1.Name)) - Expect(currentStatuses[0].TemplateRef.Name).To(Equal(template1.Name)) - Expect(currentStatuses[0].StampedRef.Name).To(Equal("obj1")) - Expect(currentStatuses[0].Inputs).To(BeNil()) - Expect(len(currentStatuses[0].Outputs)).To(Equal(2)) - Expect(currentStatuses[0].Outputs[0]).To(MatchFields(IgnoreExtras, - Fields{ - "Name": Equal("url"), - "Preview": Equal("whatever\n"), - "Digest": Equal("sha256:cd293be6cea034bd45a0352775a219ef5dc7825ce55d1f7dae9762d80ce64411"), - }, - )) - Expect(currentStatuses[0].Outputs[1]).To(MatchFields(IgnoreExtras, - Fields{ - "Name": Equal("revision"), - "Preview": Equal("whatever-rev\n"), - "Digest": Equal("sha256:2c743bc345f5599513bde37c18a4b761a0ec1f2c8de4201b745caa46d24784ec"), - }, - )) - Expect(time.Since(currentStatuses[0].Outputs[0].LastTransitionTime.Time)).To(BeNumerically("<", time.Second)) - Expect(len(currentStatuses[0].Conditions)).To(Equal(2)) - Expect(currentStatuses[0].Conditions[0]).To(MatchFields(IgnoreExtras, - Fields{ - "Type": Equal("ResourceSubmitted"), - "Status": Equal(metav1.ConditionTrue), - }, - )) - Expect(currentStatuses[0].Conditions[1]).To(MatchFields(IgnoreExtras, - Fields{ - "Type": Equal("Ready"), - "Status": Equal(metav1.ConditionTrue), - }, - )) - - Expect(currentStatuses[1].Name).To(Equal(resource2.Name)) - Expect(currentStatuses[1].TemplateRef.Name).To(Equal(template2.Name)) - Expect(currentStatuses[1].StampedRef.Name).To(Equal("obj2")) - Expect(len(currentStatuses[1].Inputs)).To(Equal(1)) - Expect(currentStatuses[1].Inputs).To(Equal([]v1alpha1.Input{{Name: "resource1"}})) - Expect(currentStatuses[1].Outputs).To(BeNil()) - Expect(len(currentStatuses[1].Conditions)).To(Equal(2)) - Expect(currentStatuses[1].Conditions[0]).To(MatchFields(IgnoreExtras, - Fields{ - "Type": Equal("ResourceSubmitted"), - "Status": Equal(metav1.ConditionTrue), - }, - )) - Expect(currentStatuses[1].Conditions[1]).To(MatchFields(IgnoreExtras, - Fields{ - "Type": Equal("Ready"), - "Status": Equal(metav1.ConditionTrue), - }, - )) - }) - - It("returns the first error encountered realizing a resource and continues to realize", func() { - template, err := templates.NewModelFromAPI(template2) - Expect(err).NotTo(HaveOccurred()) - resourceRealizer.DoReturnsOnCall(0, nil, nil, nil, errors.New("realizing is hard")) - resourceRealizer.DoReturnsOnCall(1, template, &unstructured.Unstructured{}, nil, nil) - - resourceStatuses := statuses.NewResourceStatuses(nil, conditions.AddConditionForResourceSubmittedDeliverable) - err = rlzr.Realize(context.TODO(), resourceRealizer, delivery, resourceStatuses) - Expect(err).To(MatchError("realizing is hard")) - - currentStatuses := resourceStatuses.GetCurrent() - Expect(currentStatuses).To(HaveLen(2)) - - Expect(currentStatuses[0].Name).To(Equal("resource1")) - Expect(currentStatuses[0].TemplateRef).To(BeNil()) - Expect(currentStatuses[0].StampedRef).To(BeNil()) - Expect(currentStatuses[1].TemplateRef.Name).To(Equal(template2.Name)) - }) - }) - - Context("there are previous resources", func() { - var ( - templateModel1 templates.Template - templateModel2 templates.Template - templateModel3 templates.Template - obj *unstructured.Unstructured - previousResources []v1alpha1.ResourceStatus - previousTime metav1.Time - delivery *v1alpha1.ClusterDelivery - ) - BeforeEach(func() { - previousTime = metav1.NewTime(time.Now()) - previousResources = []v1alpha1.ResourceStatus{ - { - RealizedResource: v1alpha1.RealizedResource{ - Name: "resource1", - StampedRef: &corev1.ObjectReference{ - Kind: "GitRepository", - Namespace: "", - Name: "", - APIVersion: "", - }, - TemplateRef: &corev1.ObjectReference{ - Kind: "ClusterSourceTemplate", - Name: "my-source-template", - APIVersion: "", - }, - Inputs: nil, - Outputs: []v1alpha1.Output{ - { - Name: "url", - Preview: "http://example.com\n", - Digest: fmt.Sprintf("sha256:%x", sha256.Sum256([]byte("http://example.com\n"))), - LastTransitionTime: previousTime, - }, - { - Name: "revision", - Preview: "main\n", - Digest: fmt.Sprintf("sha256:%x", sha256.Sum256([]byte("main\n"))), - LastTransitionTime: previousTime, - }, - }, - }, - }, - { - RealizedResource: v1alpha1.RealizedResource{ - Name: "resource2", - StampedRef: &corev1.ObjectReference{ - Kind: "Image", - Namespace: "", - Name: "", - APIVersion: "", - }, - TemplateRef: &corev1.ObjectReference{ - Kind: "ClusterImageTemplate", - Name: "my-image-template", - APIVersion: "", - }, - Inputs: []v1alpha1.Input{ - { - Name: "resource1", - }, - }, - Outputs: []v1alpha1.Output{ - { - Name: "config", - Preview: "whateve\nr", - Digest: fmt.Sprintf("sha256:%x", sha256.Sum256([]byte("whatever\n"))), - LastTransitionTime: previousTime, - }, - }, - }, - }, - { - RealizedResource: v1alpha1.RealizedResource{ - Name: "resource3", - StampedRef: &corev1.ObjectReference{ - Kind: "Config", - Namespace: "", - Name: "PreviousStampedObj", - APIVersion: "", - }, - TemplateRef: &corev1.ObjectReference{ - Kind: "ClusterConfigTemplate", - Name: "my-config-template", - APIVersion: "", - }, - Outputs: []v1alpha1.Output{ - { - Name: "config", - Preview: "whatever\n", - Digest: fmt.Sprintf("sha256:%x", sha256.Sum256([]byte("whatever\n"))), - LastTransitionTime: previousTime, - }, - }, - }, - }, - } - - resource1 := v1alpha1.DeliveryResource{ - Name: "resource1", - } - resource2 := v1alpha1.DeliveryResource{ - Name: "resource2", - Sources: []v1alpha1.ResourceReference{ - { - Name: "my-source", - Resource: "resource1", - }, - }, - } - resource3 := v1alpha1.DeliveryResource{ - Name: "resource3", - } - - delivery = &v1alpha1.ClusterDelivery{ - ObjectMeta: metav1.ObjectMeta{Name: "greatest-supply-chain"}, - Spec: v1alpha1.DeliverySpec{ - Resources: []v1alpha1.DeliveryResource{resource1, resource2, resource3}, - }, - } - - var err error - - template1 := &v1alpha1.ClusterSourceTemplate{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "my-source-2-template", - }, - } - templateModel1, err = templates.NewModelFromAPI(template1) - Expect(err).NotTo(HaveOccurred()) - - template2 := &v1alpha1.ClusterConfigTemplate{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "my-config-template", - }, - } - templateModel2, err = templates.NewModelFromAPI(template2) - Expect(err).NotTo(HaveOccurred()) - - template3 := &v1alpha1.ClusterConfigTemplate{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: "my-config-2-template", - }, - } - templateModel3, err = templates.NewModelFromAPI(template3) - Expect(err).NotTo(HaveOccurred()) - - resourceRealizer.DoReturnsOnCall(0, templateModel1, &unstructured.Unstructured{}, nil, nil) - resourceRealizer.DoReturnsOnCall(1, templateModel2, &unstructured.Unstructured{}, nil, nil) - resourceRealizer.DoReturnsOnCall(2, templateModel3, &unstructured.Unstructured{}, nil, nil) - }) - - It("realizes each resource and does not update last transition time since the resource has not changed", func() { - newOutput := &templates.Output{ - Source: &templates.Source{ - URL: "hi", - Revision: "bye", - }, - } - resourceRealizer.DoReturnsOnCall(0, templateModel1, &unstructured.Unstructured{}, newOutput, nil) - - oldOutput := &templates.Output{ - Config: "whatever", - } - resourceRealizer.DoReturnsOnCall(1, templateModel2, &unstructured.Unstructured{}, oldOutput, nil) - - oldOutput2 := &templates.Output{ - Config: "whatever", - } - resourceRealizer.DoReturnsOnCall(2, templateModel3, obj, oldOutput2, nil) - - resourceStatuses := statuses.NewResourceStatuses(previousResources, conditions.AddConditionForResourceSubmittedDeliverable) - err := rlzr.Realize(context.TODO(), resourceRealizer, delivery, resourceStatuses) - Expect(err).ToNot(HaveOccurred()) - - currentStatuses := resourceStatuses.GetCurrent() - var resource1Status v1alpha1.ResourceStatus - var resource2Status v1alpha1.ResourceStatus - var resource3Status v1alpha1.ResourceStatus - - for i := range currentStatuses { - switch currentStatuses[i].Name { - case "resource1": - resource1Status = currentStatuses[i] - case "resource2": - resource2Status = currentStatuses[i] - case "resource3": - resource3Status = currentStatuses[i] - } - } - - Expect(len(resource1Status.Outputs)).To(Equal(2)) - Expect(resource1Status.Outputs[0]).To(MatchFields(IgnoreExtras, - Fields{ - "Name": Equal("url"), - "Preview": Equal("hi\n"), - "Digest": HavePrefix("sha256"), - }, - )) - Expect(resource1Status.Outputs[1]).To(MatchFields(IgnoreExtras, - Fields{ - "Name": Equal("revision"), - "Preview": Equal("bye\n"), - "Digest": HavePrefix("sha256"), - }, - )) - Expect(resource1Status.Outputs[0].LastTransitionTime).ToNot(Equal(previousTime)) - Expect(resource1Status.Outputs[1].LastTransitionTime).ToNot(Equal(previousTime)) - - Expect(len(resource2Status.Outputs)).To(Equal(1)) - Expect(resource2Status.Outputs[0].LastTransitionTime).To(Equal(previousTime)) - - Expect(len(resource3Status.Outputs)).To(Equal(1)) - Expect(resource3Status.Outputs[0].LastTransitionTime).To(Equal(previousTime)) - }) - - Context("there is an error realizing resource 1", func() { - BeforeEach(func() { - resourceRealizer.DoReturnsOnCall(0, nil, nil, nil, errors.New("im in a bad state")) - resourceRealizer.DoReturnsOnCall(1, nil, nil, nil, errors.New("im missing inputs")) - - obj = &unstructured.Unstructured{} - obj.SetName("StampedObj") - - resourceRealizer.DoReturnsOnCall(2, templateModel3, obj, nil, nil) - }) - - It("the status uses the previous resource for resource 1 and resource 2", func() { - resourceStatuses := statuses.NewResourceStatuses(previousResources, conditions.AddConditionForResourceSubmittedDeliverable) - err := rlzr.Realize(context.TODO(), resourceRealizer, delivery, resourceStatuses) - Expect(err).To(MatchError("im in a bad state")) - - currentStatuses := resourceStatuses.GetCurrent() - Expect(currentStatuses).To(HaveLen(3)) - - var resource1Status v1alpha1.ResourceStatus - var resource2Status v1alpha1.ResourceStatus - var resource3Status v1alpha1.ResourceStatus - - for i := range currentStatuses { - switch currentStatuses[i].Name { - case "resource1": - resource1Status = currentStatuses[i] - case "resource2": - resource2Status = currentStatuses[i] - case "resource3": - resource3Status = currentStatuses[i] - } - } - - Expect(resource1Status.Name).To(Equal(previousResources[0].Name)) - Expect(resource1Status.StampedRef).To(Equal(previousResources[0].StampedRef)) - Expect(resource1Status.TemplateRef).To(Equal(previousResources[0].TemplateRef)) - Expect(resource1Status.Inputs).To(Equal(previousResources[0].Inputs)) - Expect(resource1Status.Outputs).To(Equal(previousResources[0].Outputs)) - Expect(len(resource1Status.Conditions)).To(Equal(2)) - Expect(resource1Status.Conditions[0]).To(MatchFields(IgnoreExtras, - Fields{ - "Type": Equal("ResourceSubmitted"), - "Status": Equal(metav1.ConditionFalse), - }, - )) - Expect(resource1Status.Conditions[1]).To(MatchFields(IgnoreExtras, - Fields{ - "Type": Equal("Ready"), - "Status": Equal(metav1.ConditionFalse), - }, - )) - - Expect(resource2Status.Name).To(Equal(previousResources[1].Name)) - Expect(resource2Status.StampedRef).To(Equal(previousResources[1].StampedRef)) - Expect(resource2Status.TemplateRef).To(Equal(previousResources[1].TemplateRef)) - Expect(resource2Status.Inputs).To(Equal(previousResources[1].Inputs)) - Expect(resource2Status.Outputs).To(Equal(previousResources[1].Outputs)) - Expect(resource2Status.Conditions[0]).To(MatchFields(IgnoreExtras, - Fields{ - "Type": Equal("ResourceSubmitted"), - "Status": Equal(metav1.ConditionFalse), - }, - )) - Expect(resource2Status.Conditions[1]).To(MatchFields(IgnoreExtras, - Fields{ - "Type": Equal("Ready"), - "Status": Equal(metav1.ConditionFalse), - }, - )) - - Expect(resource3Status).ToNot(Equal(previousResources[2])) - Expect(resource3Status.StampedRef.Name).To(Equal("StampedObj")) - }) - }) - }) -}) diff --git a/pkg/realizer/workload/outputs.go b/pkg/realizer/outputs.go similarity index 80% rename from pkg/realizer/workload/outputs.go rename to pkg/realizer/outputs.go index 80f73da94..107833eeb 100644 --- a/pkg/realizer/workload/outputs.go +++ b/pkg/realizer/outputs.go @@ -12,10 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -package workload +package realizer import ( - "github.com/vmware-tanzu/cartographer/pkg/apis/v1alpha1" "github.com/vmware-tanzu/cartographer/pkg/templates" ) @@ -54,11 +53,12 @@ func (o Outputs) getResourceConfig(resourceName string) templates.Config { return output.Config } -func (o Outputs) GenerateInputs(resource *v1alpha1.SupplyChainResource) *templates.Inputs { +func (o Outputs) GenerateInputs(resource OwnerResource) *templates.Inputs { inputs := &templates.Inputs{ - Sources: map[string]templates.SourceInput{}, - Images: map[string]templates.ImageInput{}, - Configs: map[string]templates.ConfigInput{}, + Sources: map[string]templates.SourceInput{}, + Images: map[string]templates.ImageInput{}, + Configs: map[string]templates.ConfigInput{}, + Deployment: &templates.SourceInput{}, } for _, referenceSource := range resource.Sources { @@ -92,5 +92,15 @@ func (o Outputs) GenerateInputs(resource *v1alpha1.SupplyChainResource) *templat } } + if resource.Deployment != nil { + deployment := o.getResourceSource(resource.Deployment.Resource) + if deployment != nil { + inputs.Deployment = &templates.SourceInput{ + URL: deployment.URL, + Revision: deployment.Revision, + } + } + } + return inputs } diff --git a/pkg/realizer/workload/outputs_test.go b/pkg/realizer/outputs_test.go similarity index 92% rename from pkg/realizer/workload/outputs_test.go rename to pkg/realizer/outputs_test.go index 45a9e9dc2..c7dfe5dd0 100644 --- a/pkg/realizer/workload/outputs_test.go +++ b/pkg/realizer/outputs_test.go @@ -12,14 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -package workload_test +package realizer_test import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/vmware-tanzu/cartographer/pkg/apis/v1alpha1" - realizer "github.com/vmware-tanzu/cartographer/pkg/realizer/workload" + "github.com/vmware-tanzu/cartographer/pkg/realizer" "github.com/vmware-tanzu/cartographer/pkg/templates" ) @@ -40,7 +40,7 @@ var _ = Describe("Outputs", func() { Context("And the sources have a match with the outputs", func() { It("Adds sources to inputs", func() { - resource := &v1alpha1.SupplyChainResource{ + resource := realizer.OwnerResource{ Sources: []v1alpha1.ResourceReference{ { Name: "source-ref", @@ -58,7 +58,7 @@ var _ = Describe("Outputs", func() { Context("And the sources do not have a match with the outputs", func() { It("Does not add sources to inputs", func() { - resource := &v1alpha1.SupplyChainResource{ + resource := realizer.OwnerResource{ Sources: []v1alpha1.ResourceReference{ { Name: "source-ref", @@ -84,7 +84,7 @@ var _ = Describe("Outputs", func() { Context("And the images have a match with the outputs", func() { It("Adds images to inputs", func() { - resource := &v1alpha1.SupplyChainResource{ + resource := realizer.OwnerResource{ Images: []v1alpha1.ResourceReference{ { Name: "image-ref", @@ -101,7 +101,7 @@ var _ = Describe("Outputs", func() { Context("And the images do not have a match with the outputs", func() { It("Does not add images to inputs", func() { - resource := &v1alpha1.SupplyChainResource{ + resource := realizer.OwnerResource{ Images: []v1alpha1.ResourceReference{ { Name: "image-ref", @@ -128,7 +128,7 @@ var _ = Describe("Outputs", func() { Context("And the configs have a match with the outputs", func() { It("Adds configs to inputs", func() { - resource := &v1alpha1.SupplyChainResource{ + resource := realizer.OwnerResource{ Configs: []v1alpha1.ResourceReference{ { Name: "config-ref", @@ -145,7 +145,7 @@ var _ = Describe("Outputs", func() { Context("And the configs do not have a match with the outputs", func() { It("Does not add configs to inputs", func() { - resource := &v1alpha1.SupplyChainResource{ + resource := realizer.OwnerResource{ Configs: []v1alpha1.ResourceReference{ { Name: "config-ref", diff --git a/pkg/realizer/deliverable/realizer.go b/pkg/realizer/realizer.go similarity index 67% rename from pkg/realizer/deliverable/realizer.go rename to pkg/realizer/realizer.go index 3b38a1cbe..e0760fb13 100644 --- a/pkg/realizer/deliverable/realizer.go +++ b/pkg/realizer/realizer.go @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -package deliverable +package realizer -//go:generate go run -modfile ../../../hack/tools/go.mod github.com/maxbrunsfeld/counterfeiter/v6 -generate +//go:generate go run -modfile ../../hack/tools/go.mod github.com/maxbrunsfeld/counterfeiter/v6 -generate import ( "context" @@ -31,29 +31,68 @@ import ( "github.com/vmware-tanzu/cartographer/pkg/templates" ) +func MakeSupplychainOwnerResources(supplyChain *v1alpha1.ClusterSupplyChain) []OwnerResource { + var resources []OwnerResource + for _, resource := range supplyChain.Spec.Resources { + resources = append(resources, OwnerResource{ + Name: resource.Name, + TemplateRef: v1alpha1.TemplateReference{ + Kind: resource.TemplateRef.Kind, + Name: resource.TemplateRef.Name, + }, + TemplateOptions: resource.TemplateRef.Options, + Params: resource.Params, + Sources: resource.Sources, + Images: resource.Images, + Configs: resource.Configs, + }) + } + return resources +} + +func MakeDeliveryOwnerResources(delivery *v1alpha1.ClusterDelivery) []OwnerResource { + var resources []OwnerResource + for _, resource := range delivery.Spec.Resources { + resources = append(resources, OwnerResource{ + Name: resource.Name, + TemplateRef: v1alpha1.TemplateReference{ + Kind: resource.TemplateRef.Kind, + Name: resource.TemplateRef.Name, + }, + TemplateOptions: resource.TemplateRef.Options, + Params: resource.Params, + Sources: resource.Sources, + Configs: resource.Configs, + Deployment: resource.Deployment, + }) + } + return resources +} + //counterfeiter:generate . Realizer type Realizer interface { - Realize(ctx context.Context, resourceRealizer ResourceRealizer, delivery *v1alpha1.ClusterDelivery, resourceStatuses statuses.ResourceStatuses) error + Realize(ctx context.Context, resourceRealizer ResourceRealizer, blueprintName string, ownerResources []OwnerResource, resourceStatuses statuses.ResourceStatuses) error } +type ResourceLabeler func(resource OwnerResource) templates.Labels + type realizer struct{} func NewRealizer() Realizer { return &realizer{} } -func (r *realizer) Realize(ctx context.Context, resourceRealizer ResourceRealizer, delivery *v1alpha1.ClusterDelivery, resourceStatuses statuses.ResourceStatuses) error { +func (r *realizer) Realize(ctx context.Context, resourceRealizer ResourceRealizer, blueprintName string, ownerResources []OwnerResource, resourceStatuses statuses.ResourceStatuses) error { log := logr.FromContextOrDiscard(ctx) log.V(logger.DEBUG).Info("Realize") outs := NewOutputs() var firstError error - for i := range delivery.Spec.Resources { - resource := delivery.Spec.Resources[i] + for _, resource := range ownerResources { log = log.WithValues("resource", resource.Name) ctx = logr.NewContext(ctx, log) - template, stampedObject, out, err := resourceRealizer.Do(ctx, &resource, delivery.Name, outs) + template, stampedObject, out, err := resourceRealizer.Do(ctx, resource, blueprintName, outs) if stampedObject != nil { log.V(logger.DEBUG).Info("realized resource as object", @@ -86,7 +125,7 @@ func (r *realizer) Realize(ctx context.Context, resourceRealizer ResourceRealize return firstError } -func generateRealizedResource(resource v1alpha1.DeliveryResource, template templates.Template, stampedObject *unstructured.Unstructured, output *templates.Output, previousRealizedResource *v1alpha1.RealizedResource) *v1alpha1.RealizedResource { +func generateRealizedResource(resource OwnerResource, template templates.Template, stampedObject *unstructured.Unstructured, output *templates.Output, previousRealizedResource *v1alpha1.RealizedResource) *v1alpha1.RealizedResource { if previousRealizedResource == nil { previousRealizedResource = &v1alpha1.RealizedResource{} } @@ -95,13 +134,19 @@ func generateRealizedResource(resource v1alpha1.DeliveryResource, template templ for _, source := range resource.Sources { inputs = append(inputs, v1alpha1.Input{Name: source.Resource}) } - for _, config := range resource.Configs { - inputs = append(inputs, v1alpha1.Input{Name: config.Resource}) + + for _, image := range resource.Images { + inputs = append(inputs, v1alpha1.Input{Name: image.Resource}) } + if resource.Deployment != nil { inputs = append(inputs, v1alpha1.Input{Name: resource.Deployment.Resource}) } + for _, config := range resource.Configs { + inputs = append(inputs, v1alpha1.Input{Name: config.Resource}) + } + var templateRef *corev1.ObjectReference var outputs []v1alpha1.Output if template != nil { diff --git a/pkg/realizer/workload/realizer_suite_test.go b/pkg/realizer/realizer_suite_test.go similarity index 97% rename from pkg/realizer/workload/realizer_suite_test.go rename to pkg/realizer/realizer_suite_test.go index 50b60bc50..485cdf439 100644 --- a/pkg/realizer/workload/realizer_suite_test.go +++ b/pkg/realizer/realizer_suite_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package workload_test +package realizer_test import ( "testing" diff --git a/pkg/realizer/workload/realizer_test.go b/pkg/realizer/realizer_test.go similarity index 83% rename from pkg/realizer/workload/realizer_test.go rename to pkg/realizer/realizer_test.go index 1bc32e6c2..7046dabb5 100644 --- a/pkg/realizer/workload/realizer_test.go +++ b/pkg/realizer/realizer_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package workload_test +package realizer_test import ( "context" @@ -30,20 +30,20 @@ import ( "github.com/vmware-tanzu/cartographer/pkg/apis/v1alpha1" "github.com/vmware-tanzu/cartographer/pkg/conditions" + "github.com/vmware-tanzu/cartographer/pkg/realizer" + "github.com/vmware-tanzu/cartographer/pkg/realizer/realizerfakes" "github.com/vmware-tanzu/cartographer/pkg/realizer/statuses" - realizer "github.com/vmware-tanzu/cartographer/pkg/realizer/workload" - "github.com/vmware-tanzu/cartographer/pkg/realizer/workload/workloadfakes" "github.com/vmware-tanzu/cartographer/pkg/templates" ) var _ = Describe("Realize", func() { var ( - resourceRealizer *workloadfakes.FakeResourceRealizer + resourceRealizer *realizerfakes.FakeResourceRealizer rlzr realizer.Realizer ) BeforeEach(func() { rlzr = realizer.NewRealizer() - resourceRealizer = &workloadfakes.FakeResourceRealizer{} + resourceRealizer = &realizerfakes.FakeResourceRealizer{} }) Context("there are no previous resources", func() { @@ -89,9 +89,9 @@ var _ = Describe("Realize", func() { outputFromFirstResource := &templates.Output{Image: "whatever"} - resourceRealizer.DoCalls(func(ctx context.Context, resource *v1alpha1.SupplyChainResource, supplyChainName string, outputs realizer.Outputs) (templates.Template, *unstructured.Unstructured, *templates.Output, error) { + resourceRealizer.DoCalls(func(ctx context.Context, resource realizer.OwnerResource, blueprintName string, outputs realizer.Outputs) (templates.Template, *unstructured.Unstructured, *templates.Output, error) { executedResourceOrder = append(executedResourceOrder, resource.Name) - Expect(supplyChainName).To(Equal("greatest-supply-chain")) + Expect(blueprintName).To(Equal("greatest-supply-chain")) if resource.Name == "resource1" { Expect(outputs).To(Equal(realizer.NewOutputs())) template, err := templates.NewModelFromAPI(template1) @@ -117,55 +117,55 @@ var _ = Describe("Realize", func() { It("realizes each resource in supply chain order, accumulating output for each subsequent resource", func() { resourceStatuses := statuses.NewResourceStatuses(nil, conditions.AddConditionForResourceSubmittedWorkload) - err := rlzr.Realize(context.TODO(), resourceRealizer, supplyChain, resourceStatuses) + err := rlzr.Realize(context.TODO(), resourceRealizer, supplyChain.Name, realizer.MakeSupplychainOwnerResources(supplyChain), resourceStatuses) Expect(err).ToNot(HaveOccurred()) + currentResourceStatuses := resourceStatuses.GetCurrent() Expect(executedResourceOrder).To(Equal([]string{"resource1", "resource2"})) - currentStatuses := resourceStatuses.GetCurrent() - Expect(currentStatuses).To(HaveLen(2)) - - Expect(currentStatuses[0].Name).To(Equal(resource1.Name)) - Expect(currentStatuses[0].TemplateRef.Name).To(Equal(template1.Name)) - Expect(currentStatuses[0].StampedRef.Name).To(Equal("obj1")) - Expect(currentStatuses[0].Inputs).To(BeNil()) - Expect(len(currentStatuses[0].Outputs)).To(Equal(1)) - Expect(currentStatuses[0].Outputs[0]).To(MatchFields(IgnoreExtras, + Expect(currentResourceStatuses).To(HaveLen(2)) + + Expect(currentResourceStatuses[0].Name).To(Equal(resource1.Name)) + Expect(currentResourceStatuses[0].TemplateRef.Name).To(Equal(template1.Name)) + Expect(currentResourceStatuses[0].StampedRef.Name).To(Equal("obj1")) + Expect(currentResourceStatuses[0].Inputs).To(BeNil()) + Expect(len(currentResourceStatuses[0].Outputs)).To(Equal(1)) + Expect(currentResourceStatuses[0].Outputs[0]).To(MatchFields(IgnoreExtras, Fields{ "Name": Equal("image"), "Preview": Equal("whatever\n"), "Digest": HavePrefix("sha256"), }, )) - Expect(time.Since(currentStatuses[0].Outputs[0].LastTransitionTime.Time)).To(BeNumerically("<", time.Second)) - Expect(len(currentStatuses[0].Conditions)).To(Equal(2)) - Expect(currentStatuses[0].Conditions[0]).To(MatchFields(IgnoreExtras, + Expect(time.Since(currentResourceStatuses[0].Outputs[0].LastTransitionTime.Time)).To(BeNumerically("<", time.Second)) + Expect(len(currentResourceStatuses[0].Conditions)).To(Equal(2)) + Expect(currentResourceStatuses[0].Conditions[0]).To(MatchFields(IgnoreExtras, Fields{ "Type": Equal("ResourceSubmitted"), "Status": Equal(metav1.ConditionTrue), }, )) - Expect(currentStatuses[0].Conditions[1]).To(MatchFields(IgnoreExtras, + Expect(currentResourceStatuses[0].Conditions[1]).To(MatchFields(IgnoreExtras, Fields{ "Type": Equal("Ready"), "Status": Equal(metav1.ConditionTrue), }, )) - Expect(currentStatuses[1].Name).To(Equal(resource2.Name)) - Expect(currentStatuses[1].TemplateRef.Name).To(Equal(template2.Name)) - Expect(currentStatuses[1].StampedRef.Name).To(Equal("obj2")) - Expect(len(currentStatuses[1].Inputs)).To(Equal(1)) - Expect(currentStatuses[1].Inputs).To(Equal([]v1alpha1.Input{{Name: "resource1"}})) - Expect(currentStatuses[1].Outputs).To(BeNil()) - Expect(len(currentStatuses[1].Conditions)).To(Equal(2)) - Expect(currentStatuses[1].Conditions[0]).To(MatchFields(IgnoreExtras, + Expect(currentResourceStatuses[1].Name).To(Equal(resource2.Name)) + Expect(currentResourceStatuses[1].TemplateRef.Name).To(Equal(template2.Name)) + Expect(currentResourceStatuses[1].StampedRef.Name).To(Equal("obj2")) + Expect(len(currentResourceStatuses[1].Inputs)).To(Equal(1)) + Expect(currentResourceStatuses[1].Inputs).To(Equal([]v1alpha1.Input{{Name: "resource1"}})) + Expect(currentResourceStatuses[1].Outputs).To(BeNil()) + Expect(len(currentResourceStatuses[0].Conditions)).To(Equal(2)) + Expect(currentResourceStatuses[1].Conditions[0]).To(MatchFields(IgnoreExtras, Fields{ "Type": Equal("ResourceSubmitted"), "Status": Equal(metav1.ConditionTrue), }, )) - Expect(currentStatuses[1].Conditions[1]).To(MatchFields(IgnoreExtras, + Expect(currentResourceStatuses[1].Conditions[1]).To(MatchFields(IgnoreExtras, Fields{ "Type": Equal("Ready"), "Status": Equal(metav1.ConditionTrue), @@ -180,16 +180,16 @@ var _ = Describe("Realize", func() { resourceRealizer.DoReturnsOnCall(1, template, &unstructured.Unstructured{}, nil, nil) resourceStatuses := statuses.NewResourceStatuses(nil, conditions.AddConditionForResourceSubmittedWorkload) - err = rlzr.Realize(context.TODO(), resourceRealizer, supplyChain, resourceStatuses) + err = rlzr.Realize(context.TODO(), resourceRealizer, supplyChain.Name, realizer.MakeSupplychainOwnerResources(supplyChain), resourceStatuses) Expect(err).To(MatchError("realizing is hard")) - currentStatuses := resourceStatuses.GetCurrent() - Expect(currentStatuses).To(HaveLen(2)) + currentResourceStatuses := resourceStatuses.GetCurrent() + Expect(currentResourceStatuses).To(HaveLen(2)) - Expect(currentStatuses[0].Name).To(Equal("resource1")) - Expect(currentStatuses[0].TemplateRef).To(BeNil()) - Expect(currentStatuses[0].StampedRef).To(BeNil()) - Expect(currentStatuses[1].TemplateRef.Name).To(Equal(template2.Name)) + Expect(currentResourceStatuses[0].Name).To(Equal("resource1")) + Expect(currentResourceStatuses[0].TemplateRef).To(BeNil()) + Expect(currentResourceStatuses[0].StampedRef).To(BeNil()) + Expect(currentResourceStatuses[1].TemplateRef.Name).To(Equal(template2.Name)) }) }) @@ -338,11 +338,10 @@ var _ = Describe("Realize", func() { resourceRealizer.DoReturnsOnCall(2, templateModel3, obj, oldOutput2, nil) resourceStatuses := statuses.NewResourceStatuses(previousResources, conditions.AddConditionForResourceSubmittedWorkload) - err := rlzr.Realize(context.TODO(), resourceRealizer, supplyChain, resourceStatuses) + err := rlzr.Realize(context.TODO(), resourceRealizer, supplyChain.Name, realizer.MakeSupplychainOwnerResources(supplyChain), resourceStatuses) Expect(err).ToNot(HaveOccurred()) currentStatuses := resourceStatuses.GetCurrent() - var resource1Status v1alpha1.ResourceStatus var resource2Status v1alpha1.ResourceStatus var resource3Status v1alpha1.ResourceStatus @@ -396,7 +395,7 @@ var _ = Describe("Realize", func() { It("the status uses the previous resource for resource 2", func() { resourceStatuses := statuses.NewResourceStatuses(previousResources, conditions.AddConditionForResourceSubmittedWorkload) - err := rlzr.Realize(context.TODO(), resourceRealizer, supplyChain, resourceStatuses) + err := rlzr.Realize(context.TODO(), resourceRealizer, supplyChain.Name, realizer.MakeSupplychainOwnerResources(supplyChain), resourceStatuses) Expect(err).To(MatchError("im in a bad state")) currentStatuses := resourceStatuses.GetCurrent() diff --git a/pkg/realizer/workload/workloadfakes/fake_client.go b/pkg/realizer/realizerfakes/fake_client.go similarity index 99% rename from pkg/realizer/workload/workloadfakes/fake_client.go rename to pkg/realizer/realizerfakes/fake_client.go index 648bd57b8..dfb1d7ed0 100644 --- a/pkg/realizer/workload/workloadfakes/fake_client.go +++ b/pkg/realizer/realizerfakes/fake_client.go @@ -1,5 +1,5 @@ // Code generated by counterfeiter. DO NOT EDIT. -package workloadfakes +package realizerfakes import ( "context" diff --git a/pkg/realizer/workload/workloadfakes/fake_realizer.go b/pkg/realizer/realizerfakes/fake_realizer.go similarity index 70% rename from pkg/realizer/workload/workloadfakes/fake_realizer.go rename to pkg/realizer/realizerfakes/fake_realizer.go index 64107c886..07403f026 100644 --- a/pkg/realizer/workload/workloadfakes/fake_realizer.go +++ b/pkg/realizer/realizerfakes/fake_realizer.go @@ -1,23 +1,23 @@ // Code generated by counterfeiter. DO NOT EDIT. -package workloadfakes +package realizerfakes import ( "context" "sync" - "github.com/vmware-tanzu/cartographer/pkg/apis/v1alpha1" + "github.com/vmware-tanzu/cartographer/pkg/realizer" "github.com/vmware-tanzu/cartographer/pkg/realizer/statuses" - "github.com/vmware-tanzu/cartographer/pkg/realizer/workload" ) type FakeRealizer struct { - RealizeStub func(context.Context, workload.ResourceRealizer, *v1alpha1.ClusterSupplyChain, statuses.ResourceStatuses) error + RealizeStub func(context.Context, realizer.ResourceRealizer, string, []realizer.OwnerResource, statuses.ResourceStatuses) error realizeMutex sync.RWMutex realizeArgsForCall []struct { arg1 context.Context - arg2 workload.ResourceRealizer - arg3 *v1alpha1.ClusterSupplyChain - arg4 statuses.ResourceStatuses + arg2 realizer.ResourceRealizer + arg3 string + arg4 []realizer.OwnerResource + arg5 statuses.ResourceStatuses } realizeReturns struct { result1 error @@ -29,21 +29,27 @@ type FakeRealizer struct { invocationsMutex sync.RWMutex } -func (fake *FakeRealizer) Realize(arg1 context.Context, arg2 workload.ResourceRealizer, arg3 *v1alpha1.ClusterSupplyChain, arg4 statuses.ResourceStatuses) error { +func (fake *FakeRealizer) Realize(arg1 context.Context, arg2 realizer.ResourceRealizer, arg3 string, arg4 []realizer.OwnerResource, arg5 statuses.ResourceStatuses) error { + var arg4Copy []realizer.OwnerResource + if arg4 != nil { + arg4Copy = make([]realizer.OwnerResource, len(arg4)) + copy(arg4Copy, arg4) + } fake.realizeMutex.Lock() ret, specificReturn := fake.realizeReturnsOnCall[len(fake.realizeArgsForCall)] fake.realizeArgsForCall = append(fake.realizeArgsForCall, struct { arg1 context.Context - arg2 workload.ResourceRealizer - arg3 *v1alpha1.ClusterSupplyChain - arg4 statuses.ResourceStatuses - }{arg1, arg2, arg3, arg4}) + arg2 realizer.ResourceRealizer + arg3 string + arg4 []realizer.OwnerResource + arg5 statuses.ResourceStatuses + }{arg1, arg2, arg3, arg4Copy, arg5}) stub := fake.RealizeStub fakeReturns := fake.realizeReturns - fake.recordInvocation("Realize", []interface{}{arg1, arg2, arg3, arg4}) + fake.recordInvocation("Realize", []interface{}{arg1, arg2, arg3, arg4Copy, arg5}) fake.realizeMutex.Unlock() if stub != nil { - return stub(arg1, arg2, arg3, arg4) + return stub(arg1, arg2, arg3, arg4, arg5) } if specificReturn { return ret.result1 @@ -57,17 +63,17 @@ func (fake *FakeRealizer) RealizeCallCount() int { return len(fake.realizeArgsForCall) } -func (fake *FakeRealizer) RealizeCalls(stub func(context.Context, workload.ResourceRealizer, *v1alpha1.ClusterSupplyChain, statuses.ResourceStatuses) error) { +func (fake *FakeRealizer) RealizeCalls(stub func(context.Context, realizer.ResourceRealizer, string, []realizer.OwnerResource, statuses.ResourceStatuses) error) { fake.realizeMutex.Lock() defer fake.realizeMutex.Unlock() fake.RealizeStub = stub } -func (fake *FakeRealizer) RealizeArgsForCall(i int) (context.Context, workload.ResourceRealizer, *v1alpha1.ClusterSupplyChain, statuses.ResourceStatuses) { +func (fake *FakeRealizer) RealizeArgsForCall(i int) (context.Context, realizer.ResourceRealizer, string, []realizer.OwnerResource, statuses.ResourceStatuses) { fake.realizeMutex.RLock() defer fake.realizeMutex.RUnlock() argsForCall := fake.realizeArgsForCall[i] - return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4 + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4, argsForCall.arg5 } func (fake *FakeRealizer) RealizeReturns(result1 error) { @@ -117,4 +123,4 @@ func (fake *FakeRealizer) recordInvocation(key string, args []interface{}) { fake.invocations[key] = append(fake.invocations[key], args) } -var _ workload.Realizer = new(FakeRealizer) +var _ realizer.Realizer = new(FakeRealizer) diff --git a/pkg/realizer/workload/workloadfakes/fake_resource_realizer.go b/pkg/realizer/realizerfakes/fake_resource_realizer.go similarity index 81% rename from pkg/realizer/workload/workloadfakes/fake_resource_realizer.go rename to pkg/realizer/realizerfakes/fake_resource_realizer.go index 25d2b1843..44f63b999 100644 --- a/pkg/realizer/workload/workloadfakes/fake_resource_realizer.go +++ b/pkg/realizer/realizerfakes/fake_resource_realizer.go @@ -1,24 +1,23 @@ // Code generated by counterfeiter. DO NOT EDIT. -package workloadfakes +package realizerfakes import ( "context" "sync" - "github.com/vmware-tanzu/cartographer/pkg/apis/v1alpha1" - "github.com/vmware-tanzu/cartographer/pkg/realizer/workload" + "github.com/vmware-tanzu/cartographer/pkg/realizer" "github.com/vmware-tanzu/cartographer/pkg/templates" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) type FakeResourceRealizer struct { - DoStub func(context.Context, *v1alpha1.SupplyChainResource, string, workload.Outputs) (templates.Template, *unstructured.Unstructured, *templates.Output, error) + DoStub func(context.Context, realizer.OwnerResource, string, realizer.Outputs) (templates.Template, *unstructured.Unstructured, *templates.Output, error) doMutex sync.RWMutex doArgsForCall []struct { arg1 context.Context - arg2 *v1alpha1.SupplyChainResource + arg2 realizer.OwnerResource arg3 string - arg4 workload.Outputs + arg4 realizer.Outputs } doReturns struct { result1 templates.Template @@ -36,14 +35,14 @@ type FakeResourceRealizer struct { invocationsMutex sync.RWMutex } -func (fake *FakeResourceRealizer) Do(arg1 context.Context, arg2 *v1alpha1.SupplyChainResource, arg3 string, arg4 workload.Outputs) (templates.Template, *unstructured.Unstructured, *templates.Output, error) { +func (fake *FakeResourceRealizer) Do(arg1 context.Context, arg2 realizer.OwnerResource, arg3 string, arg4 realizer.Outputs) (templates.Template, *unstructured.Unstructured, *templates.Output, error) { fake.doMutex.Lock() ret, specificReturn := fake.doReturnsOnCall[len(fake.doArgsForCall)] fake.doArgsForCall = append(fake.doArgsForCall, struct { arg1 context.Context - arg2 *v1alpha1.SupplyChainResource + arg2 realizer.OwnerResource arg3 string - arg4 workload.Outputs + arg4 realizer.Outputs }{arg1, arg2, arg3, arg4}) stub := fake.DoStub fakeReturns := fake.doReturns @@ -64,13 +63,13 @@ func (fake *FakeResourceRealizer) DoCallCount() int { return len(fake.doArgsForCall) } -func (fake *FakeResourceRealizer) DoCalls(stub func(context.Context, *v1alpha1.SupplyChainResource, string, workload.Outputs) (templates.Template, *unstructured.Unstructured, *templates.Output, error)) { +func (fake *FakeResourceRealizer) DoCalls(stub func(context.Context, realizer.OwnerResource, string, realizer.Outputs) (templates.Template, *unstructured.Unstructured, *templates.Output, error)) { fake.doMutex.Lock() defer fake.doMutex.Unlock() fake.DoStub = stub } -func (fake *FakeResourceRealizer) DoArgsForCall(i int) (context.Context, *v1alpha1.SupplyChainResource, string, workload.Outputs) { +func (fake *FakeResourceRealizer) DoArgsForCall(i int) (context.Context, realizer.OwnerResource, string, realizer.Outputs) { fake.doMutex.RLock() defer fake.doMutex.RUnlock() argsForCall := fake.doArgsForCall[i] @@ -133,4 +132,4 @@ func (fake *FakeResourceRealizer) recordInvocation(key string, args []interface{ fake.invocations[key] = append(fake.invocations[key], args) } -var _ workload.ResourceRealizer = new(FakeResourceRealizer) +var _ realizer.ResourceRealizer = new(FakeResourceRealizer) diff --git a/pkg/realizer/workload/realizer.go b/pkg/realizer/workload/realizer.go deleted file mode 100644 index 9116cc8c4..000000000 --- a/pkg/realizer/workload/realizer.go +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2021 VMware -// -// 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 workload - -//go:generate go run -modfile ../../../hack/tools/go.mod github.com/maxbrunsfeld/counterfeiter/v6 -generate - -import ( - "context" - "time" - - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - - "github.com/vmware-tanzu/cartographer/pkg/apis/v1alpha1" - "github.com/vmware-tanzu/cartographer/pkg/logger" - "github.com/vmware-tanzu/cartographer/pkg/realizer/statuses" - "github.com/vmware-tanzu/cartographer/pkg/templates" -) - -//counterfeiter:generate . Realizer -type Realizer interface { - Realize(ctx context.Context, resourceRealizer ResourceRealizer, supplyChain *v1alpha1.ClusterSupplyChain, resourceStatuses statuses.ResourceStatuses) error -} - -type realizer struct{} - -func NewRealizer() Realizer { - return &realizer{} -} - -func (r *realizer) Realize(ctx context.Context, resourceRealizer ResourceRealizer, supplyChain *v1alpha1.ClusterSupplyChain, resourceStatuses statuses.ResourceStatuses) error { - log := logr.FromContextOrDiscard(ctx) - log.V(logger.DEBUG).Info("Realize") - - outs := NewOutputs() - var firstError error - - for i := range supplyChain.Spec.Resources { - resource := supplyChain.Spec.Resources[i] - template, stampedObject, out, err := resourceRealizer.Do(ctx, &resource, supplyChain.Name, outs) - - if stampedObject != nil { - log.V(logger.DEBUG).Info("realized resource as object", - "object", stampedObject) - } - - if err != nil { - log.Error(err, "failed to realize resource") - - if firstError == nil { - firstError = err - } - } - - outs.AddOutput(resource.Name, out) - - previousRealizedResource := resourceStatuses.GetPreviousRealizedResource(resource.Name) - - var realizedResource *v1alpha1.RealizedResource - - if (stampedObject == nil || template == nil) && previousRealizedResource != nil { - realizedResource = previousRealizedResource - } else { - realizedResource = generateRealizedResource(resource, template, stampedObject, out, previousRealizedResource) - } - - resourceStatuses.Add(realizedResource, err) - } - - return firstError -} - -func generateRealizedResource(resource v1alpha1.SupplyChainResource, template templates.Template, stampedObject *unstructured.Unstructured, output *templates.Output, previousRealizedResource *v1alpha1.RealizedResource) *v1alpha1.RealizedResource { - if previousRealizedResource == nil { - previousRealizedResource = &v1alpha1.RealizedResource{} - } - - var inputs []v1alpha1.Input - for _, source := range resource.Sources { - inputs = append(inputs, v1alpha1.Input{Name: source.Resource}) - } - for _, image := range resource.Images { - inputs = append(inputs, v1alpha1.Input{Name: image.Resource}) - } - for _, config := range resource.Configs { - inputs = append(inputs, v1alpha1.Input{Name: config.Resource}) - } - - var templateRef *corev1.ObjectReference - var outputs []v1alpha1.Output - if template != nil { - templateRef = &corev1.ObjectReference{ - Kind: template.GetKind(), - Name: template.GetName(), - APIVersion: v1alpha1.SchemeGroupVersion.String(), - } - - outputs = getOutputs(template, previousRealizedResource, output) - } - - var stampedRef *corev1.ObjectReference - if stampedObject != nil { - stampedRef = &corev1.ObjectReference{ - Kind: stampedObject.GetKind(), - Namespace: stampedObject.GetNamespace(), - Name: stampedObject.GetName(), - APIVersion: stampedObject.GetAPIVersion(), - } - } - - return &v1alpha1.RealizedResource{ - Name: resource.Name, - StampedRef: stampedRef, - TemplateRef: templateRef, - Inputs: inputs, - Outputs: outputs, - } -} - -func getOutputs(template templates.Template, previousRealizedResource *v1alpha1.RealizedResource, output *templates.Output) []v1alpha1.Output { - outputs, err := template.GenerateResourceOutput(output) - if err != nil { - outputs = previousRealizedResource.Outputs - } else { - currTime := metav1.NewTime(time.Now()) - for j, out := range outputs { - outputs[j].LastTransitionTime = currTime - for _, previousOutput := range previousRealizedResource.Outputs { - if previousOutput.Name == out.Name { - if previousOutput.Digest == out.Digest { - outputs[j].LastTransitionTime = previousOutput.LastTransitionTime - } - break - } - } - } - } - - return outputs -}