github.com/itscaro/cli@v0.0.0-20190705081621-c9db0fe93829/cli/command/stack/kubernetes/convert_test.go (about)

     1  package kubernetes
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"path/filepath"
     7  	"testing"
     8  
     9  	"github.com/docker/cli/cli/compose/loader"
    10  	composetypes "github.com/docker/cli/cli/compose/types"
    11  	"github.com/docker/compose-on-kubernetes/api/compose/v1alpha3"
    12  	"github.com/docker/compose-on-kubernetes/api/compose/v1beta1"
    13  	"github.com/docker/compose-on-kubernetes/api/compose/v1beta2"
    14  	"gotest.tools/assert"
    15  	is "gotest.tools/assert/cmp"
    16  	v1 "k8s.io/api/core/v1"
    17  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    18  )
    19  
    20  func TestNewStackConverter(t *testing.T) {
    21  	_, err := NewStackConverter("v1alpha1")
    22  	assert.Check(t, is.ErrorContains(err, "stack version v1alpha1 unsupported"))
    23  
    24  	_, err = NewStackConverter("v1beta1")
    25  	assert.NilError(t, err)
    26  	_, err = NewStackConverter("v1beta2")
    27  	assert.NilError(t, err)
    28  	_, err = NewStackConverter("v1alpha3")
    29  	assert.NilError(t, err)
    30  }
    31  
    32  func TestConvertFromToV1beta1(t *testing.T) {
    33  	composefile := `version: "3.3"
    34  services: 
    35    test:
    36      image: nginx
    37  secrets:
    38    test:
    39      file: testdata/secret
    40  configs:
    41    test:
    42      file: testdata/config
    43  `
    44  	stackv1beta1 := &v1beta1.Stack{
    45  		ObjectMeta: metav1.ObjectMeta{
    46  			Name: "test",
    47  		},
    48  		Spec: v1beta1.StackSpec{
    49  			ComposeFile: composefile,
    50  		},
    51  	}
    52  
    53  	result, err := stackFromV1beta1(stackv1beta1)
    54  	assert.NilError(t, err)
    55  	expected := Stack{
    56  		Name:        "test",
    57  		ComposeFile: composefile,
    58  		Spec: &v1alpha3.StackSpec{
    59  			Services: []v1alpha3.ServiceConfig{
    60  				{
    61  					Name:        "test",
    62  					Image:       "nginx",
    63  					Environment: make(map[string]*string),
    64  				},
    65  			},
    66  			Secrets: map[string]v1alpha3.SecretConfig{
    67  				"test": {File: filepath.FromSlash("testdata/secret")},
    68  			},
    69  			Configs: map[string]v1alpha3.ConfigObjConfig{
    70  				"test": {File: filepath.FromSlash("testdata/config")},
    71  			},
    72  		},
    73  	}
    74  	assert.DeepEqual(t, expected, result)
    75  	assert.DeepEqual(t, stackv1beta1, stackToV1beta1(result))
    76  }
    77  
    78  func TestConvertFromToV1beta2(t *testing.T) {
    79  	stackv1beta2 := &v1beta2.Stack{
    80  		ObjectMeta: metav1.ObjectMeta{
    81  			Name: "test",
    82  		},
    83  		Spec: &v1beta2.StackSpec{
    84  			Services: []v1beta2.ServiceConfig{
    85  				{
    86  					Name:        "test",
    87  					Image:       "nginx",
    88  					Environment: make(map[string]*string),
    89  				},
    90  			},
    91  			Secrets: map[string]v1beta2.SecretConfig{
    92  				"test": {File: filepath.FromSlash("testdata/secret")},
    93  			},
    94  			Configs: map[string]v1beta2.ConfigObjConfig{
    95  				"test": {File: filepath.FromSlash("testdata/config")},
    96  			},
    97  		},
    98  	}
    99  	expected := Stack{
   100  		Name: "test",
   101  		Spec: &v1alpha3.StackSpec{
   102  			Services: []v1alpha3.ServiceConfig{
   103  				{
   104  					Name:        "test",
   105  					Image:       "nginx",
   106  					Environment: make(map[string]*string),
   107  				},
   108  			},
   109  			Secrets: map[string]v1alpha3.SecretConfig{
   110  				"test": {File: filepath.FromSlash("testdata/secret")},
   111  			},
   112  			Configs: map[string]v1alpha3.ConfigObjConfig{
   113  				"test": {File: filepath.FromSlash("testdata/config")},
   114  			},
   115  		},
   116  	}
   117  	result, err := stackFromV1beta2(stackv1beta2)
   118  	assert.NilError(t, err)
   119  	assert.DeepEqual(t, expected, result)
   120  	gotBack, err := stackToV1beta2(result)
   121  	assert.NilError(t, err)
   122  	assert.DeepEqual(t, stackv1beta2, gotBack)
   123  }
   124  
   125  func TestConvertFromToV1alpha3(t *testing.T) {
   126  	stackv1alpha3 := &v1alpha3.Stack{
   127  		ObjectMeta: metav1.ObjectMeta{
   128  			Name: "test",
   129  		},
   130  		Spec: &v1alpha3.StackSpec{
   131  			Services: []v1alpha3.ServiceConfig{
   132  				{
   133  					Name:        "test",
   134  					Image:       "nginx",
   135  					Environment: make(map[string]*string),
   136  				},
   137  			},
   138  			Secrets: map[string]v1alpha3.SecretConfig{
   139  				"test": {File: filepath.FromSlash("testdata/secret")},
   140  			},
   141  			Configs: map[string]v1alpha3.ConfigObjConfig{
   142  				"test": {File: filepath.FromSlash("testdata/config")},
   143  			},
   144  		},
   145  	}
   146  	expected := Stack{
   147  		Name: "test",
   148  		Spec: &v1alpha3.StackSpec{
   149  			Services: []v1alpha3.ServiceConfig{
   150  				{
   151  					Name:        "test",
   152  					Image:       "nginx",
   153  					Environment: make(map[string]*string),
   154  				},
   155  			},
   156  			Secrets: map[string]v1alpha3.SecretConfig{
   157  				"test": {File: filepath.FromSlash("testdata/secret")},
   158  			},
   159  			Configs: map[string]v1alpha3.ConfigObjConfig{
   160  				"test": {File: filepath.FromSlash("testdata/config")},
   161  			},
   162  		},
   163  	}
   164  	result := stackFromV1alpha3(stackv1alpha3)
   165  	assert.DeepEqual(t, expected, result)
   166  	gotBack := stackToV1alpha3(result)
   167  	assert.DeepEqual(t, stackv1alpha3, gotBack)
   168  }
   169  
   170  func loadTestStackWith(t *testing.T, with string) *composetypes.Config {
   171  	t.Helper()
   172  	filePath := fmt.Sprintf("testdata/compose-with-%s.yml", with)
   173  	data, err := ioutil.ReadFile(filePath)
   174  	assert.NilError(t, err)
   175  	yamlData, err := loader.ParseYAML(data)
   176  	assert.NilError(t, err)
   177  	cfg, err := loader.Load(composetypes.ConfigDetails{
   178  		ConfigFiles: []composetypes.ConfigFile{
   179  			{Config: yamlData, Filename: filePath},
   180  		},
   181  	})
   182  	assert.NilError(t, err)
   183  	return cfg
   184  }
   185  
   186  func TestHandlePullSecret(t *testing.T) {
   187  	testData := loadTestStackWith(t, "pull-secret")
   188  	cases := []struct {
   189  		version string
   190  		err     string
   191  	}{
   192  		{version: "v1beta1", err: `stack API version v1beta1 does not support pull secrets (field "x-kubernetes.pull_secret"), please use version v1alpha3 or higher`},
   193  		{version: "v1beta2", err: `stack API version v1beta2 does not support pull secrets (field "x-kubernetes.pull_secret"), please use version v1alpha3 or higher`},
   194  		{version: "v1alpha3"},
   195  	}
   196  
   197  	for _, c := range cases {
   198  		t.Run(c.version, func(t *testing.T) {
   199  			conv, err := NewStackConverter(c.version)
   200  			assert.NilError(t, err)
   201  			s, err := conv.FromCompose(ioutil.Discard, "test", testData)
   202  			if c.err != "" {
   203  				assert.Error(t, err, c.err)
   204  
   205  			} else {
   206  				assert.NilError(t, err)
   207  				assert.Equal(t, s.Spec.Services[0].PullSecret, "some-secret")
   208  			}
   209  		})
   210  	}
   211  }
   212  
   213  func TestHandlePullPolicy(t *testing.T) {
   214  	testData := loadTestStackWith(t, "pull-policy")
   215  	cases := []struct {
   216  		version string
   217  		err     string
   218  	}{
   219  		{version: "v1beta1", err: `stack API version v1beta1 does not support pull policies (field "x-kubernetes.pull_policy"), please use version v1alpha3 or higher`},
   220  		{version: "v1beta2", err: `stack API version v1beta2 does not support pull policies (field "x-kubernetes.pull_policy"), please use version v1alpha3 or higher`},
   221  		{version: "v1alpha3"},
   222  	}
   223  
   224  	for _, c := range cases {
   225  		t.Run(c.version, func(t *testing.T) {
   226  			conv, err := NewStackConverter(c.version)
   227  			assert.NilError(t, err)
   228  			s, err := conv.FromCompose(ioutil.Discard, "test", testData)
   229  			if c.err != "" {
   230  				assert.Error(t, err, c.err)
   231  
   232  			} else {
   233  				assert.NilError(t, err)
   234  				assert.Equal(t, s.Spec.Services[0].PullPolicy, "Never")
   235  			}
   236  		})
   237  	}
   238  }
   239  
   240  func TestHandleInternalServiceType(t *testing.T) {
   241  	cases := []struct {
   242  		name     string
   243  		value    string
   244  		caps     composeCapabilities
   245  		err      string
   246  		expected v1alpha3.InternalServiceType
   247  	}{
   248  		{
   249  			name:  "v1beta1",
   250  			value: "ClusterIP",
   251  			caps:  v1beta1Capabilities,
   252  			err:   `stack API version v1beta1 does not support intra-stack load balancing (field "x-kubernetes.internal_service_type"), please use version v1alpha3 or higher`,
   253  		},
   254  		{
   255  			name:  "v1beta2",
   256  			value: "ClusterIP",
   257  			caps:  v1beta2Capabilities,
   258  			err:   `stack API version v1beta2 does not support intra-stack load balancing (field "x-kubernetes.internal_service_type"), please use version v1alpha3 or higher`,
   259  		},
   260  		{
   261  			name:     "v1alpha3",
   262  			value:    "ClusterIP",
   263  			caps:     v1alpha3Capabilities,
   264  			expected: v1alpha3.InternalServiceTypeClusterIP,
   265  		},
   266  		{
   267  			name:  "v1alpha3-invalid",
   268  			value: "invalid",
   269  			caps:  v1alpha3Capabilities,
   270  			err:   `invalid value "invalid" for field "x-kubernetes.internal_service_type", valid values are "ClusterIP" or "Headless"`,
   271  		},
   272  	}
   273  	for _, c := range cases {
   274  		t.Run(c.name, func(t *testing.T) {
   275  			res, err := fromComposeServiceConfig(composetypes.ServiceConfig{
   276  				Name:  "test",
   277  				Image: "test",
   278  				Extras: map[string]interface{}{
   279  					"x-kubernetes": map[string]interface{}{
   280  						"internal_service_type": c.value,
   281  					},
   282  				},
   283  			}, c.caps)
   284  			if c.err == "" {
   285  				assert.NilError(t, err)
   286  				assert.Equal(t, res.InternalServiceType, c.expected)
   287  			} else {
   288  				assert.ErrorContains(t, err, c.err)
   289  			}
   290  		})
   291  	}
   292  }
   293  
   294  func TestIgnoreExpose(t *testing.T) {
   295  	testData := loadTestStackWith(t, "expose")
   296  	for _, version := range []string{"v1beta1", "v1beta2"} {
   297  		conv, err := NewStackConverter(version)
   298  		assert.NilError(t, err)
   299  		s, err := conv.FromCompose(ioutil.Discard, "test", testData)
   300  		assert.NilError(t, err)
   301  		assert.Equal(t, len(s.Spec.Services[0].InternalPorts), 0)
   302  	}
   303  }
   304  
   305  func TestParseExpose(t *testing.T) {
   306  	testData := loadTestStackWith(t, "expose")
   307  	conv, err := NewStackConverter("v1alpha3")
   308  	assert.NilError(t, err)
   309  	s, err := conv.FromCompose(ioutil.Discard, "test", testData)
   310  	assert.NilError(t, err)
   311  	expected := []v1alpha3.InternalPort{
   312  		{
   313  			Port:     1,
   314  			Protocol: v1.ProtocolTCP,
   315  		},
   316  		{
   317  			Port:     2,
   318  			Protocol: v1.ProtocolTCP,
   319  		},
   320  		{
   321  			Port:     3,
   322  			Protocol: v1.ProtocolTCP,
   323  		},
   324  		{
   325  			Port:     4,
   326  			Protocol: v1.ProtocolTCP,
   327  		},
   328  		{
   329  			Port:     5,
   330  			Protocol: v1.ProtocolUDP,
   331  		},
   332  		{
   333  			Port:     6,
   334  			Protocol: v1.ProtocolUDP,
   335  		},
   336  		{
   337  			Port:     7,
   338  			Protocol: v1.ProtocolUDP,
   339  		},
   340  		{
   341  			Port:     8,
   342  			Protocol: v1.ProtocolUDP,
   343  		},
   344  	}
   345  	assert.DeepEqual(t, s.Spec.Services[0].InternalPorts, expected)
   346  }