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  }