diff --git a/manifests/01-config-managed-role-binding.yaml b/manifests/01-config-managed-role-binding.yaml new file mode 100644 index 0000000000..b2d3c48fe8 --- /dev/null +++ b/manifests/01-config-managed-role-binding.yaml @@ -0,0 +1,17 @@ +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: cloud-credential-operator + namespace: openshift-config-managed + annotations: + capability.openshift.io/name: CloudCredential + 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-managed-role.yaml b/manifests/01-config-managed-role.yaml new file mode 100644 index 0000000000..ef5bf7f1c8 --- /dev/null +++ b/manifests/01-config-managed-role.yaml @@ -0,0 +1,18 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: cloud-credential-operator-role + namespace: openshift-config-managed + annotations: + capability.openshift.io/name: CloudCredential + 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/manifests/01-config-role-binding.yaml b/manifests/01-config-role-binding.yaml index b2d3c48fe8..3f79cbbfa6 100644 --- a/manifests/01-config-role-binding.yaml +++ b/manifests/01-config-role-binding.yaml @@ -2,7 +2,7 @@ kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: cloud-credential-operator - namespace: openshift-config-managed + namespace: openshift-config annotations: capability.openshift.io/name: CloudCredential include.release.openshift.io/ibm-cloud-managed: "true" diff --git a/manifests/01-config-role.yaml b/manifests/01-config-role.yaml index ef5bf7f1c8..a914914416 100644 --- a/manifests/01-config-role.yaml +++ b/manifests/01-config-role.yaml @@ -2,7 +2,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: cloud-credential-operator-role - namespace: openshift-config-managed + namespace: openshift-config annotations: capability.openshift.io/name: CloudCredential include.release.openshift.io/ibm-cloud-managed: "true" @@ -13,6 +13,8 @@ rules: resources: - configmaps resourceNames: - - kube-cloud-config + - cloud-provider-config verbs: - - "get" + - get + - list + - watch diff --git a/pkg/operator/secretannotator/openstack/reconciler.go b/pkg/operator/secretannotator/openstack/reconciler.go index 9cb3d70e0f..fed4db31a6 100644 --- a/pkg/operator/secretannotator/openstack/reconciler.go +++ b/pkg/operator/secretannotator/openstack/reconciler.go @@ -18,6 +18,7 @@ package openstack import ( "context" + "errors" "fmt" "time" @@ -52,6 +53,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), } @@ -105,6 +107,7 @@ var _ reconcile.Reconciler = &ReconcileCloudCredSecret{} type ReconcileCloudCredSecret struct { Client client.Client RootCredClient client.Client + LiveClient client.Client Logger log.FieldLogger } @@ -134,7 +137,7 @@ func (r *ReconcileCloudCredSecret) Reconcile(ctx context.Context, request reconc } if conflict { r.Logger.Error("configuration conflict between legacy configmap and operator config") - return reconcile.Result{}, fmt.Errorf("configuration conflict") + return reconcile.Result{}, errors.New("configuration conflict") } if mode == operatorv1.CloudCredentialsModeManual { r.Logger.Info("operator in disabled / manual mode") @@ -146,9 +149,11 @@ func (r *ReconcileCloudCredSecret) Reconcile(ctx context.Context, request reconc default: const msg = "OpenStack only supports Passthrough mode" r.Logger.Error(msg) - return reconcile.Result{}, fmt.Errorf(msg) + return reconcile.Result{}, errors.New(msg) } + r.Logger.Info("verifying clouds.yaml and syncing cacert (if any)") + secret := &corev1.Secret{} err = r.RootCredClient.Get(context.Background(), request.NamespacedName, secret) if err != nil { @@ -162,13 +167,30 @@ func (r *ReconcileCloudCredSecret) Reconcile(ctx context.Context, request reconc return reconcile.Result{}, err } + // Sync the cacert from its legacy location (the 'ca-bundle.pem' key of the + // 'openshift-config / cloud-provider-config' CM) to the new place, if present. + // TODO(stephenfin): Remove this syncer in a future release once CCM no longer + // relies on the legacy place during bootstrapping. + config := &corev1.ConfigMap{} + err = r.LiveClient.Get(context.Background(), types.NamespacedName{Namespace: "openshift-config", Name: "cloud-provider-config"}, config) + if err != nil { + r.Logger.Debugf("cloud provider config not found: %v", err) + return reconcile.Result{}, err + } + + cacertUpdated := false + if ccmCACert := config.Data["ca-bundle.pem"]; ccmCACert != cacert { + cacert = ccmCACert + cacertUpdated = true + } + clouds, cloudsUpdated, err := r.fixInvalidCACertFile(clouds) if err != nil { r.Logger.WithError(err).Error("errored checking clouds.yaml") return reconcile.Result{}, err } - if cloudsUpdated { + if cloudsUpdated || cacertUpdated { openstack.SetRootCloudCredentialsSecretData(secret, clouds, cacert) err := r.RootCredClient.Update(context.TODO(), secret) if err != nil { diff --git a/pkg/operator/secretannotator/openstack/reconciler_test.go b/pkg/operator/secretannotator/openstack/reconciler_test.go index e738918037..6d8f24083c 100644 --- a/pkg/operator/secretannotator/openstack/reconciler_test.go +++ b/pkg/operator/secretannotator/openstack/reconciler_test.go @@ -108,6 +108,14 @@ func TestReconcileCloudCredSecret_Reconcile(t *testing.T) { }, } + ccmConfig := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cloud-provider-config", + Namespace: "openshift-config", + }, + Data: map[string]string{}, + } + /* Test parsing of CCO configuration and the resulting annotation of the root secret. Most of this is boilerplate behaviour. @@ -181,11 +189,13 @@ func TestReconcileCloudCredSecret_Reconcile(t *testing.T) { secret := testSecret(fmt.Sprintf(cloudsWithCACert, correctCACertFile)) existing := append(tc.existing, infra, testOperatorConfig(tc.mode)) fakeClient := fake.NewClientBuilder().WithRuntimeObjects(existing...).Build() - fakeRootCredClient := fake.NewClientBuilder().WithRuntimeObjects(secret).Build() + fakeRootCredClient := fake.NewClientBuilder().WithRuntimeObjects(secret, ccmConfig).Build() + fakeLiveClient := fake.NewClientBuilder().WithRuntimeObjects(ccmConfig).Build() r := &ReconcileCloudCredSecret{ Client: fakeClient, RootCredClient: fakeRootCredClient, + LiveClient: fakeLiveClient, Logger: log.WithField("controller", "testController"), } _, err := r.Reconcile(context.TODO(), reconcile.Request{NamespacedName: types.NamespacedName{ @@ -270,12 +280,14 @@ func TestReconcileCloudCredSecret_Reconcile(t *testing.T) { t.Run(tc.name, func(t *testing.T) { secret := testSecret(tc.cloudsYAML) fakeClient := fake.NewClientBuilder().WithRuntimeObjects(infra, passthrough).Build() - fakeRootCredClient := fake.NewClientBuilder().WithRuntimeObjects(secret).Build() + fakeRootCredClient := fake.NewClientBuilder().WithRuntimeObjects(secret, ccmConfig).Build() + fakeLiveClient := fake.NewClientBuilder().WithRuntimeObjects(ccmConfig).Build() t.Logf("clouds.yaml: %s", tc.cloudsYAML) r := &ReconcileCloudCredSecret{ Client: fakeClient, RootCredClient: fakeRootCredClient, + LiveClient: fakeLiveClient, Logger: log.WithField("controller", "testController"), }