| /* |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| package builder |
| |
| import ( |
| "io/ioutil" |
| "os" |
| "path" |
| "sort" |
| "time" |
| |
| v1 "github.com/apache/camel-k/pkg/apis/camel/v1" |
| "github.com/apache/camel-k/pkg/client" |
| "github.com/apache/camel-k/pkg/util/cancellable" |
| "github.com/apache/camel-k/pkg/util/log" |
| ) |
| |
| type defaultBuilder struct { |
| log log.Logger |
| ctx cancellable.Context |
| client client.Client |
| } |
| |
| // New -- |
| func New(c client.Client) Builder { |
| m := defaultBuilder{ |
| log: log.WithName("builder"), |
| ctx: cancellable.NewContext(), |
| client: c, |
| } |
| |
| return &m |
| } |
| |
| // Run -- |
| func (b *defaultBuilder) Run(build v1.BuilderTask) v1.BuildStatus { |
| result := v1.BuildStatus{} |
| |
| var buildDir string |
| if build.BuildDir == "" { |
| tmpDir, err := ioutil.TempDir(os.TempDir(), "builder-") |
| if err != nil { |
| log.Error(err, "Unexpected error while creating a temporary dir") |
| |
| result.Phase = v1.BuildPhaseFailed |
| result.Error = err.Error() |
| } |
| buildDir = tmpDir |
| defer os.RemoveAll(buildDir) |
| } else { |
| buildDir = build.BuildDir |
| } |
| |
| c := Context{ |
| Client: b.client, |
| C: b.ctx, |
| Path: buildDir, |
| Namespace: build.Meta.Namespace, |
| Build: build, |
| BaseImage: build.BaseImage, |
| } |
| |
| if build.Image != "" { |
| c.BaseImage = build.Image |
| } |
| |
| // base image is mandatory |
| if c.BaseImage == "" { |
| result.Phase = v1.BuildPhaseFailed |
| result.Image = "" |
| result.Error = "no base image defined" |
| } |
| |
| // Add sources |
| for _, data := range build.Sources { |
| c.Resources = append(c.Resources, Resource{ |
| Content: []byte(data.Content), |
| Target: path.Join("sources", data.Name), |
| }) |
| } |
| |
| // Add resources |
| for _, data := range build.Resources { |
| t := path.Join("resources", data.Name) |
| |
| if data.MountPath != "" { |
| t = path.Join(data.MountPath, data.Name) |
| } |
| |
| c.Resources = append(c.Resources, Resource{ |
| Content: []byte(data.Content), |
| Target: t, |
| }) |
| } |
| |
| if result.Phase == v1.BuildPhaseFailed { |
| return result |
| } |
| |
| steps := make([]Step, 0) |
| for _, step := range build.Steps { |
| s, ok := stepsByID[step] |
| if !ok { |
| log.Info("Skipping unknown build step", "step", step) |
| continue |
| } |
| steps = append(steps, s) |
| } |
| // Sort steps by phase |
| sort.SliceStable(steps, func(i, j int) bool { |
| return steps[i].Phase() < steps[j].Phase() |
| }) |
| |
| b.log.Infof("steps: %v", steps) |
| for _, step := range steps { |
| if c.Error != nil || result.Phase == v1.BuildPhaseInterrupted { |
| break |
| } |
| |
| select { |
| case <-b.ctx.Done(): |
| result.Phase = v1.BuildPhaseInterrupted |
| default: |
| l := b.log.WithValues( |
| "step", step.ID(), |
| "phase", step.Phase(), |
| "name", build.Meta.Name, |
| "task", build.Name, |
| ) |
| |
| l.Infof("executing step") |
| |
| start := time.Now() |
| c.Error = step.Execute(&c) |
| |
| if c.Error == nil { |
| l.Infof("step done in %f seconds", time.Since(start).Seconds()) |
| } else { |
| l.Infof("step failed with error: %s", c.Error) |
| } |
| } |
| } |
| |
| if result.Phase != v1.BuildPhaseInterrupted { |
| result.BaseImage = c.BaseImage |
| result.Image = c.Image |
| result.Digest = c.Digest |
| |
| if c.Error != nil { |
| result.Error = c.Error.Error() |
| result.Phase = v1.BuildPhaseFailed |
| } |
| |
| result.Artifacts = make([]v1.Artifact, 0, len(c.Artifacts)) |
| result.Artifacts = append(result.Artifacts, c.Artifacts...) |
| |
| b.log.Infof("dependencies: %s", build.Dependencies) |
| b.log.Infof("artifacts: %s", artifactIDs(c.Artifacts)) |
| b.log.Infof("artifacts selected: %s", artifactIDs(c.SelectedArtifacts)) |
| b.log.Infof("base image: %s", build.BaseImage) |
| b.log.Infof("resolved base image: %s", c.BaseImage) |
| b.log.Infof("resolved image: %s", c.Image) |
| } else { |
| b.log.Infof("build task %s interrupted", build.Name) |
| } |
| |
| return result |
| } |