github.com/xeptore/docker-cli@v20.10.14+incompatible/cli/command/stack/kubernetes/conversion_test.go (about)

     1  package kubernetes
     2  
     3  import (
     4  	"testing"
     5  
     6  	. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
     7  	"github.com/docker/compose-on-kubernetes/api/labels"
     8  	"github.com/docker/docker/api/types/swarm"
     9  	"gotest.tools/v3/assert"
    10  	appsv1beta2 "k8s.io/api/apps/v1beta2"
    11  	apiv1 "k8s.io/api/core/v1"
    12  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    13  	apimachineryTypes "k8s.io/apimachinery/pkg/types"
    14  	apimachineryUtil "k8s.io/apimachinery/pkg/util/intstr"
    15  )
    16  
    17  func TestReplicasConversionNeedsAService(t *testing.T) {
    18  	replicas := appsv1beta2.ReplicaSetList{
    19  		Items: []appsv1beta2.ReplicaSet{makeReplicaSet("unknown", 0, 0)},
    20  	}
    21  	services := apiv1.ServiceList{}
    22  	_, err := convertToServices(&replicas, &appsv1beta2.DaemonSetList{}, &services)
    23  	assert.ErrorContains(t, err, "could not find service")
    24  }
    25  
    26  func TestKubernetesServiceToSwarmServiceConversion(t *testing.T) {
    27  	testCases := []struct {
    28  		doc              string
    29  		replicas         *appsv1beta2.ReplicaSetList
    30  		services         *apiv1.ServiceList
    31  		expectedServices []swarm.Service
    32  	}{
    33  		{
    34  			doc: "Match replicas with headless stack services",
    35  			replicas: &appsv1beta2.ReplicaSetList{
    36  				Items: []appsv1beta2.ReplicaSet{
    37  					makeReplicaSet("service1", 2, 5),
    38  					makeReplicaSet("service2", 3, 3),
    39  				},
    40  			},
    41  			services: &apiv1.ServiceList{
    42  				Items: []apiv1.Service{
    43  					makeKubeService("service1", "stack", "uid1", apiv1.ServiceTypeClusterIP, nil),
    44  					makeKubeService("service2", "stack", "uid2", apiv1.ServiceTypeClusterIP, nil),
    45  					makeKubeService("service3", "other-stack", "uid2", apiv1.ServiceTypeClusterIP, nil),
    46  				},
    47  			},
    48  			expectedServices: []swarm.Service{
    49  				makeSwarmService(t, "stack_service1", "uid1", ReplicatedService(5), ServiceStatus(5, 2)),
    50  				makeSwarmService(t, "stack_service2", "uid2", ReplicatedService(3), ServiceStatus(3, 3)),
    51  			},
    52  		},
    53  		{
    54  			doc: "Headless service and LoadBalancer Service are tied to the same Swarm service",
    55  			replicas: &appsv1beta2.ReplicaSetList{
    56  				Items: []appsv1beta2.ReplicaSet{
    57  					makeReplicaSet("service", 1, 1),
    58  				},
    59  			},
    60  			services: &apiv1.ServiceList{
    61  				Items: []apiv1.Service{
    62  					makeKubeService("service", "stack", "uid1", apiv1.ServiceTypeClusterIP, nil),
    63  					makeKubeService("service-published", "stack", "uid2", apiv1.ServiceTypeLoadBalancer, []apiv1.ServicePort{
    64  						{
    65  							Port:       80,
    66  							TargetPort: apimachineryUtil.FromInt(80),
    67  							Protocol:   apiv1.ProtocolTCP,
    68  						},
    69  					}),
    70  				},
    71  			},
    72  			expectedServices: []swarm.Service{
    73  				makeSwarmService(t, "stack_service", "uid1",
    74  					ReplicatedService(1),
    75  					ServiceStatus(1, 1),
    76  					withPort(swarm.PortConfig{
    77  						PublishMode:   swarm.PortConfigPublishModeIngress,
    78  						PublishedPort: 80,
    79  						TargetPort:    80,
    80  						Protocol:      swarm.PortConfigProtocolTCP,
    81  					}),
    82  				),
    83  			},
    84  		},
    85  		{
    86  			doc: "Headless service and NodePort Service are tied to the same Swarm service",
    87  			replicas: &appsv1beta2.ReplicaSetList{
    88  				Items: []appsv1beta2.ReplicaSet{
    89  					makeReplicaSet("service", 1, 1),
    90  				},
    91  			},
    92  			services: &apiv1.ServiceList{
    93  				Items: []apiv1.Service{
    94  					makeKubeService("service", "stack", "uid1", apiv1.ServiceTypeClusterIP, nil),
    95  					makeKubeService("service-random-ports", "stack", "uid2", apiv1.ServiceTypeNodePort, []apiv1.ServicePort{
    96  						{
    97  							Port:       35666,
    98  							TargetPort: apimachineryUtil.FromInt(80),
    99  							Protocol:   apiv1.ProtocolTCP,
   100  						},
   101  					}),
   102  				},
   103  			},
   104  			expectedServices: []swarm.Service{
   105  				makeSwarmService(t, "stack_service", "uid1",
   106  					ReplicatedService(1),
   107  					ServiceStatus(1, 1),
   108  					withPort(swarm.PortConfig{
   109  						PublishMode:   swarm.PortConfigPublishModeHost,
   110  						PublishedPort: 35666,
   111  						TargetPort:    80,
   112  						Protocol:      swarm.PortConfigProtocolTCP,
   113  					}),
   114  				),
   115  			},
   116  		},
   117  	}
   118  
   119  	for _, tc := range testCases {
   120  		tc := tc
   121  		t.Run(tc.doc, func(t *testing.T) {
   122  			swarmServices, err := convertToServices(tc.replicas, &appsv1beta2.DaemonSetList{}, tc.services)
   123  			assert.NilError(t, err)
   124  			assert.DeepEqual(t, tc.expectedServices, swarmServices)
   125  		})
   126  	}
   127  }
   128  
   129  func makeReplicaSet(service string, available, replicas int32) appsv1beta2.ReplicaSet {
   130  	return appsv1beta2.ReplicaSet{
   131  		ObjectMeta: metav1.ObjectMeta{
   132  			Labels: map[string]string{
   133  				labels.ForServiceName: service,
   134  			},
   135  		},
   136  		Spec: appsv1beta2.ReplicaSetSpec{
   137  			Template: apiv1.PodTemplateSpec{
   138  				Spec: apiv1.PodSpec{
   139  					Containers: []apiv1.Container{
   140  						{
   141  							Image: "image",
   142  						},
   143  					},
   144  				},
   145  			},
   146  		},
   147  		Status: appsv1beta2.ReplicaSetStatus{
   148  			AvailableReplicas: available,
   149  			Replicas:          replicas,
   150  		},
   151  	}
   152  }
   153  
   154  func makeKubeService(service, stack, uid string, serviceType apiv1.ServiceType, ports []apiv1.ServicePort) apiv1.Service {
   155  	return apiv1.Service{
   156  		ObjectMeta: metav1.ObjectMeta{
   157  			Labels: map[string]string{
   158  				labels.ForStackName: stack,
   159  			},
   160  			Name: service,
   161  			UID:  apimachineryTypes.UID(uid),
   162  		},
   163  		Spec: apiv1.ServiceSpec{
   164  			Type:  serviceType,
   165  			Ports: ports,
   166  		},
   167  	}
   168  }
   169  
   170  // TODO convertToServices currently doesn't set swarm.EndpointSpec.Ports
   171  func withPort(port swarm.PortConfig) func(*swarm.Service) {
   172  	return func(service *swarm.Service) {
   173  		if service.Endpoint.Ports == nil {
   174  			service.Endpoint.Ports = make([]swarm.PortConfig, 0)
   175  		}
   176  		service.Endpoint.Ports = append(service.Endpoint.Ports, port)
   177  	}
   178  }
   179  
   180  func makeSwarmService(t *testing.T, name, id string, opts ...func(*swarm.Service)) swarm.Service {
   181  	t.Helper()
   182  	options := []func(*swarm.Service){ServiceID(id), ServiceName(name), ServiceImage("image")}
   183  	options = append(options, opts...)
   184  	return *Service(options...)
   185  }