github.com/GoogleContainerTools/skaffold/v2@v2.13.2/integration/deploy_test.go (about)

     1  /*
     2  Copyright 2019 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 integration
    18  
    19  import (
    20  	"encoding/json"
    21  	"errors"
    22  	"fmt"
    23  	"io"
    24  	"os"
    25  	"path/filepath"
    26  	"strings"
    27  	"testing"
    28  
    29  	"github.com/google/uuid"
    30  
    31  	"github.com/GoogleContainerTools/skaffold/v2/cmd/skaffold/app/flags"
    32  	"github.com/GoogleContainerTools/skaffold/v2/integration/skaffold"
    33  	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/util"
    34  	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/walk"
    35  	"github.com/GoogleContainerTools/skaffold/v2/testutil"
    36  )
    37  
    38  func TestBuildDeploy(t *testing.T) {
    39  	MarkIntegrationTest(t, NeedsGcp)
    40  
    41  	ns, client := SetupNamespace(t)
    42  
    43  	outputBytes := skaffold.Build("--quiet", "--platform=linux/arm64,linux/amd64", "--cache-artifacts=false", "--tag", uuid.New().String()).InDir("examples/nodejs").InNs(ns.Name).RunOrFailOutput(t)
    44  	// Parse the Build Output
    45  	buildArtifacts, err := flags.ParseBuildOutput(outputBytes)
    46  	failNowIfError(t, err)
    47  
    48  	tmpDir := testutil.NewTempDir(t)
    49  	buildOutputFile := tmpDir.Path("build.out")
    50  	tmpDir.Write("build.out", string(outputBytes))
    51  
    52  	// Run Deploy using the build output
    53  	// See https://github.com/GoogleContainerTools/skaffold/issues/2372 on why status-check=false
    54  	skaffold.Deploy("--build-artifacts", buildOutputFile, "--status-check=false").InDir("examples/nodejs").InNs(ns.Name).RunOrFail(t)
    55  
    56  	depApp := client.GetDeployment("node")
    57  	testutil.CheckDeepEqual(t, buildArtifacts.Builds[0].Tag, depApp.Spec.Template.Spec.Containers[0].Image)
    58  }
    59  
    60  func TestDeploy(t *testing.T) {
    61  	MarkIntegrationTest(t, CanRunWithoutGcp)
    62  
    63  	ns, client := SetupNamespace(t)
    64  
    65  	// `--default-repo=` is used to cancel the default repo that is set by default.
    66  	skaffold.Deploy("--images", "index.docker.io/library/busybox:1", "--default-repo=").InDir("examples/kustomize").InNs(ns.Name).RunOrFail(t)
    67  
    68  	dep := client.GetDeployment("kustomize-test")
    69  	testutil.CheckDeepEqual(t, "index.docker.io/library/busybox:1", dep.Spec.Template.Spec.Containers[0].Image)
    70  }
    71  
    72  func TestDeployWithBuildArtifacts(t *testing.T) {
    73  	MarkIntegrationTest(t, CanRunWithoutGcp)
    74  
    75  	ns, client := SetupNamespace(t)
    76  
    77  	// first build the artifacts and output to file
    78  	skaffold.Build("--file-output=images.json", "--default-repo=").InDir("examples/getting-started").RunOrFail(t)
    79  
    80  	// `--default-repo=` is used to cancel the default repo that is set by default.
    81  	skaffold.Deploy("--build-artifacts=images.json", "--default-repo=", "--load-images=true").InDir("examples/getting-started").InNs(ns.Name).RunOrFail(t)
    82  
    83  	pod := client.GetPod("getting-started")
    84  	testutil.CheckContains(t, "skaffold-example", pod.Spec.Containers[0].Image)
    85  }
    86  
    87  func TestDeployWithImages(t *testing.T) {
    88  	MarkIntegrationTest(t, CanRunWithoutGcp)
    89  
    90  	ns, client := SetupNamespace(t)
    91  
    92  	// first build the artifacts and output to file
    93  	skaffold.Build("--file-output=artifacts.json", "--default-repo=").InDir("examples/getting-started").RunOrFail(t)
    94  
    95  	var artifacts flags.BuildOutput
    96  	if ba, err := os.ReadFile("examples/getting-started/artifacts.json"); err != nil {
    97  		t.Fatal("could not read artifacts.json", err)
    98  	} else if err := json.Unmarshal(ba, &artifacts); err != nil {
    99  		t.Fatal("could not decode artifacts.json", err)
   100  	}
   101  
   102  	var images []string
   103  	for _, a := range artifacts.Builds {
   104  		images = append(images, a.ImageName+"="+a.Tag)
   105  	}
   106  
   107  	// `--default-repo=` is used to cancel the default repo that is set by default.
   108  	skaffold.Deploy("--images="+strings.Join(images, ","), "--default-repo=", "--load-images=true").InDir("examples/getting-started").InNs(ns.Name).RunOrFail(t)
   109  
   110  	pod := client.GetPod("getting-started")
   111  	testutil.CheckContains(t, "skaffold-example", pod.Spec.Containers[0].Image)
   112  }
   113  
   114  func TestDeployTail(t *testing.T) {
   115  	MarkIntegrationTest(t, CanRunWithoutGcp)
   116  
   117  	ns, _ := SetupNamespace(t)
   118  
   119  	// `--default-repo=` is used to cancel the default repo that is set by default.
   120  	out := skaffold.Deploy("--tail", "--images", "busybox:latest", "--default-repo=").InDir("testdata/deploy-hello-tail").InNs(ns.Name).RunLive(t)
   121  
   122  	WaitForLogs(t, out, "Hello world!")
   123  }
   124  
   125  func TestDeployTailDefaultNamespace(t *testing.T) {
   126  	MarkIntegrationTest(t, CanRunWithoutGcp)
   127  
   128  	// `--default-repo=` is used to cancel the default repo that is set by default.
   129  	out := skaffold.Deploy("--tail", "--images", "busybox:latest", "--default-repo=").InDir("testdata/deploy-hello-tail").RunLive(t)
   130  
   131  	defer skaffold.Delete().InDir("testdata/deploy-hello-tail").Run(t)
   132  	WaitForLogs(t, out, "Hello world!")
   133  }
   134  
   135  func TestDeployWithInCorrectConfig(t *testing.T) {
   136  	MarkIntegrationTest(t, CanRunWithoutGcp)
   137  
   138  	ns, _ := SetupNamespace(t)
   139  
   140  	// We're not providing a tag for the getting-started image
   141  	output, err := skaffold.Deploy().InDir("examples/getting-started").InNs(ns.Name).RunWithCombinedOutput(t)
   142  	if err == nil {
   143  		t.Errorf("expected to see an error since not every image tag is provided: %s", output)
   144  	} else if !strings.Contains(string(output), "no tag provided for image [skaffold-example]") {
   145  		t.Errorf("failed without saying the reason: %s", output)
   146  	}
   147  }
   148  
   149  // Verify that we can deploy without artifact details (https://github.com/GoogleContainerTools/skaffold/issues/4616)
   150  func TestDeployWithoutWorkspaces(t *testing.T) {
   151  	MarkIntegrationTest(t, NeedsGcp)
   152  
   153  	ns, _ := SetupNamespace(t)
   154  
   155  	outputBytes := skaffold.Build("--quiet").InDir("examples/nodejs").InNs(ns.Name).RunOrFailOutput(t)
   156  	// Parse the Build Output
   157  	buildArtifacts, err := flags.ParseBuildOutput(outputBytes)
   158  	failNowIfError(t, err)
   159  	if len(buildArtifacts.Builds) != 1 {
   160  		t.Fatalf("expected 1 artifact to be built, but found %d", len(buildArtifacts.Builds))
   161  	}
   162  
   163  	tmpDir := testutil.NewTempDir(t)
   164  	buildOutputFile := tmpDir.Path("build.out")
   165  	tmpDir.Write("build.out", string(outputBytes))
   166  	copyFiles(tmpDir.Root(), "examples/nodejs/skaffold.yaml")
   167  	copyFiles(tmpDir.Root(), "examples/nodejs/k8s")
   168  
   169  	// Run Deploy using the build output
   170  	// See https://github.com/GoogleContainerTools/skaffold/issues/2372 on why status-check=false
   171  	skaffold.Deploy("--build-artifacts", buildOutputFile, "--status-check=false").InDir(tmpDir.Root()).InNs(ns.Name).RunOrFail(t)
   172  }
   173  
   174  func TestDeployDependenciesOrder(t *testing.T) {
   175  	tests := []struct {
   176  		description         string
   177  		dir                 string
   178  		moduleToDeploy      string
   179  		expectedDeployOrder []string
   180  	}{
   181  		{
   182  			description: "Deploy order of entire project",
   183  			dir:         "testdata/multi-config-dependencies-order",
   184  			expectedDeployOrder: []string{
   185  				"module4",
   186  				"module3",
   187  				"module2",
   188  				"module1",
   189  			},
   190  		},
   191  		{
   192  			description:    "Deploy order for just one part of the project",
   193  			dir:            "testdata/multi-config-dependencies-order",
   194  			moduleToDeploy: "module2",
   195  			expectedDeployOrder: []string{
   196  				"module4",
   197  				"module3",
   198  				"module2",
   199  			},
   200  		},
   201  	}
   202  
   203  	for _, test := range tests {
   204  		t.Run(test.description, func(t *testing.T) {
   205  			MarkIntegrationTest(t, CanRunWithoutGcp)
   206  			targetModule := []string{}
   207  			if test.moduleToDeploy != "" {
   208  				targetModule = []string{"--module", test.moduleToDeploy}
   209  			}
   210  
   211  			expectedFormatedDeployOrder := []string{}
   212  			for _, module := range test.expectedDeployOrder {
   213  				expectedFormatedDeployOrder = append(expectedFormatedDeployOrder, fmt.Sprintf(" - pod/%v created", module))
   214  				expectedFormatedDeployOrder = append(expectedFormatedDeployOrder, "Waiting for deployments to stabilize...")
   215  				expectedFormatedDeployOrder = append(expectedFormatedDeployOrder, "Deployments stabilized in \\d*\\.?\\d+ms")
   216  			}
   217  			expectedFormatedDeployOrder = append([]string{"Starting deploy..."}, expectedFormatedDeployOrder...)
   218  			expectedOutput := strings.Join(expectedFormatedDeployOrder, "\n")
   219  
   220  			ns, _ := SetupNamespace(t)
   221  			outputBytes := skaffold.Run(targetModule...).InDir(test.dir).InNs(ns.Name).RunOrFailOutput(t)
   222  			defer skaffold.Delete().InDir(test.dir).InNs(ns.Name).RunOrFail(t)
   223  
   224  			output := string(outputBytes)
   225  			testutil.CheckRegex(t, expectedOutput, output)
   226  		})
   227  	}
   228  }
   229  
   230  // Copies a file or directory tree.  There are 2x3 cases:
   231  //   1. If _src_ is a file,
   232  //      1. and _dst_ exists and is a file then _src_ is copied into _dst_
   233  //      2. and _dst_ exists and is a directory, then _src_ is copied as _dst/$(basename src)_
   234  //      3. and _dst_ does not exist, then _src_ is copied as _dst_.
   235  //   2. If _src_ is a directory,
   236  //      1. and _dst_ exists and is a file, then return an error
   237  //      2. and _dst_ exists and is a directory, then src is copied as _dst/$(basename src)_
   238  //      3. and _dst_ does not exist, then src is copied as _dst/src[1:]_.
   239  
   240  func copyFiles(dst, src string) error {
   241  	if util.IsFile(src) {
   242  		switch {
   243  		case util.IsFile(dst): // copy _src_ to _dst_
   244  		case util.IsDir(dst): // copy _src_ to _dst/src[-1]
   245  			dst = filepath.Join(dst, filepath.Base(src))
   246  		default: // copy _src_ to _dst_
   247  			if err := os.MkdirAll(filepath.Dir(dst), os.ModePerm); err != nil {
   248  				return err
   249  			}
   250  		}
   251  		in, err := os.Open(src)
   252  		if err != nil {
   253  			return err
   254  		}
   255  		out, err := os.Create(dst)
   256  		if err != nil {
   257  			return err
   258  		}
   259  		_, err = io.Copy(out, in)
   260  		return err
   261  	} else if !util.IsDir(src) {
   262  		return errors.New("src does not exist")
   263  	}
   264  	// so src is a directory
   265  	if util.IsFile(dst) {
   266  		return errors.New("cannot copy directory into file")
   267  	}
   268  	srcPrefix := src
   269  	if util.IsDir(dst) { // src is copied to _dst/$(basename src)
   270  		srcPrefix = filepath.Dir(src)
   271  	} else if err := os.MkdirAll(filepath.Dir(dst), os.ModePerm); err != nil {
   272  		return err
   273  	}
   274  	return walk.From(src).Unsorted().WhenIsFile().Do(func(path string, _ walk.Dirent) error {
   275  		rel, err := filepath.Rel(srcPrefix, path)
   276  		if err != nil {
   277  			return err
   278  		}
   279  		in, err := os.Open(path)
   280  		if err != nil {
   281  			return err
   282  		}
   283  		defer in.Close()
   284  
   285  		destFile := filepath.Join(dst, rel)
   286  		if err := os.MkdirAll(filepath.Dir(destFile), os.ModePerm); err != nil {
   287  			return err
   288  		}
   289  
   290  		out, err := os.Create(destFile)
   291  		if err != nil {
   292  			return err
   293  		}
   294  
   295  		_, err = io.Copy(out, in)
   296  		return err
   297  	})
   298  }