1/* 2Copyright 2019 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package testing 18 19import ( 20 "fmt" 21 22 "k8s.io/api/core/v1" 23 "k8s.io/apimachinery/pkg/api/resource" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/apimachinery/pkg/runtime/schema" 26 "k8s.io/apimachinery/pkg/types" 27 "k8s.io/utils/pointer" 28) 29 30var zero int64 31 32// NodeSelectorWrapper wraps a NodeSelector inside. 33type NodeSelectorWrapper struct{ v1.NodeSelector } 34 35// MakeNodeSelector creates a NodeSelector wrapper. 36func MakeNodeSelector() *NodeSelectorWrapper { 37 return &NodeSelectorWrapper{v1.NodeSelector{}} 38} 39 40// In injects a matchExpression (with an operator IN) as a selectorTerm 41// to the inner nodeSelector. 42// NOTE: appended selecterTerms are ORed. 43func (s *NodeSelectorWrapper) In(key string, vals []string) *NodeSelectorWrapper { 44 expression := v1.NodeSelectorRequirement{ 45 Key: key, 46 Operator: v1.NodeSelectorOpIn, 47 Values: vals, 48 } 49 selectorTerm := v1.NodeSelectorTerm{} 50 selectorTerm.MatchExpressions = append(selectorTerm.MatchExpressions, expression) 51 s.NodeSelectorTerms = append(s.NodeSelectorTerms, selectorTerm) 52 return s 53} 54 55// NotIn injects a matchExpression (with an operator NotIn) as a selectorTerm 56// to the inner nodeSelector. 57func (s *NodeSelectorWrapper) NotIn(key string, vals []string) *NodeSelectorWrapper { 58 expression := v1.NodeSelectorRequirement{ 59 Key: key, 60 Operator: v1.NodeSelectorOpNotIn, 61 Values: vals, 62 } 63 selectorTerm := v1.NodeSelectorTerm{} 64 selectorTerm.MatchExpressions = append(selectorTerm.MatchExpressions, expression) 65 s.NodeSelectorTerms = append(s.NodeSelectorTerms, selectorTerm) 66 return s 67} 68 69// Obj returns the inner NodeSelector. 70func (s *NodeSelectorWrapper) Obj() *v1.NodeSelector { 71 return &s.NodeSelector 72} 73 74// LabelSelectorWrapper wraps a LabelSelector inside. 75type LabelSelectorWrapper struct{ metav1.LabelSelector } 76 77// MakeLabelSelector creates a LabelSelector wrapper. 78func MakeLabelSelector() *LabelSelectorWrapper { 79 return &LabelSelectorWrapper{metav1.LabelSelector{}} 80} 81 82// Label applies a {k,v} pair to the inner LabelSelector. 83func (s *LabelSelectorWrapper) Label(k, v string) *LabelSelectorWrapper { 84 if s.MatchLabels == nil { 85 s.MatchLabels = make(map[string]string) 86 } 87 s.MatchLabels[k] = v 88 return s 89} 90 91// In injects a matchExpression (with an operator In) to the inner labelSelector. 92func (s *LabelSelectorWrapper) In(key string, vals []string) *LabelSelectorWrapper { 93 expression := metav1.LabelSelectorRequirement{ 94 Key: key, 95 Operator: metav1.LabelSelectorOpIn, 96 Values: vals, 97 } 98 s.MatchExpressions = append(s.MatchExpressions, expression) 99 return s 100} 101 102// NotIn injects a matchExpression (with an operator NotIn) to the inner labelSelector. 103func (s *LabelSelectorWrapper) NotIn(key string, vals []string) *LabelSelectorWrapper { 104 expression := metav1.LabelSelectorRequirement{ 105 Key: key, 106 Operator: metav1.LabelSelectorOpNotIn, 107 Values: vals, 108 } 109 s.MatchExpressions = append(s.MatchExpressions, expression) 110 return s 111} 112 113// Exists injects a matchExpression (with an operator Exists) to the inner labelSelector. 114func (s *LabelSelectorWrapper) Exists(k string) *LabelSelectorWrapper { 115 expression := metav1.LabelSelectorRequirement{ 116 Key: k, 117 Operator: metav1.LabelSelectorOpExists, 118 } 119 s.MatchExpressions = append(s.MatchExpressions, expression) 120 return s 121} 122 123// NotExist injects a matchExpression (with an operator NotExist) to the inner labelSelector. 124func (s *LabelSelectorWrapper) NotExist(k string) *LabelSelectorWrapper { 125 expression := metav1.LabelSelectorRequirement{ 126 Key: k, 127 Operator: metav1.LabelSelectorOpDoesNotExist, 128 } 129 s.MatchExpressions = append(s.MatchExpressions, expression) 130 return s 131} 132 133// Obj returns the inner LabelSelector. 134func (s *LabelSelectorWrapper) Obj() *metav1.LabelSelector { 135 return &s.LabelSelector 136} 137 138// PodWrapper wraps a Pod inside. 139type PodWrapper struct{ v1.Pod } 140 141// MakePod creates a Pod wrapper. 142func MakePod() *PodWrapper { 143 return &PodWrapper{v1.Pod{}} 144} 145 146// Obj returns the inner Pod. 147func (p *PodWrapper) Obj() *v1.Pod { 148 return &p.Pod 149} 150 151// Name sets `s` as the name of the inner pod. 152func (p *PodWrapper) Name(s string) *PodWrapper { 153 p.SetName(s) 154 return p 155} 156 157// UID sets `s` as the UID of the inner pod. 158func (p *PodWrapper) UID(s string) *PodWrapper { 159 p.SetUID(types.UID(s)) 160 return p 161} 162 163// SchedulerName sets `s` as the scheduler name of the inner pod. 164func (p *PodWrapper) SchedulerName(s string) *PodWrapper { 165 p.Spec.SchedulerName = s 166 return p 167} 168 169// Namespace sets `s` as the namespace of the inner pod. 170func (p *PodWrapper) Namespace(s string) *PodWrapper { 171 p.SetNamespace(s) 172 return p 173} 174 175// OwnerReference updates the owning controller of the pod. 176func (p *PodWrapper) OwnerReference(name string, gvk schema.GroupVersionKind) *PodWrapper { 177 p.OwnerReferences = []metav1.OwnerReference{ 178 { 179 APIVersion: gvk.GroupVersion().String(), 180 Kind: gvk.Kind, 181 Name: name, 182 Controller: pointer.BoolPtr(true), 183 }, 184 } 185 return p 186} 187 188// Container appends a container into PodSpec of the inner pod. 189func (p *PodWrapper) Container(s string) *PodWrapper { 190 p.Spec.Containers = append(p.Spec.Containers, v1.Container{ 191 Name: fmt.Sprintf("con%d", len(p.Spec.Containers)), 192 Image: s, 193 }) 194 return p 195} 196 197// Priority sets a priority value into PodSpec of the inner pod. 198func (p *PodWrapper) Priority(val int32) *PodWrapper { 199 p.Spec.Priority = &val 200 return p 201} 202 203// Terminating sets the inner pod's deletionTimestamp to current timestamp. 204func (p *PodWrapper) Terminating() *PodWrapper { 205 now := metav1.Now() 206 p.DeletionTimestamp = &now 207 return p 208} 209 210// ZeroTerminationGracePeriod sets the TerminationGracePeriodSeconds of the inner pod to zero. 211func (p *PodWrapper) ZeroTerminationGracePeriod() *PodWrapper { 212 p.Spec.TerminationGracePeriodSeconds = &zero 213 return p 214} 215 216// Node sets `s` as the nodeName of the inner pod. 217func (p *PodWrapper) Node(s string) *PodWrapper { 218 p.Spec.NodeName = s 219 return p 220} 221 222// NodeSelector sets `m` as the nodeSelector of the inner pod. 223func (p *PodWrapper) NodeSelector(m map[string]string) *PodWrapper { 224 p.Spec.NodeSelector = m 225 return p 226} 227 228// NodeAffinityIn creates a HARD node affinity (with the operator In) 229// and injects into the inner pod. 230func (p *PodWrapper) NodeAffinityIn(key string, vals []string) *PodWrapper { 231 if p.Spec.Affinity == nil { 232 p.Spec.Affinity = &v1.Affinity{} 233 } 234 if p.Spec.Affinity.NodeAffinity == nil { 235 p.Spec.Affinity.NodeAffinity = &v1.NodeAffinity{} 236 } 237 nodeSelector := MakeNodeSelector().In(key, vals).Obj() 238 p.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution = nodeSelector 239 return p 240} 241 242// NodeAffinityNotIn creates a HARD node affinity (with the operator NotIn) 243// and injects into the inner pod. 244func (p *PodWrapper) NodeAffinityNotIn(key string, vals []string) *PodWrapper { 245 if p.Spec.Affinity == nil { 246 p.Spec.Affinity = &v1.Affinity{} 247 } 248 if p.Spec.Affinity.NodeAffinity == nil { 249 p.Spec.Affinity.NodeAffinity = &v1.NodeAffinity{} 250 } 251 nodeSelector := MakeNodeSelector().NotIn(key, vals).Obj() 252 p.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution = nodeSelector 253 return p 254} 255 256// StartTime sets `t` as .status.startTime for the inner pod. 257func (p *PodWrapper) StartTime(t metav1.Time) *PodWrapper { 258 p.Status.StartTime = &t 259 return p 260} 261 262// NominatedNodeName sets `n` as the .Status.NominatedNodeName of the inner pod. 263func (p *PodWrapper) NominatedNodeName(n string) *PodWrapper { 264 p.Status.NominatedNodeName = n 265 return p 266} 267 268// Toleration creates a toleration (with the operator Exists) 269// and injects into the inner pod. 270func (p *PodWrapper) Toleration(key string) *PodWrapper { 271 p.Spec.Tolerations = append(p.Spec.Tolerations, v1.Toleration{ 272 Key: key, 273 Operator: v1.TolerationOpExists, 274 }) 275 return p 276} 277 278// HostPort creates a container with a hostPort valued `hostPort`, 279// and injects into the inner pod. 280func (p *PodWrapper) HostPort(port int32) *PodWrapper { 281 p.Spec.Containers = append(p.Spec.Containers, v1.Container{ 282 Ports: []v1.ContainerPort{{HostPort: port}}, 283 }) 284 return p 285} 286 287// PodAffinityKind represents different kinds of PodAffinity. 288type PodAffinityKind int 289 290const ( 291 // NilPodAffinity is a no-op which doesn't apply any PodAffinity. 292 NilPodAffinity PodAffinityKind = iota 293 // PodAffinityWithRequiredReq applies a HARD requirement to pod.spec.affinity.PodAffinity. 294 PodAffinityWithRequiredReq 295 // PodAffinityWithPreferredReq applies a SOFT requirement to pod.spec.affinity.PodAffinity. 296 PodAffinityWithPreferredReq 297 // PodAffinityWithRequiredPreferredReq applies HARD and SOFT requirements to pod.spec.affinity.PodAffinity. 298 PodAffinityWithRequiredPreferredReq 299 // PodAntiAffinityWithRequiredReq applies a HARD requirement to pod.spec.affinity.PodAntiAffinity. 300 PodAntiAffinityWithRequiredReq 301 // PodAntiAffinityWithPreferredReq applies a SOFT requirement to pod.spec.affinity.PodAntiAffinity. 302 PodAntiAffinityWithPreferredReq 303 // PodAntiAffinityWithRequiredPreferredReq applies HARD and SOFT requirements to pod.spec.affinity.PodAntiAffinity. 304 PodAntiAffinityWithRequiredPreferredReq 305) 306 307// PodAffinityExists creates an PodAffinity with the operator "Exists" 308// and injects into the inner pod. 309func (p *PodWrapper) PodAffinityExists(labelKey, topologyKey string, kind PodAffinityKind) *PodWrapper { 310 if kind == NilPodAffinity { 311 return p 312 } 313 314 if p.Spec.Affinity == nil { 315 p.Spec.Affinity = &v1.Affinity{} 316 } 317 if p.Spec.Affinity.PodAffinity == nil { 318 p.Spec.Affinity.PodAffinity = &v1.PodAffinity{} 319 } 320 labelSelector := MakeLabelSelector().Exists(labelKey).Obj() 321 term := v1.PodAffinityTerm{LabelSelector: labelSelector, TopologyKey: topologyKey} 322 switch kind { 323 case PodAffinityWithRequiredReq: 324 p.Spec.Affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution = append( 325 p.Spec.Affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution, 326 term, 327 ) 328 case PodAffinityWithPreferredReq: 329 p.Spec.Affinity.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution = append( 330 p.Spec.Affinity.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution, 331 v1.WeightedPodAffinityTerm{Weight: 1, PodAffinityTerm: term}, 332 ) 333 case PodAffinityWithRequiredPreferredReq: 334 p.Spec.Affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution = append( 335 p.Spec.Affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution, 336 term, 337 ) 338 p.Spec.Affinity.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution = append( 339 p.Spec.Affinity.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution, 340 v1.WeightedPodAffinityTerm{Weight: 1, PodAffinityTerm: term}, 341 ) 342 } 343 return p 344} 345 346// PodAntiAffinityExists creates an PodAntiAffinity with the operator "Exists" 347// and injects into the inner pod. 348func (p *PodWrapper) PodAntiAffinityExists(labelKey, topologyKey string, kind PodAffinityKind) *PodWrapper { 349 if kind == NilPodAffinity { 350 return p 351 } 352 353 if p.Spec.Affinity == nil { 354 p.Spec.Affinity = &v1.Affinity{} 355 } 356 if p.Spec.Affinity.PodAntiAffinity == nil { 357 p.Spec.Affinity.PodAntiAffinity = &v1.PodAntiAffinity{} 358 } 359 labelSelector := MakeLabelSelector().Exists(labelKey).Obj() 360 term := v1.PodAffinityTerm{LabelSelector: labelSelector, TopologyKey: topologyKey} 361 switch kind { 362 case PodAntiAffinityWithRequiredReq: 363 p.Spec.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution = append( 364 p.Spec.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution, 365 term, 366 ) 367 case PodAntiAffinityWithPreferredReq: 368 p.Spec.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution = append( 369 p.Spec.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution, 370 v1.WeightedPodAffinityTerm{Weight: 1, PodAffinityTerm: term}, 371 ) 372 case PodAntiAffinityWithRequiredPreferredReq: 373 p.Spec.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution = append( 374 p.Spec.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution, 375 term, 376 ) 377 p.Spec.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution = append( 378 p.Spec.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution, 379 v1.WeightedPodAffinityTerm{Weight: 1, PodAffinityTerm: term}, 380 ) 381 } 382 return p 383} 384 385// SpreadConstraint constructs a TopologySpreadConstraint object and injects 386// into the inner pod. 387func (p *PodWrapper) SpreadConstraint(maxSkew int, tpKey string, mode v1.UnsatisfiableConstraintAction, selector *metav1.LabelSelector) *PodWrapper { 388 c := v1.TopologySpreadConstraint{ 389 MaxSkew: int32(maxSkew), 390 TopologyKey: tpKey, 391 WhenUnsatisfiable: mode, 392 LabelSelector: selector, 393 } 394 p.Spec.TopologySpreadConstraints = append(p.Spec.TopologySpreadConstraints, c) 395 return p 396} 397 398// Label sets a {k,v} pair to the inner pod. 399func (p *PodWrapper) Label(k, v string) *PodWrapper { 400 if p.Labels == nil { 401 p.Labels = make(map[string]string) 402 } 403 p.Labels[k] = v 404 return p 405} 406 407// Req adds a new container to the inner pod with given resource map. 408func (p *PodWrapper) Req(resMap map[v1.ResourceName]string) *PodWrapper { 409 if len(resMap) == 0 { 410 return p 411 } 412 413 res := v1.ResourceList{} 414 for k, v := range resMap { 415 res[k] = resource.MustParse(v) 416 } 417 p.Spec.Containers = append(p.Spec.Containers, v1.Container{ 418 Resources: v1.ResourceRequirements{ 419 Requests: res, 420 }, 421 }) 422 return p 423} 424 425// PreemptionPolicy sets the give preemption policy to the inner pod. 426func (p *PodWrapper) PreemptionPolicy(policy v1.PreemptionPolicy) *PodWrapper { 427 p.Spec.PreemptionPolicy = &policy 428 return p 429} 430 431// NodeWrapper wraps a Node inside. 432type NodeWrapper struct{ v1.Node } 433 434// MakeNode creates a Node wrapper. 435func MakeNode() *NodeWrapper { 436 w := &NodeWrapper{v1.Node{}} 437 return w.Capacity(nil) 438} 439 440// Obj returns the inner Node. 441func (n *NodeWrapper) Obj() *v1.Node { 442 return &n.Node 443} 444 445// Name sets `s` as the name of the inner pod. 446func (n *NodeWrapper) Name(s string) *NodeWrapper { 447 n.SetName(s) 448 return n 449} 450 451// UID sets `s` as the UID of the inner pod. 452func (n *NodeWrapper) UID(s string) *NodeWrapper { 453 n.SetUID(types.UID(s)) 454 return n 455} 456 457// Label applies a {k,v} label pair to the inner node. 458func (n *NodeWrapper) Label(k, v string) *NodeWrapper { 459 if n.Labels == nil { 460 n.Labels = make(map[string]string) 461 } 462 n.Labels[k] = v 463 return n 464} 465 466// Capacity sets the capacity and the allocatable resources of the inner node. 467// Each entry in `resources` corresponds to a resource name and its quantity. 468// By default, the capacity and allocatable number of pods are set to 32. 469func (n *NodeWrapper) Capacity(resources map[v1.ResourceName]string) *NodeWrapper { 470 res := v1.ResourceList{ 471 v1.ResourcePods: resource.MustParse("32"), 472 } 473 for name, value := range resources { 474 res[name] = resource.MustParse(value) 475 } 476 n.Status.Capacity, n.Status.Allocatable = res, res 477 return n 478} 479 480// Images sets the images of the inner node. Each entry in `images` corresponds 481// to an image name and its size in bytes. 482func (n *NodeWrapper) Images(images map[string]int64) *NodeWrapper { 483 var containerImages []v1.ContainerImage 484 for name, size := range images { 485 containerImages = append(containerImages, v1.ContainerImage{Names: []string{name}, SizeBytes: size}) 486 } 487 n.Status.Images = containerImages 488 return n 489} 490