From 448280988234c6b9a85361aec81e1460cd6ecdb9 Mon Sep 17 00:00:00 2001 From: Kai Parry Date: Fri, 1 Sep 2023 15:20:33 +0200 Subject: [PATCH] feat(servicecatalog): add support for provisioned products from AWS Service Catalog Signed-off-by: Kai Parry --- apis/aws.go | 2 + apis/servicecatalog/generator-config.yaml | 99 ++ apis/servicecatalog/v1alpha1/custom_types.go | 4 + apis/servicecatalog/v1alpha1/zz_doc.go | 24 + apis/servicecatalog/v1alpha1/zz_enums.go | 303 ++++ .../v1alpha1/zz_generated.deepcopy.go | 1383 +++++++++++++++++ .../v1alpha1/zz_generated.managed.go | 97 ++ .../v1alpha1/zz_generated.managedlist.go | 30 + .../v1alpha1/zz_groupversion_info.go | 41 + .../v1alpha1/zz_provisioned_product.go | 197 +++ apis/servicecatalog/v1alpha1/zz_types.go | 330 ++++ .../servicecatalog/provisionedproduct.yaml | 24 + go.mod | 11 +- go.sum | 14 +- ...aws.crossplane.io_provisionedproducts.yaml | 511 ++++++ pkg/clients/servicecatalog/fake/fake.go | 69 + pkg/clients/servicecatalog/servicecatalog.go | 96 ++ pkg/controller/aws.go | 2 + .../provisionedproduct/lifecycle.go | 194 +++ .../provisionedproduct/setup.go | 138 ++ .../provisionedproduct/setup_test.go | 567 +++++++ .../provisionedproduct/utils.go | 101 ++ .../provisionedproduct/zz_controller.go | 315 ++++ .../provisionedproduct/zz_conversions.go | 262 ++++ pkg/controller/servicecatalog/setup.go | 34 + 25 files changed, 4839 insertions(+), 9 deletions(-) create mode 100644 apis/servicecatalog/generator-config.yaml create mode 100644 apis/servicecatalog/v1alpha1/custom_types.go create mode 100644 apis/servicecatalog/v1alpha1/zz_doc.go create mode 100644 apis/servicecatalog/v1alpha1/zz_enums.go create mode 100644 apis/servicecatalog/v1alpha1/zz_generated.deepcopy.go create mode 100644 apis/servicecatalog/v1alpha1/zz_generated.managed.go create mode 100644 apis/servicecatalog/v1alpha1/zz_generated.managedlist.go create mode 100644 apis/servicecatalog/v1alpha1/zz_groupversion_info.go create mode 100644 apis/servicecatalog/v1alpha1/zz_provisioned_product.go create mode 100644 apis/servicecatalog/v1alpha1/zz_types.go create mode 100644 examples/servicecatalog/provisionedproduct.yaml create mode 100644 package/crds/servicecatalog.aws.crossplane.io_provisionedproducts.yaml create mode 100644 pkg/clients/servicecatalog/fake/fake.go create mode 100644 pkg/clients/servicecatalog/servicecatalog.go create mode 100644 pkg/controller/servicecatalog/provisionedproduct/lifecycle.go create mode 100644 pkg/controller/servicecatalog/provisionedproduct/setup.go create mode 100644 pkg/controller/servicecatalog/provisionedproduct/setup_test.go create mode 100644 pkg/controller/servicecatalog/provisionedproduct/utils.go create mode 100644 pkg/controller/servicecatalog/provisionedproduct/zz_controller.go create mode 100644 pkg/controller/servicecatalog/provisionedproduct/zz_conversions.go create mode 100644 pkg/controller/servicecatalog/setup.go diff --git a/apis/aws.go b/apis/aws.go index 44d4847561..9b9986cbea 100644 --- a/apis/aws.go +++ b/apis/aws.go @@ -85,6 +85,7 @@ import ( s3control "github.com/crossplane-contrib/provider-aws/apis/s3control/v1alpha1" secretsmanagerv1alpha1 "github.com/crossplane-contrib/provider-aws/apis/secretsmanager/v1alpha1" secretsmanagerv1beta1 "github.com/crossplane-contrib/provider-aws/apis/secretsmanager/v1beta1" + servicecatalogv1alpha1 "github.com/crossplane-contrib/provider-aws/apis/servicecatalog/v1alpha1" servicediscoveryv1alpha1 "github.com/crossplane-contrib/provider-aws/apis/servicediscovery/v1alpha1" sesv2v1alpha1 "github.com/crossplane-contrib/provider-aws/apis/sesv2/v1alpha1" sfnv1alpha1 "github.com/crossplane-contrib/provider-aws/apis/sfn/v1alpha1" @@ -170,6 +171,7 @@ func init() { batchmanualv1alpha1.SchemeBuilder.AddToScheme, emrcontainersv1alpah1.SchemeBuilder.AddToScheme, autoscalingv1beta1.SchemeBuilder.AddToScheme, + servicecatalogv1alpha1.SchemeBuilder.AddToScheme, s3control.SchemeBuilder.AddToScheme, ) } diff --git a/apis/servicecatalog/generator-config.yaml b/apis/servicecatalog/generator-config.yaml new file mode 100644 index 0000000000..f3660c9814 --- /dev/null +++ b/apis/servicecatalog/generator-config.yaml @@ -0,0 +1,99 @@ +--- +ignore: + resource_names: + - Constraint + - PortfolioShare + - Portfolio + - Cluster + - Product + - ProvisionedProductPlan + - ProvisioningArtifact + - ServiceAction + - TagOption + field_paths: + - ProvisionProductInput.ProvisionToken + - UpdateProvisioningParameter.UsePreviousValue + - UpdateProvisioningPreferences.StackSetOperationType + - UpdateProvisionedProductInput.ProvisionedProductId + - TerminateProvisionedProductInput.ProvisionedProductId + shape_names: + - ProvisionedProductPlanSummary + - ProvisionedProductPlanDetails +operations: + ProvisionProduct: + operation_type: + - Create + resource_name: ProvisionedProduct + UpdateProvisionedProduct: + operation_type: + - Update + resource_name: ProvisionedProduct + DescribeProvisionedProduct: + operation_type: + - Read + resource_name: ProvisionedProduct + TerminateProvisionedProduct: + operation_type: + - Delete + resource_name: ProvisionedProduct +resources: + ProvisionedProduct: + exceptions: + errors: + 404: + code: ResourceNotFoundException + fields: + Outputs: + is_read_only: true + custom_field: + map_of: RecordOutput + Arn: + is_read_only: true + from: + operation: DescribeProvisionedProduct + path: ProvisionedProductDetail.Arn + CreatedTime: + is_read_only: true + from: + operation: DescribeProvisionedProduct + path: ProvisionedProductDetail.CreatedTime + LastProvisioningRecordId: + is_read_only: true + from: + operation: DescribeProvisionedProduct + path: ProvisionedProductDetail.LastProvisioningRecordId + LastPathID: + is_read_only: true + from: + operation: DescribeRecord + path: RecordDetail.PathId + LastProductID: + is_read_only: true + from: + operation: DescribeProvisionedProduct + path: ProvisionedProductDetail.ProductId + LastProvisioningArtifactID: + is_read_only: true + from: + operation: DescribeProvisionedProduct + path: ProvisionedProductDetail.ProvisioningArtifactId + LaunchRoleArn: + is_read_only: true + from: + operation: DescribeProvisionedProduct + path: ProvisionedProductDetail.LaunchRoleArn + Status: + is_read_only: true + from: + operation: DescribeProvisionedProduct + path: ProvisionedProductDetail.Status + StatusMessage: + is_read_only: true + from: + operation: DescribeProvisionedProduct + path: ProvisionedProductDetail.StatusMessage + renames: + operations: + ProvisionProduct: + input_fields: + ProvisionedProductName: Name \ No newline at end of file diff --git a/apis/servicecatalog/v1alpha1/custom_types.go b/apis/servicecatalog/v1alpha1/custom_types.go new file mode 100644 index 0000000000..0f579cb91e --- /dev/null +++ b/apis/servicecatalog/v1alpha1/custom_types.go @@ -0,0 +1,4 @@ +package v1alpha1 + +// CustomProvisionedProductParameters includes the custom fields. +type CustomProvisionedProductParameters struct{} diff --git a/apis/servicecatalog/v1alpha1/zz_doc.go b/apis/servicecatalog/v1alpha1/zz_doc.go new file mode 100644 index 0000000000..1aa26ff243 --- /dev/null +++ b/apis/servicecatalog/v1alpha1/zz_doc.go @@ -0,0 +1,24 @@ +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by ack-generate. DO NOT EDIT. + +// +kubebuilder:object:generate=true +// Package v1alpha1 is the v1alpha1 version of the servicecatalog.aws.crossplane.io API. +// +groupName=servicecatalog.aws.crossplane.io +// +versionName=v1alpha1 + +package v1alpha1 diff --git a/apis/servicecatalog/v1alpha1/zz_enums.go b/apis/servicecatalog/v1alpha1/zz_enums.go new file mode 100644 index 0000000000..8a7dfaee42 --- /dev/null +++ b/apis/servicecatalog/v1alpha1/zz_enums.go @@ -0,0 +1,303 @@ +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by ack-generate. DO NOT EDIT. + +package v1alpha1 + +type AccessLevelFilterKey string + +const ( + AccessLevelFilterKey_Account AccessLevelFilterKey = "Account" + AccessLevelFilterKey_Role AccessLevelFilterKey = "Role" + AccessLevelFilterKey_User AccessLevelFilterKey = "User" +) + +type AccessStatus string + +const ( + AccessStatus_ENABLED AccessStatus = "ENABLED" + AccessStatus_UNDER_CHANGE AccessStatus = "UNDER_CHANGE" + AccessStatus_DISABLED AccessStatus = "DISABLED" +) + +type ChangeAction string + +const ( + ChangeAction_ADD ChangeAction = "ADD" + ChangeAction_MODIFY ChangeAction = "MODIFY" + ChangeAction_REMOVE ChangeAction = "REMOVE" +) + +type CopyOption string + +const ( + CopyOption_CopyTags CopyOption = "CopyTags" +) + +type CopyProductStatus string + +const ( + CopyProductStatus_SUCCEEDED CopyProductStatus = "SUCCEEDED" + CopyProductStatus_IN_PROGRESS CopyProductStatus = "IN_PROGRESS" + CopyProductStatus_FAILED CopyProductStatus = "FAILED" +) + +type DescribePortfolioShareType string + +const ( + DescribePortfolioShareType_ACCOUNT DescribePortfolioShareType = "ACCOUNT" + DescribePortfolioShareType_ORGANIZATION DescribePortfolioShareType = "ORGANIZATION" + DescribePortfolioShareType_ORGANIZATIONAL_UNIT DescribePortfolioShareType = "ORGANIZATIONAL_UNIT" + DescribePortfolioShareType_ORGANIZATION_MEMBER_ACCOUNT DescribePortfolioShareType = "ORGANIZATION_MEMBER_ACCOUNT" +) + +type EvaluationType string + +const ( + EvaluationType_STATIC EvaluationType = "STATIC" + EvaluationType_DYNAMIC EvaluationType = "DYNAMIC" +) + +type LastSyncStatus string + +const ( + LastSyncStatus_SUCCEEDED LastSyncStatus = "SUCCEEDED" + LastSyncStatus_FAILED LastSyncStatus = "FAILED" +) + +type OrganizationNodeType string + +const ( + OrganizationNodeType_ORGANIZATION OrganizationNodeType = "ORGANIZATION" + OrganizationNodeType_ORGANIZATIONAL_UNIT OrganizationNodeType = "ORGANIZATIONAL_UNIT" + OrganizationNodeType_ACCOUNT OrganizationNodeType = "ACCOUNT" +) + +type PortfolioShareType string + +const ( + PortfolioShareType_IMPORTED PortfolioShareType = "IMPORTED" + PortfolioShareType_AWS_SERVICECATALOG PortfolioShareType = "AWS_SERVICECATALOG" + PortfolioShareType_AWS_ORGANIZATIONS PortfolioShareType = "AWS_ORGANIZATIONS" +) + +type PrincipalType string + +const ( + PrincipalType_IAM PrincipalType = "IAM" + PrincipalType_IAM_PATTERN PrincipalType = "IAM_PATTERN" +) + +type ProductSource string + +const ( + ProductSource_ACCOUNT ProductSource = "ACCOUNT" +) + +type ProductType string + +const ( + ProductType_CLOUD_FORMATION_TEMPLATE ProductType = "CLOUD_FORMATION_TEMPLATE" + ProductType_MARKETPLACE ProductType = "MARKETPLACE" +) + +type ProductViewFilterBy string + +const ( + ProductViewFilterBy_FullTextSearch ProductViewFilterBy = "FullTextSearch" + ProductViewFilterBy_Owner ProductViewFilterBy = "Owner" + ProductViewFilterBy_ProductType ProductViewFilterBy = "ProductType" + ProductViewFilterBy_SourceProductId ProductViewFilterBy = "SourceProductId" +) + +type ProductViewSortBy string + +const ( + ProductViewSortBy_Title ProductViewSortBy = "Title" + ProductViewSortBy_VersionCount ProductViewSortBy = "VersionCount" + ProductViewSortBy_CreationDate ProductViewSortBy = "CreationDate" +) + +type PropertyKey string + +const ( + PropertyKey_OWNER PropertyKey = "OWNER" + PropertyKey_LAUNCH_ROLE PropertyKey = "LAUNCH_ROLE" +) + +type ProvisionedProductPlanStatus string + +const ( + ProvisionedProductPlanStatus_CREATE_IN_PROGRESS ProvisionedProductPlanStatus = "CREATE_IN_PROGRESS" + ProvisionedProductPlanStatus_CREATE_SUCCESS ProvisionedProductPlanStatus = "CREATE_SUCCESS" + ProvisionedProductPlanStatus_CREATE_FAILED ProvisionedProductPlanStatus = "CREATE_FAILED" + ProvisionedProductPlanStatus_EXECUTE_IN_PROGRESS ProvisionedProductPlanStatus = "EXECUTE_IN_PROGRESS" + ProvisionedProductPlanStatus_EXECUTE_SUCCESS ProvisionedProductPlanStatus = "EXECUTE_SUCCESS" + ProvisionedProductPlanStatus_EXECUTE_FAILED ProvisionedProductPlanStatus = "EXECUTE_FAILED" +) + +type ProvisionedProductPlanType string + +const ( + ProvisionedProductPlanType_CLOUDFORMATION ProvisionedProductPlanType = "CLOUDFORMATION" +) + +type ProvisionedProductStatus_SDK string + +const ( + ProvisionedProductStatus_SDK_AVAILABLE ProvisionedProductStatus_SDK = "AVAILABLE" + ProvisionedProductStatus_SDK_UNDER_CHANGE ProvisionedProductStatus_SDK = "UNDER_CHANGE" + ProvisionedProductStatus_SDK_TAINTED ProvisionedProductStatus_SDK = "TAINTED" + ProvisionedProductStatus_SDK_ERROR ProvisionedProductStatus_SDK = "ERROR" + ProvisionedProductStatus_SDK_PLAN_IN_PROGRESS ProvisionedProductStatus_SDK = "PLAN_IN_PROGRESS" +) + +type ProvisionedProductViewFilterBy string + +const ( + ProvisionedProductViewFilterBy_SearchQuery ProvisionedProductViewFilterBy = "SearchQuery" +) + +type ProvisioningArtifactGuidance string + +const ( + ProvisioningArtifactGuidance_DEFAULT ProvisioningArtifactGuidance = "DEFAULT" + ProvisioningArtifactGuidance_DEPRECATED ProvisioningArtifactGuidance = "DEPRECATED" +) + +type ProvisioningArtifactPropertyName string + +const ( + ProvisioningArtifactPropertyName_Id ProvisioningArtifactPropertyName = "Id" +) + +type ProvisioningArtifactType string + +const ( + ProvisioningArtifactType_CLOUD_FORMATION_TEMPLATE ProvisioningArtifactType = "CLOUD_FORMATION_TEMPLATE" + ProvisioningArtifactType_MARKETPLACE_AMI ProvisioningArtifactType = "MARKETPLACE_AMI" + ProvisioningArtifactType_MARKETPLACE_CAR ProvisioningArtifactType = "MARKETPLACE_CAR" +) + +type RecordStatus string + +const ( + RecordStatus_CREATED RecordStatus = "CREATED" + RecordStatus_IN_PROGRESS RecordStatus = "IN_PROGRESS" + RecordStatus_IN_PROGRESS_IN_ERROR RecordStatus = "IN_PROGRESS_IN_ERROR" + RecordStatus_SUCCEEDED RecordStatus = "SUCCEEDED" + RecordStatus_FAILED RecordStatus = "FAILED" +) + +type Replacement string + +const ( + Replacement_TRUE Replacement = "TRUE" + Replacement_FALSE Replacement = "FALSE" + Replacement_CONDITIONAL Replacement = "CONDITIONAL" +) + +type RequiresRecreation string + +const ( + RequiresRecreation_NEVER RequiresRecreation = "NEVER" + RequiresRecreation_CONDITIONALLY RequiresRecreation = "CONDITIONALLY" + RequiresRecreation_ALWAYS RequiresRecreation = "ALWAYS" +) + +type ResourceAttribute string + +const ( + ResourceAttribute_PROPERTIES ResourceAttribute = "PROPERTIES" + ResourceAttribute_METADATA ResourceAttribute = "METADATA" + ResourceAttribute_CREATIONPOLICY ResourceAttribute = "CREATIONPOLICY" + ResourceAttribute_UPDATEPOLICY ResourceAttribute = "UPDATEPOLICY" + ResourceAttribute_DELETIONPOLICY ResourceAttribute = "DELETIONPOLICY" + ResourceAttribute_TAGS ResourceAttribute = "TAGS" +) + +type ServiceActionAssociationErrorCode string + +const ( + ServiceActionAssociationErrorCode_DUPLICATE_RESOURCE ServiceActionAssociationErrorCode = "DUPLICATE_RESOURCE" + ServiceActionAssociationErrorCode_INTERNAL_FAILURE ServiceActionAssociationErrorCode = "INTERNAL_FAILURE" + ServiceActionAssociationErrorCode_LIMIT_EXCEEDED ServiceActionAssociationErrorCode = "LIMIT_EXCEEDED" + ServiceActionAssociationErrorCode_RESOURCE_NOT_FOUND ServiceActionAssociationErrorCode = "RESOURCE_NOT_FOUND" + ServiceActionAssociationErrorCode_THROTTLING ServiceActionAssociationErrorCode = "THROTTLING" +) + +type ServiceActionDefinitionKey string + +const ( + ServiceActionDefinitionKey_Name ServiceActionDefinitionKey = "Name" + ServiceActionDefinitionKey_Version ServiceActionDefinitionKey = "Version" + ServiceActionDefinitionKey_AssumeRole ServiceActionDefinitionKey = "AssumeRole" + ServiceActionDefinitionKey_Parameters ServiceActionDefinitionKey = "Parameters" +) + +type ServiceActionDefinitionType string + +const ( + ServiceActionDefinitionType_SSM_AUTOMATION ServiceActionDefinitionType = "SSM_AUTOMATION" +) + +type ShareStatus string + +const ( + ShareStatus_NOT_STARTED ShareStatus = "NOT_STARTED" + ShareStatus_IN_PROGRESS ShareStatus = "IN_PROGRESS" + ShareStatus_COMPLETED ShareStatus = "COMPLETED" + ShareStatus_COMPLETED_WITH_ERRORS ShareStatus = "COMPLETED_WITH_ERRORS" + ShareStatus_ERROR ShareStatus = "ERROR" +) + +type SortOrder string + +const ( + SortOrder_ASCENDING SortOrder = "ASCENDING" + SortOrder_DESCENDING SortOrder = "DESCENDING" +) + +type SourceType string + +const ( + SourceType_CODESTAR SourceType = "CODESTAR" +) + +type StackInstanceStatus string + +const ( + StackInstanceStatus_CURRENT StackInstanceStatus = "CURRENT" + StackInstanceStatus_OUTDATED StackInstanceStatus = "OUTDATED" + StackInstanceStatus_INOPERABLE StackInstanceStatus = "INOPERABLE" +) + +type StackSetOperationType string + +const ( + StackSetOperationType_CREATE StackSetOperationType = "CREATE" + StackSetOperationType_UPDATE StackSetOperationType = "UPDATE" + StackSetOperationType_DELETE StackSetOperationType = "DELETE" +) + +type Status string + +const ( + Status_AVAILABLE Status = "AVAILABLE" + Status_CREATING Status = "CREATING" + Status_FAILED Status = "FAILED" +) diff --git a/apis/servicecatalog/v1alpha1/zz_generated.deepcopy.go b/apis/servicecatalog/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..23d673f727 --- /dev/null +++ b/apis/servicecatalog/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,1383 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudWatchDashboard) DeepCopyInto(out *CloudWatchDashboard) { + *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudWatchDashboard. +func (in *CloudWatchDashboard) DeepCopy() *CloudWatchDashboard { + if in == nil { + return nil + } + out := new(CloudWatchDashboard) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConstraintDetail) DeepCopyInto(out *ConstraintDetail) { + *out = *in + if in.ConstraintID != nil { + in, out := &in.ConstraintID, &out.ConstraintID + *out = new(string) + **out = **in + } + if in.Owner != nil { + in, out := &in.Owner, &out.Owner + *out = new(string) + **out = **in + } + if in.PortfolioID != nil { + in, out := &in.PortfolioID, &out.PortfolioID + *out = new(string) + **out = **in + } + if in.ProductID != nil { + in, out := &in.ProductID, &out.ProductID + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConstraintDetail. +func (in *ConstraintDetail) DeepCopy() *ConstraintDetail { + if in == nil { + return nil + } + out := new(ConstraintDetail) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CustomProvisionedProductParameters) DeepCopyInto(out *CustomProvisionedProductParameters) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomProvisionedProductParameters. +func (in *CustomProvisionedProductParameters) DeepCopy() *CustomProvisionedProductParameters { + if in == nil { + return nil + } + out := new(CustomProvisionedProductParameters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FailedServiceActionAssociation) DeepCopyInto(out *FailedServiceActionAssociation) { + *out = *in + if in.ProductID != nil { + in, out := &in.ProductID, &out.ProductID + *out = new(string) + **out = **in + } + if in.ProvisioningArtifactID != nil { + in, out := &in.ProvisioningArtifactID, &out.ProvisioningArtifactID + *out = new(string) + **out = **in + } + if in.ServiceActionID != nil { + in, out := &in.ServiceActionID, &out.ServiceActionID + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FailedServiceActionAssociation. +func (in *FailedServiceActionAssociation) DeepCopy() *FailedServiceActionAssociation { + if in == nil { + return nil + } + out := new(FailedServiceActionAssociation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LastSync) DeepCopyInto(out *LastSync) { + *out = *in + if in.LastSuccessfulSyncProvisioningArtifactID != nil { + in, out := &in.LastSuccessfulSyncProvisioningArtifactID, &out.LastSuccessfulSyncProvisioningArtifactID + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LastSync. +func (in *LastSync) DeepCopy() *LastSync { + if in == nil { + return nil + } + out := new(LastSync) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LaunchPath) DeepCopyInto(out *LaunchPath) { + *out = *in + if in.ID != nil { + in, out := &in.ID, &out.ID + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LaunchPath. +func (in *LaunchPath) DeepCopy() *LaunchPath { + if in == nil { + return nil + } + out := new(LaunchPath) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LaunchPathSummary) DeepCopyInto(out *LaunchPathSummary) { + *out = *in + if in.ID != nil { + in, out := &in.ID, &out.ID + *out = new(string) + **out = **in + } + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]*Tag, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(Tag) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LaunchPathSummary. +func (in *LaunchPathSummary) DeepCopy() *LaunchPathSummary { + if in == nil { + return nil + } + out := new(LaunchPathSummary) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PortfolioDetail) DeepCopyInto(out *PortfolioDetail) { + *out = *in + if in.DisplayName != nil { + in, out := &in.DisplayName, &out.DisplayName + *out = new(string) + **out = **in + } + if in.ID != nil { + in, out := &in.ID, &out.ID + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PortfolioDetail. +func (in *PortfolioDetail) DeepCopy() *PortfolioDetail { + if in == nil { + return nil + } + out := new(PortfolioDetail) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PortfolioShareDetail) DeepCopyInto(out *PortfolioShareDetail) { + *out = *in + if in.PrincipalID != nil { + in, out := &in.PrincipalID, &out.PrincipalID + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PortfolioShareDetail. +func (in *PortfolioShareDetail) DeepCopy() *PortfolioShareDetail { + if in == nil { + return nil + } + out := new(PortfolioShareDetail) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProductViewDetail) DeepCopyInto(out *ProductViewDetail) { + *out = *in + if in.CreatedTime != nil { + in, out := &in.CreatedTime, &out.CreatedTime + *out = (*in).DeepCopy() + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProductViewDetail. +func (in *ProductViewDetail) DeepCopy() *ProductViewDetail { + if in == nil { + return nil + } + out := new(ProductViewDetail) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProductViewSummary) DeepCopyInto(out *ProductViewSummary) { + *out = *in + if in.ID != nil { + in, out := &in.ID, &out.ID + *out = new(string) + **out = **in + } + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } + if in.ProductID != nil { + in, out := &in.ProductID, &out.ProductID + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProductViewSummary. +func (in *ProductViewSummary) DeepCopy() *ProductViewSummary { + if in == nil { + return nil + } + out := new(ProductViewSummary) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProvisionedProduct) DeepCopyInto(out *ProvisionedProduct) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProvisionedProduct. +func (in *ProvisionedProduct) DeepCopy() *ProvisionedProduct { + if in == nil { + return nil + } + out := new(ProvisionedProduct) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProvisionedProduct) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProvisionedProductAttribute) DeepCopyInto(out *ProvisionedProductAttribute) { + *out = *in + if in.ARN != nil { + in, out := &in.ARN, &out.ARN + *out = new(string) + **out = **in + } + if in.CreatedTime != nil { + in, out := &in.CreatedTime, &out.CreatedTime + *out = (*in).DeepCopy() + } + if in.ID != nil { + in, out := &in.ID, &out.ID + *out = new(string) + **out = **in + } + if in.IdempotencyToken != nil { + in, out := &in.IdempotencyToken, &out.IdempotencyToken + *out = new(string) + **out = **in + } + if in.LastProvisioningRecordID != nil { + in, out := &in.LastProvisioningRecordID, &out.LastProvisioningRecordID + *out = new(string) + **out = **in + } + if in.LastRecordID != nil { + in, out := &in.LastRecordID, &out.LastRecordID + *out = new(string) + **out = **in + } + if in.LastSuccessfulProvisioningRecordID != nil { + in, out := &in.LastSuccessfulProvisioningRecordID, &out.LastSuccessfulProvisioningRecordID + *out = new(string) + **out = **in + } + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } + if in.ProductID != nil { + in, out := &in.ProductID, &out.ProductID + *out = new(string) + **out = **in + } + if in.ProductName != nil { + in, out := &in.ProductName, &out.ProductName + *out = new(string) + **out = **in + } + if in.ProvisioningArtifactID != nil { + in, out := &in.ProvisioningArtifactID, &out.ProvisioningArtifactID + *out = new(string) + **out = **in + } + if in.ProvisioningArtifactName != nil { + in, out := &in.ProvisioningArtifactName, &out.ProvisioningArtifactName + *out = new(string) + **out = **in + } + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(string) + **out = **in + } + if in.StatusMessage != nil { + in, out := &in.StatusMessage, &out.StatusMessage + *out = new(string) + **out = **in + } + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]*Tag, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(Tag) + (*in).DeepCopyInto(*out) + } + } + } + if in.Type != nil { + in, out := &in.Type, &out.Type + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProvisionedProductAttribute. +func (in *ProvisionedProductAttribute) DeepCopy() *ProvisionedProductAttribute { + if in == nil { + return nil + } + out := new(ProvisionedProductAttribute) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProvisionedProductDetail) DeepCopyInto(out *ProvisionedProductDetail) { + *out = *in + if in.ARN != nil { + in, out := &in.ARN, &out.ARN + *out = new(string) + **out = **in + } + if in.CreatedTime != nil { + in, out := &in.CreatedTime, &out.CreatedTime + *out = (*in).DeepCopy() + } + if in.ID != nil { + in, out := &in.ID, &out.ID + *out = new(string) + **out = **in + } + if in.IdempotencyToken != nil { + in, out := &in.IdempotencyToken, &out.IdempotencyToken + *out = new(string) + **out = **in + } + if in.LastProvisioningRecordID != nil { + in, out := &in.LastProvisioningRecordID, &out.LastProvisioningRecordID + *out = new(string) + **out = **in + } + if in.LastRecordID != nil { + in, out := &in.LastRecordID, &out.LastRecordID + *out = new(string) + **out = **in + } + if in.LastSuccessfulProvisioningRecordID != nil { + in, out := &in.LastSuccessfulProvisioningRecordID, &out.LastSuccessfulProvisioningRecordID + *out = new(string) + **out = **in + } + if in.LaunchRoleARN != nil { + in, out := &in.LaunchRoleARN, &out.LaunchRoleARN + *out = new(string) + **out = **in + } + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } + if in.ProductID != nil { + in, out := &in.ProductID, &out.ProductID + *out = new(string) + **out = **in + } + if in.ProvisioningArtifactID != nil { + in, out := &in.ProvisioningArtifactID, &out.ProvisioningArtifactID + *out = new(string) + **out = **in + } + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(string) + **out = **in + } + if in.StatusMessage != nil { + in, out := &in.StatusMessage, &out.StatusMessage + *out = new(string) + **out = **in + } + if in.Type != nil { + in, out := &in.Type, &out.Type + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProvisionedProductDetail. +func (in *ProvisionedProductDetail) DeepCopy() *ProvisionedProductDetail { + if in == nil { + return nil + } + out := new(ProvisionedProductDetail) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProvisionedProductList) DeepCopyInto(out *ProvisionedProductList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ProvisionedProduct, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProvisionedProductList. +func (in *ProvisionedProductList) DeepCopy() *ProvisionedProductList { + if in == nil { + return nil + } + out := new(ProvisionedProductList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProvisionedProductList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProvisionedProductObservation) DeepCopyInto(out *ProvisionedProductObservation) { + *out = *in + if in.ARN != nil { + in, out := &in.ARN, &out.ARN + *out = new(string) + **out = **in + } + if in.CreatedTime != nil { + in, out := &in.CreatedTime, &out.CreatedTime + *out = (*in).DeepCopy() + } + if in.LastPathID != nil { + in, out := &in.LastPathID, &out.LastPathID + *out = new(string) + **out = **in + } + if in.LastProductID != nil { + in, out := &in.LastProductID, &out.LastProductID + *out = new(string) + **out = **in + } + if in.LastProvisioningArtifactID != nil { + in, out := &in.LastProvisioningArtifactID, &out.LastProvisioningArtifactID + *out = new(string) + **out = **in + } + if in.LastProvisioningRecordID != nil { + in, out := &in.LastProvisioningRecordID, &out.LastProvisioningRecordID + *out = new(string) + **out = **in + } + if in.LaunchRoleARN != nil { + in, out := &in.LaunchRoleARN, &out.LaunchRoleARN + *out = new(string) + **out = **in + } + if in.Outputs != nil { + in, out := &in.Outputs, &out.Outputs + *out = make(map[string]*RecordOutput, len(*in)) + for key, val := range *in { + var outVal *RecordOutput + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = new(RecordOutput) + (*in).DeepCopyInto(*out) + } + (*out)[key] = outVal + } + } + if in.ProvisionedProductID != nil { + in, out := &in.ProvisionedProductID, &out.ProvisionedProductID + *out = new(string) + **out = **in + } + if in.ProvisionedProductType != nil { + in, out := &in.ProvisionedProductType, &out.ProvisionedProductType + *out = new(string) + **out = **in + } + if in.RecordErrors != nil { + in, out := &in.RecordErrors, &out.RecordErrors + *out = make([]*RecordError, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(RecordError) + (*in).DeepCopyInto(*out) + } + } + } + if in.RecordID != nil { + in, out := &in.RecordID, &out.RecordID + *out = new(string) + **out = **in + } + if in.RecordTags != nil { + in, out := &in.RecordTags, &out.RecordTags + *out = make([]*RecordTag, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(RecordTag) + (*in).DeepCopyInto(*out) + } + } + } + if in.RecordType != nil { + in, out := &in.RecordType, &out.RecordType + *out = new(string) + **out = **in + } + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(string) + **out = **in + } + if in.StatusMessage != nil { + in, out := &in.StatusMessage, &out.StatusMessage + *out = new(string) + **out = **in + } + if in.UpdatedTime != nil { + in, out := &in.UpdatedTime, &out.UpdatedTime + *out = (*in).DeepCopy() + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProvisionedProductObservation. +func (in *ProvisionedProductObservation) DeepCopy() *ProvisionedProductObservation { + if in == nil { + return nil + } + out := new(ProvisionedProductObservation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProvisionedProductParameters) DeepCopyInto(out *ProvisionedProductParameters) { + *out = *in + if in.AcceptLanguage != nil { + in, out := &in.AcceptLanguage, &out.AcceptLanguage + *out = new(string) + **out = **in + } + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } + if in.NotificationARNs != nil { + in, out := &in.NotificationARNs, &out.NotificationARNs + *out = make([]*string, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(string) + **out = **in + } + } + } + if in.PathID != nil { + in, out := &in.PathID, &out.PathID + *out = new(string) + **out = **in + } + if in.PathName != nil { + in, out := &in.PathName, &out.PathName + *out = new(string) + **out = **in + } + if in.ProductID != nil { + in, out := &in.ProductID, &out.ProductID + *out = new(string) + **out = **in + } + if in.ProductName != nil { + in, out := &in.ProductName, &out.ProductName + *out = new(string) + **out = **in + } + if in.ProvisioningArtifactID != nil { + in, out := &in.ProvisioningArtifactID, &out.ProvisioningArtifactID + *out = new(string) + **out = **in + } + if in.ProvisioningArtifactName != nil { + in, out := &in.ProvisioningArtifactName, &out.ProvisioningArtifactName + *out = new(string) + **out = **in + } + if in.ProvisioningParameters != nil { + in, out := &in.ProvisioningParameters, &out.ProvisioningParameters + *out = make([]*ProvisioningParameter, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(ProvisioningParameter) + (*in).DeepCopyInto(*out) + } + } + } + if in.ProvisioningPreferences != nil { + in, out := &in.ProvisioningPreferences, &out.ProvisioningPreferences + *out = new(ProvisioningPreferences) + (*in).DeepCopyInto(*out) + } + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]*Tag, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(Tag) + (*in).DeepCopyInto(*out) + } + } + } + out.CustomProvisionedProductParameters = in.CustomProvisionedProductParameters +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProvisionedProductParameters. +func (in *ProvisionedProductParameters) DeepCopy() *ProvisionedProductParameters { + if in == nil { + return nil + } + out := new(ProvisionedProductParameters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProvisionedProductSpec) DeepCopyInto(out *ProvisionedProductSpec) { + *out = *in + in.ResourceSpec.DeepCopyInto(&out.ResourceSpec) + in.ForProvider.DeepCopyInto(&out.ForProvider) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProvisionedProductSpec. +func (in *ProvisionedProductSpec) DeepCopy() *ProvisionedProductSpec { + if in == nil { + return nil + } + out := new(ProvisionedProductSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProvisionedProductStatus) DeepCopyInto(out *ProvisionedProductStatus) { + *out = *in + in.ResourceStatus.DeepCopyInto(&out.ResourceStatus) + in.AtProvider.DeepCopyInto(&out.AtProvider) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProvisionedProductStatus. +func (in *ProvisionedProductStatus) DeepCopy() *ProvisionedProductStatus { + if in == nil { + return nil + } + out := new(ProvisionedProductStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProvisioningArtifact) DeepCopyInto(out *ProvisioningArtifact) { + *out = *in + if in.ID != nil { + in, out := &in.ID, &out.ID + *out = new(string) + **out = **in + } + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProvisioningArtifact. +func (in *ProvisioningArtifact) DeepCopy() *ProvisioningArtifact { + if in == nil { + return nil + } + out := new(ProvisioningArtifact) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProvisioningArtifactDetail) DeepCopyInto(out *ProvisioningArtifactDetail) { + *out = *in + if in.Description != nil { + in, out := &in.Description, &out.Description + *out = new(string) + **out = **in + } + if in.ID != nil { + in, out := &in.ID, &out.ID + *out = new(string) + **out = **in + } + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProvisioningArtifactDetail. +func (in *ProvisioningArtifactDetail) DeepCopy() *ProvisioningArtifactDetail { + if in == nil { + return nil + } + out := new(ProvisioningArtifactDetail) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProvisioningArtifactParameter) DeepCopyInto(out *ProvisioningArtifactParameter) { + *out = *in + if in.Description != nil { + in, out := &in.Description, &out.Description + *out = new(string) + **out = **in + } + if in.ParameterKey != nil { + in, out := &in.ParameterKey, &out.ParameterKey + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProvisioningArtifactParameter. +func (in *ProvisioningArtifactParameter) DeepCopy() *ProvisioningArtifactParameter { + if in == nil { + return nil + } + out := new(ProvisioningArtifactParameter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProvisioningArtifactPreferences) DeepCopyInto(out *ProvisioningArtifactPreferences) { + *out = *in + if in.StackSetAccounts != nil { + in, out := &in.StackSetAccounts, &out.StackSetAccounts + *out = make([]*string, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(string) + **out = **in + } + } + } + if in.StackSetRegions != nil { + in, out := &in.StackSetRegions, &out.StackSetRegions + *out = make([]*string, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(string) + **out = **in + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProvisioningArtifactPreferences. +func (in *ProvisioningArtifactPreferences) DeepCopy() *ProvisioningArtifactPreferences { + if in == nil { + return nil + } + out := new(ProvisioningArtifactPreferences) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProvisioningArtifactProperties) DeepCopyInto(out *ProvisioningArtifactProperties) { + *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProvisioningArtifactProperties. +func (in *ProvisioningArtifactProperties) DeepCopy() *ProvisioningArtifactProperties { + if in == nil { + return nil + } + out := new(ProvisioningArtifactProperties) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProvisioningArtifactSummary) DeepCopyInto(out *ProvisioningArtifactSummary) { + *out = *in + if in.ID != nil { + in, out := &in.ID, &out.ID + *out = new(string) + **out = **in + } + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProvisioningArtifactSummary. +func (in *ProvisioningArtifactSummary) DeepCopy() *ProvisioningArtifactSummary { + if in == nil { + return nil + } + out := new(ProvisioningArtifactSummary) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProvisioningParameter) DeepCopyInto(out *ProvisioningParameter) { + *out = *in + if in.Key != nil { + in, out := &in.Key, &out.Key + *out = new(string) + **out = **in + } + if in.Value != nil { + in, out := &in.Value, &out.Value + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProvisioningParameter. +func (in *ProvisioningParameter) DeepCopy() *ProvisioningParameter { + if in == nil { + return nil + } + out := new(ProvisioningParameter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProvisioningPreferences) DeepCopyInto(out *ProvisioningPreferences) { + *out = *in + if in.StackSetAccounts != nil { + in, out := &in.StackSetAccounts, &out.StackSetAccounts + *out = make([]*string, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(string) + **out = **in + } + } + } + if in.StackSetFailureToleranceCount != nil { + in, out := &in.StackSetFailureToleranceCount, &out.StackSetFailureToleranceCount + *out = new(int64) + **out = **in + } + if in.StackSetFailureTolerancePercentage != nil { + in, out := &in.StackSetFailureTolerancePercentage, &out.StackSetFailureTolerancePercentage + *out = new(int64) + **out = **in + } + if in.StackSetMaxConcurrencyCount != nil { + in, out := &in.StackSetMaxConcurrencyCount, &out.StackSetMaxConcurrencyCount + *out = new(int64) + **out = **in + } + if in.StackSetMaxConcurrencyPercentage != nil { + in, out := &in.StackSetMaxConcurrencyPercentage, &out.StackSetMaxConcurrencyPercentage + *out = new(int64) + **out = **in + } + if in.StackSetRegions != nil { + in, out := &in.StackSetRegions, &out.StackSetRegions + *out = make([]*string, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(string) + **out = **in + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProvisioningPreferences. +func (in *ProvisioningPreferences) DeepCopy() *ProvisioningPreferences { + if in == nil { + return nil + } + out := new(ProvisioningPreferences) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RecordDetail) DeepCopyInto(out *RecordDetail) { + *out = *in + if in.CreatedTime != nil { + in, out := &in.CreatedTime, &out.CreatedTime + *out = (*in).DeepCopy() + } + if in.LaunchRoleARN != nil { + in, out := &in.LaunchRoleARN, &out.LaunchRoleARN + *out = new(string) + **out = **in + } + if in.PathID != nil { + in, out := &in.PathID, &out.PathID + *out = new(string) + **out = **in + } + if in.ProductID != nil { + in, out := &in.ProductID, &out.ProductID + *out = new(string) + **out = **in + } + if in.ProvisionedProductID != nil { + in, out := &in.ProvisionedProductID, &out.ProvisionedProductID + *out = new(string) + **out = **in + } + if in.ProvisionedProductName != nil { + in, out := &in.ProvisionedProductName, &out.ProvisionedProductName + *out = new(string) + **out = **in + } + if in.ProvisionedProductType != nil { + in, out := &in.ProvisionedProductType, &out.ProvisionedProductType + *out = new(string) + **out = **in + } + if in.ProvisioningArtifactID != nil { + in, out := &in.ProvisioningArtifactID, &out.ProvisioningArtifactID + *out = new(string) + **out = **in + } + if in.RecordErrors != nil { + in, out := &in.RecordErrors, &out.RecordErrors + *out = make([]*RecordError, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(RecordError) + (*in).DeepCopyInto(*out) + } + } + } + if in.RecordID != nil { + in, out := &in.RecordID, &out.RecordID + *out = new(string) + **out = **in + } + if in.RecordTags != nil { + in, out := &in.RecordTags, &out.RecordTags + *out = make([]*RecordTag, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(RecordTag) + (*in).DeepCopyInto(*out) + } + } + } + if in.RecordType != nil { + in, out := &in.RecordType, &out.RecordType + *out = new(string) + **out = **in + } + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(string) + **out = **in + } + if in.UpdatedTime != nil { + in, out := &in.UpdatedTime, &out.UpdatedTime + *out = (*in).DeepCopy() + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RecordDetail. +func (in *RecordDetail) DeepCopy() *RecordDetail { + if in == nil { + return nil + } + out := new(RecordDetail) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RecordError) DeepCopyInto(out *RecordError) { + *out = *in + if in.Code != nil { + in, out := &in.Code, &out.Code + *out = new(string) + **out = **in + } + if in.Description != nil { + in, out := &in.Description, &out.Description + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RecordError. +func (in *RecordError) DeepCopy() *RecordError { + if in == nil { + return nil + } + out := new(RecordError) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RecordOutput) DeepCopyInto(out *RecordOutput) { + *out = *in + if in.Description != nil { + in, out := &in.Description, &out.Description + *out = new(string) + **out = **in + } + if in.OutputKey != nil { + in, out := &in.OutputKey, &out.OutputKey + *out = new(string) + **out = **in + } + if in.OutputValue != nil { + in, out := &in.OutputValue, &out.OutputValue + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RecordOutput. +func (in *RecordOutput) DeepCopy() *RecordOutput { + if in == nil { + return nil + } + out := new(RecordOutput) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RecordTag) DeepCopyInto(out *RecordTag) { + *out = *in + if in.Key != nil { + in, out := &in.Key, &out.Key + *out = new(string) + **out = **in + } + if in.Value != nil { + in, out := &in.Value, &out.Value + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RecordTag. +func (in *RecordTag) DeepCopy() *RecordTag { + if in == nil { + return nil + } + out := new(RecordTag) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceActionAssociation) DeepCopyInto(out *ServiceActionAssociation) { + *out = *in + if in.ProductID != nil { + in, out := &in.ProductID, &out.ProductID + *out = new(string) + **out = **in + } + if in.ProvisioningArtifactID != nil { + in, out := &in.ProvisioningArtifactID, &out.ProvisioningArtifactID + *out = new(string) + **out = **in + } + if in.ServiceActionID != nil { + in, out := &in.ServiceActionID, &out.ServiceActionID + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceActionAssociation. +func (in *ServiceActionAssociation) DeepCopy() *ServiceActionAssociation { + if in == nil { + return nil + } + out := new(ServiceActionAssociation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceActionSummary) DeepCopyInto(out *ServiceActionSummary) { + *out = *in + if in.ID != nil { + in, out := &in.ID, &out.ID + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceActionSummary. +func (in *ServiceActionSummary) DeepCopy() *ServiceActionSummary { + if in == nil { + return nil + } + out := new(ServiceActionSummary) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StackInstance) DeepCopyInto(out *StackInstance) { + *out = *in + if in.Account != nil { + in, out := &in.Account, &out.Account + *out = new(string) + **out = **in + } + if in.Region != nil { + in, out := &in.Region, &out.Region + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StackInstance. +func (in *StackInstance) DeepCopy() *StackInstance { + if in == nil { + return nil + } + out := new(StackInstance) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Tag) DeepCopyInto(out *Tag) { + *out = *in + if in.Key != nil { + in, out := &in.Key, &out.Key + *out = new(string) + **out = **in + } + if in.Value != nil { + in, out := &in.Value, &out.Value + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Tag. +func (in *Tag) DeepCopy() *Tag { + if in == nil { + return nil + } + out := new(Tag) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UpdateProvisioningParameter) DeepCopyInto(out *UpdateProvisioningParameter) { + *out = *in + if in.Key != nil { + in, out := &in.Key, &out.Key + *out = new(string) + **out = **in + } + if in.Value != nil { + in, out := &in.Value, &out.Value + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpdateProvisioningParameter. +func (in *UpdateProvisioningParameter) DeepCopy() *UpdateProvisioningParameter { + if in == nil { + return nil + } + out := new(UpdateProvisioningParameter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UpdateProvisioningPreferences) DeepCopyInto(out *UpdateProvisioningPreferences) { + *out = *in + if in.StackSetAccounts != nil { + in, out := &in.StackSetAccounts, &out.StackSetAccounts + *out = make([]*string, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(string) + **out = **in + } + } + } + if in.StackSetFailureToleranceCount != nil { + in, out := &in.StackSetFailureToleranceCount, &out.StackSetFailureToleranceCount + *out = new(int64) + **out = **in + } + if in.StackSetFailureTolerancePercentage != nil { + in, out := &in.StackSetFailureTolerancePercentage, &out.StackSetFailureTolerancePercentage + *out = new(int64) + **out = **in + } + if in.StackSetMaxConcurrencyCount != nil { + in, out := &in.StackSetMaxConcurrencyCount, &out.StackSetMaxConcurrencyCount + *out = new(int64) + **out = **in + } + if in.StackSetMaxConcurrencyPercentage != nil { + in, out := &in.StackSetMaxConcurrencyPercentage, &out.StackSetMaxConcurrencyPercentage + *out = new(int64) + **out = **in + } + if in.StackSetRegions != nil { + in, out := &in.StackSetRegions, &out.StackSetRegions + *out = make([]*string, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(string) + **out = **in + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpdateProvisioningPreferences. +func (in *UpdateProvisioningPreferences) DeepCopy() *UpdateProvisioningPreferences { + if in == nil { + return nil + } + out := new(UpdateProvisioningPreferences) + in.DeepCopyInto(out) + return out +} diff --git a/apis/servicecatalog/v1alpha1/zz_generated.managed.go b/apis/servicecatalog/v1alpha1/zz_generated.managed.go new file mode 100644 index 0000000000..48771bbdeb --- /dev/null +++ b/apis/servicecatalog/v1alpha1/zz_generated.managed.go @@ -0,0 +1,97 @@ +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by angryjet. DO NOT EDIT. + +package v1alpha1 + +import xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + +// GetCondition of this ProvisionedProduct. +func (mg *ProvisionedProduct) GetCondition(ct xpv1.ConditionType) xpv1.Condition { + return mg.Status.GetCondition(ct) +} + +// GetDeletionPolicy of this ProvisionedProduct. +func (mg *ProvisionedProduct) GetDeletionPolicy() xpv1.DeletionPolicy { + return mg.Spec.DeletionPolicy +} + +// GetManagementPolicies of this ProvisionedProduct. +func (mg *ProvisionedProduct) GetManagementPolicies() xpv1.ManagementPolicies { + return mg.Spec.ManagementPolicies +} + +// GetProviderConfigReference of this ProvisionedProduct. +func (mg *ProvisionedProduct) GetProviderConfigReference() *xpv1.Reference { + return mg.Spec.ProviderConfigReference +} + +/* +GetProviderReference of this ProvisionedProduct. +Deprecated: Use GetProviderConfigReference. +*/ +func (mg *ProvisionedProduct) GetProviderReference() *xpv1.Reference { + return mg.Spec.ProviderReference +} + +// GetPublishConnectionDetailsTo of this ProvisionedProduct. +func (mg *ProvisionedProduct) GetPublishConnectionDetailsTo() *xpv1.PublishConnectionDetailsTo { + return mg.Spec.PublishConnectionDetailsTo +} + +// GetWriteConnectionSecretToReference of this ProvisionedProduct. +func (mg *ProvisionedProduct) GetWriteConnectionSecretToReference() *xpv1.SecretReference { + return mg.Spec.WriteConnectionSecretToReference +} + +// SetConditions of this ProvisionedProduct. +func (mg *ProvisionedProduct) SetConditions(c ...xpv1.Condition) { + mg.Status.SetConditions(c...) +} + +// SetDeletionPolicy of this ProvisionedProduct. +func (mg *ProvisionedProduct) SetDeletionPolicy(r xpv1.DeletionPolicy) { + mg.Spec.DeletionPolicy = r +} + +// SetManagementPolicies of this ProvisionedProduct. +func (mg *ProvisionedProduct) SetManagementPolicies(r xpv1.ManagementPolicies) { + mg.Spec.ManagementPolicies = r +} + +// SetProviderConfigReference of this ProvisionedProduct. +func (mg *ProvisionedProduct) SetProviderConfigReference(r *xpv1.Reference) { + mg.Spec.ProviderConfigReference = r +} + +/* +SetProviderReference of this ProvisionedProduct. +Deprecated: Use SetProviderConfigReference. +*/ +func (mg *ProvisionedProduct) SetProviderReference(r *xpv1.Reference) { + mg.Spec.ProviderReference = r +} + +// SetPublishConnectionDetailsTo of this ProvisionedProduct. +func (mg *ProvisionedProduct) SetPublishConnectionDetailsTo(r *xpv1.PublishConnectionDetailsTo) { + mg.Spec.PublishConnectionDetailsTo = r +} + +// SetWriteConnectionSecretToReference of this ProvisionedProduct. +func (mg *ProvisionedProduct) SetWriteConnectionSecretToReference(r *xpv1.SecretReference) { + mg.Spec.WriteConnectionSecretToReference = r +} diff --git a/apis/servicecatalog/v1alpha1/zz_generated.managedlist.go b/apis/servicecatalog/v1alpha1/zz_generated.managedlist.go new file mode 100644 index 0000000000..8454d50a75 --- /dev/null +++ b/apis/servicecatalog/v1alpha1/zz_generated.managedlist.go @@ -0,0 +1,30 @@ +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by angryjet. DO NOT EDIT. + +package v1alpha1 + +import resource "github.com/crossplane/crossplane-runtime/pkg/resource" + +// GetItems of this ProvisionedProductList. +func (l *ProvisionedProductList) GetItems() []resource.Managed { + items := make([]resource.Managed, len(l.Items)) + for i := range l.Items { + items[i] = &l.Items[i] + } + return items +} diff --git a/apis/servicecatalog/v1alpha1/zz_groupversion_info.go b/apis/servicecatalog/v1alpha1/zz_groupversion_info.go new file mode 100644 index 0000000000..bf2b38a88b --- /dev/null +++ b/apis/servicecatalog/v1alpha1/zz_groupversion_info.go @@ -0,0 +1,41 @@ +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by ack-generate. DO NOT EDIT. + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +// Package type metadata. +const ( + CRDGroup = "servicecatalog.aws.crossplane.io" + CRDVersion = "v1alpha1" +) + +var ( + // GroupVersion is the API Group Version used to register the objects + GroupVersion = schema.GroupVersion{Group: CRDGroup, Version: CRDVersion} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/apis/servicecatalog/v1alpha1/zz_provisioned_product.go b/apis/servicecatalog/v1alpha1/zz_provisioned_product.go new file mode 100644 index 0000000000..4af2272098 --- /dev/null +++ b/apis/servicecatalog/v1alpha1/zz_provisioned_product.go @@ -0,0 +1,197 @@ +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by ack-generate. DO NOT EDIT. + +package v1alpha1 + +import ( + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// ProvisionedProductParameters defines the desired state of ProvisionedProduct +type ProvisionedProductParameters struct { + // Region is which region the ProvisionedProduct will be created. + // +kubebuilder:validation:Required + Region string `json:"region"` + // The language code. + // + // * en - English (default) + // + // * jp - Japanese + // + // * zh - Chinese + AcceptLanguage *string `json:"acceptLanguage,omitempty"` + // A user-friendly name for the provisioned product. This value must be unique + // for the Amazon Web Services account and cannot be updated after the product + // is provisioned. + // +kubebuilder:validation:Required + Name *string `json:"name"` + // Passed to CloudFormation. The SNS topic ARNs to which to publish stack-related + // events. + NotificationARNs []*string `json:"notificationARNs,omitempty"` + // The path identifier of the product. This value is optional if the product + // has a default path, and required if the product has more than one path. To + // list the paths for a product, use ListLaunchPaths. You must provide the name + // or ID, but not both. + PathID *string `json:"pathID,omitempty"` + // The name of the path. You must provide the name or ID, but not both. + PathName *string `json:"pathName,omitempty"` + // The product identifier. You must provide the name or ID, but not both. + ProductID *string `json:"productID,omitempty"` + // The name of the product. You must provide the name or ID, but not both. + ProductName *string `json:"productName,omitempty"` + // The identifier of the provisioning artifact. You must provide the name or + // ID, but not both. + ProvisioningArtifactID *string `json:"provisioningArtifactID,omitempty"` + // The name of the provisioning artifact. You must provide the name or ID, but + // not both. + ProvisioningArtifactName *string `json:"provisioningArtifactName,omitempty"` + // Parameters specified by the administrator that are required for provisioning + // the product. + ProvisioningParameters []*ProvisioningParameter `json:"provisioningParameters,omitempty"` + // An object that contains information about the provisioning preferences for + // a stack set. + ProvisioningPreferences *ProvisioningPreferences `json:"provisioningPreferences,omitempty"` + // One or more tags. + Tags []*Tag `json:"tags,omitempty"` + CustomProvisionedProductParameters `json:",inline"` +} + +// ProvisionedProductSpec defines the desired state of ProvisionedProduct +type ProvisionedProductSpec struct { + xpv1.ResourceSpec `json:",inline"` + ForProvider ProvisionedProductParameters `json:"forProvider"` +} + +// ProvisionedProductObservation defines the observed state of ProvisionedProduct +type ProvisionedProductObservation struct { + // The ARN of the provisioned product. + ARN *string `json:"arn,omitempty"` + // The UTC time stamp of the creation time. + CreatedTime *metav1.Time `json:"createdTime,omitempty"` + // The path identifier. + LastPathID *string `json:"lastPathID,omitempty"` + // The product identifier. For example, prod-abcdzk7xy33qa. + LastProductID *string `json:"lastProductID,omitempty"` + // The identifier of the provisioning artifact. For example, pa-4abcdjnxjj6ne. + LastProvisioningArtifactID *string `json:"lastProvisioningArtifactID,omitempty"` + // The record identifier of the last request performed on this provisioned product + // of the following types: + // + // * ProvisionedProduct + // + // * UpdateProvisionedProduct + // + // * ExecuteProvisionedProductPlan + // + // * TerminateProvisionedProduct + LastProvisioningRecordID *string `json:"lastProvisioningRecordID,omitempty"` + // The ARN of the launch role associated with the provisioned product. + LaunchRoleARN *string `json:"launchRoleARN,omitempty"` + + Outputs map[string]*RecordOutput `json:"outputs,omitempty"` + // The identifier of the provisioned product. + ProvisionedProductID *string `json:"provisionedProductID,omitempty"` + // The type of provisioned product. The supported values are CFN_STACK and CFN_STACKSET. + ProvisionedProductType *string `json:"provisionedProductType,omitempty"` + // The errors that occurred. + RecordErrors []*RecordError `json:"recordErrors,omitempty"` + // The identifier of the record. + RecordID *string `json:"recordID,omitempty"` + // One or more tags. + RecordTags []*RecordTag `json:"recordTags,omitempty"` + // The record type. + // + // * PROVISION_PRODUCT + // + // * UPDATE_PROVISIONED_PRODUCT + // + // * TERMINATE_PROVISIONED_PRODUCT + RecordType *string `json:"recordType,omitempty"` + // The current status of the provisioned product. + // + // * AVAILABLE - Stable state, ready to perform any operation. The most recent + // operation succeeded and completed. + // + // * UNDER_CHANGE - Transitive state. Operations performed might not have + // valid results. Wait for an AVAILABLE status before performing operations. + // + // * TAINTED - Stable state, ready to perform any operation. The stack has + // completed the requested operation but is not exactly what was requested. + // For example, a request to update to a new version failed and the stack + // rolled back to the current version. + // + // * ERROR - An unexpected error occurred. The provisioned product exists + // but the stack is not running. For example, CloudFormation received a parameter + // value that was not valid and could not launch the stack. + // + // * PLAN_IN_PROGRESS - Transitive state. The plan operations were performed + // to provision a new product, but resources have not yet been created. After + // reviewing the list of resources to be created, execute the plan. Wait + // for an AVAILABLE status before performing operations. + Status *string `json:"status,omitempty"` + // The current status message of the provisioned product. + StatusMessage *string `json:"statusMessage,omitempty"` + // The time when the record was last updated. + UpdatedTime *metav1.Time `json:"updatedTime,omitempty"` +} + +// ProvisionedProductStatus defines the observed state of ProvisionedProduct. +type ProvisionedProductStatus struct { + xpv1.ResourceStatus `json:",inline"` + AtProvider ProvisionedProductObservation `json:"atProvider,omitempty"` +} + +// +kubebuilder:object:root=true + +// ProvisionedProduct is the Schema for the ProvisionedProducts API +// +kubebuilder:printcolumn:name="READY",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status" +// +kubebuilder:printcolumn:name="SYNCED",type="string",JSONPath=".status.conditions[?(@.type=='Synced')].status" +// +kubebuilder:printcolumn:name="EXTERNAL-NAME",type="string",JSONPath=".metadata.annotations.crossplane\\.io/external-name" +// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:subresource:status +// +kubebuilder:storageversion +// +kubebuilder:resource:scope=Cluster,categories={crossplane,managed,aws} +type ProvisionedProduct struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec ProvisionedProductSpec `json:"spec"` + Status ProvisionedProductStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ProvisionedProductList contains a list of ProvisionedProducts +type ProvisionedProductList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ProvisionedProduct `json:"items"` +} + +// Repository type metadata. +var ( + ProvisionedProductKind = "ProvisionedProduct" + ProvisionedProductGroupKind = schema.GroupKind{Group: CRDGroup, Kind: ProvisionedProductKind}.String() + ProvisionedProductKindAPIVersion = ProvisionedProductKind + "." + GroupVersion.String() + ProvisionedProductGroupVersionKind = GroupVersion.WithKind(ProvisionedProductKind) +) + +func init() { + SchemeBuilder.Register(&ProvisionedProduct{}, &ProvisionedProductList{}) +} diff --git a/apis/servicecatalog/v1alpha1/zz_types.go b/apis/servicecatalog/v1alpha1/zz_types.go new file mode 100644 index 0000000000..3599661801 --- /dev/null +++ b/apis/servicecatalog/v1alpha1/zz_types.go @@ -0,0 +1,330 @@ +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by ack-generate. DO NOT EDIT. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Hack to avoid import errors during build... +var ( + _ = &metav1.Time{} +) + +// +kubebuilder:skipversion +type CloudWatchDashboard struct { + Name *string `json:"name,omitempty"` +} + +// +kubebuilder:skipversion +type ConstraintDetail struct { + ConstraintID *string `json:"constraintID,omitempty"` + + Owner *string `json:"owner,omitempty"` + + PortfolioID *string `json:"portfolioID,omitempty"` + + ProductID *string `json:"productID,omitempty"` +} + +// +kubebuilder:skipversion +type FailedServiceActionAssociation struct { + ProductID *string `json:"productID,omitempty"` + + ProvisioningArtifactID *string `json:"provisioningArtifactID,omitempty"` + + ServiceActionID *string `json:"serviceActionID,omitempty"` +} + +// +kubebuilder:skipversion +type LastSync struct { + LastSuccessfulSyncProvisioningArtifactID *string `json:"lastSuccessfulSyncProvisioningArtifactID,omitempty"` +} + +// +kubebuilder:skipversion +type LaunchPath struct { + ID *string `json:"id,omitempty"` +} + +// +kubebuilder:skipversion +type LaunchPathSummary struct { + ID *string `json:"id,omitempty"` + + Tags []*Tag `json:"tags,omitempty"` +} + +// +kubebuilder:skipversion +type PortfolioDetail struct { + DisplayName *string `json:"displayName,omitempty"` + + ID *string `json:"id,omitempty"` +} + +// +kubebuilder:skipversion +type PortfolioShareDetail struct { + PrincipalID *string `json:"principalID,omitempty"` +} + +// +kubebuilder:skipversion +type ProductViewDetail struct { + CreatedTime *metav1.Time `json:"createdTime,omitempty"` +} + +// +kubebuilder:skipversion +type ProductViewSummary struct { + ID *string `json:"id,omitempty"` + + Name *string `json:"name,omitempty"` + + ProductID *string `json:"productID,omitempty"` +} + +// +kubebuilder:skipversion +type ProvisionedProductAttribute struct { + ARN *string `json:"arn,omitempty"` + + CreatedTime *metav1.Time `json:"createdTime,omitempty"` + + ID *string `json:"id,omitempty"` + + IdempotencyToken *string `json:"idempotencyToken,omitempty"` + + LastProvisioningRecordID *string `json:"lastProvisioningRecordID,omitempty"` + + LastRecordID *string `json:"lastRecordID,omitempty"` + + LastSuccessfulProvisioningRecordID *string `json:"lastSuccessfulProvisioningRecordID,omitempty"` + + Name *string `json:"name,omitempty"` + + ProductID *string `json:"productID,omitempty"` + + ProductName *string `json:"productName,omitempty"` + + ProvisioningArtifactID *string `json:"provisioningArtifactID,omitempty"` + + ProvisioningArtifactName *string `json:"provisioningArtifactName,omitempty"` + + Status *string `json:"status,omitempty"` + + StatusMessage *string `json:"statusMessage,omitempty"` + + Tags []*Tag `json:"tags,omitempty"` + + Type *string `json:"type_,omitempty"` +} + +// +kubebuilder:skipversion +type ProvisionedProductDetail struct { + ARN *string `json:"arn,omitempty"` + + CreatedTime *metav1.Time `json:"createdTime,omitempty"` + + ID *string `json:"id,omitempty"` + + IdempotencyToken *string `json:"idempotencyToken,omitempty"` + + LastProvisioningRecordID *string `json:"lastProvisioningRecordID,omitempty"` + + LastRecordID *string `json:"lastRecordID,omitempty"` + + LastSuccessfulProvisioningRecordID *string `json:"lastSuccessfulProvisioningRecordID,omitempty"` + + LaunchRoleARN *string `json:"launchRoleARN,omitempty"` + + Name *string `json:"name,omitempty"` + + ProductID *string `json:"productID,omitempty"` + + ProvisioningArtifactID *string `json:"provisioningArtifactID,omitempty"` + + Status *string `json:"status,omitempty"` + + StatusMessage *string `json:"statusMessage,omitempty"` + + Type *string `json:"type_,omitempty"` +} + +// +kubebuilder:skipversion +type ProvisioningArtifact struct { + ID *string `json:"id,omitempty"` + + Name *string `json:"name,omitempty"` +} + +// +kubebuilder:skipversion +type ProvisioningArtifactDetail struct { + Description *string `json:"description,omitempty"` + + ID *string `json:"id,omitempty"` + + Name *string `json:"name,omitempty"` +} + +// +kubebuilder:skipversion +type ProvisioningArtifactParameter struct { + Description *string `json:"description,omitempty"` + + ParameterKey *string `json:"parameterKey,omitempty"` +} + +// +kubebuilder:skipversion +type ProvisioningArtifactPreferences struct { + StackSetAccounts []*string `json:"stackSetAccounts,omitempty"` + + StackSetRegions []*string `json:"stackSetRegions,omitempty"` +} + +// +kubebuilder:skipversion +type ProvisioningArtifactProperties struct { + Name *string `json:"name,omitempty"` +} + +// +kubebuilder:skipversion +type ProvisioningArtifactSummary struct { + ID *string `json:"id,omitempty"` + + Name *string `json:"name,omitempty"` +} + +// +kubebuilder:skipversion +type ProvisioningParameter struct { + Key *string `json:"key,omitempty"` + + Value *string `json:"value,omitempty"` +} + +// +kubebuilder:skipversion +type ProvisioningPreferences struct { + StackSetAccounts []*string `json:"stackSetAccounts,omitempty"` + + StackSetFailureToleranceCount *int64 `json:"stackSetFailureToleranceCount,omitempty"` + + StackSetFailureTolerancePercentage *int64 `json:"stackSetFailureTolerancePercentage,omitempty"` + + StackSetMaxConcurrencyCount *int64 `json:"stackSetMaxConcurrencyCount,omitempty"` + + StackSetMaxConcurrencyPercentage *int64 `json:"stackSetMaxConcurrencyPercentage,omitempty"` + + StackSetRegions []*string `json:"stackSetRegions,omitempty"` +} + +// +kubebuilder:skipversion +type RecordDetail struct { + CreatedTime *metav1.Time `json:"createdTime,omitempty"` + + LaunchRoleARN *string `json:"launchRoleARN,omitempty"` + + PathID *string `json:"pathID,omitempty"` + + ProductID *string `json:"productID,omitempty"` + + ProvisionedProductID *string `json:"provisionedProductID,omitempty"` + + ProvisionedProductName *string `json:"provisionedProductName,omitempty"` + + ProvisionedProductType *string `json:"provisionedProductType,omitempty"` + + ProvisioningArtifactID *string `json:"provisioningArtifactID,omitempty"` + + RecordErrors []*RecordError `json:"recordErrors,omitempty"` + + RecordID *string `json:"recordID,omitempty"` + + RecordTags []*RecordTag `json:"recordTags,omitempty"` + + RecordType *string `json:"recordType,omitempty"` + + Status *string `json:"status,omitempty"` + + UpdatedTime *metav1.Time `json:"updatedTime,omitempty"` +} + +// +kubebuilder:skipversion +type RecordError struct { + Code *string `json:"code,omitempty"` + + Description *string `json:"description,omitempty"` +} + +// +kubebuilder:skipversion +type RecordOutput struct { + Description *string `json:"description,omitempty"` + + OutputKey *string `json:"outputKey,omitempty"` + + OutputValue *string `json:"outputValue,omitempty"` +} + +// +kubebuilder:skipversion +type RecordTag struct { + Key *string `json:"key,omitempty"` + + Value *string `json:"value,omitempty"` +} + +// +kubebuilder:skipversion +type ServiceActionAssociation struct { + ProductID *string `json:"productID,omitempty"` + + ProvisioningArtifactID *string `json:"provisioningArtifactID,omitempty"` + + ServiceActionID *string `json:"serviceActionID,omitempty"` +} + +// +kubebuilder:skipversion +type ServiceActionSummary struct { + ID *string `json:"id,omitempty"` +} + +// +kubebuilder:skipversion +type StackInstance struct { + Account *string `json:"account,omitempty"` + + Region *string `json:"region,omitempty"` +} + +// +kubebuilder:skipversion +type Tag struct { + Key *string `json:"key,omitempty"` + + Value *string `json:"value,omitempty"` +} + +// +kubebuilder:skipversion +type UpdateProvisioningParameter struct { + Key *string `json:"key,omitempty"` + + Value *string `json:"value,omitempty"` +} + +// +kubebuilder:skipversion +type UpdateProvisioningPreferences struct { + StackSetAccounts []*string `json:"stackSetAccounts,omitempty"` + + StackSetFailureToleranceCount *int64 `json:"stackSetFailureToleranceCount,omitempty"` + + StackSetFailureTolerancePercentage *int64 `json:"stackSetFailureTolerancePercentage,omitempty"` + + StackSetMaxConcurrencyCount *int64 `json:"stackSetMaxConcurrencyCount,omitempty"` + + StackSetMaxConcurrencyPercentage *int64 `json:"stackSetMaxConcurrencyPercentage,omitempty"` + + StackSetRegions []*string `json:"stackSetRegions,omitempty"` +} diff --git a/examples/servicecatalog/provisionedproduct.yaml b/examples/servicecatalog/provisionedproduct.yaml new file mode 100644 index 0000000000..2e42b3c6dd --- /dev/null +++ b/examples/servicecatalog/provisionedproduct.yaml @@ -0,0 +1,24 @@ +--- +apiVersion: servicecatalog.aws.crossplane.io/v1alpha1 +kind: ProvisionedProduct +metadata: + name: awesome-s3-bucket +spec: + forProvider: + name: awesome-s3-bucket + region: us-east-1 + productID: prod-lqjhydvm2en9d + provisioningArtifactID: pa-luwckq1pyvaid + provisioningParameters: + - key: BucketName + value: "awesome-s3-bucket" + - key: BucketVersioning + value: "enabled" + - key: TransferAcceleration + value: "suspended" + - key: EncryptionType + value: "SSE-KMS" + - key: KMSArn + value: "none" + providerConfigRef: + name: provider-aws diff --git a/go.mod b/go.mod index 8e446c8b62..a6d58d4932 100644 --- a/go.mod +++ b/go.mod @@ -5,11 +5,12 @@ go 1.19 require ( github.com/aws-controllers-k8s/code-generator v0.26.1 github.com/aws/aws-sdk-go v1.44.174 - github.com/aws/aws-sdk-go-v2 v1.16.16 + github.com/aws/aws-sdk-go-v2 v1.19.0 github.com/aws/aws-sdk-go-v2/config v1.11.1 github.com/aws/aws-sdk-go-v2/credentials v1.6.5 github.com/aws/aws-sdk-go-v2/service/acm v1.10.0 github.com/aws/aws-sdk-go-v2/service/acmpca v1.12.0 + github.com/aws/aws-sdk-go-v2/service/cloudformation v1.30.1 github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider v1.17.3 github.com/aws/aws-sdk-go-v2/service/ec2 v1.26.0 github.com/aws/aws-sdk-go-v2/service/ecr v1.12.0 @@ -27,7 +28,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sns v1.13.0 github.com/aws/aws-sdk-go-v2/service/sqs v1.14.0 github.com/aws/aws-sdk-go-v2/service/sts v1.12.0 - github.com/aws/smithy-go v1.13.3 + github.com/aws/smithy-go v1.13.5 github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df github.com/crossplane/crossplane-runtime v0.20.0 github.com/crossplane/crossplane-tools v0.0.0-20230714144037-2684f4bc7638 @@ -35,6 +36,7 @@ require ( github.com/go-ini/ini v1.67.0 github.com/golang/mock v1.5.0 github.com/google/go-cmp v0.5.9 + github.com/google/uuid v1.3.0 github.com/mattbaird/jsonpatch v0.0.0-20200820163806-098863c1fc24 github.com/mitchellh/copystructure v1.0.0 github.com/onsi/gomega v1.27.7 @@ -58,8 +60,8 @@ require ( github.com/aws-controllers-k8s/pkg v0.0.4 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.0.0 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.8.2 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.3.2 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.5.0 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.5.2 // indirect @@ -91,7 +93,6 @@ require ( github.com/golang/protobuf v1.5.3 // indirect github.com/google/gnostic v0.6.9 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.3.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect diff --git a/go.sum b/go.sum index 9d9fd3ab76..28a0f8f693 100644 --- a/go.sum +++ b/go.sum @@ -98,8 +98,9 @@ github.com/aws/aws-sdk-go-v2 v1.10.0/go.mod h1:U/EyyVvKtzmFeQQcca7eBotKdlpcP2zzU github.com/aws/aws-sdk-go-v2 v1.11.2/go.mod h1:SQfA+m2ltnu1cA0soUkj4dRSsmITiVQUJvBIZjzfPyQ= github.com/aws/aws-sdk-go-v2 v1.16.2/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU= github.com/aws/aws-sdk-go-v2 v1.16.7/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw= -github.com/aws/aws-sdk-go-v2 v1.16.16 h1:M1fj4FE2lB4NzRb9Y0xdWsn2P0+2UHVxwKyOa4YJNjk= github.com/aws/aws-sdk-go-v2 v1.16.16/go.mod h1:SwiyXi/1zTUZ6KIAmLK5V5ll8SiURNUYOqTerZPaF9k= +github.com/aws/aws-sdk-go-v2 v1.19.0 h1:klAT+y3pGFBU/qVf1uzwttpBbiuozJYWzNLHioyDJ+k= +github.com/aws/aws-sdk-go-v2 v1.19.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.0.0 h1:yVUAwvJC/0WNPbyl0nA3j1L6CW1CN8wBubCRqtG7JLI= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.0.0/go.mod h1:Xn6sxgRuIDflLRJFj5Ev7UxABIkNbccFPV/p8itDReM= github.com/aws/aws-sdk-go-v2/config v1.11.1 h1:KXSjb7ZMLRtjxClFptukTYibiOqJS9NwBO+9WD3UMto= @@ -111,19 +112,23 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.8.2/go.mod h1:dF2F6tXEOgmW5X1ZF github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.2/go.mod h1:SgKKNBIoDC/E1ZCDhhMW3yalWjwuLjMcpLzsM/QQnWo= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.9/go.mod h1:AnVH5pvai0pAF4lXRq0bmhbes1u9R8wTE+g+183bZNM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.14/go.mod h1:kdjrMwHwrC3+FsKhNcCMJ7tUVj/8uSD5CZXeQ4wV6fM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23 h1:s4g/wnzMf+qepSNgTvaQQHNxyMLKSawNhKCPNy++2xY= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23/go.mod h1:2DFxAQ9pfIRy0imBCJv+vZ2X6RKxves6fbnEuSry6b4= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35 h1:hMUCiE3Zi5AHrRNGf5j985u0WyqI6r2NULhUfo0N/No= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35/go.mod h1:ipR5PvpSPqIqL5Mi82BxLnfMkHVbmco8kUwO2xrCi0M= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.0.2/go.mod h1:xT4XX6w5Sa3dhg50JrYyy3e4WPYo/+WjY/BXtqXVunU= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.3/go.mod h1:ssOhaLpRlh88H3UmEcsBoVKq309quMvm3Ds8e9d4eJM= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.8/go.mod h1:ZIV8GYoC6WLBW5KGs+o4rsc65/ozd+eQ0L31XF5VDwk= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17 h1:/K482T5A3623WJgWT8w1yRAFK4RzGzEl7y39yhtn9eA= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17/go.mod h1:pRwaTYCJemADaqCbUAxltMoHKata7hmB5PjEXeu0kfg= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29 h1:yOpYx+FTBdpk/g+sBU6Cb1H0U/TLEcYYp66mYqsPpcc= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29/go.mod h1:M/eUABlDbw2uVrdAn+UsI6M727qp2fxkp8K0ejcBDUY= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.2 h1:IQup8Q6lorXeiA/rK72PeToWoWK8h7VAPgHNWdSrtgE= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.2/go.mod h1:VITe/MdW6EMXPb0o0txu/fsonXbMHUU2OC2Qp7ivU4o= github.com/aws/aws-sdk-go-v2/service/acm v1.10.0 h1:h00NJuGEVi36k1BkVMpJQRRyye2SaPaCv2tQD0rm/uE= github.com/aws/aws-sdk-go-v2/service/acm v1.10.0/go.mod h1:I43heu2RNNVqzR8q069JCllVMVgGRlDfNHRThRD70Kc= github.com/aws/aws-sdk-go-v2/service/acmpca v1.12.0 h1:JL8J0ji+JK0t7JUUNtZg8atEUThdzk0KCgXY4a+7UXE= github.com/aws/aws-sdk-go-v2/service/acmpca v1.12.0/go.mod h1:AcCVrQKCGEWqfC1837nKJW2H5GdYseVfmGwhDjtcX3M= +github.com/aws/aws-sdk-go-v2/service/cloudformation v1.30.1 h1:MHKSdt+ECxOWD98MYj/Ocy4GS8GgAjgEDSPaiTaXP6U= +github.com/aws/aws-sdk-go-v2/service/cloudformation v1.30.1/go.mod h1:laKFhtn8EH6gcPl7KEQ4kcuSYcQF1tqUm82ENxMwlMk= github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider v1.17.3 h1:xtwjqxVDO9tAmoUXVfJyMKFLVJlosjKrFLLuW4V8VO0= github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider v1.17.3/go.mod h1:flSX+qf2r/mLgwTavyT/Gjs4dFtHcBmjMcVp/AqpSgc= github.com/aws/aws-sdk-go-v2/service/ec2 v1.26.0 h1:Q++veaxis1Dg7is9yi+aEPsIBRAgdkUxoIvyud7jOyo= @@ -170,8 +175,9 @@ github.com/aws/smithy-go v1.8.1/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAm github.com/aws/smithy-go v1.9.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.11.2/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM= github.com/aws/smithy-go v1.12.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.13.3 h1:l7LYxGuzK6/K+NzJ2mC+VvLUbae0sL3bXU//04MkmnA= github.com/aws/smithy-go v1.13.3/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= +github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df h1:GSoSVRLoBaFpOOds6QyY1L8AX7uoY+Ln3BHc22W40X0= github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df/go.mod h1:hiVxq5OP2bUGBRNS3Z/bt/reCLFNbdcST6gISi1fiOM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= diff --git a/package/crds/servicecatalog.aws.crossplane.io_provisionedproducts.yaml b/package/crds/servicecatalog.aws.crossplane.io_provisionedproducts.yaml new file mode 100644 index 0000000000..22d0f624bc --- /dev/null +++ b/package/crds/servicecatalog.aws.crossplane.io_provisionedproducts.yaml @@ -0,0 +1,511 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.12.1 + name: provisionedproducts.servicecatalog.aws.crossplane.io +spec: + group: servicecatalog.aws.crossplane.io + names: + categories: + - crossplane + - managed + - aws + kind: ProvisionedProduct + listKind: ProvisionedProductList + plural: provisionedproducts + singular: provisionedproduct + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=='Ready')].status + name: READY + type: string + - jsonPath: .status.conditions[?(@.type=='Synced')].status + name: SYNCED + type: string + - jsonPath: .metadata.annotations.crossplane\.io/external-name + name: EXTERNAL-NAME + type: string + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ProvisionedProduct is the Schema for the ProvisionedProducts + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ProvisionedProductSpec defines the desired state of ProvisionedProduct + properties: + deletionPolicy: + default: Delete + description: 'DeletionPolicy specifies what will happen to the underlying + external when this managed resource is deleted - either "Delete" + or "Orphan" the external resource. This field is planned to be deprecated + in favor of the ManagementPolicies field in a future release. Currently, + both could be set independently and non-default values would be + honored if the feature flag is enabled. See the design doc for more + information: https://github.com/crossplane/crossplane/blob/499895a25d1a1a0ba1604944ef98ac7a1a71f197/design/design-doc-observe-only-resources.md?plain=1#L223' + enum: + - Orphan + - Delete + type: string + forProvider: + description: ProvisionedProductParameters defines the desired state + of ProvisionedProduct + properties: + acceptLanguage: + description: "The language code. \n * en - English (default) \n + * jp - Japanese \n * zh - Chinese" + type: string + name: + description: A user-friendly name for the provisioned product. + This value must be unique for the Amazon Web Services account + and cannot be updated after the product is provisioned. + type: string + notificationARNs: + description: Passed to CloudFormation. The SNS topic ARNs to which + to publish stack-related events. + items: + type: string + type: array + pathID: + description: The path identifier of the product. This value is + optional if the product has a default path, and required if + the product has more than one path. To list the paths for a + product, use ListLaunchPaths. You must provide the name or ID, + but not both. + type: string + pathName: + description: The name of the path. You must provide the name or + ID, but not both. + type: string + productID: + description: The product identifier. You must provide the name + or ID, but not both. + type: string + productName: + description: The name of the product. You must provide the name + or ID, but not both. + type: string + provisioningArtifactID: + description: The identifier of the provisioning artifact. You + must provide the name or ID, but not both. + type: string + provisioningArtifactName: + description: The name of the provisioning artifact. You must provide + the name or ID, but not both. + type: string + provisioningParameters: + description: Parameters specified by the administrator that are + required for provisioning the product. + items: + properties: + key: + type: string + value: + type: string + type: object + type: array + provisioningPreferences: + description: An object that contains information about the provisioning + preferences for a stack set. + properties: + stackSetAccounts: + items: + type: string + type: array + stackSetFailureToleranceCount: + format: int64 + type: integer + stackSetFailureTolerancePercentage: + format: int64 + type: integer + stackSetMaxConcurrencyCount: + format: int64 + type: integer + stackSetMaxConcurrencyPercentage: + format: int64 + type: integer + stackSetRegions: + items: + type: string + type: array + type: object + region: + description: Region is which region the ProvisionedProduct will + be created. + type: string + tags: + description: One or more tags. + items: + properties: + key: + type: string + value: + type: string + type: object + type: array + required: + - name + - region + type: object + managementPolicies: + default: + - '*' + description: 'THIS IS AN ALPHA FIELD. Do not use it in production. + It is not honored unless the relevant Crossplane feature flag is + enabled, and may be changed or removed without notice. ManagementPolicies + specify the array of actions Crossplane is allowed to take on the + managed and external resources. This field is planned to replace + the DeletionPolicy field in a future release. Currently, both could + be set independently and non-default values would be honored if + the feature flag is enabled. If both are custom, the DeletionPolicy + field will be ignored. See the design doc for more information: + https://github.com/crossplane/crossplane/blob/499895a25d1a1a0ba1604944ef98ac7a1a71f197/design/design-doc-observe-only-resources.md?plain=1#L223 + and this one: https://github.com/crossplane/crossplane/blob/444267e84783136daa93568b364a5f01228cacbe/design/one-pager-ignore-changes.md' + items: + description: A ManagementAction represents an action that the Crossplane + controllers can take on an external resource. + enum: + - Observe + - Create + - Update + - Delete + - LateInitialize + - '*' + type: string + type: array + providerConfigRef: + default: + name: default + description: ProviderConfigReference specifies how the provider that + will be used to create, observe, update, and delete this managed + resource should be configured. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: Resolution specifies whether resolution of this + reference is required. The default is 'Required', which + means the reconcile will fail if the reference cannot be + resolved. 'Optional' means this reference will be a no-op + if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: Resolve specifies when this reference should + be resolved. The default is 'IfNotPresent', which will attempt + to resolve the reference only when the corresponding field + is not present. Use 'Always' to resolve the reference on + every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + providerRef: + description: 'ProviderReference specifies the provider that will be + used to create, observe, update, and delete this managed resource. + Deprecated: Please use ProviderConfigReference, i.e. `providerConfigRef`' + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: Resolution specifies whether resolution of this + reference is required. The default is 'Required', which + means the reconcile will fail if the reference cannot be + resolved. 'Optional' means this reference will be a no-op + if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: Resolve specifies when this reference should + be resolved. The default is 'IfNotPresent', which will attempt + to resolve the reference only when the corresponding field + is not present. Use 'Always' to resolve the reference on + every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + publishConnectionDetailsTo: + description: PublishConnectionDetailsTo specifies the connection secret + config which contains a name, metadata and a reference to secret + store config to which any connection details for this managed resource + should be written. Connection details frequently include the endpoint, + username, and password required to connect to the managed resource. + properties: + configRef: + default: + name: default + description: SecretStoreConfigRef specifies which secret store + config should be used for this ConnectionSecret. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: Resolution specifies whether resolution of + this reference is required. The default is 'Required', + which means the reconcile will fail if the reference + cannot be resolved. 'Optional' means this reference + will be a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: Resolve specifies when this reference should + be resolved. The default is 'IfNotPresent', which will + attempt to resolve the reference only when the corresponding + field is not present. Use 'Always' to resolve the reference + on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + metadata: + description: Metadata is the metadata for connection secret. + properties: + annotations: + additionalProperties: + type: string + description: Annotations are the annotations to be added to + connection secret. - For Kubernetes secrets, this will be + used as "metadata.annotations". - It is up to Secret Store + implementation for others store types. + type: object + labels: + additionalProperties: + type: string + description: Labels are the labels/tags to be added to connection + secret. - For Kubernetes secrets, this will be used as "metadata.labels". + - It is up to Secret Store implementation for others store + types. + type: object + type: + description: Type is the SecretType for the connection secret. + - Only valid for Kubernetes Secret Stores. + type: string + type: object + name: + description: Name is the name of the connection secret. + type: string + required: + - name + type: object + writeConnectionSecretToRef: + description: WriteConnectionSecretToReference specifies the namespace + and name of a Secret to which any connection details for this managed + resource should be written. Connection details frequently include + the endpoint, username, and password required to connect to the + managed resource. This field is planned to be replaced in a future + release in favor of PublishConnectionDetailsTo. Currently, both + could be set independently and connection details would be published + to both without affecting each other. + properties: + name: + description: Name of the secret. + type: string + namespace: + description: Namespace of the secret. + type: string + required: + - name + - namespace + type: object + required: + - forProvider + type: object + status: + description: ProvisionedProductStatus defines the observed state of ProvisionedProduct. + properties: + atProvider: + description: ProvisionedProductObservation defines the observed state + of ProvisionedProduct + properties: + arn: + description: The ARN of the provisioned product. + type: string + createdTime: + description: The UTC time stamp of the creation time. + format: date-time + type: string + lastPathID: + description: The path identifier. + type: string + lastProductID: + description: The product identifier. For example, prod-abcdzk7xy33qa. + type: string + lastProvisioningArtifactID: + description: The identifier of the provisioning artifact. For + example, pa-4abcdjnxjj6ne. + type: string + lastProvisioningRecordID: + description: "The record identifier of the last request performed + on this provisioned product of the following types: \n * ProvisionedProduct + \n * UpdateProvisionedProduct \n * ExecuteProvisionedProductPlan + \n * TerminateProvisionedProduct" + type: string + launchRoleARN: + description: The ARN of the launch role associated with the provisioned + product. + type: string + outputs: + additionalProperties: + properties: + description: + type: string + outputKey: + type: string + outputValue: + type: string + type: object + type: object + provisionedProductID: + description: The identifier of the provisioned product. + type: string + provisionedProductType: + description: The type of provisioned product. The supported values + are CFN_STACK and CFN_STACKSET. + type: string + recordErrors: + description: The errors that occurred. + items: + properties: + code: + type: string + description: + type: string + type: object + type: array + recordID: + description: The identifier of the record. + type: string + recordTags: + description: One or more tags. + items: + properties: + key: + type: string + value: + type: string + type: object + type: array + recordType: + description: "The record type. \n * PROVISION_PRODUCT \n * UPDATE_PROVISIONED_PRODUCT + \n * TERMINATE_PROVISIONED_PRODUCT" + type: string + status: + description: "The current status of the provisioned product. \n + * AVAILABLE - Stable state, ready to perform any operation. + The most recent operation succeeded and completed. \n * UNDER_CHANGE + - Transitive state. Operations performed might not have valid + results. Wait for an AVAILABLE status before performing operations. + \n * TAINTED - Stable state, ready to perform any operation. + The stack has completed the requested operation but is not exactly + what was requested. For example, a request to update to a new + version failed and the stack rolled back to the current version. + \n * ERROR - An unexpected error occurred. The provisioned product + exists but the stack is not running. For example, CloudFormation + received a parameter value that was not valid and could not + launch the stack. \n * PLAN_IN_PROGRESS - Transitive state. + The plan operations were performed to provision a new product, + but resources have not yet been created. After reviewing the + list of resources to be created, execute the plan. Wait for + an AVAILABLE status before performing operations." + type: string + statusMessage: + description: The current status message of the provisioned product. + type: string + updatedTime: + description: The time when the record was last updated. + format: date-time + type: string + type: object + conditions: + description: Conditions of the resource. + items: + description: A Condition that may apply to a resource. + properties: + lastTransitionTime: + description: LastTransitionTime is the last time this condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A Message containing details about this condition's + last transition from one status to another, if any. + type: string + reason: + description: A Reason for this condition's last transition from + one status to another. + type: string + status: + description: Status of this condition; is it currently True, + False, or Unknown? + type: string + type: + description: Type of this condition. At most one of each condition + type may apply to a resource at any point in time. + type: string + required: + - lastTransitionTime + - reason + - status + - type + type: object + type: array + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/pkg/clients/servicecatalog/fake/fake.go b/pkg/clients/servicecatalog/fake/fake.go new file mode 100644 index 0000000000..1d6a10d298 --- /dev/null +++ b/pkg/clients/servicecatalog/fake/fake.go @@ -0,0 +1,69 @@ +/* +Copyright 2023 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package fake + +import ( + "context" + + cfsdkv2types "github.com/aws/aws-sdk-go-v2/service/cloudformation/types" + "github.com/aws/aws-sdk-go/aws/request" + svcsdk "github.com/aws/aws-sdk-go/service/servicecatalog" + + clientset "github.com/crossplane-contrib/provider-aws/pkg/clients/servicecatalog" +) + +var _ clientset.Client = (*MockCustomServiceCatalogClient)(nil) + +// MockCustomServiceCatalogClient is a type that implements all the methods for Client interface +type MockCustomServiceCatalogClient struct { + MockGetCloudformationStackParameters func(provisionedProductOutputs []*svcsdk.RecordOutput) ([]cfsdkv2types.Parameter, error) + MockGetProvisionedProductOutputs func(getPPInput *svcsdk.GetProvisionedProductOutputsInput) (*svcsdk.GetProvisionedProductOutputsOutput, error) + MockDescribeRecord func(describeRecordInput *svcsdk.DescribeRecordInput) (*svcsdk.DescribeRecordOutput, error) + MockDescribeProduct func(*svcsdk.DescribeProductInput) (*svcsdk.DescribeProductOutput, error) + MockDescribeProvisioningArtifact func(*svcsdk.DescribeProvisioningArtifactInput) (*svcsdk.DescribeProvisioningArtifactOutput, error) + MockUpdateProvisionedProductWithContext func(context.Context, *svcsdk.UpdateProvisionedProductInput, ...request.Option) (*svcsdk.UpdateProvisionedProductOutput, error) +} + +// GetCloudformationStackParameters mocks GetCloudformationStackParameters method +func (m *MockCustomServiceCatalogClient) GetCloudformationStackParameters(provisionedProductOutputs []*svcsdk.RecordOutput) ([]cfsdkv2types.Parameter, error) { + return m.MockGetCloudformationStackParameters(provisionedProductOutputs) +} + +// GetProvisionedProductOutputs mocks GetProvisionedProductOutputs method +func (m *MockCustomServiceCatalogClient) GetProvisionedProductOutputs(getPPInput *svcsdk.GetProvisionedProductOutputsInput) (*svcsdk.GetProvisionedProductOutputsOutput, error) { + return m.MockGetProvisionedProductOutputs(getPPInput) +} + +// DescribeRecord mocks DescribeRecord method +func (m *MockCustomServiceCatalogClient) DescribeRecord(describeRecordInput *svcsdk.DescribeRecordInput) (*svcsdk.DescribeRecordOutput, error) { + return m.MockDescribeRecord(describeRecordInput) +} + +// DescribeProvisioningArtifact mocks DescribeProvisioningArtifact method +func (m *MockCustomServiceCatalogClient) DescribeProvisioningArtifact(input *svcsdk.DescribeProvisioningArtifactInput) (*svcsdk.DescribeProvisioningArtifactOutput, error) { + return m.MockDescribeProvisioningArtifact(input) +} + +// UpdateProvisionedProductWithContext mocks UpdateProvisionedProductWithContext method +func (m *MockCustomServiceCatalogClient) UpdateProvisionedProductWithContext(ctx context.Context, input *svcsdk.UpdateProvisionedProductInput, opts ...request.Option) (*svcsdk.UpdateProvisionedProductOutput, error) { + return m.MockUpdateProvisionedProductWithContext(ctx, input, opts...) +} + +// DescribeProduct mocks DescribeProduct method +func (m *MockCustomServiceCatalogClient) DescribeProduct(input *svcsdk.DescribeProductInput) (*svcsdk.DescribeProductOutput, error) { + return m.MockDescribeProduct(input) +} diff --git a/pkg/clients/servicecatalog/servicecatalog.go b/pkg/clients/servicecatalog/servicecatalog.go new file mode 100644 index 0000000000..e62802b351 --- /dev/null +++ b/pkg/clients/servicecatalog/servicecatalog.go @@ -0,0 +1,96 @@ +/* +Copyright 2023 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package servicecatalog + +import ( + "context" + "errors" + + cfsdkv2 "github.com/aws/aws-sdk-go-v2/service/cloudformation" + cfsdkv2types "github.com/aws/aws-sdk-go-v2/service/cloudformation/types" + "github.com/aws/aws-sdk-go/aws/request" + svcsdk "github.com/aws/aws-sdk-go/service/servicecatalog" + svcsdkapi "github.com/aws/aws-sdk-go/service/servicecatalog/servicecatalogiface" +) + +const ( + cloudformationArnOutputName = "CloudformationStackARN" + errCloudformationStackArnNotFound = "provisioned product outputs do not contain cloudformation stack arn" +) + +// Client represents a custom client to retrieve information from AWS related to service catalog or cloud formation as resource behind the provisioned product +type Client interface { + GetCloudformationStackParameters([]*svcsdk.RecordOutput) ([]cfsdkv2types.Parameter, error) + GetProvisionedProductOutputs(*svcsdk.GetProvisionedProductOutputsInput) (*svcsdk.GetProvisionedProductOutputsOutput, error) + DescribeRecord(*svcsdk.DescribeRecordInput) (*svcsdk.DescribeRecordOutput, error) + DescribeProduct(*svcsdk.DescribeProductInput) (*svcsdk.DescribeProductOutput, error) + DescribeProvisioningArtifact(*svcsdk.DescribeProvisioningArtifactInput) (*svcsdk.DescribeProvisioningArtifactOutput, error) + UpdateProvisionedProductWithContext(context.Context, *svcsdk.UpdateProvisionedProductInput, ...request.Option) (*svcsdk.UpdateProvisionedProductOutput, error) +} + +// CustomServiceCatalogClient is the implementation of a Client +type CustomServiceCatalogClient struct { + CfClient *cfsdkv2.Client + Client svcsdkapi.ServiceCatalogAPI +} + +// GetCloudformationStackParameters retrieves parameters from cloudformation stack based on outputs from provisioned product +func (c *CustomServiceCatalogClient) GetCloudformationStackParameters(provisionedProductOutputs []*svcsdk.RecordOutput) ([]cfsdkv2types.Parameter, error) { + describeCfStacksInput := cfsdkv2.DescribeStacksInput{} + for i, output := range provisionedProductOutputs { + if *output.OutputKey == cloudformationArnOutputName { + describeCfStacksInput.StackName = output.OutputValue + break + } + if i+1 == len(provisionedProductOutputs) { + return []cfsdkv2types.Parameter{}, errors.New(errCloudformationStackArnNotFound) + } + } + describeCfStacksOutput, err := c.CfClient.DescribeStacks(context.TODO(), &describeCfStacksInput) + if err != nil { + return []cfsdkv2types.Parameter{}, err + } + return describeCfStacksOutput.Stacks[0].Parameters, nil +} + +// GetProvisionedProductOutputs is wrapped (*ServiceCatalog) GetProvisionedProductOutputs from github.com/aws/aws-sdk-go/service/servicecatalog +func (c *CustomServiceCatalogClient) GetProvisionedProductOutputs(getPPInput *svcsdk.GetProvisionedProductOutputsInput) (*svcsdk.GetProvisionedProductOutputsOutput, error) { + getPPOutput, err := c.Client.GetProvisionedProductOutputs(getPPInput) + return getPPOutput, err +} + +// DescribeRecord is wrapped (*ServiceCatalog) DescribeRecord from github.com/aws/aws-sdk-go/service/servicecatalog +func (c *CustomServiceCatalogClient) DescribeRecord(describeRecordInput *svcsdk.DescribeRecordInput) (*svcsdk.DescribeRecordOutput, error) { + describeRecordOutput, err := c.Client.DescribeRecord(describeRecordInput) + return describeRecordOutput, err +} + +// DescribeProvisioningArtifact is wrapped (*ServiceCatalog) DescribeProvisioningArtifact from github.com/aws/aws-sdk-go/service/servicecatalog +func (c *CustomServiceCatalogClient) DescribeProvisioningArtifact(input *svcsdk.DescribeProvisioningArtifactInput) (*svcsdk.DescribeProvisioningArtifactOutput, error) { + output, err := c.Client.DescribeProvisioningArtifact(input) + return output, err +} + +// UpdateProvisionedProductWithContext is wrapped (*ServiceCatalog) UpdateProvisionedProductWithContext from github.com/aws/aws-sdk-go/service/servicecatalog +func (c *CustomServiceCatalogClient) UpdateProvisionedProductWithContext(ctx context.Context, in *svcsdk.UpdateProvisionedProductInput, opts ...request.Option) (*svcsdk.UpdateProvisionedProductOutput, error) { + return c.Client.UpdateProvisionedProductWithContext(ctx, in, opts...) +} + +// DescribeProduct is wrapped (*ServiceCatalog) DescribeProduct from github.com/aws/aws-sdk-go/service/servicecatalog +func (c *CustomServiceCatalogClient) DescribeProduct(input *svcsdk.DescribeProductInput) (*svcsdk.DescribeProductOutput, error) { + return c.Client.DescribeProduct(input) +} diff --git a/pkg/controller/aws.go b/pkg/controller/aws.go index 00ccada293..b4c7060dd1 100644 --- a/pkg/controller/aws.go +++ b/pkg/controller/aws.go @@ -67,6 +67,7 @@ import ( "github.com/crossplane-contrib/provider-aws/pkg/controller/s3" "github.com/crossplane-contrib/provider-aws/pkg/controller/s3control" "github.com/crossplane-contrib/provider-aws/pkg/controller/secretsmanager" + "github.com/crossplane-contrib/provider-aws/pkg/controller/servicecatalog" "github.com/crossplane-contrib/provider-aws/pkg/controller/servicediscovery" "github.com/crossplane-contrib/provider-aws/pkg/controller/sesv2" "github.com/crossplane-contrib/provider-aws/pkg/controller/sfn" @@ -127,6 +128,7 @@ func Setup(mgr ctrl.Manager, o controller.Options) error { s3.Setup, s3control.Setup, secretsmanager.Setup, + servicecatalog.Setup, servicediscovery.Setup, sesv2.Setup, sfn.Setup, diff --git a/pkg/controller/servicecatalog/provisionedproduct/lifecycle.go b/pkg/controller/servicecatalog/provisionedproduct/lifecycle.go new file mode 100644 index 0000000000..9967fcd6b3 --- /dev/null +++ b/pkg/controller/servicecatalog/provisionedproduct/lifecycle.go @@ -0,0 +1,194 @@ +package provisionedproduct + +import ( + "context" + + "github.com/aws/aws-sdk-go/aws/awserr" + svcsdk "github.com/aws/aws-sdk-go/service/servicecatalog" + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + "github.com/crossplane/crossplane-runtime/pkg/meta" + "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" + "github.com/crossplane/crossplane-runtime/pkg/resource" + "github.com/pkg/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/pointer" + + svcapitypes "github.com/crossplane-contrib/provider-aws/apis/servicecatalog/v1alpha1" + aws "github.com/crossplane-contrib/provider-aws/pkg/clients" + awsclient "github.com/crossplane-contrib/provider-aws/pkg/clients" +) + +const ( + msgProvisionedProductStatusSdkTainted = "provisioned product has status TAINTED" + msgProvisionedProductStatusSdkUnderChange = "provisioned product is updating, availability depends on product" + msgProvisionedProductStatusSdkPlanInProgress = "provisioned product is awaiting plan approval" + msgProvisionedProductStatusSdkError = "provisioned product has status ERROR" + + errUpdatePending = "Provisioned product is already under change, not updating" +) + +func (c *custom) lateInitialize(spec *svcapitypes.ProvisionedProductParameters, _ *svcsdk.DescribeProvisionedProductOutput) error { + acceptLanguageEnglish := acceptLanguageEnglish + spec.AcceptLanguage = awsclient.LateInitializeStringPtr(spec.AcceptLanguage, &acceptLanguageEnglish) + return nil +} + +func (c *custom) isUpToDate(_ context.Context, ds *svcapitypes.ProvisionedProduct, resp *svcsdk.DescribeProvisionedProductOutput) (bool, string, error) { + // If the product is undergoing change, we want to assume that it is not up-to-date. This will force this resource + // to be queued for an update (which will be skipped due to UNDER_CHANGE), and once that update fails, we will + // recheck the status again. This will allow us to quickly transition from UNDER_CHANGE to AVAILABLE without having + // to wait for the entire polling interval to pass before re-checking the status. + if pointer.StringDeref(ds.Status.AtProvider.Status, "") == string(svcapitypes.ProvisionedProductStatus_SDK_UNDER_CHANGE) { + return true, "", nil + } + + getPPOutputInput := &svcsdk.GetProvisionedProductOutputsInput{ProvisionedProductId: resp.ProvisionedProductDetail.Id} + getPPOutput, err := c.client.GetProvisionedProductOutputs(getPPOutputInput) + if err != nil { + // We want to specifically handle this exception, since it will occur when something + // is wrong with the provisioned product (error on creation, tainted, etc) + // We will be able to handle those specific cases in postObserve + var aerr awserr.Error + if ok := errors.As(err, &aerr); ok { + if aerr.Code() == svcsdk.ErrCodeInvalidParametersException { + if aerr.Message() == "Last Successful Provisioning Record doesn't exist." { + return false, "", nil + } + } + } + + return false, "", errors.Wrap(err, "could not get provisioned product outputs") + } + c.cache.getProvisionedProductOutputs = getPPOutput.Outputs + cfStackParameters, err := c.client.GetCloudformationStackParameters(getPPOutput.Outputs) + if err != nil { + return false, "", errors.Wrap(err, "could not get cloudformation stack parameters") + } + + productOrArtifactAreChanged, err := c.productOrArtifactAreChanged(&ds.Spec.ForProvider, resp.ProvisionedProductDetail) + if err != nil { + return false, "", errors.Wrap(err, "could not discover if product or artifact ids have changed") + } + + if productOrArtifactAreChanged || provisioningParamsAreChanged(cfStackParameters, ds.Spec.ForProvider.ProvisioningParameters) { + return false, "", nil + } + return true, "", nil +} + +func (c *custom) postObserve(_ context.Context, cr *svcapitypes.ProvisionedProduct, resp *svcsdk.DescribeProvisionedProductOutput, obs managed.ExternalObservation, err error) (managed.ExternalObservation, error) { // nolint:gocyclo + if err != nil { + return managed.ExternalObservation{}, err + } + + describeRecordInput := svcsdk.DescribeRecordInput{Id: resp.ProvisionedProductDetail.LastRecordId} + describeRecordOutput, err := c.client.DescribeRecord(&describeRecordInput) + if err != nil { + return managed.ExternalObservation{}, errors.Wrap(err, "could not describe record") + } + + recordType := "" + if describeRecordOutput != nil && describeRecordOutput.RecordDetail != nil { + recordType = pointer.StringDeref(describeRecordOutput.RecordDetail.RecordType, "UPDATE_PROVISIONED_PRODUCT") + } + + ppStatus := aws.StringValue(resp.ProvisionedProductDetail.Status) + switch { + case ppStatus == string(svcapitypes.ProvisionedProductStatus_SDK_AVAILABLE): + cr.SetConditions(xpv1.Available()) + case ppStatus == string(svcapitypes.ProvisionedProductStatus_SDK_UNDER_CHANGE) && recordType == "PROVISION_PRODUCT": + cr.SetConditions(xpv1.Creating()) + case ppStatus == string(svcapitypes.ProvisionedProductStatus_SDK_UNDER_CHANGE) && recordType == "UPDATE_PROVISIONED_PRODUCT": + cr.SetConditions(xpv1.Unavailable().WithMessage(msgProvisionedProductStatusSdkUnderChange)) + case ppStatus == string(svcapitypes.ProvisionedProductStatus_SDK_UNDER_CHANGE) && recordType == "TERMINATE_PROVISIONED_PRODUCT": + cr.SetConditions(xpv1.Deleting()) + case ppStatus == string(svcapitypes.ProvisionedProductStatus_SDK_PLAN_IN_PROGRESS): + cr.SetConditions(xpv1.Available().WithMessage(msgProvisionedProductStatusSdkPlanInProgress)) + case ppStatus == string(svcapitypes.ProvisionedProductStatus_SDK_ERROR): + cr.SetConditions(xpv1.Unavailable().WithMessage(msgProvisionedProductStatusSdkError)) + case ppStatus == string(svcapitypes.ProvisionedProductStatus_SDK_TAINTED): + cr.SetConditions(xpv1.Unavailable().WithMessage(msgProvisionedProductStatusSdkTainted)) + } + + var outputs = make(map[string]*svcapitypes.RecordOutput) + for _, v := range c.cache.getProvisionedProductOutputs { + outputs[*v.OutputKey] = &svcapitypes.RecordOutput{ + Description: v.Description, + OutputValue: v.OutputValue} + } + + cr.Status.AtProvider.Outputs = outputs + cr.Status.AtProvider.ARN = resp.ProvisionedProductDetail.Arn + cr.Status.AtProvider.CreatedTime = &metav1.Time{Time: *resp.ProvisionedProductDetail.CreatedTime} + cr.Status.AtProvider.LastProvisioningRecordID = resp.ProvisionedProductDetail.LastProvisioningRecordId + cr.Status.AtProvider.LaunchRoleARN = resp.ProvisionedProductDetail.LaunchRoleArn + cr.Status.AtProvider.Status = resp.ProvisionedProductDetail.Status + cr.Status.AtProvider.StatusMessage = resp.ProvisionedProductDetail.StatusMessage + cr.Status.AtProvider.ProvisionedProductType = resp.ProvisionedProductDetail.Type + cr.Status.AtProvider.RecordType = describeRecordOutput.RecordDetail.RecordType + cr.Status.AtProvider.LastPathID = describeRecordOutput.RecordDetail.PathId + cr.Status.AtProvider.LastProductID = describeRecordOutput.RecordDetail.ProductId + cr.Status.AtProvider.LastProvisioningArtifactID = describeRecordOutput.RecordDetail.ProvisioningArtifactId + + return obs, nil +} + +func (c *custom) preCreate(_ context.Context, cr *svcapitypes.ProvisionedProduct, obj *svcsdk.ProvisionProductInput) error { + obj.ProvisionToken = aws.String(genIdempotencyToken()) + + // We want to specifically set this to match the forProvider name, as that + // is what we use to track the provisioned product + if cr.Spec.ForProvider.Name != nil { + meta.SetExternalName(cr, *cr.Spec.ForProvider.Name) + } + + return nil +} + +func (c *custom) postCreate(_ context.Context, cr *svcapitypes.ProvisionedProduct, obj *svcsdk.ProvisionProductOutput, cre managed.ExternalCreation, err error) (managed.ExternalCreation, error) { + if err != nil { + return cre, err + } + + // We are expected to set the external-name annotation upon creation since + // it can differ from the metadata.name for the ProvisionedProduct + if obj.RecordDetail != nil && obj.RecordDetail.ProvisionedProductName != nil { + meta.SetExternalName(cr, *obj.RecordDetail.ProvisionedProductName) + } + + return cre, nil +} + +// Update replaces the ExternalClient.Update function so we can perform some custom actions before delegating back to it +func (c *custom) Update(ctx context.Context, mg resource.Managed) (managed.ExternalUpdate, error) { + cr, ok := mg.(*svcapitypes.ProvisionedProduct) + if !ok { + return managed.ExternalUpdate{}, errors.New(errUnexpectedObject) + } + + // We want to check if we are in a state where we can actually update it + ppStatus := pointer.StringDeref(cr.Status.AtProvider.Status, "") + if ppStatus == "" || ppStatus == string(svcapitypes.ProvisionedProductStatus_SDK_UNDER_CHANGE) { + // If we do not yet have a status, or it is still under change, it has + // not yet been processed by the Service Catalog API, and requesting an + // update will cause a 400 error code to be returned prematurely. + return managed.ExternalUpdate{}, errors.New(errUpdatePending) + } + + return c.external.Update(ctx, mg) +} + +func (c *custom) preUpdate(_ context.Context, cr *svcapitypes.ProvisionedProduct, input *svcsdk.UpdateProvisionedProductInput) error { + input.UpdateToken = aws.String(genIdempotencyToken()) + input.ProvisionedProductName = aws.String(meta.GetExternalName(cr)) + return nil +} + +func (c *custom) preDelete(_ context.Context, cr *svcapitypes.ProvisionedProduct, obj *svcsdk.TerminateProvisionedProductInput) (bool, error) { + if pointer.StringDeref(cr.Status.AtProvider.Status, "") == string(svcapitypes.ProvisionedProductStatus_SDK_UNDER_CHANGE) { + return true, nil + } + obj.TerminateToken = aws.String(genIdempotencyToken()) + obj.ProvisionedProductName = aws.String(meta.GetExternalName(cr)) + return false, nil +} diff --git a/pkg/controller/servicecatalog/provisionedproduct/setup.go b/pkg/controller/servicecatalog/provisionedproduct/setup.go new file mode 100644 index 0000000000..965863cd0c --- /dev/null +++ b/pkg/controller/servicecatalog/provisionedproduct/setup.go @@ -0,0 +1,138 @@ +/* +Copyright 2023 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provisionedproduct + +import ( + "context" + + cfsdkv2 "github.com/aws/aws-sdk-go-v2/service/cloudformation" + svcsdk "github.com/aws/aws-sdk-go/service/servicecatalog" + "github.com/crossplane/crossplane-runtime/pkg/connection" + "github.com/crossplane/crossplane-runtime/pkg/controller" + "github.com/crossplane/crossplane-runtime/pkg/event" + "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" + "github.com/crossplane/crossplane-runtime/pkg/resource" + "github.com/pkg/errors" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + svcapitypes "github.com/crossplane-contrib/provider-aws/apis/servicecatalog/v1alpha1" + "github.com/crossplane-contrib/provider-aws/apis/v1alpha1" + awsclient "github.com/crossplane-contrib/provider-aws/pkg/clients" + clientset "github.com/crossplane-contrib/provider-aws/pkg/clients/servicecatalog" + "github.com/crossplane-contrib/provider-aws/pkg/features" +) + +const ( + acceptLanguageEnglish = "en" +) + +type customConnector struct { + kube client.Client +} + +type custom struct { + *external + + client clientset.Client + cache cache +} + +type cache struct { + getProvisionedProductOutputs []*svcsdk.RecordOutput +} + +// SetupProvisionedProduct adds a controller that reconciles a ProvisionedProduct +func SetupProvisionedProduct(mgr ctrl.Manager, o controller.Options) error { + name := managed.ControllerName(svcapitypes.ProvisionedProductKind) + + cps := []managed.ConnectionPublisher{managed.NewAPISecretPublisher(mgr.GetClient(), mgr.GetScheme())} + if o.Features.Enabled(features.EnableAlphaExternalSecretStores) { + cps = append(cps, connection.NewDetailsManager(mgr.GetClient(), v1alpha1.StoreConfigGroupVersionKind)) + } + + reconcilerOpts := []managed.ReconcilerOption{ + managed.WithExternalConnecter(&customConnector{kube: mgr.GetClient()}), + managed.WithPollInterval(o.PollInterval), + managed.WithLogger(o.Logger.WithValues("controller", name)), + managed.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name))), + managed.WithConnectionPublishers(cps...), + } + + if o.Features.Enabled(features.EnableAlphaManagementPolicies) { + reconcilerOpts = append(reconcilerOpts, managed.WithManagementPolicies()) + } + + return ctrl.NewControllerManagedBy(mgr). + Named(name). + WithOptions(o.ForControllerRuntime()). + WithEventFilter(resource.DesiredStateChanged()). + For(&svcapitypes.ProvisionedProduct{}). + Complete(managed.NewReconciler(mgr, + resource.ManagedKind(svcapitypes.ProvisionedProductGroupVersionKind), + reconcilerOpts...)) +} + +func (c *customConnector) Connect(ctx context.Context, mg resource.Managed) (managed.ExternalClient, error) { + cr, ok := mg.(*svcapitypes.ProvisionedProduct) + if !ok { + return nil, errors.New(errUnexpectedObject) + } + sess, err := awsclient.GetConfigV1(ctx, c.kube, mg, cr.Spec.ForProvider.Region) + if err != nil { + return nil, errors.Wrap(err, errCreateSession) + } + + awsCfg, err := awsclient.GetConfig(ctx, c.kube, mg, cr.Spec.ForProvider.Region) + if err != nil { + return nil, errors.Wrap(err, errCreateSession) + } + cfClient := cfsdkv2.NewFromConfig(*awsCfg) + + svcclient := svcsdk.New(sess) + + cust := &custom{ + client: &clientset.CustomServiceCatalogClient{ + CfClient: cfClient, + Client: svcclient, + }, + } + + // We do not re-implement all of the ExternalClient interface, so we want + // to reuse the generated one as much as we can (mostly for the Observe, + // Create, Update, Delete methods which call all of our custom hooks) + cust.external = &external{ + kube: c.kube, + client: svcclient, + + // All of our overrides must go here + postObserve: cust.postObserve, + lateInitialize: cust.lateInitialize, + isUpToDate: cust.isUpToDate, + preCreate: cust.preCreate, + postCreate: cust.postCreate, + preDelete: cust.preDelete, + preUpdate: cust.preUpdate, + + // If we do not implement a method, we must specify the no-op function + preObserve: nopPreObserve, + postDelete: nopPostDelete, + postUpdate: nopPostUpdate, + } + + return cust, nil +} diff --git a/pkg/controller/servicecatalog/provisionedproduct/setup_test.go b/pkg/controller/servicecatalog/provisionedproduct/setup_test.go new file mode 100644 index 0000000000..b417be64a5 --- /dev/null +++ b/pkg/controller/servicecatalog/provisionedproduct/setup_test.go @@ -0,0 +1,567 @@ +/* +Copyright 2023 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provisionedproduct + +import ( + "context" + "testing" + "time" + + cfsdkv2types "github.com/aws/aws-sdk-go-v2/service/cloudformation/types" + svcsdk "github.com/aws/aws-sdk-go/service/servicecatalog" + svcsdkapi "github.com/aws/aws-sdk-go/service/servicecatalog/servicecatalogiface" + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" + "github.com/crossplane/crossplane-runtime/pkg/test" + "github.com/google/go-cmp/cmp" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/crossplane-contrib/provider-aws/apis/servicecatalog/v1alpha1" + aws "github.com/crossplane-contrib/provider-aws/pkg/clients" + clientset "github.com/crossplane-contrib/provider-aws/pkg/clients/servicecatalog" + "github.com/crossplane-contrib/provider-aws/pkg/clients/servicecatalog/fake" +) + +const ( + provisioningArtifactID = "pa-1234567890" + acceptLanguage = "jp" + latelyInitializedAcceptLanguage = "en" +) + +type args struct { + kube client.Client + client svcsdkapi.ServiceCatalogAPI + customClient *fake.MockCustomServiceCatalogClient + provisionedProduct *v1alpha1.ProvisionedProduct + describeProvisionedProductOutput *svcsdk.DescribeProvisionedProductOutput +} + +type provisionedProductModifier func(provisionedProduct *v1alpha1.ProvisionedProduct) + +func withSpec(p v1alpha1.ProvisionedProductParameters) provisionedProductModifier { + return func(cr *v1alpha1.ProvisionedProduct) { cr.Spec.ForProvider = p } +} + +func withStatus(p v1alpha1.ProvisionedProductStatus) provisionedProductModifier { + return func(cr *v1alpha1.ProvisionedProduct) { cr.Status = p } +} + +func provisionedProduct(m ...provisionedProductModifier) *v1alpha1.ProvisionedProduct { + cr := &v1alpha1.ProvisionedProduct{} + cr.Name = "test-provisioned-product-name" + for _, f := range m { + f(cr) + } + return cr +} + +type describeProvisionedProductOutputModifier func(describeProvisionedProductOutput *svcsdk.DescribeProvisionedProductOutput) + +func withDetails(d svcsdk.ProvisionedProductDetail) describeProvisionedProductOutputModifier { + return func(output *svcsdk.DescribeProvisionedProductOutput) { output.ProvisionedProductDetail = &d } +} + +func describeProvisionedProduct(m ...describeProvisionedProductOutputModifier) *svcsdk.DescribeProvisionedProductOutput { + output := &svcsdk.DescribeProvisionedProductOutput{} + for _, f := range m { + f(output) + } + return output +} + +func prepareFakeExternal(fakeClient clientset.Client) func(*external) { + return func(e *external) { + c := &custom{client: fakeClient} + e.isUpToDate = c.isUpToDate + e.lateInitialize = c.lateInitialize + e.postObserve = c.postObserve + e.preCreate = c.preCreate + e.preDelete = c.preDelete + e.preUpdate = c.preUpdate + } +} + +func TestIsUpToDate(t *testing.T) { + provisioningArtifactID := provisioningArtifactID + type want struct { + result bool + err error + } + cases := map[string]struct { + args + want + }{ + "ArtifactIdHasChanged": { + args: args{ + provisionedProduct: provisionedProduct([]provisionedProductModifier{ + withSpec(v1alpha1.ProvisionedProductParameters{ + ProvisioningArtifactID: aws.String("pa-new1234567890"), + ProductID: aws.String("prod-1234567890"), + ProvisioningParameters: []*v1alpha1.ProvisioningParameter{ + {Key: aws.String("Parameter"), Value: aws.String("foo")}}, + }), + }...), + describeProvisionedProductOutput: describeProvisionedProduct([]describeProvisionedProductOutputModifier{ + withDetails(svcsdk.ProvisionedProductDetail{ + Id: aws.String("pp-fake"), + ProductId: aws.String("prod-1234567890"), + ProvisioningArtifactId: aws.String(provisioningArtifactID), + }), + }...), + customClient: &fake.MockCustomServiceCatalogClient{ + MockGetCloudformationStackParameters: func(provisionedProductOutputs []*svcsdk.RecordOutput) ([]cfsdkv2types.Parameter, error) { + return []cfsdkv2types.Parameter{{ParameterKey: aws.String("Parameter"), ParameterValue: aws.String("foo")}}, nil + }, + MockGetProvisionedProductOutputs: func(getPPInput *svcsdk.GetProvisionedProductOutputsInput) (*svcsdk.GetProvisionedProductOutputsOutput, error) { + return &svcsdk.GetProvisionedProductOutputsOutput{}, nil + }, + MockDescribeProduct: func(dpInput *svcsdk.DescribeProductInput) (*svcsdk.DescribeProductOutput, error) { + return &svcsdk.DescribeProductOutput{ + ProductViewSummary: &svcsdk.ProductViewSummary{ + ProductId: dpInput.Id, + Name: aws.String("fake product"), + }, + ProvisioningArtifacts: []*svcsdk.ProvisioningArtifact{ + { + Id: aws.String("prod-new1234567890"), + }, + }, + }, nil + }, + }, + }, + want: want{ + result: false, + err: nil, + }, + }, + "ParametersValueHasChanged": { + args: args{ + provisionedProduct: provisionedProduct([]provisionedProductModifier{ + withSpec(v1alpha1.ProvisionedProductParameters{ + ProvisioningArtifactID: aws.String(provisioningArtifactID), + ProvisioningParameters: []*v1alpha1.ProvisioningParameter{ + {Key: aws.String("ParameterIsNotChanged"), Value: aws.String("true")}}, + }), + }...), + describeProvisionedProductOutput: describeProvisionedProduct([]describeProvisionedProductOutputModifier{ + withDetails(svcsdk.ProvisionedProductDetail{ + Id: aws.String("pp-fake"), + ProvisioningArtifactId: aws.String(provisioningArtifactID), + }), + }...), + customClient: &fake.MockCustomServiceCatalogClient{ + MockGetCloudformationStackParameters: func(provisionedProductOutputs []*svcsdk.RecordOutput) ([]cfsdkv2types.Parameter, error) { + return []cfsdkv2types.Parameter{{ParameterKey: aws.String("ParameterIsNotChanged"), ParameterValue: aws.String("false")}}, nil + }, + MockGetProvisionedProductOutputs: func(getPPInput *svcsdk.GetProvisionedProductOutputsInput) (*svcsdk.GetProvisionedProductOutputsOutput, error) { + return &svcsdk.GetProvisionedProductOutputsOutput{}, nil + }, + MockDescribeProduct: func(dpInput *svcsdk.DescribeProductInput) (*svcsdk.DescribeProductOutput, error) { + return &svcsdk.DescribeProductOutput{ + ProductViewSummary: &svcsdk.ProductViewSummary{ + ProductId: dpInput.Id, + Name: aws.String("fake product"), + }, + ProvisioningArtifacts: []*svcsdk.ProvisioningArtifact{ + { + Id: aws.String(provisioningArtifactID), + }, + }, + }, nil + }, + }, + }, + want: want{ + result: false, + err: nil, + }, + }, + "NewParameterHasBeenAdded": { + args: args{ + provisionedProduct: provisionedProduct([]provisionedProductModifier{ + withSpec(v1alpha1.ProvisionedProductParameters{ + ProvisioningArtifactID: aws.String(provisioningArtifactID), + ProvisioningParameters: []*v1alpha1.ProvisioningParameter{ + {Key: aws.String("OldParameter"), Value: aws.String("foo")}, + {Key: aws.String("NewParameter"), Value: aws.String("bar")}, + }, + }), + }...), + describeProvisionedProductOutput: describeProvisionedProduct([]describeProvisionedProductOutputModifier{ + withDetails(svcsdk.ProvisionedProductDetail{ + Id: aws.String("pp-fake"), + ProvisioningArtifactId: aws.String(provisioningArtifactID), + }), + }...), + customClient: &fake.MockCustomServiceCatalogClient{ + MockGetCloudformationStackParameters: func(provisionedProductOutputs []*svcsdk.RecordOutput) ([]cfsdkv2types.Parameter, error) { + return []cfsdkv2types.Parameter{ + {ParameterKey: aws.String("OldParameter"), ParameterValue: aws.String("foo")}}, nil + }, + MockGetProvisionedProductOutputs: func(getPPInput *svcsdk.GetProvisionedProductOutputsInput) (*svcsdk.GetProvisionedProductOutputsOutput, error) { + return &svcsdk.GetProvisionedProductOutputsOutput{}, nil + }, + MockDescribeProduct: func(dpInput *svcsdk.DescribeProductInput) (*svcsdk.DescribeProductOutput, error) { + return &svcsdk.DescribeProductOutput{ + ProductViewSummary: &svcsdk.ProductViewSummary{ + ProductId: aws.String("prod-fake"), + Name: aws.String("fake product"), + }, + ProvisioningArtifacts: []*svcsdk.ProvisioningArtifact{ + { + Id: aws.String(provisioningArtifactID), + }, + }, + }, nil + }, + }, + }, + want: want{ + result: false, + err: nil, + }, + }, + "ExistingParameterHasBeenRemoved": { + args: args{ + provisionedProduct: provisionedProduct([]provisionedProductModifier{ + withSpec(v1alpha1.ProvisionedProductParameters{ + ProvisioningArtifactID: aws.String(provisioningArtifactID), + ProvisioningParameters: []*v1alpha1.ProvisioningParameter{ + {Key: aws.String("Parameter1"), Value: aws.String("foo")}, + }, + }), + }...), + describeProvisionedProductOutput: describeProvisionedProduct([]describeProvisionedProductOutputModifier{ + withDetails(svcsdk.ProvisionedProductDetail{ + Id: aws.String("pp-fake"), + ProvisioningArtifactId: aws.String(provisioningArtifactID), + }), + }...), + customClient: &fake.MockCustomServiceCatalogClient{ + MockGetCloudformationStackParameters: func(provisionedProductOutputs []*svcsdk.RecordOutput) ([]cfsdkv2types.Parameter, error) { + return []cfsdkv2types.Parameter{ + {ParameterKey: aws.String("Parameter1"), ParameterValue: aws.String("foo")}, + {ParameterKey: aws.String("Parameter2"), ParameterValue: aws.String("bar")}, + }, + nil + }, + MockGetProvisionedProductOutputs: func(getPPInput *svcsdk.GetProvisionedProductOutputsInput) (*svcsdk.GetProvisionedProductOutputsOutput, error) { + return &svcsdk.GetProvisionedProductOutputsOutput{}, nil + }, + MockDescribeProduct: func(dpInput *svcsdk.DescribeProductInput) (*svcsdk.DescribeProductOutput, error) { + return &svcsdk.DescribeProductOutput{ + ProductViewSummary: &svcsdk.ProductViewSummary{ + ProductId: dpInput.Id, + Name: aws.String("fake product"), + }, + ProvisioningArtifacts: []*svcsdk.ProvisioningArtifact{ + { + Id: aws.String(provisioningArtifactID), + }, + }, + }, nil + }, + }, + }, + want: want{ + result: false, + err: nil, + }, + }, + "ParametersAreNotChanged": { + args: args{ + provisionedProduct: provisionedProduct([]provisionedProductModifier{ + withSpec(v1alpha1.ProvisionedProductParameters{ + ProvisioningArtifactID: aws.String(provisioningArtifactID), + ProvisioningParameters: []*v1alpha1.ProvisioningParameter{ + {Key: aws.String("Parameter1"), Value: aws.String("foo")}, + {Key: aws.String("Parameter2"), Value: aws.String("bar")}, + }, + }), + }...), + describeProvisionedProductOutput: describeProvisionedProduct([]describeProvisionedProductOutputModifier{ + withDetails(svcsdk.ProvisionedProductDetail{ + Id: aws.String("pp-fake"), + ProvisioningArtifactId: aws.String(provisioningArtifactID), + }), + }...), + customClient: &fake.MockCustomServiceCatalogClient{ + MockGetCloudformationStackParameters: func(provisionedProductOutputs []*svcsdk.RecordOutput) ([]cfsdkv2types.Parameter, error) { + return []cfsdkv2types.Parameter{ + {ParameterKey: aws.String("Parameter1"), ParameterValue: aws.String("foo")}, + {ParameterKey: aws.String("Parameter2"), ParameterValue: aws.String("bar")}, + }, + nil + }, + MockGetProvisionedProductOutputs: func(getPPInput *svcsdk.GetProvisionedProductOutputsInput) (*svcsdk.GetProvisionedProductOutputsOutput, error) { + return &svcsdk.GetProvisionedProductOutputsOutput{}, nil + }, + MockDescribeProduct: func(dpInput *svcsdk.DescribeProductInput) (*svcsdk.DescribeProductOutput, error) { + return &svcsdk.DescribeProductOutput{ + ProductViewSummary: &svcsdk.ProductViewSummary{ + ProductId: dpInput.Id, + Name: aws.String("fake product"), + }, + ProvisioningArtifacts: []*svcsdk.ProvisioningArtifact{ + { + Id: aws.String(provisioningArtifactID), + }, + }, + }, nil + }, + }, + }, + want: want{ + result: true, + err: nil, + }, + }, + } + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + opts := []option{prepareFakeExternal(tc.args.customClient)} + e := newExternal(tc.args.kube, tc.args.client, opts) + result, _, err := e.isUpToDate(nil, tc.args.provisionedProduct, tc.args.describeProvisionedProductOutput) + if diff := cmp.Diff(err, tc.want.err, test.EquateErrors()); diff != "" { + t.Errorf("r: -want, +got:\n%s", diff) + } + if diff := cmp.Diff(result, tc.want.result); diff != "" { + t.Errorf("r: -want, +got:\n%s", diff) + } + }) + } +} + +func TestLateInitialize(t *testing.T) { + type want struct { + acceptLanguage string + } + cases := map[string]struct { + args + want + }{ + "ValuesAreNotSpecified": { + args: args{ + provisionedProduct: provisionedProduct([]provisionedProductModifier{ + withSpec(v1alpha1.ProvisionedProductParameters{ + AcceptLanguage: aws.String(""), + }), + }...), + }, + want: want{ + acceptLanguage: latelyInitializedAcceptLanguage, + }, + }, + "ValuesAreSpecified": { + args: args{ + provisionedProduct: provisionedProduct([]provisionedProductModifier{ + withSpec(v1alpha1.ProvisionedProductParameters{ + AcceptLanguage: aws.String(acceptLanguage), + }), + }...), + }, + want: want{ + acceptLanguage: acceptLanguage, + }, + }, + } + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + opts := []option{prepareFakeExternal(tc.args.customClient)} + e := newExternal(tc.args.kube, tc.args.client, opts) + _ = e.lateInitialize(&tc.args.provisionedProduct.Spec.ForProvider, tc.args.describeProvisionedProductOutput) + if diff := cmp.Diff(*tc.args.provisionedProduct.Spec.ForProvider.AcceptLanguage, tc.want.acceptLanguage); diff != "" { + t.Errorf("r: -want, +got:\n%s", diff) + } + }) + } +} + +func TestPostObserve(t *testing.T) { + type want struct { + status xpv1.Condition + } + testStaringTime := time.Now() + provisionedProductStatus := withStatus(v1alpha1.ProvisionedProductStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{}}}, + }) + cases := map[string]struct { + args + want + }{ + "StatusAvailable": { + args: args{ + provisionedProduct: provisionedProduct([]provisionedProductModifier{provisionedProductStatus}...), + describeProvisionedProductOutput: describeProvisionedProduct([]describeProvisionedProductOutputModifier{ + withDetails(svcsdk.ProvisionedProductDetail{ + Status: aws.String(string(v1alpha1.ProvisionedProductStatus_SDK_AVAILABLE)), + Arn: aws.String("arn:aws:servicecatalog:fake"), + CreatedTime: &testStaringTime, + LastSuccessfulProvisioningRecordId: aws.String("rec-fake"), + LaunchRoleArn: aws.String("arn:aws:iam::fake"), + StatusMessage: aws.String("fake"), + Type: aws.String("CFN_STACK"), + }), + }...), + customClient: &fake.MockCustomServiceCatalogClient{ + MockDescribeRecord: func(describeRecordInput *svcsdk.DescribeRecordInput) (*svcsdk.DescribeRecordOutput, error) { + return &svcsdk.DescribeRecordOutput{RecordDetail: &svcsdk.RecordDetail{RecordType: aws.String("PROVISION_PRODUCT")}}, nil + }, + }, + }, + want: want{ + status: xpv1.Available(), + }, + }, + "StatusUnavailableWithAmendment": { + args: args{ + provisionedProduct: provisionedProduct([]provisionedProductModifier{provisionedProductStatus}...), + describeProvisionedProductOutput: describeProvisionedProduct([]describeProvisionedProductOutputModifier{ + withDetails(svcsdk.ProvisionedProductDetail{ + Status: aws.String(string(v1alpha1.ProvisionedProductStatus_SDK_UNDER_CHANGE)), + Arn: aws.String("arn:aws:servicecatalog:fake"), + CreatedTime: &testStaringTime, + LastSuccessfulProvisioningRecordId: aws.String("rec-fake"), + LaunchRoleArn: aws.String("arn:aws:iam::fake"), + StatusMessage: aws.String("fake"), + Type: aws.String("CFN_STACK"), + }), + }...), + customClient: &fake.MockCustomServiceCatalogClient{ + MockDescribeRecord: func(describeRecordInput *svcsdk.DescribeRecordInput) (*svcsdk.DescribeRecordOutput, error) { + return &svcsdk.DescribeRecordOutput{RecordDetail: &svcsdk.RecordDetail{RecordType: aws.String("UPDATE_PROVISIONED_PRODUCT")}}, nil + }, + }, + }, + want: want{ + status: xpv1.Unavailable().WithMessage(msgProvisionedProductStatusSdkUnderChange), + }, + }, + "StatusReconcileErrorProductError": { + args: args{ + provisionedProduct: provisionedProduct([]provisionedProductModifier{provisionedProductStatus}...), + describeProvisionedProductOutput: describeProvisionedProduct([]describeProvisionedProductOutputModifier{ + withDetails(svcsdk.ProvisionedProductDetail{ + Status: aws.String(string(v1alpha1.ProvisionedProductStatus_SDK_ERROR)), + Arn: aws.String("arn:aws:servicecatalog:fake"), + CreatedTime: &testStaringTime, + LastSuccessfulProvisioningRecordId: aws.String("rec-fake"), + LaunchRoleArn: aws.String("arn:aws:iam::fake"), + StatusMessage: aws.String("fake"), + Type: aws.String("CFN_STACK"), + }), + }...), + customClient: &fake.MockCustomServiceCatalogClient{ + MockDescribeRecord: func(describeRecordInput *svcsdk.DescribeRecordInput) (*svcsdk.DescribeRecordOutput, error) { + return &svcsdk.DescribeRecordOutput{RecordDetail: &svcsdk.RecordDetail{RecordType: aws.String("PROVISION_PRODUCT")}}, nil + }, + }, + }, + want: want{ + status: xpv1.Unavailable().WithMessage(msgProvisionedProductStatusSdkError), + }, + }, + "StatusReconcileErrorProductTainted": { + args: args{ + provisionedProduct: provisionedProduct([]provisionedProductModifier{provisionedProductStatus}...), + describeProvisionedProductOutput: describeProvisionedProduct([]describeProvisionedProductOutputModifier{ + withDetails(svcsdk.ProvisionedProductDetail{ + Status: aws.String(string(v1alpha1.ProvisionedProductStatus_SDK_TAINTED)), + Arn: aws.String("arn:aws:servicecatalog:fake"), + CreatedTime: &testStaringTime, + LastSuccessfulProvisioningRecordId: aws.String("rec-fake"), + LaunchRoleArn: aws.String("arn:aws:iam::fake"), + StatusMessage: aws.String("fake"), + Type: aws.String("CFN_STACK"), + }), + }...), + customClient: &fake.MockCustomServiceCatalogClient{ + MockDescribeRecord: func(describeRecordInput *svcsdk.DescribeRecordInput) (*svcsdk.DescribeRecordOutput, error) { + return &svcsdk.DescribeRecordOutput{RecordDetail: &svcsdk.RecordDetail{RecordType: aws.String("PROVISION_PRODUCT")}}, nil + }, + }, + }, + want: want{ + status: xpv1.Unavailable().WithMessage(msgProvisionedProductStatusSdkTainted), + }, + }, + } + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + opts := []option{prepareFakeExternal(tc.args.customClient)} + e := newExternal(tc.args.kube, tc.args.client, opts) + _, _ = e.postObserve(context.TODO(), tc.args.provisionedProduct, tc.args.describeProvisionedProductOutput, managed.ExternalObservation{}, nil) + conditions := tc.args.provisionedProduct.Status.Conditions + latestCondition := conditions[len(conditions)-1] + if diff := cmp.Diff(latestCondition, tc.want.status); diff != "" { + t.Errorf("r: -want, +got:\n%s", diff) + } + test.EquateConditions() + }) + } +} + +func TestPreDelete(t *testing.T) { + type want struct { + ignoreDeletion bool + } + cases := map[string]struct { + args + want + }{ + "ignoreDeletion": { + args: args{ + provisionedProduct: provisionedProduct([]provisionedProductModifier{ + withStatus(v1alpha1.ProvisionedProductStatus{ + AtProvider: v1alpha1.ProvisionedProductObservation{ + Status: aws.String(string(v1alpha1.ProvisionedProductStatus_SDK_UNDER_CHANGE))}, + }), + }...), + }, + want: want{ + ignoreDeletion: true, + }, + }, + "passDeletion": { + args: args{ + provisionedProduct: provisionedProduct([]provisionedProductModifier{ + withStatus(v1alpha1.ProvisionedProductStatus{ + AtProvider: v1alpha1.ProvisionedProductObservation{ + Status: aws.String("NOT_UNDER_CHANGE")}, + }), + }...), + }, + want: want{ + ignoreDeletion: false, + }, + }, + } + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + opts := []option{prepareFakeExternal(tc.args.customClient)} + e := newExternal(tc.args.kube, tc.args.client, opts) + ignore, _ := e.preDelete(context.TODO(), tc.args.provisionedProduct, &svcsdk.TerminateProvisionedProductInput{}) + if diff := cmp.Diff(ignore, tc.want.ignoreDeletion); diff != "" { + t.Errorf("r: -want, +got:\n%s", diff) + + } + }) + } +} diff --git a/pkg/controller/servicecatalog/provisionedproduct/utils.go b/pkg/controller/servicecatalog/provisionedproduct/utils.go new file mode 100644 index 0000000000..d4fd526c42 --- /dev/null +++ b/pkg/controller/servicecatalog/provisionedproduct/utils.go @@ -0,0 +1,101 @@ +package provisionedproduct + +import ( + "fmt" + + cfsdkv2types "github.com/aws/aws-sdk-go-v2/service/cloudformation/types" + svcsdk "github.com/aws/aws-sdk-go/service/servicecatalog" + "github.com/google/uuid" + "github.com/pkg/errors" + "k8s.io/utils/pointer" + + svcapitypes "github.com/crossplane-contrib/provider-aws/apis/servicecatalog/v1alpha1" + aws "github.com/crossplane-contrib/provider-aws/pkg/clients" +) + +func provisioningParamsAreChanged(cfStackParams []cfsdkv2types.Parameter, currentParams []*svcapitypes.ProvisioningParameter) bool { + if len(cfStackParams) != len(currentParams) { + return true + } + + cfStackKeyValue := make(map[string]string) + for _, v := range cfStackParams { + cfStackKeyValue[*v.ParameterKey] = pointer.StringDeref(v.ParameterValue, "") + } + + for _, v := range currentParams { + if cfv, ok := cfStackKeyValue[*v.Key]; ok && pointer.StringEqual(&cfv, v.Value) { + continue + } else { + return true + } + } + + return false +} + +// productOrArtifactAreChanged will attempt to determine whether or not the currently requested SC product and provisioning artifact IDs have changed when using names instead of IDs +func (c *custom) productOrArtifactAreChanged(ds *svcapitypes.ProvisionedProductParameters, resp *svcsdk.ProvisionedProductDetail) (bool, error) { // nolint:gocyclo + var productID, artifactID string + + if ds.ProductID != nil { + productID = pointer.StringDeref(ds.ProductID, "") + } + if ds.ProvisioningArtifactID != nil { + artifactID = pointer.StringDeref(ds.ProvisioningArtifactID, "") + } + + if productID == "" || artifactID == "" { + // If we are using names for either the product or artifact, we should + // do a lookup using the DescribeProduct API to find the matching + // product (either by name or ID). In the returned output, we will have + // access to both the product ID and all of the available provisioning + // artifact IDs so we will be able to discover both IDs with a single + // API call. + dpInput := &svcsdk.DescribeProductInput{} + + if productID != "" { + dpInput.Id = aws.String(productID) + } else { + dpInput.Name = ds.ProductName + } + + describeProduct, err := c.client.DescribeProduct(dpInput) + if err != nil { + return true, errors.Wrap(err, "could not lookup product") + } + if describeProduct == nil || describeProduct.ProductViewSummary == nil { + return true, errors.New("could not find product") + } + + if productID == "" { + // fill in the product ID if we do not yet have it + if describeProduct == nil || describeProduct.ProductViewSummary == nil { + return true, errors.New("could not find product") + } + productID = pointer.StringDeref(describeProduct.ProductViewSummary.ProductId, "") + } + + if artifactID == "" { + // find the matching artifact + for _, artifact := range describeProduct.ProvisioningArtifacts { + if pointer.StringEqual(ds.ProvisioningArtifactName, artifact.Name) { + artifactID = pointer.StringDeref(artifact.Id, "") + break + } + } + } + + if artifactID == "" { + // we were unable to find an artifact that matches + return true, errors.New("could not find provisioning artifact") + } + } + + return pointer.StringDeref(resp.ProductId, "") != productID || + pointer.StringDeref(resp.ProvisioningArtifactId, "") != artifactID, nil +} + +func genIdempotencyToken() string { + return fmt.Sprintf("provider-aws-%s", uuid.New()) +} diff --git a/pkg/controller/servicecatalog/provisionedproduct/zz_controller.go b/pkg/controller/servicecatalog/provisionedproduct/zz_controller.go new file mode 100644 index 0000000000..344e2a527d --- /dev/null +++ b/pkg/controller/servicecatalog/provisionedproduct/zz_controller.go @@ -0,0 +1,315 @@ +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by ack-generate. DO NOT EDIT. + +package provisionedproduct + +import ( + "context" + + svcapi "github.com/aws/aws-sdk-go/service/servicecatalog" + svcsdk "github.com/aws/aws-sdk-go/service/servicecatalog" + svcsdkapi "github.com/aws/aws-sdk-go/service/servicecatalog/servicecatalogiface" + "github.com/google/go-cmp/cmp" + "github.com/pkg/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + "github.com/crossplane/crossplane-runtime/pkg/meta" + "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" + cpresource "github.com/crossplane/crossplane-runtime/pkg/resource" + + svcapitypes "github.com/crossplane-contrib/provider-aws/apis/servicecatalog/v1alpha1" + awsclient "github.com/crossplane-contrib/provider-aws/pkg/clients" +) + +const ( + errUnexpectedObject = "managed resource is not an ProvisionedProduct resource" + + errCreateSession = "cannot create a new session" + errCreate = "cannot create ProvisionedProduct in AWS" + errUpdate = "cannot update ProvisionedProduct in AWS" + errDescribe = "failed to describe ProvisionedProduct" + errDelete = "failed to delete ProvisionedProduct" +) + +type connector struct { + kube client.Client + opts []option +} + +func (c *connector) Connect(ctx context.Context, mg cpresource.Managed) (managed.ExternalClient, error) { + cr, ok := mg.(*svcapitypes.ProvisionedProduct) + if !ok { + return nil, errors.New(errUnexpectedObject) + } + sess, err := awsclient.GetConfigV1(ctx, c.kube, mg, cr.Spec.ForProvider.Region) + if err != nil { + return nil, errors.Wrap(err, errCreateSession) + } + return newExternal(c.kube, svcapi.New(sess), c.opts), nil +} + +func (e *external) Observe(ctx context.Context, mg cpresource.Managed) (managed.ExternalObservation, error) { + cr, ok := mg.(*svcapitypes.ProvisionedProduct) + if !ok { + return managed.ExternalObservation{}, errors.New(errUnexpectedObject) + } + if meta.GetExternalName(cr) == "" { + return managed.ExternalObservation{ + ResourceExists: false, + }, nil + } + input := GenerateDescribeProvisionedProductInput(cr) + if err := e.preObserve(ctx, cr, input); err != nil { + return managed.ExternalObservation{}, errors.Wrap(err, "pre-observe failed") + } + resp, err := e.client.DescribeProvisionedProductWithContext(ctx, input) + if err != nil { + return managed.ExternalObservation{ResourceExists: false}, awsclient.Wrap(cpresource.Ignore(IsNotFound, err), errDescribe) + } + currentSpec := cr.Spec.ForProvider.DeepCopy() + if err := e.lateInitialize(&cr.Spec.ForProvider, resp); err != nil { + return managed.ExternalObservation{}, errors.Wrap(err, "late-init failed") + } + GenerateProvisionedProduct(resp).Status.AtProvider.DeepCopyInto(&cr.Status.AtProvider) + + upToDate, diff, err := e.isUpToDate(ctx, cr, resp) + if err != nil { + return managed.ExternalObservation{}, errors.Wrap(err, "isUpToDate check failed") + } + return e.postObserve(ctx, cr, resp, managed.ExternalObservation{ + ResourceExists: true, + ResourceUpToDate: upToDate, + Diff: diff, + ResourceLateInitialized: !cmp.Equal(&cr.Spec.ForProvider, currentSpec), + }, nil) +} + +func (e *external) Create(ctx context.Context, mg cpresource.Managed) (managed.ExternalCreation, error) { + cr, ok := mg.(*svcapitypes.ProvisionedProduct) + if !ok { + return managed.ExternalCreation{}, errors.New(errUnexpectedObject) + } + cr.Status.SetConditions(xpv1.Creating()) + input := GenerateProvisionProductInput(cr) + if err := e.preCreate(ctx, cr, input); err != nil { + return managed.ExternalCreation{}, errors.Wrap(err, "pre-create failed") + } + resp, err := e.client.ProvisionProductWithContext(ctx, input) + if err != nil { + return managed.ExternalCreation{}, awsclient.Wrap(err, errCreate) + } + + if resp.RecordDetail.CreatedTime != nil { + cr.Status.AtProvider.CreatedTime = &metav1.Time{*resp.RecordDetail.CreatedTime} + } else { + cr.Status.AtProvider.CreatedTime = nil + } + if resp.RecordDetail.LaunchRoleArn != nil { + cr.Status.AtProvider.LaunchRoleARN = resp.RecordDetail.LaunchRoleArn + } else { + cr.Status.AtProvider.LaunchRoleARN = nil + } + if resp.RecordDetail.PathId != nil { + cr.Spec.ForProvider.PathID = resp.RecordDetail.PathId + } else { + cr.Spec.ForProvider.PathID = nil + } + if resp.RecordDetail.ProductId != nil { + cr.Spec.ForProvider.ProductID = resp.RecordDetail.ProductId + } else { + cr.Spec.ForProvider.ProductID = nil + } + if resp.RecordDetail.ProvisionedProductId != nil { + cr.Status.AtProvider.ProvisionedProductID = resp.RecordDetail.ProvisionedProductId + } else { + cr.Status.AtProvider.ProvisionedProductID = nil + } + if resp.RecordDetail.ProvisionedProductName != nil { + cr.Spec.ForProvider.Name = resp.RecordDetail.ProvisionedProductName + } else { + cr.Spec.ForProvider.Name = nil + } + if resp.RecordDetail.ProvisionedProductType != nil { + cr.Status.AtProvider.ProvisionedProductType = resp.RecordDetail.ProvisionedProductType + } else { + cr.Status.AtProvider.ProvisionedProductType = nil + } + if resp.RecordDetail.ProvisioningArtifactId != nil { + cr.Spec.ForProvider.ProvisioningArtifactID = resp.RecordDetail.ProvisioningArtifactId + } else { + cr.Spec.ForProvider.ProvisioningArtifactID = nil + } + if resp.RecordDetail.RecordErrors != nil { + f8 := []*svcapitypes.RecordError{} + for _, f8iter := range resp.RecordDetail.RecordErrors { + f8elem := &svcapitypes.RecordError{} + if f8iter.Code != nil { + f8elem.Code = f8iter.Code + } + if f8iter.Description != nil { + f8elem.Description = f8iter.Description + } + f8 = append(f8, f8elem) + } + cr.Status.AtProvider.RecordErrors = f8 + } else { + cr.Status.AtProvider.RecordErrors = nil + } + if resp.RecordDetail.RecordId != nil { + cr.Status.AtProvider.RecordID = resp.RecordDetail.RecordId + } else { + cr.Status.AtProvider.RecordID = nil + } + if resp.RecordDetail.RecordTags != nil { + f10 := []*svcapitypes.RecordTag{} + for _, f10iter := range resp.RecordDetail.RecordTags { + f10elem := &svcapitypes.RecordTag{} + if f10iter.Key != nil { + f10elem.Key = f10iter.Key + } + if f10iter.Value != nil { + f10elem.Value = f10iter.Value + } + f10 = append(f10, f10elem) + } + cr.Status.AtProvider.RecordTags = f10 + } else { + cr.Status.AtProvider.RecordTags = nil + } + if resp.RecordDetail.RecordType != nil { + cr.Status.AtProvider.RecordType = resp.RecordDetail.RecordType + } else { + cr.Status.AtProvider.RecordType = nil + } + if resp.RecordDetail.Status != nil { + cr.Status.AtProvider.Status = resp.RecordDetail.Status + } else { + cr.Status.AtProvider.Status = nil + } + if resp.RecordDetail.UpdatedTime != nil { + cr.Status.AtProvider.UpdatedTime = &metav1.Time{*resp.RecordDetail.UpdatedTime} + } else { + cr.Status.AtProvider.UpdatedTime = nil + } + + return e.postCreate(ctx, cr, resp, managed.ExternalCreation{}, err) +} + +func (e *external) Update(ctx context.Context, mg cpresource.Managed) (managed.ExternalUpdate, error) { + cr, ok := mg.(*svcapitypes.ProvisionedProduct) + if !ok { + return managed.ExternalUpdate{}, errors.New(errUnexpectedObject) + } + input := GenerateUpdateProvisionedProductInput(cr) + if err := e.preUpdate(ctx, cr, input); err != nil { + return managed.ExternalUpdate{}, errors.Wrap(err, "pre-update failed") + } + resp, err := e.client.UpdateProvisionedProductWithContext(ctx, input) + return e.postUpdate(ctx, cr, resp, managed.ExternalUpdate{}, awsclient.Wrap(err, errUpdate)) +} + +func (e *external) Delete(ctx context.Context, mg cpresource.Managed) error { + cr, ok := mg.(*svcapitypes.ProvisionedProduct) + if !ok { + return errors.New(errUnexpectedObject) + } + cr.Status.SetConditions(xpv1.Deleting()) + input := GenerateTerminateProvisionedProductInput(cr) + ignore, err := e.preDelete(ctx, cr, input) + if err != nil { + return errors.Wrap(err, "pre-delete failed") + } + if ignore { + return nil + } + resp, err := e.client.TerminateProvisionedProductWithContext(ctx, input) + return e.postDelete(ctx, cr, resp, awsclient.Wrap(cpresource.Ignore(IsNotFound, err), errDelete)) +} + +type option func(*external) + +func newExternal(kube client.Client, client svcsdkapi.ServiceCatalogAPI, opts []option) *external { + e := &external{ + kube: kube, + client: client, + preObserve: nopPreObserve, + postObserve: nopPostObserve, + lateInitialize: nopLateInitialize, + isUpToDate: alwaysUpToDate, + preCreate: nopPreCreate, + postCreate: nopPostCreate, + preDelete: nopPreDelete, + postDelete: nopPostDelete, + preUpdate: nopPreUpdate, + postUpdate: nopPostUpdate, + } + for _, f := range opts { + f(e) + } + return e +} + +type external struct { + kube client.Client + client svcsdkapi.ServiceCatalogAPI + preObserve func(context.Context, *svcapitypes.ProvisionedProduct, *svcsdk.DescribeProvisionedProductInput) error + postObserve func(context.Context, *svcapitypes.ProvisionedProduct, *svcsdk.DescribeProvisionedProductOutput, managed.ExternalObservation, error) (managed.ExternalObservation, error) + lateInitialize func(*svcapitypes.ProvisionedProductParameters, *svcsdk.DescribeProvisionedProductOutput) error + isUpToDate func(context.Context, *svcapitypes.ProvisionedProduct, *svcsdk.DescribeProvisionedProductOutput) (bool, string, error) + preCreate func(context.Context, *svcapitypes.ProvisionedProduct, *svcsdk.ProvisionProductInput) error + postCreate func(context.Context, *svcapitypes.ProvisionedProduct, *svcsdk.ProvisionProductOutput, managed.ExternalCreation, error) (managed.ExternalCreation, error) + preDelete func(context.Context, *svcapitypes.ProvisionedProduct, *svcsdk.TerminateProvisionedProductInput) (bool, error) + postDelete func(context.Context, *svcapitypes.ProvisionedProduct, *svcsdk.TerminateProvisionedProductOutput, error) error + preUpdate func(context.Context, *svcapitypes.ProvisionedProduct, *svcsdk.UpdateProvisionedProductInput) error + postUpdate func(context.Context, *svcapitypes.ProvisionedProduct, *svcsdk.UpdateProvisionedProductOutput, managed.ExternalUpdate, error) (managed.ExternalUpdate, error) +} + +func nopPreObserve(context.Context, *svcapitypes.ProvisionedProduct, *svcsdk.DescribeProvisionedProductInput) error { + return nil +} + +func nopPostObserve(_ context.Context, _ *svcapitypes.ProvisionedProduct, _ *svcsdk.DescribeProvisionedProductOutput, obs managed.ExternalObservation, err error) (managed.ExternalObservation, error) { + return obs, err +} +func nopLateInitialize(*svcapitypes.ProvisionedProductParameters, *svcsdk.DescribeProvisionedProductOutput) error { + return nil +} +func alwaysUpToDate(context.Context, *svcapitypes.ProvisionedProduct, *svcsdk.DescribeProvisionedProductOutput) (bool, string, error) { + return true, "", nil +} + +func nopPreCreate(context.Context, *svcapitypes.ProvisionedProduct, *svcsdk.ProvisionProductInput) error { + return nil +} +func nopPostCreate(_ context.Context, _ *svcapitypes.ProvisionedProduct, _ *svcsdk.ProvisionProductOutput, cre managed.ExternalCreation, err error) (managed.ExternalCreation, error) { + return cre, err +} +func nopPreDelete(context.Context, *svcapitypes.ProvisionedProduct, *svcsdk.TerminateProvisionedProductInput) (bool, error) { + return false, nil +} +func nopPostDelete(_ context.Context, _ *svcapitypes.ProvisionedProduct, _ *svcsdk.TerminateProvisionedProductOutput, err error) error { + return err +} +func nopPreUpdate(context.Context, *svcapitypes.ProvisionedProduct, *svcsdk.UpdateProvisionedProductInput) error { + return nil +} +func nopPostUpdate(_ context.Context, _ *svcapitypes.ProvisionedProduct, _ *svcsdk.UpdateProvisionedProductOutput, upd managed.ExternalUpdate, err error) (managed.ExternalUpdate, error) { + return upd, err +} diff --git a/pkg/controller/servicecatalog/provisionedproduct/zz_conversions.go b/pkg/controller/servicecatalog/provisionedproduct/zz_conversions.go new file mode 100644 index 0000000000..a223f8e864 --- /dev/null +++ b/pkg/controller/servicecatalog/provisionedproduct/zz_conversions.go @@ -0,0 +1,262 @@ +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by ack-generate. DO NOT EDIT. + +package provisionedproduct + +import ( + "github.com/aws/aws-sdk-go/aws/awserr" + svcsdk "github.com/aws/aws-sdk-go/service/servicecatalog" + + svcapitypes "github.com/crossplane-contrib/provider-aws/apis/servicecatalog/v1alpha1" +) + +// NOTE(muvaf): We return pointers in case the function needs to start with an +// empty object, hence need to return a new pointer. + +// GenerateDescribeProvisionedProductInput returns input for read +// operation. +func GenerateDescribeProvisionedProductInput(cr *svcapitypes.ProvisionedProduct) *svcsdk.DescribeProvisionedProductInput { + res := &svcsdk.DescribeProvisionedProductInput{} + + if cr.Spec.ForProvider.AcceptLanguage != nil { + res.SetAcceptLanguage(*cr.Spec.ForProvider.AcceptLanguage) + } + if cr.Spec.ForProvider.Name != nil { + res.SetName(*cr.Spec.ForProvider.Name) + } + + return res +} + +// GenerateProvisionedProduct returns the current state in the form of *svcapitypes.ProvisionedProduct. +func GenerateProvisionedProduct(resp *svcsdk.DescribeProvisionedProductOutput) *svcapitypes.ProvisionedProduct { + cr := &svcapitypes.ProvisionedProduct{} + + return cr +} + +// GenerateProvisionProductInput returns a create input. +func GenerateProvisionProductInput(cr *svcapitypes.ProvisionedProduct) *svcsdk.ProvisionProductInput { + res := &svcsdk.ProvisionProductInput{} + + if cr.Spec.ForProvider.AcceptLanguage != nil { + res.SetAcceptLanguage(*cr.Spec.ForProvider.AcceptLanguage) + } + if cr.Spec.ForProvider.NotificationARNs != nil { + f1 := []*string{} + for _, f1iter := range cr.Spec.ForProvider.NotificationARNs { + var f1elem string + f1elem = *f1iter + f1 = append(f1, &f1elem) + } + res.SetNotificationArns(f1) + } + if cr.Spec.ForProvider.PathID != nil { + res.SetPathId(*cr.Spec.ForProvider.PathID) + } + if cr.Spec.ForProvider.PathName != nil { + res.SetPathName(*cr.Spec.ForProvider.PathName) + } + if cr.Spec.ForProvider.ProductID != nil { + res.SetProductId(*cr.Spec.ForProvider.ProductID) + } + if cr.Spec.ForProvider.ProductName != nil { + res.SetProductName(*cr.Spec.ForProvider.ProductName) + } + if cr.Spec.ForProvider.Name != nil { + res.SetProvisionedProductName(*cr.Spec.ForProvider.Name) + } + if cr.Spec.ForProvider.ProvisioningArtifactID != nil { + res.SetProvisioningArtifactId(*cr.Spec.ForProvider.ProvisioningArtifactID) + } + if cr.Spec.ForProvider.ProvisioningArtifactName != nil { + res.SetProvisioningArtifactName(*cr.Spec.ForProvider.ProvisioningArtifactName) + } + if cr.Spec.ForProvider.ProvisioningParameters != nil { + f9 := []*svcsdk.ProvisioningParameter{} + for _, f9iter := range cr.Spec.ForProvider.ProvisioningParameters { + f9elem := &svcsdk.ProvisioningParameter{} + if f9iter.Key != nil { + f9elem.SetKey(*f9iter.Key) + } + if f9iter.Value != nil { + f9elem.SetValue(*f9iter.Value) + } + f9 = append(f9, f9elem) + } + res.SetProvisioningParameters(f9) + } + if cr.Spec.ForProvider.ProvisioningPreferences != nil { + f10 := &svcsdk.ProvisioningPreferences{} + if cr.Spec.ForProvider.ProvisioningPreferences.StackSetAccounts != nil { + f10f0 := []*string{} + for _, f10f0iter := range cr.Spec.ForProvider.ProvisioningPreferences.StackSetAccounts { + var f10f0elem string + f10f0elem = *f10f0iter + f10f0 = append(f10f0, &f10f0elem) + } + f10.SetStackSetAccounts(f10f0) + } + if cr.Spec.ForProvider.ProvisioningPreferences.StackSetFailureToleranceCount != nil { + f10.SetStackSetFailureToleranceCount(*cr.Spec.ForProvider.ProvisioningPreferences.StackSetFailureToleranceCount) + } + if cr.Spec.ForProvider.ProvisioningPreferences.StackSetFailureTolerancePercentage != nil { + f10.SetStackSetFailureTolerancePercentage(*cr.Spec.ForProvider.ProvisioningPreferences.StackSetFailureTolerancePercentage) + } + if cr.Spec.ForProvider.ProvisioningPreferences.StackSetMaxConcurrencyCount != nil { + f10.SetStackSetMaxConcurrencyCount(*cr.Spec.ForProvider.ProvisioningPreferences.StackSetMaxConcurrencyCount) + } + if cr.Spec.ForProvider.ProvisioningPreferences.StackSetMaxConcurrencyPercentage != nil { + f10.SetStackSetMaxConcurrencyPercentage(*cr.Spec.ForProvider.ProvisioningPreferences.StackSetMaxConcurrencyPercentage) + } + if cr.Spec.ForProvider.ProvisioningPreferences.StackSetRegions != nil { + f10f5 := []*string{} + for _, f10f5iter := range cr.Spec.ForProvider.ProvisioningPreferences.StackSetRegions { + var f10f5elem string + f10f5elem = *f10f5iter + f10f5 = append(f10f5, &f10f5elem) + } + f10.SetStackSetRegions(f10f5) + } + res.SetProvisioningPreferences(f10) + } + if cr.Spec.ForProvider.Tags != nil { + f11 := []*svcsdk.Tag{} + for _, f11iter := range cr.Spec.ForProvider.Tags { + f11elem := &svcsdk.Tag{} + if f11iter.Key != nil { + f11elem.SetKey(*f11iter.Key) + } + if f11iter.Value != nil { + f11elem.SetValue(*f11iter.Value) + } + f11 = append(f11, f11elem) + } + res.SetTags(f11) + } + + return res +} + +// GenerateUpdateProvisionedProductInput returns an update input. +func GenerateUpdateProvisionedProductInput(cr *svcapitypes.ProvisionedProduct) *svcsdk.UpdateProvisionedProductInput { + res := &svcsdk.UpdateProvisionedProductInput{} + + if cr.Spec.ForProvider.AcceptLanguage != nil { + res.SetAcceptLanguage(*cr.Spec.ForProvider.AcceptLanguage) + } + if cr.Spec.ForProvider.PathID != nil { + res.SetPathId(*cr.Spec.ForProvider.PathID) + } + if cr.Spec.ForProvider.PathName != nil { + res.SetPathName(*cr.Spec.ForProvider.PathName) + } + if cr.Spec.ForProvider.ProductID != nil { + res.SetProductId(*cr.Spec.ForProvider.ProductID) + } + if cr.Spec.ForProvider.ProductName != nil { + res.SetProductName(*cr.Spec.ForProvider.ProductName) + } + if cr.Spec.ForProvider.ProvisioningArtifactID != nil { + res.SetProvisioningArtifactId(*cr.Spec.ForProvider.ProvisioningArtifactID) + } + if cr.Spec.ForProvider.ProvisioningArtifactName != nil { + res.SetProvisioningArtifactName(*cr.Spec.ForProvider.ProvisioningArtifactName) + } + if cr.Spec.ForProvider.ProvisioningParameters != nil { + f8 := []*svcsdk.UpdateProvisioningParameter{} + for _, f8iter := range cr.Spec.ForProvider.ProvisioningParameters { + f8elem := &svcsdk.UpdateProvisioningParameter{} + if f8iter.Key != nil { + f8elem.SetKey(*f8iter.Key) + } + if f8iter.Value != nil { + f8elem.SetValue(*f8iter.Value) + } + f8 = append(f8, f8elem) + } + res.SetProvisioningParameters(f8) + } + if cr.Spec.ForProvider.ProvisioningPreferences != nil { + f9 := &svcsdk.UpdateProvisioningPreferences{} + if cr.Spec.ForProvider.ProvisioningPreferences.StackSetAccounts != nil { + f9f0 := []*string{} + for _, f9f0iter := range cr.Spec.ForProvider.ProvisioningPreferences.StackSetAccounts { + var f9f0elem string + f9f0elem = *f9f0iter + f9f0 = append(f9f0, &f9f0elem) + } + f9.SetStackSetAccounts(f9f0) + } + if cr.Spec.ForProvider.ProvisioningPreferences.StackSetFailureToleranceCount != nil { + f9.SetStackSetFailureToleranceCount(*cr.Spec.ForProvider.ProvisioningPreferences.StackSetFailureToleranceCount) + } + if cr.Spec.ForProvider.ProvisioningPreferences.StackSetFailureTolerancePercentage != nil { + f9.SetStackSetFailureTolerancePercentage(*cr.Spec.ForProvider.ProvisioningPreferences.StackSetFailureTolerancePercentage) + } + if cr.Spec.ForProvider.ProvisioningPreferences.StackSetMaxConcurrencyCount != nil { + f9.SetStackSetMaxConcurrencyCount(*cr.Spec.ForProvider.ProvisioningPreferences.StackSetMaxConcurrencyCount) + } + if cr.Spec.ForProvider.ProvisioningPreferences.StackSetMaxConcurrencyPercentage != nil { + f9.SetStackSetMaxConcurrencyPercentage(*cr.Spec.ForProvider.ProvisioningPreferences.StackSetMaxConcurrencyPercentage) + } + if cr.Spec.ForProvider.ProvisioningPreferences.StackSetRegions != nil { + f9f5 := []*string{} + for _, f9f5iter := range cr.Spec.ForProvider.ProvisioningPreferences.StackSetRegions { + var f9f5elem string + f9f5elem = *f9f5iter + f9f5 = append(f9f5, &f9f5elem) + } + f9.SetStackSetRegions(f9f5) + } + res.SetProvisioningPreferences(f9) + } + if cr.Spec.ForProvider.Tags != nil { + f10 := []*svcsdk.Tag{} + for _, f10iter := range cr.Spec.ForProvider.Tags { + f10elem := &svcsdk.Tag{} + if f10iter.Key != nil { + f10elem.SetKey(*f10iter.Key) + } + if f10iter.Value != nil { + f10elem.SetValue(*f10iter.Value) + } + f10 = append(f10, f10elem) + } + res.SetTags(f10) + } + + return res +} + +// GenerateTerminateProvisionedProductInput returns a deletion input. +func GenerateTerminateProvisionedProductInput(cr *svcapitypes.ProvisionedProduct) *svcsdk.TerminateProvisionedProductInput { + res := &svcsdk.TerminateProvisionedProductInput{} + + if cr.Spec.ForProvider.AcceptLanguage != nil { + res.SetAcceptLanguage(*cr.Spec.ForProvider.AcceptLanguage) + } + + return res +} + +// IsNotFound returns whether the given error is of type NotFound or not. +func IsNotFound(err error) bool { + awsErr, ok := err.(awserr.Error) + return ok && awsErr.Code() == "ResourceNotFoundException" +} diff --git a/pkg/controller/servicecatalog/setup.go b/pkg/controller/servicecatalog/setup.go new file mode 100644 index 0000000000..ee3aa38c23 --- /dev/null +++ b/pkg/controller/servicecatalog/setup.go @@ -0,0 +1,34 @@ +/* +Copyright 2023 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package servicecatalog + +import ( + ctrl "sigs.k8s.io/controller-runtime" + + "github.com/crossplane/crossplane-runtime/pkg/controller" + + "github.com/crossplane-contrib/provider-aws/pkg/controller/servicecatalog/provisionedproduct" + "github.com/crossplane-contrib/provider-aws/pkg/utils/setup" +) + +// Setup servicecatalog controllers. +func Setup(mgr ctrl.Manager, o controller.Options) error { + return setup.SetupControllers( + mgr, o, + provisionedproduct.SetupProvisionedProduct, + ) +}