Skip to content

Commit a1a7e6a

Browse files
committed
Application Credential support
Signed-off-by: Veronika Fisarova <vfisarov@redhat.com>
1 parent 9348332 commit a1a7e6a

File tree

7 files changed

+195
-11
lines changed

7 files changed

+195
-11
lines changed

api/go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,5 @@ replace k8s.io/component-base => k8s.io/component-base v0.31.13 //allow-merging
9595
replace github.com/rabbitmq/cluster-operator/v2 => github.com/openstack-k8s-operators/rabbitmq-cluster-operator/v2 v2.6.1-0.20250929174222-a0d328fa4dec //allow-merging
9696

9797
replace k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20250627150254-e9823e99808e //allow-merging
98+
99+
replace github.com/openstack-k8s-operators/keystone-operator/api => github.com/Deydra71/keystone-operator/api v0.0.0-20251103091514-244e15fe5d63

controllers/manila_controller.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,6 +1025,17 @@ func (r *ManilaReconciler) generateServiceConfig(
10251025
// Check if Quorum Queues are enabled
10261026
templateParameters["QuorumQueues"] = transportURLQuorumQueues
10271027

1028+
// Check for Application Credentials
1029+
templateParameters["UseApplicationCredentials"] = false
1030+
if acData, err := keystonev1.GetApplicationCredentialFromSecret(ctx, r.Client, instance.Namespace, manila.ServiceName); err != nil {
1031+
h.GetLogger().Error(err, "Failed to get ApplicationCredential for service", "service", manila.ServiceName)
1032+
} else if acData != nil {
1033+
templateParameters["UseApplicationCredentials"] = true
1034+
templateParameters["ApplicationCredentialID"] = acData.ID
1035+
templateParameters["ApplicationCredentialSecret"] = acData.Secret
1036+
h.GetLogger().Info("Using ApplicationCredentials auth", "service", manila.ServiceName)
1037+
}
1038+
10281039
// create httpd vhost template parameters
10291040
httpdVhostConfig := map[string]any{}
10301041
for _, endpt := range []service.Endpoint{service.EndpointInternal, service.EndpointPublic} {

controllers/manilaapi_controller.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,41 @@ func (r *ManilaAPIReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Man
276276
return nil
277277
}
278278

279+
// Application Credential secret watching function
280+
acSecretFn := func(_ context.Context, o client.Object) []reconcile.Request {
281+
name := o.GetName()
282+
ns := o.GetNamespace()
283+
result := []reconcile.Request{}
284+
285+
// Only handle Secret objects
286+
if _, isSecret := o.(*corev1.Secret); !isSecret {
287+
return nil
288+
}
289+
290+
// Check if this is a manila AC secret by name pattern (ac-manila-secret)
291+
if name == keystonev1.GetACSecretName(manila.ServiceName) {
292+
// get all ManilaAPI CRs in this namespace
293+
manilaAPIs := &manilav1beta1.ManilaAPIList{}
294+
listOpts := []client.ListOption{
295+
client.InNamespace(ns),
296+
}
297+
if err := r.List(context.Background(), manilaAPIs, listOpts...); err != nil {
298+
return nil
299+
}
300+
301+
// Enqueue reconcile for all manila API instances
302+
for _, cr := range manilaAPIs.Items {
303+
objKey := client.ObjectKey{
304+
Namespace: ns,
305+
Name: cr.Name,
306+
}
307+
result = append(result, reconcile.Request{NamespacedName: objKey})
308+
}
309+
}
310+
311+
return result
312+
}
313+
279314
// index passwordSecretField
280315
if err := mgr.GetFieldIndexer().IndexField(context.Background(), &manilav1beta1.ManilaAPI{}, passwordSecretField, func(rawObj client.Object) []string {
281316
// Extract the secret name from the spec, if one is provided
@@ -351,6 +386,8 @@ func (r *ManilaAPIReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Man
351386
handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc),
352387
builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}),
353388
).
389+
Watches(&corev1.Secret{},
390+
handler.EnqueueRequestsFromMapFunc(acSecretFn)).
354391
Watches(&topologyv1.Topology{},
355392
handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc),
356393
builder.WithPredicates(predicate.GenerationChangedPredicate{})).
@@ -725,6 +762,11 @@ func (r *ManilaAPIReconciler) reconcileNormal(ctx context.Context, instance *man
725762
}
726763
instance.Status.Conditions.MarkTrue(condition.InputReadyCondition, condition.InputReadyMessage)
727764

