github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/build/jib/maven_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 jib 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "io/ioutil" 24 "os" 25 "os/exec" 26 "runtime" 27 "strings" 28 "testing" 29 "time" 30 31 v1 "github.com/opencontainers/image-spec/specs-go/v1" 32 33 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" 34 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/platform" 35 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" 36 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" 37 "github.com/GoogleContainerTools/skaffold/testutil" 38 ) 39 40 func TestBuildJibMavenToDocker(t *testing.T) { 41 tests := []struct { 42 description string 43 artifact *latest.JibArtifact 44 commands util.Command 45 shouldErr bool 46 expectedError string 47 }{ 48 { 49 description: "build", 50 artifact: &latest.JibArtifact{}, 51 commands: testutil.CmdRun( 52 "mvn fake-mavenBuildArgs-for-dockerBuild -Dimage=img:tag", 53 ), 54 }, 55 { 56 description: "build with module", 57 artifact: &latest.JibArtifact{Project: "module"}, 58 commands: testutil.CmdRun( 59 "mvn fake-mavenBuildArgs-for-module-for-dockerBuild -Dimage=img:tag", 60 ), 61 }, 62 { 63 description: "build with custom base image", 64 artifact: &latest.JibArtifact{BaseImage: "docker://busybox"}, 65 commands: testutil.CmdRun( 66 "mvn fake-mavenBuildArgs-for-dockerBuild -Djib.from.image=docker://busybox -Dimage=img:tag", 67 ), 68 }, 69 { 70 description: "fail build", 71 artifact: &latest.JibArtifact{}, 72 commands: testutil.CmdRunErr( 73 "mvn fake-mavenBuildArgs-for-dockerBuild -Dimage=img:tag", 74 errors.New("BUG"), 75 ), 76 shouldErr: true, 77 expectedError: "maven build failed", 78 }, 79 } 80 81 for _, test := range tests { 82 testutil.Run(t, test.description, func(t *testutil.T) { 83 t.Override(&mavenBuildArgsFunc, getMavenBuildArgsFuncFake(t, MinimumJibMavenVersion)) 84 t.NewTempDir().Touch("pom.xml").Chdir() 85 t.Override(&util.DefaultExecCommand, test.commands) 86 api := (&testutil.FakeAPIClient{}).Add("img:tag", "imageID") 87 localDocker := fakeLocalDaemon(api) 88 89 builder := NewArtifactBuilder(localDocker, &mockConfig{}, false, false, mockArtifactResolver{}) 90 result, err := builder.Build(context.Background(), ioutil.Discard, &latest.Artifact{ 91 ArtifactType: latest.ArtifactType{ 92 JibArtifact: test.artifact, 93 }, 94 }, "img:tag", platform.Matcher{}) 95 96 t.CheckError(test.shouldErr, err) 97 if test.shouldErr { 98 t.CheckErrorContains(test.expectedError, err) 99 } else { 100 t.CheckDeepEqual("imageID", result) 101 } 102 }) 103 } 104 } 105 106 func TestBuildJibMavenToRegistry(t *testing.T) { 107 tests := []struct { 108 description string 109 artifact *latest.JibArtifact 110 commands util.Command 111 shouldErr bool 112 expectedError string 113 }{ 114 { 115 description: "build", 116 artifact: &latest.JibArtifact{}, 117 commands: testutil.CmdRun("mvn fake-mavenBuildArgs-for-build -Dimage=img:tag"), 118 }, 119 { 120 description: "build with module", 121 artifact: &latest.JibArtifact{Project: "module"}, 122 commands: testutil.CmdRun("mvn fake-mavenBuildArgs-for-module-for-build -Dimage=img:tag"), 123 }, 124 { 125 description: "build with custom base image", 126 artifact: &latest.JibArtifact{BaseImage: "docker://busybox"}, 127 commands: testutil.CmdRun("mvn fake-mavenBuildArgs-for-build -Djib.from.image=docker://busybox -Dimage=img:tag"), 128 }, 129 { 130 description: "fail build", 131 artifact: &latest.JibArtifact{}, 132 commands: testutil.CmdRunErr( 133 "mvn fake-mavenBuildArgs-for-build -Dimage=img:tag", 134 errors.New("BUG"), 135 ), 136 shouldErr: true, 137 expectedError: "maven build failed", 138 }, 139 } 140 141 for _, test := range tests { 142 testutil.Run(t, test.description, func(t *testutil.T) { 143 t.Override(&mavenBuildArgsFunc, getMavenBuildArgsFuncFake(t, MinimumJibMavenVersion)) 144 t.NewTempDir().Touch("pom.xml").Chdir() 145 t.Override(&util.DefaultExecCommand, test.commands) 146 t.Override(&docker.RemoteDigest, func(identifier string, _ docker.Config, _ []v1.Platform) (string, error) { 147 if identifier == "img:tag" { 148 return "digest", nil 149 } 150 return "", errors.New("unknown remote tag") 151 }) 152 localDocker := fakeLocalDaemon(&testutil.FakeAPIClient{}) 153 154 builder := NewArtifactBuilder(localDocker, &mockConfig{}, true, false, mockArtifactResolver{}) 155 result, err := builder.Build(context.Background(), ioutil.Discard, &latest.Artifact{ 156 ArtifactType: latest.ArtifactType{ 157 JibArtifact: test.artifact, 158 }, 159 }, "img:tag", platform.Matcher{}) 160 161 t.CheckError(test.shouldErr, err) 162 if test.shouldErr { 163 t.CheckErrorContains(test.expectedError, err) 164 } else { 165 t.CheckDeepEqual("digest", result) 166 } 167 }) 168 } 169 } 170 171 func TestMinimumMavenVersion(t *testing.T) { 172 testutil.CheckDeepEqual(t, "1.4.0", MinimumJibMavenVersion) 173 } 174 175 func TestMavenWrapperDefinition(t *testing.T) { 176 testutil.CheckDeepEqual(t, "mvn", MavenCommand.Executable) 177 testutil.CheckDeepEqual(t, "mvnw", MavenCommand.Wrapper) 178 } 179 180 func TestGetDependenciesMaven(t *testing.T) { 181 tmpDir := testutil.NewTempDir(t) 182 183 tmpDir.Touch("build", "dep1", "dep2") 184 build := tmpDir.Path("build") 185 dep1 := tmpDir.Path("dep1") 186 dep2 := tmpDir.Path("dep2") 187 188 ctx := context.Background() 189 190 tests := []struct { 191 description string 192 stdout string 193 modTime time.Time 194 expected []string 195 err error 196 }{ 197 { 198 description: "failure", 199 stdout: "", 200 modTime: time.Unix(0, 0), 201 err: errors.New("error"), 202 }, 203 { 204 description: "success", 205 stdout: fmt.Sprintf("BEGIN JIB JSON\n{\"build\":[\"%s\"],\"inputs\":[\"%s\"],\"ignore\":[]}", build, dep1), 206 modTime: time.Unix(0, 0), 207 expected: []string{"build", "dep1"}, 208 }, 209 { 210 // Expected output differs from stdout since build file hasn't change, thus maven command won't run 211 description: "success", 212 stdout: fmt.Sprintf("BEGIN JIB JSON\n{\"build\":[\"%s\"],\"inputs\":[\"%s\", \"%s\"],\"ignore\":[]}", build, dep1, dep2), 213 modTime: time.Unix(0, 0), 214 expected: []string{"build", "dep1"}, 215 }, 216 { 217 description: "success", 218 stdout: fmt.Sprintf("BEGIN JIB JSON\n{\"build\":[\"%s\"],\"inputs\":[\"%s\", \"%s\"],\"ignore\":[]}", build, dep1, dep2), 219 modTime: time.Unix(10000, 0), 220 expected: []string{"build", "dep1", "dep2"}, 221 }, 222 } 223 for _, test := range tests { 224 testutil.Run(t, test.description, func(t *testutil.T) { 225 t.Override(&util.DefaultExecCommand, testutil.CmdRunOutErr( 226 strings.Join(getCommandMaven(ctx, tmpDir.Root(), &latest.JibArtifact{Project: "maven-test"}).Args, " "), 227 test.stdout, 228 test.err, 229 )) 230 231 // Change build file mod time 232 if err := os.Chtimes(build, test.modTime, test.modTime); err != nil { 233 t.Fatal(err) 234 } 235 ws := tmpDir.Root() 236 deps, err := getDependenciesMaven(ctx, ws, &latest.JibArtifact{Project: "maven-test"}) 237 if test.err != nil { 238 prefix := fmt.Sprintf("could not fetch dependencies for workspace %s: initial Jib dependency refresh failed: failed to get Jib dependencies: ", ws) 239 t.CheckErrorAndDeepEqual(true, err, prefix+test.err.Error(), err.Error()) 240 } else { 241 t.CheckDeepEqual(test.expected, deps) 242 } 243 }) 244 } 245 } 246 247 func TestGetCommandMaven(t *testing.T) { 248 ctx := context.Background() 249 tests := []struct { 250 description string 251 jibArtifact latest.JibArtifact 252 filesInWorkspace []string 253 expectedCmd func(workspace string) exec.Cmd 254 }{ 255 { 256 description: "maven basic", 257 jibArtifact: latest.JibArtifact{}, 258 filesInWorkspace: []string{}, 259 expectedCmd: func(workspace string) exec.Cmd { 260 return MavenCommand.CreateCommand(ctx, workspace, []string{"fake-mavenArgs", "jib:_skaffold-files-v2", "--quiet", "--batch-mode"}) 261 }, 262 }, 263 { 264 description: "maven with wrapper", 265 jibArtifact: latest.JibArtifact{}, 266 filesInWorkspace: []string{"mvnw", "mvnw.bat"}, 267 expectedCmd: func(workspace string) exec.Cmd { 268 return MavenCommand.CreateCommand(ctx, workspace, []string{"fake-mavenArgs", "jib:_skaffold-files-v2", "--quiet", "--batch-mode"}) 269 }, 270 }, 271 { 272 description: "maven with multi-modules", 273 jibArtifact: latest.JibArtifact{Project: "module"}, 274 filesInWorkspace: []string{"mvnw", "mvnw.bat"}, 275 expectedCmd: func(workspace string) exec.Cmd { 276 return MavenCommand.CreateCommand(ctx, workspace, []string{"fake-mavenArgs-for-module", "jib:_skaffold-files-v2", "--quiet", "--batch-mode"}) 277 }, 278 }, 279 } 280 for _, test := range tests { 281 testutil.Run(t, test.description, func(t *testutil.T) { 282 t.Override(&mavenArgsFunc, getMavenArgsFuncFake(t, MinimumJibMavenVersion)) 283 tmpDir := t.NewTempDir(). 284 Touch(test.filesInWorkspace...) 285 286 cmd := getCommandMaven(ctx, tmpDir.Root(), &test.jibArtifact) 287 288 expectedCmd := test.expectedCmd(tmpDir.Root()) 289 t.CheckDeepEqual(expectedCmd.Path, cmd.Path) 290 t.CheckDeepEqual(expectedCmd.Args, cmd.Args) 291 t.CheckDeepEqual(expectedCmd.Dir, cmd.Dir) 292 }) 293 } 294 } 295 296 func TestGetSyncMapCommandMaven(t *testing.T) { 297 ctx := context.Background() 298 tests := []struct { 299 description string 300 workspace string 301 jibArtifact latest.JibArtifact 302 expectedCmd func(workspace string) exec.Cmd 303 }{ 304 { 305 description: "single module", 306 jibArtifact: latest.JibArtifact{}, 307 expectedCmd: func(workspace string) exec.Cmd { 308 return MavenCommand.CreateCommand(ctx, workspace, []string{"fake-mavenBuildArgs-for-_skaffold-sync-map-skipTests"}) 309 }, 310 }, 311 { 312 description: "multi module", 313 jibArtifact: latest.JibArtifact{Project: "module"}, 314 expectedCmd: func(workspace string) exec.Cmd { 315 return MavenCommand.CreateCommand(ctx, workspace, []string{"fake-mavenBuildArgs-for-module-for-_skaffold-sync-map-skipTests"}) 316 }, 317 }, 318 } 319 for _, test := range tests { 320 testutil.Run(t, test.description, func(t *testutil.T) { 321 t.Override(&mavenBuildArgsFunc, getMavenBuildArgsFuncFake(t, MinimumJibMavenVersionForSync)) 322 cmd := getSyncMapCommandMaven(ctx, test.workspace, &test.jibArtifact) 323 expectedCmd := test.expectedCmd(test.workspace) 324 t.CheckDeepEqual(expectedCmd.Path, cmd.Path) 325 t.CheckDeepEqual(expectedCmd.Args, cmd.Args) 326 t.CheckDeepEqual(expectedCmd.Dir, cmd.Dir) 327 }) 328 } 329 } 330 331 func TestGenerateMavenBuildArgs(t *testing.T) { 332 tests := []struct { 333 description string 334 a latest.JibArtifact 335 platforms platform.Matcher 336 deps []*latest.ArtifactDependency 337 image string 338 expectedMinVersion string 339 r ArtifactResolver 340 skipTests bool 341 pushImages bool 342 insecureRegistries map[string]bool 343 out []string 344 }{ 345 {description: "single module", image: "image", out: []string{"fake-mavenBuildArgs-for-test-goal", "-Dimage=image"}}, 346 {description: "single module without tests", image: "image", skipTests: true, out: []string{"fake-mavenBuildArgs-for-test-goal-skipTests", "-Dimage=image"}}, 347 {description: "multi module", a: latest.JibArtifact{Project: "module"}, image: "image", out: []string{"fake-mavenBuildArgs-for-module-for-test-goal", "-Dimage=image"}}, 348 {description: "multi module without tests", a: latest.JibArtifact{Project: "module"}, image: "image", skipTests: true, out: []string{"fake-mavenBuildArgs-for-module-for-test-goal-skipTests", "-Dimage=image"}}, 349 {description: "multi module without tests with insecure-registry", a: latest.JibArtifact{Project: "module"}, image: "registry.tld/image", skipTests: true, insecureRegistries: map[string]bool{"registry.tld": true}, out: []string{"fake-mavenBuildArgs-for-module-for-test-goal-skipTests", "-Djib.allowInsecureRegistries=true", "-Dimage=registry.tld/image"}}, 350 {description: "single module with custom base image", a: latest.JibArtifact{BaseImage: "docker://busybox"}, image: "image", out: []string{"fake-mavenBuildArgs-for-test-goal", "-Djib.from.image=docker://busybox", "-Dimage=image"}}, 351 {description: "multi module with custom base image", a: latest.JibArtifact{Project: "module", BaseImage: "docker://busybox"}, image: "image", out: []string{"fake-mavenBuildArgs-for-module-for-test-goal", "-Djib.from.image=docker://busybox", "-Dimage=image"}}, 352 {description: "host platform", image: "image", platforms: platform.Matcher{Platforms: []v1.Platform{{OS: runtime.GOOS, Architecture: runtime.GOARCH}}}, out: []string{"fake-mavenBuildArgs-for-test-goal", fmt.Sprintf("-Djib.from.platforms=%s/%s", runtime.GOOS, runtime.GOARCH), "-Dimage=image"}}, 353 {description: "cross-platform", image: "image", platforms: platform.Matcher{Platforms: []v1.Platform{{OS: "freebsd", Architecture: "arm"}}}, out: []string{"fake-mavenBuildArgs-for-test-goal", "-Djib.from.platforms=freebsd/arm", "-Dimage=image"}, expectedMinVersion: MinimumJibMavenVersionForCrossPlatform}, 354 {description: "multi-platform", image: "image", platforms: platform.Matcher{Platforms: []v1.Platform{{OS: "linux", Architecture: "amd64"}, {OS: "darwin", Architecture: "arm64"}}}, out: []string{"fake-mavenBuildArgs-for-test-goal", "-Djib.from.platforms=linux/amd64,darwin/arm64", "-Dimage=image"}, expectedMinVersion: MinimumJibMavenVersionForCrossPlatform}, 355 { 356 description: "single module with local base image from required artifacts", 357 a: latest.JibArtifact{BaseImage: "alias"}, 358 deps: []*latest.ArtifactDependency{{ImageName: "img", Alias: "alias"}}, 359 image: "image", 360 r: mockArtifactResolver{m: map[string]string{"img": "img:tag"}}, 361 out: []string{"fake-mavenBuildArgs-for-test-goal", "-Djib.from.image=docker://img:tag", "-Dimage=image"}, 362 }, 363 { 364 description: "multi module with local base image from required artifacts", 365 a: latest.JibArtifact{Project: "module", BaseImage: "alias"}, 366 deps: []*latest.ArtifactDependency{{ImageName: "img", Alias: "alias"}}, 367 image: "image", 368 r: mockArtifactResolver{m: map[string]string{"img": "img:tag"}}, 369 out: []string{"fake-mavenBuildArgs-for-module-for-test-goal", "-Djib.from.image=docker://img:tag", "-Dimage=image"}, 370 }, 371 { 372 description: "single module with remote base image from required artifacts", 373 a: latest.JibArtifact{BaseImage: "alias"}, 374 deps: []*latest.ArtifactDependency{{ImageName: "img", Alias: "alias"}}, 375 image: "image", 376 pushImages: true, 377 r: mockArtifactResolver{m: map[string]string{"img": "img:tag"}}, 378 out: []string{"fake-mavenBuildArgs-for-test-goal", "-Djib.from.image=img:tag", "-Dimage=image"}, 379 }, 380 { 381 description: "multi module with remote base image from required artifacts", 382 a: latest.JibArtifact{Project: "module", BaseImage: "alias"}, 383 deps: []*latest.ArtifactDependency{{ImageName: "img", Alias: "alias"}}, 384 image: "image", 385 pushImages: true, 386 r: mockArtifactResolver{m: map[string]string{"img": "img:tag"}}, 387 out: []string{"fake-mavenBuildArgs-for-module-for-test-goal", "-Djib.from.image=img:tag", "-Dimage=image"}, 388 }, 389 } 390 for _, test := range tests { 391 testutil.Run(t, test.description, func(t *testutil.T) { 392 minVersion := MinimumJibMavenVersion 393 if test.expectedMinVersion != "" { 394 minVersion = test.expectedMinVersion 395 } 396 t.Override(&mavenBuildArgsFunc, getMavenBuildArgsFuncFake(t, minVersion)) 397 args := GenerateMavenBuildArgs("test-goal", test.image, &test.a, test.platforms, test.skipTests, test.pushImages, test.deps, test.r, test.insecureRegistries, false) 398 t.CheckDeepEqual(test.out, args) 399 }) 400 } 401 } 402 403 func TestMavenBuildArgs(t *testing.T) { 404 tests := []struct { 405 description string 406 jibArtifact latest.JibArtifact 407 skipTests bool 408 showColors bool 409 expected []string 410 }{ 411 { 412 description: "single module", 413 jibArtifact: latest.JibArtifact{}, 414 skipTests: false, 415 showColors: true, 416 expected: []string{"-Dstyle.color=always", "-Djansi.passthrough=true", "-Djib.console=plain", "fake-mavenArgs", "prepare-package", "jib:test-goal"}, 417 }, 418 { 419 description: "single module skip tests", 420 jibArtifact: latest.JibArtifact{}, 421 skipTests: true, 422 showColors: true, 423 expected: []string{"-Dstyle.color=always", "-Djansi.passthrough=true", "-Djib.console=plain", "fake-mavenArgs", "-DskipTests=true", "prepare-package", "jib:test-goal"}, 424 }, 425 { 426 description: "single module plain console", 427 jibArtifact: latest.JibArtifact{}, 428 skipTests: true, 429 showColors: false, 430 expected: []string{"--batch-mode", "fake-mavenArgs", "-DskipTests=true", "prepare-package", "jib:test-goal"}, 431 }, 432 { 433 description: "multi module", 434 jibArtifact: latest.JibArtifact{Project: "module"}, 435 skipTests: false, 436 showColors: true, 437 expected: []string{"-Dstyle.color=always", "-Djansi.passthrough=true", "-Djib.console=plain", "fake-mavenArgs-for-module", "package", "jib:test-goal", "-Djib.containerize=module"}, 438 }, 439 { 440 description: "single module skip tests", 441 jibArtifact: latest.JibArtifact{Project: "module"}, 442 skipTests: true, 443 showColors: true, 444 expected: []string{"-Dstyle.color=always", "-Djansi.passthrough=true", "-Djib.console=plain", "fake-mavenArgs-for-module", "-DskipTests=true", "package", "jib:test-goal", "-Djib.containerize=module"}, 445 }, 446 } 447 for _, test := range tests { 448 testutil.Run(t, test.description, func(t *testutil.T) { 449 t.Override(&mavenArgsFunc, getMavenArgsFuncFake(t, "test-version")) 450 args := mavenBuildArgs("test-goal", &test.jibArtifact, test.skipTests, test.showColors, "test-version") 451 t.CheckDeepEqual(test.expected, args) 452 }) 453 } 454 } 455 456 func TestMavenArgs(t *testing.T) { 457 tests := []struct { 458 description string 459 jibArtifact latest.JibArtifact 460 expected []string 461 }{ 462 { 463 description: "single module", 464 jibArtifact: latest.JibArtifact{}, 465 expected: []string{"jib:_skaffold-fail-if-jib-out-of-date", "-Djib.requiredVersion=test-version", "--non-recursive"}, 466 }, 467 { 468 description: "single module with extra flags", 469 jibArtifact: latest.JibArtifact{ 470 Flags: []string{"--flag1", "--flag2"}, 471 }, 472 expected: []string{"jib:_skaffold-fail-if-jib-out-of-date", "-Djib.requiredVersion=test-version", "--flag1", "--flag2", "--non-recursive"}, 473 }, 474 { 475 description: "multi module", 476 jibArtifact: latest.JibArtifact{Project: "module"}, 477 expected: []string{"jib:_skaffold-fail-if-jib-out-of-date", "-Djib.requiredVersion=test-version", "--projects", "module", "--also-make"}, 478 }, 479 { 480 description: "multi module with extra falgs", 481 jibArtifact: latest.JibArtifact{ 482 Project: "module", 483 Flags: []string{"--flag1", "--flag2"}, 484 }, 485 expected: []string{"jib:_skaffold-fail-if-jib-out-of-date", "-Djib.requiredVersion=test-version", "--flag1", "--flag2", "--projects", "module", "--also-make"}, 486 }, 487 } 488 for _, test := range tests { 489 args := mavenArgs(&test.jibArtifact, "test-version") 490 testutil.CheckDeepEqual(t, test.expected, args) 491 } 492 } 493 494 func getMavenArgsFuncFake(t *testutil.T, expectedMinimumVersion string) func(*latest.JibArtifact, string) []string { 495 return func(a *latest.JibArtifact, minimumVersion string) []string { 496 t.CheckDeepEqual(expectedMinimumVersion, minimumVersion) 497 if a.Project == "" { 498 return []string{"fake-mavenArgs"} 499 } 500 return []string{"fake-mavenArgs-for-" + a.Project} 501 } 502 } 503 504 func getMavenBuildArgsFuncFake(t *testutil.T, expectedMinimumVersion string) func(string, *latest.JibArtifact, bool, bool, string) []string { 505 return func(goal string, a *latest.JibArtifact, skipTests, showColors bool, minimumVersion string) []string { 506 t.CheckDeepEqual(expectedMinimumVersion, minimumVersion) 507 testString := "" 508 if skipTests { 509 testString = "-skipTests" 510 } 511 512 if a.Project == "" { 513 return []string{"fake-mavenBuildArgs-for-" + goal + testString} 514 } 515 return []string{"fake-mavenBuildArgs-for-" + a.Project + "-for-" + goal + testString} 516 } 517 }