Skip to content

Commit c85c8f0

Browse files
authored
Merge pull request #1129 from tkan145/THREESCALE-11081
THREESCALE-11081 The ability to set the application_id & application_key in the application CRD creation
2 parents 13b48e3 + 2f7b415 commit c85c8f0

File tree

9 files changed

+280
-2
lines changed

9 files changed

+280
-2
lines changed

apis/capabilities/v1beta1/application_types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ type ApplicationSpec struct {
5959
// Suspend application if true suspends application, if false resumes application.
6060
//+optional
6161
Suspend bool `json:"suspend,omitempty"`
62+
63+
// AuthSecretRef reference to the API credentials secret. This secret is
64+
// used only once when creating a new application
65+
//+optional
66+
AuthSecretRef *corev1.LocalObjectReference `json:"authSecretRef"`
6267
}
6368

6469
// ApplicationStatus defines the observed state of Application

apis/capabilities/v1beta1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bundle/manifests/capabilities.3scale.net_applications.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,19 @@ spec:
5555
applicationPlanName:
5656
description: ApplicationPlanName name of application plan that the application will use
5757
type: string
58+
authSecretRef:
59+
description: |-
60+
AuthSecretRef reference to the API credentials secret. This secret is
61+
used only once when creating a new application
62+
properties:
63+
name:
64+
description: |-
65+
Name of the referent.
66+
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
67+
TODO: Add other useful fields. apiVersion, kind, uid?
68+
type: string
69+
type: object
70+
x-kubernetes-map-type: atomic
5871
description:
5972
description: Description human-readable text of the application
6073
type: string

config/crd/bases/capabilities.3scale.net_applications.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,19 @@ spec:
5858
description: ApplicationPlanName name of application plan that the
5959
application will use
6060
type: string
61+
authSecretRef:
62+
description: |-
63+
AuthSecretRef reference to the API credentials secret. This secret is
64+
used only once when creating a new application
65+
properties:
66+
name:
67+
description: |-
68+
Name of the referent.
69+
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
70+
TODO: Add other useful fields. apiVersion, kind, uid?
71+
type: string
72+
type: object
73+
x-kubernetes-map-type: atomic
6174
description:
6275
description: Description human-readable text of the application
6376
type: string

controllers/capabilities/application_controller.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,29 @@ func (r *ApplicationReconciler) applicationReconciler(applicationResource *capab
243243
return nil, err
244244
}
245245