765+
// Verify Application Credentials if available
766+
if ctrlResult, err = keystonev1.VerifyApplicationCredentialsForService(ctx, r.Client, instance.Namespace, manila.ServiceName, &configVars, manila.NormalDuration); err != nil || ctrlResult.RequeueAfter > 0 {
767+
return ctrlResult, err
768+
}
769+
728770
//
729771
// TLS input validation
730772
//

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,5 @@ replace k8s.io/component-base => k8s.io/component-base v0.31.13 //allow-merging
119119
replace github.com/rabbitmq/cluster-operator/v2 => github.com/openstack-k8s-operators/rabbitmq-cluster-operator/v2 v2.6.1-0.20250929174222-a0d328fa4dec //allow-merging
120120

121121
replace k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20250627150254-e9823e99808e //allow-merging
122+
123+
replace github.com/openstack-k8s-operators/keystone-operator/api => github.com/Deydra71/keystone-operator/api v0.0.0-20251103091514-244e15fe5d63

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
github.com/Deydra71/keystone-operator/api v0.0.0-20251103091514-244e15fe5d63 h1:ug2YPMQJ/+0ifOjFyaPx1YtX0zsVnL02pB2ngacYviw=
2+
github.com/Deydra71/keystone-operator/api v0.0.0-20251103091514-244e15fe5d63/go.mod h1:FMFoO4MjEQ85JpdLtDHxYSZxvJ9KzHua+HdKhpl0KRI=
13
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
24
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
35
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -100,8 +102,6 @@ github.com/openshift/api v0.0.0-20250711200046-c86d80652a9e h1:E1OdwSpqWuDPCedyU
100102
github.com/openshift/api v0.0.0-20250711200046-c86d80652a9e/go.mod h1:Shkl4HanLwDiiBzakv+con/aMGnVE2MAGvoKp5oyYUo=
101103
github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251030184102-82d2cbaafd35 h1:QFFGu93A+XCvDUxZIgfBE4gB5hEdVQAIw+E8dF1kP/E=
102104
github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251030184102-82d2cbaafd35/go.mod h1:qq8BCRxTEmLRriUsQ4HeDUzqltWg32MQPDTMhgbBGK4=
103-
github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20251027074845-ed8154b20ad1 h1:QohvX44nxoV2GwvvOURGXYyDuCn4SCrnwubTKJtzehY=
104-
github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20251027074845-ed8154b20ad1/go.mod h1:FMFoO4MjEQ85JpdLtDHxYSZxvJ9KzHua+HdKhpl0KRI=
105105
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251027074416-ab5c045dbe00 h1:Xih6tYYqiDVllo4fDGHqTPL+M2biO5YLOUmbiTqrW/I=
106106
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251027074416-ab5c045dbe00/go.mod h1:PMoNILOdQ1Ij7DyrKgljN6RAiq8pFM2AGsUb6mcxe98=
107107
github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20251021145236-2b84ec9fd9bb h1:wToXqX7AS1JV3Kna7RcJfkRart8rSGun2biKNfyY6Zg=

templates/manila/config/00-config.conf

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,18 @@ auth_url = {{ .KeystoneInternalURL }}
3131
memcached_servers = {{ .MemcachedServers }}
3232
memcache_pool_dead_retry = 10
3333
memcache_pool_conn_get_timeout = 2
34+
{{ if .UseApplicationCredentials -}}
35+
auth_type = v3applicationcredential
36+
application_credential_id = {{ .ApplicationCredentialID }}
37+
application_credential_secret = {{ .ApplicationCredentialSecret }}
38+
{{ else -}}
3439
auth_type = password
40+
username = {{ .ServiceUser }}
41+
password = {{ .ServicePassword }}
42+
{{- end }}
3543
project_domain_name = Default
3644
user_domain_name = Default
3745
project_name = service
38-
username = {{ .ServiceUser }}
39-
password = {{ .ServicePassword }}
4046
interface = internal
4147
{{if (index . "MemcachedAuthCert")}}
4248
memcache_tls_certfile = {{ .MemcachedAuthCert }}
@@ -47,19 +53,31 @@ memcache_tls_enabled = true
4753

