Skip to content

Commit 09e4eb3

Browse files
committed
Application Credential support
Adds the end-to-end support for consuming Keystone ApplicationCredentials (AC) in the Placement operator, enabling Placement pod to use AC-based authentication when available. Signed-off-by: Veronika Fisarova <[email protected]>
1 parent 59f5c91 commit 09e4eb3

File tree

6 files changed

+113
-7
lines changed

6 files changed

+113
-7
lines changed

api/go.mod

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

9696
replace k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20250627150254-e9823e99808e //allow-merging
97+
98+
replace github.com/openstack-k8s-operators/keystone-operator/api => github.com/Deydra71/keystone-operator/api v0.0.0-20251110074936-69e69f698212

controllers/placementapi_controller.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,11 @@ func (r *PlacementAPIReconciler) Reconcile(ctx context.Context, req ctrl.Request
365365
return result, nil
366366
}
367367

368+
// Verify Application Credentials if available
369+
if result, err := keystonev1.VerifyApplicationCredentialsForService(ctx, r.Client, instance.Namespace, placement.ServiceName, &configMapVars, 10*time.Second); err != nil || result.RequeueAfter > 0 {
370+
return result, err
371+
}
372+
368373
err = r.generateServiceConfigMaps(ctx, h, instance, secret, &configMapVars, db)
369374
if err != nil {
370375
instance.Status.Conditions.Set(condition.FalseCondition(
@@ -859,6 +864,41 @@ var allWatchFields = []string{
859864
topologyField,
860865
}
861866

867+
// Application Credential secret watching function
868+
func (r *PlacementAPIReconciler) acSecretFn(_ context.Context, o client.Object) []reconcile.Request {
869+
name := o.GetName()
870+
ns := o.GetNamespace()
871+
result := []reconcile.Request{}
872+
873+
// Only handle Secret objects
874+
if _, isSecret := o.(*corev1.Secret); !isSecret {
875+
return nil
876+
}
877+
878+
// Check if this is a placement AC secret by name pattern (ac-placement-secret)
879+
expectedSecretName := keystonev1.GetACSecretName(placement.ServiceName)
880+
if name == expectedSecretName {
881+
// get all PlacementAPI CRs in this namespace
882+
placementAPIs := &placementv1.PlacementAPIList{}
883+
listOpts := []client.ListOption{
884+
client.InNamespace(ns),
885+
}
886+
if err := r.List(context.Background(), placementAPIs, listOpts...); err != nil {
887+
return nil
888+
}
889+
890+
for _, cr := range placementAPIs.Items {
891+
result = append(result, reconcile.Request{
892+
NamespacedName: types.NamespacedName{
893+
Namespace: cr.Namespace,
894+
Name: cr.Name,
895+
},
896+
})
897+
}
898+
}
899+
return result
900+
}
901+
862902
// SetupWithManager sets up the controller with the Manager.
863903
func (r *PlacementAPIReconciler) SetupWithManager(mgr ctrl.Manager) error {
864904
// index passwordSecretField
@@ -940,6 +980,8 @@ func (r *PlacementAPIReconciler) SetupWithManager(mgr ctrl.Manager) error {
940980
handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc),
941981
builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}),
942982
).
983+
Watches(&corev1.Secret{},
984+
handler.EnqueueRequestsFromMapFunc(r.acSecretFn)).
943985
Watches(&topologyv1.Topology{},
944986
handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc),
945987
builder.WithPredicates(predicate.GenerationChangedPredicate{})).
@@ -1378,6 +1420,18 @@ func (r *PlacementAPIReconciler) generateServiceConfigMaps(
13781420
),
13791421
}
13801422

