Skip to content

Commit 4962009

Browse files
committed
Add AppCred types and controller support
Signed-off-by: Veronika Fisarova <[email protected]>
1 parent 6db2b5a commit 4962009

12 files changed

+1266
-2
lines changed
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
---
2+
apiVersion: apiextensions.k8s.io/v1
3+
kind: CustomResourceDefinition
4+
metadata:
5+
annotations:
6+
controller-gen.kubebuilder.io/version: v0.14.0
7+
name: applicationcredentials.keystone.openstack.org
8+
spec:
9+
group: keystone.openstack.org
10+
names:
11+
kind: ApplicationCredential
12+
listKind: ApplicationCredentialList
13+
plural: applicationcredentials
14+
shortNames:
15+
- appcred
16+
singular: applicationcredential
17+
scope: Namespaced
18+
versions:
19+
- additionalPrinterColumns:
20+
- description: Keystone AC ID
21+
jsonPath: .status.acID
22+
name: ACID
23+
type: string
24+
- description: Secret holding AC secret
25+
jsonPath: .status.secretName
26+
name: SecretName
27+
type: string
28+
- description: Status
29+
jsonPath: .status.conditions[0].status
30+
name: Status
31+
type: string
32+
- description: Message
33+
jsonPath: .status.conditions[0].message
34+
name: Message
35+
type: string
36+
name: v1beta1
37+
schema:
38+
openAPIV3Schema:
39+
description: ApplicationCredential is the Schema for the applicationcredentials
40+
API
41+
properties:
42+
apiVersion:
43+
description: |-
44+
APIVersion defines the versioned schema of this representation of an object.
45+
Servers should convert recognized schemas to the latest internal value, and
46+
may reject unrecognized values.
47+
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
48+
type: string
49+
kind:
50+
description: |-
51+
Kind is a string value representing the REST resource this object represents.
52+
Servers may infer this from the endpoint the client submits requests to.
53+
Cannot be updated.
54+
In CamelCase.
55+
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
56+
type: string
57+
metadata:
58+
type: object
59+
spec:
60+
description: ApplicationCredentialSpec defines what the user can set
61+
properties:
62+
accessRules:
63+
description: AccessRules defines which services the AC is permitted
64+
to access
65+
items:
66+
description: ACRule defines a service-only access rule for an ApplicationCredential
67+
properties:
68+
method:
69+
description: Method is the HTTP verb to allow (defaults to all
70+
if empty)
71+
type: string
72+
path:
73+
description: Path is the API path to allow
74+
type: string
75+
service:
76+
description: Service is the OpenStack service type
77+
type: string
78+
type: object
79+
type: array
80+
expirationDays:
81+
default: 365
82+
description: ExpirationDays sets the lifetime in days for the AC
83+
minimum: 2
84+
type: integer
85+
gracePeriodDays:
86+
default: 182
87+
description: GracePeriodDays sets how many days before expiration
88+
the AC should be rotated
89+
minimum: 1
90+
type: integer
91+
passwordSelector:
92+
description: PasswordSelector for extracting the service password
93+
type: string
94+
roles:
95+
default:
96+
- admin
97+
- service
98+
description: Roles to assign to the ApplicationCredential
99+
items:
100+
type: string
101+
minItems: 1
102+
type: array
103+
secret:
104+
default: osp-secret
105+
description: Secret containing service user password
106+
type: string
107+
unrestricted:
108+
default: false
109+
description: Unrestricted indicates whether the AC may be used to
110+
create or destroy other credentials or trusts
111+
type: boolean
112+
userName:
113+
description: UserName - the Keystone user under which this AC is created
114+
type: string
115+
required:
116+
- userName
117+
type: object
118+
x-kubernetes-validations:
119+
- message: gracePeriodDays must be smaller than expirationDays
120+
rule: self.gracePeriodDays < self.expirationDays
121+
status:
122+
description: ApplicationCredentialStatus defines the observed state
123+
properties:
124+
acID:
125+
description: ACID - the ID in Keystone for this AC
126+
type: string
127+
conditions:
128+
description: Conditions
129+
items:
130+
description: Condition defines an observation of a API resource
131+
operational state.
132+
properties:
133+
lastTransitionTime:
134+
description: |-
135+
Last time the condition transitioned from one status to another.
136+
This should be when the underlying condition changed. If that is not known, then using the time when
137+
the API field changed is acceptable.
138+
format: date-time
139+
type: string
140+
message:
141+
description: A human readable message indicating details about
142+
the transition.
143+
type: string
144+
reason:
145+
description: The reason for the condition's last transition
146+
in CamelCase.
147+
type: string
148+
severity:
149+
description: |-
150+
Severity provides a classification of Reason code, so the current situation is immediately
151+
understandable and could act accordingly.
152+
It is meant for situations where Status=False and it should be indicated if it is just
153+
informational, warning (next reconciliation might fix it) or an error (e.g. DB create issue
154+
and no actions to automatically resolve the issue can/should be done).
155+
For conditions where Status=Unknown or Status=True the Severity should be SeverityNone.
156+
type: string
157+
status:
158+
description: Status of the condition, one of True, False, Unknown.
159+
type: string
160+
type:
161+
description: Type of condition in CamelCase.
162+
type: string
163+
required:
164+
- lastTransitionTime
165+
- status
166+
- type
167+
type: object
168+
type: array
169+
createdAt:
170+
description: CreatedAt - timestap of creation
171+
format: date-time
172+
type: string
173+
expiresAt:
174+
description: ExpiresAt - time of validity expiration
175+
format: date-time
176+
type: string
177+
secretName:
178+
description: SecretName - name of the k8s Secret storing the AC secret
179+
type: string
180+
type: object
181+
type: object
182+
served: true
183+
storage: true
184+
subresources:
185+
status: {}