4854
[neutron]
4955
auth_url = {{ .KeystoneInternalURL }}
50-
auth_type=password
56+
{{ if .UseApplicationCredentials -}}
57+
auth_type = v3applicationcredential
58+
application_credential_id = {{ .ApplicationCredentialID }}
59+
application_credential_secret = {{ .ApplicationCredentialSecret }}
60+
{{ else -}}
61+
auth_type = password
62+
username = {{ .ServiceUser }}
63+
password = {{ .ServicePassword }}
64+
{{- end }}
5165
project_domain_name=Default
5266
project_name=service
5367
user_domain_name=Default
54-
username = {{ .ServiceUser }}
55-
password = {{ .ServicePassword }}
5668

5769
[nova]
5870
interface = internal
71+
{{ if .UseApplicationCredentials -}}
72+
auth_type = v3applicationcredential
73+
application_credential_id = {{ .ApplicationCredentialID }}
74+
application_credential_secret = {{ .ApplicationCredentialSecret }}
75+
{{ else -}}
5976
auth_type = password
60-
auth_url = {{ .KeystoneInternalURL }}
6177
username = {{ .ServiceUser }}
6278
password = {{ .ServicePassword }}
79+
{{- end }}
80+
auth_url = {{ .KeystoneInternalURL }}
6381
user_domain_name = Default
6482
project_name = service
6583
project_domain_name = Default
@@ -104,12 +122,18 @@ file_event_handler=/var/lib/manila
104122
auth_endpoint = {{ .KeystoneInternalURL }}
105123
barbican_endpoint_type = internal
106124
auth_url = {{ .KeystoneInternalURL }}
107-
auth_type=password
125+
{{ if .UseApplicationCredentials -}}
126+
auth_type = v3applicationcredential
127+
application_credential_id = {{ .ApplicationCredentialID }}
128+
application_credential_secret = {{ .ApplicationCredentialSecret }}
129+
{{ else -}}
130+
auth_type = password
131+
username = {{ .ServiceUser }}
132+
password = {{ .ServicePassword }}
133+
{{- end }}
108134
project_domain_name=Default
109135
project_name=service
110136
user_domain_name=Default
111-
username = {{ .ServiceUser }}
112-
password = {{ .ServicePassword }}
113137

114138
[key_manager]
115139
backend = barbican

