github.com/docker/compose-on-kubernetes@v0.5.0/internal/convert/service_test.go (about)

     1  package convert
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/docker/compose-on-kubernetes/api/compose/latest"
     9  	"github.com/docker/compose-on-kubernetes/internal/stackresources"
    10  	. "github.com/docker/compose-on-kubernetes/internal/test/builders"
    11  	"github.com/stretchr/testify/assert"
    12  	apiv1 "k8s.io/api/core/v1"
    13  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    14  	"k8s.io/apimachinery/pkg/util/intstr"
    15  )
    16  
    17  func services(t *testing.T, stack *latest.Stack, strategy ServiceStrategy) (*apiv1.Service, *apiv1.Service, *apiv1.Service) {
    18  	s, err := StackToStack(*stack, strategy, stackresources.EmptyStackState)
    19  	assert.NoError(t, err)
    20  	var (
    21  		headless    *apiv1.Service
    22  		published   *apiv1.Service
    23  		randomPorts *apiv1.Service
    24  	)
    25  	for k, v := range s.Services {
    26  		local := v
    27  		if strings.HasSuffix(k, publishedServiceSuffix) {
    28  			published = &local
    29  		} else if strings.HasSuffix(k, publishedOnRandomPortSuffix) {
    30  			randomPorts = &local
    31  		} else {
    32  			headless = &local
    33  		}
    34  	}
    35  	return headless, published, randomPorts
    36  }
    37  
    38  func TestToServiceWithPublishedPort(t *testing.T) {
    39  	headless, published, randomPorts := services(t, Stack("demo",
    40  		WithService("nginx",
    41  			Image("any"),
    42  			WithPort(8080, Published(80)),
    43  			WithLabel("container.key", "container.value"),
    44  			Deploy(WithDeployLabel("deploy.key", "deploy.value")),
    45  		),
    46  	), loadBalancerServiceStrategy{})
    47  
    48  	expectedHeadless := &apiv1.Service{
    49  		ObjectMeta: metav1.ObjectMeta{
    50  			Name: "nginx",
    51  			Labels: map[string]string{
    52  				"com.docker.stack.namespace": "demo",
    53  				"com.docker.service.name":    "nginx",
    54  				"com.docker.service.id":      "demo-nginx",
    55  				"deploy.key":                 "deploy.value",
    56  			},
    57  		},
    58  		Spec: apiv1.ServiceSpec{
    59  			ClusterIP: apiv1.ClusterIPNone,
    60  			Ports: []apiv1.ServicePort{
    61  				{
    62  					Name:       headlessPortName,
    63  					Port:       headlessPort,
    64  					Protocol:   apiv1.ProtocolTCP,
    65  					TargetPort: intstr.FromInt(headlessPort),
    66  				},
    67  			},
    68  			Selector: map[string]string{
    69  				"com.docker.stack.namespace": "demo",
    70  				"com.docker.service.name":    "nginx",
    71  				"com.docker.service.id":      "demo-nginx",
    72  			},
    73  		},
    74  	}
    75  	assert.Equal(t, headless, expectedHeadless)
    76  
    77  	expectedPublished := &apiv1.Service{
    78  		ObjectMeta: metav1.ObjectMeta{
    79  			Name: fmt.Sprintf("nginx%s", publishedServiceSuffix),
    80  			Labels: map[string]string{
    81  				"com.docker.stack.namespace": "demo",
    82  				"com.docker.service.name":    "nginx",
    83  				"com.docker.service.id":      "demo-nginx",
    84  				"deploy.key":                 "deploy.value",
    85  			},
    86  		},
    87  		Spec: apiv1.ServiceSpec{
    88  			Type: apiv1.ServiceTypeLoadBalancer,
    89  			Ports: []apiv1.ServicePort{
    90  				{
    91  					Name:       "80-tcp",
    92  					Port:       80,
    93  					Protocol:   apiv1.ProtocolTCP,
    94  					TargetPort: intstr.FromInt(8080),
    95  				},
    96  			},
    97  			Selector: map[string]string{
    98  				"com.docker.stack.namespace": "demo",
    99  				"com.docker.service.name":    "nginx",
   100  				"com.docker.service.id":      "demo-nginx",
   101  			},
   102  		},
   103  	}
   104  	assert.Equal(t, published, expectedPublished)
   105  	assert.Nil(t, randomPorts)
   106  }
   107  
   108  func TestToServiceWithLongPort(t *testing.T) {
   109  	headless, published, randomPorts := services(t, Stack("demo",
   110  		WithService("nginx",
   111  			Image("any"),
   112  			WithPort(8888, Published(80), ProtocolUDP),
   113  		),
   114  	), loadBalancerServiceStrategy{})
   115  
   116  	assert.NotNil(t, headless)
   117  
   118  	expectedPorts := []apiv1.ServicePort{
   119  		{
   120  			Name:       "80-udp",
   121  			Port:       80,
   122  			TargetPort: intstr.FromInt(8888),
   123  			Protocol:   apiv1.ProtocolUDP,
   124  		},
   125  	}
   126  	assert.Equal(t, published.Spec.Ports, expectedPorts)
   127  	assert.Nil(t, randomPorts)
   128  }
   129  
   130  func TestToServiceWithNonPublishedPort(t *testing.T) {
   131  	headless, published, randomPorts := services(t, Stack("demo",
   132  		WithService("nginx",
   133  			Image("any"),
   134  		),
   135  	), loadBalancerServiceStrategy{})
   136  
   137  	expectedHeadless := &apiv1.Service{
   138  		ObjectMeta: metav1.ObjectMeta{
   139  			Name: "nginx",
   140  			Labels: map[string]string{
   141  				"com.docker.stack.namespace": "demo",
   142  				"com.docker.service.name":    "nginx",
   143  				"com.docker.service.id":      "demo-nginx",
   144  			},
   145  		},
   146  		Spec: apiv1.ServiceSpec{
   147  			ClusterIP: apiv1.ClusterIPNone,
   148  			Ports: []apiv1.ServicePort{
   149  				{
   150  					Name:       headlessPortName,
   151  					Port:       headlessPort,
   152  					TargetPort: intstr.FromInt(headlessPort),
   153  					Protocol:   apiv1.ProtocolTCP,
   154  				},
   155  			},
   156  			Selector: map[string]string{
   157  				"com.docker.stack.namespace": "demo",
   158  				"com.docker.service.name":    "nginx",
   159  				"com.docker.service.id":      "demo-nginx",
   160  			},
   161  		},
   162  	}
   163  	assert.Equal(t, headless, expectedHeadless)
   164  
   165  	assert.Nil(t, published)
   166  	assert.Nil(t, randomPorts)
   167  }
   168  
   169  func TestToServiceWithRandomPublishedPort(t *testing.T) {
   170  	headless, published, randomPorts := services(t, Stack("demo",
   171  		WithService("nginx",
   172  			Image("any"),
   173  			WithPort(8888),
   174  		),
   175  	), loadBalancerServiceStrategy{})
   176  	expectedHeadless := &apiv1.Service{
   177  		ObjectMeta: metav1.ObjectMeta{
   178  			Name: "nginx",
   179  			Labels: map[string]string{
   180  				"com.docker.stack.namespace": "demo",
   181  				"com.docker.service.name":    "nginx",
   182  				"com.docker.service.id":      "demo-nginx",
   183  			},
   184  		},
   185  		Spec: apiv1.ServiceSpec{
   186  			ClusterIP: apiv1.ClusterIPNone,
   187  			Ports: []apiv1.ServicePort{
   188  				{
   189  					Name:       headlessPortName,
   190  					Port:       headlessPort,
   191  					TargetPort: intstr.FromInt(headlessPort),
   192  					Protocol:   apiv1.ProtocolTCP,
   193  				},
   194  			},
   195  			Selector: map[string]string{
   196  				"com.docker.stack.namespace": "demo",
   197  				"com.docker.service.name":    "nginx",
   198  				"com.docker.service.id":      "demo-nginx",
   199  			},
   200  		},
   201  	}
   202  	expectedRandomPorts := &apiv1.Service{
   203  		ObjectMeta: metav1.ObjectMeta{
   204  			Name: fmt.Sprintf("nginx%s", publishedOnRandomPortSuffix),
   205  			Labels: map[string]string{
   206  				"com.docker.stack.namespace": "demo",
   207  				"com.docker.service.name":    "nginx",
   208  				"com.docker.service.id":      "demo-nginx",
   209  			},
   210  		},
   211  		Spec: apiv1.ServiceSpec{
   212  			Type: apiv1.ServiceTypeNodePort,
   213  			Ports: []apiv1.ServicePort{
   214  				{
   215  					Name:       "8888-tcp",
   216  					Port:       8888,
   217  					Protocol:   apiv1.ProtocolTCP,
   218  					TargetPort: intstr.FromInt(8888),
   219  				},
   220  			},
   221  			Selector: map[string]string{
   222  				"com.docker.stack.namespace": "demo",
   223  				"com.docker.service.name":    "nginx",
   224  				"com.docker.service.id":      "demo-nginx",
   225  			},
   226  		},
   227  	}
   228  	assert.Equal(t, headless, expectedHeadless)
   229  	assert.Nil(t, published)
   230  	assert.Equal(t, randomPorts, expectedRandomPorts)
   231  }
   232  
   233  func TestToServiceWithoutStack(t *testing.T) {
   234  	headless, published, randomPorts := services(t, Stack("demo",
   235  		WithService("nginx",
   236  			Image("any"),
   237  			WithPort(8080, Published(8080)),
   238  		),
   239  	), loadBalancerServiceStrategy{})
   240  
   241  	assert.NotNil(t, headless)
   242  
   243  	expectedPublished := &apiv1.Service{
   244  		ObjectMeta: metav1.ObjectMeta{
   245  			Name: fmt.Sprintf("nginx%s", publishedServiceSuffix),
   246  			Labels: map[string]string{
   247  				"com.docker.stack.namespace": "demo",
   248  				"com.docker.service.name":    "nginx",
   249  				"com.docker.service.id":      "demo-nginx",
   250  			},
   251  		},
   252  		Spec: apiv1.ServiceSpec{
   253  			Type: apiv1.ServiceTypeLoadBalancer,
   254  			Ports: []apiv1.ServicePort{
   255  				{
   256  					Name:       "8080-tcp",
   257  					Port:       8080,
   258  					Protocol:   apiv1.ProtocolTCP,
   259  					TargetPort: intstr.FromInt(8080),
   260  				},
   261  			},
   262  			Selector: map[string]string{
   263  				"com.docker.stack.namespace": "demo",
   264  				"com.docker.service.name":    "nginx",
   265  				"com.docker.service.id":      "demo-nginx",
   266  			},
   267  		},
   268  	}
   269  	assert.Equal(t, published, expectedPublished)
   270  	assert.Nil(t, randomPorts)
   271  }
   272  
   273  func TestToServiceWithPublishedPortWithNodePorts(t *testing.T) {
   274  	headless, published, randomPorts := services(t, Stack("demo",
   275  		WithService("nginx",
   276  			Image("any"),
   277  			WithPort(8080, Published(80)),
   278  			WithLabel("container.key", "container.value"),
   279  			Deploy(WithDeployLabel("deploy.key", "deploy.value")),
   280  		),
   281  	), nodePortServiceStrategy{})
   282  
   283  	expectedHeadless := &apiv1.Service{
   284  		ObjectMeta: metav1.ObjectMeta{
   285  			Name: "nginx",
   286  			Labels: map[string]string{
   287  				"com.docker.stack.namespace": "demo",
   288  				"com.docker.service.name":    "nginx",
   289  				"com.docker.service.id":      "demo-nginx",
   290  				"deploy.key":                 "deploy.value",
   291  			},
   292  		},
   293  		Spec: apiv1.ServiceSpec{
   294  			ClusterIP: apiv1.ClusterIPNone,
   295  			Ports: []apiv1.ServicePort{
   296  				{
   297  					Name:       headlessPortName,
   298  					Port:       headlessPort,
   299  					Protocol:   apiv1.ProtocolTCP,
   300  					TargetPort: intstr.FromInt(headlessPort),
   301  				},
   302  			},
   303  			Selector: map[string]string{
   304  				"com.docker.stack.namespace": "demo",
   305  				"com.docker.service.name":    "nginx",
   306  				"com.docker.service.id":      "demo-nginx",
   307  			},
   308  		},
   309  	}
   310  	assert.Equal(t, headless, expectedHeadless)
   311  
   312  	expectedPublished := &apiv1.Service{
   313  		ObjectMeta: metav1.ObjectMeta{
   314  			Name: fmt.Sprintf("nginx%s", publishedServiceSuffix),
   315  			Labels: map[string]string{
   316  				"com.docker.stack.namespace": "demo",
   317  				"com.docker.service.name":    "nginx",
   318  				"com.docker.service.id":      "demo-nginx",
   319  				"deploy.key":                 "deploy.value",
   320  			},
   321  		},
   322  		Spec: apiv1.ServiceSpec{
   323  			Type: apiv1.ServiceTypeNodePort,
   324  			Ports: []apiv1.ServicePort{
   325  				{
   326  					Name:       "80-tcp",
   327  					NodePort:   80,
   328  					Port:       8080,
   329  					Protocol:   apiv1.ProtocolTCP,
   330  					TargetPort: intstr.FromInt(8080),
   331  				},
   332  			},
   333  			Selector: map[string]string{
   334  				"com.docker.stack.namespace": "demo",
   335  				"com.docker.service.name":    "nginx",
   336  				"com.docker.service.id":      "demo-nginx",
   337  			},
   338  		},
   339  	}
   340  	assert.Equal(t, published, expectedPublished)
   341  	assert.Nil(t, randomPorts)
   342  }
   343  
   344  func TestToServiceWithLongPortWithNodePorts(t *testing.T) {
   345  	headless, published, randomPorts := services(t, Stack("demo",
   346  		WithService("nginx",
   347  			Image("any"),
   348  			WithPort(8888, Published(80), ProtocolUDP),
   349  		),
   350  	), nodePortServiceStrategy{})
   351  
   352  	assert.NotNil(t, headless)
   353  
   354  	expectedPorts := []apiv1.ServicePort{
   355  		{
   356  			Name:       "80-udp",
   357  			NodePort:   80,
   358  			Port:       8888,
   359  			TargetPort: intstr.FromInt(8888),
   360  			Protocol:   apiv1.ProtocolUDP,
   361  		},
   362  	}
   363  	assert.Equal(t, published.Spec.Ports, expectedPorts)
   364  	assert.Nil(t, randomPorts)
   365  }
   366  
   367  func TestToServiceWithNonPublishedPortWithNodePorts(t *testing.T) {
   368  	headless, published, randomPorts := services(t, Stack("demo",
   369  		WithService("nginx",
   370  			Image("any"),
   371  		),
   372  	), nodePortServiceStrategy{})
   373  
   374  	expectedHeadless := &apiv1.Service{
   375  		ObjectMeta: metav1.ObjectMeta{
   376  			Name: "nginx",
   377  			Labels: map[string]string{
   378  				"com.docker.stack.namespace": "demo",
   379  				"com.docker.service.name":    "nginx",
   380  				"com.docker.service.id":      "demo-nginx",
   381  			},
   382  		},
   383  		Spec: apiv1.ServiceSpec{
   384  			ClusterIP: apiv1.ClusterIPNone,
   385  			Ports: []apiv1.ServicePort{
   386  				{
   387  					Name:       headlessPortName,
   388  					Port:       headlessPort,
   389  					TargetPort: intstr.FromInt(headlessPort),
   390  					Protocol:   apiv1.ProtocolTCP,
   391  				},
   392  			},
   393  			Selector: map[string]string{
   394  				"com.docker.stack.namespace": "demo",
   395  				"com.docker.service.name":    "nginx",
   396  				"com.docker.service.id":      "demo-nginx",
   397  			},
   398  		},
   399  	}
   400  	assert.Equal(t, headless, expectedHeadless)
   401  
   402  	assert.Nil(t, published)
   403  	assert.Nil(t, randomPorts)
   404  }
   405  
   406  func TestToServiceWithRandomPublishedPortWithNodePorts(t *testing.T) {
   407  	headless, published, randomPorts := services(t, Stack("demo",
   408  		WithService("nginx",
   409  			Image("any"),
   410  			WithPort(8888),
   411  		),
   412  	), nodePortServiceStrategy{})
   413  	expectedHeadless := &apiv1.Service{
   414  		ObjectMeta: metav1.ObjectMeta{
   415  			Name: "nginx",
   416  			Labels: map[string]string{
   417  				"com.docker.stack.namespace": "demo",
   418  				"com.docker.service.name":    "nginx",
   419  				"com.docker.service.id":      "demo-nginx",
   420  			},
   421  		},
   422  		Spec: apiv1.ServiceSpec{
   423  			ClusterIP: apiv1.ClusterIPNone,
   424  			Ports: []apiv1.ServicePort{
   425  				{
   426  					Name:       headlessPortName,
   427  					Port:       headlessPort,
   428  					TargetPort: intstr.FromInt(headlessPort),
   429  					Protocol:   apiv1.ProtocolTCP,
   430  				},
   431  			},
   432  			Selector: map[string]string{
   433  				"com.docker.stack.namespace": "demo",
   434  				"com.docker.service.name":    "nginx",
   435  				"com.docker.service.id":      "demo-nginx",
   436  			},
   437  		},
   438  	}
   439  	expectedRandomPorts := &apiv1.Service{
   440  		ObjectMeta: metav1.ObjectMeta{
   441  			Name: fmt.Sprintf("nginx%s", publishedOnRandomPortSuffix),
   442  			Labels: map[string]string{
   443  				"com.docker.stack.namespace": "demo",
   444  				"com.docker.service.name":    "nginx",
   445  				"com.docker.service.id":      "demo-nginx",
   446  			},
   447  		},
   448  		Spec: apiv1.ServiceSpec{
   449  			Type: apiv1.ServiceTypeNodePort,
   450  			Ports: []apiv1.ServicePort{
   451  				{
   452  					Name:       "8888-tcp",
   453  					Protocol:   apiv1.ProtocolTCP,
   454  					Port:       8888,
   455  					TargetPort: intstr.FromInt(8888),
   456  				},
   457  			},
   458  			Selector: map[string]string{
   459  				"com.docker.stack.namespace": "demo",
   460  				"com.docker.service.name":    "nginx",
   461  				"com.docker.service.id":      "demo-nginx",
   462  			},
   463  		},
   464  	}
   465  	assert.Equal(t, headless, expectedHeadless)
   466  	assert.Nil(t, published)
   467  	assert.Equal(t, randomPorts, expectedRandomPorts)
   468  }
   469  
   470  func TestToServiceWithoutStackWithNodePorts(t *testing.T) {
   471  	headless, published, randomPorts := services(t, Stack("demo",
   472  		WithService("nginx",
   473  			Image("any"),
   474  			WithPort(8080, Published(8080)),
   475  		),
   476  	), nodePortServiceStrategy{})
   477  
   478  	assert.NotNil(t, headless)
   479  
   480  	expectedPublished := &apiv1.Service{
   481  		ObjectMeta: metav1.ObjectMeta{
   482  			Name: fmt.Sprintf("nginx%s", publishedServiceSuffix),
   483  			Labels: map[string]string{
   484  				"com.docker.stack.namespace": "demo",
   485  				"com.docker.service.name":    "nginx",
   486  				"com.docker.service.id":      "demo-nginx",
   487  			},
   488  		},
   489  		Spec: apiv1.ServiceSpec{
   490  			Type: apiv1.ServiceTypeNodePort,
   491  			Ports: []apiv1.ServicePort{
   492  				{
   493  					Name:       "8080-tcp",
   494  					NodePort:   8080,
   495  					Port:       8080,
   496  					Protocol:   apiv1.ProtocolTCP,
   497  					TargetPort: intstr.FromInt(8080),
   498  				},
   499  			},
   500  			Selector: map[string]string{
   501  				"com.docker.stack.namespace": "demo",
   502  				"com.docker.service.name":    "nginx",
   503  				"com.docker.service.id":      "demo-nginx",
   504  			},
   505  		},
   506  	}
   507  	assert.Equal(t, published, expectedPublished)
   508  	assert.Nil(t, randomPorts)
   509  }
   510  
   511  func TestServiceStrategyFor(t *testing.T) {
   512  	cases := []apiv1.ServiceType{
   513  		apiv1.ServiceTypeLoadBalancer,
   514  		apiv1.ServiceTypeNodePort,
   515  	}
   516  	for _, c := range cases {
   517  		s, err := ServiceStrategyFor(c)
   518  		assert.NoError(t, err)
   519  		assert.Equal(t, c, s.publishedServiceType())
   520  	}
   521  }
   522  
   523  func TestInternalServiceStrategy(t *testing.T) {
   524  	cases := []struct {
   525  		name              string
   526  		desiredType       latest.InternalServiceType
   527  		ports             []latest.InternalPort
   528  		expectedClusterIP string
   529  		original          apiv1.Service
   530  	}{
   531  		{
   532  			name:              "auto-no-ports",
   533  			expectedClusterIP: apiv1.ClusterIPNone,
   534  		},
   535  		{
   536  			name: "auto-some-ports",
   537  			ports: []latest.InternalPort{
   538  				{
   539  					Port:     8080,
   540  					Protocol: apiv1.ProtocolUDP,
   541  				},
   542  			},
   543  		},
   544  		{
   545  			name: "headless-some-ports",
   546  			ports: []latest.InternalPort{
   547  				{
   548  					Port:     8080,
   549  					Protocol: apiv1.ProtocolUDP,
   550  				},
   551  			},
   552  			desiredType:       latest.InternalServiceTypeHeadless,
   553  			expectedClusterIP: apiv1.ClusterIPNone,
   554  		},
   555  		{
   556  			name:        "clusterip-no-ports",
   557  			desiredType: latest.InternalServiceTypeClusterIP,
   558  		},
   559  
   560  		{
   561  			name:              "auto-no-ports-existing-with-ip",
   562  			expectedClusterIP: apiv1.ClusterIPNone,
   563  			original: apiv1.Service{
   564  				Spec: apiv1.ServiceSpec{
   565  					ClusterIP: "1.1.1.1",
   566  				},
   567  			},
   568  		},
   569  		{
   570  			name: "auto-some-ports-with-ip",
   571  			ports: []latest.InternalPort{
   572  				{
   573  					Port:     8080,
   574  					Protocol: apiv1.ProtocolUDP,
   575  				},
   576  			},
   577  			original: apiv1.Service{
   578  				Spec: apiv1.ServiceSpec{
   579  					ClusterIP: "1.1.1.1",
   580  				},
   581  			},
   582  			expectedClusterIP: "1.1.1.1",
   583  		},
   584  		{
   585  			name: "auto-some-ports-no-ip",
   586  			ports: []latest.InternalPort{
   587  				{
   588  					Port:     8080,
   589  					Protocol: apiv1.ProtocolUDP,
   590  				},
   591  			},
   592  			original: apiv1.Service{
   593  				Spec: apiv1.ServiceSpec{
   594  					ClusterIP: apiv1.ClusterIPNone,
   595  				},
   596  			},
   597  			expectedClusterIP: "",
   598  		},
   599  	}
   600  
   601  	for _, c := range cases {
   602  		t.Run(c.name, func(t *testing.T) {
   603  			svc := toInternalService(metav1.ObjectMeta{}, nil, c.original, c.ports, c.desiredType)
   604  			assert.Equal(t, c.expectedClusterIP, svc.Spec.ClusterIP)
   605  		})
   606  	}
   607  }