github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/test/test_factory.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 test 18 19 import ( 20 "bytes" 21 "context" 22 "fmt" 23 "io" 24 25 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" 26 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" 27 eventV2 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event/v2" 28 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph" 29 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/logfile" 30 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/output" 31 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" 32 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/test/custom" 33 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/test/structure" 34 ) 35 36 type Config interface { 37 docker.Config 38 39 TestCases() []*latest.TestCase 40 Muted() config.Muted 41 } 42 43 // NewTester parses the provided test cases from the Skaffold config, 44 // and returns a Tester instance with all the necessary test runners 45 // to run all specified tests. 46 func NewTester(ctx context.Context, cfg Config, imagesAreLocal func(imageName string) (bool, error)) (Tester, error) { 47 testers, err := getImageTesters(ctx, cfg, imagesAreLocal, cfg.TestCases()) 48 if err != nil { 49 return nil, err 50 } 51 52 return FullTester{ 53 Testers: testers, 54 muted: cfg.Muted(), 55 }, nil 56 } 57 58 // TestDependencies returns the watch dependencies for the target artifact to the runner. 59 func (t FullTester) TestDependencies(ctx context.Context, artifact *latest.Artifact) ([]string, error) { 60 var deps []string 61 for _, tester := range t.Testers[artifact.ImageName] { 62 result, err := tester.TestDependencies(ctx) 63 if err != nil { 64 return nil, err 65 } 66 deps = append(deps, result...) 67 } 68 return deps, nil 69 } 70 71 // Test is the top level testing execution call. It serves as the 72 // entrypoint to all individual tests. 73 func (t FullTester) Test(ctx context.Context, out io.Writer, bRes []graph.Artifact) error { 74 if len(t.Testers) == 0 { 75 return nil 76 } 77 78 output.Default.Fprintln(out, "Testing images...") 79 80 if t.muted.MuteTest() { 81 file, err := logfile.Create("test.log") 82 if err != nil { 83 return fmt.Errorf("unable to create log file for tests: %w", err) 84 } 85 fmt.Fprintln(out, " - writing logs to", file.Name()) 86 87 // Print logs to a memory buffer and to a file. 88 var buf bytes.Buffer 89 w := io.MultiWriter(file, &buf) 90 91 // Run the tests. 92 err = t.runTests(ctx, w, bRes) 93 94 // After the test finish, close the log file. If the tests failed, print the full log to the console. 95 file.Close() 96 if err != nil { 97 buf.WriteTo(out) 98 } 99 100 return err 101 } 102 103 if err := t.runTests(ctx, out, bRes); err != nil { 104 return err 105 } 106 107 return nil 108 } 109 110 func (t FullTester) runTests(ctx context.Context, out io.Writer, bRes []graph.Artifact) error { 111 testerID := 0 112 for _, b := range bRes { 113 for _, tester := range t.Testers[b.ImageName] { 114 eventV2.TesterInProgress(testerID) 115 if err := tester.Test(ctx, out, b.Tag); err != nil { 116 eventV2.TesterFailed(testerID, err) 117 return fmt.Errorf("running tests: %w", err) 118 } 119 eventV2.TesterSucceeded(testerID) 120 testerID++ 121 } 122 } 123 return nil 124 } 125 126 func getImageTesters(ctx context.Context, cfg docker.Config, imagesAreLocal func(imageName string) (bool, error), tcs []*latest.TestCase) (ImageTesters, error) { 127 runners := make(map[string][]ImageTester) 128 for _, tc := range tcs { 129 isLocal, err := imagesAreLocal(tc.ImageName) 130 if err != nil { 131 return nil, err 132 } 133 134 if len(tc.StructureTests) != 0 { 135 structureRunner, err := structure.New(ctx, cfg, tc, isLocal) 136 if err != nil { 137 return nil, err 138 } 139 runners[tc.ImageName] = append(runners[tc.ImageName], structureRunner) 140 } 141 142 for _, customTest := range tc.CustomTests { 143 customRunner, err := custom.New(cfg, tc.ImageName, tc.Workspace, customTest) 144 if err != nil { 145 return nil, err 146 } 147 runners[tc.ImageName] = append(runners[tc.ImageName], customRunner) 148 } 149 } 150 return runners, nil 151 }