github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/engine/build_step_delegate.go (about) 1 package engine 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "io" 8 "strings" 9 "time" 10 11 "code.cloudfoundry.org/clock" 12 "code.cloudfoundry.org/lager" 13 "github.com/pf-qiu/concourse/v6/atc" 14 "github.com/pf-qiu/concourse/v6/atc/db" 15 "github.com/pf-qiu/concourse/v6/atc/event" 16 "github.com/pf-qiu/concourse/v6/atc/exec" 17 "github.com/pf-qiu/concourse/v6/atc/exec/build" 18 "github.com/pf-qiu/concourse/v6/atc/policy" 19 "github.com/pf-qiu/concourse/v6/atc/worker" 20 "github.com/pf-qiu/concourse/v6/tracing" 21 "go.opentelemetry.io/otel/api/trace" 22 ) 23 24 type buildStepDelegate struct { 25 build db.Build 26 planID atc.PlanID 27 clock clock.Clock 28 state exec.RunState 29 stderr io.Writer 30 stdout io.Writer 31 policyChecker policy.Checker 32 } 33 34 func NewBuildStepDelegate( 35 build db.Build, 36 planID atc.PlanID, 37 state exec.RunState, 38 clock clock.Clock, 39 policyChecker policy.Checker, 40 ) *buildStepDelegate { 41 return &buildStepDelegate{ 42 build: build, 43 planID: planID, 44 clock: clock, 45 state: state, 46 stdout: nil, 47 stderr: nil, 48 policyChecker: policyChecker, 49 } 50 } 51 52 func (delegate *buildStepDelegate) StartSpan( 53 ctx context.Context, 54 component string, 55 extraAttrs tracing.Attrs, 56 ) (context.Context, trace.Span) { 57 attrs := delegate.build.TracingAttrs() 58 for k, v := range extraAttrs { 59 attrs[k] = v 60 } 61 62 return tracing.StartSpan(ctx, component, attrs) 63 } 64 65 type credVarsIterator struct { 66 line string 67 } 68 69 func (it *credVarsIterator) YieldCred(name, value string) { 70 for _, lineValue := range strings.Split(value, "\n") { 71 lineValue = strings.TrimSpace(lineValue) 72 // Don't consider a single char as a secret. 73 if len(lineValue) > 1 { 74 it.line = strings.Replace(it.line, lineValue, "((redacted))", -1) 75 } 76 } 77 } 78 79 func (delegate *buildStepDelegate) Stdout() io.Writer { 80 if delegate.stdout != nil { 81 return delegate.stdout 82 } 83 if delegate.state.RedactionEnabled() { 84 delegate.stdout = newDBEventWriterWithSecretRedaction( 85 delegate.build, 86 event.Origin{ 87 Source: event.OriginSourceStdout, 88 ID: event.OriginID(delegate.planID), 89 }, 90 delegate.clock, 91 delegate.buildOutputFilter, 92 ) 93 } else { 94 delegate.stdout = newDBEventWriter( 95 delegate.build, 96 event.Origin{ 97 Source: event.OriginSourceStdout, 98 ID: event.OriginID(delegate.planID), 99 }, 100 delegate.clock, 101 ) 102 } 103 return delegate.stdout 104 } 105 106 func (delegate *buildStepDelegate) Stderr() io.Writer { 107 if delegate.stderr != nil { 108 return delegate.stderr 109 } 110 if delegate.state.RedactionEnabled() { 111 delegate.stderr = newDBEventWriterWithSecretRedaction( 112 delegate.build, 113 event.Origin{ 114 Source: event.OriginSourceStderr, 115 ID: event.OriginID(delegate.planID), 116 }, 117 delegate.clock, 118 delegate.buildOutputFilter, 119 ) 120 } else { 121 delegate.stderr = newDBEventWriter( 122 delegate.build, 123 event.Origin{ 124 Source: event.OriginSourceStderr, 125 ID: event.OriginID(delegate.planID), 126 }, 127 delegate.clock, 128 ) 129 } 130 return delegate.stderr 131 } 132 133 func (delegate *buildStepDelegate) Initializing(logger lager.Logger) { 134 err := delegate.build.SaveEvent(event.Initialize{ 135 Origin: event.Origin{ 136 ID: event.OriginID(delegate.planID), 137 }, 138 Time: time.Now().Unix(), 139 }) 140 if err != nil { 141 logger.Error("failed-to-save-initialize-event", err) 142 return 143 } 144 145 logger.Info("initializing") 146 } 147 148 func (delegate *buildStepDelegate) Starting(logger lager.Logger) { 149 err := delegate.build.SaveEvent(event.Start{ 150 Origin: event.Origin{ 151 ID: event.OriginID(delegate.planID), 152 }, 153 Time: time.Now().Unix(), 154 }) 155 if err != nil { 156 logger.Error("failed-to-save-start-event", err) 157 return 158 } 159 160 logger.Debug("starting") 161 } 162 163 func (delegate *buildStepDelegate) Finished(logger lager.Logger, succeeded bool) { 164 // PR#4398: close to flush stdout and stderr 165 delegate.Stdout().(io.Closer).Close() 166 delegate.Stderr().(io.Closer).Close() 167 168 err := delegate.build.SaveEvent(event.Finish{ 169 Origin: event.Origin{ 170 ID: event.OriginID(delegate.planID), 171 }, 172 Time: time.Now().Unix(), 173 Succeeded: succeeded, 174 }) 175 if err != nil { 176 logger.Error("failed-to-save-finish-event", err) 177 return 178 } 179 180 logger.Info("finished") 181 } 182 183 func (delegate *buildStepDelegate) SelectedWorker(logger lager.Logger, workerName string) { 184 err := delegate.build.SaveEvent(event.SelectedWorker{ 185 Time: time.Now().Unix(), 186 Origin: event.Origin{ 187 ID: event.OriginID(delegate.planID), 188 }, 189 WorkerName: workerName, 190 }) 191 if err != nil { 192 logger.Error("failed-to-save-selected-worker-event", err) 193 return 194 } 195 } 196 197 func (delegate *buildStepDelegate) Errored(logger lager.Logger, message string) { 198 err := delegate.build.SaveEvent(event.Error{ 199 Message: message, 200 Origin: event.Origin{ 201 ID: event.OriginID(delegate.planID), 202 }, 203 Time: delegate.clock.Now().Unix(), 204 }) 205 if err != nil { 206 logger.Error("failed-to-save-error-event", err) 207 } 208 } 209 210 // Name of the artifact fetched when using image_resource. Note that this only 211 // exists within a local scope, so it doesn't pollute the build state. 212 const defaultImageName = "image" 213 214 func (delegate *buildStepDelegate) FetchImage( 215 ctx context.Context, 216 image atc.ImageResource, 217 types atc.VersionedResourceTypes, 218 privileged bool, 219 ) (worker.ImageSpec, error) { 220 err := delegate.checkImagePolicy(image, privileged) 221 if err != nil { 222 return worker.ImageSpec{}, err 223 } 224 225 fetchState := delegate.state.NewLocalScope() 226 227 imageName := defaultImageName 228 if image.Name != "" { 229 imageName = image.Name 230 } 231 232 version := image.Version 233 if version == nil { 234 checkID := delegate.planID + "/image-check" 235 236 checkPlan := atc.Plan{ 237 ID: checkID, 238 Check: &atc.CheckPlan{ 239 Name: imageName, 240 Type: image.Type, 241 Source: image.Source, 242 243 VersionedResourceTypes: types, 244 245 Tags: image.Tags, 246 }, 247 } 248 249 err := delegate.build.SaveEvent(event.ImageCheck{ 250 Time: delegate.clock.Now().Unix(), 251 Origin: event.Origin{ 252 ID: event.OriginID(delegate.planID), 253 }, 254 PublicPlan: checkPlan.Public(), 255 }) 256 if err != nil { 257 return worker.ImageSpec{}, fmt.Errorf("save image check event: %w", err) 258 } 259 260 ok, err := fetchState.Run(ctx, checkPlan) 261 if err != nil { 262 return worker.ImageSpec{}, err 263 } 264 265 if !ok { 266 return worker.ImageSpec{}, fmt.Errorf("image check failed") 267 } 268 269 if !fetchState.Result(checkID, &version) { 270 return worker.ImageSpec{}, fmt.Errorf("check did not return a version") 271 } 272 } 273 274 getID := delegate.planID + "/image-get" 275 276 getPlan := atc.Plan{ 277 ID: getID, 278 Get: &atc.GetPlan{ 279 Name: imageName, 280 Type: image.Type, 281 Source: image.Source, 282 Version: &version, 283 Params: image.Params, 284 285 VersionedResourceTypes: types, 286 287 Tags: image.Tags, 288 }, 289 } 290 291 err = delegate.build.SaveEvent(event.ImageGet{ 292 Time: delegate.clock.Now().Unix(), 293 Origin: event.Origin{ 294 ID: event.OriginID(delegate.planID), 295 }, 296 PublicPlan: getPlan.Public(), 297 }) 298 if err != nil { 299 return worker.ImageSpec{}, fmt.Errorf("save image get event: %w", err) 300 } 301 302 ok, err := fetchState.Run(ctx, getPlan) 303 if err != nil { 304 return worker.ImageSpec{}, err 305 } 306 307 if !ok { 308 return worker.ImageSpec{}, fmt.Errorf("image fetching failed") 309 } 310 311 var cache db.UsedResourceCache 312 if !fetchState.Result(getID, &cache) { 313 return worker.ImageSpec{}, fmt.Errorf("get did not return a cache") 314 } 315 316 err = delegate.build.SaveImageResourceVersion(cache) 317 if err != nil { 318 return worker.ImageSpec{}, fmt.Errorf("save image version: %w", err) 319 } 320 321 art, found := fetchState.ArtifactRepository().ArtifactFor(build.ArtifactName(imageName)) 322 if !found { 323 return worker.ImageSpec{}, fmt.Errorf("fetched artifact not found") 324 } 325 326 return worker.ImageSpec{ 327 ImageArtifact: art, 328 Privileged: privileged, 329 }, nil 330 } 331 332 func (delegate *buildStepDelegate) checkImagePolicy(image atc.ImageResource, privileged bool) error { 333 if !delegate.policyChecker.ShouldCheckAction(policy.ActionUseImage) { 334 return nil 335 } 336 337 redactedSource, err := delegate.redactImageSource(image.Source) 338 if err != nil { 339 return fmt.Errorf("redact source: %w", err) 340 } 341 342 result, err := delegate.policyChecker.Check(policy.PolicyCheckInput{ 343 Action: policy.ActionUseImage, 344 Team: delegate.build.TeamName(), 345 Pipeline: delegate.build.PipelineName(), 346 Data: map[string]interface{}{ 347 "image_type": image.Type, 348 "image_source": redactedSource, 349 "privileged": privileged, 350 }, 351 }) 352 if err != nil { 353 return fmt.Errorf("perform check: %w", err) 354 } 355 356 if !result.Allowed { 357 return policy.PolicyCheckNotPass{ 358 Reasons: result.Reasons, 359 } 360 } 361 362 return nil 363 } 364 365 func (delegate *buildStepDelegate) buildOutputFilter(str string) string { 366 it := &credVarsIterator{line: str} 367 delegate.state.IterateInterpolatedCreds(it) 368 return it.line 369 } 370 371 func (delegate *buildStepDelegate) redactImageSource(source atc.Source) (atc.Source, error) { 372 b, err := json.Marshal(&source) 373 if err != nil { 374 return source, err 375 } 376 s := delegate.buildOutputFilter(string(b)) 377 newSource := atc.Source{} 378 err = json.Unmarshal([]byte(s), &newSource) 379 if err != nil { 380 return source, err 381 } 382 return newSource, nil 383 }