diff --git a/Makefile b/Makefile index 06eae3f99c..070b0970a1 100644 --- a/Makefile +++ b/Makefile @@ -190,6 +190,7 @@ defaulters: $(DEFAULTER_GEN) ## Generate all Go types $(DEFAULTER_GEN) \ --input-dirs=./api/v1beta2 \ --input-dirs=./$(EXP_DIR)/api/v1beta2 \ + --input-dirs=./controlplane/rosa/api/v1beta2 \ --input-dirs=./cmd/clusterawsadm/api/bootstrap/v1beta1 \ --input-dirs=./cmd/clusterawsadm/api/bootstrap/v1alpha1 \ --extra-peer-dirs=sigs.k8s.io/cluster-api/api/v1beta1 \ diff --git a/api/v1beta1/awscluster_types.go b/api/v1beta1/awscluster_types.go index 02a17346a0..0e06987b4b 100644 --- a/api/v1beta1/awscluster_types.go +++ b/api/v1beta1/awscluster_types.go @@ -87,8 +87,10 @@ type AWSClusterSpec struct { // +optional Bastion Bastion `json:"bastion"` - // IdentityRef is a reference to a identity to be used when reconciling this cluster // +optional + + // IdentityRef is a reference to an identity to be used when reconciling the managed control plane. + // If no identity is specified, the default identity for this controller will be used. IdentityRef *AWSIdentityReference `json:"identityRef,omitempty"` // S3Bucket contains options to configure a supporting S3 bucket for this diff --git a/api/v1beta2/awscluster_types.go b/api/v1beta2/awscluster_types.go index 8043186085..1df6c53b89 100644 --- a/api/v1beta2/awscluster_types.go +++ b/api/v1beta2/awscluster_types.go @@ -99,8 +99,10 @@ type AWSClusterSpec struct { // +optional Bastion Bastion `json:"bastion"` - // IdentityRef is a reference to a identity to be used when reconciling this cluster // +optional + + // IdentityRef is a reference to an identity to be used when reconciling the managed control plane. + // If no identity is specified, the default identity for this controller will be used. IdentityRef *AWSIdentityReference `json:"identityRef,omitempty"` // S3Bucket contains options to configure a supporting S3 bucket for this diff --git a/config/crd/bases/controlplane.cluster.x-k8s.io_awsmanagedcontrolplanes.yaml b/config/crd/bases/controlplane.cluster.x-k8s.io_awsmanagedcontrolplanes.yaml index 6e95bfcb48..3227df4d81 100644 --- a/config/crd/bases/controlplane.cluster.x-k8s.io_awsmanagedcontrolplanes.yaml +++ b/config/crd/bases/controlplane.cluster.x-k8s.io_awsmanagedcontrolplanes.yaml @@ -258,8 +258,9 @@ spec: type: array type: object identityRef: - description: IdentityRef is a reference to a identity to be used when - reconciling the managed control plane. + description: IdentityRef is a reference to an identity to be used + when reconciling the managed control plane. If no identity is specified, + the default identity for this controller will be used. properties: kind: description: Kind of the identity. @@ -2096,8 +2097,9 @@ spec: type: array type: object identityRef: - description: IdentityRef is a reference to a identity to be used when - reconciling the managed control plane. + description: IdentityRef is a reference to an identity to be used + when reconciling the managed control plane. If no identity is specified, + the default identity for this controller will be used. properties: kind: description: Kind of the identity. diff --git a/config/crd/bases/controlplane.cluster.x-k8s.io_rosacontrolplanes.yaml b/config/crd/bases/controlplane.cluster.x-k8s.io_rosacontrolplanes.yaml index db89c2c86b..629a1d09d0 100644 --- a/config/crd/bases/controlplane.cluster.x-k8s.io_rosacontrolplanes.yaml +++ b/config/crd/bases/controlplane.cluster.x-k8s.io_rosacontrolplanes.yaml @@ -45,10 +45,6 @@ spec: type: object spec: properties: - accountID: - description: 'TODO: these are to satisfy ocm sdk. Explore how to drop - them.' - type: string availabilityZones: description: AWS AvailabilityZones of the worker nodes should match the AvailabilityZones of the Subnets. @@ -70,8 +66,6 @@ spec: - host - port type: object - creatorARN: - type: string credentialsSecretRef: description: 'CredentialsSecretRef references a secret with necessary credentials to connect to the OCM API. The secret should contain @@ -84,7 +78,29 @@ spec: type: string type: object x-kubernetes-map-type: atomic + identityRef: + description: IdentityRef is a reference to an identity to be used + when reconciling the managed control plane. If no identity is specified, + the default identity for this controller will be used. + properties: + kind: + description: Kind of the identity. + enum: + - AWSClusterControllerIdentity + - AWSClusterRoleIdentity + - AWSClusterStaticIdentity + type: string + name: + description: Name of the identity. + minLength: 1 + type: string + required: + - kind + - name + type: object installerRoleARN: + description: 'TODO: these are to satisfy ocm sdk. Explore how to drop + them.' type: string machineCIDR: description: Block of IP addresses used by OpenShift while installing @@ -276,9 +292,7 @@ spec: workerRoleARN: type: string required: - - accountID - availabilityZones - - creatorARN - installerRoleARN - machineCIDR - oidcID diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml index ebdddcd307..a74b9c7e82 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml @@ -167,8 +167,9 @@ spec: type: array type: object identityRef: - description: IdentityRef is a reference to a identity to be used when - reconciling this cluster + description: IdentityRef is a reference to an identity to be used + when reconciling the managed control plane. If no identity is specified, + the default identity for this controller will be used. properties: kind: description: Kind of the identity. @@ -1142,8 +1143,9 @@ spec: type: array type: object identityRef: - description: IdentityRef is a reference to a identity to be used when - reconciling this cluster + description: IdentityRef is a reference to an identity to be used + when reconciling the managed control plane. If no identity is specified, + the default identity for this controller will be used. properties: kind: description: Kind of the identity. diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclustertemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclustertemplates.yaml index 7433d63cb9..df369e0c2d 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclustertemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclustertemplates.yaml @@ -183,8 +183,10 @@ spec: type: array type: object identityRef: - description: IdentityRef is a reference to a identity to be - used when reconciling this cluster + description: IdentityRef is a reference to an identity to + be used when reconciling the managed control plane. If no + identity is specified, the default identity for this controller + will be used. properties: kind: description: Kind of the identity. @@ -742,8 +744,10 @@ spec: type: array type: object identityRef: - description: IdentityRef is a reference to a identity to be - used when reconciling this cluster + description: IdentityRef is a reference to an identity to + be used when reconciling the managed control plane. If no + identity is specified, the default identity for this controller + will be used. properties: kind: description: Kind of the identity. diff --git a/controllers/rosacluster_controller.go b/controllers/rosacluster_controller.go index 7e92841e07..e57cb7402a 100644 --- a/controllers/rosacluster_controller.go +++ b/controllers/rosacluster_controller.go @@ -35,6 +35,7 @@ import ( infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2" rosacontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/v2/controlplane/rosa/api/v1beta2" expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/exp/api/v1beta2" + "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/scope" "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" "sigs.k8s.io/cluster-api/util" @@ -48,6 +49,7 @@ type ROSAClusterReconciler struct { client.Client Recorder record.EventRecorder WatchFilterValue string + Endpoints []scope.ServiceEndpoint } // +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=rosaclusters,verbs=get;list;watch;update;patch;delete diff --git a/controlplane/eks/api/v1beta1/awsmanagedcontrolplane_types.go b/controlplane/eks/api/v1beta1/awsmanagedcontrolplane_types.go index 93466d40b6..4f7fc33cc5 100644 --- a/controlplane/eks/api/v1beta1/awsmanagedcontrolplane_types.go +++ b/controlplane/eks/api/v1beta1/awsmanagedcontrolplane_types.go @@ -40,8 +40,10 @@ type AWSManagedControlPlaneSpec struct { //nolint: maligned // +optional EKSClusterName string `json:"eksClusterName,omitempty"` - // IdentityRef is a reference to a identity to be used when reconciling the managed control plane. // +optional + + // IdentityRef is a reference to an identity to be used when reconciling the managed control plane. + // If no identity is specified, the default identity for this controller will be used. IdentityRef *infrav1.AWSIdentityReference `json:"identityRef,omitempty"` // NetworkSpec encapsulates all things related to AWS network. diff --git a/controlplane/eks/api/v1beta2/awsmanagedcontrolplane_types.go b/controlplane/eks/api/v1beta2/awsmanagedcontrolplane_types.go index 3ca8ded16f..89d5e8bc2b 100644 --- a/controlplane/eks/api/v1beta2/awsmanagedcontrolplane_types.go +++ b/controlplane/eks/api/v1beta2/awsmanagedcontrolplane_types.go @@ -40,8 +40,10 @@ type AWSManagedControlPlaneSpec struct { //nolint: maligned // +optional EKSClusterName string `json:"eksClusterName,omitempty"` - // IdentityRef is a reference to a identity to be used when reconciling the managed control plane. // +optional + + // IdentityRef is a reference to an identity to be used when reconciling the managed control plane. + // If no identity is specified, the default identity for this controller will be used. IdentityRef *infrav1.AWSIdentityReference `json:"identityRef,omitempty"` // NetworkSpec encapsulates all things related to AWS network. diff --git a/controlplane/rosa/api/v1beta2/defaults.go b/controlplane/rosa/api/v1beta2/defaults.go new file mode 100644 index 0000000000..a2006137c1 --- /dev/null +++ b/controlplane/rosa/api/v1beta2/defaults.go @@ -0,0 +1,13 @@ +package v1beta2 + +import "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2" + +// SetDefaults_RosaControlPlaneSpec is used by defaulter-gen. +func SetDefaults_RosaControlPlaneSpec(s *RosaControlPlaneSpec) { //nolint:golint,stylecheck + if s.IdentityRef == nil { + s.IdentityRef = &v1beta2.AWSIdentityReference{ + Kind: v1beta2.ControllerIdentityKind, + Name: v1beta2.AWSClusterControllerIdentityName, + } + } +} diff --git a/controlplane/rosa/api/v1beta2/rosacontrolplane_types.go b/controlplane/rosa/api/v1beta2/rosacontrolplane_types.go index 185aa3a9fe..831adeaa90 100644 --- a/controlplane/rosa/api/v1beta2/rosacontrolplane_types.go +++ b/controlplane/rosa/api/v1beta2/rosacontrolplane_types.go @@ -20,6 +20,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" ) @@ -64,8 +65,6 @@ type RosaControlPlaneSpec struct { //nolint: maligned OIDCID *string `json:"oidcID"` // TODO: these are to satisfy ocm sdk. Explore how to drop them. - AccountID *string `json:"accountID"` - CreatorARN *string `json:"creatorARN"` InstallerRoleARN *string `json:"installerRoleARN"` SupportRoleARN *string `json:"supportRoleARN"` WorkerRoleARN *string `json:"workerRoleARN"` @@ -76,6 +75,12 @@ type RosaControlPlaneSpec struct { //nolint: maligned // - ocmApiUrl: Optional, defaults to 'https://api.openshift.com' // +optional CredentialsSecretRef *corev1.LocalObjectReference `json:"credentialsSecretRef,omitempty"` + + // +optional + + // IdentityRef is a reference to an identity to be used when reconciling the managed control plane. + // If no identity is specified, the default identity for this controller will be used. + IdentityRef *infrav1.AWSIdentityReference `json:"identityRef,omitempty"` } // AWSRolesRef contains references to various AWS IAM roles required for operators to make calls against the AWS API. @@ -489,6 +494,7 @@ type RosaControlPlaneStatus struct { // +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="Cluster",type="string",JSONPath=".metadata.labels.cluster\\.x-k8s\\.io/cluster-name",description="Cluster to which this RosaControl belongs" // +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.ready",description="Control plane infrastructure is ready for worker nodes" +// +k8s:defaulter-gen=true type ROSAControlPlane struct { metav1.TypeMeta `json:",inline"` diff --git a/controlplane/rosa/api/v1beta2/zz_generated.deepcopy.go b/controlplane/rosa/api/v1beta2/zz_generated.deepcopy.go index 017f4f60dd..bfb980ac14 100644 --- a/controlplane/rosa/api/v1beta2/zz_generated.deepcopy.go +++ b/controlplane/rosa/api/v1beta2/zz_generated.deepcopy.go @@ -23,6 +23,7 @@ package v1beta2 import ( "k8s.io/api/core/v1" runtime "k8s.io/apimachinery/pkg/runtime" + apiv1beta2 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2" "sigs.k8s.io/cluster-api/api/v1beta1" ) @@ -130,16 +131,6 @@ func (in *RosaControlPlaneSpec) DeepCopyInto(out *RosaControlPlaneSpec) { *out = new(string) **out = **in } - if in.AccountID != nil { - in, out := &in.AccountID, &out.AccountID - *out = new(string) - **out = **in - } - if in.CreatorARN != nil { - in, out := &in.CreatorARN, &out.CreatorARN - *out = new(string) - **out = **in - } if in.InstallerRoleARN != nil { in, out := &in.InstallerRoleARN, &out.InstallerRoleARN *out = new(string) @@ -160,6 +151,11 @@ func (in *RosaControlPlaneSpec) DeepCopyInto(out *RosaControlPlaneSpec) { *out = new(v1.LocalObjectReference) **out = **in } + if in.IdentityRef != nil { + in, out := &in.IdentityRef, &out.IdentityRef + *out = new(apiv1beta2.AWSIdentityReference) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RosaControlPlaneSpec. diff --git a/controlplane/rosa/api/v1beta2/zz_generated.defaults.go b/controlplane/rosa/api/v1beta2/zz_generated.defaults.go new file mode 100644 index 0000000000..510687638d --- /dev/null +++ b/controlplane/rosa/api/v1beta2/zz_generated.defaults.go @@ -0,0 +1,38 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by defaulter-gen. DO NOT EDIT. + +package v1beta2 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// RegisterDefaults adds defaulters functions to the given scheme. +// Public to allow building arbitrary schemes. +// All generated defaulters are covering - they call all nested defaulters. +func RegisterDefaults(scheme *runtime.Scheme) error { + scheme.AddTypeDefaultingFunc(&ROSAControlPlane{}, func(obj interface{}) { SetObjectDefaults_ROSAControlPlane(obj.(*ROSAControlPlane)) }) + return nil +} + +func SetObjectDefaults_ROSAControlPlane(in *ROSAControlPlane) { + SetDefaults_RosaControlPlaneSpec(&in.Spec) +} diff --git a/controlplane/rosa/controllers/rosacontrolplane_controller.go b/controlplane/rosa/controllers/rosacontrolplane_controller.go index a01289587d..00476fb538 100644 --- a/controlplane/rosa/controllers/rosacontrolplane_controller.go +++ b/controlplane/rosa/controllers/rosacontrolplane_controller.go @@ -68,6 +68,7 @@ type ROSAControlPlaneReconciler struct { client.Client WatchFilterValue string WaitInfraPeriod time.Duration + Endpoints []scope.ServiceEndpoint } // SetupWithManager is used to setup the controller. @@ -148,6 +149,7 @@ func (r *ROSAControlPlaneReconciler) Reconcile(ctx context.Context, req ctrl.Req Cluster: cluster, ControlPlane: rosaControlPlane, ControllerName: strings.ToLower(rosaControlPlaneKind), + Endpoints: r.Endpoints, Logger: log, }) if err != nil { @@ -344,8 +346,8 @@ func (r *ROSAControlPlaneReconciler) reconcileNormal(ctx context.Context, rosaSc stsBuilder.AutoMode(true) awsBuilder := cmv1.NewAWS(). - AccountID(*rosaScope.ControlPlane.Spec.AccountID). - BillingAccountID(*rosaScope.ControlPlane.Spec.AccountID). + AccountID(*rosaScope.Identity.Account). + BillingAccountID(*rosaScope.Identity.Account). SubnetIDs(rosaScope.ControlPlane.Spec.Subnets...). STS(stsBuilder) clusterBuilder = clusterBuilder.AWS(awsBuilder) @@ -355,7 +357,7 @@ func (r *ROSAControlPlaneReconciler) reconcileNormal(ctx context.Context, rosaSc clusterBuilder = clusterBuilder.Nodes(clusterNodesBuilder) clusterProperties := map[string]string{} - clusterProperties[rosaCreatorArnProperty] = *rosaScope.ControlPlane.Spec.CreatorARN + clusterProperties[rosaCreatorArnProperty] = *rosaScope.Identity.Arn clusterBuilder = clusterBuilder.Properties(clusterProperties) clusterSpec, err := clusterBuilder.Build() diff --git a/exp/controllers/rosamachinepool_controller.go b/exp/controllers/rosamachinepool_controller.go index 38a63f1b51..0bc668e4a8 100644 --- a/exp/controllers/rosamachinepool_controller.go +++ b/exp/controllers/rosamachinepool_controller.go @@ -37,6 +37,7 @@ type ROSAMachinePoolReconciler struct { client.Client Recorder record.EventRecorder WatchFilterValue string + Endpoints []scope.ServiceEndpoint } // SetupWithManager is used to setup the controller. diff --git a/main.go b/main.go index b27fa4ea11..32954e6dfd 100644 --- a/main.go +++ b/main.go @@ -227,6 +227,7 @@ func main() { Client: mgr.GetClient(), WatchFilterValue: watchFilterValue, WaitInfraPeriod: waitInfraPeriod, + Endpoints: awsServiceEndpoints, }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: awsClusterConcurrency, RecoverPanic: ptr.To[bool](true)}); err != nil { setupLog.Error(err, "unable to create controller", "controller", "ROSAControlPlane") os.Exit(1) @@ -237,6 +238,7 @@ func main() { Client: mgr.GetClient(), Recorder: mgr.GetEventRecorderFor("rosacluster-controller"), WatchFilterValue: watchFilterValue, + Endpoints: awsServiceEndpoints, }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: awsClusterConcurrency, RecoverPanic: ptr.To[bool](true)}); err != nil { setupLog.Error(err, "unable to create controller", "controller", "ROSACluster") os.Exit(1) @@ -247,6 +249,7 @@ func main() { Client: mgr.GetClient(), Recorder: mgr.GetEventRecorderFor("rosamachinepool-controller"), WatchFilterValue: watchFilterValue, + Endpoints: awsServiceEndpoints, }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: awsClusterConcurrency, RecoverPanic: ptr.To[bool](true)}); err != nil { setupLog.Error(err, "unable to create controller", "controller", "ROSAMachinePool") os.Exit(1) diff --git a/pkg/cloud/interfaces.go b/pkg/cloud/interfaces.go index a7f6609bdc..7d6115d429 100644 --- a/pkg/cloud/interfaces.go +++ b/pkg/cloud/interfaces.go @@ -84,3 +84,15 @@ type ClusterScoper interface { // Close closes the current scope persisting the cluster configuration and status. Close() error } + +// SessionMetadata knows how to extract the information for managing AWS sessions for a resource. +type SessionMetadata interface { + // Namespace returns the cluster namespace. + Namespace() string + // InfraClusterName returns the AWS infrastructure cluster name. + InfraClusterName() string + // InfraCluster returns the AWS infrastructure cluster object. + InfraCluster() ClusterObject + // IdentityRef returns the AWS infrastructure cluster identityRef. + IdentityRef() *infrav1.AWSIdentityReference +} diff --git a/pkg/cloud/scope/rosacontrolplane.go b/pkg/cloud/scope/rosacontrolplane.go index 5d2ee972f8..da4c36cb13 100644 --- a/pkg/cloud/scope/rosacontrolplane.go +++ b/pkg/cloud/scope/rosacontrolplane.go @@ -20,13 +20,18 @@ import ( "context" "fmt" + awsclient "github.com/aws/aws-sdk-go/aws/client" + "github.com/aws/aws-sdk-go/service/sts" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/client" + infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2" rosacontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/v2/controlplane/rosa/api/v1beta2" + "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud" + "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/throttle" "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" "sigs.k8s.io/cluster-api/util/patch" @@ -38,6 +43,7 @@ type ROSAControlPlaneScopeParams struct { Cluster *clusterv1.Cluster ControlPlane *rosacontrolplanev1.ROSAControlPlane ControllerName string + Endpoints []ServiceEndpoint } func NewROSAControlPlaneScope(params ROSAControlPlaneScopeParams) (*ROSAControlPlaneScope, error) { @@ -53,11 +59,17 @@ func NewROSAControlPlaneScope(params ROSAControlPlaneScopeParams) (*ROSAControlP } managedScope := &ROSAControlPlaneScope{ - Logger: *params.Logger, - Client: params.Client, - Cluster: params.Cluster, - ControlPlane: params.ControlPlane, - patchHelper: nil, + Logger: *params.Logger, + Client: params.Client, + Cluster: params.Cluster, + ControlPlane: params.ControlPlane, + patchHelper: nil, + controllerName: params.ControllerName, + } + + session, serviceLimiters, err := sessionForClusterWithRegion(params.Client, managedScope, *params.ControlPlane.Spec.Region, params.Endpoints, params.Logger) + if err != nil { + return nil, errors.Errorf("failed to create aws session: %v", err) } helper, err := patch.NewHelper(params.ControlPlane, params.Client) @@ -66,6 +78,16 @@ func NewROSAControlPlaneScope(params ROSAControlPlaneScopeParams) (*ROSAControlP } managedScope.patchHelper = helper + managedScope.session = session + managedScope.serviceLimiters = serviceLimiters + + stsClient := NewSTSClient(managedScope, managedScope, managedScope, managedScope.ControlPlane) + identity, err := stsClient.GetCallerIdentity(&sts.GetCallerIdentityInput{}) + if err != nil { + return nil, fmt.Errorf("failed to identify the AWS caller: %w", err) + } + managedScope.Identity = identity + return managedScope, nil } @@ -77,8 +99,40 @@ type ROSAControlPlaneScope struct { Cluster *clusterv1.Cluster ControlPlane *rosacontrolplanev1.ROSAControlPlane + + session awsclient.ConfigProvider + serviceLimiters throttle.ServiceLimiters + controllerName string + Identity *sts.GetCallerIdentityOutput +} + +func (s *ROSAControlPlaneScope) InfraCluster() cloud.ClusterObject { + return s.ControlPlane } +func (s *ROSAControlPlaneScope) IdentityRef() *infrav1.AWSIdentityReference { + return s.ControlPlane.Spec.IdentityRef +} + +func (s *ROSAControlPlaneScope) Session() awsclient.ConfigProvider { + return s.session +} + +func (s *ROSAControlPlaneScope) ServiceLimiter(service string) *throttle.ServiceLimiter { + if sl, ok := s.serviceLimiters[service]; ok { + return sl + } + return nil +} + +func (s *ROSAControlPlaneScope) ControllerName() string { + return s.controllerName +} + +var _ cloud.ScopeUsage = (*ROSAControlPlaneScope)(nil) +var _ cloud.Session = (*ROSAControlPlaneScope)(nil) +var _ cloud.SessionMetadata = (*ROSAControlPlaneScope)(nil) + // Name returns the CAPI cluster name. func (s *ROSAControlPlaneScope) Name() string { return s.Cluster.Name diff --git a/pkg/cloud/scope/session.go b/pkg/cloud/scope/session.go index acf3fa3ab8..cda46352f5 100644 --- a/pkg/cloud/scope/session.go +++ b/pkg/cloud/scope/session.go @@ -104,7 +104,7 @@ func sessionForRegion(region string, endpoint []ServiceEndpoint) (*session.Sessi return ns, sl, nil } -func sessionForClusterWithRegion(k8sClient client.Client, clusterScoper cloud.ClusterScoper, region string, endpoint []ServiceEndpoint, log logger.Wrapper) (*session.Session, throttle.ServiceLimiters, error) { +func sessionForClusterWithRegion(k8sClient client.Client, clusterScoper cloud.SessionMetadata, region string, endpoint []ServiceEndpoint, log logger.Wrapper) (*session.Session, throttle.ServiceLimiters, error) { log = log.WithName("identity") log.Trace("Creating an AWS Session") @@ -186,7 +186,7 @@ func sessionForClusterWithRegion(k8sClient client.Client, clusterScoper cloud.Cl return ns, sl, nil } -func getSessionName(region string, clusterScoper cloud.ClusterScoper) string { +func getSessionName(region string, clusterScoper cloud.SessionMetadata) string { return fmt.Sprintf("%s-%s-%s", region, clusterScoper.InfraClusterName(), clusterScoper.Namespace()) } @@ -254,7 +254,7 @@ func buildProvidersForRef( ctx context.Context, providers []identity.AWSPrincipalTypeProvider, k8sClient client.Client, - clusterScoper cloud.ClusterScoper, + clusterScoper cloud.SessionMetadata, ref *infrav1.AWSIdentityReference, log logger.Wrapper) ([]identity.AWSPrincipalTypeProvider, error) { if ref == nil { @@ -326,11 +326,11 @@ func buildProvidersForRef( return providers, nil } -func setPrincipalUsageAllowedCondition(clusterScoper cloud.ClusterScoper) { +func setPrincipalUsageAllowedCondition(clusterScoper cloud.SessionMetadata) { conditions.MarkTrue(clusterScoper.InfraCluster(), infrav1.PrincipalUsageAllowedCondition) } -func setPrincipalUsageNotAllowedCondition(kind infrav1.AWSIdentityKind, identityObjectKey client.ObjectKey, clusterScoper cloud.ClusterScoper) { +func setPrincipalUsageNotAllowedCondition(kind infrav1.AWSIdentityKind, identityObjectKey client.ObjectKey, clusterScoper cloud.SessionMetadata) { errMsg := fmt.Sprintf(notPermittedError, kind, identityObjectKey.Name) if clusterScoper.IdentityRef().Name == identityObjectKey.Name { @@ -340,7 +340,7 @@ func setPrincipalUsageNotAllowedCondition(kind infrav1.AWSIdentityKind, identity } } -func buildAWSClusterStaticIdentity(ctx context.Context, identityObjectKey client.ObjectKey, k8sClient client.Client, clusterScoper cloud.ClusterScoper) (*identity.AWSStaticPrincipalTypeProvider, error) { +func buildAWSClusterStaticIdentity(ctx context.Context, identityObjectKey client.ObjectKey, k8sClient client.Client, clusterScoper cloud.SessionMetadata) (*identity.AWSStaticPrincipalTypeProvider, error) { staticPrincipal := &infrav1.AWSClusterStaticIdentity{} err := k8sClient.Get(ctx, identityObjectKey, staticPrincipal) if err != nil { @@ -382,7 +382,7 @@ func buildAWSClusterStaticIdentity(ctx context.Context, identityObjectKey client return identity.NewAWSStaticPrincipalTypeProvider(staticPrincipal, secret), nil } -func buildAWSClusterControllerIdentity(ctx context.Context, identityObjectKey client.ObjectKey, k8sClient client.Client, clusterScoper cloud.ClusterScoper) error { +func buildAWSClusterControllerIdentity(ctx context.Context, identityObjectKey client.ObjectKey, k8sClient client.Client, clusterScoper cloud.SessionMetadata) error { controllerIdentity := &infrav1.AWSClusterControllerIdentity{} controllerIdentity.Kind = string(infrav1.ControllerIdentityKind) @@ -408,7 +408,7 @@ func buildAWSClusterControllerIdentity(ctx context.Context, identityObjectKey cl return nil } -func getProvidersForCluster(ctx context.Context, k8sClient client.Client, clusterScoper cloud.ClusterScoper, log logger.Wrapper) ([]identity.AWSPrincipalTypeProvider, error) { +func getProvidersForCluster(ctx context.Context, k8sClient client.Client, clusterScoper cloud.SessionMetadata, log logger.Wrapper) ([]identity.AWSPrincipalTypeProvider, error) { providers := make([]identity.AWSPrincipalTypeProvider, 0) providers, err := buildProvidersForRef(ctx, providers, k8sClient, clusterScoper, clusterScoper.IdentityRef(), log) if err != nil { diff --git a/pkg/rosa/clusters.go b/pkg/rosa/clusters.go index 1b6605245a..98dc0c5d2c 100644 --- a/pkg/rosa/clusters.go +++ b/pkg/rosa/clusters.go @@ -42,7 +42,7 @@ func (c *RosaClient) DeleteCluster(clusterID string) error { func (c *RosaClient) GetCluster() (*cmv1.Cluster, error) { clusterKey := c.rosaScope.RosaClusterName() query := fmt.Sprintf("%s AND (id = '%s' OR name = '%s' OR external_id = '%s')", - getClusterFilter(c.rosaScope.ControlPlane.Spec.CreatorARN), + getClusterFilter(c.rosaScope.Identity.Arn), clusterKey, clusterKey, clusterKey, ) response, err := c.ocm.ClustersMgmt().V1().Clusters().List().