This is an automated email from the ASF dual-hosted git repository. lburgazzoli pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel-k.git
commit 4390c77a9c9a79e1752dd838a2793f0d247c4f2c Author: nferraro <ni.ferr...@gmail.com> AuthorDate: Fri Jan 11 11:41:20 2019 +0100 Fix #312: refactor platform image lookup into its own package --- cmd/util/publisher/publisher.go | 20 +++--- pkg/builder/builder.go | 5 -- pkg/builder/builder_steps.go | 49 ------------- pkg/builder/kaniko/kaniko.go | 1 - pkg/builder/s2i/s2i.go | 1 - pkg/controller/integration/build_context.go | 20 ++++-- pkg/controller/integration/util.go | 44 ++++++++++-- pkg/{builder/s2i/s2i.go => platform/images/doc.go} | 17 +---- pkg/platform/images/images.go | 82 ++++++++++++++++++++++ pkg/platform/images/images_test.go | 68 ++++++++++++++++++ pkg/trait/builder_test.go | 4 +- 11 files changed, 219 insertions(+), 92 deletions(-) diff --git a/cmd/util/publisher/publisher.go b/cmd/util/publisher/publisher.go index 857bbc0..e4dc8c3 100644 --- a/cmd/util/publisher/publisher.go +++ b/cmd/util/publisher/publisher.go @@ -33,6 +33,7 @@ import ( "github.com/apache/camel-k/pkg/apis" "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" "github.com/apache/camel-k/pkg/builder" + "github.com/apache/camel-k/pkg/platform/images" "github.com/apache/camel-k/pkg/util/camel" "github.com/apache/camel-k/pkg/util/kubernetes" "github.com/pkg/errors" @@ -60,7 +61,7 @@ func main() { cmd.Flags().StringVar(&options.StartWith, "start-with", "", "The component to start with") cmd.Flags().StringVar(&options.EndWith, "end-with", "", "The component to end with") - cmd.Flags().IntVar(&options.BuildAttempts, "attempts", 5, "The maximum number of build attempts") + cmd.Flags().IntVar(&options.BuildAttempts, "attempts", 5, "The maximum number of build attempts for each image") panicIfErr(cmd.Execute()) } @@ -127,6 +128,13 @@ func (options *PublisherOptions) build(component string, camelVersion string) er } defer os.RemoveAll(dir) + dependencies := make([]string, 0) + for d := range images.StandardDependencies { + dependencies = append(dependencies, d) + } + dependencies = append(dependencies, images.BaseDependency) + dependencies = append(dependencies, "camel:"+component) + ctx := builder.Context{ C: context.TODO(), Path: dir, @@ -136,13 +144,7 @@ func (options *PublisherOptions) build(component string, camelVersion string) er CamelVersion: camelVersion, }, }, - Dependencies: []string{ - "camel-k:knative", - "camel:core", - "runtime:jvm", - "runtime:yaml", - "camel:" + component, - }, + Dependencies: dependencies, }, } @@ -170,7 +172,7 @@ func (options *PublisherOptions) build(component string, camelVersion string) er return err } - image := builder.PredefinedImageNameFor(component) + image := images.PredefinedImageNameFor(component) buildCmd := exec.Command("docker", "build", "-t", image, archiveDir) buildCmd.Stdout = os.Stdout buildCmd.Stderr = os.Stderr diff --git a/pkg/builder/builder.go b/pkg/builder/builder.go index ae61eaa..9becccb 100644 --- a/pkg/builder/builder.go +++ b/pkg/builder/builder.go @@ -168,11 +168,6 @@ func (b *defaultBuilder) submit(request Request) { break } - if c.Image != "" && c.PublicImage != "" { - logrus.Info("image already computed: skipping following steps") - break - } - select { case <-b.interrupt: c.Error = errors.New("build canceled") diff --git a/pkg/builder/builder_steps.go b/pkg/builder/builder_steps.go index b5db1ca..79d98d8 100644 --- a/pkg/builder/builder_steps.go +++ b/pkg/builder/builder_steps.go @@ -152,55 +152,6 @@ func ComputeDependencies(ctx *Context) error { return nil } -// LookupPredefinedImage is used to find a suitable predefined image if available -func LookupPredefinedImage(ctx *Context) error { - if !ctx.Request.Platform.Build.PredefinedImages { - // Usage of predefined images not enabled - return nil - } - - standardDependencies := map[string]bool{ - "camel:core": true, - "runtime:jvm": true, - "runtime:yaml": true, - } - - realDependencies := make(map[string]bool) - for _, d := range ctx.Request.Dependencies { - if _, std := standardDependencies[d]; !std { - realDependencies[d] = true - } - } - - knativeDep := "camel-k:knative" - if len(realDependencies) != 2 || !realDependencies[knativeDep] { - return nil - } - - var otherDep string - for d := range realDependencies { - if d != knativeDep { - otherDep = d - break - } - } - - camelPrefix := "camel:" - if !strings.HasPrefix(otherDep, camelPrefix) { - return nil - } - - comp := strings.TrimPrefix(otherDep, camelPrefix) - ctx.Image = PredefinedImageNameFor(comp) - ctx.PublicImage = ctx.Image - return nil -} - -// PredefinedImageNameFor -- -func PredefinedImageNameFor(comp string) string { - return fmt.Sprintf("camelk/camel-base-knative-%s:%s", comp, version.Version) -} - // ArtifactsSelector -- type ArtifactsSelector func(ctx *Context) error diff --git a/pkg/builder/kaniko/kaniko.go b/pkg/builder/kaniko/kaniko.go index 0d80075..4b2d826 100644 --- a/pkg/builder/kaniko/kaniko.go +++ b/pkg/builder/kaniko/kaniko.go @@ -25,7 +25,6 @@ import ( var DefaultSteps = []builder.Step{ builder.NewStep("generate", builder.ProjectGenerationPhase, builder.GenerateProject), builder.NewStep("build/compute-dependencies", builder.ProjectBuildPhase, builder.ComputeDependencies), - builder.NewStep("lookup/predefined-image", builder.ProjectBuildPhase + 1, builder.LookupPredefinedImage), builder.NewStep("packager", builder.ApplicationPackagePhase, builder.StandardPackager), builder.NewStep("publisher/kaniko", builder.ApplicationPublishPhase, Publisher), builder.NewStep("notify/context", builder.NotifyPhase, builder.NotifyIntegrationContext), diff --git a/pkg/builder/s2i/s2i.go b/pkg/builder/s2i/s2i.go index 9e105a0..8824e5d 100644 --- a/pkg/builder/s2i/s2i.go +++ b/pkg/builder/s2i/s2i.go @@ -25,7 +25,6 @@ import ( var DefaultSteps = []builder.Step{ builder.NewStep("generate", builder.ProjectGenerationPhase, builder.GenerateProject), builder.NewStep("build/compute-dependencies", builder.ProjectBuildPhase, builder.ComputeDependencies), - builder.NewStep("lookup/predefined-image", builder.ProjectBuildPhase + 1, builder.LookupPredefinedImage), builder.NewStep("packager/incremental", builder.ApplicationPackagePhase, builder.IncrementalPackager), builder.NewStep("publisher/s2i", builder.ApplicationPublishPhase, Publisher), builder.NewStep("notify/context", builder.NotifyPhase, builder.NotifyIntegrationContext), diff --git a/pkg/controller/integration/build_context.go b/pkg/controller/integration/build_context.go index 427794c..3bbb4a9 100644 --- a/pkg/controller/integration/build_context.go +++ b/pkg/controller/integration/build_context.go @@ -21,16 +21,13 @@ import ( "context" "fmt" + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" + "github.com/apache/camel-k/pkg/platform" "github.com/apache/camel-k/pkg/trait" - - "github.com/sirupsen/logrus" - "github.com/apache/camel-k/pkg/util" "github.com/apache/camel-k/pkg/util/digest" - "github.com/rs/xid" - - "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" + "github.com/sirupsen/logrus" ) // NewBuildContextAction create an action that handles integration context build @@ -60,6 +57,17 @@ func (action *buildContextAction) Handle(ctx context.Context, integration *v1alp return err } + if ictx == nil { + // Try to create an external context if possible + pl, err := platform.GetCurrentPlatform(ctx, action.client, integration.Namespace) + if err != nil { + return nil + } + if pl.Spec.Build.PredefinedImages { + ictx, err = ImportPredefinedContextIfPresent(ctx, action.client, integration) + } + } + if ictx != nil { if ictx.Labels["camel.apache.org/context.type"] == v1alpha1.IntegrationContextTypePlatform { // This is a platform context and as it is auto generated it may get diff --git a/pkg/controller/integration/util.go b/pkg/controller/integration/util.go index 9c31ec8..9eecf11 100644 --- a/pkg/controller/integration/util.go +++ b/pkg/controller/integration/util.go @@ -19,14 +19,22 @@ package integration import ( "context" - - "github.com/apache/camel-k/pkg/util" - k8sclient "sigs.k8s.io/controller-runtime/pkg/client" + "fmt" "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" + "github.com/apache/camel-k/pkg/client" + "github.com/apache/camel-k/pkg/platform/images" + "github.com/apache/camel-k/pkg/util" "github.com/pkg/errors" + "github.com/rs/xid" + k8sclient "sigs.k8s.io/controller-runtime/pkg/client" ) +var allowedLookupLabels = map[string]bool{ + v1alpha1.IntegrationContextTypePlatform: true, + v1alpha1.IntegrationContextTypeExternal: true, +} + // LookupContextForIntegration -- func LookupContextForIntegration(ctx context.Context, c k8sclient.Reader, integration *v1alpha1.Integration) (*v1alpha1.IntegrationContext, error) { if integration.Status.Context != "" { @@ -50,7 +58,7 @@ func LookupContextForIntegration(ctx context.Context, c k8sclient.Reader, integr for _, ctx := range ctxList.Items { ctx := ctx // pin - if ctx.Labels["camel.apache.org/context.type"] == v1alpha1.IntegrationContextTypePlatform { + if allowed, ok := allowedLookupLabels[ctx.Labels["camel.apache.org/context.type"]]; ok && allowed { ideps := len(integration.Status.Dependencies) cdeps := len(ctx.Spec.Dependencies) @@ -66,3 +74,31 @@ func LookupContextForIntegration(ctx context.Context, c k8sclient.Reader, integr return nil, nil } + +// ImportPredefinedContextIfPresent tries to create an external context from a predefined image +func ImportPredefinedContextIfPresent(ctx context.Context, c client.Client, integration *v1alpha1.Integration) (*v1alpha1.IntegrationContext, error) { + image := images.LookupPredefinedImage(integration.Status.Dependencies) + if image == "" { + return nil, nil + } + + externalCtxName := fmt.Sprintf("ctx-base-%s", xid.New()) + externalCtx := v1alpha1.NewIntegrationContext(integration.Namespace, externalCtxName) + + externalCtx.Labels = map[string]string{ + "camel.apache.org/context.type": v1alpha1.IntegrationContextTypeExternal, + "camel.apache.org/context.created.by.kind": v1alpha1.IntegrationKind, + "camel.apache.org/context.created.by.name": integration.Name, + "camel.apache.org/context.created.by.version": integration.ResourceVersion, + } + + externalCtx.Spec = v1alpha1.IntegrationContextSpec{ + Dependencies: integration.Status.Dependencies, + Image: image, + } + + if err := c.Create(ctx, &externalCtx); err != nil { + return nil, err + } + return &externalCtx, nil +} diff --git a/pkg/builder/s2i/s2i.go b/pkg/platform/images/doc.go similarity index 52% copy from pkg/builder/s2i/s2i.go copy to pkg/platform/images/doc.go index 9e105a0..cdf53c0 100644 --- a/pkg/builder/s2i/s2i.go +++ b/pkg/platform/images/doc.go @@ -15,18 +15,5 @@ See the License for the specific language governing permissions and limitations under the License. */ -package s2i - -import ( - "github.com/apache/camel-k/pkg/builder" -) - -// DefaultSteps -- -var DefaultSteps = []builder.Step{ - builder.NewStep("generate", builder.ProjectGenerationPhase, builder.GenerateProject), - builder.NewStep("build/compute-dependencies", builder.ProjectBuildPhase, builder.ComputeDependencies), - builder.NewStep("lookup/predefined-image", builder.ProjectBuildPhase + 1, builder.LookupPredefinedImage), - builder.NewStep("packager/incremental", builder.ApplicationPackagePhase, builder.IncrementalPackager), - builder.NewStep("publisher/s2i", builder.ApplicationPublishPhase, Publisher), - builder.NewStep("notify/context", builder.NotifyPhase, builder.NotifyIntegrationContext), -} +// Package images contains information for retrieval of platform predefined images +package images diff --git a/pkg/platform/images/images.go b/pkg/platform/images/images.go new file mode 100644 index 0000000..ab98a21 --- /dev/null +++ b/pkg/platform/images/images.go @@ -0,0 +1,82 @@ +/* +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 images + +import ( + "fmt" + "strings" + + "github.com/apache/camel-k/version" +) + +// BaseRepository is the docker repository that contains images +const ( + BaseRepository = "camelk" + ImagePrefix = "camel-base-knative-" +) + +// BaseDependency is a required dependency that must be found in the list +var BaseDependency = "camel-k:knative" + +// StandardDependencies are common dependencies included in the image +var StandardDependencies = map[string]bool{ + "camel:core": true, + "runtime:jvm": true, + "runtime:yaml": true, +} + +// LookupPredefinedImage is used to find a suitable predefined image if available +func LookupPredefinedImage(dependencies []string) string { + + realDependencies := make([]string, 0) + baseDependencyFound := false + for _, d := range dependencies { + if _, std := StandardDependencies[d]; std { + continue + } + if d == BaseDependency { + baseDependencyFound = true + continue + } + realDependencies = append(realDependencies, d) + } + + if !baseDependencyFound { + return "" + } + if len(realDependencies) == 0 { + return PredefinedImageNameFor("core") + } + if len(realDependencies) != 1 { + return "" + } + + otherDep := realDependencies[0] + camelPrefix := "camel:" + if !strings.HasPrefix(otherDep, camelPrefix) { + return "" + } + + comp := strings.TrimPrefix(otherDep, camelPrefix) + return PredefinedImageNameFor(comp) +} + +// PredefinedImageNameFor -- +func PredefinedImageNameFor(comp string) string { + return fmt.Sprintf("%s/%s%s:%s", BaseRepository, ImagePrefix, comp, version.Version) +} diff --git a/pkg/platform/images/images_test.go b/pkg/platform/images/images_test.go new file mode 100644 index 0000000..53441e1 --- /dev/null +++ b/pkg/platform/images/images_test.go @@ -0,0 +1,68 @@ +/* +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 images + +import ( + "github.com/apache/camel-k/version" + "github.com/stretchr/testify/assert" + "strconv" + "testing" +) + +func TestImageLookup(t *testing.T) { + cases := []struct { + dependencies []string + image string + }{ + { + dependencies: []string{"camel:telegram"}, + }, + { + dependencies: []string{"camel:telegram", "camel:core"}, + }, + { + dependencies: []string{"camel:telegram", "camel:core", "camel-k:knative"}, + image: BaseRepository + "/" + ImagePrefix + "telegram:" + version.Version, + }, + { + dependencies: []string{"camel:core", "camel-k:knative"}, + image: BaseRepository + "/" + ImagePrefix + "core:" + version.Version, + }, + { + dependencies: []string{"camel:dropbox", "camel:core", "camel-k:knative", "runtime:jvm"}, + image: BaseRepository + "/" + ImagePrefix + "dropbox:" + version.Version, + }, + { + dependencies: []string{"camel:dropbox", "camel:core", "camel-k:knative", "runtime:jvm", "runtime:yaml"}, + image: BaseRepository + "/" + ImagePrefix + "dropbox:" + version.Version, + }, + { + dependencies: []string{"camel:dropbox", "camel:core", "runtime:jvm", "runtime:yaml"}, + }, + { + dependencies: []string{"camel:dropbox", "camel:core", "camel-k:knative", "runtime:jvm", "runtime:groovy"}, + }, + } + + for i, tc := range cases { + t.Run("case-"+strconv.Itoa(i), func(t *testing.T) { + assert.Equal(t, tc.image, LookupPredefinedImage(tc.dependencies)) + }) + } + +} diff --git a/pkg/trait/builder_test.go b/pkg/trait/builder_test.go index f67fad0..1afefab 100644 --- a/pkg/trait/builder_test.go +++ b/pkg/trait/builder_test.go @@ -82,7 +82,7 @@ func TestS2IBuilderTrait(t *testing.T) { assert.NotEmpty(t, env.ExecutedTraits) assert.NotNil(t, env.GetTrait(ID("builder"))) assert.NotEmpty(t, env.Steps) - assert.Len(t, env.Steps, 6) + assert.Len(t, env.Steps, 5) assert.Condition(t, func() bool { for _, s := range env.Steps { if s.ID() == "publisher/s2i" && s.Phase() == builder.ApplicationPublishPhase { @@ -102,7 +102,7 @@ func TestKanikoBuilderTrait(t *testing.T) { assert.NotEmpty(t, env.ExecutedTraits) assert.NotNil(t, env.GetTrait(ID("builder"))) assert.NotEmpty(t, env.Steps) - assert.Len(t, env.Steps, 6) + assert.Len(t, env.Steps, 5) assert.Condition(t, func() bool { for _, s := range env.Steps { if s.ID() == "publisher/kaniko" && s.Phase() == builder.ApplicationPublishPhase {