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  }