github.com/GoogleContainerTools/skaffold/v2@v2.13.2/integration/verify_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  	"os"
    21  	"path/filepath"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/google/uuid"
    26  
    27  	"github.com/GoogleContainerTools/skaffold/v2/integration/skaffold"
    28  	"github.com/GoogleContainerTools/skaffold/v2/testutil"
    29  )
    30  
    31  func TestLocalVerifyPassingTestsWithEnvVar(t *testing.T) {
    32  	MarkIntegrationTest(t, CanRunWithoutGcp)
    33  	tmp := t.TempDir()
    34  	logFile := filepath.Join(tmp, uuid.New().String()+"logs.json")
    35  
    36  	rpcPort := randomPort()
    37  	// `--default-repo=` is used to cancel the default repo that is set by default.
    38  	out, err := skaffold.Verify("--default-repo=", "--rpc-port", rpcPort,
    39  		"--event-log-file", logFile, "--env-file", "verify.env").InDir("testdata/verify-succeed").RunWithCombinedOutput(t)
    40  	logs := string(out)
    41  
    42  	testutil.CheckError(t, false, err)
    43  	testutil.CheckContains(t, "Hello from Docker!", logs)
    44  	testutil.CheckContains(t, "foo-var", logs)
    45  	testutil.CheckContains(t, "alpine-1", logs)
    46  	testutil.CheckContains(t, "alpine-2", logs)
    47  
    48  	// verify logs are in the event output as well
    49  	b, err := os.ReadFile(logFile + ".v2")
    50  	if err != nil {
    51  		t.Fatalf("error reading %s", logFile+".v2")
    52  	}
    53  	v2EventLogs := string(b)
    54  	testutil.CheckContains(t, "Hello from Docker!", v2EventLogs)
    55  	testutil.CheckContains(t, "foo-var", v2EventLogs)
    56  
    57  	// TODO(aaron-prindle) verify that SUCCEEDED event is found where expected
    58  }
    59  
    60  func TestVerifyWithNotCreatedNetwork(t *testing.T) {
    61  	MarkIntegrationTest(t, CanRunWithoutGcp)
    62  	// `--default-repo=` is used to cancel the default repo that is set by default.
    63  	logs, err := skaffold.Verify("--default-repo=", "--env-file", "verify.env", "--docker-network", "not-created-network").InDir("testdata/verify-succeed").RunWithCombinedOutput(t)
    64  	testutil.CheckError(t, true, err)
    65  	testutil.CheckContains(t, "network not-created-network not found", string(logs))
    66  }
    67  
    68  func TestLocalVerifyOneTestFailsWithEnvVar(t *testing.T) {
    69  	MarkIntegrationTest(t, CanRunWithoutGcp)
    70  	tmp := t.TempDir()
    71  	logFile := filepath.Join(tmp, uuid.New().String()+"logs.json")
    72  
    73  	rpcPort := randomPort()
    74  	// `--default-repo=` is used to cancel the default repo that is set by default.
    75  	out, err := skaffold.Verify("--default-repo=", "--rpc-port", rpcPort,
    76  		"--event-log-file", logFile, "--env-file", "verify.env").InDir("testdata/verify-fail").RunWithCombinedOutput(t)
    77  	logs := string(out)
    78  
    79  	testutil.CheckError(t, true, err)
    80  	testutil.CheckContains(t, "Hello from Docker!", logs)
    81  	testutil.CheckContains(t, "foo-var", logs)
    82  
    83  	// verify logs are in the event output as well
    84  	b, err := os.ReadFile(logFile + ".v2")
    85  	if err != nil {
    86  		t.Fatalf("error reading %s", logFile+".v2")
    87  	}
    88  	v2EventLogs := string(b)
    89  	testutil.CheckContains(t, "Hello from Docker!", v2EventLogs)
    90  	testutil.CheckContains(t, "foo-var", v2EventLogs)
    91  
    92  	// TODO(aaron-prindle) verify that FAILED event is found where expected
    93  }
    94  
    95  func TestVerifyNoTestsFails(t *testing.T) {
    96  	MarkIntegrationTest(t, CanRunWithoutGcp)
    97  	// `--default-repo=` is used to cancel the default repo that is set by default.
    98  	out, err := skaffold.Verify("--default-repo=").InDir("testdata/verify-no-tests").RunWithCombinedOutput(t)
    99  	logs := string(out)
   100  
   101  	testutil.CheckError(t, true, err)
   102  	testutil.CheckContains(t, "verify command expects non-zero number of test cases", logs)
   103  }
   104  
   105  func TestKubernetesJobVerifyPassingTestsWithEnvVar(t *testing.T) {
   106  	MarkIntegrationTest(t, CanRunWithoutGcp)
   107  	tmp := t.TempDir()
   108  	logFile := filepath.Join(tmp, uuid.New().String()+"logs.json")
   109  
   110  	rpcPort := randomPort()
   111  	// `--default-repo=` is used to cancel the default repo that is set by default.
   112  	ns, _ := SetupNamespace(t)
   113  	out, err := skaffold.Verify("--default-repo=", "--rpc-port", rpcPort,
   114  		"--event-log-file", logFile, "--env-file", "verify.env").InNs(ns.Name).InDir("testdata/verify-succeed-k8s").RunWithCombinedOutput(t)
   115  	logs := string(out)
   116  
   117  	testutil.CheckError(t, false, err)
   118  	testutil.CheckContains(t, "Hello from Docker!", logs)
   119  	testutil.CheckContains(t, "foo-var", logs)
   120  	testutil.CheckContains(t, "verify-succeed-k8s-1", logs)
   121  	testutil.CheckContains(t, "alpine-2", logs)
   122  
   123  	// verify logs are in the event output as well
   124  	b, err := os.ReadFile(logFile + ".v2")
   125  	if err != nil {
   126  		t.Fatalf("error reading %s", logFile+".v2")
   127  	}
   128  	v2EventLogs := string(b)
   129  	testutil.CheckContains(t, "Hello from Docker!", v2EventLogs)
   130  	testutil.CheckContains(t, "foo-var", v2EventLogs)
   131  
   132  	// TODO(aaron-prindle) verify that SUCCEEDED event is found where expected
   133  }
   134  
   135  func TestKubernetesJobVerifyEnvVarFromJobManifest(t *testing.T) {
   136  	MarkIntegrationTest(t, CanRunWithoutGcp)
   137  	tmp := t.TempDir()
   138  	logFile := filepath.Join(tmp, uuid.New().String()+"logs.json")
   139  
   140  	rpcPort := randomPort()
   141  	// `--default-repo=` is used to cancel the default repo that is set by default.
   142  	out, err := skaffold.Verify("--default-repo=", "--rpc-port", rpcPort,
   143  		"--event-log-file", logFile, "-p", "with-job-manifest").InDir("testdata/verify-succeed-k8s").RunWithCombinedOutput(t)
   144  	logs := string(out)
   145  
   146  	testutil.CheckError(t, false, err)
   147  	testutil.CheckContains(t, "ZZZ with-job-manifest", logs)
   148  }
   149  
   150  func TestKubernetesJobVerifyOneTestFailsWithEnvVar(t *testing.T) {
   151  	MarkIntegrationTest(t, CanRunWithoutGcp)
   152  	tmp := t.TempDir()
   153  	logFile := filepath.Join(tmp, uuid.New().String()+"logs.json")
   154  
   155  	rpcPort := randomPort()
   156  	// `--default-repo=` is used to cancel the default repo that is set by default.
   157  	ns, _ := SetupNamespace(t)
   158  	out, err := skaffold.Verify("--default-repo=", "--rpc-port", rpcPort,
   159  		"--event-log-file", logFile, "--env-file", "verify.env").InNs(ns.Name).InDir("testdata/verify-fail-k8s").RunWithCombinedOutput(t)
   160  	logs := string(out)
   161  
   162  	testutil.CheckError(t, true, err)
   163  	testutil.CheckContains(t, "Hello from Docker!", logs)
   164  	testutil.CheckContains(t, "foo-var", logs)
   165  
   166  	// verify logs are in the event output as well
   167  	b, err := os.ReadFile(logFile + ".v2")
   168  	if err != nil {
   169  		t.Fatalf("error reading %s", logFile+".v2")
   170  	}
   171  	v2EventLogs := string(b)
   172  	testutil.CheckContains(t, "Hello from Docker!", v2EventLogs)
   173  	testutil.CheckContains(t, "foo-var", v2EventLogs)
   174  
   175  	// TODO(aaron-prindle) verify that FAILED event is found where expected
   176  }
   177  
   178  func TestNoDuplicateLogsLocal(t *testing.T) {
   179  	tests := []struct {
   180  		description        string
   181  		dir                string
   182  		profile            string
   183  		shouldErr          bool
   184  		expectedUniqueLogs []string
   185  	}{
   186  		{
   187  			description: "no duplicated logs in docker actions, success execution",
   188  			dir:         "testdata/verify-succeed",
   189  			profile:     "no-duplicated-logs",
   190  			expectedUniqueLogs: []string{
   191  				"[alpine-1] alpine-1",
   192  				"[alpine-1] bye alpine-1",
   193  			},
   194  		},
   195  		{
   196  			description: "no duplicated logs in docker actions, fail execution",
   197  			dir:         "testdata/verify-fail",
   198  			profile:     "no-duplicated-logs",
   199  			shouldErr:   true,
   200  			expectedUniqueLogs: []string{
   201  				"[alpine-1] alpine-1",
   202  				"[alpine-1] bye alpine-1",
   203  				"[alpine-2] alpine-2",
   204  				"[alpine-2] bye alpine-2",
   205  			},
   206  		},
   207  	}
   208  
   209  	for _, test := range tests {
   210  		testutil.Run(t, test.description, func(t *testutil.T) {
   211  			MarkIntegrationTest(t.T, CanRunWithoutGcp)
   212  
   213  			args := []string{"-p", test.profile}
   214  			out, err := skaffold.Verify(args...).InDir(test.dir).RunWithCombinedOutput(t.T)
   215  
   216  			t.CheckError(test.shouldErr, err)
   217  
   218  			logs := string(out)
   219  			checkUniqueLogs(t, logs, test.expectedUniqueLogs)
   220  		})
   221  	}
   222  }
   223  
   224  func TestNoDuplicateLogsK8SJobs(t *testing.T) {
   225  	tests := []struct {
   226  		description        string
   227  		dir                string
   228  		profile            string
   229  		shouldErr          bool
   230  		expectedUniqueLogs []string
   231  	}{
   232  		{
   233  			description: "no duplicated logs in k8s actions, success execution",
   234  			dir:         "testdata/verify-succeed-k8s",
   235  			profile:     "no-duplicated-logs",
   236  			expectedUniqueLogs: []string{
   237  				"[no-duplicated-logs-1] alpine-1",
   238  				"[no-duplicated-logs-1] bye alpine-1",
   239  			},
   240  		},
   241  		{
   242  			description: "no duplicated logs in k8s actions, fail execution",
   243  			dir:         "testdata/verify-fail-k8s",
   244  			profile:     "no-duplicated-logs",
   245  			shouldErr:   true,
   246  			expectedUniqueLogs: []string{
   247  				"[alpine-1] alpine-1",
   248  				"[alpine-1] bye alpine-1",
   249  				"[alpine-2] alpine-2",
   250  				"[alpine-2] bye alpine-2",
   251  			},
   252  		},
   253  	}
   254  
   255  	for _, test := range tests {
   256  		testutil.Run(t, test.description, func(t *testutil.T) {
   257  			MarkIntegrationTest(t.T, CanRunWithoutGcp)
   258  
   259  			args := []string{"-p", test.profile}
   260  			ns, _ := SetupNamespace(t.T)
   261  			out, err := skaffold.Verify(args...).InNs(ns.Name).InDir(test.dir).RunWithCombinedOutput(t.T)
   262  
   263  			t.CheckError(test.shouldErr, err)
   264  
   265  			logs := string(out)
   266  			checkUniqueLogs(t, logs, test.expectedUniqueLogs)
   267  		})
   268  	}
   269  }
   270  
   271  func TestTimeoutK8s(t *testing.T) {
   272  	tests := []struct {
   273  		description     string
   274  		dir             string
   275  		profile         string
   276  		shouldErr       bool
   277  		expectedLogs    []string
   278  		notExpectedLogs []string
   279  	}{
   280  		{
   281  			description: "K8s - One test fail due to timeout",
   282  			dir:         "testdata/verify-fail-k8s",
   283  			profile:     "fail-timeout",
   284  			shouldErr:   true,
   285  			expectedLogs: []string{
   286  				`1 error(s) occurred:`,
   287  				`* "alpine-3" running k8s job timed out after : 5s`,
   288  			},
   289  			notExpectedLogs: []string{
   290  				`[alpine-3] bye alpine-3`,
   291  			},
   292  		},
   293  		{
   294  			description: "K8s - Two tests with different timeouts",
   295  			dir:         "testdata/verify-fail-k8s",
   296  			profile:     "fail-two-test-timeout",
   297  			shouldErr:   true,
   298  			expectedLogs: []string{
   299  				`[alpine-4] alpine-4`,
   300  				`[alpine-5] alpine-5`,
   301  				`* "alpine-4" running k8s job timed out after : 6s`,
   302  				`* "alpine-5" running k8s job timed out after : 5s`,
   303  			},
   304  			notExpectedLogs: []string{
   305  				`[alpine-4] bye alpine-4`,
   306  				`[alpine-5] bye alpine-5`,
   307  			},
   308  		},
   309  		{
   310  			description: "K8s - Two tests, one fail other succeed",
   311  			dir:         "testdata/verify-fail-k8s",
   312  			profile:     "fail-only-one-test-timeout",
   313  			shouldErr:   true,
   314  			expectedLogs: []string{
   315  				`[alpine-6] alpine-6`,
   316  				`[alpine-7] alpine-7`,
   317  				`[alpine-7] bye alpine-7`,
   318  				`* "alpine-6" running k8s job timed out after : 6s`,
   319  			},
   320  			notExpectedLogs: []string{
   321  				`[alpine-6] bye alpine-6`,
   322  			},
   323  		},
   324  		{
   325  			description: "K8s - Two tests with timeouts, all succeed",
   326  			dir:         "testdata/verify-succeed-k8s",
   327  			profile:     "succeed-with-timeout",
   328  			expectedLogs: []string{
   329  				`[alpine-8] alpine-8`,
   330  				`[alpine-8] bye alpine-8`,
   331  				`[alpine-9] alpine-9`,
   332  				`[alpine-9] bye alpine-9`,
   333  			},
   334  			notExpectedLogs: []string{
   335  				`* "alpine-8" running k8s job timed out after : 20s`,
   336  				`* "alpine-9" running k8s job timed out after : 25s`,
   337  			},
   338  		},
   339  		{
   340  			description: "K8s - Two tests, one with timeout, all succeed",
   341  			dir:         "testdata/verify-succeed-k8s",
   342  			profile:     "succeed-all-one-with-timeout",
   343  			expectedLogs: []string{
   344  				`[alpine-10] alpine-10`,
   345  				`[alpine-10] bye alpine-10`,
   346  				`[alpine-11] alpine-11`,
   347  				`[alpine-11] bye alpine-11`,
   348  			},
   349  			notExpectedLogs: []string{
   350  				`* "alpine-11" running k8s job timed out after : 25s`,
   351  			},
   352  		},
   353  	}
   354  
   355  	for _, test := range tests {
   356  		testutil.Run(t, test.description, func(t *testutil.T) {
   357  			MarkIntegrationTest(t.T, CanRunWithoutGcp)
   358  
   359  			args := []string{"-p", test.profile}
   360  			ns, _ := SetupNamespace(t.T)
   361  			out, err := skaffold.Verify(args...).InNs(ns.Name).InDir(test.dir).RunWithCombinedOutput(t.T)
   362  			logs := string(out)
   363  
   364  			t.CheckError(test.shouldErr, err)
   365  
   366  			for _, el := range test.expectedLogs {
   367  				t.CheckContains(el, logs)
   368  			}
   369  
   370  			for _, nel := range test.notExpectedLogs {
   371  				testutil.CheckNotContains(t.T, nel, logs)
   372  			}
   373  		})
   374  	}
   375  }
   376  
   377  func TestTimeoutDocker(t *testing.T) {
   378  	tests := []struct {
   379  		description     string
   380  		dir             string
   381  		profile         string
   382  		shouldErr       bool
   383  		expectedLogs    []string
   384  		notExpectedLogs []string
   385  	}{
   386  		{
   387  			description: "Docker - One test fail due to timeout",
   388  			dir:         "testdata/verify-fail",
   389  			profile:     "fail-timeout",
   390  			shouldErr:   true,
   391  			expectedLogs: []string{
   392  				`1 error(s) occurred:`,
   393  				`* verify test failed: "alpine-1" running container image "alpine:3.15.4" timed out after : 5s`,
   394  			},
   395  			notExpectedLogs: []string{
   396  				`[alpine-1] bye alpine-1`,
   397  			},
   398  		},
   399  		{
   400  			description: "Docker - Two tests with different timeouts",
   401  			dir:         "testdata/verify-fail",
   402  			profile:     "fail-two-test-timeout",
   403  			shouldErr:   true,
   404  			expectedLogs: []string{
   405  				`[alpine-2] alpine-2`,
   406  				`[alpine-1] alpine-1`,
   407  				`* verify test failed: "alpine-1" running container image "alpine:3.15.4" timed out after : 6s`,
   408  				`* verify test failed: "alpine-2" running container image "alpine:3.15.4" timed out after : 5s`,
   409  			},
   410  			notExpectedLogs: []string{
   411  				`[alpine-1] bye alpine-1`,
   412  				`[alpine-2] bye alpine-2`,
   413  			},
   414  		},
   415  		{
   416  			description: "Docker - Two tests, one fail other succeed",
   417  			dir:         "testdata/verify-fail",
   418  			profile:     "fail-only-one-test-timeout",
   419  			shouldErr:   true,
   420  			expectedLogs: []string{
   421  				`[alpine-1] alpine-1`,
   422  				`[alpine-2] alpine-2`,
   423  				`[alpine-2] bye alpine-2`,
   424  				`* verify test failed: "alpine-1" running container image "alpine:3.15.4" timed out after : 6s`,
   425  			},
   426  			notExpectedLogs: []string{
   427  				`[alpine-1] bye alpine-1`,
   428  			},
   429  		},
   430  		{
   431  			description: "Docker - Two tests with timeouts, all succeed",
   432  			dir:         "testdata/verify-succeed",
   433  			profile:     "succeed-with-timeout",
   434  			expectedLogs: []string{
   435  				`[alpine-1] alpine-1`,
   436  				`[alpine-1] bye alpine-1`,
   437  				`[alpine-2] alpine-2`,
   438  				`[alpine-2] bye alpine-2`,
   439  			},
   440  			notExpectedLogs: []string{
   441  				`* verify test failed: "alpine-1" running container image "alpine:3.15.4" timed out after : 20s`,
   442  				`* verify test failed: "alpine-2" running container image "alpine:3.15.4" timed out after : 25s`,
   443  			},
   444  		},
   445  		{
   446  			description: "Docker - Two tests, one with timeout, all succeed",
   447  			dir:         "testdata/verify-succeed",
   448  			profile:     "succeed-all-one-with-timeout",
   449  			expectedLogs: []string{
   450  				`[alpine-1] alpine-1`,
   451  				`[alpine-1] bye alpine-1`,
   452  				`[alpine-2] alpine-2`,
   453  				`[alpine-2] bye alpine-2`,
   454  			},
   455  			notExpectedLogs: []string{
   456  				`* verify test failed: "alpine-2" running container image "alpine:3.15.4" timed out after : 25s`,
   457  			},
   458  		},
   459  	}
   460  
   461  	for _, test := range tests {
   462  		testutil.Run(t, test.description, func(t *testutil.T) {
   463  			MarkIntegrationTest(t.T, CanRunWithoutGcp)
   464  
   465  			args := []string{"-p", test.profile}
   466  			out, err := skaffold.Verify(args...).InDir(test.dir).RunWithCombinedOutput(t.T)
   467  			logs := string(out)
   468  
   469  			t.CheckError(test.shouldErr, err)
   470  
   471  			for _, el := range test.expectedLogs {
   472  				t.CheckContains(el, logs)
   473  			}
   474  
   475  			for _, nel := range test.notExpectedLogs {
   476  				testutil.CheckNotContains(t.T, nel, logs)
   477  			}
   478  		})
   479  	}
   480  }
   481  
   482  func checkUniqueLogs(t *testutil.T, logs string, expectedUniqueLogs []string) {
   483  	for _, uniqueLog := range expectedUniqueLogs {
   484  		timesFound := strings.Count(logs, uniqueLog)
   485  		if timesFound != 1 {
   486  			t.Fatalf(`Log message "%v" found %v times, expected exactly 1 time`, uniqueLog, timesFound)
   487  		}
   488  	}
   489  }
   490  
   491  func TestVerify_WithLocalArtifact(t *testing.T) {
   492  	tests := []struct {
   493  		description     string
   494  		dir             string
   495  		profile         string
   496  		shouldErr       bool
   497  		shouldBuild     bool
   498  		expectedMsgs    []string
   499  		notExpectedMsgs []string
   500  	}{
   501  		{
   502  			description: "build and verify",
   503  			dir:         "testdata/verify-succeed",
   504  			profile:     "local-built-artifact",
   505  			shouldBuild: true,
   506  			expectedMsgs: []string{
   507  				"Tags used in verification:",
   508  				"- localtask ->",
   509  				"[localtask] Hello world ! 0",
   510  				"[alpine-1] alpine-1",
   511  			},
   512  			notExpectedMsgs: []string{
   513  				"- img-not-used-in-verify ->",
   514  			},
   515  		},
   516  		{
   517  			description: "fail due not found image",
   518  			dir:         "testdata/verify-succeed-k8s",
   519  			profile:     "local-built-artifact",
   520  			shouldErr:   true,
   521  			expectedMsgs: []string{
   522  				"1 error(s) occurred",
   523  				"creating container for localtask: ErrImagePull",
   524  			},
   525  		},
   526  	}
   527  
   528  	for _, test := range tests {
   529  		testutil.Run(t, test.description, func(t *testutil.T) {
   530  			MarkIntegrationTest(t.T, CanRunWithoutGcp)
   531  
   532  			ns, _ := SetupNamespace(t.T)
   533  
   534  			args := []string{"-p", test.profile}
   535  
   536  			if test.shouldBuild {
   537  				tmpfile := testutil.TempFile(t.T, "", []byte{})
   538  				skaffold.Build(append(args, "--file-output", tmpfile)...).InDir(test.dir).RunOrFail(t.T)
   539  				args = append(args, "--build-artifacts", tmpfile)
   540  			}
   541  
   542  			out, err := skaffold.Verify(args...).InDir(test.dir).InNs(ns.Name).RunWithCombinedOutput(t.T)
   543  			logs := string(out)
   544  
   545  			t.CheckError(test.shouldErr, err)
   546  
   547  			for _, expectedMsg := range test.expectedMsgs {
   548  				t.CheckContains(expectedMsg, logs)
   549  			}
   550  
   551  			for _, notExpectedMsg := range test.notExpectedMsgs {
   552  				testutil.CheckNotContains(t.T, notExpectedMsg, logs)
   553  			}
   554  		})
   555  	}
   556  }