github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/test/structure/structure.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 structure 18 19 import ( 20 "context" 21 "fmt" 22 "io" 23 "os" 24 "os/exec" 25 26 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" 27 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" 28 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log" 29 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" 30 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" 31 ) 32 33 type Runner struct { 34 structureTests []string 35 structureTestArgs []string 36 imageName string 37 imageIsLocal bool 38 workspace string 39 localDaemon docker.LocalDaemon 40 } 41 42 // New creates a new structure.Runner. 43 func New(ctx context.Context, cfg docker.Config, tc *latest.TestCase, imageIsLocal bool) (*Runner, error) { 44 localDaemon, err := docker.NewAPIClient(ctx, cfg) 45 if err != nil { 46 return nil, err 47 } 48 return &Runner{ 49 structureTests: tc.StructureTests, 50 structureTestArgs: tc.StructureTestArgs, 51 imageName: tc.ImageName, 52 workspace: tc.Workspace, 53 localDaemon: localDaemon, 54 imageIsLocal: imageIsLocal, 55 }, nil 56 } 57 58 // Test is the entrypoint for running structure tests 59 func (cst *Runner) Test(ctx context.Context, out io.Writer, imageTag string) error { 60 event.TestInProgress() 61 if err := cst.runStructureTests(ctx, out, imageTag); err != nil { 62 event.TestFailed(cst.imageName, err) 63 return containerStructureTestErr(err) 64 } 65 event.TestComplete() 66 return nil 67 } 68 69 func (cst *Runner) runStructureTests(ctx context.Context, out io.Writer, imageTag string) error { 70 if !cst.imageIsLocal { 71 // The image is remote so we have to pull it locally. 72 // `container-structure-test` currently can't do it: 73 // https://github.com/GoogleContainerTools/container-structure-test/issues/253. 74 if err := cst.localDaemon.Pull(ctx, out, imageTag); err != nil { 75 return dockerPullImageErr(imageTag, err) 76 } 77 } 78 79 files, err := cst.TestDependencies(ctx) 80 if err != nil { 81 return err 82 } 83 84 log.Entry(ctx).Infof("Running structure tests for files %v", files) 85 86 args := []string{"test", "-v", "warn", "--image", imageTag} 87 for _, f := range files { 88 args = append(args, "--config", f) 89 } 90 args = append(args, cst.structureTestArgs...) 91 cmd := exec.CommandContext(ctx, "container-structure-test", args...) 92 cmd.Stdout = out 93 cmd.Stderr = out 94 cmd.Env = cst.env() 95 96 if err := util.RunCmd(ctx, cmd); err != nil { 97 return fmt.Errorf("error running container-structure-test command: %w", err) 98 } 99 100 return nil 101 } 102 103 // TestDependencies returns dependencies listed for the structure tests 104 func (cst *Runner) TestDependencies(context.Context) ([]string, error) { 105 files, err := util.ExpandPathsGlob(cst.workspace, cst.structureTests) 106 if err != nil { 107 return nil, expandingFilePathsErr(err) 108 } 109 110 return files, nil 111 } 112 113 // env returns a merged environment of the current process environment and any extra environment. 114 // This ensures that the correct docker environment configuration is passed to container-structure-test, 115 // for example when running on minikube. 116 func (cst *Runner) env() []string { 117 extraEnv := cst.localDaemon.ExtraEnv() 118 if extraEnv == nil { 119 return nil 120 } 121 122 parentEnv := os.Environ() 123 mergedEnv := make([]string, len(parentEnv), len(parentEnv)+len(extraEnv)) 124 copy(mergedEnv, parentEnv) 125 return append(mergedEnv, extraEnv...) 126 }