Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions api/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,5 @@ replace k8s.io/component-base => k8s.io/component-base v0.31.13 //allow-merging
replace github.com/rabbitmq/cluster-operator/v2 => github.com/openstack-k8s-operators/rabbitmq-cluster-operator/v2 v2.6.1-0.20250929174222-a0d328fa4dec //allow-merging

replace k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20250627150254-e9823e99808e //allow-merging

replace github.com/openstack-k8s-operators/keystone-operator/api => github.com/Deydra71/keystone-operator/api v0.0.0-20251110074936-69e69f698212
54 changes: 54 additions & 0 deletions controllers/placementapi_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,11 @@ func (r *PlacementAPIReconciler) Reconcile(ctx context.Context, req ctrl.Request
return result, nil
}

// Verify Application Credentials if available
if result, err := keystonev1.VerifyApplicationCredentialsForService(ctx, r.Client, instance.Namespace, placement.ServiceName, &configMapVars, 10*time.Second); err != nil || result.RequeueAfter > 0 {
return result, err
}

err = r.generateServiceConfigMaps(ctx, h, instance, secret, &configMapVars, db)
if err != nil {
instance.Status.Conditions.Set(condition.FalseCondition(
Expand Down Expand Up @@ -859,6 +864,41 @@ var allWatchFields = []string{
topologyField,
}

// Application Credential secret watching function
func (r *PlacementAPIReconciler) acSecretFn(_ context.Context, o client.Object) []reconcile.Request {
name := o.GetName()
ns := o.GetNamespace()
result := []reconcile.Request{}

// Only handle Secret objects
if _, isSecret := o.(*corev1.Secret); !isSecret {
return nil
}

// Check if this is a placement AC secret by name pattern (ac-placement-secret)
expectedSecretName := keystonev1.GetACSecretName(placement.ServiceName)
if name == expectedSecretName {
// get all PlacementAPI CRs in this namespace
placementAPIs := &placementv1.PlacementAPIList{}
listOpts := []client.ListOption{
client.InNamespace(ns),
}
if err := r.List(context.Background(), placementAPIs, listOpts...); err != nil {
return nil
}

for _, cr := range placementAPIs.Items {
result = append(result, reconcile.Request{
NamespacedName: types.NamespacedName{
Namespace: cr.Namespace,
Name: cr.Name,
},
})
}
}
return result
}

// SetupWithManager sets up the controller with the Manager.
func (r *PlacementAPIReconciler) SetupWithManager(mgr ctrl.Manager) error {
// index passwordSecretField
Expand Down Expand Up @@ -940,6 +980,8 @@ func (r *PlacementAPIReconciler) SetupWithManager(mgr ctrl.Manager) error {
handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc),
builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}),
).
Watches(&corev1.Secret{},
handler.EnqueueRequestsFromMapFunc(r.acSecretFn)).
Watches(&topologyv1.Topology{},
handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc),
builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Expand Down Expand Up @@ -1378,6 +1420,18 @@ func (r *PlacementAPIReconciler) generateServiceConfigMaps(
),
}

templateParameters["UseApplicationCredentials"] = false
// Try to get Application Credential for this service
if acData, err := keystonev1.GetApplicationCredentialFromSecret(ctx, r.Client, instance.Namespace, placement.ServiceName); err != nil {
h.GetLogger().Error(err, "Failed to get ApplicationCredential for service", "service", placement.ServiceName)
return err
} else if acData != nil {
templateParameters["UseApplicationCredentials"] = true
templateParameters["ACID"] = acData.ID
templateParameters["ACSecret"] = acData.Secret
h.GetLogger().Info("Using ApplicationCredentials auth", "service", placement.ServiceName)
}

