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 }