246-
reconciler := NewApplicationReconciler(r.BaseReconciler, applicationResource, *accountResource.Status.ID, *productResource.Status.ID, threescaleAPIClient)
246+
var authParams map[string]string
247+
if applicationResource.Spec.AuthSecretRef != nil {
248+
authSecretObj, err := helper.GetSecret(applicationResource.Spec.AuthSecretRef.Name, applicationResource.Namespace, r.Client())
249+
if err != nil {
250+
return nil, err
251+
}
252+
253+
authMode, err := extractApplicationCredentialType(productResource)
254+
if err != nil {
255+
return nil, err
256+
}
257+
258+
if err := validateApplicationCrendentialSecret(authSecretObj, authMode); err != nil {
259+
return nil, err
260+
}
261+
262+
authParams, err = handleCredentials(authSecretObj, authMode)
263+
if err != nil {
264+
return nil, err
265+
}
266+
}
267+
268+
reconciler := NewApplicationReconciler(r.BaseReconciler, applicationResource, authParams, *accountResource.Status.ID, *productResource.Status.ID, threescaleAPIClient)
247269
return reconciler.Reconcile()
248270
}
249271

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package controllers
2+
3+
import (
4+
"fmt"
5+
6+
capabilitiesv1beta1 "github.com/3scale/3scale-operator/apis/capabilities/v1beta1"
7+
corev1 "k8s.io/api/core/v1"
8+
"sigs.k8s.io/controller-runtime/pkg/client"
9+
)
10+
11+
const (
12+
ThreescaleCredentialTypeUserKey = "1"
13+
ThreescaleCredentialTypeAppID = "2"
14+
ThreescaleCredentialTypeOIDC = "oidc"
15+
)
16+
17+
const (
18+
ThreescaleCredentialParamUserKey = "user_key"
19+
ThreescaleCredentialParamAppID = "application_id"
20+
ThreescaleCredentialParamAppKey = "application_key"
21+
)
22+
23+
const (
24+
CredentialSecretKeyNameUserKey = "UserKey"
25+
CredentialSecretKeyNameAppID = "ApplicationID"
26+
CredentialSecretKeyNameAppKey = "ApplicationKey"
27+
CredentialSecretKeyNameClientID = "ClientID"
28+
CredentialSecretKeyNameClientSecret = "ClientSecret"
29+
)
30+
31+
// extractApplicationCredentialType returns the credential type from the product
32+
// setting
33+
func extractApplicationCredentialType(productResource *capabilitiesv1beta1.Product) (string, error) {
34+
credType := productResource.Spec.AuthenticationMode()
35+
if credType == nil {
36+
return "", fmt.Errorf("unable to identify authentication mode from Product CR")
37+
}
38+
return *credType, nil
39+
}
40+
41+
func validateApplicationCrendentialSecret(s *corev1.Secret, authMode string) error {
42+
nn := client.ObjectKeyFromObject(s)
43+
44+
switch authMode {
45+
case ThreescaleCredentialTypeUserKey:
46+
if err := validateSecretForAuthModeUserKey(s); err != nil {
47+
return err
48+
}
49+
case ThreescaleCredentialTypeAppID:
50+
if err := validateSecretForAuthModeAppIDAppKey(s); err != nil {
51+
return err
52+
}
53+
case ThreescaleCredentialTypeOIDC:
54+
if err := validateSecretForAuthModeOIDC(s); err != nil {
55+
return err
56+
}
57+
default:
58+
return fmt.Errorf("secret %s used, but has unsupported type %s", nn, authMode)
59+
}
60+
return nil
61+
}
62+
63+
func validateSecretForAuthModeUserKey(s *corev1.Secret) error {
64+
if _, ok := s.Data[CredentialSecretKeyNameUserKey]; !ok {
65+
return fmt.Errorf("secret %s used as user-key authentication mode, but lacks %s key",
66+
client.ObjectKeyFromObject(s), CredentialSecretKeyNameUserKey,
67+
)
68+
}
69+
return nil
70+
}
71+
72+
func validateSecretForAuthModeAppIDAppKey(s *corev1.Secret) error {
73+
if _, ok := s.Data[CredentialSecretKeyNameAppID]; !ok {
74+
return fmt.Errorf("secret %s used as app-id/app-key authentication mode, but lacks %s key",
75+
client.ObjectKeyFromObject(s), CredentialSecretKeyNameAppID,
76+
)
77+
}
78+
if _, ok := s.Data[CredentialSecretKeyNameAppKey]; !ok {
79+
return fmt.Errorf("secret %s used as app-id/app-key authentication mode, but lacks %s key",
80+
client.ObjectKeyFromObject(s), CredentialSecretKeyNameAppKey,
81+
)
82+
}
83+
return nil
84+
}
85+
86+
func validateSecretForAuthModeOIDC(s *corev1.Secret) error {
87+
if _, ok := s.Data[CredentialSecretKeyNameClientID]; !ok {
88+
return fmt.Errorf("secret %s used as oidc authentication mode, but lacks %s key",
89+
client.ObjectKeyFromObject(s), CredentialSecretKeyNameClientID,
90+
)
91+
}
92+
if _, ok := s.Data[CredentialSecretKeyNameClientSecret]; !ok {
93+
return fmt.Errorf("secret %s used as oidc authentication mode, but lacks %s key",
94+
client.ObjectKeyFromObject(s), CredentialSecretKeyNameClientSecret,
95+
)
96+
}
97+
return nil
98+
}
99+
100+
func handleCredentials(creds *corev1.Secret, authType string) (map[string]string, error) {
101+
authParams := make(map[string]string)
102+
switch authType {
103+
case ThreescaleCredentialTypeUserKey:
104+
authParams[ThreescaleCredentialParamUserKey] = string(creds.Data[CredentialSecretKeyNameUserKey])
105+
case ThreescaleCredentialTypeAppID:
106+
authParams[ThreescaleCredentialParamAppID] = string(creds.Data[CredentialSecretKeyNameAppID])
107+
authParams[ThreescaleCredentialParamAppKey] = string(creds.Data[CredentialSecretKeyNameAppKey])
108+
case ThreescaleCredentialTypeOIDC:
109+
authParams[ThreescaleCredentialParamAppID] = string(creds.Data[CredentialSecretKeyNameClientID])
110+
authParams[ThreescaleCredentialParamAppKey] = string(creds.Data[CredentialSecretKeyNameClientSecret])
111+
default:
112+
return nil, fmt.Errorf("unknown authentication mode")
113+
}
114+
return authParams, nil
115+
}

