diff --git a/api/v1beta1/ovndbcluster_types.go b/api/v1beta1/ovndbcluster_types.go index 2f5fa313..833e598c 100644 --- a/api/v1beta1/ovndbcluster_types.go +++ b/api/v1beta1/ovndbcluster_types.go @@ -31,6 +31,10 @@ const ( // SBDBType - Southbound database type SBDBType = "SB" + // TODO: retrieve it from environment + // DNS Suffix is a hardcoded value on how DNSCore domain is configured + DNSSuffix = "cluster.local" + // Container image fall-back defaults // OvnNBContainerImage is the fall-back container image for OVNDBCluster NB diff --git a/controllers/ovndbcluster_controller.go b/controllers/ovndbcluster_controller.go index 917114ed..1202f045 100644 --- a/controllers/ovndbcluster_controller.go +++ b/controllers/ovndbcluster_controller.go @@ -23,9 +23,7 @@ import ( "time" "github.com/go-logr/logr" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "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/client" @@ -51,6 +49,7 @@ import ( corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" k8s_errors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // OVNDBClusterReconciler reconciles a OVNDBCluster object @@ -471,8 +470,8 @@ func (r *OVNDBClusterReconciler) reconcileNormal(ctx context.Context, instance * // Filter out headless services if svc.Spec.ClusterIP != "None" { - internalDbAddress = append(internalDbAddress, fmt.Sprintf("tcp:%s.%s.svc.cluster.local:%d", svc.Name, svc.Namespace, svcPort)) - raftAddress = append(raftAddress, fmt.Sprintf("tcp:%s.%s.svc.cluster.local:%d", svc.Name, svc.Namespace, svc.Spec.Ports[1].Port)) + internalDbAddress = append(internalDbAddress, fmt.Sprintf("tcp:%s.%s.svc.%s:%d", svc.Name, svc.Namespace, v1beta1.DNSSuffix, svcPort)) + raftAddress = append(raftAddress, fmt.Sprintf("tcp:%s.%s.svc.%s:%d", svc.Name, svc.Namespace, v1beta1.DNSSuffix, svc.Spec.Ports[1].Port)) } } @@ -485,6 +484,41 @@ func (r *OVNDBClusterReconciler) reconcileNormal(ctx context.Context, instance * return ctrl.Result{}, nil } +func getPodIPv4InNetwork(ovnPod corev1.Pod, namespace string, networkAttachment string) (string, error) { + netStat, err := nad.GetNetworkStatusFromAnnotation(ovnPod.Annotations) + if err != nil { + err = fmt.Errorf("Error while getting the Network Status for pod %s: %v", ovnPod.Name, err) + return "", err + } + for _, v := range netStat { + if v.Name == namespace + "/" + networkAttachment { + for i := 0; i < len(v.IPs); i++ { + if !strings.Contains(v.IPs[i], ":") { + return v.IPs[i], nil + } + } + } + } + // If this is reached it means that no IP was found, construct error and return + err = fmt.Errorf("Error while getting IPv4 address from pod %s in network %s, IP is empty", ovnPod.Name, networkAttachment) + return "", err +} + +func deleteDNSData(ctx context.Context, helper *helper.Helper, dnsName string, namespace string) error { + // Delete DNS records for deleted services/pods + dnsData := &infranetworkv1.DNSData{ + ObjectMeta: metav1.ObjectMeta{ + Name: dnsName, + Namespace: namespace, + }, + } + err := helper.GetClient().Delete(ctx, dnsData) + if err != nil && !k8s_errors.IsNotFound(err) { + return fmt.Errorf("Error while cleaning up DNS record %s: %w", dnsName, err) + } + return nil +} + func (r *OVNDBClusterReconciler) reconcileServices( ctx context.Context, instance *ovnv1.OVNDBCluster, @@ -558,9 +592,10 @@ func (r *OVNDBClusterReconciler) reconcileServices( ) if err == nil && len(svcList.Items) > int(*(instance.Spec.Replicas)) { for i := len(svcList.Items) - 1; i >= int(*(instance.Spec.Replicas)); i-- { + fullServiceName := fmt.Sprintf("%s-%d", serviceName, i) svcLabels := map[string]string{ common.AppSelector: serviceName, - "statefulset.kubernetes.io/pod-name": serviceName + fmt.Sprintf("-%d", i), + "statefulset.kubernetes.io/pod-name": fullServiceName, } err = service.DeleteServicesWithLabel( ctx, @@ -569,47 +604,31 @@ func (r *OVNDBClusterReconciler) reconcileServices( svcLabels, ) if err != nil { + err = fmt.Errorf("Error while deleting service with name %s: %w", serviceName, err) return ctrl.Result{}, err } // Delete DNS records for deleted services/pods - dnsData := &infranetworkv1.DNSData{} - dnsName := fmt.Sprintf("dns-%s-%d", serviceName, i) - dnsNamespace := helper.GetBeforeObject().GetNamespace() - err = helper.GetClient().Get(ctx, types.NamespacedName{Name: dnsName, Namespace: dnsNamespace}, dnsData) - if err != nil && !k8s_errors.IsNotFound(err) { - Log.Info(fmt.Sprintf("Error while getting DNS object: %v", err)) - } - err := helper.GetClient().Delete(ctx, dnsData) + namespace := helper.GetBeforeObject().GetNamespace() + err = deleteDNSData(ctx, helper, fullServiceName, namespace) if err != nil { - Log.Info(fmt.Sprintf("Error while cleaning up the DNS record %s: %v", dnsName, err)) + return ctrl.Result{}, err } - } } podList, err = ovndbcluster.OVNDBPods(ctx, instance, helper, serviceLabels) if err != nil { + err = fmt.Errorf("Error while retrieving pods on %s cluster: %w", instance.Spec.DBType, err) return ctrl.Result{}, err } - // Assuming that the replicas is greater than 0 AND networkAttachment != "" - // dbAddress will contain ovsdbserver-(nb|sb).openstack.svc - // if replicas == 0 or networkAttachment == "" dbAddress will be empty. - // Meaning that Status.DBAddress is not ready. - dbAddress := "" - // DNSData is needed to provide DNS outside the cluster (edpm nodes) which needs to have - // spec.NetworkAttachment != "" + var svc *corev1.Service + + // When the cluster is attached to an external network, create DNS record for every + // cluster member so it can be resolved from outside cluster (edpm nodes) if instance.Spec.NetworkAttachment != "" { - for i, ovnPod := range podList.Items { - if i >= int(*(instance.Spec.Replicas)) { - break - } - var dnsName string - var dnsIP string - var hostnames []string - dnsName = "dns-" + ovnPod.Name - // Get Hostname - svc, err := service.GetServiceWithName( + for _, ovnPod := range podList.Items[:*(instance.Spec.Replicas)] { + svc, err = service.GetServiceWithName( ctx, helper, ovnPod.Name, @@ -618,75 +637,31 @@ func (r *OVNDBClusterReconciler) reconcileServices( if err != nil { return ctrl.Result{}, err } - hostname := svc.ObjectMeta.Annotations[infranetworkv1.AnnotationHostnameKey] - hostnames = append(hostnames, hostname) - // Get IP - net_stat, err := nad.GetNetworkStatusFromAnnotation(ovnPod.Annotations) + // Currently only IPv4 is supported + dnsIP, err := getPodIPv4InNetwork(ovnPod, instance.Namespace, instance.Spec.NetworkAttachment) if err != nil { - Log.Info(fmt.Sprintf("Error while getting the NAD for pod %s: %v", ovnPod.Name, err)) return ctrl.Result{}, err } - for _, v := range net_stat { - if v.Interface == instance.Spec.NetworkAttachment { - // Return only IPv4 at the moment - if !strings.Contains(v.IPs[0], ":") { - dnsIP = v.IPs[0] - break - } - } - } - - if len(dnsIP) == 0 { - Log.Info(fmt.Sprintf("Error while getting pod %s %s IP, IP is empty", ovnPod.Name, instance.Spec.NetworkAttachment)) - // TODO: uncommenting this 'continue' will break some tests. - // working on fixing it - //continue - } - // Create DNSRecord - var DNSHosts []infranetworkv1.DNSHost - // ovsdbserver-(nb|sb)-n entry - DNSHost := infranetworkv1.DNSHost{} - DNSHost.IP = dnsIP - DNSHost.Hostnames = hostnames - // ovsdbserver-(sb|nb) entry - headlessDnsHostname := ovndbcluster.ServiceNameSB + "." + instance.Namespace + ".svc" - if instance.Spec.DBType == v1beta1.NBDBType { - headlessDnsHostname = ovndbcluster.ServiceNameNB + "." + instance.Namespace + ".svc" - } - dbAddress = fmt.Sprintf("tcp:%s:%d", headlessDnsHostname, svc.Spec.Ports[0].Port) - DNSHostCname := infranetworkv1.DNSHost{} - DNSHostCname.IP = dnsIP - DNSHostCname.Hostnames = append(DNSHostCname.Hostnames, headlessDnsHostname) - DNSHosts = append(DNSHosts, DNSHost) - DNSHosts = append(DNSHosts, DNSHostCname) - - // Create DNSData - DNSData := &infranetworkv1.DNSData{ - ObjectMeta: metav1.ObjectMeta{ - Name: dnsName, - Namespace: ovnPod.Namespace, - }, - } - _, err = controllerutil.CreateOrPatch(ctx, helper.GetClient(), DNSData, func() error { - DNSData.Spec.Hosts = DNSHosts - DNSData.Spec.DNSDataLabelSelectorValue = "dnsdata" - err := controllerutil.SetControllerReference(helper.GetBeforeObject(), DNSData, helper.GetScheme()) - if err != nil { - return err - } - return nil - }) + // Create DNSData CR + err = ovndbcluster.DnsData( + ctx, + helper, + svc, + dnsIP, + instance, + ovnPod, + serviceLabels, + ) if err != nil { - Log.Error(err, "Error Creating DNSData.") return ctrl.Result{}, err } - } - } - instance.Status.DBAddress = dbAddress + // dbAddress will contain ovsdbserver-(nb|sb).openstack.svc or empty + // IPv6 is not handled + instance.Status.DBAddress = ovndbcluster.GetDBAddress(instance, svc) Log.Info("Reconciled OVN DB Cluster Service successfully") return ctrl.Result{}, nil diff --git a/pkg/ovndbcluster/dnsdata.go b/pkg/ovndbcluster/dnsdata.go new file mode 100644 index 00000000..2588370d --- /dev/null +++ b/pkg/ovndbcluster/dnsdata.go @@ -0,0 +1,74 @@ +package ovndbcluster + +import ( + "context" + "fmt" + + infranetworkv1 "github.com/openstack-k8s-operators/infra-operator/apis/network/v1beta1" + "github.com/openstack-k8s-operators/lib-common/modules/common/helper" + "github.com/openstack-k8s-operators/ovn-operator/api/v1beta1" + ovnv1 "github.com/openstack-k8s-operators/ovn-operator/api/v1beta1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +func DnsData( + ctx context.Context, + helper *helper.Helper, + svc *corev1.Service, + ip string, + instance *ovnv1.OVNDBCluster, + ovnPod corev1.Pod, + serviceLabels map[string]string, +) error { + // ovsdbserver-(sb|nb) entry + headlessDnsHostname := ServiceNameSB + "." + instance.Namespace + ".svc" + if instance.Spec.DBType == v1beta1.NBDBType { + headlessDnsHostname = ServiceNameNB + "." + instance.Namespace + ".svc" + } + dnsHostCname := infranetworkv1.DNSHost{ + IP: ip, + Hostnames: []string{ + headlessDnsHostname, + }, + } + + // Create DNSData object + dnsData := &infranetworkv1.DNSData{ + ObjectMeta: metav1.ObjectMeta{ + Name: ovnPod.Name, + Namespace: ovnPod.Namespace, + Labels: serviceLabels, + }, + } + dnsHosts := []infranetworkv1.DNSHost{dnsHostCname} + + _, err := controllerutil.CreateOrPatch(ctx, helper.GetClient(), dnsData, func() error { + dnsData.Spec.Hosts = dnsHosts + // TODO: use value from DNSMasq instance instead of hardcode + dnsData.Spec.DNSDataLabelSelectorValue = "dnsdata" + err := controllerutil.SetControllerReference(helper.GetBeforeObject(), dnsData, helper.GetScheme()) + if err != nil { + return err + } + return nil + }) + if err != nil { + return fmt.Errorf("Error creating DNSData %s: %w", dnsData.Name, err) + } + return nil +} + +func GetDBAddress(instance *ovnv1.OVNDBCluster, svc *corev1.Service) string { + if svc == nil { + return "" + } + headlessDnsHostname := "" + if instance.Spec.DBType == v1beta1.NBDBType { + headlessDnsHostname = ServiceNameNB + "." + instance.Namespace + ".svc" + } else { + headlessDnsHostname = ServiceNameSB + "." + instance.Namespace + ".svc" + } + return fmt.Sprintf("tcp:%s:%d", headlessDnsHostname, svc.Spec.Ports[0].Port) +} diff --git a/tests/functional/base_test.go b/tests/functional/base_test.go index 17f5a7be..78ec48cf 100644 --- a/tests/functional/base_test.go +++ b/tests/functional/base_test.go @@ -119,28 +119,35 @@ func CreateOVNDBClusters(namespace string, nad map[string][]string, replicas int for _, db := range []string{v1beta1.NBDBType, v1beta1.SBDBType} { name := fmt.Sprintf("ovn-%s", uuid.New().String()) spec := GetDefaultOVNDBClusterSpec() - string_nad := "" + stringNad := "" + // OVNDBCluster doesn't allow multiple NADs, hence map len + // must be <= 1 + Expect(len(nad)).Should(BeNumerically("<=", 1)) for k, _ := range nad { if strings.Contains(k, "/") { // k = namespace/nad_name, split[1] will return nad_name (e.g. internalapi) - string_nad += strings.Split(k, "/")[1] + stringNad = strings.Split(k, "/")[1] } } + if len(nad) != 0 { + // nad format needs to be map[string][]string{namespace + "/" + nad_name: ...} or empty + Expect(stringNad).ToNot(Equal("")) + } spec["dbType"] = db - spec["networkAttachment"] = string_nad + spec["networkAttachment"] = stringNad spec["replicas"] = replicas instance := CreateOVNDBCluster(namespace, name, spec) instance_name := types.NamespacedName{Name: instance.GetName(), Namespace: instance.GetNamespace()} - db_name := "nb" + dbName := "nb" if db == v1beta1.SBDBType { - db_name = "sb" + dbName = "sb" } statefulSetName := types.NamespacedName{ Namespace: instance.GetNamespace(), - Name: "ovsdbserver-" + db_name, + Name: "ovsdbserver-" + dbName, } th.SimulateStatefulSetReplicaReadyWithPods( statefulSetName, @@ -151,8 +158,16 @@ func CreateOVNDBClusters(namespace string, nad map[string][]string, replicas int // are set at the end of the reconcileService) Eventually(func(g Gomega) { ovndbcluster := GetOVNDBCluster(instance_name) - g.Expect(ovndbcluster.GetExternalEndpoint()).ToNot(BeEmpty()) - }) + endpoint := "" + // If cluster NAD is empty, ExtenralEndpoint will never be + // set, instead look at internalEndpoint + if len(nad) == 0 { + endpoint, _ = ovndbcluster.GetInternalEndpoint() + } else { + endpoint, _ = ovndbcluster.GetExternalEndpoint() + } + g.Expect(endpoint).ToNot(BeEmpty()) + }).Should(Succeed()) dbs = append(dbs, instance_name) @@ -239,18 +254,6 @@ func OVNControllerConditionGetter(name types.NamespacedName) condition.Condition return instance.Status.Conditions } -func SetExternalEndpoint(name types.NamespacedName, endpoint string) { - if endpoint != "" { - Eventually(func(g Gomega) { - cluster := GetOVNDBCluster(name) - cluster.Status.DBAddress = endpoint - g.Expect(k8sClient.Status().Update(ctx, cluster)).To(Succeed()) - }, timeout, interval).Should(Succeed()) - } else { - ScaleDBCluster(name, 0) - } -} - func SimulateDaemonsetNumberReadyWithPods(name types.NamespacedName, networkIPs map[string][]string) { ds := GetDaemonSet(name) @@ -304,15 +307,36 @@ func GetDNSData(name types.NamespacedName) *infranetworkv1.DNSData { return dns } -func GetDNSDataList(name types.NamespacedName) *infranetworkv1.DNSDataList { +func GetDNSDataList(name types.NamespacedName, labelSelector string) *infranetworkv1.DNSDataList { dnsList := &infranetworkv1.DNSDataList{} + dnsListOpts := client.ListOptions{ + Namespace: name.Namespace, + } + client.MatchingLabels{ + "service": labelSelector, + }.ApplyToList(&dnsListOpts) Eventually(func(g Gomega) { - g.Expect(k8sClient.List(ctx, dnsList, client.InNamespace(name.Namespace))).Should(Succeed()) + g.Expect(k8sClient.List(ctx, dnsList, &dnsListOpts)).Should(Succeed()) }).Should(Succeed()) return dnsList } +func GetDNSDataHostnameIP(dnsDataName string, namespace string, dnsHostname string) string { + dnsEntry := GetDNSData(types.NamespacedName{Name: dnsDataName, Namespace: namespace}) + // DNSData contains Hosts (an array), this array will contain + // ovsdbserver-sb-0.ns.svc and ovsdbserver-sb.ns.svc, need to get + // in which index is located the one which the flavor is currently testing (DNS Cname or DNS) + for _, host := range dnsEntry.Spec.Hosts { + for i, hostname := range host.Hostnames { + if hostname == dnsHostname { + return dnsEntry.Spec.Hosts[i].IP + } + } + } + return "" +} + func GetPod(name types.NamespacedName) *corev1.Pod { pod := &corev1.Pod{} Eventually(func(g Gomega) { diff --git a/tests/functional/ovncontroller_controller_test.go b/tests/functional/ovncontroller_controller_test.go index 0c1e6222..ad7e6662 100644 --- a/tests/functional/ovncontroller_controller_test.go +++ b/tests/functional/ovncontroller_controller_test.go @@ -222,7 +222,6 @@ var _ = Describe("OVNController controller", func() { ovndbcluster.Spec.NetworkAttachment = "" g.Expect(k8sClient.Update(ctx, ovndbcluster)).Should(Succeed()) }, timeout, interval).Should(Succeed()) - SetExternalEndpoint(dbs[1], "") th.AssertConfigMapDoesNotExist(configCM) }) }) @@ -438,8 +437,6 @@ var _ = Describe("OVNController controller", func() { map[string][]string{namespace + "/internalapi": {"10.0.0.1"}}, ) ExpectedExternalSBEndpoint := "tcp:ovsdbserver-sb." + namespace + ".svc:6642" - externalSBEndpoint := "10.0.0.254" - SetExternalEndpoint(dbs[1], externalSBEndpoint) Eventually(func() corev1.ConfigMap { return *th.GetConfigMap(externalCM) @@ -474,8 +471,6 @@ var _ = Describe("OVNController controller", func() { daemonSetName, map[string][]string{namespace + "/internalapi": {"10.0.0.1"}}, ) - externalSBEndpoint := "10.0.0.254" - SetExternalEndpoint(dbs[1], externalSBEndpoint) Eventually(func() corev1.ConfigMap { return *th.GetConfigMap(externalCM) @@ -500,52 +495,18 @@ var _ = Describe("OVNController controller", func() { daemonSetName, map[string][]string{namespace + "/internalapi": {"10.0.0.1"}}, ) - externalSBEndpoint := "10.0.0.254" - SetExternalEndpoint(dbs[1], externalSBEndpoint) Eventually(func() corev1.ConfigMap { return *th.GetConfigMap(externalCM) }, timeout, interval).ShouldNot(BeNil()) - SetExternalEndpoint(dbs[1], "") - th.AssertConfigMapDoesNotExist(externalCM) - }) - - It("should update the external ConfigMap once SB DBCluster is updated", func() { - - externalCM := types.NamespacedName{ - Namespace: OVNControllerName.Namespace, - Name: fmt.Sprintf("%s-%s", OVNControllerName.Name, "config"), - } - - daemonSetName := types.NamespacedName{ - Namespace: namespace, - Name: "ovn-controller", - } - SimulateDaemonsetNumberReadyWithPods( - daemonSetName, - map[string][]string{namespace + "/internalapi": {"10.0.0.1"}}, - ) - externalSBEndpoint := "10.0.0.254" - ExpectedExternalSBEndpoint := "tcp:ovsdbserver-sb." + namespace + ".svc:6642" - SetExternalEndpoint(dbs[1], externalSBEndpoint) - - Eventually(func() corev1.ConfigMap { - return *th.GetConfigMap(externalCM) - }, timeout, interval).ShouldNot(BeNil()) - - Eventually(func(g Gomega) { - g.Expect(th.GetConfigMap(externalCM).Data["ovsdb-config"]).Should( - ContainSubstring("ovn-remote: %s", ExpectedExternalSBEndpoint)) - }, timeout, interval).Should(Succeed()) - - newExternalSBEndpoint := "10.0.0.250" - SetExternalEndpoint(dbs[1], newExternalSBEndpoint) - + // Detach SBCluster from NAD Eventually(func(g Gomega) { - g.Expect(th.GetConfigMap(externalCM).Data["ovsdb-config"]).Should( - ContainSubstring("ovn-remote: %s", ExpectedExternalSBEndpoint)) + ovndbcluster := GetOVNDBCluster(dbs[1]) + ovndbcluster.Spec.NetworkAttachment = "" + g.Expect(k8sClient.Update(ctx, ovndbcluster)).Should(Succeed()) }, timeout, interval).Should(Succeed()) + th.AssertConfigMapDoesNotExist(externalCM) }) }) When("OVNController is created with missing networkAttachment", func() { diff --git a/tests/functional/ovndbcluster_controller_test.go b/tests/functional/ovndbcluster_controller_test.go index f9510d06..1c481388 100644 --- a/tests/functional/ovndbcluster_controller_test.go +++ b/tests/functional/ovndbcluster_controller_test.go @@ -59,7 +59,7 @@ var _ = Describe("OVNDBCluster controller", func() { } Eventually(func(g Gomega) int { - listDns := GetDNSDataList(statefulSetName) + listDns := GetDNSDataList(statefulSetName, "ovsdbserver-"+DnsName) return len(listDns.Items) }).Should(BeNumerically("==", 3)) @@ -73,7 +73,7 @@ var _ = Describe("OVNDBCluster controller", func() { // Check if dnsdata CR is down to 1 Eventually(func() int { targetedDnsOcurrences := 0 - listDns := GetDNSDataList(statefulSetName) + listDns := GetDNSDataList(statefulSetName, "ovsdbserver-"+DnsName) // This calls CreateOVNDBClusters at the BeforeEach, which will // create NB and SB cluster, both belonging at the same namespace // GetDNSDataList will return ALL occurrences of DNSData (will include @@ -91,57 +91,58 @@ var _ = Describe("OVNDBCluster controller", func() { Entry("DNS entry NB", "sb"), Entry("DNS entry SB", "nb"), ) - It("Should update DNSData IP if pod IP changes", func() { - var clusterName types.NamespacedName - var OVNSBDBClusterName types.NamespacedName - var cluster *ovnv1.OVNDBCluster - var dbs []types.NamespacedName - _ = th.CreateNetworkAttachmentDefinition(types.NamespacedName{Namespace: namespace, Name: "internalapi"}) - dbs = CreateOVNDBClusters(namespace, map[string][]string{namespace + "/internalapi": {"10.0.0.1"}}, 3) - OVNSBDBClusterName = types.NamespacedName{Name: dbs[1].Name, Namespace: dbs[1].Namespace} - cluster = GetOVNDBCluster(OVNSBDBClusterName) - clusterName = OVNSBDBClusterName - // DnsEntry will have two entries, the ovsdbserver-sb.namespace.svc - // and the ovsdbserver-sb-0.namespace.svc, both will have same IP - // for this test ovsdbserver-sb-0 will be used. - Eventually(func(g Gomega) { - dnsEntry := GetDNSData(types.NamespacedName{Name: "dns-ovsdbserver-sb-0", Namespace: cluster.Namespace}) - g.Expect(dnsEntry).To(Equal("10.0.0.1")) - }) - - // Modify pod IP section - pod := GetPod(types.NamespacedName{Name: "ovsdbserver-sb-0", Namespace: cluster.Namespace}) - // Create new pod NAD and add it to POD - var netStatus []networkv1.NetworkStatus - var IPString []string - IPString = append(IPString, "10.0.0.10") - netStatus = append( - netStatus, - networkv1.NetworkStatus{ - Name: cluster.Namespace + "/internalip", - IPs: IPString, - }, - ) - netStatusAnnotation, err := json.Marshal(netStatus) - Expect(err).NotTo(HaveOccurred()) - pod.Annotations[networkv1.NetworkStatusAnnot] = string(netStatusAnnotation) - UpdatePod(pod) - // End modify pod IP section - - // Call reconcile loop - Eventually(func(g Gomega) { - c := GetOVNDBCluster(clusterName) - // Change something just to call reconcile loop - *&c.Spec.ElectionTimer = 11000 - g.Expect(k8sClient.Update(ctx, c)).Should(Succeed()) - }).Should(Succeed()) - - Eventually(func(g Gomega) { - dnsEntry := GetDNSData(types.NamespacedName{Name: "dns-ovsdbserver-sb-0", Namespace: cluster.Namespace}) - g.Expect(dnsEntry).To(Equal("10.0.0.10")) - }) - - }) + DescribeTable("Should update DNSData IP if pod IP changes", + func(DNSEntryName string) { + var clusterName types.NamespacedName + var OVNSBDBClusterName types.NamespacedName + var cluster *ovnv1.OVNDBCluster + var dbs []types.NamespacedName + + _ = th.CreateNetworkAttachmentDefinition(types.NamespacedName{Namespace: namespace, Name: "internalapi"}) + dbs = CreateOVNDBClusters(namespace, map[string][]string{namespace + "/internalapi": {"10.0.0.1"}}, 3) + OVNSBDBClusterName = types.NamespacedName{Name: dbs[1].Name, Namespace: dbs[1].Namespace} + cluster = GetOVNDBCluster(OVNSBDBClusterName) + clusterName = OVNSBDBClusterName + fullDNSEntryName := DNSEntryName + "." + cluster.Namespace + ".svc" + + // Check that DNSData info has been created with correct IP (10.0.0.1) + Eventually(func(g Gomega) { + ip := GetDNSDataHostnameIP("ovsdbserver-sb-0", cluster.Namespace, fullDNSEntryName) + g.Expect(ip).Should(Equal("10.0.0.1")) + }).Should(Succeed()) + + // Modify pod IP section + pod := GetPod(types.NamespacedName{Name: "ovsdbserver-sb-0", Namespace: cluster.Namespace}) + // Create new pod NAD and add it to POD + netStatus := []networkv1.NetworkStatus{ + networkv1.NetworkStatus{ + Name: cluster.Namespace + "/internalapi", + IPs: []string{"10.0.0.10"}, + }, + } + netStatusAnnotation, err := json.Marshal(netStatus) + Expect(err).NotTo(HaveOccurred()) + pod.Annotations[networkv1.NetworkStatusAnnot] = string(netStatusAnnotation) + UpdatePod(pod) + // End modify pod IP section + + // Call reconcile loop + Eventually(func(g Gomega) { + c := GetOVNDBCluster(clusterName) + // Change something just to call reconcile loop + *&c.Spec.ElectionTimer += 1000 + g.Expect(k8sClient.Update(ctx, c)).Should(Succeed()) + }).Should(Succeed()) + + // Check that DNSData info has been modified with correct IP (10.0.0.10) + Eventually(func(g Gomega) { + ip := GetDNSDataHostnameIP("ovsdbserver-sb-0", cluster.Namespace, fullDNSEntryName) + g.Expect(ip).Should(Equal("10.0.0.10")) + }).Should(Succeed()) + + }, + Entry("DNS CName entry", "ovsdbserver-sb"), + ) }) When("A OVNDBCluster instance is created", func() {