github.com/interconnectedcloud/qdr-operator@v0.0.0-20210826174505-576d2b33dac7/test/e2e/framework/interconnect.go (about) 1 // Copyright 2019 The Interconnectedcloud Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package framework 16 17 import ( 18 "context" 19 "regexp" 20 "sort" 21 "time" 22 23 "github.com/interconnectedcloud/qdr-operator/pkg/apis/interconnectedcloud/v1alpha1" 24 "github.com/interconnectedcloud/qdr-operator/pkg/utils/selectors" 25 26 corev1 "k8s.io/api/core/v1" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 ) 29 30 const ( 31 timeout time.Duration = 60 * time.Second 32 ) 33 34 // InterconnectCustomizer represents a function that allows for 35 // customizing an Interconnect resource before it is created. 36 type InterconnectCustomizer func(interconnect *v1alpha1.Interconnect) 37 38 // CreateInterconnect creates an interconnect resource 39 func (f *Framework) CreateInterconnect(namespace string, size int32, fn ...InterconnectCustomizer) (*v1alpha1.Interconnect, error) { 40 41 obj := &v1alpha1.Interconnect{ 42 TypeMeta: metav1.TypeMeta{ 43 Kind: "Interconnect", 44 APIVersion: "interconnectedcloud.github.io/v1alpha1", 45 }, 46 ObjectMeta: metav1.ObjectMeta{ 47 Name: f.UniqueName, 48 Namespace: namespace, 49 }, 50 Spec: v1alpha1.InterconnectSpec{ 51 DeploymentPlan: v1alpha1.DeploymentPlanType{ 52 Size: size, 53 Image: TestContext.QdrImage, 54 Role: "interior", 55 Placement: "Any", 56 }, 57 }, 58 } 59 60 // Customize the interconnect resource before creation 61 for _, f := range fn { 62 f(obj) 63 } 64 // create the interconnect resource 65 return f.QdrClient.InterconnectedcloudV1alpha1().Interconnects(f.Namespace).Create(obj) 66 } 67 68 func (f *Framework) DeleteInterconnect(interconnect *v1alpha1.Interconnect) error { 69 return f.QdrClient.InterconnectedcloudV1alpha1().Interconnects(f.Namespace).Delete(interconnect.Name, &metav1.DeleteOptions{}) 70 } 71 72 func (f *Framework) GetInterconnect(name string) (*v1alpha1.Interconnect, error) { 73 return f.QdrClient.InterconnectedcloudV1alpha1().Interconnects(f.Namespace).Get(name, metav1.GetOptions{}) 74 } 75 76 func (f *Framework) UpdateInterconnect(interconnect *v1alpha1.Interconnect) (*v1alpha1.Interconnect, error) { 77 return f.QdrClient.InterconnectedcloudV1alpha1().Interconnects(f.Namespace).Update(interconnect) 78 } 79 80 func (f *Framework) InterconnectHasExpectedSize(interconnect *v1alpha1.Interconnect, expectedSize int) (bool, error) { 81 dep, err := f.GetDeployment(interconnect.Name) 82 if err != nil { 83 return false, err 84 } 85 86 if int(dep.Status.AvailableReplicas) == expectedSize { 87 return true, nil 88 } else { 89 return false, nil 90 } 91 } 92 93 func (f *Framework) InterconnectHasExpectedVersion(interconnect *v1alpha1.Interconnect, expectedVersion string) (bool, error) { 94 pods, err := f.PodsForInterconnect(interconnect) 95 if err != nil { 96 return false, err 97 } 98 99 for _, pod := range pods { 100 version, err := f.VersionForPod(pod) 101 if err != nil { 102 return false, err 103 } 104 105 // Retrieve version from returned string 106 r, _ := regexp.Compile(".*(\\d+\\.\\d+\\.\\d+).*") 107 match := r.FindStringSubmatch(version) 108 extractedVersion := match[1] 109 110 if extractedVersion != expectedVersion { 111 return false, nil 112 } 113 } 114 return true, nil 115 } 116 117 func (f *Framework) PodsForInterconnect(interconnect *v1alpha1.Interconnect) ([]corev1.Pod, error) { 118 selector := selectors.ResourcesByInterconnectName(interconnect.Name) 119 listOps := metav1.ListOptions{LabelSelector: selector.String()} 120 pods, err := f.KubeClient.CoreV1().Pods(interconnect.Namespace).List(listOps) 121 if err != nil { 122 return nil, err 123 } 124 return pods.Items, nil 125 } 126 127 func (f *Framework) VersionForPod(pod corev1.Pod) (string, error) { 128 command := []string{"qdrouterd", "--version"} 129 kubeExec := NewKubectlExecCommand(f, pod.Name, timeout, command...) 130 131 return kubeExec.Exec() 132 } 133 134 func (f *Framework) WaitForNewInterconnectPods(ctx context.Context, initialPodNames []string, interconnect *v1alpha1.Interconnect, retryInterval, timeout time.Duration) error { 135 sort.Strings(initialPodNames) 136 137 err := RetryWithContext(ctx, RetryInterval, func() (bool, error) { 138 // Sorting and comparing current pod names 139 podNames, err := f.GetInterconnectPodNames(interconnect) 140 if err != nil { 141 return true, err 142 } 143 sort.Strings(podNames) 144 145 // Wait till expected number of replicas are available 146 if int32(len(podNames)) != interconnect.Spec.DeploymentPlan.Size { 147 return false, nil 148 } 149 150 // Not same amount of pods available 151 if len(podNames) != len(initialPodNames) { 152 return false, nil 153 } 154 155 // Expect no pod names to match 156 for _, newName := range podNames { 157 for _, oldName := range initialPodNames { 158 if newName == oldName { 159 return false, nil 160 } 161 } 162 } 163 164 // Same amount of pods found and no names matching 165 return true, nil 166 }) 167 168 return err 169 } 170 171 // WaitUntilFullInterconnectWithSize waits until all the pods belonging to 172 // the Interconnect deployment report the expected state and size. 173 // The expected state will differs for interior versus edge roles 174 func (f *Framework) WaitUntilFullInterconnectWithSize(ctx context.Context, interconnect *v1alpha1.Interconnect, expectedSize int) error { 175 return f.WaitUntilFullInterconnectWithVersion(ctx, interconnect, expectedSize, "") 176 } 177 178 // WaitUntilFullInterconnectWithVersion waits until all the pods belonging to 179 // the Interconnect deployment report the expected state and expected version (if one 180 // has been provided). 181 // The expected state will differs for interior versus edge roles 182 func (f *Framework) WaitUntilFullInterconnectWithVersion(ctx context.Context, interconnect *v1alpha1.Interconnect, expectedSize int, expectedVersion string) error { 183 // Wait for the expected size to be reported on the Interconnect deployment 184 err := WaitForDeployment(f.KubeClient, f.Namespace, interconnect.Name, expectedSize, RetryInterval, Timeout) 185 if err != nil { 186 return err 187 } 188 189 return RetryWithContext(ctx, RetryInterval, func() (bool, error) { 190 // Check that the Interconnect deployment of the expected size is created 191 s, err := f.InterconnectHasExpectedSize(interconnect, expectedSize) 192 if err != nil { 193 return false, nil 194 } 195 if !s { 196 return false, nil 197 } 198 199 // Check whether all pods in the cluster are reporting the expected version 200 if expectedVersion != "" { 201 v, err := f.InterconnectHasExpectedVersion(interconnect, expectedVersion) 202 if err != nil { 203 return false, nil 204 } 205 if !v { 206 return false, nil 207 } 208 } 209 return true, nil 210 }) 211 }