controllers/capabilities/application_threescale_reconciler.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,18 @@ type ApplicationThreescaleReconciler struct {
1717
*reconcilers.BaseReconciler
1818
applicationResource *capabilitiesv1beta1.Application
1919
applicationEntity *controllerhelper.ApplicationEntity
20+
authParams map[string]string
2021
accountID int64
2122
productID int64
2223
threescaleAPIClient *threescaleapi.ThreeScaleClient
2324
logger logr.Logger
2425
}
2526

26-
func NewApplicationReconciler(b *reconcilers.BaseReconciler, applicationResource *capabilitiesv1beta1.Application, accountID int64, productID int64, threescaleAPIClient *threescaleapi.ThreeScaleClient) *ApplicationThreescaleReconciler {
27+
func NewApplicationReconciler(b *reconcilers.BaseReconciler, applicationResource *capabilitiesv1beta1.Application, authParams map[string]string, accountID int64, productID int64, threescaleAPIClient *threescaleapi.ThreeScaleClient) *ApplicationThreescaleReconciler {
2728
return &ApplicationThreescaleReconciler{
2829
BaseReconciler: b,
2930
applicationResource: applicationResource,
31+
authParams: authParams,
3032
accountID: accountID,
3133
productID: productID,
3234
threescaleAPIClient: threescaleAPIClient,
@@ -113,6 +115,12 @@ func (t *ApplicationThreescaleReconciler) syncApplication(_ any) error {
113115
"name": t.applicationResource.Spec.Name,
114116
"description": t.applicationResource.Spec.Description,
115117
}
118+
119+
if t.authParams != nil {
120+
for key, value := range t.authParams {
121+
params.AddParam(key, value)
122+
}
123+
}
116124
// Application doesn't exist yet - create it
117125
a, err := t.threescaleAPIClient.CreateApplication(t.accountID, plan.Element.ID, t.applicationResource.Spec.Name, params)
118126
if err != nil {
@@ -133,6 +141,12 @@ func (t *ApplicationThreescaleReconciler) syncApplication(_ any) error {
133141
"description": t.applicationResource.Spec.Description,
134142
}
135143

144+
if t.authParams != nil {
145+
for key, value := range t.authParams {
146+
params.AddParam(key, value)
147+
}
148+
}
149+
136150
// Application doesn't exist yet - create it
137151
a, err := t.threescaleAPIClient.CreateApplication(t.accountID, plan.Element.ID, t.applicationResource.Spec.Name, params)
138152
if err != nil {

doc/application-reference.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Created by [github-markdown-toc](https://github.com/ekalinin/github-markdown-toc
2828
| ProductCR | `productCR` | object | name of product CR via [v1.LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#localobjectreference-v1-core) | Yes |
2929
| ApplicationPlanName | `applicationPlanName` | string | name of application plan that the application will use | Yes |
3030
| Suspend | `suspend` | bool | suspend application if true suspends application, if false resumes application | No |
31+
| AuthSecretRef | `authSecretRef` | object | [Auth secret reference](#Auth-secret-reference) | No |
3132

3233

3334

@@ -36,6 +37,56 @@ Created by [github-markdown-toc](https://github.com/ekalinin/github-markdown-toc
3637
Application CR relies on the provider account reference for the [developer account](./developeruser-reference.md#provider-account-reference)
3738
and the [product](./product-reference.md#provider-account-reference) being the same. If not you will see an error in the status.
3839

40+
#### Auth secret reference
41+
42+
Auth secret reference by a [v1.LocalObjectReference](https://v1-15.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.15/#localobjectreference-v1-core) type object.
43+
44+
| **Field** | **Description** | **Required** |
45+
| ----------------- | ------------------------------------------------------------------------------------------------------------------ | ------------ |
46+
| *UserKey* | UserKey field can be populated with a secret key or left an empty string if you wish to generate the secret | Yes |
47+
| *ApplicationID* | ApplicationID field can be populated with a secret key or left an empty string if you wish to generate the secret | Yes |
48+
| *ApplicationKey* | ApplicationKey field can be populated with a secret key or left an empty string if you wish to generate the secret | Yes |
49+
| *ClientID* | ClientID field can be populated with a secret key or left an empty string if you wish to generate the secret | Yes |
50+
| *ClientSecret* | ClientSecret field can be populated with a secret key or left an empty string if you wish to generate the secret | Yes |
51+
52+
53+
NOTE: ApplicationCR relies on ProductCR authentication mode to determine which fields to use from the secret
54+
55+
For example:
56+
* With UserKey authentication mode
57+
```
58+
apiVersion: v1
59+
kind: Secret
60+
metadata:
61+
name: authsecret
62+
type: Opaque
63+
stringData:
64+
UserKey: "testApplicationUserKey"
65+
```
66+
67+
* With AppID/AppKey authentication mode
68+
```
69+
apiVersion: v1
70+
kind: Secret
71+
metadata:
72+
name: authsecret
73+
type: Opaque
74+
stringData:
75+
ApplicationID: "testApplicationID"
76+
ApplicationKey: "testApplicationKey"
77+
```
78+
79+
* With OIDC authentication mode
80+
```
81+
apiVersion: v1
82+
kind: Secret
83+
metadata:
84+
name: authsecret
85+
type: Opaque
86+
stringData:
87+
ClientID: "testApplicationClientID"
88+
ClientSecret: "testApplicationClientSecret"
89+
```
3990

4091
### ApplicationStatus
4192

doc/operator-application-capabilities.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1672,6 +1672,11 @@ spec:
16721672
systemName: hits
16731673
backend: backend1
16741674
name: product1
1675+
deployment:
1676+
apicastHosted:
1677+
authentication:
1678+
appKeyAppID
1679+
appID: token
16751680
backendUsages:
16761681
backend1:
16771682
path: /
@@ -1734,6 +1739,41 @@ status:
17341739
providerAccountHost: 'https://3scale-admin.example.com'
17351740
state: suspended
17361741
```
1742+
1743+
By default, 3scale automatically generates a random and immutable ID for each application upon creation.
1744+
1745+
If you require an application to have a predefined ID, you must supply an authentication secret.
1746+
1747+
```
1748+
apiVersion: v1
1749+
kind: Secret
1750+
metadata:
1751+
name: authsecret
1752+
type: Opaque
1753+
stringData:
1754+
ApplicationID: "testApplicationID"
1755+
ApplicationKey: "testApplicationKey"
1756+
```
1757+
1758+
Reference the secret with `authSecretRef`
1759+
1760+
```yaml
1761+
apiVersion: capabilities.3scale.net/v1beta1
1762+
kind: Application
1763+
metadata:
1764+
name: example
1765+
spec:
1766+
accountCR:
1767+
name: developeraccount01
1768+
applicationPlanName: plan02
1769+
productCR:
1770+
name: product1-cr
1771+
name: testApp12
1772+
description: further testing12
1773+
suspend: true
1774+
authSecretRef: authsecret
1775+
```
1776+
17371777
[Application CRD reference](application-reference.md) for more info about fields.
17381778

17391779
### Application Custom Resource Status Fields

0 commit comments

Comments
 (0)