github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/runner/v1/new.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 v1 18 19 import ( 20 "context" 21 "fmt" 22 23 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" 24 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/cache" 25 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy" 26 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/label" 27 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" 28 eventV2 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event/v2" 29 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/filemon" 30 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph" 31 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/instrumentation" 32 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log" 33 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/platform" 34 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner" 35 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext" 36 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" 37 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/server" 38 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/tag" 39 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/test" 40 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/trigger" 41 ) 42 43 // NewForConfig returns a new SkaffoldRunner for a SkaffoldConfig 44 func NewForConfig(ctx context.Context, runCtx *runcontext.RunContext) (*SkaffoldRunner, error) { 45 event.InitializeState(runCtx) 46 event.LogMetaEvent() 47 eventV2.InitializeState(runCtx) 48 eventV2.LogMetaEvent() 49 _, endTrace := instrumentation.StartTrace(context.Background(), "NewForConfig") 50 defer endTrace() 51 52 tagger, err := tag.NewTaggerMux(runCtx) 53 if err != nil { 54 endTrace(instrumentation.TraceEndError(err)) 55 return nil, fmt.Errorf("creating tagger: %w", err) 56 } 57 58 store := build.NewArtifactStore() 59 g := graph.ToArtifactGraph(runCtx.Artifacts()) 60 sourceDependencies := graph.NewSourceDependenciesCache(runCtx, store, g) 61 62 isLocalImage := func(imageName string) (bool, error) { 63 return isImageLocal(runCtx, imageName) 64 } 65 66 // Always add skaffold-specific labels, except during `skaffold render` 67 labeller := label.NewLabeller(runCtx.AddSkaffoldLabels(), runCtx.CustomLabels(), runCtx.GetRunID()) 68 tester, err := getTester(ctx, runCtx, isLocalImage) 69 if err != nil { 70 endTrace(instrumentation.TraceEndError(err)) 71 return nil, fmt.Errorf("creating tester: %w", err) 72 } 73 74 var deployer deploy.Deployer 75 deployer, err = runner.GetDeployer(ctx, runCtx, labeller) 76 if err != nil { 77 endTrace(instrumentation.TraceEndError(err)) 78 return nil, fmt.Errorf("creating deployer: %w", err) 79 } 80 platforms, err := platform.NewResolver(ctx, runCtx.Pipelines.All(), runCtx.Opts.Platforms, runCtx.Mode(), runCtx.KubeContext) 81 if err != nil { 82 endTrace(instrumentation.TraceEndError(err)) 83 return nil, fmt.Errorf("getting target platforms: %w", err) 84 } 85 // The Builder must be instantiated AFTER the Deployer, because the Deploy target influences 86 // the Cluster object on the RunContext, which in turn influences whether or not we will push images. 87 var builder build.Builder 88 builder, err = build.NewBuilderMux(runCtx, store, func(p latest.Pipeline) (build.PipelineBuilder, error) { 89 return runner.GetBuilder(ctx, runCtx, store, sourceDependencies, p) 90 }) 91 if err != nil { 92 endTrace(instrumentation.TraceEndError(err)) 93 return nil, fmt.Errorf("creating builder: %w", err) 94 } 95 96 depLister := func(ctx context.Context, artifact *latest.Artifact) ([]string, error) { 97 ctx, endTrace := instrumentation.StartTrace(ctx, "NewForConfig_depLister") 98 defer endTrace() 99 100 buildDependencies, err := sourceDependencies.SingleArtifactDependencies(ctx, artifact) 101 if err != nil { 102 endTrace(instrumentation.TraceEndError(err)) 103 return nil, err 104 } 105 106 testDependencies, err := tester.TestDependencies(ctx, artifact) 107 if err != nil { 108 endTrace(instrumentation.TraceEndError(err)) 109 return nil, err 110 } 111 return append(buildDependencies, testDependencies...), nil 112 } 113 114 artifactCache, err := cache.NewCache(ctx, runCtx, isLocalImage, depLister, g, store) 115 if err != nil { 116 endTrace(instrumentation.TraceEndError(err)) 117 return nil, fmt.Errorf("initializing cache: %w", err) 118 } 119 120 builder, tester, deployer = runner.WithTimings(builder, tester, deployer, runCtx.CacheArtifacts()) 121 if runCtx.Notification() { 122 deployer = runner.WithNotification(deployer) 123 } 124 125 monitor := filemon.NewMonitor() 126 intents, intentChan := setupIntents(runCtx) 127 rtrigger, err := trigger.NewTrigger(runCtx, intents.IsAnyAutoEnabled) 128 if err != nil { 129 endTrace(instrumentation.TraceEndError(err)) 130 return nil, fmt.Errorf("creating watch trigger: %w", err) 131 } 132 133 rbuilder := runner.NewBuilder(builder, tagger, platforms, artifactCache, runCtx) 134 return &SkaffoldRunner{ 135 Builder: *rbuilder, 136 Pruner: runner.Pruner{Builder: builder}, 137 tester: tester, 138 deployer: deployer, 139 platforms: platforms, 140 monitor: monitor, 141 listener: runner.NewSkaffoldListener(monitor, rtrigger, sourceDependencies, intentChan), 142 artifactStore: store, 143 sourceDependencies: sourceDependencies, 144 labeller: labeller, 145 cache: artifactCache, 146 runCtx: runCtx, 147 intents: intents, 148 isLocalImage: isLocalImage, 149 }, nil 150 } 151 152 func setupIntents(runCtx *runcontext.RunContext) (*runner.Intents, chan bool) { 153 intents := runner.NewIntents(runCtx.AutoBuild(), runCtx.AutoSync(), runCtx.AutoDeploy()) 154 155 intentChan := make(chan bool, 1) 156 setupTrigger("build", intents.SetBuild, intents.SetAutoBuild, intents.GetAutoBuild, server.SetBuildCallback, server.SetAutoBuildCallback, intentChan) 157 setupTrigger("sync", intents.SetSync, intents.SetAutoSync, intents.GetAutoSync, server.SetSyncCallback, server.SetAutoSyncCallback, intentChan) 158 setupTrigger("deploy", intents.SetDeploy, intents.SetAutoDeploy, intents.GetAutoDeploy, server.SetDeployCallback, server.SetAutoDeployCallback, intentChan) 159 // Setup callback function to buildCallback since build is the start of the devloop. 160 setupTrigger("devloop", intents.SetDevloop, intents.SetAutoDevloop, intents.GetAutoDevloop, server.SetDevloopCallback, server.SetAutoDevloopCallback, intentChan) 161 162 return intents, intentChan 163 } 164 165 func setupTrigger(triggerName string, setIntent func(bool), setAutoTrigger func(bool), getAutoTrigger func() bool, singleTriggerCallback func(func()), autoTriggerCallback func(func(bool)), c chan<- bool) { 166 setIntent(getAutoTrigger()) 167 // give the server a callback to set the intent value when a user request is received 168 singleTriggerCallback(func() { 169 if !getAutoTrigger() { // if auto trigger is disabled, we're in manual mode 170 log.Entry(context.TODO()).Debugf("%s intent received, calling back to runner", triggerName) 171 c <- true 172 setIntent(true) 173 } 174 }) 175 176 // give the server a callback to update auto trigger value when a user request is received 177 autoTriggerCallback(func(val bool) { 178 log.Entry(context.TODO()).Debugf("%s auto trigger update to %t received, calling back to runner", triggerName, val) 179 // signal chan only when auto trigger is set to true 180 if val { 181 c <- true 182 } 183 setAutoTrigger(val) 184 setIntent(val) 185 }) 186 } 187 188 func isImageLocal(runCtx *runcontext.RunContext, imageName string) (bool, error) { 189 pipeline, found := runCtx.PipelineForImage(imageName) 190 if !found { 191 pipeline = runCtx.DefaultPipeline() 192 } 193 if pipeline.Build.GoogleCloudBuild != nil || pipeline.Build.Cluster != nil { 194 return false, nil 195 } 196 197 // if we're deploying to local Docker, all images must be local 198 if pipeline.Deploy.DockerDeploy != nil { 199 return true, nil 200 } 201 202 cl := runCtx.GetCluster() 203 var pushImages bool 204 205 switch { 206 case runCtx.Opts.PushImages.Value() != nil: 207 log.Entry(context.TODO()).Debugf("push value set via skaffold build --push flag, --push=%t", *runCtx.Opts.PushImages.Value()) 208 pushImages = *runCtx.Opts.PushImages.Value() 209 case pipeline.Build.LocalBuild.Push == nil: 210 pushImages = cl.PushImages 211 log.Entry(context.TODO()).Debugf("push value not present in isImageLocal(), defaulting to %t because cluster.PushImages is %t", pushImages, cl.PushImages) 212 default: 213 pushImages = *pipeline.Build.LocalBuild.Push 214 } 215 return !pushImages, nil 216 } 217 218 func getTester(ctx context.Context, cfg test.Config, isLocalImage func(imageName string) (bool, error)) (test.Tester, error) { 219 tester, err := test.NewTester(ctx, cfg, isLocalImage) 220 if err != nil { 221 return nil, err 222 } 223 224 return tester, nil 225 }