sigs.k8s.io/cluster-api-provider-azure@v1.14.3/test/e2e/kubernetes/deployment/deployment.go (about) 1 //go:build e2e 2 // +build e2e 3 4 /* 5 Copyright 2020 The Kubernetes Authors. 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 */ 19 20 package deployment 21 22 import ( 23 "context" 24 "fmt" 25 "log" 26 "time" 27 28 . "github.com/onsi/gomega" 29 appsv1 "k8s.io/api/apps/v1" 30 corev1 "k8s.io/api/core/v1" 31 "k8s.io/apimachinery/pkg/api/resource" 32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 33 "k8s.io/apimachinery/pkg/labels" 34 "k8s.io/client-go/kubernetes" 35 typedappsv1 "k8s.io/client-go/kubernetes/typed/apps/v1" 36 "k8s.io/utils/ptr" 37 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 38 ) 39 40 const ( 41 ExternalLoadbalancer = LoadbalancerType("external") 42 InternalLoadbalancer = LoadbalancerType("internal") 43 deploymentOperationTimeout = 30 * time.Second 44 deploymentOperationSleepBetweenRetries = 3 * time.Second 45 ) 46 47 type ( 48 // Builder provides a helper interface for building Kubernetes deployments within this test suite 49 Builder struct { 50 deployment *appsv1.Deployment 51 } 52 53 LoadbalancerType string 54 ) 55 56 // Create will create a deployment for a given image with a name in a namespace 57 func Create(image, name, namespace string) *Builder { 58 e2eDeployment := &Builder{ 59 deployment: &appsv1.Deployment{ 60 ObjectMeta: metav1.ObjectMeta{ 61 Name: name, 62 Namespace: namespace, 63 Labels: map[string]string{}, 64 }, 65 Spec: appsv1.DeploymentSpec{ 66 Replicas: ptr.To[int32](1), 67 Selector: &metav1.LabelSelector{ 68 MatchLabels: map[string]string{ 69 "app": name, 70 }, 71 }, 72 Template: corev1.PodTemplateSpec{ 73 ObjectMeta: metav1.ObjectMeta{ 74 Labels: map[string]string{ 75 "app": name, 76 }, 77 }, 78 Spec: corev1.PodSpec{ 79 Containers: []corev1.Container{ 80 { 81 Name: name, 82 Image: image, 83 Resources: corev1.ResourceRequirements{ 84 Requests: corev1.ResourceList{ 85 corev1.ResourceCPU: resource.MustParse("10m"), 86 corev1.ResourceMemory: resource.MustParse("10M"), 87 }, 88 }, 89 }, 90 }, 91 NodeSelector: map[string]string{ 92 "kubernetes.io/os": "linux", 93 }, 94 }, 95 }, 96 }, 97 }, 98 } 99 100 return e2eDeployment 101 } 102 103 func (d *Builder) AddLabels(labels map[string]string) { 104 for k, v := range labels { 105 d.deployment.ObjectMeta.Labels[k] = v 106 d.deployment.Spec.Selector.MatchLabels[k] = v 107 d.deployment.Spec.Template.ObjectMeta.Labels[k] = v 108 } 109 } 110 111 func (d *Builder) GetName() string { 112 return d.deployment.Name 113 } 114 115 func (d *Builder) SetReplicas(replicas int32) *Builder { 116 d.deployment.Spec.Replicas = &replicas 117 return d 118 } 119 120 func (d *Builder) AddContainerPort(name, portName string, portNumber int32, protocol corev1.Protocol) { 121 for _, c := range d.deployment.Spec.Template.Spec.Containers { 122 if c.Name == name { 123 c.Ports = []corev1.ContainerPort{ 124 { 125 Name: portName, 126 ContainerPort: portNumber, 127 Protocol: protocol, 128 }, 129 } 130 } 131 } 132 } 133 134 func (d *Builder) AddPVC(pvcName string) *Builder { 135 volumes := []corev1.Volume{ 136 { 137 Name: "managed", 138 VolumeSource: corev1.VolumeSource{ 139 PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ 140 ClaimName: pvcName, 141 }, 142 }, 143 }, 144 } 145 d.deployment.Spec.Template.Spec.Volumes = volumes 146 return d 147 } 148 149 func (d *Builder) Deploy(ctx context.Context, clientset *kubernetes.Clientset) (*appsv1.Deployment, error) { 150 var deployment *appsv1.Deployment 151 Eventually(func(g Gomega) { 152 var err error 153 deployment, err = d.Client(clientset).Create(ctx, d.deployment, metav1.CreateOptions{}) 154 if err != nil { 155 log.Printf("Error trying to deploy %s in namespace %s:%s\n", d.deployment.Name, d.deployment.ObjectMeta.Namespace, err.Error()) 156 } 157 g.Expect(err).NotTo(HaveOccurred()) 158 }, deploymentOperationTimeout, deploymentOperationSleepBetweenRetries).Should(Succeed()) 159 160 return deployment, nil 161 } 162 163 func (d *Builder) Client(clientset *kubernetes.Clientset) typedappsv1.DeploymentInterface { 164 return clientset.AppsV1().Deployments(d.deployment.ObjectMeta.Namespace) 165 } 166 167 func (d *Builder) GetPodsFromDeployment(ctx context.Context, clientset *kubernetes.Clientset) ([]corev1.Pod, error) { 168 opts := metav1.ListOptions{ 169 LabelSelector: labels.Set(d.deployment.Labels).String(), 170 Limit: 100, 171 } 172 var pods *corev1.PodList 173 Eventually(func(g Gomega) { 174 var err error 175 pods, err = clientset.CoreV1().Pods(d.deployment.GetNamespace()).List(ctx, opts) 176 if err != nil { 177 log.Printf("Error trying to get the pods from deployment %s:%s\n", d.deployment.GetName(), err.Error()) 178 } 179 g.Expect(err).NotTo(HaveOccurred()) 180 }, deploymentOperationTimeout, deploymentOperationSleepBetweenRetries).Should(Succeed()) 181 return pods.Items, nil 182 } 183 184 func (d *Builder) CreateServiceResourceSpec(ports []corev1.ServicePort, lbtype LoadbalancerType, ipFamilies []corev1.IPFamily) *corev1.Service { 185 suffix := "elb" 186 annotations := map[string]string{} 187 if lbtype == InternalLoadbalancer { 188 suffix = "ilb" 189 annotations["service.beta.kubernetes.io/azure-load-balancer-internal"] = "true" 190 } 191 192 return &corev1.Service{ 193 ObjectMeta: metav1.ObjectMeta{ 194 Name: fmt.Sprintf("%s-%s", d.deployment.Name, suffix), 195 Namespace: d.deployment.Namespace, 196 Annotations: annotations, 197 }, 198 Spec: corev1.ServiceSpec{ 199 Type: corev1.ServiceTypeLoadBalancer, 200 Ports: ports, 201 Selector: map[string]string{ 202 "app": d.deployment.Name, 203 }, 204 IPFamilies: ipFamilies, 205 }, 206 } 207 } 208 209 func (d *Builder) SetImage(name, image string) { 210 for i, c := range d.deployment.Spec.Template.Spec.Containers { 211 if c.Name == name { 212 c.Image = image 213 d.deployment.Spec.Template.Spec.Containers[i] = c 214 } 215 } 216 } 217 218 func (d *Builder) AddWindowsSelectors() *Builder { 219 if d.deployment.Spec.Template.Spec.NodeSelector == nil { 220 d.deployment.Spec.Template.Spec.NodeSelector = map[string]string{} 221 } 222 223 d.deployment.Spec.Template.Spec.NodeSelector["kubernetes.io/os"] = "windows" 224 return d 225 } 226 227 // AddMachinePoolSelectors will add node selectors which will ensure the workload runs on a specific MachinePool 228 func (d *Builder) AddMachinePoolSelectors(machinePoolName string) *Builder { 229 if d.deployment.Spec.Template.Spec.NodeSelector == nil { 230 d.deployment.Spec.Template.Spec.NodeSelector = map[string]string{} 231 } 232 233 d.deployment.Spec.Template.Spec.NodeSelector[clusterv1.OwnerKindAnnotation] = "MachinePool" 234 d.deployment.Spec.Template.Spec.NodeSelector[clusterv1.OwnerNameAnnotation] = machinePoolName 235 return d 236 } 237 238 func (d *Builder) AddPodAntiAffinity(affinity corev1.PodAntiAffinity) *Builder { 239 if d.deployment.Spec.Template.Spec.Affinity == nil { 240 d.deployment.Spec.Template.Spec.Affinity = &corev1.Affinity{} 241 } 242 243 d.deployment.Spec.Template.Spec.Affinity.PodAntiAffinity = &affinity 244 return d 245 }