diff --git a/controllers/ironic_controller.go b/controllers/ironic_controller.go index 7a2f904d..19fdf8eb 100644 --- a/controllers/ironic_controller.go +++ b/controllers/ironic_controller.go @@ -43,9 +43,13 @@ import ( k8s_errors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/reconcile" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" @@ -228,9 +232,43 @@ func (r *IronicReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&corev1.ServiceAccount{}). Owns(&rbacv1.Role{}). Owns(&rbacv1.RoleBinding{}). + Watches(&keystonev1.KeystoneAPI{}, + handler.EnqueueRequestsFromMapFunc(r.findObjectForSrc), + builder.WithPredicates(keystonev1.KeystoneAPIStatusChangedPredicate)). Complete(r) } +func (r *IronicReconciler) findObjectForSrc(ctx context.Context, src client.Object) []reconcile.Request { + requests := []reconcile.Request{} + + l := log.FromContext(ctx).WithName("Controllers").WithName("Ironic") + + crList := &ironicv1.IronicList{} + listOps := &client.ListOptions{ + Namespace: src.GetNamespace(), + } + err := r.Client.List(ctx, crList, listOps) + if err != nil { + l.Error(err, fmt.Sprintf("listing %s for namespace: %s", crList.GroupVersionKind().Kind, src.GetNamespace())) + return requests + } + + for _, item := range crList.Items { + l.Info(fmt.Sprintf("input source %s changed, reconcile: %s - %s", src.GetName(), item.GetName(), item.GetNamespace())) + + requests = append(requests, + reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: item.GetName(), + Namespace: item.GetNamespace(), + }, + }, + ) + } + + return requests +} + func (r *IronicReconciler) reconcileDelete(ctx context.Context, instance *ironicv1.Ironic, helper *helper.Helper) (ctrl.Result, error) { Log := r.GetLogger(ctx) diff --git a/controllers/ironicinspector_controller.go b/controllers/ironicinspector_controller.go index 1f15903d..f4ea3527 100644 --- a/controllers/ironicinspector_controller.go +++ b/controllers/ironicinspector_controller.go @@ -43,6 +43,7 @@ import ( routev1 "github.com/openshift/api/route/v1" k8s_errors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/types" k8s_types "k8s.io/apimachinery/pkg/types" "k8s.io/utils/ptr" @@ -384,6 +385,9 @@ func (r *IronicInspectorReconciler) SetupWithManager( Watches(&topologyv1.Topology{}, handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc), builder.WithPredicates(predicate.GenerationChangedPredicate{})). + Watches(&keystonev1.KeystoneAPI{}, + handler.EnqueueRequestsFromMapFunc(r.findObjectForSrc), + builder.WithPredicates(keystonev1.KeystoneAPIStatusChangedPredicate)). Complete(r) } @@ -421,6 +425,37 @@ func (r *IronicInspectorReconciler) findObjectsForSrc(ctx context.Context, src c return requests } +func (r *IronicInspectorReconciler) findObjectForSrc(ctx context.Context, src client.Object) []reconcile.Request { + requests := []reconcile.Request{} + + l := log.FromContext(ctx).WithName("Controllers").WithName("IronicInspector") + + crList := &ironicv1.IronicInspectorList{} + listOps := &client.ListOptions{ + Namespace: src.GetNamespace(), + } + err := r.Client.List(ctx, crList, listOps) + if err != nil { + l.Error(err, fmt.Sprintf("listing %s for namespace: %s", crList.GroupVersionKind().Kind, src.GetNamespace())) + return requests + } + + for _, item := range crList.Items { + l.Info(fmt.Sprintf("input source %s changed, reconcile: %s - %s", src.GetName(), item.GetName(), item.GetNamespace())) + + requests = append(requests, + reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: item.GetName(), + Namespace: item.GetNamespace(), + }, + }, + ) + } + + return requests +} + func (r *IronicInspectorReconciler) getTransportURL( ctx context.Context, h *helper.Helper, diff --git a/controllers/ironicneutronagent_controller.go b/controllers/ironicneutronagent_controller.go index 02985ff0..b79cb0be 100644 --- a/controllers/ironicneutronagent_controller.go +++ b/controllers/ironicneutronagent_controller.go @@ -280,6 +280,9 @@ func (r *IronicNeutronAgentReconciler) SetupWithManager( Watches(&topologyv1.Topology{}, handler.EnqueueRequestsFromMapFunc(r.findObjectsForSrc), builder.WithPredicates(predicate.GenerationChangedPredicate{})). + Watches(&keystonev1.KeystoneAPI{}, + handler.EnqueueRequestsFromMapFunc(r.findObjectForSrc), + builder.WithPredicates(keystonev1.KeystoneAPIStatusChangedPredicate)). Complete(r) } @@ -317,6 +320,37 @@ func (r *IronicNeutronAgentReconciler) findObjectsForSrc(ctx context.Context, sr return requests } +func (r *IronicNeutronAgentReconciler) findObjectForSrc(ctx context.Context, src client.Object) []reconcile.Request { + requests := []reconcile.Request{} + + l := log.FromContext(ctx).WithName("Controllers").WithName("IronicNeutronAgent") + + crList := &ironicv1.IronicNeutronAgentList{} + listOps := &client.ListOptions{ + Namespace: src.GetNamespace(), + } + err := r.Client.List(ctx, crList, listOps) + if err != nil { + l.Error(err, fmt.Sprintf("listing %s for namespace: %s", crList.GroupVersionKind().Kind, src.GetNamespace())) + return requests + } + + for _, item := range crList.Items { + l.Info(fmt.Sprintf("input source %s changed, reconcile: %s - %s", src.GetName(), item.GetName(), item.GetNamespace())) + + requests = append(requests, + reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: item.GetName(), + Namespace: item.GetNamespace(), + }, + }, + ) + } + + return requests +} + func (r *IronicNeutronAgentReconciler) getTransportURL( ctx context.Context, h *helper.Helper, diff --git a/go.mod b/go.mod index 49b03f9e..4cd920c4 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/openshift/api v3.9.0+incompatible github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20250513115636-b549982a5d8f github.com/openstack-k8s-operators/ironic-operator/api v0.0.0-00010101000000-000000000000 - github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20250521085253-60910fbce943 + github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20250604143452-2260c431b9f1 github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20250508141203-be026d3164f7 github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20250508141203-be026d3164f7 github.com/openstack-k8s-operators/mariadb-operator/api v0.6.1-0.20250521084122-c6dc1ca7ed7c @@ -54,8 +54,8 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20250423055245-3cb2ae8df6f0 // indirect - github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20250423055245-3cb2ae8df6f0 // indirect + github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20250508141203-be026d3164f7 // indirect + github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20250508141203-be026d3164f7 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.19.0 // indirect github.com/prometheus/client_model v0.6.0 // indirect diff --git a/go.sum b/go.sum index 11783f1f..bfa07991 100644 --- a/go.sum +++ b/go.sum @@ -80,14 +80,14 @@ github.com/openshift/api v0.0.0-20240830023148-b7d0481c9094 h1:J1wuGhVxpsHykZBa6 github.com/openshift/api v0.0.0-20240830023148-b7d0481c9094/go.mod h1:CxgbWAlvu2iQB0UmKTtRu1YfepRg1/vJ64n2DlIEVz4= github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20250513115636-b549982a5d8f h1:b3WGatQCIBoNj8RvbVGNITL9RuQLwkXzXAgt7s/D5zc= github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20250513115636-b549982a5d8f/go.mod h1:47iJk3vedZWnBkZyNyYij4ma2HjG4l2VCqKz3f+XDkQ= -github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20250521085253-60910fbce943 h1:FbalBf7TrezrxTQYHBGCGqx9ZZl6rLKuCap30sp4LYg= -github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20250521085253-60910fbce943/go.mod h1:zaXbveCF80JXvRUuoXAXFXFMCbsiERvtyVoE0lpfTJ0= +github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20250604143452-2260c431b9f1 h1:YQuJwgoQ9mEyzNq9/SgS3wPCtLG0wMQWH/caWAMZeSc= +github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20250604143452-2260c431b9f1/go.mod h1:dgYQJbbVaRuP98yZZB3K1rNpqnF54I1HM1ZTaOzPKBY= github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20250508141203-be026d3164f7 h1:c3h1q3fDoit3NmvNL89xUL9A12bJivaTF+IOPEOAwLc= github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20250508141203-be026d3164f7/go.mod h1:UwHXRIrMSPJD3lFqrA4oKmRXVLFQCRkLAj9x6KLEHiQ= -github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20250423055245-3cb2ae8df6f0 h1:FAHrScvlj6w17wvcDhJ0ZnmraMrrOX1CxzvqZK595hA= -github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20250423055245-3cb2ae8df6f0/go.mod h1:fesgTbs2j30Fhw2hebXkPgbeAIqG0Yk2oaeOklAInZg= -github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20250423055245-3cb2ae8df6f0 h1:JejCQvZ28JmG87iGpy0tk8v4WJzZ07PIIAxXRpiMR9E= -github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20250423055245-3cb2ae8df6f0/go.mod h1:5+v92XC/PRATIiBrhNLEpJ+T4R9JpxBCgRP6QvbfwgE= +github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20250508141203-be026d3164f7 h1:IybBq3PrxwdvzAF19TjdMCqbEVkX2p3gIkme/Fju6do= +github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20250508141203-be026d3164f7/go.mod h1:fesgTbs2j30Fhw2hebXkPgbeAIqG0Yk2oaeOklAInZg= +github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20250508141203-be026d3164f7 h1:N7HNoUrjqrWZWWcQdtaZubrQ1pFeWai1Cbls0RoCjK8= +github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20250508141203-be026d3164f7/go.mod h1:5+v92XC/PRATIiBrhNLEpJ+T4R9JpxBCgRP6QvbfwgE= github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20250508141203-be026d3164f7 h1:SiJ+T5OK5y4dkPFvHZZeLpSHjDFULaaGKZ2dFdE8uRg= github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20250508141203-be026d3164f7/go.mod h1:oKvVb28i6wwBR5uQO2B2KMzZnCFTPCUCj31c5Zvz2lo= github.com/openstack-k8s-operators/mariadb-operator/api v0.6.1-0.20250521084122-c6dc1ca7ed7c h1:8IysMuJ8t37aBts1N04i1mkWOJNIfzBZHkkJjNkZy5g= diff --git a/tests/functional/base_test.go b/tests/functional/base_test.go index 89323a9e..a8a6949e 100644 --- a/tests/functional/base_test.go +++ b/tests/functional/base_test.go @@ -80,6 +80,7 @@ type IronicNames struct { InspectorConfigSecretName types.NamespacedName INAName types.NamespacedName INATransportURLName types.NamespacedName + INAConfigSecretName types.NamespacedName KeystoneServiceName types.NamespacedName InternalCertSecretName types.NamespacedName PublicCertSecretName types.NamespacedName @@ -233,6 +234,10 @@ func GetIronicNames( Namespace: ironicNeutronAgent.Namespace, Name: ironicNeutronAgent.Name + "-transport", }, + INAConfigSecretName: types.NamespacedName{ + Namespace: ironicNeutronAgent.Namespace, + Name: "ironic-ironic-neutron-agent-config-data", + }, InternalCertSecretName: types.NamespacedName{ Namespace: ironicName.Namespace, Name: "internal-tls-certs", diff --git a/tests/functional/ironic_controller_test.go b/tests/functional/ironic_controller_test.go index d7fed5d1..b0fdd45b 100644 --- a/tests/functional/ironic_controller_test.go +++ b/tests/functional/ironic_controller_test.go @@ -686,6 +686,109 @@ var _ = Describe("Ironic controller", func() { }) + Context("Ironic is fully deployed", func() { + keystoneAPIName := types.NamespacedName{} + BeforeEach(func() { + DeferCleanup( + k8sClient.Delete, + ctx, + CreateIronicSecret(ironicNames.Namespace, SecretName), + ) + DeferCleanup( + k8sClient.Delete, + ctx, + CreateMessageBusSecret(ironicNames.Namespace, MessageBusSecretName), + ) + + apiMariaDBAccount, apiMariaDBSecret := mariadb.CreateMariaDBAccountAndSecret(ironicNames.IronicDatabaseAccount, mariadbv1.MariaDBAccountSpec{}) + DeferCleanup(k8sClient.Delete, ctx, apiMariaDBAccount) + DeferCleanup(k8sClient.Delete, ctx, apiMariaDBSecret) + DeferCleanup( + mariadb.DeleteDBService, + mariadb.CreateDBService( + ironicNames.Namespace, + "openstack", + corev1.ServiceSpec{ + Ports: []corev1.ServicePort{{Port: 3306}}, + }, + ), + ) + keystoneAPIName = keystone.CreateKeystoneAPI(ironicNames.Namespace) + DeferCleanup( + keystone.DeleteKeystoneAPI, + keystoneAPIName) + DeferCleanup( + th.DeleteInstance, + CreateIronic(ironicNames.IronicName, GetDefaultIronicSpec()), + ) + mariadb.GetMariaDBDatabase(ironicNames.IronicDatabaseName) + mariadb.SimulateMariaDBAccountCompleted(ironicNames.IronicDatabaseAccount) + mariadb.SimulateMariaDBDatabaseCompleted(ironicNames.IronicDatabaseName) + th.SimulateJobSuccess(ironicNames.IronicDBSyncJobName) + + keystone.SimulateKeystoneServiceReady(ironicNames.IronicName) + keystone.SimulateKeystoneEndpointReady(ironicNames.IronicName) + + mariadb.GetMariaDBDatabase(ironicNames.InspectorDatabaseName) + mariadb.SimulateMariaDBAccountCompleted(ironicNames.InspectorDatabaseAccount) + mariadb.SimulateMariaDBDatabaseCompleted(ironicNames.InspectorDatabaseName) + th.SimulateJobSuccess(ironicNames.InspectorDBSyncJobName) + + nestedINATransportURLName := ironicNames.INATransportURLName + nestedINATransportURLName.Name = ironicNames.IronicName.Name + "-" + nestedINATransportURLName.Name + infra.GetTransportURL(nestedINATransportURLName) + infra.SimulateTransportURLReady(nestedINATransportURLName) + + th.SimulateDeploymentReplicaReady(ironicNames.IronicName) + th.SimulateStatefulSetReplicaReady(ironicNames.ConductorName) + th.SimulateStatefulSetReplicaReady(ironicNames.InspectorName) + keystone.SimulateKeystoneServiceReady(ironicNames.InspectorName) + keystone.SimulateKeystoneEndpointReady(ironicNames.InspectorName) + th.SimulateDeploymentReplicaReady(ironicNames.INAName) + + th.ExpectCondition( + ironicNames.IronicName, + ConditionGetterFunc(IronicConditionGetter), + condition.ReadyCondition, + corev1.ConditionTrue, + ) + }) + + It("updates the KeystoneAuthURL if keystone internal endpoint changes", func() { + newInternalEndpoint := "https://keystone-internal" + + keystone.UpdateKeystoneAPIEndpoint(keystoneAPIName, "internal", newInternalEndpoint) + logger.Info("Reconfigured") + + Eventually(func(g Gomega) { + confSecret := th.GetSecret(ironicNames.IronicConfigSecretName) + g.Expect(confSecret).ShouldNot(BeNil()) + + conf := confSecret.Data["ironic.conf"] + g.Expect(string(conf)).Should( + ContainSubstring("auth_url=%s", newInternalEndpoint)) + }, timeout, interval).Should(Succeed()) + + Eventually(func(g Gomega) { + confSecret := th.GetSecret(ironicNames.InspectorConfigSecretName) + g.Expect(confSecret).ShouldNot(BeNil()) + + conf := confSecret.Data["01-inspector.conf"] + g.Expect(string(conf)).Should( + ContainSubstring("auth_url=%s", newInternalEndpoint)) + }, timeout, interval).Should(Succeed()) + + Eventually(func(g Gomega) { + confSecret := th.GetSecret(ironicNames.INAConfigSecretName) + g.Expect(confSecret).ShouldNot(BeNil()) + + conf := confSecret.Data["01-ironic_neutron_agent.conf"] + g.Expect(string(conf)).Should( + ContainSubstring("auth_url=%s", newInternalEndpoint)) + }, timeout, interval).Should(Succeed()) + }) + }) + When("Ironic is created with topologyref", func() { var topologyRef, topologyRefAlt *topologyv1.TopoRef BeforeEach(func() {