k8s.io/kubernetes@v1.29.3/test/e2e/network/netpol/model.go (about) 1 /* 2 Copyright 2020 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package netpol 18 19 import ( 20 "fmt" 21 "strings" 22 23 v1 "k8s.io/api/core/v1" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/kubernetes/test/e2e/framework" 26 imageutils "k8s.io/kubernetes/test/utils/image" 27 ) 28 29 // Model defines the namespaces, deployments, services, pods, containers and associated 30 // data for network policy test cases and provides the source of truth 31 type Model struct { 32 Namespaces []*Namespace 33 PodNames []string 34 Ports []int32 35 Protocols []v1.Protocol 36 } 37 38 // NewWindowsModel returns a model specific to windows testing. 39 func NewWindowsModel(namespaceBaseNames []string, podNames []string, ports []int32) *Model { 40 return NewModel(namespaceBaseNames, podNames, ports, []v1.Protocol{v1.ProtocolTCP, v1.ProtocolUDP}) 41 } 42 43 // NewModel instantiates a model based on: 44 // - namespaceBaseNames 45 // - pods 46 // - ports to listen on 47 // - protocols to listen on 48 // The total number of pods is the number of namespaces x the number of pods per namespace. 49 // The number of containers per pod is the number of ports x the number of protocols. 50 // The *total* number of containers is namespaces x pods x ports x protocols. 51 func NewModel(namespaceBaseNames []string, podNames []string, ports []int32, protocols []v1.Protocol) *Model { 52 model := &Model{ 53 PodNames: podNames, 54 Ports: ports, 55 Protocols: protocols, 56 } 57 58 // build the entire "model" for the overall test, which means, building 59 // namespaces, pods, containers for each protocol. 60 for _, ns := range namespaceBaseNames { 61 var pods []*Pod 62 for _, podName := range podNames { 63 var containers []*Container 64 for _, port := range ports { 65 for _, protocol := range protocols { 66 containers = append(containers, &Container{ 67 Port: port, 68 Protocol: protocol, 69 }) 70 } 71 } 72 pods = append(pods, &Pod{ 73 Name: podName, 74 Containers: containers, 75 }) 76 } 77 model.Namespaces = append(model.Namespaces, &Namespace{ 78 BaseName: ns, 79 Pods: pods, 80 }) 81 } 82 return model 83 } 84 85 // Namespace is the abstract representation of what matters to network policy 86 // tests for a namespace; i.e. it ignores kube implementation details 87 type Namespace struct { 88 BaseName string 89 Pods []*Pod 90 } 91 92 // Pod is the abstract representation of what matters to network policy tests for 93 // a pod; i.e. it ignores kube implementation details 94 type Pod struct { 95 Name string 96 Containers []*Container 97 } 98 99 // ContainerSpecs builds kubernetes container specs for the pod 100 func (p *Pod) ContainerSpecs() []v1.Container { 101 var containers []v1.Container 102 for _, cont := range p.Containers { 103 containers = append(containers, cont.Spec()) 104 } 105 return containers 106 } 107 108 func podNameLabelKey() string { 109 return "pod" 110 } 111 112 // Labels returns the default labels that should be placed on a pod/deployment 113 // in order for it to be uniquely selectable by label selectors 114 func (p *Pod) Labels() map[string]string { 115 return map[string]string{ 116 podNameLabelKey(): p.Name, 117 } 118 } 119 120 // KubePod returns the kube pod (will add label selectors for windows if needed). 121 func (p *Pod) KubePod(namespace string) *v1.Pod { 122 zero := int64(0) 123 124 thePod := &v1.Pod{ 125 ObjectMeta: metav1.ObjectMeta{ 126 Name: p.Name, 127 Labels: p.Labels(), 128 Namespace: namespace, 129 }, 130 Spec: v1.PodSpec{ 131 TerminationGracePeriodSeconds: &zero, 132 Containers: p.ContainerSpecs(), 133 }, 134 } 135 136 if framework.NodeOSDistroIs("windows") { 137 thePod.Spec.NodeSelector = map[string]string{ 138 "kubernetes.io/os": "windows", 139 } 140 } 141 return thePod 142 } 143 144 // QualifiedServiceAddress returns the address that can be used to access the service 145 func (p *Pod) QualifiedServiceAddress(namespace string, dnsDomain string) string { 146 return fmt.Sprintf("%s.%s.svc.%s", p.ServiceName(namespace), namespace, dnsDomain) 147 } 148 149 // ServiceName returns the unqualified service name 150 func (p *Pod) ServiceName(namespace string) string { 151 return fmt.Sprintf("s-%s-%s", namespace, p.Name) 152 } 153 154 // Service returns a kube service spec 155 func (p *Pod) Service(namespace string) *v1.Service { 156 service := &v1.Service{ 157 ObjectMeta: metav1.ObjectMeta{ 158 Name: p.ServiceName(namespace), 159 Namespace: namespace, 160 }, 161 Spec: v1.ServiceSpec{ 162 Selector: p.Labels(), 163 }, 164 } 165 for _, container := range p.Containers { 166 service.Spec.Ports = append(service.Spec.Ports, v1.ServicePort{ 167 Name: fmt.Sprintf("service-port-%s-%d", strings.ToLower(string(container.Protocol)), container.Port), 168 Protocol: container.Protocol, 169 Port: container.Port, 170 }) 171 } 172 return service 173 } 174 175 // Container is the abstract representation of what matters to network policy tests for 176 // a container; i.e. it ignores kube implementation details 177 type Container struct { 178 Port int32 179 Protocol v1.Protocol 180 } 181 182 // Name returns the container name 183 func (c *Container) Name() string { 184 return fmt.Sprintf("cont-%d-%s", c.Port, strings.ToLower(string(c.Protocol))) 185 } 186 187 // PortName returns the container port name 188 func (c *Container) PortName() string { 189 return fmt.Sprintf("serve-%d-%s", c.Port, strings.ToLower(string(c.Protocol))) 190 } 191 192 // Spec returns the kube container spec 193 func (c *Container) Spec() v1.Container { 194 var ( 195 // agnHostImage is the image URI of AgnHost 196 agnHostImage = imageutils.GetE2EImage(imageutils.Agnhost) 197 env = []v1.EnvVar{} 198 cmd []string 199 ) 200 201 switch c.Protocol { 202 case v1.ProtocolTCP: 203 cmd = []string{"/agnhost", "serve-hostname", "--tcp", "--http=false", "--port", fmt.Sprintf("%d", c.Port)} 204 case v1.ProtocolUDP: 205 cmd = []string{"/agnhost", "serve-hostname", "--udp", "--http=false", "--port", fmt.Sprintf("%d", c.Port)} 206 case v1.ProtocolSCTP: 207 env = append(env, v1.EnvVar{ 208 Name: fmt.Sprintf("SERVE_SCTP_PORT_%d", c.Port), 209 Value: "foo", 210 }) 211 cmd = []string{"/agnhost", "porter"} 212 default: 213 framework.Failf("invalid protocol %v", c.Protocol) 214 } 215 216 return v1.Container{ 217 Name: c.Name(), 218 ImagePullPolicy: v1.PullIfNotPresent, 219 Image: agnHostImage, 220 Command: cmd, 221 Env: env, 222 SecurityContext: &v1.SecurityContext{}, 223 Ports: []v1.ContainerPort{ 224 { 225 ContainerPort: c.Port, 226 Name: c.PortName(), 227 Protocol: c.Protocol, 228 }, 229 }, 230 } 231 }