// Copyright 2019 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 package commands_test import ( "bytes" "io/ioutil" "os" "path/filepath" "strings" "testing" "github.com/stretchr/testify/assert" "sigs.k8s.io/kustomize/cmd/config/internal/commands" "sigs.k8s.io/kustomize/kyaml/openapi" ) func TestListSettersCommand(t *testing.T) { var tests = []struct { name string openapi string input string args []string expected string }{ { name: "list-replicas", openapi: ` openAPI: definitions: io.k8s.cli.setters.replicas: x-k8s-cli: setter: name: replicas value: "3" setBy: me required: true description: "hello world" `, input: ` apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"} `, expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET replicas 3 me hello world 1 Yes No `, }, { name: "list-replicas inconsistent with openapi", openapi: ` openAPI: definitions: io.k8s.cli.setters.replicas: x-k8s-cli: setter: name: replicas value: "4" setBy: me description: "hello world" `, input: ` apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"} `, expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET replicas 4 me hello world 1 No No `, }, { name: "list-multiple", args: []string{"--include-subst"}, openapi: ` openAPI: definitions: io.k8s.cli.setters.replicas: description: "hello world 1" x-k8s-cli: setter: name: replicas value: "3" setBy: me1 io.k8s.cli.setters.image: description: "hello world 2" x-k8s-cli: setter: name: image value: "nginx" setBy: me2 io.k8s.cli.setters.tag: description: "hello world 3" x-k8s-cli: setter: name: tag value: "1.7.9" setBy: me3 required: true isSet: false io.k8s.cli.substitutions.image: x-k8s-cli: substitution: name: image pattern: IMAGE:TAG values: - marker: IMAGE ref: '#/definitions/io.k8s.cli.setters.image' - marker: TAG ref: '#/definitions/io.k8s.cli.setters.tag' `, input: ` apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"} template: spec: containers: - name: nginx image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"} - name: nginx2 image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"} `, expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET image nginx me2 hello world 2 2 No No replicas 3 me1 hello world 1 1 No No tag 1.7.9 me3 hello world 3 1 Yes No --------------- ----------- -------------- SUBSTITUTION PATTERN REFERENCES image IMAGE:TAG [image,tag] `, }, { name: "list-multiple-resources", args: []string{"--include-subst"}, openapi: ` openAPI: definitions: io.k8s.cli.setters.replicas: description: "hello world 1" x-k8s-cli: setter: name: replicas value: "3" setBy: me1 io.k8s.cli.setters.image: description: "hello world 2" x-k8s-cli: setter: name: image value: "nginx" setBy: me2 io.k8s.cli.setters.tag: description: "hello world 3" x-k8s-cli: setter: name: tag value: "1.7.9" setBy: me3 io.k8s.cli.substitutions.image: x-k8s-cli: substitution: name: image pattern: IMAGE:TAG values: - marker: IMAGE ref: '#/definitions/io.k8s.cli.setters.image' - marker: TAG ref: '#/definitions/io.k8s.cli.setters.tag' `, input: ` apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment-1 spec: replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"} template: spec: containers: - name: nginx image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"} - name: nginx2 image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"} --- apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment-2 spec: replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"} template: spec: containers: - name: nginx image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"} - name: nginx2 image: nginx `, expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET image nginx me2 hello world 2 3 No No replicas 3 me1 hello world 1 2 No No tag 1.7.9 me3 hello world 3 2 No No --------------- ----------- -------------- SUBSTITUTION PATTERN REFERENCES image IMAGE:TAG [image,tag] `, }, { name: "list-name", args: []string{"image"}, openapi: ` openAPI: definitions: io.k8s.cli.setters.replicas: description: "hello world 1" x-k8s-cli: setter: name: replicas value: "3" setBy: me1 io.k8s.cli.setters.image: description: "hello world 2" x-k8s-cli: setter: name: image value: "nginx" setBy: me2 required: true io.k8s.cli.setters.tag: description: "hello world 3" x-k8s-cli: setter: name: tag value: "1.7.9" setBy: me3 io.k8s.cli.substitutions.image-subst: x-k8s-cli: substitution: name: image-subst pattern: IMAGE:TAG values: - marker: IMAGE ref: '#/definitions/io.k8s.cli.setters.image' - marker: TAG ref: '#/definitions/io.k8s.cli.setters.tag' `, input: ` apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment-1 spec: replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"} template: spec: containers: - name: nginx image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-subst"} - name: nginx2 image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"} --- apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment-2 spec: replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"} template: spec: containers: - name: nginx image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-subst"} - name: nginx2 image: nginx `, expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET image nginx me2 hello world 2 3 Yes No `, }, { name: "list array setter", openapi: ` apiVersion: v1alpha1 kind: Example openAPI: definitions: io.k8s.cli.setters.list: items: type: string maxItems: 3 type: array description: hello world x-k8s-cli: setter: name: list listValues: - a - b - c setBy: me required: true `, input: ` apiVersion: example.com/v1beta1 kind: Example metadata: annotations: foo: bar spec: list: # {"$openapi":"list"} - "a" - "b" - "c" `, expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET list [a,b,c] me hello world 1 Yes No `, }, { name: "nested substitution", args: []string{"--include-subst"}, input: ` apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 template: spec: containers: - name: nginx image: something/nginx::1.7.9/nginxotherthing # {"$openapi":"my-nested-subst"} - name: sidecar image: nginx::1.7.9 # {"$openapi":"my-image-subst"} `, openapi: ` apiVersion: v1alpha1 kind: Example openAPI: definitions: io.k8s.cli.setters.my-image-setter: x-k8s-cli: setter: name: my-image-setter value: nginx io.k8s.cli.setters.my-tag-setter: x-k8s-cli: setter: name: my-tag-setter value: 1.7.9 required: true isSet: true io.k8s.cli.substitutions.my-image-subst: x-k8s-cli: substitution: name: my-image-subst pattern: ${my-image-setter}::${my-tag-setter} values: - marker: ${my-image-setter} ref: '#/definitions/io.k8s.cli.setters.my-image-setter' - marker: ${my-tag-setter} ref: '#/definitions/io.k8s.cli.setters.my-tag-setter' io.k8s.cli.substitutions.my-nested-subst: x-k8s-cli: substitution: name: my-nested-subst pattern: something/${my-image-subst}/${my-other-setter} values: - marker: ${my-image-subst} ref: '#/definitions/io.k8s.cli.substitutions.my-image-subst' - marker: ${my-other-setter} ref: '#/definitions/io.k8s.cli.setters.my-other-setter' io.k8s.cli.setters.my-other-setter: x-k8s-cli: setter: name: my-other-setter value: nginxotherthing `, expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET my-image-setter nginx 2 No No my-other-setter nginxotherthing 1 No No my-tag-setter 1.7.9 2 Yes Yes ------------------ ------------------------------------------------ ----------------------------------- SUBSTITUTION PATTERN REFERENCES my-image-subst ${my-image-setter}::${my-tag-setter} [my-image-setter,my-tag-setter] my-nested-subst something/${my-image-subst}/${my-other-setter} [my-image-subst,my-other-setter] `, }, } for i := range tests { test := tests[i] t.Run(test.name, func(t *testing.T) { // reset the openAPI afterward openapi.ResetOpenAPI() defer openapi.ResetOpenAPI() dir, err := ioutil.TempDir("", "") if !assert.NoError(t, err) { t.FailNow() } defer os.RemoveAll(dir) err = ioutil.WriteFile(filepath.Join(dir, "Krmfile"), []byte(test.openapi), 0600) if !assert.NoError(t, err) { t.FailNow() } err = ioutil.WriteFile(filepath.Join(dir, "deployment.yaml"), []byte(test.input), 0600) if !assert.NoError(t, err) { t.FailNow() } runner := commands.NewListSettersRunner("") actual := &bytes.Buffer{} runner.Command.SetOut(actual) runner.Command.SetArgs(append([]string{dir}, test.args...)) err = runner.Command.Execute() if !assert.NoError(t, err) { t.FailNow() } if !assert.Contains(t, actual.String(), test.expected) { t.FailNow() } // make sure that the resources are not altered actualResources, err := ioutil.ReadFile(filepath.Join(dir, "deployment.yaml")) if !assert.NoError(t, err) { t.FailNow() } if !assert.Equal(t, strings.TrimSpace(test.input), strings.TrimSpace(string(actualResources))) { t.FailNow() } actualOpenAPI, err := ioutil.ReadFile(filepath.Join(dir, "Krmfile")) if !assert.NoError(t, err) { t.FailNow() } if !assert.Equal(t, strings.TrimSpace(test.openapi), strings.TrimSpace(string(actualOpenAPI))) { t.FailNow() } }) } } func TestListSettersSubPackages(t *testing.T) { var tests = []struct { name string dataset string args []string expected string }{ { name: "list-replicas", dataset: "dataset-with-setters", args: []string{"--include-subst", "-R"}, expected: ` test/testdata/dataset-with-setters/mysql/ NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET image mysql 1 No No namespace myspace 1 No No tag 1.7.9 1 No No --------------- ----------------- -------------- SUBSTITUTION PATTERN REFERENCES image-tag ${image}:${tag} [image,tag] test/testdata/dataset-with-setters/mysql/nosetters/ NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET test/testdata/dataset-with-setters/mysql/storage/ NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET namespace myspace 1 No No `, }, { name: "list-replicas", dataset: "dataset-with-setters/mysql", expected: ` test/testdata/dataset-with-setters/mysql/ NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET image mysql 1 No No namespace myspace 1 No No tag 1.7.9 1 No No `, }, } for i := range tests { test := tests[i] t.Run(test.name, func(t *testing.T) { // reset the openAPI afterward openapi.ResetOpenAPI() defer openapi.ResetOpenAPI() dir := filepath.Join("test", "testdata", test.dataset) runner := commands.NewListSettersRunner("") actual := &bytes.Buffer{} runner.Command.SetOut(actual) runner.Command.SetArgs(append([]string{dir}, test.args...)) err := runner.Command.Execute() if !assert.NoError(t, err) { t.FailNow() } // normalize path format for windows actualNormalized := strings.ReplaceAll(actual.String(), "\\", "/") if !assert.Contains(t, strings.TrimSpace(actualNormalized), strings.TrimSpace(test.expected)) { t.FailNow() } }) } }