1423+
templateParameters["UseApplicationCredentials"] = false
1424+
// Try to get Application Credential for this service
1425+
if acData, err := keystonev1.GetApplicationCredentialFromSecret(ctx, r.Client, instance.Namespace, placement.ServiceName); err != nil {
1426+
h.GetLogger().Error(err, "Failed to get ApplicationCredential for service", "service", placement.ServiceName)
1427+
return err
1428+
} else if acData != nil {
1429+
templateParameters["UseApplicationCredentials"] = true
1430+
templateParameters["ACID"] = acData.ID
1431+
templateParameters["ACSecret"] = acData.Secret
1432+
h.GetLogger().Info("Using ApplicationCredentials auth", "service", placement.ServiceName)
1433+
}
1434+
13811435
// create httpd vhost template parameters
13821436
httpdVhostConfig := map[string]any{}
13831437
for _, endpt := range []service.Endpoint{service.EndpointInternal, service.EndpointPublic} {

go.mod

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

117117
replace k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20250627150254-e9823e99808e //allow-merging
118+
119+
replace github.com/openstack-k8s-operators/keystone-operator/api => github.com/Deydra71/keystone-operator/api v0.0.0-20251110074936-69e69f698212

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-20251110074936-69e69f698212 h1:m/OIYRcJvoKnfhoJCAJDwN54cnu495rgQAngyGPTKvc=
2+
github.com/Deydra71/keystone-operator/api v0.0.0-20251110074936-69e69f698212/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/placementapi/config/placement.conf

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,20 @@ connection = {{ .DatabaseConnection }}
1515
auth_strategy = keystone
1616

1717
[keystone_authtoken]
18-
project_domain_name = Default
19-
user_domain_name = Default
20-
project_name = service
21-
username = {{ .ServiceUser }}
22-
password = {{ .PlacementPassword }}
2318
www_authenticate_uri = {{ .KeystonePublicURL }}
2419
auth_url = {{ .KeystoneInternalURL }}
20+
{{ if .UseApplicationCredentials -}}
21+
auth_type = v3applicationcredential
22+
application_credential_id = {{ .ACID }}
23+
application_credential_secret = {{ .ACSecret }}
24+
{{ else -}}
2525
auth_type = password
26+
username = {{ .ServiceUser }}
27+
password = {{ .PlacementPassword }}
28+
{{- end }}
29+
user_domain_name = Default
30+
project_domain_name = Default
31+
project_name = service
2632
interface = internal
2733

2834
[oslo_policy]

tests/functional/placementapi_controller_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1438,4 +1438,46 @@ var _ = Describe("PlacementAPI reconfiguration", func() {
14381438

14391439
})
14401440

1441+
When("an ApplicationCredential is created for Placement", func() {
1442+
BeforeEach(func() {
1443+
DeferCleanup(th.DeleteInstance, CreatePlacementAPI(names.PlacementAPIName, GetDefaultPlacementAPISpec()))
1444+
DeferCleanup(
1445+
k8sClient.Delete, ctx, CreatePlacementAPISecret(names.Namespace, SecretName))
1446+
keystoneAPIName := keystone.CreateKeystoneAPI(names.Namespace)
1447+
DeferCleanup(keystone.DeleteKeystoneAPI, keystoneAPIName)
1448+
1449+
DeferCleanup(
1450+
mariadb.DeleteDBService,
1451+
mariadb.CreateDBService(
1452+
names.Namespace,
1453+
GetDefaultPlacementAPISpec()["databaseInstance"].(string),
1454+
corev1.ServiceSpec{
1455+
Ports: []corev1.ServicePort{{Port: 3306}},
1456+
},
1457+
),
1458+
)
1459+
mariadb.SimulateMariaDBDatabaseCompleted(names.MariaDBDatabaseName)
1460+
mariadb.SimulateMariaDBAccountCompleted(names.MariaDBAccount)
1461+
1462+
// Create AC secret
1463+
acSecretName := fmt.Sprintf("ac-%s-secret", placement.ServiceName)
1464+
acSecret := th.CreateSecret(
1465+
types.NamespacedName{Namespace: names.Namespace, Name: acSecretName},
1466+
map[string][]byte{
1467+
"AC_ID": []byte("test-ac-id"),
1468+
"AC_SECRET": []byte("test-ac-secret"),
1469+
},
1470+
)
1471+
DeferCleanup(th.DeleteInstance, acSecret)
1472+
})
1473+
1474+
It("should render ApplicationCredential auth in placement.conf", func() {
1475+
configSecret := th.GetSecret(names.ConfigMapName)
1476+
conf := configSecret.Data["placement.conf"]
1477+
1478+
Expect(conf).To(ContainSubstring("application_credential_id = test-ac-id"))
1479+
Expect(conf).To(ContainSubstring("application_credential_secret = test-ac-secret"))
1480+
})
1481+
})
1482+
14411483
})

0 commit comments

Comments
 (0)