api/v1beta1/keystoneapi.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,107 @@ func GetAdminServiceClient(
9292
return os, ctrlResult, nil
9393
}
9494

95+
// GetUserServiceClient - returns an *openstack.OpenStack object scoped as the given service user
96+
func GetUserServiceClient(
97+
ctx context.Context,
98+
h *helper.Helper,
99+
keystoneAPI *KeystoneAPI,
100+
userName string,
101+
secretName string,
102+
passwordSelector string,
103+
) (*openstack.OpenStack, ctrl.Result, error) {
104+
105+
authURL, err := keystoneAPI.GetEndpoint(endpoint.EndpointInternal)
106+
if err != nil {
107+
return nil, ctrl.Result{}, err
108+
}
109+
110+
parsedAuthURL, err := url.Parse(authURL)
111+
if err != nil {
112+
return nil, ctrl.Result{}, err
113+
}
114+
115+
tlsConfig := &openstack.TLSConfig{}
116+
if parsedAuthURL.Scheme == "https" && keystoneAPI.Spec.TLS.CaBundleSecretName != "" {
117+
caCert, ctrlResult, err := secret.GetDataFromSecret(
118+
ctx,
119+
h,
120+
keystoneAPI.Spec.TLS.CaBundleSecretName,
121+
10*time.Second,
122+
tls.InternalCABundleKey)
123+
if err != nil {
124+
return nil, ctrlResult, err
125+
}
126+
if (ctrlResult != ctrl.Result{}) {
127+
return nil, ctrlResult,
128+
fmt.Errorf("CABundleSecret %s not found",
129+
keystoneAPI.Spec.TLS.CaBundleSecretName)
130+
}
131+
132+
tlsConfig = &openstack.TLSConfig{
133+
CACerts: []string{caCert},
134+
}
135+
}
136+
137+
password, err := getPasswordFromOSPSecret(ctx, h, secretName, passwordSelector)
138+
if err != nil {
139+
return nil, ctrl.Result{}, fmt.Errorf("failed to get password from osp-secret for user %q: %w", userName, err)
140+
}
141+
142+
scope := &gophercloud.AuthScope{
143+
ProjectName: "service",
144+
DomainName: "Default",
145+
}
146+
147+
osClient, err := openstack.NewOpenStack(
148+
h.GetLogger(),
149+
openstack.AuthOpts{
150+
AuthURL: authURL,
151+
Username: userName,
152+
Password: password,
153+
TenantName: "service",
154+
DomainName: "Default",
155+
Region: keystoneAPI.Spec.Region,
156+
TLS: tlsConfig,
157+
Scope: scope,
158+
},
159+
)
160+
if err != nil {
161+
return nil, ctrl.Result{}, err
162+
}
163+
164+
// DEBUG
165+
logger := h.GetLogger()
166+
logger.Info("GetUserServiceClient debug",
167+
"authURL", authURL,
168+
"region", keystoneAPI.Spec.Region,
169+
"userName", userName,
170+
)
171+
172+
return osClient, ctrl.Result{}, nil
173+
}
174+
175+
func getPasswordFromOSPSecret(
176+
ctx context.Context,
177+
h *helper.Helper,
178+
ospSecretName, passwordSelector string,
179+
) (string, error) {
180+
data, res, err := secret.GetDataFromSecret(
181+
ctx,
182+
h,
183+
ospSecretName,
184+
10*time.Second,
185+
passwordSelector,
186+
)
187+
if err != nil {
188+
return "", fmt.Errorf("failed to get %q from Secret/%s: %w", passwordSelector, ospSecretName, err)
189+
}
190+
if res != (ctrl.Result{}) {
191+
return "", fmt.Errorf("secret/%s didn’t contain %q", ospSecretName, passwordSelector)
192+
}
193+
return data, nil
194+
}
195+
95196
// GetScopedAdminServiceClient - get a scoped admin serviceClient for the keystoneAPI instance
96197
func GetScopedAdminServiceClient(
97198
ctx context.Context,

0 commit comments

Comments
 (0)