diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml index 12414514..4db29cb3 100755 --- a/apis/v1alpha1/ack-generate-metadata.yaml +++ b/apis/v1alpha1/ack-generate-metadata.yaml @@ -1,13 +1,13 @@ ack_generate_info: - build_date: "2025-10-16T19:00:35Z" + build_date: "2025-11-06T03:23:30Z" build_hash: eaabefb6bd7b2be8a1baf4478f22b3310e6921c8 go_version: go1.25.2 version: v0.52.0-6-geaabefb -api_directory_checksum: cdae97bbaa2ee28d0f130fa6ce3a7dfbf6393d1b +api_directory_checksum: c4e5b363f35aec7f0c52278a9b963e41bb4a5fd5 api_version: v1alpha1 aws_sdk_go_version: v1.39.2 generator_config_info: - file_checksum: f7a59503a2ce9b492347cbe3d62af9872cb061cd + file_checksum: fb892de952cd5ed06626c135b268287a8314221f original_file_name: generator.yaml last_modification: reason: API generation diff --git a/apis/v1alpha1/enums.go b/apis/v1alpha1/enums.go index 64017c9f..24a4ca0e 100644 --- a/apis/v1alpha1/enums.go +++ b/apis/v1alpha1/enums.go @@ -2798,19 +2798,19 @@ const ( ProjectSortOrder_Descending ProjectSortOrder = "Descending" ) -type ProjectStatus string - -const ( - ProjectStatus_CreateCompleted ProjectStatus = "CreateCompleted" - ProjectStatus_CreateFailed ProjectStatus = "CreateFailed" - ProjectStatus_CreateInProgress ProjectStatus = "CreateInProgress" - ProjectStatus_DeleteCompleted ProjectStatus = "DeleteCompleted" - ProjectStatus_DeleteFailed ProjectStatus = "DeleteFailed" - ProjectStatus_DeleteInProgress ProjectStatus = "DeleteInProgress" - ProjectStatus_Pending ProjectStatus = "Pending" - ProjectStatus_UpdateCompleted ProjectStatus = "UpdateCompleted" - ProjectStatus_UpdateFailed ProjectStatus = "UpdateFailed" - ProjectStatus_UpdateInProgress ProjectStatus = "UpdateInProgress" +type ProjectStatus_SDK string + +const ( + ProjectStatus_SDK_CreateCompleted ProjectStatus_SDK = "CreateCompleted" + ProjectStatus_SDK_CreateFailed ProjectStatus_SDK = "CreateFailed" + ProjectStatus_SDK_CreateInProgress ProjectStatus_SDK = "CreateInProgress" + ProjectStatus_SDK_DeleteCompleted ProjectStatus_SDK = "DeleteCompleted" + ProjectStatus_SDK_DeleteFailed ProjectStatus_SDK = "DeleteFailed" + ProjectStatus_SDK_DeleteInProgress ProjectStatus_SDK = "DeleteInProgress" + ProjectStatus_SDK_Pending ProjectStatus_SDK = "Pending" + ProjectStatus_SDK_UpdateCompleted ProjectStatus_SDK = "UpdateCompleted" + ProjectStatus_SDK_UpdateFailed ProjectStatus_SDK = "UpdateFailed" + ProjectStatus_SDK_UpdateInProgress ProjectStatus_SDK = "UpdateInProgress" ) type RStudioServerProAccessStatus string diff --git a/apis/v1alpha1/generator.yaml b/apis/v1alpha1/generator.yaml index 583f021c..1809a8cd 100644 --- a/apis/v1alpha1/generator.yaml +++ b/apis/v1alpha1/generator.yaml @@ -931,6 +931,47 @@ resources: Tags: compare: is_ignored: true + Project: + exceptions: + errors: + 404: + code: ResourceNotFound + terminal_codes: + - InvalidParameterCombination + - InvalidParameterValue + - MissingParameter + renames: + operations: + UpdateProject: + input_fields: + ServiceCatalogProvisioningUpdateDetails: ServiceCatalogProvisioningDetails + TemplateProvidersToUpdate: TemplateProviders + fields: + ProjectStatus: + is_read_only: true + print: + name: STATUS + from: + operation: DescribeProject + path: ProjectStatus + CreationTime: + is_read_only: true + from: + operation: DescribeProject + path: CreationTime + ServiceCatalogProvisionedProductDetails: + is_read_only: true + from: + operation: DescribeProject + path: ServiceCatalogProvisionedProductDetails + TemplateProviderDetails: + is_read_only: true + from: + operation: DescribeProject + path: TemplateProviderDetails + Tags: + compare: + is_ignored: true Space: exceptions: errors: @@ -1236,7 +1277,7 @@ ignore: - PresignedNotebookInstanceUrl - PresignedMlflowTrackingServerUrl # - ProcessingJob - - Project + # - Project # - Space - StudioLifecycleConfig # - TrainingJob diff --git a/apis/v1alpha1/project.go b/apis/v1alpha1/project.go new file mode 100644 index 00000000..25804140 --- /dev/null +++ b/apis/v1alpha1/project.go @@ -0,0 +1,105 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file 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 ( + ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ProjectSpec defines the desired state of Project. +// +// The properties of a project as returned by the Search API. +type ProjectSpec struct { + + // A description for the project. + // + // Regex Pattern: `^[\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*$` + ProjectDescription *string `json:"projectDescription,omitempty"` + // The name of the project. + // + // Regex Pattern: `^[a-zA-Z0-9](-*[a-zA-Z0-9]){0,31}$` + // +kubebuilder:validation:Required + ProjectName *string `json:"projectName"` + // The product ID and provisioning artifact ID to provision a service catalog. + // The provisioning artifact ID will default to the latest provisioning artifact + // ID of the product, if you don't provide the provisioning artifact ID. For + // more information, see What is Amazon Web Services Service Catalog (https://docs.aws.amazon.com/servicecatalog/latest/adminguide/introduction.html). + ServiceCatalogProvisioningDetails *ServiceCatalogProvisioningDetails `json:"serviceCatalogProvisioningDetails,omitempty"` + // An array of key-value pairs that you want to use to organize and track your + // Amazon Web Services resource costs. For more information, see Tagging Amazon + // Web Services resources (https://docs.aws.amazon.com/general/latest/gr/aws_tagging.html) + // in the Amazon Web Services General Reference Guide. + Tags []*Tag `json:"tags,omitempty"` + // An array of template provider configurations for creating infrastructure + // resources for the project. + TemplateProviders []*CreateTemplateProvider `json:"templateProviders,omitempty"` +} + +// ProjectStatus defines the observed state of Project +type ProjectStatus struct { + // All CRs managed by ACK have a common `Status.ACKResourceMetadata` member + // that is used to contain resource sync state, account ownership, + // constructed ARN for the resource + // +kubebuilder:validation:Optional + ACKResourceMetadata *ackv1alpha1.ResourceMetadata `json:"ackResourceMetadata"` + // All CRs managed by ACK have a common `Status.Conditions` member that + // contains a collection of `ackv1alpha1.Condition` objects that describe + // the various terminal states of the CR and its backend AWS service API + // resource + // +kubebuilder:validation:Optional + Conditions []*ackv1alpha1.Condition `json:"conditions"` + // The time when the project was created. + // +kubebuilder:validation:Optional + CreationTime *metav1.Time `json:"creationTime,omitempty"` + // The ID of the new project. + // + // Regex Pattern: `^[a-zA-Z0-9](-*[a-zA-Z0-9])*$` + // +kubebuilder:validation:Optional + ProjectID *string `json:"projectID,omitempty"` + // The status of the project. + // +kubebuilder:validation:Optional + ProjectStatus *string `json:"projectStatus,omitempty"` + // Information about a provisioned service catalog product. + // +kubebuilder:validation:Optional + ServiceCatalogProvisionedProductDetails *ServiceCatalogProvisionedProductDetails `json:"serviceCatalogProvisionedProductDetails,omitempty"` + // An array of template providers associated with the project. + // +kubebuilder:validation:Optional + TemplateProviderDetails []*TemplateProviderDetail `json:"templateProviderDetails,omitempty"` +} + +// Project is the Schema for the Projects API +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="STATUS",type=string,priority=0,JSONPath=`.status.projectStatus` +type Project struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec ProjectSpec `json:"spec,omitempty"` + Status ProjectStatus `json:"status,omitempty"` +} + +// ProjectList contains a list of Project +// +kubebuilder:object:root=true +type ProjectList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Project `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Project{}, &ProjectList{}) +} diff --git a/apis/v1alpha1/types.go b/apis/v1alpha1/types.go index 72e3fde5..93e7deee 100644 --- a/apis/v1alpha1/types.go +++ b/apis/v1alpha1/types.go @@ -532,13 +532,54 @@ type CategoricalParameterRangeSpecification struct { // The CloudFormation template provider configuration for creating infrastructure // resources. type CfnCreateTemplateProvider struct { - RoleARN *string `json:"roleARN,omitempty"` + Parameters []*CfnStackCreateParameter `json:"parameters,omitempty"` + RoleARN *string `json:"roleARN,omitempty"` + TemplateName *string `json:"templateName,omitempty"` + TemplateURL *string `json:"templateURL,omitempty"` +} + +// A key-value pair that represents a parameter for the CloudFormation stack. +type CfnStackCreateParameter struct { + Key *string `json:"key,omitempty"` + Value *string `json:"value,omitempty"` +} + +// Details about the CloudFormation stack. +type CfnStackDetail struct { + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + StatusMessage *string `json:"statusMessage,omitempty"` +} + +// A key-value pair representing a parameter used in the CloudFormation stack. +type CfnStackParameter struct { + Key *string `json:"key,omitempty"` + Value *string `json:"value,omitempty"` +} + +// A key-value pair representing a parameter used in the CloudFormation stack. +type CfnStackUpdateParameter struct { + Key *string `json:"key,omitempty"` + Value *string `json:"value,omitempty"` } // Details about a CloudFormation template provider configuration and associated // provisioning information. type CfnTemplateProviderDetail struct { - RoleARN *string `json:"roleARN,omitempty"` + Parameters []*CfnStackParameter `json:"parameters,omitempty"` + RoleARN *string `json:"roleARN,omitempty"` + // Details about the CloudFormation stack. + StackDetail *CfnStackDetail `json:"stackDetail,omitempty"` + TemplateName *string `json:"templateName,omitempty"` + TemplateURL *string `json:"templateURL,omitempty"` +} + +// Contains configuration details for updating an existing CloudFormation template +// provider in the project. +type CfnUpdateTemplateProvider struct { + Parameters []*CfnStackUpdateParameter `json:"parameters,omitempty"` + TemplateName *string `json:"templateName,omitempty"` + TemplateURL *string `json:"templateURL,omitempty"` } // A channel is a named input source that training algorithms can consume. @@ -873,6 +914,14 @@ type ContinuousParameterRangeSpecification struct { MinValue *string `json:"minValue,omitempty"` } +// Contains configuration details for a template provider. Only one type of +// template provider can be specified. +type CreateTemplateProvider struct { + // The CloudFormation template provider configuration for creating infrastructure + // resources. + CfnTemplateProvider *CfnCreateTemplateProvider `json:"cfnTemplateProvider,omitempty"` +} + // A file system, created by you, that you assign to a user profile or space // for an Amazon SageMaker AI Domain. Permitted users can access this file system // in Amazon SageMaker AI Studio. @@ -4502,22 +4551,44 @@ type ProfilerRuleEvaluationStatus struct { StatusDetails *string `json:"statusDetails,omitempty"` } +// Information about a project. +type ProjectSummary struct { + CreationTime *metav1.Time `json:"creationTime,omitempty"` + ProjectARN *string `json:"projectARN,omitempty"` + ProjectDescription *string `json:"projectDescription,omitempty"` + ProjectID *string `json:"projectID,omitempty"` + ProjectName *string `json:"projectName,omitempty"` + ProjectStatus *string `json:"projectStatus,omitempty"` +} + // The properties of a project as returned by the Search API. -type Project struct { +type Project_SDK struct { // Information about the user who created or modified a SageMaker resource. CreatedBy *UserContext `json:"createdBy,omitempty"` CreationTime *metav1.Time `json:"creationTime,omitempty"` // Information about the user who created or modified a SageMaker resource. LastModifiedBy *UserContext `json:"lastModifiedBy,omitempty"` LastModifiedTime *metav1.Time `json:"lastModifiedTime,omitempty"` + ProjectARN *string `json:"projectARN,omitempty"` ProjectDescription *string `json:"projectDescription,omitempty"` - Tags []*Tag `json:"tags,omitempty"` -} - -// Information about a project. -type ProjectSummary struct { - CreationTime *metav1.Time `json:"creationTime,omitempty"` - ProjectDescription *string `json:"projectDescription,omitempty"` + ProjectID *string `json:"projectID,omitempty"` + ProjectName *string `json:"projectName,omitempty"` + ProjectStatus *string `json:"projectStatus,omitempty"` + // Details of a provisioned service catalog product. For information about service + // catalog, see What is Amazon Web Services Service Catalog (https://docs.aws.amazon.com/servicecatalog/latest/adminguide/introduction.html). + ServiceCatalogProvisionedProductDetails *ServiceCatalogProvisionedProductDetails `json:"serviceCatalogProvisionedProductDetails,omitempty"` + // Details that you specify to provision a service catalog product. For information + // about service catalog, see What is Amazon Web Services Service Catalog (https://docs.aws.amazon.com/servicecatalog/latest/adminguide/introduction.html). + ServiceCatalogProvisioningDetails *ServiceCatalogProvisioningDetails `json:"serviceCatalogProvisioningDetails,omitempty"` + Tags []*Tag `json:"tags,omitempty"` + TemplateProviderDetails []*TemplateProviderDetail `json:"templateProviderDetails,omitempty"` +} + +// A key value pair used when you provision a project as a service catalog product. +// For information, see What is Amazon Web Services Service Catalog (https://docs.aws.amazon.com/servicecatalog/latest/adminguide/introduction.html). +type ProvisioningParameter struct { + Key *string `json:"key,omitempty"` + Value *string `json:"value,omitempty"` } // Defines the amount of money paid to an Amazon Mechanical Turk worker for @@ -5142,6 +5213,29 @@ type SelectiveExecutionResult struct { SourcePipelineExecutionARN *string `json:"sourcePipelineExecutionARN,omitempty"` } +// Details of a provisioned service catalog product. For information about service +// catalog, see What is Amazon Web Services Service Catalog (https://docs.aws.amazon.com/servicecatalog/latest/adminguide/introduction.html). +type ServiceCatalogProvisionedProductDetails struct { + ProvisionedProductID *string `json:"provisionedProductID,omitempty"` + ProvisionedProductStatusMessage *string `json:"provisionedProductStatusMessage,omitempty"` +} + +// Details that you specify to provision a service catalog product. For information +// about service catalog, see What is Amazon Web Services Service Catalog (https://docs.aws.amazon.com/servicecatalog/latest/adminguide/introduction.html). +type ServiceCatalogProvisioningDetails struct { + PathID *string `json:"pathID,omitempty"` + ProductID *string `json:"productID,omitempty"` + ProvisioningArtifactID *string `json:"provisioningArtifactID,omitempty"` + ProvisioningParameters []*ProvisioningParameter `json:"provisioningParameters,omitempty"` +} + +// Details that you specify to provision a service catalog product. For information +// about service catalog, see What is Amazon Web Services Service Catalog (https://docs.aws.amazon.com/servicecatalog/latest/adminguide/introduction.html). +type ServiceCatalogProvisioningUpdateDetails struct { + ProvisioningArtifactID *string `json:"provisioningArtifactID,omitempty"` + ProvisioningParameters []*ProvisioningParameter `json:"provisioningParameters,omitempty"` +} + // Specifies options for sharing Amazon SageMaker AI Studio notebooks. These // settings are specified as part of DefaultUserSettings when the CreateDomain // API is called, and as part of UserSettings when the CreateUserProfile API @@ -5359,6 +5453,14 @@ type Tag struct { Value *string `json:"value,omitempty"` } +// Details about a template provider configuration and associated provisioning +// information. +type TemplateProviderDetail struct { + // Details about a CloudFormation template provider configuration and associated + // provisioning information. + CfnTemplateProviderDetail *CfnTemplateProviderDetail `json:"cfnTemplateProviderDetail,omitempty"` +} + // The TensorBoard app settings. type TensorBoardAppSettings struct { // Specifies the ARN's of a SageMaker AI image and SageMaker AI image version, @@ -5981,6 +6083,14 @@ type UnifiedStudioSettings struct { StudioWebPortalAccess *string `json:"studioWebPortalAccess,omitempty"` } +// Contains configuration details for updating an existing template provider +// in the project. +type UpdateTemplateProvider struct { + // Contains configuration details for updating an existing CloudFormation template + // provider in the project. + CfnTemplateProvider *CfnUpdateTemplateProvider `json:"cfnTemplateProvider,omitempty"` +} + // Information about the user who created or modified a SageMaker resource. type UserContext struct { DomainID *string `json:"domainID,omitempty"` diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index 175f3120..47cc797c 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -1732,11 +1732,32 @@ func (in *CategoricalParameterRangeSpecification) DeepCopy() *CategoricalParamet // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CfnCreateTemplateProvider) DeepCopyInto(out *CfnCreateTemplateProvider) { *out = *in + if in.Parameters != nil { + in, out := &in.Parameters, &out.Parameters + *out = make([]*CfnStackCreateParameter, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(CfnStackCreateParameter) + (*in).DeepCopyInto(*out) + } + } + } if in.RoleARN != nil { in, out := &in.RoleARN, &out.RoleARN *out = new(string) **out = **in } + if in.TemplateName != nil { + in, out := &in.TemplateName, &out.TemplateName + *out = new(string) + **out = **in + } + if in.TemplateURL != nil { + in, out := &in.TemplateURL, &out.TemplateURL + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CfnCreateTemplateProvider. @@ -1749,14 +1770,145 @@ func (in *CfnCreateTemplateProvider) DeepCopy() *CfnCreateTemplateProvider { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CfnStackCreateParameter) DeepCopyInto(out *CfnStackCreateParameter) { + *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 CfnStackCreateParameter. +func (in *CfnStackCreateParameter) DeepCopy() *CfnStackCreateParameter { + if in == nil { + return nil + } + out := new(CfnStackCreateParameter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CfnStackDetail) DeepCopyInto(out *CfnStackDetail) { + *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.StatusMessage != nil { + in, out := &in.StatusMessage, &out.StatusMessage + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CfnStackDetail. +func (in *CfnStackDetail) DeepCopy() *CfnStackDetail { + if in == nil { + return nil + } + out := new(CfnStackDetail) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CfnStackParameter) DeepCopyInto(out *CfnStackParameter) { + *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 CfnStackParameter. +func (in *CfnStackParameter) DeepCopy() *CfnStackParameter { + if in == nil { + return nil + } + out := new(CfnStackParameter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CfnStackUpdateParameter) DeepCopyInto(out *CfnStackUpdateParameter) { + *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 CfnStackUpdateParameter. +func (in *CfnStackUpdateParameter) DeepCopy() *CfnStackUpdateParameter { + if in == nil { + return nil + } + out := new(CfnStackUpdateParameter) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CfnTemplateProviderDetail) DeepCopyInto(out *CfnTemplateProviderDetail) { *out = *in + if in.Parameters != nil { + in, out := &in.Parameters, &out.Parameters + *out = make([]*CfnStackParameter, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(CfnStackParameter) + (*in).DeepCopyInto(*out) + } + } + } if in.RoleARN != nil { in, out := &in.RoleARN, &out.RoleARN *out = new(string) **out = **in } + if in.StackDetail != nil { + in, out := &in.StackDetail, &out.StackDetail + *out = new(CfnStackDetail) + (*in).DeepCopyInto(*out) + } + if in.TemplateName != nil { + in, out := &in.TemplateName, &out.TemplateName + *out = new(string) + **out = **in + } + if in.TemplateURL != nil { + in, out := &in.TemplateURL, &out.TemplateURL + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CfnTemplateProviderDetail. @@ -1769,6 +1921,42 @@ func (in *CfnTemplateProviderDetail) DeepCopy() *CfnTemplateProviderDetail { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CfnUpdateTemplateProvider) DeepCopyInto(out *CfnUpdateTemplateProvider) { + *out = *in + if in.Parameters != nil { + in, out := &in.Parameters, &out.Parameters + *out = make([]*CfnStackUpdateParameter, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(CfnStackUpdateParameter) + (*in).DeepCopyInto(*out) + } + } + } + if in.TemplateName != nil { + in, out := &in.TemplateName, &out.TemplateName + *out = new(string) + **out = **in + } + if in.TemplateURL != nil { + in, out := &in.TemplateURL, &out.TemplateURL + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CfnUpdateTemplateProvider. +func (in *CfnUpdateTemplateProvider) DeepCopy() *CfnUpdateTemplateProvider { + if in == nil { + return nil + } + out := new(CfnUpdateTemplateProvider) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Channel) DeepCopyInto(out *Channel) { *out = *in @@ -2739,6 +2927,26 @@ func (in *ContinuousParameterRangeSpecification) DeepCopy() *ContinuousParameter return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CreateTemplateProvider) DeepCopyInto(out *CreateTemplateProvider) { + *out = *in + if in.CfnTemplateProvider != nil { + in, out := &in.CfnTemplateProvider, &out.CfnTemplateProvider + *out = new(CfnCreateTemplateProvider) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CreateTemplateProvider. +func (in *CreateTemplateProvider) DeepCopy() *CreateTemplateProvider { + if in == nil { + return nil + } + out := new(CreateTemplateProvider) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CustomFileSystem) DeepCopyInto(out *CustomFileSystem) { *out = *in @@ -16993,6 +17201,222 @@ func (in *ProfilerRuleEvaluationStatus) DeepCopy() *ProfilerRuleEvaluationStatus // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Project) DeepCopyInto(out *Project) { + *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 Project. +func (in *Project) DeepCopy() *Project { + if in == nil { + return nil + } + out := new(Project) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Project) 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 *ProjectList) DeepCopyInto(out *ProjectList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Project, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectList. +func (in *ProjectList) DeepCopy() *ProjectList { + if in == nil { + return nil + } + out := new(ProjectList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProjectList) 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 *ProjectSpec) DeepCopyInto(out *ProjectSpec) { + *out = *in + if in.ProjectDescription != nil { + in, out := &in.ProjectDescription, &out.ProjectDescription + *out = new(string) + **out = **in + } + if in.ProjectName != nil { + in, out := &in.ProjectName, &out.ProjectName + *out = new(string) + **out = **in + } + if in.ServiceCatalogProvisioningDetails != nil { + in, out := &in.ServiceCatalogProvisioningDetails, &out.ServiceCatalogProvisioningDetails + *out = new(ServiceCatalogProvisioningDetails) + (*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) + } + } + } + if in.TemplateProviders != nil { + in, out := &in.TemplateProviders, &out.TemplateProviders + *out = make([]*CreateTemplateProvider, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(CreateTemplateProvider) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectSpec. +func (in *ProjectSpec) DeepCopy() *ProjectSpec { + if in == nil { + return nil + } + out := new(ProjectSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProjectStatus) DeepCopyInto(out *ProjectStatus) { + *out = *in + if in.ACKResourceMetadata != nil { + in, out := &in.ACKResourceMetadata, &out.ACKResourceMetadata + *out = new(corev1alpha1.ResourceMetadata) + (*in).DeepCopyInto(*out) + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]*corev1alpha1.Condition, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(corev1alpha1.Condition) + (*in).DeepCopyInto(*out) + } + } + } + if in.CreationTime != nil { + in, out := &in.CreationTime, &out.CreationTime + *out = (*in).DeepCopy() + } + if in.ProjectID != nil { + in, out := &in.ProjectID, &out.ProjectID + *out = new(string) + **out = **in + } + if in.ProjectStatus != nil { + in, out := &in.ProjectStatus, &out.ProjectStatus + *out = new(string) + **out = **in + } + if in.ServiceCatalogProvisionedProductDetails != nil { + in, out := &in.ServiceCatalogProvisionedProductDetails, &out.ServiceCatalogProvisionedProductDetails + *out = new(ServiceCatalogProvisionedProductDetails) + (*in).DeepCopyInto(*out) + } + if in.TemplateProviderDetails != nil { + in, out := &in.TemplateProviderDetails, &out.TemplateProviderDetails + *out = make([]*TemplateProviderDetail, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TemplateProviderDetail) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectStatus. +func (in *ProjectStatus) DeepCopy() *ProjectStatus { + if in == nil { + return nil + } + out := new(ProjectStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProjectSummary) DeepCopyInto(out *ProjectSummary) { + *out = *in + if in.CreationTime != nil { + in, out := &in.CreationTime, &out.CreationTime + *out = (*in).DeepCopy() + } + if in.ProjectARN != nil { + in, out := &in.ProjectARN, &out.ProjectARN + *out = new(string) + **out = **in + } + if in.ProjectDescription != nil { + in, out := &in.ProjectDescription, &out.ProjectDescription + *out = new(string) + **out = **in + } + if in.ProjectID != nil { + in, out := &in.ProjectID, &out.ProjectID + *out = new(string) + **out = **in + } + if in.ProjectName != nil { + in, out := &in.ProjectName, &out.ProjectName + *out = new(string) + **out = **in + } + if in.ProjectStatus != nil { + in, out := &in.ProjectStatus, &out.ProjectStatus + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectSummary. +func (in *ProjectSummary) DeepCopy() *ProjectSummary { + if in == nil { + return nil + } + out := new(ProjectSummary) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Project_SDK) DeepCopyInto(out *Project_SDK) { *out = *in if in.CreatedBy != nil { in, out := &in.CreatedBy, &out.CreatedBy @@ -17012,11 +17436,41 @@ func (in *Project) DeepCopyInto(out *Project) { in, out := &in.LastModifiedTime, &out.LastModifiedTime *out = (*in).DeepCopy() } + if in.ProjectARN != nil { + in, out := &in.ProjectARN, &out.ProjectARN + *out = new(string) + **out = **in + } if in.ProjectDescription != nil { in, out := &in.ProjectDescription, &out.ProjectDescription *out = new(string) **out = **in } + if in.ProjectID != nil { + in, out := &in.ProjectID, &out.ProjectID + *out = new(string) + **out = **in + } + if in.ProjectName != nil { + in, out := &in.ProjectName, &out.ProjectName + *out = new(string) + **out = **in + } + if in.ProjectStatus != nil { + in, out := &in.ProjectStatus, &out.ProjectStatus + *out = new(string) + **out = **in + } + if in.ServiceCatalogProvisionedProductDetails != nil { + in, out := &in.ServiceCatalogProvisionedProductDetails, &out.ServiceCatalogProvisionedProductDetails + *out = new(ServiceCatalogProvisionedProductDetails) + (*in).DeepCopyInto(*out) + } + if in.ServiceCatalogProvisioningDetails != nil { + in, out := &in.ServiceCatalogProvisioningDetails, &out.ServiceCatalogProvisioningDetails + *out = new(ServiceCatalogProvisioningDetails) + (*in).DeepCopyInto(*out) + } if in.Tags != nil { in, out := &in.Tags, &out.Tags *out = make([]*Tag, len(*in)) @@ -17028,38 +17482,50 @@ func (in *Project) DeepCopyInto(out *Project) { } } } + if in.TemplateProviderDetails != nil { + in, out := &in.TemplateProviderDetails, &out.TemplateProviderDetails + *out = make([]*TemplateProviderDetail, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TemplateProviderDetail) + (*in).DeepCopyInto(*out) + } + } + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Project. -func (in *Project) DeepCopy() *Project { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Project_SDK. +func (in *Project_SDK) DeepCopy() *Project_SDK { if in == nil { return nil } - out := new(Project) + out := new(Project_SDK) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProjectSummary) DeepCopyInto(out *ProjectSummary) { +func (in *ProvisioningParameter) DeepCopyInto(out *ProvisioningParameter) { *out = *in - if in.CreationTime != nil { - in, out := &in.CreationTime, &out.CreationTime - *out = (*in).DeepCopy() + if in.Key != nil { + in, out := &in.Key, &out.Key + *out = new(string) + **out = **in } - if in.ProjectDescription != nil { - in, out := &in.ProjectDescription, &out.ProjectDescription + 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 ProjectSummary. -func (in *ProjectSummary) DeepCopy() *ProjectSummary { +// 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(ProjectSummary) + out := new(ProvisioningParameter) in.DeepCopyInto(out) return out } @@ -18323,6 +18789,103 @@ func (in *SelectiveExecutionResult) DeepCopy() *SelectiveExecutionResult { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceCatalogProvisionedProductDetails) DeepCopyInto(out *ServiceCatalogProvisionedProductDetails) { + *out = *in + if in.ProvisionedProductID != nil { + in, out := &in.ProvisionedProductID, &out.ProvisionedProductID + *out = new(string) + **out = **in + } + if in.ProvisionedProductStatusMessage != nil { + in, out := &in.ProvisionedProductStatusMessage, &out.ProvisionedProductStatusMessage + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceCatalogProvisionedProductDetails. +func (in *ServiceCatalogProvisionedProductDetails) DeepCopy() *ServiceCatalogProvisionedProductDetails { + if in == nil { + return nil + } + out := new(ServiceCatalogProvisionedProductDetails) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceCatalogProvisioningDetails) DeepCopyInto(out *ServiceCatalogProvisioningDetails) { + *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.ProvisioningArtifactID != nil { + in, out := &in.ProvisioningArtifactID, &out.ProvisioningArtifactID + *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) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceCatalogProvisioningDetails. +func (in *ServiceCatalogProvisioningDetails) DeepCopy() *ServiceCatalogProvisioningDetails { + if in == nil { + return nil + } + out := new(ServiceCatalogProvisioningDetails) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceCatalogProvisioningUpdateDetails) DeepCopyInto(out *ServiceCatalogProvisioningUpdateDetails) { + *out = *in + if in.ProvisioningArtifactID != nil { + in, out := &in.ProvisioningArtifactID, &out.ProvisioningArtifactID + *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) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceCatalogProvisioningUpdateDetails. +func (in *ServiceCatalogProvisioningUpdateDetails) DeepCopy() *ServiceCatalogProvisioningUpdateDetails { + if in == nil { + return nil + } + out := new(ServiceCatalogProvisioningUpdateDetails) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SharingSettings) DeepCopyInto(out *SharingSettings) { *out = *in @@ -19067,6 +19630,26 @@ func (in *Tag) DeepCopy() *Tag { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TemplateProviderDetail) DeepCopyInto(out *TemplateProviderDetail) { + *out = *in + if in.CfnTemplateProviderDetail != nil { + in, out := &in.CfnTemplateProviderDetail, &out.CfnTemplateProviderDetail + *out = new(CfnTemplateProviderDetail) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TemplateProviderDetail. +func (in *TemplateProviderDetail) DeepCopy() *TemplateProviderDetail { + if in == nil { + return nil + } + out := new(TemplateProviderDetail) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TensorBoardAppSettings) DeepCopyInto(out *TensorBoardAppSettings) { *out = *in @@ -21263,6 +21846,26 @@ func (in *UnifiedStudioSettings) DeepCopy() *UnifiedStudioSettings { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UpdateTemplateProvider) DeepCopyInto(out *UpdateTemplateProvider) { + *out = *in + if in.CfnTemplateProvider != nil { + in, out := &in.CfnTemplateProvider, &out.CfnTemplateProvider + *out = new(CfnUpdateTemplateProvider) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpdateTemplateProvider. +func (in *UpdateTemplateProvider) DeepCopy() *UpdateTemplateProvider { + if in == nil { + return nil + } + out := new(UpdateTemplateProvider) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *UserContext) DeepCopyInto(out *UserContext) { *out = *in diff --git a/cmd/controller/main.go b/cmd/controller/main.go index 8d02def0..de6f88ed 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -60,6 +60,7 @@ import ( _ "github.com/aws-controllers-k8s/sagemaker-controller/pkg/resource/pipeline" _ "github.com/aws-controllers-k8s/sagemaker-controller/pkg/resource/pipeline_execution" _ "github.com/aws-controllers-k8s/sagemaker-controller/pkg/resource/processing_job" + _ "github.com/aws-controllers-k8s/sagemaker-controller/pkg/resource/project" _ "github.com/aws-controllers-k8s/sagemaker-controller/pkg/resource/space" _ "github.com/aws-controllers-k8s/sagemaker-controller/pkg/resource/training_job" _ "github.com/aws-controllers-k8s/sagemaker-controller/pkg/resource/transform_job" diff --git a/config/crd/bases/sagemaker.services.k8s.aws_projects.yaml b/config/crd/bases/sagemaker.services.k8s.aws_projects.yaml new file mode 100644 index 00000000..96d8e236 --- /dev/null +++ b/config/crd/bases/sagemaker.services.k8s.aws_projects.yaml @@ -0,0 +1,287 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.19.0 + name: projects.sagemaker.services.k8s.aws +spec: + group: sagemaker.services.k8s.aws + names: + kind: Project + listKind: ProjectList + plural: projects + singular: project + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.projectStatus + name: STATUS + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Project is the Schema for the Projects 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: |- + ProjectSpec defines the desired state of Project. + + The properties of a project as returned by the Search API. + properties: + projectDescription: + description: |- + A description for the project. + + Regex Pattern: `^[\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*$` + type: string + projectName: + description: |- + The name of the project. + + Regex Pattern: `^[a-zA-Z0-9](-*[a-zA-Z0-9]){0,31}$` + type: string + serviceCatalogProvisioningDetails: + description: |- + The product ID and provisioning artifact ID to provision a service catalog. + The provisioning artifact ID will default to the latest provisioning artifact + ID of the product, if you don't provide the provisioning artifact ID. For + more information, see What is Amazon Web Services Service Catalog (https://docs.aws.amazon.com/servicecatalog/latest/adminguide/introduction.html). + properties: + pathID: + type: string + productID: + type: string + provisioningArtifactID: + type: string + provisioningParameters: + items: + description: |- + A key value pair used when you provision a project as a service catalog product. + For information, see What is Amazon Web Services Service Catalog (https://docs.aws.amazon.com/servicecatalog/latest/adminguide/introduction.html). + properties: + key: + type: string + value: + type: string + type: object + type: array + type: object + tags: + description: |- + An array of key-value pairs that you want to use to organize and track your + Amazon Web Services resource costs. For more information, see Tagging Amazon + Web Services resources (https://docs.aws.amazon.com/general/latest/gr/aws_tagging.html) + in the Amazon Web Services General Reference Guide. + items: + description: |- + A tag object that consists of a key and an optional value, used to manage + metadata for SageMaker Amazon Web Services resources. + + You can add tags to notebook instances, training jobs, hyperparameter tuning + jobs, batch transform jobs, models, labeling jobs, work teams, endpoint configurations, + and endpoints. For more information on adding tags to SageMaker resources, + see AddTags (https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_AddTags.html). + + For more information on adding metadata to your Amazon Web Services resources + with tagging, see Tagging Amazon Web Services resources (https://docs.aws.amazon.com/general/latest/gr/aws_tagging.html). + For advice on best practices for managing Amazon Web Services resources with + tagging, see Tagging Best Practices: Implement an Effective Amazon Web Services + Resource Tagging Strategy (https://d1.awsstatic.com/whitepapers/aws-tagging-best-practices.pdf). + properties: + key: + type: string + value: + type: string + type: object + type: array + templateProviders: + description: |- + An array of template provider configurations for creating infrastructure + resources for the project. + items: + description: |- + Contains configuration details for a template provider. Only one type of + template provider can be specified. + properties: + cfnTemplateProvider: + description: |- + The CloudFormation template provider configuration for creating infrastructure + resources. + properties: + parameters: + items: + description: A key-value pair that represents a parameter + for the CloudFormation stack. + properties: + key: + type: string + value: + type: string + type: object + type: array + roleARN: + type: string + templateName: + type: string + templateURL: + type: string + type: object + type: object + type: array + required: + - projectName + type: object + status: + description: ProjectStatus defines the observed state of Project + properties: + ackResourceMetadata: + description: |- + All CRs managed by ACK have a common `Status.ACKResourceMetadata` member + that is used to contain resource sync state, account ownership, + constructed ARN for the resource + properties: + arn: + description: |- + ARN is the Amazon Resource Name for the resource. This is a + globally-unique identifier and is set only by the ACK service controller + once the controller has orchestrated the creation of the resource OR + when it has verified that an "adopted" resource (a resource where the + ARN annotation was set by the Kubernetes user on the CR) exists and + matches the supplied CR's Spec field values. + https://github.com/aws/aws-controllers-k8s/issues/270 + type: string + ownerAccountID: + description: |- + OwnerAccountID is the AWS Account ID of the account that owns the + backend AWS service API resource. + type: string + region: + description: Region is the AWS region in which the resource exists + or will exist. + type: string + required: + - ownerAccountID + - region + type: object + conditions: + description: |- + All CRs managed by ACK have a common `Status.Conditions` member that + contains a collection of `ackv1alpha1.Condition` objects that describe + the various terminal states of the CR and its backend AWS service API + resource + items: + description: |- + Condition is the common struct used by all CRDs managed by ACK service + controllers to indicate terminal states of the CR and its backend AWS + service API resource + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type is the type of the Condition + type: string + required: + - status + - type + type: object + type: array + creationTime: + description: The time when the project was created. + format: date-time + type: string + projectID: + description: |- + The ID of the new project. + + Regex Pattern: `^[a-zA-Z0-9](-*[a-zA-Z0-9])*$` + type: string + projectStatus: + description: The status of the project. + type: string + serviceCatalogProvisionedProductDetails: + description: Information about a provisioned service catalog product. + properties: + provisionedProductID: + type: string + provisionedProductStatusMessage: + type: string + type: object + templateProviderDetails: + description: An array of template providers associated with the project. + items: + description: |- + Details about a template provider configuration and associated provisioning + information. + properties: + cfnTemplateProviderDetail: + description: |- + Details about a CloudFormation template provider configuration and associated + provisioning information. + properties: + parameters: + items: + description: A key-value pair representing a parameter + used in the CloudFormation stack. + properties: + key: + type: string + value: + type: string + type: object + type: array + roleARN: + type: string + stackDetail: + description: Details about the CloudFormation stack. + properties: + id: + type: string + name: + type: string + statusMessage: + type: string + type: object + templateName: + type: string + templateURL: + type: string + type: object + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/common/bases/services.k8s.aws_adoptedresources.yaml b/config/crd/common/bases/services.k8s.aws_adoptedresources.yaml index b7be3224..d6cdd10f 100644 --- a/config/crd/common/bases/services.k8s.aws_adoptedresources.yaml +++ b/config/crd/common/bases/services.k8s.aws_adoptedresources.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.2 + controller-gen.kubebuilder.io/version: v0.19.0 name: adoptedresources.services.k8s.aws spec: group: services.k8s.aws diff --git a/config/crd/common/bases/services.k8s.aws_fieldexports.yaml b/config/crd/common/bases/services.k8s.aws_fieldexports.yaml index 49b4f383..6e2c61e6 100644 --- a/config/crd/common/bases/services.k8s.aws_fieldexports.yaml +++ b/config/crd/common/bases/services.k8s.aws_fieldexports.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.2 + controller-gen.kubebuilder.io/version: v0.19.0 name: fieldexports.services.k8s.aws spec: group: services.k8s.aws diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 153ea40f..5ed5c96c 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -23,6 +23,7 @@ resources: - bases/sagemaker.services.k8s.aws_pipelines.yaml - bases/sagemaker.services.k8s.aws_pipelineexecutions.yaml - bases/sagemaker.services.k8s.aws_processingjobs.yaml + - bases/sagemaker.services.k8s.aws_projects.yaml - bases/sagemaker.services.k8s.aws_spaces.yaml - bases/sagemaker.services.k8s.aws_trainingjobs.yaml - bases/sagemaker.services.k8s.aws_transformjobs.yaml diff --git a/config/rbac/cluster-role-controller.yaml b/config/rbac/cluster-role-controller.yaml index 345e5599..c1531cb8 100644 --- a/config/rbac/cluster-role-controller.yaml +++ b/config/rbac/cluster-role-controller.yaml @@ -46,6 +46,7 @@ rules: - pipelineexecutions - pipelines - processingjobs + - projects - spaces - trainingjobs - transformjobs @@ -82,6 +83,7 @@ rules: - pipelineexecutions/status - pipelines/status - processingjobs/status + - projects/status - spaces/status - trainingjobs/status - transformjobs/status diff --git a/config/rbac/role-reader.yaml b/config/rbac/role-reader.yaml index 0c912037..e76d0eda 100644 --- a/config/rbac/role-reader.yaml +++ b/config/rbac/role-reader.yaml @@ -30,6 +30,7 @@ rules: - pipelines - pipelineexecutions - processingjobs + - projects - spaces - trainingjobs - transformjobs diff --git a/config/rbac/role-writer.yaml b/config/rbac/role-writer.yaml index 77e2af05..80933538 100644 --- a/config/rbac/role-writer.yaml +++ b/config/rbac/role-writer.yaml @@ -30,6 +30,7 @@ rules: - pipelines - pipelineexecutions - processingjobs + - projects - spaces - trainingjobs - transformjobs @@ -66,6 +67,7 @@ rules: - pipelines - pipelineexecutions - processingjobs + - projects - spaces - trainingjobs - transformjobs diff --git a/generator.yaml b/generator.yaml index 583f021c..1809a8cd 100644 --- a/generator.yaml +++ b/generator.yaml @@ -931,6 +931,47 @@ resources: Tags: compare: is_ignored: true + Project: + exceptions: + errors: + 404: + code: ResourceNotFound + terminal_codes: + - InvalidParameterCombination + - InvalidParameterValue + - MissingParameter + renames: + operations: + UpdateProject: + input_fields: + ServiceCatalogProvisioningUpdateDetails: ServiceCatalogProvisioningDetails + TemplateProvidersToUpdate: TemplateProviders + fields: + ProjectStatus: + is_read_only: true + print: + name: STATUS + from: + operation: DescribeProject + path: ProjectStatus + CreationTime: + is_read_only: true + from: + operation: DescribeProject + path: CreationTime + ServiceCatalogProvisionedProductDetails: + is_read_only: true + from: + operation: DescribeProject + path: ServiceCatalogProvisionedProductDetails + TemplateProviderDetails: + is_read_only: true + from: + operation: DescribeProject + path: TemplateProviderDetails + Tags: + compare: + is_ignored: true Space: exceptions: errors: @@ -1236,7 +1277,7 @@ ignore: - PresignedNotebookInstanceUrl - PresignedMlflowTrackingServerUrl # - ProcessingJob - - Project + # - Project # - Space - StudioLifecycleConfig # - TrainingJob diff --git a/helm/crds/sagemaker.services.k8s.aws_projects.yaml b/helm/crds/sagemaker.services.k8s.aws_projects.yaml new file mode 100644 index 00000000..96d8e236 --- /dev/null +++ b/helm/crds/sagemaker.services.k8s.aws_projects.yaml @@ -0,0 +1,287 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.19.0 + name: projects.sagemaker.services.k8s.aws +spec: + group: sagemaker.services.k8s.aws + names: + kind: Project + listKind: ProjectList + plural: projects + singular: project + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.projectStatus + name: STATUS + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Project is the Schema for the Projects 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: |- + ProjectSpec defines the desired state of Project. + + The properties of a project as returned by the Search API. + properties: + projectDescription: + description: |- + A description for the project. + + Regex Pattern: `^[\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*$` + type: string + projectName: + description: |- + The name of the project. + + Regex Pattern: `^[a-zA-Z0-9](-*[a-zA-Z0-9]){0,31}$` + type: string + serviceCatalogProvisioningDetails: + description: |- + The product ID and provisioning artifact ID to provision a service catalog. + The provisioning artifact ID will default to the latest provisioning artifact + ID of the product, if you don't provide the provisioning artifact ID. For + more information, see What is Amazon Web Services Service Catalog (https://docs.aws.amazon.com/servicecatalog/latest/adminguide/introduction.html). + properties: + pathID: + type: string + productID: + type: string + provisioningArtifactID: + type: string + provisioningParameters: + items: + description: |- + A key value pair used when you provision a project as a service catalog product. + For information, see What is Amazon Web Services Service Catalog (https://docs.aws.amazon.com/servicecatalog/latest/adminguide/introduction.html). + properties: + key: + type: string + value: + type: string + type: object + type: array + type: object + tags: + description: |- + An array of key-value pairs that you want to use to organize and track your + Amazon Web Services resource costs. For more information, see Tagging Amazon + Web Services resources (https://docs.aws.amazon.com/general/latest/gr/aws_tagging.html) + in the Amazon Web Services General Reference Guide. + items: + description: |- + A tag object that consists of a key and an optional value, used to manage + metadata for SageMaker Amazon Web Services resources. + + You can add tags to notebook instances, training jobs, hyperparameter tuning + jobs, batch transform jobs, models, labeling jobs, work teams, endpoint configurations, + and endpoints. For more information on adding tags to SageMaker resources, + see AddTags (https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_AddTags.html). + + For more information on adding metadata to your Amazon Web Services resources + with tagging, see Tagging Amazon Web Services resources (https://docs.aws.amazon.com/general/latest/gr/aws_tagging.html). + For advice on best practices for managing Amazon Web Services resources with + tagging, see Tagging Best Practices: Implement an Effective Amazon Web Services + Resource Tagging Strategy (https://d1.awsstatic.com/whitepapers/aws-tagging-best-practices.pdf). + properties: + key: + type: string + value: + type: string + type: object + type: array + templateProviders: + description: |- + An array of template provider configurations for creating infrastructure + resources for the project. + items: + description: |- + Contains configuration details for a template provider. Only one type of + template provider can be specified. + properties: + cfnTemplateProvider: + description: |- + The CloudFormation template provider configuration for creating infrastructure + resources. + properties: + parameters: + items: + description: A key-value pair that represents a parameter + for the CloudFormation stack. + properties: + key: + type: string + value: + type: string + type: object + type: array + roleARN: + type: string + templateName: + type: string + templateURL: + type: string + type: object + type: object + type: array + required: + - projectName + type: object + status: + description: ProjectStatus defines the observed state of Project + properties: + ackResourceMetadata: + description: |- + All CRs managed by ACK have a common `Status.ACKResourceMetadata` member + that is used to contain resource sync state, account ownership, + constructed ARN for the resource + properties: + arn: + description: |- + ARN is the Amazon Resource Name for the resource. This is a + globally-unique identifier and is set only by the ACK service controller + once the controller has orchestrated the creation of the resource OR + when it has verified that an "adopted" resource (a resource where the + ARN annotation was set by the Kubernetes user on the CR) exists and + matches the supplied CR's Spec field values. + https://github.com/aws/aws-controllers-k8s/issues/270 + type: string + ownerAccountID: + description: |- + OwnerAccountID is the AWS Account ID of the account that owns the + backend AWS service API resource. + type: string + region: + description: Region is the AWS region in which the resource exists + or will exist. + type: string + required: + - ownerAccountID + - region + type: object + conditions: + description: |- + All CRs managed by ACK have a common `Status.Conditions` member that + contains a collection of `ackv1alpha1.Condition` objects that describe + the various terminal states of the CR and its backend AWS service API + resource + items: + description: |- + Condition is the common struct used by all CRDs managed by ACK service + controllers to indicate terminal states of the CR and its backend AWS + service API resource + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type is the type of the Condition + type: string + required: + - status + - type + type: object + type: array + creationTime: + description: The time when the project was created. + format: date-time + type: string + projectID: + description: |- + The ID of the new project. + + Regex Pattern: `^[a-zA-Z0-9](-*[a-zA-Z0-9])*$` + type: string + projectStatus: + description: The status of the project. + type: string + serviceCatalogProvisionedProductDetails: + description: Information about a provisioned service catalog product. + properties: + provisionedProductID: + type: string + provisionedProductStatusMessage: + type: string + type: object + templateProviderDetails: + description: An array of template providers associated with the project. + items: + description: |- + Details about a template provider configuration and associated provisioning + information. + properties: + cfnTemplateProviderDetail: + description: |- + Details about a CloudFormation template provider configuration and associated + provisioning information. + properties: + parameters: + items: + description: A key-value pair representing a parameter + used in the CloudFormation stack. + properties: + key: + type: string + value: + type: string + type: object + type: array + roleARN: + type: string + stackDetail: + description: Details about the CloudFormation stack. + properties: + id: + type: string + name: + type: string + statusMessage: + type: string + type: object + templateName: + type: string + templateURL: + type: string + type: object + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl index 9998c974..18e9f287 100644 --- a/helm/templates/_helpers.tpl +++ b/helm/templates/_helpers.tpl @@ -93,6 +93,7 @@ rules: - pipelineexecutions - pipelines - processingjobs + - projects - spaces - trainingjobs - transformjobs @@ -129,6 +130,7 @@ rules: - pipelineexecutions/status - pipelines/status - processingjobs/status + - projects/status - spaces/status - trainingjobs/status - transformjobs/status diff --git a/helm/templates/role-reader.yaml b/helm/templates/role-reader.yaml index 1f737a92..0ce3d7bf 100644 --- a/helm/templates/role-reader.yaml +++ b/helm/templates/role-reader.yaml @@ -37,6 +37,7 @@ rules: - pipelines - pipelineexecutions - processingjobs + - projects - spaces - trainingjobs - transformjobs diff --git a/helm/templates/role-writer.yaml b/helm/templates/role-writer.yaml index a95e0ef0..88dae913 100644 --- a/helm/templates/role-writer.yaml +++ b/helm/templates/role-writer.yaml @@ -37,6 +37,7 @@ rules: - pipelines - pipelineexecutions - processingjobs + - projects - spaces - trainingjobs - transformjobs @@ -73,6 +74,7 @@ rules: - pipelines - pipelineexecutions - processingjobs + - projects - spaces - trainingjobs - transformjobs diff --git a/helm/values.yaml b/helm/values.yaml index 84b8e997..1f49e6ef 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -166,6 +166,7 @@ reconcile: - Pipeline - PipelineExecution - ProcessingJob + - Project - Space - TrainingJob - TransformJob diff --git a/pkg/resource/project/delta.go b/pkg/resource/project/delta.go new file mode 100644 index 00000000..5e2cc0c5 --- /dev/null +++ b/pkg/resource/project/delta.go @@ -0,0 +1,101 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file 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 project + +import ( + "bytes" + "reflect" + + ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" + acktags "github.com/aws-controllers-k8s/runtime/pkg/tags" +) + +// Hack to avoid import errors during build... +var ( + _ = &bytes.Buffer{} + _ = &reflect.Method{} + _ = &acktags.Tags{} +) + +// newResourceDelta returns a new `ackcompare.Delta` used to compare two +// resources +func newResourceDelta( + a *resource, + b *resource, +) *ackcompare.Delta { + delta := ackcompare.NewDelta() + if (a == nil && b != nil) || + (a != nil && b == nil) { + delta.Add("", a, b) + return delta + } + + if ackcompare.HasNilDifference(a.ko.Spec.ProjectDescription, b.ko.Spec.ProjectDescription) { + delta.Add("Spec.ProjectDescription", a.ko.Spec.ProjectDescription, b.ko.Spec.ProjectDescription) + } else if a.ko.Spec.ProjectDescription != nil && b.ko.Spec.ProjectDescription != nil { + if *a.ko.Spec.ProjectDescription != *b.ko.Spec.ProjectDescription { + delta.Add("Spec.ProjectDescription", a.ko.Spec.ProjectDescription, b.ko.Spec.ProjectDescription) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.ProjectName, b.ko.Spec.ProjectName) { + delta.Add("Spec.ProjectName", a.ko.Spec.ProjectName, b.ko.Spec.ProjectName) + } else if a.ko.Spec.ProjectName != nil && b.ko.Spec.ProjectName != nil { + if *a.ko.Spec.ProjectName != *b.ko.Spec.ProjectName { + delta.Add("Spec.ProjectName", a.ko.Spec.ProjectName, b.ko.Spec.ProjectName) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.ServiceCatalogProvisioningDetails, b.ko.Spec.ServiceCatalogProvisioningDetails) { + delta.Add("Spec.ServiceCatalogProvisioningDetails", a.ko.Spec.ServiceCatalogProvisioningDetails, b.ko.Spec.ServiceCatalogProvisioningDetails) + } else if a.ko.Spec.ServiceCatalogProvisioningDetails != nil && b.ko.Spec.ServiceCatalogProvisioningDetails != nil { + if ackcompare.HasNilDifference(a.ko.Spec.ServiceCatalogProvisioningDetails.PathID, b.ko.Spec.ServiceCatalogProvisioningDetails.PathID) { + delta.Add("Spec.ServiceCatalogProvisioningDetails.PathID", a.ko.Spec.ServiceCatalogProvisioningDetails.PathID, b.ko.Spec.ServiceCatalogProvisioningDetails.PathID) + } else if a.ko.Spec.ServiceCatalogProvisioningDetails.PathID != nil && b.ko.Spec.ServiceCatalogProvisioningDetails.PathID != nil { + if *a.ko.Spec.ServiceCatalogProvisioningDetails.PathID != *b.ko.Spec.ServiceCatalogProvisioningDetails.PathID { + delta.Add("Spec.ServiceCatalogProvisioningDetails.PathID", a.ko.Spec.ServiceCatalogProvisioningDetails.PathID, b.ko.Spec.ServiceCatalogProvisioningDetails.PathID) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.ServiceCatalogProvisioningDetails.ProductID, b.ko.Spec.ServiceCatalogProvisioningDetails.ProductID) { + delta.Add("Spec.ServiceCatalogProvisioningDetails.ProductID", a.ko.Spec.ServiceCatalogProvisioningDetails.ProductID, b.ko.Spec.ServiceCatalogProvisioningDetails.ProductID) + } else if a.ko.Spec.ServiceCatalogProvisioningDetails.ProductID != nil && b.ko.Spec.ServiceCatalogProvisioningDetails.ProductID != nil { + if *a.ko.Spec.ServiceCatalogProvisioningDetails.ProductID != *b.ko.Spec.ServiceCatalogProvisioningDetails.ProductID { + delta.Add("Spec.ServiceCatalogProvisioningDetails.ProductID", a.ko.Spec.ServiceCatalogProvisioningDetails.ProductID, b.ko.Spec.ServiceCatalogProvisioningDetails.ProductID) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningArtifactID, b.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningArtifactID) { + delta.Add("Spec.ServiceCatalogProvisioningDetails.ProvisioningArtifactID", a.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningArtifactID, b.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningArtifactID) + } else if a.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningArtifactID != nil && b.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningArtifactID != nil { + if *a.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningArtifactID != *b.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningArtifactID { + delta.Add("Spec.ServiceCatalogProvisioningDetails.ProvisioningArtifactID", a.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningArtifactID, b.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningArtifactID) + } + } + if len(a.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningParameters) != len(b.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningParameters) { + delta.Add("Spec.ServiceCatalogProvisioningDetails.ProvisioningParameters", a.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningParameters, b.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningParameters) + } else if len(a.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningParameters) > 0 { + if !reflect.DeepEqual(a.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningParameters, b.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningParameters) { + delta.Add("Spec.ServiceCatalogProvisioningDetails.ProvisioningParameters", a.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningParameters, b.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningParameters) + } + } + } + if len(a.ko.Spec.TemplateProviders) != len(b.ko.Spec.TemplateProviders) { + delta.Add("Spec.TemplateProviders", a.ko.Spec.TemplateProviders, b.ko.Spec.TemplateProviders) + } else if len(a.ko.Spec.TemplateProviders) > 0 { + if !reflect.DeepEqual(a.ko.Spec.TemplateProviders, b.ko.Spec.TemplateProviders) { + delta.Add("Spec.TemplateProviders", a.ko.Spec.TemplateProviders, b.ko.Spec.TemplateProviders) + } + } + + return delta +} diff --git a/pkg/resource/project/descriptor.go b/pkg/resource/project/descriptor.go new file mode 100644 index 00000000..6705a7b4 --- /dev/null +++ b/pkg/resource/project/descriptor.go @@ -0,0 +1,155 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file 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 project + +import ( + ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" + ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" + acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + rtclient "sigs.k8s.io/controller-runtime/pkg/client" + k8sctrlutil "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + svcapitypes "github.com/aws-controllers-k8s/sagemaker-controller/apis/v1alpha1" +) + +const ( + FinalizerString = "finalizers.sagemaker.services.k8s.aws/Project" +) + +var ( + GroupVersionResource = svcapitypes.GroupVersion.WithResource("projects") + GroupKind = metav1.GroupKind{ + Group: "sagemaker.services.k8s.aws", + Kind: "Project", + } +) + +// resourceDescriptor implements the +// `aws-service-operator-k8s/pkg/types.AWSResourceDescriptor` interface +type resourceDescriptor struct { +} + +// GroupVersionKind returns a Kubernetes schema.GroupVersionKind struct that +// describes the API Group, Version and Kind of CRs described by the descriptor +func (d *resourceDescriptor) GroupVersionKind() schema.GroupVersionKind { + return svcapitypes.GroupVersion.WithKind(GroupKind.Kind) +} + +// EmptyRuntimeObject returns an empty object prototype that may be used in +// apimachinery and k8s client operations +func (d *resourceDescriptor) EmptyRuntimeObject() rtclient.Object { + return &svcapitypes.Project{} +} + +// ResourceFromRuntimeObject returns an AWSResource that has been initialized +// with the supplied runtime.Object +func (d *resourceDescriptor) ResourceFromRuntimeObject( + obj rtclient.Object, +) acktypes.AWSResource { + return &resource{ + ko: obj.(*svcapitypes.Project), + } +} + +// Delta returns an `ackcompare.Delta` object containing the difference between +// one `AWSResource` and another. +func (d *resourceDescriptor) Delta(a, b acktypes.AWSResource) *ackcompare.Delta { + return newResourceDelta(a.(*resource), b.(*resource)) +} + +// IsManaged returns true if the supplied AWSResource is under the management +// of an ACK service controller. What this means in practice is that the +// underlying custom resource (CR) in the AWSResource has had a +// resource-specific finalizer associated with it. +func (d *resourceDescriptor) IsManaged( + res acktypes.AWSResource, +) bool { + obj := res.RuntimeObject() + if obj == nil { + // Should not happen. If it does, there is a bug in the code + panic("nil RuntimeMetaObject in AWSResource") + } + // Remove use of custom code once + // https://github.com/kubernetes-sigs/controller-runtime/issues/994 is + // fixed. This should be able to be: + // + // return k8sctrlutil.ContainsFinalizer(obj, FinalizerString) + return containsFinalizer(obj, FinalizerString) +} + +// Remove once https://github.com/kubernetes-sigs/controller-runtime/issues/994 +// is fixed. +func containsFinalizer(obj rtclient.Object, finalizer string) bool { + f := obj.GetFinalizers() + for _, e := range f { + if e == finalizer { + return true + } + } + return false +} + +// MarkManaged places the supplied resource under the management of ACK. What +// this typically means is that the resource manager will decorate the +// underlying custom resource (CR) with a finalizer that indicates ACK is +// managing the resource and the underlying CR may not be deleted until ACK is +// finished cleaning up any backend AWS service resources associated with the +// CR. +func (d *resourceDescriptor) MarkManaged( + res acktypes.AWSResource, +) { + obj := res.RuntimeObject() + if obj == nil { + // Should not happen. If it does, there is a bug in the code + panic("nil RuntimeMetaObject in AWSResource") + } + k8sctrlutil.AddFinalizer(obj, FinalizerString) +} + +// MarkUnmanaged removes the supplied resource from management by ACK. What +// this typically means is that the resource manager will remove a finalizer +// underlying custom resource (CR) that indicates ACK is managing the resource. +// This will allow the Kubernetes API server to delete the underlying CR. +func (d *resourceDescriptor) MarkUnmanaged( + res acktypes.AWSResource, +) { + obj := res.RuntimeObject() + if obj == nil { + // Should not happen. If it does, there is a bug in the code + panic("nil RuntimeMetaObject in AWSResource") + } + k8sctrlutil.RemoveFinalizer(obj, FinalizerString) +} + +// MarkAdopted places descriptors on the custom resource that indicate the +// resource was not created from within ACK. +func (d *resourceDescriptor) MarkAdopted( + res acktypes.AWSResource, +) { + obj := res.RuntimeObject() + if obj == nil { + // Should not happen. If it does, there is a bug in the code + panic("nil RuntimeObject in AWSResource") + } + curr := obj.GetAnnotations() + if curr == nil { + curr = make(map[string]string) + } + curr[ackv1alpha1.AnnotationAdopted] = "true" + obj.SetAnnotations(curr) +} diff --git a/pkg/resource/project/identifiers.go b/pkg/resource/project/identifiers.go new file mode 100644 index 00000000..d7e63497 --- /dev/null +++ b/pkg/resource/project/identifiers.go @@ -0,0 +1,55 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file 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 project + +import ( + ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" +) + +// resourceIdentifiers implements the +// `aws-service-operator-k8s/pkg/types.AWSResourceIdentifiers` interface +type resourceIdentifiers struct { + meta *ackv1alpha1.ResourceMetadata +} + +// ARN returns the AWS Resource Name for the backend AWS resource. If nil, +// this means the resource has not yet been created in the backend AWS +// service. +func (ri *resourceIdentifiers) ARN() *ackv1alpha1.AWSResourceName { + if ri.meta != nil { + return ri.meta.ARN + } + return nil +} + +// OwnerAccountID returns the AWS account identifier in which the +// backend AWS resource resides, or nil if this information is not known +// for the resource +func (ri *resourceIdentifiers) OwnerAccountID() *ackv1alpha1.AWSAccountID { + if ri.meta != nil { + return ri.meta.OwnerAccountID + } + return nil +} + +// Region returns the AWS region in which the resource exists, or +// nil if this information is not known. +func (ri *resourceIdentifiers) Region() *ackv1alpha1.AWSRegion { + if ri.meta != nil { + return ri.meta.Region + } + return nil +} diff --git a/pkg/resource/project/manager.go b/pkg/resource/project/manager.go new file mode 100644 index 00000000..0ae834c5 --- /dev/null +++ b/pkg/resource/project/manager.go @@ -0,0 +1,404 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file 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 project + +import ( + "context" + "fmt" + "time" + + ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" + ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" + ackcondition "github.com/aws-controllers-k8s/runtime/pkg/condition" + ackcfg "github.com/aws-controllers-k8s/runtime/pkg/config" + ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors" + ackmetrics "github.com/aws-controllers-k8s/runtime/pkg/metrics" + ackrequeue "github.com/aws-controllers-k8s/runtime/pkg/requeue" + ackrt "github.com/aws-controllers-k8s/runtime/pkg/runtime" + ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log" + acktags "github.com/aws-controllers-k8s/runtime/pkg/tags" + acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" + ackutil "github.com/aws-controllers-k8s/runtime/pkg/util" + "github.com/aws/aws-sdk-go-v2/aws" + svcsdk "github.com/aws/aws-sdk-go-v2/service/sagemaker" + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + + svcapitypes "github.com/aws-controllers-k8s/sagemaker-controller/apis/v1alpha1" +) + +var ( + _ = ackutil.InStrings + _ = acktags.NewTags() + _ = ackrt.MissingImageTagValue + _ = svcapitypes.Project{} +) + +// +kubebuilder:rbac:groups=sagemaker.services.k8s.aws,resources=projects,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=sagemaker.services.k8s.aws,resources=projects/status,verbs=get;update;patch + +var lateInitializeFieldNames = []string{} + +// resourceManager is responsible for providing a consistent way to perform +// CRUD operations in a backend AWS service API for Book custom resources. +type resourceManager struct { + // cfg is a copy of the ackcfg.Config object passed on start of the service + // controller + cfg ackcfg.Config + // clientcfg is a copy of the client configuration passed on start of the + // service controller + clientcfg aws.Config + // log refers to the logr.Logger object handling logging for the service + // controller + log logr.Logger + // metrics contains a collection of Prometheus metric objects that the + // service controller and its reconcilers track + metrics *ackmetrics.Metrics + // rr is the Reconciler which can be used for various utility + // functions such as querying for Secret values given a SecretReference + rr acktypes.Reconciler + // awsAccountID is the AWS account identifier that contains the resources + // managed by this resource manager + awsAccountID ackv1alpha1.AWSAccountID + // The AWS Region that this resource manager targets + awsRegion ackv1alpha1.AWSRegion + // sdk is a pointer to the AWS service API client exposed by the + // aws-sdk-go-v2/services/{alias} package. + sdkapi *svcsdk.Client +} + +// concreteResource returns a pointer to a resource from the supplied +// generic AWSResource interface +func (rm *resourceManager) concreteResource( + res acktypes.AWSResource, +) *resource { + // cast the generic interface into a pointer type specific to the concrete + // implementing resource type managed by this resource manager + return res.(*resource) +} + +// ReadOne returns the currently-observed state of the supplied AWSResource in +// the backend AWS service API. +func (rm *resourceManager) ReadOne( + ctx context.Context, + res acktypes.AWSResource, +) (acktypes.AWSResource, error) { + r := rm.concreteResource(res) + if r.ko == nil { + // Should never happen... if it does, it's buggy code. + panic("resource manager's ReadOne() method received resource with nil CR object") + } + observed, err := rm.sdkFind(ctx, r) + mirrorAWSTags(r, observed) + if err != nil { + if observed != nil { + return rm.onError(observed, err) + } + return rm.onError(r, err) + } + return rm.onSuccess(observed) +} + +// Create attempts to create the supplied AWSResource in the backend AWS +// service API, returning an AWSResource representing the newly-created +// resource +func (rm *resourceManager) Create( + ctx context.Context, + res acktypes.AWSResource, +) (acktypes.AWSResource, error) { + r := rm.concreteResource(res) + if r.ko == nil { + // Should never happen... if it does, it's buggy code. + panic("resource manager's Create() method received resource with nil CR object") + } + created, err := rm.sdkCreate(ctx, r) + if err != nil { + if created != nil { + return rm.onError(created, err) + } + return rm.onError(r, err) + } + return rm.onSuccess(created) +} + +// Update attempts to mutate the supplied desired AWSResource in the backend AWS +// service API, returning an AWSResource representing the newly-mutated +// resource. +// Note for specialized logic implementers can check to see how the latest +// observed resource differs from the supplied desired state. The +// higher-level reonciler determines whether or not the desired differs +// from the latest observed and decides whether to call the resource +// manager's Update method +func (rm *resourceManager) Update( + ctx context.Context, + resDesired acktypes.AWSResource, + resLatest acktypes.AWSResource, + delta *ackcompare.Delta, +) (acktypes.AWSResource, error) { + desired := rm.concreteResource(resDesired) + latest := rm.concreteResource(resLatest) + if desired.ko == nil || latest.ko == nil { + // Should never happen... if it does, it's buggy code. + panic("resource manager's Update() method received resource with nil CR object") + } + updated, err := rm.sdkUpdate(ctx, desired, latest, delta) + if err != nil { + if updated != nil { + return rm.onError(updated, err) + } + return rm.onError(latest, err) + } + return rm.onSuccess(updated) +} + +// Delete attempts to destroy the supplied AWSResource in the backend AWS +// service API, returning an AWSResource representing the +// resource being deleted (if delete is asynchronous and takes time) +func (rm *resourceManager) Delete( + ctx context.Context, + res acktypes.AWSResource, +) (acktypes.AWSResource, error) { + r := rm.concreteResource(res) + if r.ko == nil { + // Should never happen... if it does, it's buggy code. + panic("resource manager's Update() method received resource with nil CR object") + } + observed, err := rm.sdkDelete(ctx, r) + if err != nil { + if observed != nil { + return rm.onError(observed, err) + } + return rm.onError(r, err) + } + + return rm.onSuccess(observed) +} + +// ARNFromName returns an AWS Resource Name from a given string name. This +// is useful for constructing ARNs for APIs that require ARNs in their +// GetAttributes operations but all we have (for new CRs at least) is a +// name for the resource +func (rm *resourceManager) ARNFromName(name string) string { + return fmt.Sprintf( + "arn:aws:sagemaker:%s:%s:%s", + rm.awsRegion, + rm.awsAccountID, + name, + ) +} + +// LateInitialize returns an acktypes.AWSResource after setting the late initialized +// fields from the readOne call. This method will initialize the optional fields +// which were not provided by the k8s user but were defaulted by the AWS service. +// If there are no such fields to be initialized, the returned object is similar to +// object passed in the parameter. +func (rm *resourceManager) LateInitialize( + ctx context.Context, + latest acktypes.AWSResource, +) (acktypes.AWSResource, error) { + rlog := ackrtlog.FromContext(ctx) + // If there are no fields to late initialize, do nothing + if len(lateInitializeFieldNames) == 0 { + rlog.Debug("no late initialization required.") + return latest, nil + } + latestCopy := latest.DeepCopy() + lateInitConditionReason := "" + lateInitConditionMessage := "" + observed, err := rm.ReadOne(ctx, latestCopy) + if err != nil { + lateInitConditionMessage = "Unable to complete Read operation required for late initialization" + lateInitConditionReason = "Late Initialization Failure" + ackcondition.SetLateInitialized(latestCopy, corev1.ConditionFalse, &lateInitConditionMessage, &lateInitConditionReason) + ackcondition.SetSynced(latestCopy, corev1.ConditionFalse, nil, nil) + return latestCopy, err + } + lateInitializedRes := rm.lateInitializeFromReadOneOutput(observed, latestCopy) + incompleteInitialization := rm.incompleteLateInitialization(lateInitializedRes) + if incompleteInitialization { + // Add the condition with LateInitialized=False + lateInitConditionMessage = "Late initialization did not complete, requeuing with delay of 5 seconds" + lateInitConditionReason = "Delayed Late Initialization" + ackcondition.SetLateInitialized(lateInitializedRes, corev1.ConditionFalse, &lateInitConditionMessage, &lateInitConditionReason) + ackcondition.SetSynced(lateInitializedRes, corev1.ConditionFalse, nil, nil) + return lateInitializedRes, ackrequeue.NeededAfter(nil, time.Duration(5)*time.Second) + } + // Set LateInitialized condition to True + lateInitConditionMessage = "Late initialization successful" + lateInitConditionReason = "Late initialization successful" + ackcondition.SetLateInitialized(lateInitializedRes, corev1.ConditionTrue, &lateInitConditionMessage, &lateInitConditionReason) + return lateInitializedRes, nil +} + +// incompleteLateInitialization return true if there are fields which were supposed to be +// late initialized but are not. If all the fields are late initialized, false is returned +func (rm *resourceManager) incompleteLateInitialization( + res acktypes.AWSResource, +) bool { + return false +} + +// lateInitializeFromReadOneOutput late initializes the 'latest' resource from the 'observed' +// resource and returns 'latest' resource +func (rm *resourceManager) lateInitializeFromReadOneOutput( + observed acktypes.AWSResource, + latest acktypes.AWSResource, +) acktypes.AWSResource { + return latest +} + +// IsSynced returns true if the resource is synced. +func (rm *resourceManager) IsSynced(ctx context.Context, res acktypes.AWSResource) (bool, error) { + r := rm.concreteResource(res) + if r.ko == nil { + // Should never happen... if it does, it's buggy code. + panic("resource manager's IsSynced() method received resource with nil CR object") + } + + return true, nil +} + +// EnsureTags ensures that tags are present inside the AWSResource. +// If the AWSResource does not have any existing resource tags, the 'tags' +// field is initialized and the controller tags are added. +// If the AWSResource has existing resource tags, then controller tags are +// added to the existing resource tags without overriding them. +// If the AWSResource does not support tags, only then the controller tags +// will not be added to the AWSResource. +func (rm *resourceManager) EnsureTags( + ctx context.Context, + res acktypes.AWSResource, + md acktypes.ServiceControllerMetadata, +) error { + r := rm.concreteResource(res) + if r.ko == nil { + // Should never happen... if it does, it's buggy code. + panic("resource manager's EnsureTags method received resource with nil CR object") + } + defaultTags := ackrt.GetDefaultTags(&rm.cfg, r.ko, md) + var existingTags []*svcapitypes.Tag + existingTags = r.ko.Spec.Tags + resourceTags, keyOrder := convertToOrderedACKTags(existingTags) + tags := acktags.Merge(resourceTags, defaultTags) + r.ko.Spec.Tags = fromACKTags(tags, keyOrder) + return nil +} + +// FilterAWSTags ignores tags that have keys that start with "aws:" +// is needed to ensure the controller does not attempt to remove +// tags set by AWS. This function needs to be called after each Read +// operation. +// Eg. resources created with cloudformation have tags that cannot be +// removed by an ACK controller +func (rm *resourceManager) FilterSystemTags(res acktypes.AWSResource) { + r := rm.concreteResource(res) + if r == nil || r.ko == nil { + return + } + var existingTags []*svcapitypes.Tag + existingTags = r.ko.Spec.Tags + resourceTags, tagKeyOrder := convertToOrderedACKTags(existingTags) + ignoreSystemTags(resourceTags) + r.ko.Spec.Tags = fromACKTags(resourceTags, tagKeyOrder) +} + +// mirrorAWSTags ensures that AWS tags are included in the desired resource +// if they are present in the latest resource. This will ensure that the +// aws tags are not present in a diff. The logic of the controller will +// ensure these tags aren't patched to the resource in the cluster, and +// will only be present to make sure we don't try to remove these tags. +// +// Although there are a lot of similarities between this function and +// EnsureTags, they are very much different. +// While EnsureTags tries to make sure the resource contains the controller +// tags, mirrowAWSTags tries to make sure tags injected by AWS are mirrored +// from the latest resoruce to the desired resource. +func mirrorAWSTags(a *resource, b *resource) { + if a == nil || a.ko == nil || b == nil || b.ko == nil { + return + } + var existingLatestTags []*svcapitypes.Tag + var existingDesiredTags []*svcapitypes.Tag + existingDesiredTags = a.ko.Spec.Tags + existingLatestTags = b.ko.Spec.Tags + desiredTags, desiredTagKeyOrder := convertToOrderedACKTags(existingDesiredTags) + latestTags, _ := convertToOrderedACKTags(existingLatestTags) + syncAWSTags(desiredTags, latestTags) + a.ko.Spec.Tags = fromACKTags(desiredTags, desiredTagKeyOrder) +} + +// newResourceManager returns a new struct implementing +// acktypes.AWSResourceManager +// This is for AWS-SDK-GO-V2 - Created newResourceManager With AWS sdk-Go-ClientV2 +func newResourceManager( + cfg ackcfg.Config, + clientcfg aws.Config, + log logr.Logger, + metrics *ackmetrics.Metrics, + rr acktypes.Reconciler, + id ackv1alpha1.AWSAccountID, + region ackv1alpha1.AWSRegion, +) (*resourceManager, error) { + return &resourceManager{ + cfg: cfg, + clientcfg: clientcfg, + log: log, + metrics: metrics, + rr: rr, + awsAccountID: id, + awsRegion: region, + sdkapi: svcsdk.NewFromConfig(clientcfg), + }, nil +} + +// onError updates resource conditions and returns updated resource +// it returns nil if no condition is updated. +func (rm *resourceManager) onError( + r *resource, + err error, +) (acktypes.AWSResource, error) { + if r == nil { + return nil, err + } + r1, updated := rm.updateConditions(r, false, err) + if !updated { + return r, err + } + for _, condition := range r1.Conditions() { + if condition.Type == ackv1alpha1.ConditionTypeTerminal && + condition.Status == corev1.ConditionTrue { + // resource is in Terminal condition + // return Terminal error + return r1, ackerr.Terminal + } + } + return r1, err +} + +// onSuccess updates resource conditions and returns updated resource +// it returns the supplied resource if no condition is updated. +func (rm *resourceManager) onSuccess( + r *resource, +) (acktypes.AWSResource, error) { + if r == nil { + return nil, nil + } + r1, updated := rm.updateConditions(r, true, nil) + if !updated { + return r, nil + } + return r1, nil +} diff --git a/pkg/resource/project/manager_factory.go b/pkg/resource/project/manager_factory.go new file mode 100644 index 00000000..511a81f4 --- /dev/null +++ b/pkg/resource/project/manager_factory.go @@ -0,0 +1,100 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file 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 project + +import ( + "fmt" + "sync" + + ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" + ackcfg "github.com/aws-controllers-k8s/runtime/pkg/config" + ackmetrics "github.com/aws-controllers-k8s/runtime/pkg/metrics" + acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/go-logr/logr" + + svcresource "github.com/aws-controllers-k8s/sagemaker-controller/pkg/resource" +) + +// resourceManagerFactory produces resourceManager objects. It implements the +// `types.AWSResourceManagerFactory` interface. +type resourceManagerFactory struct { + sync.RWMutex + // rmCache contains resource managers for a particular AWS account ID + rmCache map[string]*resourceManager +} + +// ResourcePrototype returns an AWSResource that resource managers produced by +// this factory will handle +func (f *resourceManagerFactory) ResourceDescriptor() acktypes.AWSResourceDescriptor { + return &resourceDescriptor{} +} + +// ManagerFor returns a resource manager object that can manage resources for a +// supplied AWS account +func (f *resourceManagerFactory) ManagerFor( + cfg ackcfg.Config, + clientcfg aws.Config, + log logr.Logger, + metrics *ackmetrics.Metrics, + rr acktypes.Reconciler, + id ackv1alpha1.AWSAccountID, + region ackv1alpha1.AWSRegion, + roleARN ackv1alpha1.AWSResourceName, +) (acktypes.AWSResourceManager, error) { + // We use the account ID, region, and role ARN to uniquely identify a + // resource manager. This helps us to avoid creating multiple resource + // managers for the same account/region/roleARN combination. + rmId := fmt.Sprintf("%s/%s/%s", id, region, roleARN) + f.RLock() + rm, found := f.rmCache[rmId] + f.RUnlock() + + if found { + return rm, nil + } + + f.Lock() + defer f.Unlock() + + rm, err := newResourceManager(cfg, clientcfg, log, metrics, rr, id, region) + if err != nil { + return nil, err + } + f.rmCache[rmId] = rm + return rm, nil +} + +// IsAdoptable returns true if the resource is able to be adopted +func (f *resourceManagerFactory) IsAdoptable() bool { + return true +} + +// RequeueOnSuccessSeconds returns true if the resource should be requeued after specified seconds +// Default is false which means resource will not be requeued after success. +func (f *resourceManagerFactory) RequeueOnSuccessSeconds() int { + return 0 +} + +func newResourceManagerFactory() *resourceManagerFactory { + return &resourceManagerFactory{ + rmCache: map[string]*resourceManager{}, + } +} + +func init() { + svcresource.RegisterManagerFactory(newResourceManagerFactory()) +} diff --git a/pkg/resource/project/references.go b/pkg/resource/project/references.go new file mode 100644 index 00000000..904793d3 --- /dev/null +++ b/pkg/resource/project/references.go @@ -0,0 +1,57 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file 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 project + +import ( + "context" + + "sigs.k8s.io/controller-runtime/pkg/client" + + acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" + + svcapitypes "github.com/aws-controllers-k8s/sagemaker-controller/apis/v1alpha1" +) + +// ClearResolvedReferences removes any reference values that were made +// concrete in the spec. It returns a copy of the input AWSResource which +// contains the original *Ref values, but none of their respective concrete +// values. +func (rm *resourceManager) ClearResolvedReferences(res acktypes.AWSResource) acktypes.AWSResource { + ko := rm.concreteResource(res).ko.DeepCopy() + + return &resource{ko} +} + +// ResolveReferences finds if there are any Reference field(s) present +// inside AWSResource passed in the parameter and attempts to resolve those +// reference field(s) into their respective target field(s). It returns a +// copy of the input AWSResource with resolved reference(s), a boolean which +// is set to true if the resource contains any references (regardless of if +// they are resolved successfully) and an error if the passed AWSResource's +// reference field(s) could not be resolved. +func (rm *resourceManager) ResolveReferences( + ctx context.Context, + apiReader client.Reader, + res acktypes.AWSResource, +) (acktypes.AWSResource, bool, error) { + return res, false, nil +} + +// validateReferenceFields validates the reference field and corresponding +// identifier field. +func validateReferenceFields(ko *svcapitypes.Project) error { + return nil +} diff --git a/pkg/resource/project/resource.go b/pkg/resource/project/resource.go new file mode 100644 index 00000000..12b21835 --- /dev/null +++ b/pkg/resource/project/resource.go @@ -0,0 +1,113 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file 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 project + +import ( + "fmt" + + ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" + ackerrors "github.com/aws-controllers-k8s/runtime/pkg/errors" + acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + rtclient "sigs.k8s.io/controller-runtime/pkg/client" + + svcapitypes "github.com/aws-controllers-k8s/sagemaker-controller/apis/v1alpha1" +) + +// Hack to avoid import errors during build... +var ( + _ = &ackerrors.MissingNameIdentifier +) + +// resource implements the `aws-controller-k8s/runtime/pkg/types.AWSResource` +// interface +type resource struct { + // The Kubernetes-native CR representing the resource + ko *svcapitypes.Project +} + +// Identifiers returns an AWSResourceIdentifiers object containing various +// identifying information, including the AWS account ID that owns the +// resource, the resource's AWS Resource Name (ARN) +func (r *resource) Identifiers() acktypes.AWSResourceIdentifiers { + return &resourceIdentifiers{r.ko.Status.ACKResourceMetadata} +} + +// IsBeingDeleted returns true if the Kubernetes resource has a non-zero +// deletion timestamp +func (r *resource) IsBeingDeleted() bool { + return !r.ko.DeletionTimestamp.IsZero() +} + +// RuntimeObject returns the Kubernetes apimachinery/runtime representation of +// the AWSResource +func (r *resource) RuntimeObject() rtclient.Object { + return r.ko +} + +// MetaObject returns the Kubernetes apimachinery/apis/meta/v1.Object +// representation of the AWSResource +func (r *resource) MetaObject() metav1.Object { + return r.ko.GetObjectMeta() +} + +// Conditions returns the ACK Conditions collection for the AWSResource +func (r *resource) Conditions() []*ackv1alpha1.Condition { + return r.ko.Status.Conditions +} + +// ReplaceConditions sets the Conditions status field for the resource +func (r *resource) ReplaceConditions(conditions []*ackv1alpha1.Condition) { + r.ko.Status.Conditions = conditions +} + +// SetObjectMeta sets the ObjectMeta field for the resource +func (r *resource) SetObjectMeta(meta metav1.ObjectMeta) { + r.ko.ObjectMeta = meta +} + +// SetStatus will set the Status field for the resource +func (r *resource) SetStatus(desired acktypes.AWSResource) { + r.ko.Status = desired.(*resource).ko.Status +} + +// SetIdentifiers sets the Spec or Status field that is referenced as the unique +// resource identifier +func (r *resource) SetIdentifiers(identifier *ackv1alpha1.AWSIdentifiers) error { + if identifier.NameOrID == "" { + return ackerrors.MissingNameIdentifier + } + r.ko.Spec.ProjectName = &identifier.NameOrID + + return nil +} + +// PopulateResourceFromAnnotation populates the fields passed from adoption annotation +func (r *resource) PopulateResourceFromAnnotation(fields map[string]string) error { + f0, ok := fields["projectName"] + if !ok { + return ackerrors.NewTerminalError(fmt.Errorf("required field missing: projectName")) + } + r.ko.Spec.ProjectName = &f0 + + return nil +} + +// DeepCopy will return a copy of the resource +func (r *resource) DeepCopy() acktypes.AWSResource { + koCopy := r.ko.DeepCopy() + return &resource{koCopy} +} diff --git a/pkg/resource/project/sdk.go b/pkg/resource/project/sdk.go new file mode 100644 index 00000000..9a8d3d69 --- /dev/null +++ b/pkg/resource/project/sdk.go @@ -0,0 +1,661 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file 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 project + +import ( + "context" + "errors" + "fmt" + "reflect" + "strings" + + ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" + ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" + ackcondition "github.com/aws-controllers-k8s/runtime/pkg/condition" + ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors" + ackrequeue "github.com/aws-controllers-k8s/runtime/pkg/requeue" + ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log" + "github.com/aws/aws-sdk-go-v2/aws" + svcsdk "github.com/aws/aws-sdk-go-v2/service/sagemaker" + svcsdktypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types" + smithy "github.com/aws/smithy-go" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + svcapitypes "github.com/aws-controllers-k8s/sagemaker-controller/apis/v1alpha1" +) + +// Hack to avoid import errors during build... +var ( + _ = &metav1.Time{} + _ = strings.ToLower("") + _ = &svcsdk.Client{} + _ = &svcapitypes.Project{} + _ = ackv1alpha1.AWSAccountID("") + _ = &ackerr.NotFound + _ = &ackcondition.NotManagedMessage + _ = &reflect.Value{} + _ = fmt.Sprintf("") + _ = &ackrequeue.NoRequeue{} + _ = &aws.Config{} +) + +// sdkFind returns SDK-specific information about a supplied resource +func (rm *resourceManager) sdkFind( + ctx context.Context, + r *resource, +) (latest *resource, err error) { + rlog := ackrtlog.FromContext(ctx) + exit := rlog.Trace("rm.sdkFind") + defer func() { + exit(err) + }() + // If any required fields in the input shape are missing, AWS resource is + // not created yet. Return NotFound here to indicate to callers that the + // resource isn't yet created. + if rm.requiredFieldsMissingFromReadOneInput(r) { + return nil, ackerr.NotFound + } + + input, err := rm.newDescribeRequestPayload(r) + if err != nil { + return nil, err + } + + var resp *svcsdk.DescribeProjectOutput + resp, err = rm.sdkapi.DescribeProject(ctx, input) + rm.metrics.RecordAPICall("READ_ONE", "DescribeProject", err) + if err != nil { + var awsErr smithy.APIError + if errors.As(err, &awsErr) && awsErr.ErrorCode() == "ResourceNotFound" { + return nil, ackerr.NotFound + } + return nil, err + } + + // Merge in the information we read from the API call above to the copy of + // the original Kubernetes object we passed to the function + ko := r.ko.DeepCopy() + + if resp.CreationTime != nil { + ko.Status.CreationTime = &metav1.Time{*resp.CreationTime} + } else { + ko.Status.CreationTime = nil + } + if ko.Status.ACKResourceMetadata == nil { + ko.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{} + } + if resp.ProjectArn != nil { + arn := ackv1alpha1.AWSResourceName(*resp.ProjectArn) + ko.Status.ACKResourceMetadata.ARN = &arn + } + if resp.ProjectDescription != nil { + ko.Spec.ProjectDescription = resp.ProjectDescription + } else { + ko.Spec.ProjectDescription = nil + } + if resp.ProjectId != nil { + ko.Status.ProjectID = resp.ProjectId + } else { + ko.Status.ProjectID = nil + } + if resp.ProjectName != nil { + ko.Spec.ProjectName = resp.ProjectName + } else { + ko.Spec.ProjectName = nil + } + if resp.ProjectStatus != "" { + ko.Status.ProjectStatus = aws.String(string(resp.ProjectStatus)) + } else { + ko.Status.ProjectStatus = nil + } + if resp.ServiceCatalogProvisionedProductDetails != nil { + f9 := &svcapitypes.ServiceCatalogProvisionedProductDetails{} + if resp.ServiceCatalogProvisionedProductDetails.ProvisionedProductId != nil { + f9.ProvisionedProductID = resp.ServiceCatalogProvisionedProductDetails.ProvisionedProductId + } + if resp.ServiceCatalogProvisionedProductDetails.ProvisionedProductStatusMessage != nil { + f9.ProvisionedProductStatusMessage = resp.ServiceCatalogProvisionedProductDetails.ProvisionedProductStatusMessage + } + ko.Status.ServiceCatalogProvisionedProductDetails = f9 + } else { + ko.Status.ServiceCatalogProvisionedProductDetails = nil + } + if resp.ServiceCatalogProvisioningDetails != nil { + f10 := &svcapitypes.ServiceCatalogProvisioningDetails{} + if resp.ServiceCatalogProvisioningDetails.PathId != nil { + f10.PathID = resp.ServiceCatalogProvisioningDetails.PathId + } + if resp.ServiceCatalogProvisioningDetails.ProductId != nil { + f10.ProductID = resp.ServiceCatalogProvisioningDetails.ProductId + } + if resp.ServiceCatalogProvisioningDetails.ProvisioningArtifactId != nil { + f10.ProvisioningArtifactID = resp.ServiceCatalogProvisioningDetails.ProvisioningArtifactId + } + if resp.ServiceCatalogProvisioningDetails.ProvisioningParameters != nil { + f10f3 := []*svcapitypes.ProvisioningParameter{} + for _, f10f3iter := range resp.ServiceCatalogProvisioningDetails.ProvisioningParameters { + f10f3elem := &svcapitypes.ProvisioningParameter{} + if f10f3iter.Key != nil { + f10f3elem.Key = f10f3iter.Key + } + if f10f3iter.Value != nil { + f10f3elem.Value = f10f3iter.Value + } + f10f3 = append(f10f3, f10f3elem) + } + f10.ProvisioningParameters = f10f3 + } + ko.Spec.ServiceCatalogProvisioningDetails = f10 + } else { + ko.Spec.ServiceCatalogProvisioningDetails = nil + } + if resp.TemplateProviderDetails != nil { + f11 := []*svcapitypes.TemplateProviderDetail{} + for _, f11iter := range resp.TemplateProviderDetails { + f11elem := &svcapitypes.TemplateProviderDetail{} + if f11iter.CfnTemplateProviderDetail != nil { + f11elemf0 := &svcapitypes.CfnTemplateProviderDetail{} + if f11iter.CfnTemplateProviderDetail.Parameters != nil { + f11elemf0f0 := []*svcapitypes.CfnStackParameter{} + for _, f11elemf0f0iter := range f11iter.CfnTemplateProviderDetail.Parameters { + f11elemf0f0elem := &svcapitypes.CfnStackParameter{} + if f11elemf0f0iter.Key != nil { + f11elemf0f0elem.Key = f11elemf0f0iter.Key + } + if f11elemf0f0iter.Value != nil { + f11elemf0f0elem.Value = f11elemf0f0iter.Value + } + f11elemf0f0 = append(f11elemf0f0, f11elemf0f0elem) + } + f11elemf0.Parameters = f11elemf0f0 + } + if f11iter.CfnTemplateProviderDetail.RoleARN != nil { + f11elemf0.RoleARN = f11iter.CfnTemplateProviderDetail.RoleARN + } + if f11iter.CfnTemplateProviderDetail.StackDetail != nil { + f11elemf0f2 := &svcapitypes.CfnStackDetail{} + if f11iter.CfnTemplateProviderDetail.StackDetail.Id != nil { + f11elemf0f2.ID = f11iter.CfnTemplateProviderDetail.StackDetail.Id + } + if f11iter.CfnTemplateProviderDetail.StackDetail.Name != nil { + f11elemf0f2.Name = f11iter.CfnTemplateProviderDetail.StackDetail.Name + } + if f11iter.CfnTemplateProviderDetail.StackDetail.StatusMessage != nil { + f11elemf0f2.StatusMessage = f11iter.CfnTemplateProviderDetail.StackDetail.StatusMessage + } + f11elemf0.StackDetail = f11elemf0f2 + } + if f11iter.CfnTemplateProviderDetail.TemplateName != nil { + f11elemf0.TemplateName = f11iter.CfnTemplateProviderDetail.TemplateName + } + if f11iter.CfnTemplateProviderDetail.TemplateURL != nil { + f11elemf0.TemplateURL = f11iter.CfnTemplateProviderDetail.TemplateURL + } + f11elem.CfnTemplateProviderDetail = f11elemf0 + } + f11 = append(f11, f11elem) + } + ko.Status.TemplateProviderDetails = f11 + } else { + ko.Status.TemplateProviderDetails = nil + } + + rm.setStatusDefaults(ko) + return &resource{ko}, nil +} + +// requiredFieldsMissingFromReadOneInput returns true if there are any fields +// for the ReadOne Input shape that are required but not present in the +// resource's Spec or Status +func (rm *resourceManager) requiredFieldsMissingFromReadOneInput( + r *resource, +) bool { + return r.ko.Spec.ProjectName == nil + +} + +// newDescribeRequestPayload returns SDK-specific struct for the HTTP request +// payload of the Describe API call for the resource +func (rm *resourceManager) newDescribeRequestPayload( + r *resource, +) (*svcsdk.DescribeProjectInput, error) { + res := &svcsdk.DescribeProjectInput{} + + if r.ko.Spec.ProjectName != nil { + res.ProjectName = r.ko.Spec.ProjectName + } + + return res, nil +} + +// sdkCreate creates the supplied resource in the backend AWS service API and +// returns a copy of the resource with resource fields (in both Spec and +// Status) filled in with values from the CREATE API operation's Output shape. +func (rm *resourceManager) sdkCreate( + ctx context.Context, + desired *resource, +) (created *resource, err error) { + rlog := ackrtlog.FromContext(ctx) + exit := rlog.Trace("rm.sdkCreate") + defer func() { + exit(err) + }() + input, err := rm.newCreateRequestPayload(ctx, desired) + if err != nil { + return nil, err + } + + var resp *svcsdk.CreateProjectOutput + _ = resp + resp, err = rm.sdkapi.CreateProject(ctx, input) + rm.metrics.RecordAPICall("CREATE", "CreateProject", err) + if err != nil { + return nil, err + } + // Merge in the information we read from the API call above to the copy of + // the original Kubernetes object we passed to the function + ko := desired.ko.DeepCopy() + + if ko.Status.ACKResourceMetadata == nil { + ko.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{} + } + if resp.ProjectArn != nil { + arn := ackv1alpha1.AWSResourceName(*resp.ProjectArn) + ko.Status.ACKResourceMetadata.ARN = &arn + } + if resp.ProjectId != nil { + ko.Status.ProjectID = resp.ProjectId + } else { + ko.Status.ProjectID = nil + } + + rm.setStatusDefaults(ko) + return &resource{ko}, nil +} + +// newCreateRequestPayload returns an SDK-specific struct for the HTTP request +// payload of the Create API call for the resource +func (rm *resourceManager) newCreateRequestPayload( + ctx context.Context, + r *resource, +) (*svcsdk.CreateProjectInput, error) { + res := &svcsdk.CreateProjectInput{} + + if r.ko.Spec.ProjectDescription != nil { + res.ProjectDescription = r.ko.Spec.ProjectDescription + } + if r.ko.Spec.ProjectName != nil { + res.ProjectName = r.ko.Spec.ProjectName + } + if r.ko.Spec.ServiceCatalogProvisioningDetails != nil { + f2 := &svcsdktypes.ServiceCatalogProvisioningDetails{} + if r.ko.Spec.ServiceCatalogProvisioningDetails.PathID != nil { + f2.PathId = r.ko.Spec.ServiceCatalogProvisioningDetails.PathID + } + if r.ko.Spec.ServiceCatalogProvisioningDetails.ProductID != nil { + f2.ProductId = r.ko.Spec.ServiceCatalogProvisioningDetails.ProductID + } + if r.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningArtifactID != nil { + f2.ProvisioningArtifactId = r.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningArtifactID + } + if r.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningParameters != nil { + f2f3 := []svcsdktypes.ProvisioningParameter{} + for _, f2f3iter := range r.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningParameters { + f2f3elem := &svcsdktypes.ProvisioningParameter{} + if f2f3iter.Key != nil { + f2f3elem.Key = f2f3iter.Key + } + if f2f3iter.Value != nil { + f2f3elem.Value = f2f3iter.Value + } + f2f3 = append(f2f3, *f2f3elem) + } + f2.ProvisioningParameters = f2f3 + } + res.ServiceCatalogProvisioningDetails = f2 + } + if r.ko.Spec.Tags != nil { + f3 := []svcsdktypes.Tag{} + for _, f3iter := range r.ko.Spec.Tags { + f3elem := &svcsdktypes.Tag{} + if f3iter.Key != nil { + f3elem.Key = f3iter.Key + } + if f3iter.Value != nil { + f3elem.Value = f3iter.Value + } + f3 = append(f3, *f3elem) + } + res.Tags = f3 + } + if r.ko.Spec.TemplateProviders != nil { + f4 := []svcsdktypes.CreateTemplateProvider{} + for _, f4iter := range r.ko.Spec.TemplateProviders { + f4elem := &svcsdktypes.CreateTemplateProvider{} + if f4iter.CfnTemplateProvider != nil { + f4elemf0 := &svcsdktypes.CfnCreateTemplateProvider{} + if f4iter.CfnTemplateProvider.Parameters != nil { + f4elemf0f0 := []svcsdktypes.CfnStackCreateParameter{} + for _, f4elemf0f0iter := range f4iter.CfnTemplateProvider.Parameters { + f4elemf0f0elem := &svcsdktypes.CfnStackCreateParameter{} + if f4elemf0f0iter.Key != nil { + f4elemf0f0elem.Key = f4elemf0f0iter.Key + } + if f4elemf0f0iter.Value != nil { + f4elemf0f0elem.Value = f4elemf0f0iter.Value + } + f4elemf0f0 = append(f4elemf0f0, *f4elemf0f0elem) + } + f4elemf0.Parameters = f4elemf0f0 + } + if f4iter.CfnTemplateProvider.RoleARN != nil { + f4elemf0.RoleARN = f4iter.CfnTemplateProvider.RoleARN + } + if f4iter.CfnTemplateProvider.TemplateName != nil { + f4elemf0.TemplateName = f4iter.CfnTemplateProvider.TemplateName + } + if f4iter.CfnTemplateProvider.TemplateURL != nil { + f4elemf0.TemplateURL = f4iter.CfnTemplateProvider.TemplateURL + } + f4elem.CfnTemplateProvider = f4elemf0 + } + f4 = append(f4, *f4elem) + } + res.TemplateProviders = f4 + } + + return res, nil +} + +// sdkUpdate patches the supplied resource in the backend AWS service API and +// returns a new resource with updated fields. +func (rm *resourceManager) sdkUpdate( + ctx context.Context, + desired *resource, + latest *resource, + delta *ackcompare.Delta, +) (updated *resource, err error) { + rlog := ackrtlog.FromContext(ctx) + exit := rlog.Trace("rm.sdkUpdate") + defer func() { + exit(err) + }() + input, err := rm.newUpdateRequestPayload(ctx, desired, delta) + if err != nil { + return nil, err + } + + var resp *svcsdk.UpdateProjectOutput + _ = resp + resp, err = rm.sdkapi.UpdateProject(ctx, input) + rm.metrics.RecordAPICall("UPDATE", "UpdateProject", err) + if err != nil { + return nil, err + } + // Merge in the information we read from the API call above to the copy of + // the original Kubernetes object we passed to the function + ko := desired.ko.DeepCopy() + + if ko.Status.ACKResourceMetadata == nil { + ko.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{} + } + if resp.ProjectArn != nil { + arn := ackv1alpha1.AWSResourceName(*resp.ProjectArn) + ko.Status.ACKResourceMetadata.ARN = &arn + } + + rm.setStatusDefaults(ko) + return &resource{ko}, nil +} + +// newUpdateRequestPayload returns an SDK-specific struct for the HTTP request +// payload of the Update API call for the resource +func (rm *resourceManager) newUpdateRequestPayload( + ctx context.Context, + r *resource, + delta *ackcompare.Delta, +) (*svcsdk.UpdateProjectInput, error) { + res := &svcsdk.UpdateProjectInput{} + + if r.ko.Spec.ProjectDescription != nil { + res.ProjectDescription = r.ko.Spec.ProjectDescription + } + if r.ko.Spec.ProjectName != nil { + res.ProjectName = r.ko.Spec.ProjectName + } + if r.ko.Spec.ServiceCatalogProvisioningDetails != nil { + f2 := &svcsdktypes.ServiceCatalogProvisioningUpdateDetails{} + if r.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningArtifactID != nil { + f2.ProvisioningArtifactId = r.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningArtifactID + } + if r.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningParameters != nil { + f2f1 := []svcsdktypes.ProvisioningParameter{} + for _, f2f1iter := range r.ko.Spec.ServiceCatalogProvisioningDetails.ProvisioningParameters { + f2f1elem := &svcsdktypes.ProvisioningParameter{} + if f2f1iter.Key != nil { + f2f1elem.Key = f2f1iter.Key + } + if f2f1iter.Value != nil { + f2f1elem.Value = f2f1iter.Value + } + f2f1 = append(f2f1, *f2f1elem) + } + f2.ProvisioningParameters = f2f1 + } + res.ServiceCatalogProvisioningUpdateDetails = f2 + } + if r.ko.Spec.Tags != nil { + f3 := []svcsdktypes.Tag{} + for _, f3iter := range r.ko.Spec.Tags { + f3elem := &svcsdktypes.Tag{} + if f3iter.Key != nil { + f3elem.Key = f3iter.Key + } + if f3iter.Value != nil { + f3elem.Value = f3iter.Value + } + f3 = append(f3, *f3elem) + } + res.Tags = f3 + } + if r.ko.Spec.TemplateProviders != nil { + f4 := []svcsdktypes.UpdateTemplateProvider{} + for _, f4iter := range r.ko.Spec.TemplateProviders { + f4elem := &svcsdktypes.UpdateTemplateProvider{} + if f4iter.CfnTemplateProvider != nil { + f4elemf0 := &svcsdktypes.CfnUpdateTemplateProvider{} + if f4iter.CfnTemplateProvider.Parameters != nil { + f4elemf0f0 := []svcsdktypes.CfnStackUpdateParameter{} + for _, f4elemf0f0iter := range f4iter.CfnTemplateProvider.Parameters { + f4elemf0f0elem := &svcsdktypes.CfnStackUpdateParameter{} + if f4elemf0f0iter.Key != nil { + f4elemf0f0elem.Key = f4elemf0f0iter.Key + } + if f4elemf0f0iter.Value != nil { + f4elemf0f0elem.Value = f4elemf0f0iter.Value + } + f4elemf0f0 = append(f4elemf0f0, *f4elemf0f0elem) + } + f4elemf0.Parameters = f4elemf0f0 + } + if f4iter.CfnTemplateProvider.TemplateName != nil { + f4elemf0.TemplateName = f4iter.CfnTemplateProvider.TemplateName + } + if f4iter.CfnTemplateProvider.TemplateURL != nil { + f4elemf0.TemplateURL = f4iter.CfnTemplateProvider.TemplateURL + } + f4elem.CfnTemplateProvider = f4elemf0 + } + f4 = append(f4, *f4elem) + } + res.TemplateProvidersToUpdate = f4 + } + + return res, nil +} + +// sdkDelete deletes the supplied resource in the backend AWS service API +func (rm *resourceManager) sdkDelete( + ctx context.Context, + r *resource, +) (latest *resource, err error) { + rlog := ackrtlog.FromContext(ctx) + exit := rlog.Trace("rm.sdkDelete") + defer func() { + exit(err) + }() + input, err := rm.newDeleteRequestPayload(r) + if err != nil { + return nil, err + } + var resp *svcsdk.DeleteProjectOutput + _ = resp + resp, err = rm.sdkapi.DeleteProject(ctx, input) + rm.metrics.RecordAPICall("DELETE", "DeleteProject", err) + return nil, err +} + +// newDeleteRequestPayload returns an SDK-specific struct for the HTTP request +// payload of the Delete API call for the resource +func (rm *resourceManager) newDeleteRequestPayload( + r *resource, +) (*svcsdk.DeleteProjectInput, error) { + res := &svcsdk.DeleteProjectInput{} + + if r.ko.Spec.ProjectName != nil { + res.ProjectName = r.ko.Spec.ProjectName + } + + return res, nil +} + +// setStatusDefaults sets default properties into supplied custom resource +func (rm *resourceManager) setStatusDefaults( + ko *svcapitypes.Project, +) { + if ko.Status.ACKResourceMetadata == nil { + ko.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{} + } + if ko.Status.ACKResourceMetadata.Region == nil { + ko.Status.ACKResourceMetadata.Region = &rm.awsRegion + } + if ko.Status.ACKResourceMetadata.OwnerAccountID == nil { + ko.Status.ACKResourceMetadata.OwnerAccountID = &rm.awsAccountID + } + if ko.Status.Conditions == nil { + ko.Status.Conditions = []*ackv1alpha1.Condition{} + } +} + +// updateConditions returns updated resource, true; if conditions were updated +// else it returns nil, false +func (rm *resourceManager) updateConditions( + r *resource, + onSuccess bool, + err error, +) (*resource, bool) { + ko := r.ko.DeepCopy() + rm.setStatusDefaults(ko) + + // Terminal condition + var terminalCondition *ackv1alpha1.Condition = nil + var recoverableCondition *ackv1alpha1.Condition = nil + var syncCondition *ackv1alpha1.Condition = nil + for _, condition := range ko.Status.Conditions { + if condition.Type == ackv1alpha1.ConditionTypeTerminal { + terminalCondition = condition + } + if condition.Type == ackv1alpha1.ConditionTypeRecoverable { + recoverableCondition = condition + } + if condition.Type == ackv1alpha1.ConditionTypeResourceSynced { + syncCondition = condition + } + } + var termError *ackerr.TerminalError + if rm.terminalAWSError(err) || err == ackerr.SecretTypeNotSupported || err == ackerr.SecretNotFound || errors.As(err, &termError) { + if terminalCondition == nil { + terminalCondition = &ackv1alpha1.Condition{ + Type: ackv1alpha1.ConditionTypeTerminal, + } + ko.Status.Conditions = append(ko.Status.Conditions, terminalCondition) + } + var errorMessage = "" + if err == ackerr.SecretTypeNotSupported || err == ackerr.SecretNotFound || errors.As(err, &termError) { + errorMessage = err.Error() + } else { + awsErr, _ := ackerr.AWSError(err) + errorMessage = awsErr.Error() + } + terminalCondition.Status = corev1.ConditionTrue + terminalCondition.Message = &errorMessage + } else { + // Clear the terminal condition if no longer present + if terminalCondition != nil { + terminalCondition.Status = corev1.ConditionFalse + terminalCondition.Message = nil + } + // Handling Recoverable Conditions + if err != nil { + if recoverableCondition == nil { + // Add a new Condition containing a non-terminal error + recoverableCondition = &ackv1alpha1.Condition{ + Type: ackv1alpha1.ConditionTypeRecoverable, + } + ko.Status.Conditions = append(ko.Status.Conditions, recoverableCondition) + } + recoverableCondition.Status = corev1.ConditionTrue + awsErr, _ := ackerr.AWSError(err) + errorMessage := err.Error() + if awsErr != nil { + errorMessage = awsErr.Error() + } + recoverableCondition.Message = &errorMessage + } else if recoverableCondition != nil { + recoverableCondition.Status = corev1.ConditionFalse + recoverableCondition.Message = nil + } + } + // Required to avoid the "declared but not used" error in the default case + _ = syncCondition + if terminalCondition != nil || recoverableCondition != nil || syncCondition != nil { + return &resource{ko}, true // updated + } + return nil, false // not updated +} + +// terminalAWSError returns awserr, true; if the supplied error is an aws Error type +// and if the exception indicates that it is a Terminal exception +// 'Terminal' exception are specified in generator configuration +func (rm *resourceManager) terminalAWSError(err error) bool { + if err == nil { + return false + } + + var terminalErr smithy.APIError + if !errors.As(err, &terminalErr) { + return false + } + switch terminalErr.ErrorCode() { + case "InvalidParameterCombination", + "InvalidParameterValue", + "MissingParameter": + return true + default: + return false + } +} diff --git a/pkg/resource/project/tags.go b/pkg/resource/project/tags.go new file mode 100644 index 00000000..2e55421e --- /dev/null +++ b/pkg/resource/project/tags.go @@ -0,0 +1,119 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file 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 project + +import ( + "slices" + "strings" + + acktags "github.com/aws-controllers-k8s/runtime/pkg/tags" + + svcapitypes "github.com/aws-controllers-k8s/sagemaker-controller/apis/v1alpha1" +) + +var ( + _ = svcapitypes.Project{} + _ = acktags.NewTags() + ACKSystemTags = []string{"services.k8s.aws/namespace", "services.k8s.aws/controller-version"} +) + +// convertToOrderedACKTags converts the tags parameter into 'acktags.Tags' shape. +// This method helps in creating the hub(acktags.Tags) for merging +// default controller tags with existing resource tags. It also returns a slice +// of keys maintaining the original key Order when the tags are a list +func convertToOrderedACKTags(tags []*svcapitypes.Tag) (acktags.Tags, []string) { + result := acktags.NewTags() + keyOrder := []string{} + + if len(tags) == 0 { + return result, keyOrder + } + for _, t := range tags { + if t.Key != nil { + keyOrder = append(keyOrder, *t.Key) + if t.Value != nil { + result[*t.Key] = *t.Value + } else { + result[*t.Key] = "" + } + } + } + + return result, keyOrder +} + +// fromACKTags converts the tags parameter into []*svcapitypes.Tag shape. +// This method helps in setting the tags back inside AWSResource after merging +// default controller tags with existing resource tags. When a list, +// it maintains the order from original +func fromACKTags(tags acktags.Tags, keyOrder []string) []*svcapitypes.Tag { + result := []*svcapitypes.Tag{} + + for _, k := range keyOrder { + v, ok := tags[k] + if ok { + tag := svcapitypes.Tag{Key: &k, Value: &v} + result = append(result, &tag) + delete(tags, k) + } + } + for k, v := range tags { + tag := svcapitypes.Tag{Key: &k, Value: &v} + result = append(result, &tag) + } + + return result +} + +// ignoreSystemTags ignores tags that have keys that start with "aws:" +// and ACKSystemTags, to avoid patching them to the resourceSpec. +// Eg. resources created with cloudformation have tags that cannot be +// removed by an ACK controller +func ignoreSystemTags(tags acktags.Tags) { + for k := range tags { + if strings.HasPrefix(k, "aws:") || + slices.Contains(ACKSystemTags, k) { + delete(tags, k) + } + } +} + +// syncAWSTags ensures AWS-managed tags (prefixed with "aws:") from the latest resource state +// are preserved in the desired state. This prevents the controller from attempting to +// modify AWS-managed tags, which would result in an error. +// +// AWS-managed tags are automatically added by AWS services (e.g., CloudFormation, Service Catalog) +// and cannot be modified or deleted through normal tag operations. Common examples include: +// - aws:cloudformation:stack-name +// - aws:servicecatalog:productArn +// +// Parameters: +// - a: The target Tags map to be updated (typically desired state) +// - b: The source Tags map containing AWS-managed tags (typically latest state) +// +// Example: +// +// latest := Tags{"aws:cloudformation:stack-name": "my-stack", "environment": "prod"} +// desired := Tags{"environment": "dev"} +// SyncAWSTags(desired, latest) +// desired now contains {"aws:cloudformation:stack-name": "my-stack", "environment": "dev"} +func syncAWSTags(a acktags.Tags, b acktags.Tags) { + for k := range b { + if strings.HasPrefix(k, "aws:") { + a[k] = b[k] + } + } +}