diff --git a/controllers/dataplane/openstackdataplanedeployment_controller.go b/controllers/dataplane/openstackdataplanedeployment_controller.go index c689a34a4..e911dddff 100644 --- a/controllers/dataplane/openstackdataplanedeployment_controller.go +++ b/controllers/dataplane/openstackdataplanedeployment_controller.go @@ -331,11 +331,7 @@ func (r *OpenStackDataPlaneDeploymentReconciler) Reconcile(ctx context.Context, // deploy those services for each OpenStackDataPlaneNodeSet. Otherwise, // deploy with the OpenStackDataPlaneNodeSet's Services. var deployResult *ctrl.Result - if len(instance.Spec.ServicesOverride) != 0 { - deployResult, err = deployer.Deploy(nodesetServiceMap[deployment.AllNodeSets]) - } else { - deployResult, err = deployer.Deploy(nodesetServiceMap[nodeSet.Name]) - } + deployResult, err = deployer.Deploy(nodesetServiceMap[nodeSet.Name]) nsConditions := instance.Status.NodeSetConditions[nodeSet.Name] nsConditions.Set(nsConditions.Mirror(dataplanev1.NodeSetDeploymentReadyCondition)) diff --git a/pkg/dataplane/const.go b/pkg/dataplane/const.go index 8589ff56d..79f24c64a 100644 --- a/pkg/dataplane/const.go +++ b/pkg/dataplane/const.go @@ -71,8 +71,4 @@ const ( //HostnameLabel label for marking secrets to be watched for changes HostnameLabel = "hostname" - - //AllNodeSets Key used in nodeset service map for all nodesets when - //using serviceOverride. - AllNodeSets = "AllNodeSets" ) diff --git a/pkg/dataplane/service.go b/pkg/dataplane/service.go index e45c9c946..30500901c 100644 --- a/pkg/dataplane/service.go +++ b/pkg/dataplane/service.go @@ -161,22 +161,21 @@ func DedupeServices(ctx context.Context, helper *helper.Helper, var globalServices []string var services []string var err error - if len(serviceOverride) != 0 { - services, globalServices, err = dedupe(ctx, helper, serviceOverride, globalServices) + + for _, nodeset := range nodesets { + var nodeSetServices []string + if len(serviceOverride) != 0 { + nodeSetServices = serviceOverride + } else { + nodeSetServices = nodeset.Spec.Services + } + services, globalServices, err = dedupe(ctx, helper, nodeSetServices, globalServices) if err != nil { return nil, err } - nodeSetServiceMap[AllNodeSets] = services - } else { - for _, nodeset := range nodesets { - services, globalServices, err = dedupe(ctx, helper, nodeset.Spec.Services, globalServices) - if err != nil { - return nil, err - } - nodeSetServiceMap[nodeset.Name] = services - } + nodeSetServiceMap[nodeset.Name] = services } - helper.GetLogger().Info(fmt.Sprintf("Current Global Services %s", globalServices)) + helper.GetLogger().Info(fmt.Sprintf("Current Global Services %v", globalServices)) return nodeSetServiceMap, nil } diff --git a/tests/functional/dataplane/base_test.go b/tests/functional/dataplane/base_test.go index fb6ff1008..a3bfc2de7 100644 --- a/tests/functional/dataplane/base_test.go +++ b/tests/functional/dataplane/base_test.go @@ -259,6 +259,34 @@ func DuplicateServiceDeploymentSpec() map[string]interface{} { } } +// Build OpenStackDataPlnaeDeploymentSpec with global service +func GlobalServiceDeploymentSpec() map[string]interface{} { + return map[string]interface{}{ + "nodeSets": []string{ + "alpha-nodeset", + "beta-nodeset", + }, + "servicesOverride": []string{ + "foo-service", + "global-service", + "foo-update-service", + }, + } +} + +// Build OpenStackDataPlnaeDeploymentSpec with single global service +func SingleGlobalServiceDeploymentSpec() map[string]interface{} { + return map[string]interface{}{ + "nodeSets": []string{ + "alpha-nodeset", + "beta-nodeset", + }, + "servicesOverride": []string{ + "global-service", + }, + } +} + func DefaultNetConfigSpec() map[string]interface{} { return map[string]interface{}{ "networks": []map[string]interface{}{{ diff --git a/tests/functional/dataplane/openstackdataplanedeployment_controller_test.go b/tests/functional/dataplane/openstackdataplanedeployment_controller_test.go index 3a6cee76e..d2938af45 100644 --- a/tests/functional/dataplane/openstackdataplanedeployment_controller_test.go +++ b/tests/functional/dataplane/openstackdataplanedeployment_controller_test.go @@ -1041,6 +1041,434 @@ var _ = Describe("Dataplane Deployment Test", func() { ) }) }) + + When("A dataplaneDeployment is created with serviceoverride containing single global service", func() { + BeforeEach(func() { + CreateSSHSecret(dataplaneSSHSecretName) + DeferCleanup(th.DeleteInstance, th.CreateSecret(neutronOvnMetadataSecretName, map[string][]byte{ + "fake_keys": []byte("blih"), + })) + DeferCleanup(th.DeleteInstance, th.CreateSecret(novaNeutronMetadataSecretName, map[string][]byte{ + "fake_keys": []byte("blih"), + })) + DeferCleanup(th.DeleteInstance, th.CreateSecret(novaCellComputeConfigSecretName, map[string][]byte{ + "fake_keys": []byte("blih"), + })) + DeferCleanup(th.DeleteInstance, th.CreateSecret(novaMigrationSSHKey, map[string][]byte{ + "ssh-privatekey": []byte("fake-ssh-private-key"), + "ssh-publickey": []byte("fake-ssh-public-key"), + })) + DeferCleanup(th.DeleteInstance, th.CreateSecret(ceilometerConfigSecretName, map[string][]byte{ + "fake_keys": []byte("blih"), + })) + + alphaNodeSetName := types.NamespacedName{ + Name: "alpha-nodeset", + Namespace: namespace, + } + betaNodeSetName := types.NamespacedName{ + Name: "beta-nodeset", + Namespace: namespace, + } + + // Three services on both nodesets + CreateDataplaneService(dataplaneServiceName, false) + CreateDataplaneService(dataplaneGlobalServiceName, true) + CreateDataPlaneServiceFromSpec(dataplaneUpdateServiceName, map[string]interface{}{ + "EDPMServiceType": "foo-update-service"}) + + DeferCleanup(th.DeleteService, dataplaneServiceName) + DeferCleanup(th.DeleteService, dataplaneGlobalServiceName) + DeferCleanup(th.DeleteService, dataplaneUpdateServiceName) + + DeferCleanup(th.DeleteInstance, CreateNetConfig(dataplaneNetConfigName, DefaultNetConfigSpec())) + DeferCleanup(th.DeleteInstance, CreateDNSMasq(dnsMasqName, DefaultDNSMasqSpec())) + SimulateDNSMasqComplete(dnsMasqName) + // Create both nodesets + + betaNodeName := fmt.Sprintf("%s-node-1", betaNodeSetName.Name) + betaNodeSetSpec := map[string]interface{}{ + "preProvisioned": false, + "services": []string{ + "foo-service", + }, + "nodeTemplate": map[string]interface{}{ + "ansibleSSHPrivateKeySecret": "dataplane-ansible-ssh-private-key-secret", + "ansible": map[string]interface{}{ + "ansibleUser": "cloud-user", + }, + }, + "nodes": map[string]interface{}{ + betaNodeName: map[string]interface{}{ + "hostname": betaNodeName, + "networks": []map[string]interface{}{{ + "name": "CtlPlane", + "subnetName": "subnet1", + }, + }, + }, + }, + "baremetalSetTemplate": map[string]interface{}{ + "baremetalHosts": map[string]interface{}{ + "ctlPlaneIP": map[string]interface{}{}, + }, + "deploymentSSHSecret": "dataplane-ansible-ssh-private-key-secret", + "ctlplaneInterface": "172.20.12.1", + }, + "tlsEnabled": true, + } + DeferCleanup(th.DeleteInstance, CreateDataplaneNodeSet(alphaNodeSetName, DefaultDataPlaneNodeSetSpec(alphaNodeSetName.Name))) + DeferCleanup(th.DeleteInstance, CreateDataplaneNodeSet(betaNodeSetName, betaNodeSetSpec)) + SimulateIPSetComplete(dataplaneNodeName) + SimulateDNSDataComplete(alphaNodeSetName) + SimulateIPSetComplete(types.NamespacedName{Name: betaNodeName, Namespace: namespace}) + SimulateDNSDataComplete(betaNodeSetName) + + DeferCleanup(th.DeleteInstance, CreateDataplaneDeployment(dataplaneMultiNodesetDeploymentName, SingleGlobalServiceDeploymentSpec())) + }) + + It("Should have Spec fields initialized", func() { + dataplaneDeploymentInstance := GetDataplaneDeployment(dataplaneMultiNodesetDeploymentName) + nodeSetsNames := []string{ + "alpha-nodeset", + "beta-nodeset", + } + + expectedSpec := dataplanev1.OpenStackDataPlaneDeploymentSpec{ + NodeSets: nodeSetsNames, + AnsibleTags: "", + AnsibleLimit: "", + AnsibleSkipTags: "", + BackoffLimit: &DefaultBackoffLimit, + DeploymentRequeueTime: 15, + ServicesOverride: []string{"global-service"}, + } + Expect(dataplaneDeploymentInstance.Spec).Should(Equal(expectedSpec)) + }) + + It("should have conditions set", func() { + alphaNodeSetName := types.NamespacedName{ + Name: "alpha-nodeset", + Namespace: namespace, + } + betaNodeSetName := types.NamespacedName{ + Name: "beta-nodeset", + Namespace: namespace, + } + + baremetalAlpha := baremetalv1.OpenStackBaremetalSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: alphaNodeSetName.Name, + Namespace: alphaNodeSetName.Namespace, + }, + } + + baremetalBeta := baremetalv1.OpenStackBaremetalSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: betaNodeSetName.Name, + Namespace: betaNodeSetName.Namespace, + }, + } + + // Create config map for OVN service + ovnConfigMapName := types.NamespacedName{ + Namespace: namespace, + Name: "ovncontroller-config", + } + mapData := map[string]interface{}{ + "ovsdb-config": "test-ovn-config", + } + th.CreateConfigMap(ovnConfigMapName, mapData) + + nodeSetAlpha := *GetDataplaneNodeSet(alphaNodeSetName) + + // Set baremetal provisioning conditions to True + Eventually(func(g Gomega) { + // OpenStackBaremetalSet has the same name as OpenStackDataPlaneNodeSet + g.Expect(th.K8sClient.Get(th.Ctx, alphaNodeSetName, &baremetalAlpha)).To(Succeed()) + baremetalAlpha.Status.Conditions.MarkTrue( + condition.ReadyCondition, + condition.ReadyMessage) + g.Expect(th.K8sClient.Status().Update(th.Ctx, &baremetalAlpha)).To(Succeed()) + // OpenStackBaremetalSet has the same name as OpenStackDataPlaneNodeSet + g.Expect(th.K8sClient.Get(th.Ctx, betaNodeSetName, &baremetalBeta)).To(Succeed()) + baremetalBeta.Status.Conditions.MarkTrue( + condition.ReadyCondition, + condition.ReadyMessage) + g.Expect(th.K8sClient.Status().Update(th.Ctx, &baremetalBeta)).To(Succeed()) + + }, th.Timeout, th.Interval).Should(Succeed()) + + // Create all services necessary for deployment + for _, serviceName := range []string{"global-service"} { + dataplaneServiceName := types.NamespacedName{ + Name: serviceName, + Namespace: namespace, + } + service := GetService(dataplaneServiceName) + deployment := GetDataplaneDeployment(dataplaneMultiNodesetDeploymentName) + aeeName, _ := dataplaneutil.GetAnsibleExecutionNameAndLabels( + service, deployment.GetName(), nodeSetAlpha.GetName()) + //Retrieve service AnsibleEE and set JobStatus to Successful + Eventually(func(g Gomega) { + // Make an AnsibleEE name for each service + ansibleeeName := types.NamespacedName{ + Name: aeeName, + Namespace: dataplaneMultiNodesetDeploymentName.Namespace, + } + ansibleEE := GetAnsibleee(ansibleeeName) + ansibleEE.Status.JobStatus = ansibleeev1.JobStatusSucceeded + g.Expect(th.K8sClient.Status().Update(th.Ctx, ansibleEE)).To(Succeed()) + }, th.Timeout, th.Interval).Should(Succeed()) + } + th.ExpectCondition( + betaNodeSetName, + ConditionGetterFunc(DataplaneConditionGetter), + condition.ReadyCondition, + corev1.ConditionTrue, + ) + th.ExpectCondition( + alphaNodeSetName, + ConditionGetterFunc(DataplaneConditionGetter), + condition.ReadyCondition, + corev1.ConditionTrue, + ) + th.ExpectCondition( + dataplaneMultiNodesetDeploymentName, + ConditionGetterFunc(DataplaneDeploymentConditionGetter), + condition.ReadyCondition, + corev1.ConditionTrue, + ) + th.ExpectCondition( + dataplaneMultiNodesetDeploymentName, + ConditionGetterFunc(DataplaneDeploymentConditionGetter), + condition.InputReadyCondition, + corev1.ConditionTrue, + ) + }) + }) + + When("A dataplaneDeployment is created with serviceoverride containing global service", func() { + BeforeEach(func() { + CreateSSHSecret(dataplaneSSHSecretName) + DeferCleanup(th.DeleteInstance, th.CreateSecret(neutronOvnMetadataSecretName, map[string][]byte{ + "fake_keys": []byte("blih"), + })) + DeferCleanup(th.DeleteInstance, th.CreateSecret(novaNeutronMetadataSecretName, map[string][]byte{ + "fake_keys": []byte("blih"), + })) + DeferCleanup(th.DeleteInstance, th.CreateSecret(novaCellComputeConfigSecretName, map[string][]byte{ + "fake_keys": []byte("blih"), + })) + DeferCleanup(th.DeleteInstance, th.CreateSecret(novaMigrationSSHKey, map[string][]byte{ + "ssh-privatekey": []byte("fake-ssh-private-key"), + "ssh-publickey": []byte("fake-ssh-public-key"), + })) + DeferCleanup(th.DeleteInstance, th.CreateSecret(ceilometerConfigSecretName, map[string][]byte{ + "fake_keys": []byte("blih"), + })) + + alphaNodeSetName := types.NamespacedName{ + Name: "alpha-nodeset", + Namespace: namespace, + } + betaNodeSetName := types.NamespacedName{ + Name: "beta-nodeset", + Namespace: namespace, + } + + // Three services on both nodesets + CreateDataplaneService(dataplaneServiceName, false) + CreateDataplaneService(dataplaneGlobalServiceName, true) + CreateDataPlaneServiceFromSpec(dataplaneUpdateServiceName, map[string]interface{}{ + "EDPMServiceType": "foo-update-service"}) + + DeferCleanup(th.DeleteService, dataplaneServiceName) + DeferCleanup(th.DeleteService, dataplaneGlobalServiceName) + DeferCleanup(th.DeleteService, dataplaneUpdateServiceName) + + DeferCleanup(th.DeleteInstance, CreateNetConfig(dataplaneNetConfigName, DefaultNetConfigSpec())) + DeferCleanup(th.DeleteInstance, CreateDNSMasq(dnsMasqName, DefaultDNSMasqSpec())) + SimulateDNSMasqComplete(dnsMasqName) + // Create both nodesets + + betaNodeName := fmt.Sprintf("%s-node-1", betaNodeSetName.Name) + betaNodeSetSpec := map[string]interface{}{ + "preProvisioned": false, + "services": []string{ + "foo-service", + }, + "nodeTemplate": map[string]interface{}{ + "ansibleSSHPrivateKeySecret": "dataplane-ansible-ssh-private-key-secret", + "ansible": map[string]interface{}{ + "ansibleUser": "cloud-user", + }, + }, + "nodes": map[string]interface{}{ + betaNodeName: map[string]interface{}{ + "hostname": betaNodeName, + "networks": []map[string]interface{}{{ + "name": "CtlPlane", + "subnetName": "subnet1", + }, + }, + }, + }, + "baremetalSetTemplate": map[string]interface{}{ + "baremetalHosts": map[string]interface{}{ + "ctlPlaneIP": map[string]interface{}{}, + }, + "deploymentSSHSecret": "dataplane-ansible-ssh-private-key-secret", + "ctlplaneInterface": "172.20.12.1", + }, + "tlsEnabled": true, + } + DeferCleanup(th.DeleteInstance, CreateDataplaneNodeSet(alphaNodeSetName, DefaultDataPlaneNodeSetSpec(alphaNodeSetName.Name))) + DeferCleanup(th.DeleteInstance, CreateDataplaneNodeSet(betaNodeSetName, betaNodeSetSpec)) + SimulateIPSetComplete(dataplaneNodeName) + SimulateDNSDataComplete(alphaNodeSetName) + SimulateIPSetComplete(types.NamespacedName{Name: betaNodeName, Namespace: namespace}) + SimulateDNSDataComplete(betaNodeSetName) + + DeferCleanup(th.DeleteInstance, CreateDataplaneDeployment(dataplaneMultiNodesetDeploymentName, GlobalServiceDeploymentSpec())) + }) + + It("Should have Spec fields initialized", func() { + dataplaneDeploymentInstance := GetDataplaneDeployment(dataplaneMultiNodesetDeploymentName) + nodeSetsNames := []string{ + "alpha-nodeset", + "beta-nodeset", + } + + expectedSpec := dataplanev1.OpenStackDataPlaneDeploymentSpec{ + NodeSets: nodeSetsNames, + AnsibleTags: "", + AnsibleLimit: "", + AnsibleSkipTags: "", + BackoffLimit: &DefaultBackoffLimit, + DeploymentRequeueTime: 15, + ServicesOverride: []string{"foo-service", "global-service", "foo-update-service"}, + } + Expect(dataplaneDeploymentInstance.Spec).Should(Equal(expectedSpec)) + }) + + It("should have conditions set", func() { + alphaNodeSetName := types.NamespacedName{ + Name: "alpha-nodeset", + Namespace: namespace, + } + betaNodeSetName := types.NamespacedName{ + Name: "beta-nodeset", + Namespace: namespace, + } + + baremetalAlpha := baremetalv1.OpenStackBaremetalSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: alphaNodeSetName.Name, + Namespace: alphaNodeSetName.Namespace, + }, + } + + baremetalBeta := baremetalv1.OpenStackBaremetalSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: betaNodeSetName.Name, + Namespace: betaNodeSetName.Namespace, + }, + } + + // Create config map for OVN service + ovnConfigMapName := types.NamespacedName{ + Namespace: namespace, + Name: "ovncontroller-config", + } + mapData := map[string]interface{}{ + "ovsdb-config": "test-ovn-config", + } + th.CreateConfigMap(ovnConfigMapName, mapData) + + nodeSetAlpha := *GetDataplaneNodeSet(alphaNodeSetName) + nodeSetBeta := *GetDataplaneNodeSet(betaNodeSetName) + + // Set baremetal provisioning conditions to True + Eventually(func(g Gomega) { + // OpenStackBaremetalSet has the same name as OpenStackDataPlaneNodeSet + g.Expect(th.K8sClient.Get(th.Ctx, alphaNodeSetName, &baremetalAlpha)).To(Succeed()) + baremetalAlpha.Status.Conditions.MarkTrue( + condition.ReadyCondition, + condition.ReadyMessage) + g.Expect(th.K8sClient.Status().Update(th.Ctx, &baremetalAlpha)).To(Succeed()) + // OpenStackBaremetalSet has the same name as OpenStackDataPlaneNodeSet + g.Expect(th.K8sClient.Get(th.Ctx, betaNodeSetName, &baremetalBeta)).To(Succeed()) + baremetalBeta.Status.Conditions.MarkTrue( + condition.ReadyCondition, + condition.ReadyMessage) + g.Expect(th.K8sClient.Status().Update(th.Ctx, &baremetalBeta)).To(Succeed()) + + }, th.Timeout, th.Interval).Should(Succeed()) + + // Create all services necessary for deployment + for _, serviceName := range []string{"foo-service", "global-service", "foo-update-service"} { + dataplaneServiceName := types.NamespacedName{ + Name: serviceName, + Namespace: namespace, + } + service := GetService(dataplaneServiceName) + deployment := GetDataplaneDeployment(dataplaneMultiNodesetDeploymentName) + aeeName, _ := dataplaneutil.GetAnsibleExecutionNameAndLabels( + service, deployment.GetName(), nodeSetAlpha.GetName()) + //Retrieve service AnsibleEE and set JobStatus to Successful + Eventually(func(g Gomega) { + // Make an AnsibleEE name for each service + ansibleeeName := types.NamespacedName{ + Name: aeeName, + Namespace: dataplaneMultiNodesetDeploymentName.Namespace, + } + ansibleEE := GetAnsibleee(ansibleeeName) + ansibleEE.Status.JobStatus = ansibleeev1.JobStatusSucceeded + g.Expect(th.K8sClient.Status().Update(th.Ctx, ansibleEE)).To(Succeed()) + }, th.Timeout, th.Interval).Should(Succeed()) + } + + servicesExcludingGlobal := []string{"foo-service", "foo-update-service"} + // Create all services necessary for deployment + for _, serviceName := range servicesExcludingGlobal { + dataplaneServiceName := types.NamespacedName{ + Name: serviceName, + Namespace: namespace, + } + service := GetService(dataplaneServiceName) + deployment := GetDataplaneDeployment(dataplaneMultiNodesetDeploymentName) + aeeName, _ := dataplaneutil.GetAnsibleExecutionNameAndLabels( + service, deployment.GetName(), nodeSetBeta.GetName()) + + //Retrieve service AnsibleEE and set JobStatus to Successful + Eventually(func(g Gomega) { + // Make an AnsibleEE name for each service + ansibleeeName := types.NamespacedName{ + Name: aeeName, + Namespace: dataplaneMultiNodesetDeploymentName.Namespace, + } + ansibleEE := GetAnsibleee(ansibleeeName) + ansibleEE.Status.JobStatus = ansibleeev1.JobStatusSucceeded + g.Expect(th.K8sClient.Status().Update(th.Ctx, ansibleEE)).To(Succeed()) + }, th.Timeout, th.Interval).Should(Succeed()) + } + + th.ExpectCondition( + dataplaneMultiNodesetDeploymentName, + ConditionGetterFunc(DataplaneDeploymentConditionGetter), + condition.ReadyCondition, + corev1.ConditionTrue, + ) + th.ExpectCondition( + dataplaneMultiNodesetDeploymentName, + ConditionGetterFunc(DataplaneDeploymentConditionGetter), + condition.InputReadyCondition, + corev1.ConditionTrue, + ) + }) + }) + When("A dataplaneDeployment is created with non-existent service in nodeset", func() { BeforeEach(func() { CreateSSHSecret(dataplaneSSHSecretName)