test/functional/manila_controller_test.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ import (
2727
. "github.com/openstack-k8s-operators/lib-common/modules/common/test/helpers"
2828
mariadb_test "github.com/openstack-k8s-operators/mariadb-operator/api/test/helpers"
2929
corev1 "k8s.io/api/core/v1"
30+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3031
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
3132
"k8s.io/apimachinery/pkg/types"
3233

3334
memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1"
3435
topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1"
36+
keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1"
3537
condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition"
3638
util "github.com/openstack-k8s-operators/lib-common/modules/common/util"
3739
manilav1 "github.com/openstack-k8s-operators/manila-operator/api/v1beta1"
@@ -1459,6 +1461,106 @@ var _ = Describe("Manila controller", func() {
14591461

14601462
})
14611463

1464+
When("An ApplicationCredential is created for Manila", func() {
1465+
var (
1466+
acName string
1467+
acSecretName string
1468+
servicePasswordSecret string
1469+
passwordSelector string
1470+
)
1471+
BeforeEach(func() {
1472+
servicePasswordSecret = "ac-test-osp-secret" //nolint:gosec // G101
1473+
passwordSelector = "ManilaPassword"
1474+
1475+
DeferCleanup(th.DeleteInstance, CreateManilaSecret(manilaTest.Instance.Namespace, servicePasswordSecret))
1476+
DeferCleanup(th.DeleteInstance, CreateManilaMessageBusSecret(manilaTest.Instance.Namespace, manilaTest.RabbitmqSecretName))
1477+
DeferCleanup(
1478+
infra.DeleteMemcached,
1479+
infra.CreateMemcached(manilaTest.ManilaMemcached.Namespace, manilaTest.MemcachedInstance, memcachedSpec))
1480+
infra.SimulateMemcachedReady(manilaTest.ManilaMemcached)
1481+
1482+
spec := GetDefaultManilaSpec()
1483+
spec["secret"] = servicePasswordSecret
1484+
DeferCleanup(th.DeleteInstance, CreateManila(manilaTest.Instance, spec))
1485+
DeferCleanup(
1486+
mariadb.DeleteDBService,
1487+
mariadb.CreateDBService(
1488+
manilaTest.ManilaDatabaseName.Namespace,
1489+
GetManila(manilaTest.Instance).Spec.DatabaseInstance,
1490+
corev1.ServiceSpec{
1491+
Ports: []corev1.ServicePort{{Port: 3306}},
1492+
},
1493+
),
1494+
)
1495+
DeferCleanup(keystone.DeleteKeystoneAPI, keystone.CreateKeystoneAPI(manilaTest.Instance.Namespace))
1496+
1497+
acName = fmt.Sprintf("ac-%s", manila.ServiceName)
1498+
acSecretName = acName + "-secret"
1499+
secret := &corev1.Secret{}
1500+
secret.Name = acSecretName
1501+
secret.Namespace = manilaTest.Instance.Namespace
1502+
secret.Data = map[string][]byte{
1503+
"AC_ID": []byte("test-ac-id"),
1504+
"AC_SECRET": []byte("test-ac-secret"),
1505+
}
1506+
DeferCleanup(k8sClient.Delete, ctx, secret)
1507+
Expect(k8sClient.Create(ctx, secret)).To(Succeed())
1508+
1509+
ac := &keystonev1.KeystoneApplicationCredential{
1510+
ObjectMeta: metav1.ObjectMeta{
1511+
Namespace: manilaTest.Instance.Namespace,
1512+
Name: acName,
1513+
},
1514+
Spec: keystonev1.KeystoneApplicationCredentialSpec{
1515+
UserName: manila.ServiceName,
1516+
Secret: servicePasswordSecret,
1517+
PasswordSelector: passwordSelector,
1518+
Roles: []string{"admin", "member"},
1519+
AccessRules: []keystonev1.ACRule{{Service: "identity", Method: "POST", Path: "/auth/tokens"}},
1520+
ExpirationDays: 30,
1521+
GracePeriodDays: 5,
1522+
},
1523+
}
1524+
DeferCleanup(k8sClient.Delete, ctx, ac)
1525+
Expect(k8sClient.Create(ctx, ac)).To(Succeed())
1526+
1527+
fetched := &keystonev1.KeystoneApplicationCredential{}
1528+
key := types.NamespacedName{Namespace: ac.Namespace, Name: ac.Name}
1529+
Expect(k8sClient.Get(ctx, key, fetched)).To(Succeed())
1530+
1531+
fetched.Status.SecretName = acSecretName
1532+
now := metav1.Now()
1533+
readyCond := condition.Condition{
1534+
Type: condition.ReadyCondition,
1535+
Status: corev1.ConditionTrue,
1536+
Reason: condition.ReadyReason,
1537+
Message: condition.ReadyMessage,
1538+
LastTransitionTime: now,
1539+
}
1540+
fetched.Status.Conditions = condition.Conditions{readyCond}
1541+
Expect(k8sClient.Status().Update(ctx, fetched)).To(Succeed())
1542+
1543+
infra.SimulateTransportURLReady(manilaTest.ManilaTransportURL)
1544+
mariadb.SimulateMariaDBAccountCompleted(manilaTest.ManilaDatabaseAccount)
1545+
mariadb.SimulateMariaDBDatabaseCompleted(manilaTest.ManilaDatabaseName)
1546+
})
1547+
1548+
It("should render ApplicationCredential auth in 00-config.conf", func() {
1549+
// Retrieve the generated config secret
1550+
configDataMap := th.GetSecret(manilaTest.ManilaConfigSecret)
1551+
1552+
conf := configDataMap.Data["00-config.conf"]
1553+
Expect(string(conf)).Should(
1554+
ContainSubstring("auth_type = v3applicationcredential"))
1555+
Expect(string(conf)).Should(
1556+
ContainSubstring("application_credential_id = test-ac-id"))
1557+
Expect(string(conf)).Should(
1558+
ContainSubstring("application_credential_secret = test-ac-secret"))
1559+
Expect(string(conf)).Should(
1560+
Not(ContainSubstring("auth_type = password")))
1561+
})
1562+
})
1563+
14621564
})
14631565

14641566
var _ = Describe("Manila Webhook", func() {
@@ -1567,4 +1669,5 @@ var _ = Describe("Manila Webhook", func() {
15671669
return instance, fmt.Sprintf("manilaShares[%s].topologyRef", instance)
15681670
}),
15691671
)
1672+
15701673
})

0 commit comments

Comments
 (0)