// create httpd vhost template parameters
httpdVhostConfig := map[string]any{}
for _, endpt := range []service.Endpoint{service.EndpointInternal, service.EndpointPublic} {
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,5 @@ replace k8s.io/component-base => k8s.io/component-base v0.31.13 //allow-merging
replace github.com/rabbitmq/cluster-operator/v2 => github.com/openstack-k8s-operators/rabbitmq-cluster-operator/v2 v2.6.1-0.20250929174222-a0d328fa4dec //allow-merging

replace k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20250627150254-e9823e99808e //allow-merging

replace github.com/openstack-k8s-operators/keystone-operator/api => github.com/Deydra71/keystone-operator/api v0.0.0-20251110074936-69e69f698212
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/Deydra71/keystone-operator/api v0.0.0-20251110074936-69e69f698212 h1:m/OIYRcJvoKnfhoJCAJDwN54cnu495rgQAngyGPTKvc=
github.com/Deydra71/keystone-operator/api v0.0.0-20251110074936-69e69f698212/go.mod h1:FMFoO4MjEQ85JpdLtDHxYSZxvJ9KzHua+HdKhpl0KRI=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
Expand Down Expand Up @@ -100,8 +102,6 @@ github.com/openshift/api v0.0.0-20250711200046-c86d80652a9e h1:E1OdwSpqWuDPCedyU
github.com/openshift/api v0.0.0-20250711200046-c86d80652a9e/go.mod h1:Shkl4HanLwDiiBzakv+con/aMGnVE2MAGvoKp5oyYUo=
github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251030184102-82d2cbaafd35 h1:QFFGu93A+XCvDUxZIgfBE4gB5hEdVQAIw+E8dF1kP/E=
github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251030184102-82d2cbaafd35/go.mod h1:qq8BCRxTEmLRriUsQ4HeDUzqltWg32MQPDTMhgbBGK4=
github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20251027074845-ed8154b20ad1 h1:QohvX44nxoV2GwvvOURGXYyDuCn4SCrnwubTKJtzehY=
github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20251027074845-ed8154b20ad1/go.mod h1:FMFoO4MjEQ85JpdLtDHxYSZxvJ9KzHua+HdKhpl0KRI=
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251027074416-ab5c045dbe00 h1:Xih6tYYqiDVllo4fDGHqTPL+M2biO5YLOUmbiTqrW/I=
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251027074416-ab5c045dbe00/go.mod h1:PMoNILOdQ1Ij7DyrKgljN6RAiq8pFM2AGsUb6mcxe98=
github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20251021145236-2b84ec9fd9bb h1:wToXqX7AS1JV3Kna7RcJfkRart8rSGun2biKNfyY6Zg=
Expand Down
16 changes: 11 additions & 5 deletions templates/placementapi/config/placement.conf
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,20 @@ connection = {{ .DatabaseConnection }}
auth_strategy = keystone

[keystone_authtoken]
project_domain_name = Default
user_domain_name = Default
project_name = service
username = {{ .ServiceUser }}
password = {{ .PlacementPassword }}
www_authenticate_uri = {{ .KeystonePublicURL }}
auth_url = {{ .KeystoneInternalURL }}
{{ if .UseApplicationCredentials -}}
auth_type = v3applicationcredential
application_credential_id = {{ .ACID }}
application_credential_secret = {{ .ACSecret }}
{{ else -}}
auth_type = password
username = {{ .ServiceUser }}
password = {{ .PlacementPassword }}
{{- end }}
user_domain_name = Default
project_domain_name = Default
project_name = service
interface = internal

[oslo_policy]
Expand Down
42 changes: 42 additions & 0 deletions tests/functional/placementapi_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1438,4 +1438,46 @@ var _ = Describe("PlacementAPI reconfiguration", func() {

})

When("an ApplicationCredential is created for Placement", func() {
BeforeEach(func() {
DeferCleanup(th.DeleteInstance, CreatePlacementAPI(names.PlacementAPIName, GetDefaultPlacementAPISpec()))
DeferCleanup(
k8sClient.Delete, ctx, CreatePlacementAPISecret(names.Namespace, SecretName))
keystoneAPIName := keystone.CreateKeystoneAPI(names.Namespace)
DeferCleanup(keystone.DeleteKeystoneAPI, keystoneAPIName)

DeferCleanup(
mariadb.DeleteDBService,
mariadb.CreateDBService(
names.Namespace,
GetDefaultPlacementAPISpec()["databaseInstance"].(string),
corev1.ServiceSpec{
Ports: []corev1.ServicePort{{Port: 3306}},
},
),
)
mariadb.SimulateMariaDBDatabaseCompleted(names.MariaDBDatabaseName)
mariadb.SimulateMariaDBAccountCompleted(names.MariaDBAccount)

// Create AC secret
acSecretName := fmt.Sprintf("ac-%s-secret", placement.ServiceName)
acSecret := th.CreateSecret(
types.NamespacedName{Namespace: names.Namespace, Name: acSecretName},
map[string][]byte{
"AC_ID": []byte("test-ac-id"),
"AC_SECRET": []byte("test-ac-secret"),
},
)
DeferCleanup(th.DeleteInstance, acSecret)
})

It("should render ApplicationCredential auth in placement.conf", func() {
configSecret := th.GetSecret(names.ConfigMapName)
conf := configSecret.Data["placement.conf"]

Expect(conf).To(ContainSubstring("application_credential_id = test-ac-id"))
Expect(conf).To(ContainSubstring("application_credential_secret = test-ac-secret"))
})
})

})
Loading