Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 5 additions & 0 deletions apis/capabilities/v1beta1/application_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ type ApplicationSpec struct {
// Suspend application if true suspends application, if false resumes application.
//+optional
Suspend bool `json:"suspend,omitempty"`

// AuthSecretRef reference to the API credentials secret. This secret is
// used only once when creating a new application
//+optional
AuthSecretRef *corev1.LocalObjectReference `json:"authSecretRef"`
}

// ApplicationStatus defines the observed state of Application
Expand Down
5 changes: 5 additions & 0 deletions apis/capabilities/v1beta1/zz_generated.deepcopy.go

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

13 changes: 13 additions & 0 deletions bundle/manifests/capabilities.3scale.net_applications.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ spec:
applicationPlanName:
description: ApplicationPlanName name of application plan that the application will use
type: string
authSecretRef:
description: |-
AuthSecretRef reference to the API credentials secret. This secret is
used only once when creating a new application
properties:
name:
description: |-
Name of the referent.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?
type: string
type: object
x-kubernetes-map-type: atomic
description:
description: Description human-readable text of the application
type: string
Expand Down
13 changes: 13 additions & 0 deletions config/crd/bases/capabilities.3scale.net_applications.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,19 @@ spec:
description: ApplicationPlanName name of application plan that the
application will use
type: string
authSecretRef:
description: |-
AuthSecretRef reference to the API credentials secret. This secret is
used only once when creating a new application
properties:
name:
description: |-
Name of the referent.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?
type: string
type: object
x-kubernetes-map-type: atomic
description:
description: Description human-readable text of the application
type: string
Expand Down
24 changes: 23 additions & 1 deletion controllers/capabilities/application_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,29 @@ func (r *ApplicationReconciler) applicationReconciler(applicationResource *capab
return nil, err
}

reconciler := NewApplicationReconciler(r.BaseReconciler, applicationResource, *accountResource.Status.ID, *productResource.Status.ID, threescaleAPIClient)
var authParams map[string]string
if applicationResource.Spec.AuthSecretRef != nil {
authSecretObj, err := helper.GetSecret(applicationResource.Spec.AuthSecretRef.Name, applicationResource.Namespace, r.Client())
if err != nil {
return nil, err
}

authMode, err := extractApplicationCredentialType(productResource)
if err != nil {
return nil, err
}

if err := validateApplicationCrendentialSecret(authSecretObj, authMode); err != nil {
return nil, err
}

authParams, err = handleCredentials(authSecretObj, authMode)
if err != nil {
return nil, err
}
}

reconciler := NewApplicationReconciler(r.BaseReconciler, applicationResource, authParams, *accountResource.Status.ID, *productResource.Status.ID, threescaleAPIClient)
return reconciler.Reconcile()
}

Expand Down
115 changes: 115 additions & 0 deletions controllers/capabilities/application_credentials.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package controllers

import (
"fmt"

capabilitiesv1beta1 "github.com/3scale/3scale-operator/apis/capabilities/v1beta1"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)

const (
ThreescaleCredentialTypeUserKey = "1"
ThreescaleCredentialTypeAppID = "2"
ThreescaleCredentialTypeOIDC = "oidc"
)

const (
ThreescaleCredentialParamUserKey = "user_key"
ThreescaleCredentialParamAppID = "application_id"
ThreescaleCredentialParamAppKey = "application_key"
)

const (
CredentialSecretKeyNameUserKey = "UserKey"
CredentialSecretKeyNameAppID = "ApplicationID"
CredentialSecretKeyNameAppKey = "ApplicationKey"
CredentialSecretKeyNameClientID = "ClientID"
CredentialSecretKeyNameClientSecret = "ClientSecret"
)

// extractApplicationCredentialType returns the credential type from the product
// setting
func extractApplicationCredentialType(productResource *capabilitiesv1beta1.Product) (string, error) {
credType := productResource.Spec.AuthenticationMode()
if credType == nil {
return "", fmt.Errorf("unable to identify authentication mode from Product CR")
}
return *credType, nil
}

func validateApplicationCrendentialSecret(s *corev1.Secret, authMode string) error {
nn := client.ObjectKeyFromObject(s)

switch authMode {
case ThreescaleCredentialTypeUserKey:
if err := validateSecretForAuthModeUserKey(s); err != nil {
return err
}
case ThreescaleCredentialTypeAppID:
if err := validateSecretForAuthModeAppIDAppKey(s); err != nil {
return err
}
case ThreescaleCredentialTypeOIDC:
if err := validateSecretForAuthModeOIDC(s); err != nil {
return err
}
default:
return fmt.Errorf("secret %s used, but has unsupported type %s", nn, authMode)
}
return nil
}

