Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Internal - BTP access secret resolving improvement #456

Merged
merged 8 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion api/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

288 changes: 157 additions & 131 deletions config/crd/bases/services.cloud.sap.com_servicebindings.yaml

Large diffs are not rendered by default.

239 changes: 131 additions & 108 deletions config/crd/bases/services.cloud.sap.com_serviceinstances.yaml

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: manager-role
rules:
- apiGroups:
Expand Down
2 changes: 0 additions & 2 deletions config/webhook/manifests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
creationTimestamp: null
name: mutating-webhook-configuration
webhooks:
- admissionReviewVersions:
Expand Down Expand Up @@ -51,7 +50,6 @@ webhooks:
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
creationTimestamp: null
name: validating-webhook-configuration
webhooks:
- admissionReviewVersions:
Expand Down
110 changes: 51 additions & 59 deletions controllers/servicebinding_controller.go

Large diffs are not rendered by default.

42 changes: 21 additions & 21 deletions controllers/serviceinstance_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import (
"k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/controller"

servicesv1 "github.com/SAP/sap-btp-service-operator/api/v1"
v1 "github.com/SAP/sap-btp-service-operator/api/v1"
"k8s.io/apimachinery/pkg/api/meta"

"github.com/google/uuid"
Expand All @@ -55,7 +55,7 @@ type ServiceInstanceReconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
GetSMClient func(ctx context.Context, resourceNamespace, btpAccessSecretName string) (sm.Client, error)
GetSMClient func(ctx context.Context, serviceInstance *v1.ServiceInstance) (sm.Client, error)
Config config.Config
Recorder record.EventRecorder
}
Expand All @@ -69,7 +69,7 @@ func (r *ServiceInstanceReconciler) Reconcile(ctx context.Context, req ctrl.Requ
log := r.Log.WithValues("serviceinstance", req.NamespacedName).WithValues("correlation_id", uuid.New().String())
ctx = context.WithValue(ctx, utils.LogKey{}, log)

serviceInstance := &servicesv1.ServiceInstance{}
serviceInstance := &v1.ServiceInstance{}
if err := r.Client.Get(ctx, req.NamespacedName, serviceInstance); err != nil {
if !apierrors.IsNotFound(err) {
log.Error(err, "unable to fetch ServiceInstance")
Expand Down Expand Up @@ -122,7 +122,7 @@ func (r *ServiceInstanceReconciler) Reconcile(ctx context.Context, req ctrl.Requ
log.Info(fmt.Sprintf("instance is not in final state, handling... (generation: %d, observedGen: %d", serviceInstance.Generation, serviceInstance.Status.ObservedGeneration))
serviceInstance.SetObservedGeneration(serviceInstance.Generation)

smClient, err := r.GetSMClient(ctx, serviceInstance.Namespace, serviceInstance.Spec.BTPAccessCredentialsSecret)
smClient, err := r.GetSMClient(ctx, serviceInstance)
if err != nil {
log.Error(err, "failed to get sm client")
return utils.MarkAsTransientError(ctx, r.Client, common.Unknown, err, serviceInstance)
Expand Down Expand Up @@ -163,12 +163,12 @@ func (r *ServiceInstanceReconciler) Reconcile(ctx context.Context, req ctrl.Requ

func (r *ServiceInstanceReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&servicesv1.ServiceInstance{}).
For(&v1.ServiceInstance{}).
WithOptions(controller.Options{RateLimiter: workqueue.NewItemExponentialFailureRateLimiter(r.Config.RetryBaseDelay, r.Config.RetryMaxDelay)}).
Complete(r)
}

func (r *ServiceInstanceReconciler) createInstance(ctx context.Context, smClient sm.Client, serviceInstance *servicesv1.ServiceInstance) (ctrl.Result, error) {
func (r *ServiceInstanceReconciler) createInstance(ctx context.Context, smClient sm.Client, serviceInstance *v1.ServiceInstance) (ctrl.Result, error) {
log := utils.GetLogger(ctx)
log.Info("Creating instance in SM")
updateHashedSpecValue(serviceInstance)
Expand Down Expand Up @@ -222,7 +222,7 @@ func (r *ServiceInstanceReconciler) createInstance(ctx context.Context, smClient
return ctrl.Result{}, utils.UpdateStatus(ctx, r.Client, serviceInstance)
}

func (r *ServiceInstanceReconciler) updateInstance(ctx context.Context, smClient sm.Client, serviceInstance *servicesv1.ServiceInstance) (ctrl.Result, error) {
func (r *ServiceInstanceReconciler) updateInstance(ctx context.Context, smClient sm.Client, serviceInstance *v1.ServiceInstance) (ctrl.Result, error) {
log := utils.GetLogger(ctx)
log.Info(fmt.Sprintf("updating instance %s in SM", serviceInstance.Status.InstanceID))

Expand Down Expand Up @@ -262,11 +262,11 @@ func (r *ServiceInstanceReconciler) updateInstance(ctx context.Context, smClient
return ctrl.Result{}, utils.UpdateStatus(ctx, r.Client, serviceInstance)
}

func (r *ServiceInstanceReconciler) deleteInstance(ctx context.Context, serviceInstance *servicesv1.ServiceInstance) (ctrl.Result, error) {
func (r *ServiceInstanceReconciler) deleteInstance(ctx context.Context, serviceInstance *v1.ServiceInstance) (ctrl.Result, error) {
log := utils.GetLogger(ctx)

if controllerutil.ContainsFinalizer(serviceInstance, common.FinalizerName) {
smClient, err := r.GetSMClient(ctx, serviceInstance.Namespace, serviceInstance.Spec.BTPAccessCredentialsSecret)
smClient, err := r.GetSMClient(ctx, serviceInstance)
if err != nil {
log.Error(err, "failed to get sm client")
return utils.MarkAsTransientError(ctx, r.Client, common.Unknown, err, serviceInstance)
Expand Down Expand Up @@ -311,7 +311,7 @@ func (r *ServiceInstanceReconciler) deleteInstance(ctx context.Context, serviceI
return ctrl.Result{}, nil
}

func (r *ServiceInstanceReconciler) handleInstanceSharing(ctx context.Context, serviceInstance *servicesv1.ServiceInstance, smClient sm.Client) (ctrl.Result, error) {
func (r *ServiceInstanceReconciler) handleInstanceSharing(ctx context.Context, serviceInstance *v1.ServiceInstance, smClient sm.Client) (ctrl.Result, error) {
log := utils.GetLogger(ctx)
log.Info("Handling change in instance sharing")

Expand Down Expand Up @@ -345,10 +345,10 @@ func (r *ServiceInstanceReconciler) handleInstanceSharing(ctx context.Context, s
return ctrl.Result{}, utils.UpdateStatus(ctx, r.Client, serviceInstance)
}

func (r *ServiceInstanceReconciler) poll(ctx context.Context, serviceInstance *servicesv1.ServiceInstance) (ctrl.Result, error) {
func (r *ServiceInstanceReconciler) poll(ctx context.Context, serviceInstance *v1.ServiceInstance) (ctrl.Result, error) {
log := utils.GetLogger(ctx)
log.Info(fmt.Sprintf("resource is in progress, found operation url %s", serviceInstance.Status.OperationURL))
smClient, err := r.GetSMClient(ctx, serviceInstance.Namespace, serviceInstance.Spec.BTPAccessCredentialsSecret)
smClient, err := r.GetSMClient(ctx, serviceInstance)
if err != nil {
log.Error(err, "failed to get sm client")
return utils.MarkAsTransientError(ctx, r.Client, common.Unknown, err, serviceInstance)
Expand All @@ -359,7 +359,7 @@ func (r *ServiceInstanceReconciler) poll(ctx context.Context, serviceInstance *s
log.Info(fmt.Sprintf("failed to fetch operation, got error from SM: %s", statusErr.Error()), "operationURL", serviceInstance.Status.OperationURL)
utils.SetInProgressConditions(ctx, serviceInstance.Status.OperationType, string(smClientTypes.INPROGRESS), serviceInstance)
// if failed to read operation status we cleanup the status to trigger re-sync from SM
freshStatus := servicesv1.ServiceInstanceStatus{Conditions: serviceInstance.GetConditions(), ObservedGeneration: serviceInstance.Generation}
freshStatus := v1.ServiceInstanceStatus{Conditions: serviceInstance.GetConditions(), ObservedGeneration: serviceInstance.Generation}
if utils.IsMarkedForDeletion(serviceInstance.ObjectMeta) {
freshStatus.InstanceID = serviceInstance.Status.InstanceID
}
Expand Down Expand Up @@ -425,7 +425,7 @@ func (r *ServiceInstanceReconciler) poll(ctx context.Context, serviceInstance *s
return ctrl.Result{}, utils.UpdateStatus(ctx, r.Client, serviceInstance)
}

func (r *ServiceInstanceReconciler) handleAsyncDelete(ctx context.Context, serviceInstance *servicesv1.ServiceInstance, opURL string) (ctrl.Result, error) {
func (r *ServiceInstanceReconciler) handleAsyncDelete(ctx context.Context, serviceInstance *v1.ServiceInstance, opURL string) (ctrl.Result, error) {
serviceInstance.Status.OperationURL = opURL
serviceInstance.Status.OperationType = smClientTypes.DELETE
utils.SetInProgressConditions(ctx, smClientTypes.DELETE, "", serviceInstance)
Expand All @@ -437,7 +437,7 @@ func (r *ServiceInstanceReconciler) handleAsyncDelete(ctx context.Context, servi
return ctrl.Result{Requeue: true, RequeueAfter: r.Config.PollInterval}, nil
}

func (r *ServiceInstanceReconciler) getInstanceForRecovery(ctx context.Context, smClient sm.Client, serviceInstance *servicesv1.ServiceInstance) (*smClientTypes.ServiceInstance, error) {
func (r *ServiceInstanceReconciler) getInstanceForRecovery(ctx context.Context, smClient sm.Client, serviceInstance *v1.ServiceInstance) (*smClientTypes.ServiceInstance, error) {
log := utils.GetLogger(ctx)
parameters := sm.Parameters{
FieldQuery: []string{
Expand All @@ -462,7 +462,7 @@ func (r *ServiceInstanceReconciler) getInstanceForRecovery(ctx context.Context,
return nil, nil
}

func (r *ServiceInstanceReconciler) recover(ctx context.Context, smClient sm.Client, k8sInstance *servicesv1.ServiceInstance, smInstance *smClientTypes.ServiceInstance) (ctrl.Result, error) {
func (r *ServiceInstanceReconciler) recover(ctx context.Context, smClient sm.Client, k8sInstance *v1.ServiceInstance, smInstance *smClientTypes.ServiceInstance) (ctrl.Result, error) {
log := utils.GetLogger(ctx)

log.Info(fmt.Sprintf("found existing instance in SM with id %s, updating status", smInstance.ID))
Expand Down Expand Up @@ -546,7 +546,7 @@ func (r *ServiceInstanceReconciler) handleInstanceSharingError(ctx context.Conte
return ctrl.Result{Requeue: isTransient}, utils.UpdateStatus(ctx, r.Client, object)
}

func isFinalState(ctx context.Context, serviceInstance *servicesv1.ServiceInstance) bool {
func isFinalState(ctx context.Context, serviceInstance *v1.ServiceInstance) bool {
log := utils.GetLogger(ctx)
if utils.IsMarkedForDeletion(serviceInstance.ObjectMeta) {
log.Info("instance is not in final state, it is marked for deletion")
Expand Down Expand Up @@ -579,7 +579,7 @@ func isFinalState(ctx context.Context, serviceInstance *servicesv1.ServiceInstan
return true
}

func updateRequired(serviceInstance *servicesv1.ServiceInstance) bool {
func updateRequired(serviceInstance *v1.ServiceInstance) bool {
//update is not supported for failed instances (this can occur when instance creation was asynchronously)
if serviceInstance.Status.Ready != metav1.ConditionTrue {
return false
Expand All @@ -593,7 +593,7 @@ func updateRequired(serviceInstance *servicesv1.ServiceInstance) bool {
return getSpecHash(serviceInstance) != serviceInstance.Status.HashedSpec
}

func sharingUpdateRequired(serviceInstance *servicesv1.ServiceInstance) bool {
func sharingUpdateRequired(serviceInstance *v1.ServiceInstance) bool {
//relevant only for non-shared instances - sharing instance is possible only for usable instances
if serviceInstance.Status.Ready != metav1.ConditionTrue {
return false
Expand Down Expand Up @@ -661,7 +661,7 @@ func getTags(tags []byte) ([]string, error) {
return tagsArr, nil
}

func getSpecHash(serviceInstance *servicesv1.ServiceInstance) string {
func getSpecHash(serviceInstance *v1.ServiceInstance) string {
spec := serviceInstance.Spec
spec.Shared = ptr.To(false)
specBytes, _ := json.Marshal(spec)
Expand Down Expand Up @@ -699,7 +699,7 @@ func setSharedCondition(object common.SAPBTPResource, status metav1.ConditionSta
object.SetConditions(conditions)
}

func updateHashedSpecValue(serviceInstance *servicesv1.ServiceInstance) {
func updateHashedSpecValue(serviceInstance *v1.ServiceInstance) {
serviceInstance.Status.HashedSpec = getSpecHash(serviceInstance)
}

Expand Down
4 changes: 2 additions & 2 deletions controllers/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ var _ = BeforeSuite(func(done Done) {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Log: ctrl.Log.WithName("controllers").WithName("ServiceInstance"),
GetSMClient: func(_ context.Context, _, _ string) (sm.Client, error) {
GetSMClient: func(_ context.Context, _ *v1.ServiceInstance) (sm.Client, error) {
return fakeClient, nil
},
Config: testConfig,
Expand All @@ -170,7 +170,7 @@ var _ = BeforeSuite(func(done Done) {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Log: ctrl.Log.WithName("controllers").WithName("ServiceBinding"),
GetSMClient: func(_ context.Context, _, _ string) (sm.Client, error) {
GetSMClient: func(_ context.Context, _ *v1.ServiceInstance) (sm.Client, error) {
return fakeClient, nil
},
Config: testConfig,
Expand Down
8 changes: 4 additions & 4 deletions internal/utils/secret_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ func (sr *secretClient) getSecretForResource(ctx context.Context, namespace, nam
}

// secret not found in resource namespace, search for namespace-specific secret in management namespace
sr.Log.Info(fmt.Sprintf("Searching a secret for namespace %s in the management namespace %s", namespace, sr.ManagementNamespace))
err := sr.getWithClientFallback(ctx, types.NamespacedName{Namespace: sr.ManagementNamespace, Name: fmt.Sprintf("%s-%s", namespace, name)}, secretForResource)
var err error
secretForResource, err = secretsClient.getSecretFromManagementNamespace(ctx, fmt.Sprintf("%s-%s", namespace, name))
if err == nil {
return secretForResource, nil
}
Expand All @@ -100,10 +100,10 @@ func (sr *secretClient) getSecretForResource(ctx context.Context, namespace, nam
}

// namespace-specific secret not found in management namespace, fallback to central cluster secret
return sr.getDefaultSecret(ctx, name)
return sr.getClusterDefaultSecret(ctx, name)
}

func (sr *secretClient) getDefaultSecret(ctx context.Context, name string) (*v1.Secret, error) {
func (sr *secretClient) getClusterDefaultSecret(ctx context.Context, name string) (*v1.Secret, error) {
secretForResource := &v1.Secret{}
sr.Log.Info(fmt.Sprintf("Searching for cluster secret %s in releaseNamespace %s", name, sr.ReleaseNamespace))
err := sr.getWithClientFallback(ctx, types.NamespacedName{Namespace: sr.ReleaseNamespace, Name: name}, secretForResource)
Expand Down
77 changes: 35 additions & 42 deletions internal/utils/sm_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,35 @@ import (
"context"
"fmt"

v1 "github.com/SAP/sap-btp-service-operator/api/v1"
"github.com/SAP/sap-btp-service-operator/client/sm"
v1 "k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)

func GetSMClient(ctx context.Context, resourceNamespace, btpAccessSecretName string) (sm.Client, error) {
log := GetLogger(ctx)
type InvalidCredentialsError struct{}

if len(btpAccessSecretName) > 0 {
return getBTPAccessClient(ctx, btpAccessSecretName)
}
func (ic *InvalidCredentialsError) Error() string {
return "invalid Service-Manager credentials, contact your cluster administrator"
}

func GetSMClient(ctx context.Context, serviceInstance *v1.ServiceInstance) (sm.Client, error) {
log := GetLogger(ctx)
var err error

secret, err := GetSecretForResource(ctx, resourceNamespace, SAPBTPOperatorSecretName)
if err != nil {
return nil, err
var secret *corev1.Secret
if len(serviceInstance.Spec.BTPAccessCredentialsSecret) > 0 {
secret, err = GetSecretFromManagementNamespace(ctx, serviceInstance.Spec.BTPAccessCredentialsSecret)
if err != nil {
log.Error(err, "failed to get secret BTPAccessCredentialsSecret")
return nil, err
}
} else {
secret, err = GetSecretForResource(ctx, serviceInstance.Namespace, SAPBTPOperatorSecretName)
if err != nil {
log.Error(err, "failed to get secret for instance")
return nil, err
}
}

clientConfig := &sm.ClientConfig{
Expand All @@ -27,8 +41,8 @@ func GetSMClient(ctx context.Context, resourceNamespace, btpAccessSecretName str
URL: string(secret.Data["sm_url"]),
TokenURL: string(secret.Data["tokenurl"]),
TokenURLSuffix: string(secret.Data["tokenurlsuffix"]),
TLSPrivateKey: string(secret.Data[v1.TLSPrivateKeyKey]),
TLSCertKey: string(secret.Data[v1.TLSCertKey]),
TLSPrivateKey: string(secret.Data[corev1.TLSPrivateKeyKey]),
TLSCertKey: string(secret.Data[corev1.TLSCertKey]),
SSLDisabled: false,
}

Expand All @@ -39,45 +53,24 @@ func GetSMClient(ctx context.Context, resourceNamespace, btpAccessSecretName str

//backward compatibility (tls data in a dedicated secret)
if len(clientConfig.ClientSecret) == 0 && (len(clientConfig.TLSPrivateKey) == 0 || len(clientConfig.TLSCertKey) == 0) {
tlsSecret, err := GetSecretForResource(ctx, resourceNamespace, SAPBTPOperatorTLSSecretName)
if len(serviceInstance.Spec.BTPAccessCredentialsSecret) > 0 && !clientConfig.IsValid() {
log.Info("btpAccess secret found but did not contain all the required data")
return nil, fmt.Errorf("invalid Service-Manager credentials, contact your cluster administrator")
}

tlsSecret, err := GetSecretForResource(ctx, serviceInstance.Namespace, SAPBTPOperatorTLSSecretName)
if client.IgnoreNotFound(err) != nil {
return nil, err
}

if tlsSecret == nil || len(tlsSecret.Data) == 0 || len(tlsSecret.Data[v1.TLSCertKey]) == 0 || len(tlsSecret.Data[v1.TLSPrivateKeyKey]) == 0 {
if tlsSecret == nil || len(tlsSecret.Data) == 0 || len(tlsSecret.Data[corev1.TLSCertKey]) == 0 || len(tlsSecret.Data[corev1.TLSPrivateKeyKey]) == 0 {
log.Info("clientsecret not found in SM credentials, and tls secret is invalid")
return nil, fmt.Errorf("invalid Service-Manager credentials, contact your cluster administrator")
return nil, &InvalidCredentialsError{}
}

log.Info("found tls configuration")
clientConfig.TLSCertKey = string(tlsSecret.Data[v1.TLSCertKey])
clientConfig.TLSPrivateKey = string(tlsSecret.Data[v1.TLSPrivateKeyKey])
}

return sm.NewClient(ctx, clientConfig, nil)
}

func getBTPAccessClient(ctx context.Context, secretName string) (sm.Client, error) {
log := GetLogger(ctx)
secret, err := GetSecretFromManagementNamespace(ctx, secretName)
if err != nil {
return nil, err
}

clientConfig := &sm.ClientConfig{
ClientID: string(secret.Data["clientid"]),
ClientSecret: string(secret.Data["clientsecret"]),
URL: string(secret.Data["sm_url"]),
TokenURL: string(secret.Data["tokenurl"]),
TokenURLSuffix: string(secret.Data["tokenurlsuffix"]),
TLSPrivateKey: string(secret.Data[v1.TLSPrivateKeyKey]),
TLSCertKey: string(secret.Data[v1.TLSCertKey]),
SSLDisabled: false,
}

if !clientConfig.IsValid() {
log.Info("btpAccess secret found but did not contain all the required data")
return nil, fmt.Errorf("invalid Service-Manager credentials, contact your cluster administrator")
clientConfig.TLSCertKey = string(tlsSecret.Data[corev1.TLSCertKey])
clientConfig.TLSPrivateKey = string(tlsSecret.Data[corev1.TLSPrivateKeyKey])
}

return sm.NewClient(ctx, clientConfig, nil)
Expand Down
Loading
Loading