github.com/ali-iotechsys/cli@v20.10.0+incompatible/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/v3/assert"
    15  	is "gotest.tools/v3/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  		c := c
   199  		t.Run(c.version, func(t *testing.T) {
   200  			conv, err := NewStackConverter(c.version)
   201  			assert.NilError(t, err)
   202  			s, err := conv.FromCompose(ioutil.Discard, "test", testData)
   203  			if c.err != "" {
   204  				assert.Error(t, err, c.err)
   205  
   206  			} else {
   207  				assert.NilError(t, err)
   208  				assert.Equal(t, s.Spec.Services[0].PullSecret, "some-secret")
   209  			}
   210  		})
   211  	}
   212  }
   213  
   214  func TestHandlePullPolicy(t *testing.T) {
   215  	testData := loadTestStackWith(t, "pull-policy")
   216  	cases := []struct {
   217  		version string
   218  		err     string
   219  	}{
   220  		{version: "v1beta1", err: `stack API version v1beta1 does not support pull policies (field "x-kubernetes.pull_policy"), please use version v1alpha3 or higher`},
   221  		{version: "v1beta2", err: `stack API version v1beta2 does not support pull policies (field "x-kubernetes.pull_policy"), please use version v1alpha3 or higher`},
   222  		{version: "v1alpha3"},
   223  	}
   224  
   225  	for _, c := range cases {
   226  		c := c
   227  		t.Run(c.version, func(t *testing.T) {
   228  			conv, err := NewStackConverter(c.version)
   229  			assert.NilError(t, err)
   230  			s, err := conv.FromCompose(ioutil.Discard, "test", testData)
   231  			if c.err != "" {
   232  				assert.Error(t, err, c.err)
   233  
   234  			} else {
   235  				assert.NilError(t, err)
   236  				assert.Equal(t, s.Spec.Services[0].PullPolicy, "Never")
   237  			}
   238  		})
   239  	}
   240  }
   241  
   242  func TestHandleInternalServiceType(t *testing.T) {
   243  	cases := []struct {
   244  		name     string
   245  		value    string
   246  		caps     composeCapabilities
   247  		err      string
   248  		expected v1alpha3.InternalServiceType
   249  	}{
   250  		{
   251  			name:  "v1beta1",
   252  			value: "ClusterIP",
   253  			caps:  v1beta1Capabilities,
   254  			err:   `stack API version v1beta1 does not support intra-stack load balancing (field "x-kubernetes.internal_service_type"), please use version v1alpha3 or higher`,
   255  		},
   256  		{
   257  			name:  "v1beta2",
   258  			value: "ClusterIP",
   259  			caps:  v1beta2Capabilities,
   260  			err:   `stack API version v1beta2 does not support intra-stack load balancing (field "x-kubernetes.internal_service_type"), please use version v1alpha3 or higher`,
   261  		},
   262  		{
   263  			name:     "v1alpha3",
   264  			value:    "ClusterIP",
   265  			caps:     v1alpha3Capabilities,
   266  			expected: v1alpha3.InternalServiceTypeClusterIP,
   267  		},
   268  		{
   269  			name:  "v1alpha3-invalid",
   270  			value: "invalid",
   271  			caps:  v1alpha3Capabilities,
   272  			err:   `invalid value "invalid" for field "x-kubernetes.internal_service_type", valid values are "ClusterIP" or "Headless"`,
   273  		},
   274  	}
   275  	for _, c := range cases {
   276  		c := c
   277  		t.Run(c.name, func(t *testing.T) {
   278  			res, err := fromComposeServiceConfig(composetypes.ServiceConfig{
   279  				Name:  "test",
   280  				Image: "test",
   281  				Extras: map[string]interface{}{
   282  					"x-kubernetes": map[string]interface{}{
   283  						"internal_service_type": c.value,
   284  					},
   285  				},
   286  			}, c.caps)
   287  			if c.err == "" {
   288  				assert.NilError(t, err)
   289  				assert.Equal(t, res.InternalServiceType, c.expected)
   290  			} else {
   291  				assert.ErrorContains(t, err, c.err)
   292  			}
   293  		})
   294  	}
   295  }
   296  
   297  func TestIgnoreExpose(t *testing.T) {
   298  	testData := loadTestStackWith(t, "expose")
   299  	for _, version := range []string{"v1beta1", "v1beta2"} {
   300  		conv, err := NewStackConverter(version)
   301  		assert.NilError(t, err)
   302  		s, err := conv.FromCompose(ioutil.Discard, "test", testData)
   303  		assert.NilError(t, err)
   304  		assert.Equal(t, len(s.Spec.Services[0].InternalPorts), 0)
   305  	}
   306  }
   307  
   308  func TestParseExpose(t *testing.T) {
   309  	testData := loadTestStackWith(t, "expose")
   310  	conv, err := NewStackConverter("v1alpha3")
   311  	assert.NilError(t, err)
   312  	s, err := conv.FromCompose(ioutil.Discard, "test", testData)
   313  	assert.NilError(t, err)
   314  	expected := []v1alpha3.InternalPort{
   315  		{
   316  			Port:     1,
   317  			Protocol: v1.ProtocolTCP,
   318  		},
   319  		{
   320  			Port:     2,
   321  			Protocol: v1.ProtocolTCP,
   322  		},
   323  		{
   324  			Port:     3,
   325  			Protocol: v1.ProtocolTCP,
   326  		},
   327  		{
   328  			Port:     4,
   329  			Protocol: v1.ProtocolTCP,
   330  		},
   331  		{
   332  			Port:     5,
   333  			Protocol: v1.ProtocolUDP,
   334  		},
   335  		{
   336  			Port:     6,
   337  			Protocol: v1.ProtocolUDP,
   338  		},
   339  		{
   340  			Port:     7,
   341  			Protocol: v1.ProtocolUDP,
   342  		},
   343  		{
   344  			Port:     8,
   345  			Protocol: v1.ProtocolUDP,
   346  		},
   347  	}
   348  	assert.DeepEqual(t, s.Spec.Services[0].InternalPorts, expected)
   349  }