func validateSecretForAuthModeUserKey(s *corev1.Secret) error {
if _, ok := s.Data[CredentialSecretKeyNameUserKey]; !ok {
return fmt.Errorf("secret %s used as user-key authentication mode, but lacks %s key",
client.ObjectKeyFromObject(s), CredentialSecretKeyNameUserKey,
)
}
return nil
}

func validateSecretForAuthModeAppIDAppKey(s *corev1.Secret) error {
if _, ok := s.Data[CredentialSecretKeyNameAppID]; !ok {
return fmt.Errorf("secret %s used as user-key authentication mode, but lacks %s key",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the error message should say appid/appkey as authentication mode (not user-key), and the lacking key should be appid/appkey instead of userkey. Same issue for OIDC validation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Thanks for the review, much appreciated!

client.ObjectKeyFromObject(s), CredentialSecretKeyNameUserKey,
)
}
if _, ok := s.Data[CredentialSecretKeyNameAppKey]; !ok {
return fmt.Errorf("secret %s used as user-key authentication mode, but lacks %s key",
client.ObjectKeyFromObject(s), CredentialSecretKeyNameUserKey,
)
}
return nil
}

func validateSecretForAuthModeOIDC(s *corev1.Secret) error {
if _, ok := s.Data[CredentialSecretKeyNameClientID]; !ok {
return fmt.Errorf("secret %s used as user-key authentication mode, but lacks %s key",
client.ObjectKeyFromObject(s), CredentialSecretKeyNameUserKey,
)
}
if _, ok := s.Data[CredentialSecretKeyNameClientSecret]; !ok {
return fmt.Errorf("secret %s used as user-key authentication mode, but lacks %s key",
client.ObjectKeyFromObject(s), CredentialSecretKeyNameUserKey,
)
}
return nil
}

func handleCredentials(creds *corev1.Secret, authType string) (map[string]string, error) {
authParams := make(map[string]string)
switch authType {
case ThreescaleCredentialTypeUserKey:
authParams[ThreescaleCredentialParamUserKey] = string(creds.Data[CredentialSecretKeyNameUserKey])
case ThreescaleCredentialTypeAppID:
authParams[ThreescaleCredentialParamAppID] = string(creds.Data[CredentialSecretKeyNameAppID])
authParams[ThreescaleCredentialParamAppKey] = string(creds.Data[CredentialSecretKeyNameAppKey])
case ThreescaleCredentialTypeOIDC:
authParams[ThreescaleCredentialParamAppID] = string(creds.Data[CredentialSecretKeyNameClientID])
authParams[ThreescaleCredentialParamAppKey] = string(creds.Data[CredentialSecretKeyNameClientSecret])
default:
return nil, fmt.Errorf("unknown authentication mode")
}
return authParams, nil
}
16 changes: 15 additions & 1 deletion controllers/capabilities/application_threescale_reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,18 @@ type ApplicationThreescaleReconciler struct {
*reconcilers.BaseReconciler
applicationResource *capabilitiesv1beta1.Application
applicationEntity *controllerhelper.ApplicationEntity
authParams map[string]string
accountID int64
productID int64
threescaleAPIClient *threescaleapi.ThreeScaleClient
logger logr.Logger
}

func NewApplicationReconciler(b *reconcilers.BaseReconciler, applicationResource *capabilitiesv1beta1.Application, accountID int64, productID int64, threescaleAPIClient *threescaleapi.ThreeScaleClient) *ApplicationThreescaleReconciler {
func NewApplicationReconciler(b *reconcilers.BaseReconciler, applicationResource *capabilitiesv1beta1.Application, authParams map[string]string, accountID int64, productID int64, threescaleAPIClient *threescaleapi.ThreeScaleClient) *ApplicationThreescaleReconciler {
return &ApplicationThreescaleReconciler{
BaseReconciler: b,
applicationResource: applicationResource,
authParams: authParams,
accountID: accountID,
productID: productID,
threescaleAPIClient: threescaleAPIClient,
Expand Down Expand Up @@ -113,6 +115,12 @@ func (t *ApplicationThreescaleReconciler) syncApplication(_ any) error {
"name": t.applicationResource.Spec.Name,
"description": t.applicationResource.Spec.Description,
}

if t.authParams != nil {
for key, value := range t.authParams {
params.AddParam(key, value)
}
}
// Application doesn't exist yet - create it
a, err := t.threescaleAPIClient.CreateApplication(t.accountID, plan.Element.ID, t.applicationResource.Spec.Name, params)
if err != nil {
Expand All @@ -133,6 +141,12 @@ func (t *ApplicationThreescaleReconciler) syncApplication(_ any) error {
"description": t.applicationResource.Spec.Description,
}

if t.authParams != nil {
for key, value := range t.authParams {
params.AddParam(key, value)
}
}

// Application doesn't exist yet - create it
a, err := t.threescaleAPIClient.CreateApplication(t.accountID, plan.Element.ID, t.applicationResource.Spec.Name, params)
if err != nil {
Expand Down