diff --git a/manifests/01-cluster-role.yaml b/manifests/01-cluster-role.yaml index 86b1388b0e..0d6a8cb117 100644 --- a/manifests/01-cluster-role.yaml +++ b/manifests/01-cluster-role.yaml @@ -42,16 +42,6 @@ rules: - get - list - watch -- apiGroups: - - "" - resources: - - configmaps - resourceNames: - - cloud-credential-operator-config - verbs: - - get - - list - - watch - apiGroups: - config.openshift.io resources: diff --git a/manifests/01-config-role-binding.yaml b/manifests/01-config-role-binding.yaml new file mode 100644 index 0000000000..37e78e09ea --- /dev/null +++ b/manifests/01-config-role-binding.yaml @@ -0,0 +1,16 @@ +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: cloud-credential-operator + namespace: openshift-config-managed + annotations: + include.release.openshift.io/ibm-cloud-managed: "true" + include.release.openshift.io/self-managed-high-availability: "true" +subjects: +- kind: ServiceAccount + name: cloud-credential-operator + namespace: openshift-cloud-credential-operator +roleRef: + kind: Role + apiGroup: rbac.authorization.k8s.io + name: cloud-credential-operator-role diff --git a/manifests/01-config-role.yaml b/manifests/01-config-role.yaml new file mode 100644 index 0000000000..1c37d7db95 --- /dev/null +++ b/manifests/01-config-role.yaml @@ -0,0 +1,17 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: cloud-credential-operator-role + namespace: openshift-config-managed + annotations: + include.release.openshift.io/ibm-cloud-managed: "true" + include.release.openshift.io/self-managed-high-availability: "true" +rules: +- apiGroups: + - "" + resources: + - configmaps + resourceNames: + - kube-cloud-config + verbs: + - "get" diff --git a/pkg/aws/actuator/actuator.go b/pkg/aws/actuator/actuator.go index a23a31a1bf..34e95c9a6b 100644 --- a/pkg/aws/actuator/actuator.go +++ b/pkg/aws/actuator/actuator.go @@ -71,6 +71,7 @@ var _ actuatoriface.Actuator = (*AWSActuator)(nil) type AWSActuator struct { Client client.Client RootCredClient client.Client + LiveClient client.Client Codec *minterv1.ProviderCodec AWSClientBuilder func(accessKeyID, secretAccessKey []byte, c client.Client) (ccaws.Client, error) Scheme *runtime.Scheme @@ -78,7 +79,7 @@ type AWSActuator struct { } // NewAWSActuator creates a new AWSActuator. -func NewAWSActuator(client, rootCredClient client.Client, scheme *runtime.Scheme, awsSecurityTokenServiceGateEnabled bool) (*AWSActuator, error) { +func NewAWSActuator(client, rootCredClient, liveClient client.Client, scheme *runtime.Scheme, awsSecurityTokenServiceGateEnabled bool) (*AWSActuator, error) { codec, err := minterv1.NewCodec() if err != nil { log.WithError(err).Error("error creating AWS codec") @@ -88,6 +89,7 @@ func NewAWSActuator(client, rootCredClient client.Client, scheme *runtime.Scheme return &AWSActuator{ Codec: codec, Client: client, + LiveClient: liveClient, RootCredClient: rootCredClient, AWSClientBuilder: awsutils.ClientBuilder, Scheme: scheme, @@ -170,7 +172,7 @@ func (a *AWSActuator) needsUpdate(ctx context.Context, cr *minterv1.CredentialsR // Various checks for the kinds of reasons that would trigger a needed update _, existingAccessKey, existingSecretKey, existingCredentialsKey := a.loadExistingSecret(cr) - awsClient, err := a.AWSClientBuilder([]byte(existingAccessKey), []byte(existingSecretKey), a.Client) + awsClient, err := a.AWSClientBuilder([]byte(existingAccessKey), []byte(existingSecretKey), a.LiveClient) if err != nil { return true, err } @@ -489,7 +491,7 @@ func (a *AWSActuator) syncPassthrough(ctx context.Context, cr *minterv1.Credenti } // build client with root secret and verify that the creds are good enough to pass through - awsClient, err := a.AWSClientBuilder([]byte(accessKeyID), []byte(secretAccessKey), a.Client) + awsClient, err := a.AWSClientBuilder([]byte(accessKeyID), []byte(secretAccessKey), a.LiveClient) if err != nil { msg := "error building AWS client" logger.WithError(err).Error(msg) @@ -952,7 +954,7 @@ func (a *AWSActuator) buildRootAWSClient(cr *minterv1.CredentialsRequest) (minte } logger.Debug("creating root AWS client") - return a.AWSClientBuilder(accessKeyID, secretAccessKey, a.RootCredClient) + return a.AWSClientBuilder(accessKeyID, secretAccessKey, a.LiveClient) } // buildReadAWSClient will return an AWS client using the the scaled down read only AWS creds @@ -982,7 +984,7 @@ func (a *AWSActuator) buildReadAWSClient(cr *minterv1.CredentialsRequest) (minte } logger.Debug("creating read AWS client") - client, err := a.AWSClientBuilder(accessKeyID, secretAccessKey, a.Client) + client, err := a.AWSClientBuilder(accessKeyID, secretAccessKey, a.LiveClient) if err != nil { return nil, err } diff --git a/pkg/cmd/operator/cmd.go b/pkg/cmd/operator/cmd.go index b33afe543f..be2edb6879 100644 --- a/pkg/cmd/operator/cmd.go +++ b/pkg/cmd/operator/cmd.go @@ -42,7 +42,6 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" @@ -163,29 +162,29 @@ func NewOperator() *cobra.Command { filteredWatchPossible = true } + objectSelectors := map[client.Object]cache.ByObject{ + &corev1.ConfigMap{}: { + Field: fields.SelectorFromSet(fields.Set{ + "metadata.namespace": minterv1.CloudCredOperatorNamespace, + "metadata.name": constants.CloudCredOperatorConfigMap, + }), + }, + } + if filteredWatchPossible { + log.Infof("adding label selector %s=%s to cache options for Secrets", minterv1.LabelCredentialsRequest, minterv1.LabelCredentialsRequestValue) + objectSelectors[&corev1.Secret{}] = cache.ByObject{ + Label: labels.SelectorFromSet(labels.Set{ + minterv1.LabelCredentialsRequest: minterv1.LabelCredentialsRequestValue, + }), + } + } + // Create a new Cmd to provide shared dependencies and start components log.Info("setting up managers") mgr, err := manager.New(cfg, manager.Options{ MetricsBindAddress: ":2112", - NewCache: func(config *rest.Config, opts cache.Options) (cache.Cache, error) { - if opts.ByObject == nil { - opts.ByObject = map[client.Object]cache.ByObject{} - } - opts.ByObject[&corev1.ConfigMap{}] = cache.ByObject{ - Field: fields.SelectorFromSet(fields.Set{ - "metadata.namespace": minterv1.CloudCredOperatorNamespace, - "metadata.name": constants.CloudCredOperatorConfigMap, - }), - } - if filteredWatchPossible { - log.Infof("adding label selector %s=%s to cache options for Secrets", minterv1.LabelCredentialsRequest, minterv1.LabelCredentialsRequestValue) - opts.ByObject[&corev1.Secret{}] = cache.ByObject{ - Label: labels.SelectorFromSet(labels.Set{ - minterv1.LabelCredentialsRequest: minterv1.LabelCredentialsRequestValue, - }), - } - } - return cache.New(config, opts) + Cache: cache.Options{ + ByObject: objectSelectors, }, }) if err != nil { @@ -194,14 +193,12 @@ func NewOperator() *cobra.Command { rootMgr, err := manager.New(cfg, manager.Options{ MetricsBindAddress: ":2113", - NewCache: func(config *rest.Config, opts cache.Options) (cache.Cache, error) { - if opts.ByObject == nil { - opts.ByObject = map[client.Object]cache.ByObject{} - } - opts.ByObject[&corev1.Secret{}] = cache.ByObject{ - Field: selectorForRootCredential(platformType), - } - return cache.New(config, opts) + Cache: cache.Options{ + ByObject: map[client.Object]cache.ByObject{ + &corev1.Secret{}: { + Field: selectorForRootCredential(platformType), + }, + }, }, }) if err != nil { diff --git a/pkg/operator/controller.go b/pkg/operator/controller.go index f7c1807556..ddcd8ddd71 100644 --- a/pkg/operator/controller.go +++ b/pkg/operator/controller.go @@ -32,6 +32,7 @@ import ( "github.com/openshift/cloud-credential-operator/pkg/operator/platform" "github.com/openshift/cloud-credential-operator/pkg/operator/secretannotator" "github.com/openshift/cloud-credential-operator/pkg/operator/status" + "github.com/openshift/cloud-credential-operator/pkg/operator/utils" "github.com/openshift/cloud-credential-operator/pkg/ovirt" "github.com/openshift/cloud-credential-operator/pkg/util" vsphereactuator "github.com/openshift/cloud-credential-operator/pkg/vsphere/actuator" @@ -85,7 +86,7 @@ func AddToManager(m, rootM manager.Manager, explicitKubeconfig string, coreClien switch platformType { case configv1.AWSPlatformType: log.Info("initializing AWS actuator") - a, err = awsactuator.NewAWSActuator(m.GetClient(), rootM.GetClient(), m.GetScheme(), awsSecurityTokenServiceGateEnabled) + a, err = awsactuator.NewAWSActuator(m.GetClient(), rootM.GetClient(), utils.LiveClient(m), m.GetScheme(), awsSecurityTokenServiceGateEnabled) if err != nil { return err } diff --git a/pkg/operator/secretannotator/aws/reconciler.go b/pkg/operator/secretannotator/aws/reconciler.go index 5de5a07db4..728b15de78 100644 --- a/pkg/operator/secretannotator/aws/reconciler.go +++ b/pkg/operator/secretannotator/aws/reconciler.go @@ -42,6 +42,7 @@ func NewReconciler(c client.Client, mgr manager.Manager) reconcile.Reconciler { r := &ReconcileCloudCredSecret{ Client: c, RootCredClient: mgr.GetClient(), + LiveClient: utils.LiveClient(mgr), Logger: log.WithField("controller", constants.SecretAnnotatorControllerName), AWSClientBuilder: awsutils.ClientBuilder, } @@ -96,6 +97,7 @@ var _ reconcile.Reconciler = &ReconcileCloudCredSecret{} type ReconcileCloudCredSecret struct { Client client.Client RootCredClient client.Client + LiveClient client.Client Logger log.FieldLogger AWSClientBuilder func(accessKeyID, secretAccessKey []byte, c client.Client) (ccaws.Client, error) } @@ -183,7 +185,7 @@ func (r *ReconcileCloudCredSecret) validateCloudCredsSecret(secret *corev1.Secre return r.updateSecretAnnotations(secret, constants.InsufficientAnnotation) } - awsClient, err := r.AWSClientBuilder(accessKey, secretKey, r.Client) + awsClient, err := r.AWSClientBuilder(accessKey, secretKey, r.LiveClient) if err != nil { return fmt.Errorf("error creating aws client: %v", err) } diff --git a/pkg/operator/utils/client.go b/pkg/operator/utils/client.go new file mode 100644 index 0000000000..f7886fa515 --- /dev/null +++ b/pkg/operator/utils/client.go @@ -0,0 +1,32 @@ +package utils + +import ( + "context" + + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +type delegatingClient struct { + reader client.Reader + client.Client +} + +func (d *delegatingClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + return d.reader.Get(ctx, key, obj, opts...) +} + +func (d *delegatingClient) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { + return d.reader.List(ctx, list, opts...) +} + +var _ client.Client = (*delegatingClient)(nil) + +// LiveClient returns a client.Client that never uses the cache by virtue of using the APIReader() for +// all read operations. +func LiveClient(mgr manager.Manager) client.Client { + return &delegatingClient{ + reader: mgr.GetAPIReader(), + Client: mgr.GetClient(), + } +}