github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/initializer/init_test.go (about)

     1  /*
     2  Copyright 2020 The Skaffold Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package initializer
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"errors"
    23  	"fmt"
    24  	"os"
    25  	"path/filepath"
    26  	"runtime"
    27  	"strings"
    28  	"testing"
    29  
    30  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
    31  	initconfig "github.com/GoogleContainerTools/skaffold/pkg/skaffold/initializer/config"
    32  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema"
    33  	"github.com/GoogleContainerTools/skaffold/testutil"
    34  )
    35  
    36  func TestDoInit(t *testing.T) {
    37  	tests := []struct {
    38  		name             string
    39  		shouldSkip       bool
    40  		dir              string
    41  		config           initconfig.Config
    42  		expectedError    string
    43  		expectedExitCode int
    44  	}{
    45  		//TODO: mocked kompose test
    46  		{
    47  			name: "getting-started",
    48  			dir:  "testdata/init/hello",
    49  			config: initconfig.Config{
    50  				Force: true,
    51  				Opts: config.SkaffoldOptions{
    52  					ConfigurationFile: "skaffold.yaml.out",
    53  				},
    54  			},
    55  		},
    56  		{
    57  			name: "ignore existing tags",
    58  			dir:  "testdata/init/ignore-tags",
    59  			config: initconfig.Config{
    60  				Force: true,
    61  				Opts: config.SkaffoldOptions{
    62  					ConfigurationFile: "skaffold.yaml.out",
    63  				},
    64  			},
    65  		},
    66  		{
    67  			name: "microservices (backwards compatibility)",
    68  			dir:  "testdata/init/microservices",
    69  			config: initconfig.Config{
    70  				Force: true,
    71  				CliArtifacts: []string{
    72  					"leeroy-app/Dockerfile=gcr.io/k8s-skaffold/leeroy-app",
    73  					"leeroy-web/Dockerfile=gcr.io/k8s-skaffold/leeroy-web",
    74  				},
    75  				Opts: config.SkaffoldOptions{
    76  					ConfigurationFile: "skaffold.yaml.out",
    77  				},
    78  			},
    79  		},
    80  		{
    81  			name: "microservices",
    82  			dir:  "testdata/init/microservices",
    83  			config: initconfig.Config{
    84  				Force: true,
    85  				CliArtifacts: []string{
    86  					`{"builder":"Docker","payload":{"path":"leeroy-app/Dockerfile"},"image":"gcr.io/k8s-skaffold/leeroy-app"}`,
    87  					`{"builder":"Docker","payload":{"path":"leeroy-web/Dockerfile"},"image":"gcr.io/k8s-skaffold/leeroy-web"}`,
    88  				},
    89  				Opts: config.SkaffoldOptions{
    90  					ConfigurationFile: "skaffold.yaml.out",
    91  				},
    92  			},
    93  		},
    94  		{
    95  			name:       "windows paths use forward slashes",
    96  			shouldSkip: runtime.GOOS != "windows",
    97  			dir:        `testdata\init\windows`,
    98  			config: initconfig.Config{
    99  				Force: true,
   100  				CliArtifacts: []string{
   101  					`{"builder":"Docker","context":"apps\\web","payload":{"path":"apps\\web\\build\\Dockerfile"},"image":"gcr.io/k8s-skaffold/leeroy-web"}`,
   102  				},
   103  				Opts: config.SkaffoldOptions{
   104  					ConfigurationFile: "skaffold.yaml.out",
   105  				},
   106  			},
   107  		},
   108  		{
   109  			name: "CLI artifacts + manifest placeholders",
   110  			dir:  "testdata/init/allcli",
   111  			config: initconfig.Config{
   112  				Force: true,
   113  				CliArtifacts: []string{
   114  					`{"builder":"Docker","payload":{"path":"Dockerfile"},"image":"passed-in-artifact"}`,
   115  				},
   116  				CliKubernetesManifests: []string{
   117  					"manifest-placeholder1.yaml",
   118  					"manifest-placeholder2.yaml",
   119  				},
   120  				Opts: config.SkaffoldOptions{
   121  					ConfigurationFile: "skaffold.yaml.out",
   122  				},
   123  			},
   124  		},
   125  		{
   126  			name: "CLI artifacts but no manifests",
   127  			dir:  "testdata/init/allcli",
   128  			config: initconfig.Config{
   129  				Force: true,
   130  				CliArtifacts: []string{
   131  					`{"builder":"Docker","payload":{"path":"Dockerfile"},"image":"passed-in-artifact"}`,
   132  				},
   133  				Opts: config.SkaffoldOptions{
   134  					ConfigurationFile: "skaffold.yaml.out",
   135  				},
   136  			},
   137  			expectedError:    "one or more valid Kubernetes manifests are required to run skaffold",
   138  			expectedExitCode: 102,
   139  		},
   140  		{
   141  			name: "error writing config file",
   142  			dir:  "testdata/init/microservices",
   143  			config: initconfig.Config{
   144  				Force: true,
   145  				CliArtifacts: []string{
   146  					`{"builder":"Docker","payload":{"path":"leeroy-app/Dockerfile"},"image":"gcr.io/k8s-skaffold/leeroy-app"}`,
   147  					`{"builder":"Docker","payload":{"path":"leeroy-web/Dockerfile"},"image":"gcr.io/k8s-skaffold/leeroy-web"}`,
   148  				},
   149  				Opts: config.SkaffoldOptions{
   150  					// erroneous config file as . is a directory
   151  					ConfigurationFile: ".",
   152  				},
   153  			},
   154  			expectedError:    "writing config to file: open .: is a directory",
   155  			expectedExitCode: 1,
   156  		},
   157  		{
   158  			name: "error no builders",
   159  			dir:  "testdata/init/no-builder",
   160  			config: initconfig.Config{
   161  				Force: true,
   162  				Opts: config.SkaffoldOptions{
   163  					ConfigurationFile: "skaffold.yaml.out",
   164  				},
   165  			},
   166  			expectedError:    "please provide at least one build config",
   167  			expectedExitCode: 101,
   168  		},
   169  		{
   170  			name: "error no manifests",
   171  			dir:  "testdata/init/hello-no-manifest",
   172  			config: initconfig.Config{
   173  				Force: true,
   174  				Opts: config.SkaffoldOptions{
   175  					ConfigurationFile: "skaffold.yaml.out",
   176  				},
   177  			},
   178  			expectedError:    "one or more valid Kubernetes manifests are required to run skaffold",
   179  			expectedExitCode: 102,
   180  		},
   181  		{
   182  			name: "existing config",
   183  			dir:  "testdata/init/hello",
   184  			config: initconfig.Config{
   185  				Force: false,
   186  				Opts: config.SkaffoldOptions{
   187  					ConfigurationFile: "skaffold.yaml",
   188  				},
   189  			},
   190  			expectedError:    "pre-existing skaffold.yaml found",
   191  			expectedExitCode: 103,
   192  		},
   193  		{
   194  			name: "builder/image ambiguity",
   195  			dir:  "testdata/init/microservices",
   196  			config: initconfig.Config{
   197  				Force: true,
   198  				Opts: config.SkaffoldOptions{
   199  					ConfigurationFile: "skaffold.yaml.out",
   200  				},
   201  			},
   202  			expectedError:    "unable to automatically resolve builder/image pairs",
   203  			expectedExitCode: 104,
   204  		},
   205  		{
   206  			name: "kustomize",
   207  			dir:  "testdata/init/getting-started-kustomize",
   208  			config: initconfig.Config{
   209  				Force: true,
   210  				Opts: config.SkaffoldOptions{
   211  					ConfigurationFile: "skaffold.yaml.out",
   212  				},
   213  			},
   214  		},
   215  		{
   216  			name: "helm fails",
   217  			dir:  "testdata/init/helm-deployment",
   218  			config: initconfig.Config{
   219  				Opts: config.SkaffoldOptions{
   220  					ConfigurationFile: "skaffold.yaml.out",
   221  				},
   222  			},
   223  			expectedError: `Projects set up to deploy with helm must be manually configured.
   224  
   225  See https://skaffold.dev/docs/pipeline-stages/deployers/helm/ for a detailed guide on setting your project up with skaffold.`,
   226  			expectedExitCode: 1,
   227  		},
   228  	}
   229  	for _, test := range tests {
   230  		testutil.Run(t, test.name, func(t *testutil.T) {
   231  			if test.shouldSkip {
   232  				t.Logf("Skipped test %q", test.name)
   233  				return
   234  			}
   235  			t.Chdir(test.dir)
   236  
   237  			err := DoInit(context.TODO(), os.Stdout, test.config)
   238  
   239  			if test.expectedError != "" {
   240  				t.CheckErrorContains(test.expectedError, err)
   241  				t.CheckDeepEqual(exitCode(err), test.expectedExitCode)
   242  			} else {
   243  				t.CheckNoError(err)
   244  				checkGeneratedConfig(t, ".")
   245  			}
   246  		})
   247  	}
   248  }
   249  
   250  func TestDoInitAnalyze(t *testing.T) {
   251  	tests := []struct {
   252  		name        string
   253  		dir         string
   254  		config      initconfig.Config
   255  		expectedOut string
   256  	}{
   257  		{
   258  			name: "analyze microservices",
   259  			dir:  "testdata/init/microservices",
   260  			config: initconfig.Config{
   261  				Analyze: true,
   262  			},
   263  			expectedOut: strip(`{
   264  							"dockerfiles":["leeroy-app/Dockerfile","leeroy-web/Dockerfile"],
   265  							"images":["gcr.io/k8s-skaffold/leeroy-app","gcr.io/k8s-skaffold/leeroy-web"]
   266  							}`) + "\n",
   267  		},
   268  		{
   269  			name: "analyze microservices new format",
   270  			dir:  "testdata/init/microservices",
   271  			config: initconfig.Config{
   272  				Analyze:             true,
   273  				EnableNewInitFormat: true,
   274  			},
   275  			expectedOut: strip(`{
   276  									"builders":[
   277  										{"name":"Docker","payload":{"path":"leeroy-app/Dockerfile"}},
   278  										{"name":"Docker","payload":{"path":"leeroy-web/Dockerfile"}}
   279  									],
   280  									"images":[
   281  										{"name":"gcr.io/k8s-skaffold/leeroy-app","foundMatch":false},
   282  										{"name":"gcr.io/k8s-skaffold/leeroy-web","foundMatch":false}]}`) + "\n",
   283  		},
   284  		{
   285  			name: "no error with no manifests in analyze mode with skip-deploy",
   286  			dir:  "testdata/init/hello-no-manifest",
   287  
   288  			config: initconfig.Config{
   289  				Analyze:    true,
   290  				SkipDeploy: true,
   291  				Opts: config.SkaffoldOptions{
   292  					ConfigurationFile: "skaffold.yaml.out",
   293  				},
   294  			},
   295  
   296  			expectedOut: strip(`{"dockerfiles":["Dockerfile"]}`) + "\n",
   297  		},
   298  	}
   299  
   300  	for _, test := range tests {
   301  		testutil.Run(t, test.name, func(t *testutil.T) {
   302  			var out bytes.Buffer
   303  			t.Chdir(test.dir)
   304  
   305  			err := DoInit(context.TODO(), &out, test.config)
   306  
   307  			t.CheckNoError(err)
   308  			t.CheckDeepEqual(test.expectedOut, out.String())
   309  		})
   310  	}
   311  }
   312  
   313  func strip(s string) string {
   314  	cutString := "\n\t\r"
   315  	stripped := ""
   316  	for _, r := range s {
   317  		if strings.ContainsRune(cutString, r) {
   318  			continue
   319  		}
   320  		stripped = fmt.Sprintf("%s%c", stripped, r)
   321  	}
   322  	return stripped
   323  }
   324  
   325  func checkGeneratedConfig(t *testutil.T, dir string) {
   326  	expectedOutput, err := schema.ParseConfig(filepath.Join(dir, "skaffold.yaml"))
   327  	t.CheckNoError(err)
   328  
   329  	output, err := schema.ParseConfig(filepath.Join(dir, "skaffold.yaml.out"))
   330  	t.CheckNoError(err)
   331  	t.CheckDeepEqual(expectedOutput, output)
   332  }
   333  
   334  type ExitCoder interface {
   335  	ExitCode() int
   336  }
   337  
   338  func exitCode(err error) int {
   339  	var exitErr ExitCoder
   340  	if ok := errors.As(err, &exitErr); ok {
   341  		return exitErr.ExitCode()
   342  	}
   343  
   344  	return 1
   345  }