github.com/GoogleContainerTools/skaffold/v2@v2.13.2/integration/build_dependencies_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 integration
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  	"testing"
    23  
    24  	"github.com/google/uuid"
    25  
    26  	"github.com/GoogleContainerTools/skaffold/v2/integration/skaffold"
    27  )
    28  
    29  func TestBuildDependenciesOrder(t *testing.T) {
    30  	tests := []struct {
    31  		description  string
    32  		args         []string
    33  		cacheEnabled bool
    34  		failure      string
    35  	}{
    36  		{
    37  			description: "default concurrency=1",
    38  		},
    39  		{
    40  			description: "concurrency=0",
    41  			args:        []string{"-p", "concurrency-0"},
    42  		},
    43  		{
    44  			description: "concurrency=3",
    45  			args:        []string{"-p", "concurrency-3"},
    46  		},
    47  		{
    48  			description: "invalid dependency",
    49  			args:        []string{"-p", "invalid-dependency"},
    50  			failure:     `unknown build dependency "image5" for artifact "image1"`,
    51  		},
    52  		{
    53  			description: "circular dependency",
    54  			args:        []string{"-p", "circular-dependency"},
    55  			failure:     `cycle detected in build dependencies involving "image1"`,
    56  		},
    57  		{
    58  			description: "build failure with concurrency=1",
    59  			args:        []string{"-p", "failed-dependency"},
    60  			failure:     `docker build failure: The command '/bin/sh -c [ "${FAIL}" == "0" ] || false' returned a non-zero code: 1`,
    61  		},
    62  		{
    63  			description: "build failure with concurrency=0",
    64  			args:        []string{"-p", "failed-dependency", "-p", "concurrency-0"},
    65  			failure:     `docker build failure: The command '/bin/sh -c [ "${FAIL}" == "0" ] || false' returned a non-zero code: 1`,
    66  		},
    67  	}
    68  
    69  	for _, test := range tests {
    70  		t.Run(test.description, func(t *testing.T) {
    71  			MarkIntegrationTest(t, CanRunWithoutGcp)
    72  			if test.cacheEnabled {
    73  				test.args = append(test.args, "--cache-artifacts=true")
    74  			} else {
    75  				test.args = append(test.args, "--cache-artifacts=false")
    76  			}
    77  
    78  			if test.failure == "" {
    79  				// Run without artifact caching
    80  				skaffold.Build(test.args...).InDir("testdata/build-dependencies").RunOrFail(t)
    81  				checkImagesExist(t)
    82  			} else {
    83  				if out, err := skaffold.Build(test.args...).InDir("testdata/build-dependencies").RunWithCombinedOutput(t); err == nil {
    84  					t.Fatal("expected build to fail")
    85  				} else if !strings.Contains(string(out), test.failure) {
    86  					t.Log("build output: ", string(out))
    87  					t.Fatalf("build failed but for wrong reason")
    88  				}
    89  			}
    90  		})
    91  	}
    92  }
    93  
    94  func TestBuildDependenciesCache(t *testing.T) {
    95  	// These tests build 4 images and then make a file change to the images in `change`.
    96  	// The test then triggers another build and verifies that the images in `rebuilt` were built
    97  	// (e.g., the changed images and their dependents), and that the other images were found in the artifact cache.
    98  	// It runs the profile `concurrency-0` which builds with maximum concurrency.
    99  	tests := []struct {
   100  		description string
   101  		change      []int
   102  		rebuilt     []int
   103  	}{
   104  		{
   105  			description: "no change",
   106  		},
   107  		{
   108  			description: "change 1",
   109  			change:      []int{1},
   110  			rebuilt:     []int{1},
   111  		},
   112  		{
   113  			description: "change 2",
   114  			change:      []int{2},
   115  			rebuilt:     []int{1, 2},
   116  		},
   117  		{
   118  			description: "change 3",
   119  			change:      []int{3},
   120  			rebuilt:     []int{1, 2, 3},
   121  		},
   122  		{
   123  			description: "change 4",
   124  			change:      []int{4},
   125  			rebuilt:     []int{4},
   126  		},
   127  		{
   128  			description: "change all",
   129  			change:      []int{1, 2, 3, 4},
   130  			rebuilt:     []int{1, 2, 3, 4},
   131  		},
   132  	}
   133  
   134  	skaffold.Build("--cache-artifacts=true", "-p", "concurrency-0").InDir("testdata/build-dependencies").RunOrFail(t)
   135  	checkImagesExist(t)
   136  
   137  	for _, test := range tests {
   138  		t.Run(test.description, func(t *testing.T) {
   139  			MarkIntegrationTest(t, CanRunWithoutGcp)
   140  			// modify file `foo` to invalidate cache for target artifacts
   141  			for _, i := range test.change {
   142  				Run(t, fmt.Sprintf("testdata/build-dependencies/app%d", i), "sh", "-c", fmt.Sprintf("echo %s > foo", uuid.New().String()))
   143  			}
   144  			out, err := skaffold.Build("--cache-artifacts=true", "-p", "concurrency-0").InDir("testdata/build-dependencies").RunWithCombinedOutput(t)
   145  			if err != nil {
   146  				t.Fatal("expected build to succeed")
   147  			}
   148  			log := string(out)
   149  
   150  			for i := 1; i <= 4; i++ {
   151  				if !contains(test.rebuilt, i) && !strings.Contains(log, fmt.Sprintf("image%d: Found Locally", i)) {
   152  					t.Log("build output: ", string(out))
   153  					t.Fatalf("expected image%d to be cached", i)
   154  				}
   155  
   156  				if contains(test.rebuilt, i) && !strings.Contains(log, fmt.Sprintf("image%d: Not found. Building", i)) {
   157  					t.Log("build output: ", string(out))
   158  					t.Fatalf("expected image%d to be rebuilt", i)
   159  				}
   160  			}
   161  			checkImagesExist(t)
   162  		})
   163  	}
   164  
   165  	// revert file changes
   166  	for i := 1; i <= 4; i++ {
   167  		Run(t, fmt.Sprintf("testdata/build-dependencies/app%d", i), "sh", "-c", "> foo")
   168  	}
   169  }
   170  
   171  func checkImagesExist(t *testing.T) {
   172  	checkImageExists(t, "us-central1-docker.pkg.dev/k8s-skaffold/testing/image1:latest")
   173  	checkImageExists(t, "us-central1-docker.pkg.dev/k8s-skaffold/testing/image2:latest")
   174  	checkImageExists(t, "us-central1-docker.pkg.dev/k8s-skaffold/testing/image3:latest")
   175  	checkImageExists(t, "us-central1-docker.pkg.dev/k8s-skaffold/testing/image4:latest")
   176  }
   177  
   178  func contains(sl []int, t int) bool {
   179  	for _, i := range sl {
   180  		if i == t {
   181  			return true
   182  		}
   183  	}
   184  	return false
   185  }