nicolaferraro closed pull request #61: Allow buildless direct deploy URL: https://github.com/apache/camel-k/pull/61
This is a PR merged from a forked repository. As GitHub hides the original diff on merge, it is displayed below for the sake of provenance: As this is a foreign pull request (from a fork), the diff is supplied below (as it won't show otherwise due to GitHub magic): diff --git a/deploy/crd-integration-context.yaml b/deploy/crd-integration-context.yaml index 612c848..fbbbf49 100644 --- a/deploy/crd-integration-context.yaml +++ b/deploy/crd-integration-context.yaml @@ -11,5 +11,7 @@ spec: listKind: IntegrationContextList plural: integrationcontexts singular: integrationcontext + shortNames: + - ictx scope: Namespaced version: v1alpha1 diff --git a/deploy/crd-integration.yaml b/deploy/crd-integration.yaml index 1cdbcc6..115d995 100644 --- a/deploy/crd-integration.yaml +++ b/deploy/crd-integration.yaml @@ -11,5 +11,7 @@ spec: listKind: IntegrationList plural: integrations singular: integration + shortNames: + - it scope: Namespaced version: v1alpha1 diff --git a/deploy/resources.go b/deploy/resources.go index 96995ce..3b40638 100644 --- a/deploy/resources.go +++ b/deploy/resources.go @@ -39,6 +39,8 @@ spec: listKind: IntegrationContextList plural: integrationcontexts singular: integrationcontext + shortNames: + - ictx scope: Namespaced version: v1alpha1 @@ -58,6 +60,8 @@ spec: listKind: IntegrationList plural: integrations singular: integration + shortNames: + - it scope: Namespaced version: v1alpha1 @@ -372,6 +376,9 @@ spec: containers: - name: camel-k-operator image: docker.io/apache/camel-k:0.0.1-SNAPSHOT + ports: + - containerPort: 60000 + name: metrics command: - camel-k-operator imagePullPolicy: Always @@ -380,6 +387,8 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace + - name: OPERATOR_NAME + value: "camel-k-operator" ` Resources["user-cluster-role.yaml"] = diff --git a/pkg/apis/camel/v1alpha1/register.go b/pkg/apis/camel/v1alpha1/register.go index dbbdfd7..52af408 100644 --- a/pkg/apis/camel/v1alpha1/register.go +++ b/pkg/apis/camel/v1alpha1/register.go @@ -46,6 +46,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &Integration{}, &IntegrationList{}, + &IntegrationContext{}, + &IntegrationContextList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil diff --git a/pkg/apis/camel/v1alpha1/types.go b/pkg/apis/camel/v1alpha1/types.go index c40b6b1..72da35a 100644 --- a/pkg/apis/camel/v1alpha1/types.go +++ b/pkg/apis/camel/v1alpha1/types.go @@ -93,6 +93,7 @@ type PropertySpec struct { Name string Value string } + type EnvironmentSpec struct { Name string Value string @@ -100,18 +101,22 @@ type EnvironmentSpec struct { type IntegrationContextStatus struct { Phase IntegrationContextPhase `json:"phase,omitempty"` - Digest string `json:"digest,omitempty"` Image string `json:"image,omitempty"` - From int `json:"from,omitempty"` + Digest string `json:"digest,omitempty"` } type IntegrationContextPhase string const ( - // IntegrationContextPhaseDraft -- - IntegrationContextPhaseDraft IntegrationContextPhase = "Draft" + // IntegrationContextKind -- + IntegrationContextKind string = "IntegrationContext" + // IntegrationContextPhaseBuilding -- IntegrationContextPhaseBuilding IntegrationContextPhase = "Building" + // IntegrationContextPhaseDeploying -- + IntegrationContextPhaseDeploying IntegrationContextPhase = "Deploying" // IntegrationContextPhaseReady -- IntegrationContextPhaseReady IntegrationContextPhase = "Ready" + // IntegrationContextPhaseError -- + IntegrationContextPhaseError IntegrationContextPhase = "Error" ) diff --git a/pkg/apis/camel/v1alpha1/types_support.go b/pkg/apis/camel/v1alpha1/types_support.go new file mode 100644 index 0000000..5168405 --- /dev/null +++ b/pkg/apis/camel/v1alpha1/types_support.go @@ -0,0 +1,66 @@ +/* +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 v1alpha1 + +import ( + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ********************************** +// +// Methods +// +// ********************************** + +func (spec PropertySpec) String() string { + return fmt.Sprint("%s=%s", spec.Name, spec.Value) +} + +func (spec EnvironmentSpec) String() string { + return fmt.Sprint("%s=%s", spec.Name, spec.Value) +} + +// ********************************** +// +// Helpers +// +// ********************************** + +func NewIntegrationContext(namespace string, name string) IntegrationContext { + return IntegrationContext{ + TypeMeta: metav1.TypeMeta{ + APIVersion: SchemeGroupVersion.String(), + Kind: IntegrationContextKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + } +} + +func NewIntegrationContextList() IntegrationContextList { + return IntegrationContextList{ + TypeMeta: metav1.TypeMeta{ + APIVersion: SchemeGroupVersion.String(), + Kind: IntegrationContextKind, + }, + } +} diff --git a/pkg/build/api/types.go b/pkg/build/api/types.go index 5eba444..b6c178e 100644 --- a/pkg/build/api/types.go +++ b/pkg/build/api/types.go @@ -25,8 +25,8 @@ type BuildSource struct { } type BuildIdentifier struct { - Name string - Digest string + Name string + Qualifier string } type Code struct { diff --git a/pkg/build/build_manager.go b/pkg/build/build_manager.go index 7006053..49f04f1 100644 --- a/pkg/build/build_manager.go +++ b/pkg/build/build_manager.go @@ -19,50 +19,40 @@ package build import ( "context" + "sync" + "github.com/apache/camel-k/pkg/build/api" "github.com/apache/camel-k/pkg/build/local" - "sync" ) // main facade to the image build system -type BuildManager struct { - builds map[api.BuildIdentifier]*api.BuildResult - mutex sync.Mutex +type Manager struct { + builds sync.Map builder api.Builder } -func NewBuildManager(ctx context.Context, namespace string) *BuildManager { - return &BuildManager{ - builds: make(map[api.BuildIdentifier]*api.BuildResult), +func NewManager(ctx context.Context, namespace string) *Manager { + return &Manager{ builder: local.NewLocalBuilder(ctx, namespace), } } -func (m *BuildManager) Get(identifier api.BuildIdentifier) api.BuildResult { - m.mutex.Lock() - defer m.mutex.Unlock() - - if info, present := m.builds[identifier]; !present || info == nil { +func (m *Manager) Get(identifier api.BuildIdentifier) api.BuildResult { + if info, present := m.builds.Load(identifier); !present || info == nil { return noBuildInfo() } else { - return *info + return *info.(*api.BuildResult) } } -func (m *BuildManager) Start(source api.BuildSource) { - m.mutex.Lock() - defer m.mutex.Unlock() - +func (m *Manager) Start(source api.BuildSource) { initialBuildInfo := initialBuildInfo(&source) - m.builds[source.Identifier] = &initialBuildInfo + m.builds.Store(source.Identifier, &initialBuildInfo) resChannel := m.builder.Build(source) go func() { res := <-resChannel - m.mutex.Lock() - defer m.mutex.Unlock() - - m.builds[res.Source.Identifier] = &res + m.builds.Store(res.Source.Identifier, &res) }() } diff --git a/pkg/build/build_manager_integration_test.go b/pkg/build/build_manager_integration_test.go index 11deca7..e271721 100644 --- a/pkg/build/build_manager_integration_test.go +++ b/pkg/build/build_manager_integration_test.go @@ -32,7 +32,7 @@ import ( func TestBuild(t *testing.T) { ctx := context.TODO() - buildManager := NewBuildManager(ctx, test.GetTargetNamespace()) + buildManager := NewManager(ctx, test.GetTargetNamespace()) identifier := build.BuildIdentifier{ Name: "man-test", Digest: digest.Random(), @@ -62,7 +62,7 @@ func TestBuild(t *testing.T) { func TestFailedBuild(t *testing.T) { ctx := context.TODO() - buildManager := NewBuildManager(ctx, test.GetTargetNamespace()) + buildManager := NewManager(ctx, test.GetTargetNamespace()) identifier := build.BuildIdentifier{ Name: "man-test-2", Digest: digest.Random(), diff --git a/pkg/build/local/local_builder.go b/pkg/build/local/local_builder.go index cd5b7b0..127657d 100644 --- a/pkg/build/local/local_builder.go +++ b/pkg/build/local/local_builder.go @@ -154,7 +154,7 @@ func (b *localBuilder) publish(tarFile string, source build.BuildSource) (string Output: buildv1.BuildOutput{ To: &v1.ObjectReference{ Kind: "ImageStreamTag", - Name: "camel-k-" + source.Identifier.Name + ":" + source.Identifier.Digest, + Name: "camel-k-" + source.Identifier.Name + ":" + source.Identifier.Qualifier, }, }, }, @@ -248,7 +248,7 @@ func (b *localBuilder) publish(tarFile string, source build.BuildSource) (string if is.Status.DockerImageRepository == "" { return "", errors.New("dockerImageRepository not available in ImageStream") } - return is.Status.DockerImageRepository + ":" + source.Identifier.Digest, nil + return is.Status.DockerImageRepository + ":" + source.Identifier.Qualifier, nil } func generateProjectDefinition(source build.BuildSource) (maven.ProjectDefinition, error) { diff --git a/pkg/build/local/local_builder_integration_test.go b/pkg/build/local/local_builder_integration_test.go index 8c4f4a4..967a0ad 100644 --- a/pkg/build/local/local_builder_integration_test.go +++ b/pkg/build/local/local_builder_integration_test.go @@ -36,8 +36,8 @@ func TestBuild(t *testing.T) { execution := builder.Build(build.BuildSource{ Identifier: build.BuildIdentifier{ - Name: "test0", - Digest: digest.Random(), + Name: "test0", + Qualifier: digest.Random(), }, Code: build.Code{ Content: code(), @@ -56,8 +56,8 @@ func TestDoubleBuild(t *testing.T) { execution1 := builder.Build(build.BuildSource{ Identifier: build.BuildIdentifier{ - Name: "test1", - Digest: digest.Random(), + Name: "test1", + Qualifier: digest.Random(), }, Code: build.Code{ Content: code(), @@ -66,8 +66,8 @@ func TestDoubleBuild(t *testing.T) { execution2 := builder.Build(build.BuildSource{ Identifier: build.BuildIdentifier{ - Name: "test2", - Digest: digest.Random(), + Name: "test2", + Qualifier: digest.Random(), }, Code: build.Code{ Content: code(), @@ -88,8 +88,8 @@ func TestFailedBuild(t *testing.T) { execution := builder.Build(build.BuildSource{ Identifier: build.BuildIdentifier{ - Name: "test3", - Digest: digest.Random(), + Name: "test3", + Qualifier: digest.Random(), }, Code: build.Code{ Content: code() + "-", diff --git a/pkg/build/local/local_builder_test.go b/pkg/build/local/local_builder_test.go index 49decab..b88567e 100644 --- a/pkg/build/local/local_builder_test.go +++ b/pkg/build/local/local_builder_test.go @@ -27,8 +27,8 @@ import ( func TestProjectGeneration(t *testing.T) { source := api.BuildSource{ Identifier: api.BuildIdentifier{ - Name: "my-integration", - Digest: "", + Name: "my-integration", + Qualifier: "", }, Code: api.Code{ Name: "my-code.js", @@ -59,8 +59,8 @@ func TestProjectGeneration(t *testing.T) { func TestProjectGenerationWithFailure(t *testing.T) { source := api.BuildSource{ Identifier: api.BuildIdentifier{ - Name: "my-integration", - Digest: "", + Name: "my-integration", + Qualifier: "", }, Code: api.Code{ Name: "my-code.js", diff --git a/pkg/client/cmd/context.go b/pkg/client/cmd/context.go index 6f1e502..949dab0 100644 --- a/pkg/client/cmd/context.go +++ b/pkg/client/cmd/context.go @@ -29,7 +29,8 @@ func NewCmdContext(rootCmdOptions *RootCmdOptions) *cobra.Command { Long: `Configure an Integration Context.`, } - cmd.AddCommand(newContextEditCmd(rootCmdOptions)) + cmd.AddCommand(newContextCreateCmd(rootCmdOptions)) + cmd.AddCommand(newContextDeleteCmd(rootCmdOptions)) cmd.AddCommand(newContextGetCmd(rootCmdOptions)) return &cmd diff --git a/pkg/client/cmd/context_create.go b/pkg/client/cmd/context_create.go new file mode 100644 index 0000000..de2c1a5 --- /dev/null +++ b/pkg/client/cmd/context_create.go @@ -0,0 +1,120 @@ +/* +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 cmd + +import ( + "errors" + "fmt" + "strconv" + "strings" + + "github.com/operator-framework/operator-sdk/pkg/sdk" + + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" + "github.com/apache/camel-k/pkg/util/kubernetes" + + "github.com/spf13/cobra" + k8serrors "k8s.io/apimachinery/pkg/api/errors" +) + +// NewCmdContext -- +func newContextCreateCmd(rootCmdOptions *RootCmdOptions) *cobra.Command { + impl := &contextCreateCommand{ + RootCmdOptions: rootCmdOptions, + } + + cmd := cobra.Command{ + Use: "create", + Short: "Create an Integration Context", + Long: `Create an Integration Context.`, + Args: impl.validateArgs, + RunE: impl.run, + } + + cmd.Flags().StringSliceVarP(&impl.env, "env", "e", nil, "Add an environment variable") + cmd.Flags().StringSliceVarP(&impl.properties, "property", "p", nil, "Add a system property") + cmd.Flags().StringSliceVarP(&impl.dependencies, "dependency", "d", nil, "Add a dependency") + + return &cmd +} + +type contextCreateCommand struct { + *RootCmdOptions + + env []string + properties []string + dependencies []string +} + +func (command *contextCreateCommand) validateArgs(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("accepts 1 arg, received " + strconv.Itoa(len(args))) + } + + return nil +} + +func (command *contextCreateCommand) run(cmd *cobra.Command, args []string) error { + namespace := command.Namespace + name := kubernetes.SanitizeName(args[0]) + + ctx := v1alpha1.NewIntegrationContext(namespace, name) + ctx.Spec = v1alpha1.IntegrationContextSpec{ + Dependencies: command.dependencies, + Environment: make([]v1alpha1.EnvironmentSpec, 0), + Properties: make([]v1alpha1.PropertySpec, 0), + } + + for _, item := range command.env { + pair := strings.Split(item, "=") + if len(pair) == 2 { + ctx.Spec.Environment = append(ctx.Spec.Environment, v1alpha1.EnvironmentSpec{Name: pair[0], Value: pair[1]}) + } + } + for _, item := range command.properties { + pair := strings.Split(item, "=") + if len(pair) == 2 { + ctx.Spec.Environment = append(ctx.Spec.Environment, v1alpha1.EnvironmentSpec{Name: pair[0], Value: pair[1]}) + } + } + + existed := false + err := sdk.Create(&ctx) + if err != nil && k8serrors.IsAlreadyExists(err) { + existed = true + clone := ctx.DeepCopy() + err = sdk.Get(clone) + if err != nil { + return err + } + ctx.ResourceVersion = clone.ResourceVersion + err = sdk.Update(&ctx) + } + + if err != nil { + return err + } + + if !existed { + fmt.Printf("integration context \"%s\" created\n", name) + } else { + fmt.Printf("integration context \"%s\" updated\n", name) + } + + return nil +} diff --git a/pkg/client/cmd/context_delete.go b/pkg/client/cmd/context_delete.go new file mode 100644 index 0000000..c4cb893 --- /dev/null +++ b/pkg/client/cmd/context_delete.go @@ -0,0 +1,71 @@ +/* +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 cmd + +import ( + "errors" + "fmt" + "strconv" + + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" + "github.com/apache/camel-k/pkg/util/kubernetes" + "github.com/operator-framework/operator-sdk/pkg/sdk" + "github.com/spf13/cobra" +) + +func newContextDeleteCmd(rootCmdOptions *RootCmdOptions) *cobra.Command { + impl := contextDeleteCommand{ + RootCmdOptions: rootCmdOptions, + } + + cmd := cobra.Command{ + Use: "delete", + Short: "Delete an Integration Context", + Long: `Delete anIntegration Context.`, + Args: impl.validateArgs, + RunE: impl.run, + } + + return &cmd +} + +type contextDeleteCommand struct { + *RootCmdOptions +} + +func (command *contextDeleteCommand) validateArgs(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("accepts 1 arg, received " + strconv.Itoa(len(args))) + } + + return nil +} + +func (command *contextDeleteCommand) run(cmd *cobra.Command, args []string) error { + name := kubernetes.SanitizeName(args[0]) + ctx := v1alpha1.NewIntegrationContext(command.Namespace, name) + + if err := sdk.Delete(&ctx); err != nil { + fmt.Printf("error deleting integration context %s, %s", ctx.Name, err) + return err + } + + fmt.Printf("integration context %s has been deleted", ctx.Name) + + return nil +} diff --git a/pkg/client/cmd/context_edit.go b/pkg/client/cmd/context_edit.go deleted file mode 100644 index 18879ed..0000000 --- a/pkg/client/cmd/context_edit.go +++ /dev/null @@ -1,101 +0,0 @@ -/* -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 cmd - -import ( - "errors" - "log" - "strconv" - - "github.com/spf13/cobra" -) - -// NewCmdContext -- -func newContextEditCmd(rootCmdOptions *RootCmdOptions) *cobra.Command { - impl := &contextEditCommand{ - RootCmdOptions: rootCmdOptions, - discard: false, - save: true, - dependencies: contextResource{ - toAdd: make([]string, 0), - toRemove: make([]string, 0), - }, - env: contextResource{ - toAdd: make([]string, 0), - toRemove: make([]string, 0), - }, - properties: contextResource{ - toAdd: make([]string, 0), - toRemove: make([]string, 0), - }, - } - - cmd := cobra.Command{ - Use: "edit", - Short: "Edit an Integration Context", - Long: `Edit an Integration Context.`, - Args: impl.validateArgs, - RunE: impl.run, - } - - cmd.Flags().BoolVarP(&impl.discard, "discard", "x", false, "Discard the draft") - cmd.Flags().BoolVarP(&impl.save, "save", "s", true, "Save the context") - - cmd.Flags().StringSliceVarP(&impl.env.toAdd, "env", "e", nil, "Add an environment variable") - cmd.Flags().StringSliceVarP(&impl.env.toRemove, "env-rm", "E", nil, "Remove an environment variable") - cmd.Flags().StringSliceVarP(&impl.properties.toAdd, "property", "p", nil, "Add a system property") - cmd.Flags().StringSliceVarP(&impl.properties.toRemove, "property-rm", "P", nil, "Remove a system property") - cmd.Flags().StringSliceVarP(&impl.dependencies.toAdd, "dependency", "d", nil, "Add a dependency") - cmd.Flags().StringSliceVarP(&impl.dependencies.toRemove, "dependency-rm", "D", nil, "Remove a dependency") - - return &cmd -} - -type contextResource struct { - toAdd []string - toRemove []string -} - -type contextEditCommand struct { - *RootCmdOptions - - env contextResource - properties contextResource - dependencies contextResource - - // rollback the context to the state before it was edited - discard bool - - // save the context then the operator should rebuild the image, this is - // set as true by default, if you want to mark a context as a draft, - // set it to false - save bool -} - -func (command *contextEditCommand) validateArgs(cmd *cobra.Command, args []string) error { - if len(args) != 1 { - return errors.New("accepts 1 arg, received " + strconv.Itoa(len(args))) - } - - return nil -} - -func (command *contextEditCommand) run(cmd *cobra.Command, args []string) error { - log.Printf("context=%s, config=%+v", args[0], command) - return nil -} diff --git a/pkg/client/cmd/context_get.go b/pkg/client/cmd/context_get.go index 36d16c1..05a96ca 100644 --- a/pkg/client/cmd/context_get.go +++ b/pkg/client/cmd/context_get.go @@ -25,8 +25,6 @@ import ( "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" "github.com/operator-framework/operator-sdk/pkg/sdk" "github.com/spf13/cobra" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func newContextGetCmd(rootCmdOptions *RootCmdOptions) *cobra.Command { @@ -49,17 +47,8 @@ type contextGetCommand struct { } func (command *contextGetCommand) run(cmd *cobra.Command, args []string) error { - ctxList := v1alpha1.IntegrationContextList{ - TypeMeta: metav1.TypeMeta{ - APIVersion: v1alpha1.SchemeGroupVersion.String(), - Kind: "IntegrationContext", - }, - } - - namespace := command.Namespace - - err := sdk.List(namespace, &ctxList) - if err != nil { + ctxList := v1alpha1.NewIntegrationContextList() + if err := sdk.List(command.Namespace, &ctxList); err != nil { return err } diff --git a/pkg/client/cmd/run.go b/pkg/client/cmd/run.go index cee146e..312f49b 100644 --- a/pkg/client/cmd/run.go +++ b/pkg/client/cmd/run.go @@ -27,19 +27,20 @@ import ( "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" "github.com/apache/camel-k/pkg/util/kubernetes" + "github.com/apache/camel-k/pkg/util/watch" "github.com/operator-framework/operator-sdk/pkg/sdk" "github.com/spf13/cobra" k8serrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/apache/camel-k/pkg/util/watch" ) type RunCmdOptions struct { *RootCmdOptions - Language string - IntegrationName string - Dependencies []string - Wait bool + IntegrationContext string + Language string + IntegrationName string + Dependencies []string + Wait bool } func NewCmdRun(rootCmdOptions *RootCmdOptions) *cobra.Command { @@ -59,7 +60,7 @@ func NewCmdRun(rootCmdOptions *RootCmdOptions) *cobra.Command { cmd.Flags().StringVar(&options.IntegrationName, "name", "", "The integration name") cmd.Flags().StringSliceVarP(&options.Dependencies, "dependency", "d", nil, "The integration dependency") cmd.Flags().BoolVarP(&options.Wait, "wait", "w", false, "Waits for the integration to be running") - cmd.ParseFlags(os.Args) + cmd.Flags().StringVarP(&options.IntegrationContext, "context", "x", "", "The contex used to run the integration") return &cmd } @@ -170,6 +171,7 @@ func (o *RunCmdOptions) createIntegration(cmd *cobra.Command, args []string) (*v Language: o.Language, }, Dependencies: o.Dependencies, + Context: o.IntegrationContext, }, } diff --git a/pkg/stub/action/context/build.go b/pkg/stub/action/context/build.go index 1541e9f..ca9b44b 100644 --- a/pkg/stub/action/context/build.go +++ b/pkg/stub/action/context/build.go @@ -18,29 +18,58 @@ limitations under the License. package action import ( - "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" + "context" + "github.com/operator-framework/operator-sdk/pkg/sdk" + "github.com/sirupsen/logrus" + + "github.com/apache/camel-k/pkg/build/api" + + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" + "github.com/apache/camel-k/pkg/build" ) -func NewIntegrationContextBuildAction() IntegrationContextAction { - return &integrationContextBuildAction{} +func NewIntegrationContextBuildAction(ctx context.Context, namespace string) IntegrationContextAction { + return &integrationContextBuildAction{ + buildManager: build.NewManager(ctx, namespace), + } } -// start edit context type integrationContextBuildAction struct { + buildManager *build.Manager } func (action *integrationContextBuildAction) Name() string { - return "Edit" + return "build" } func (action *integrationContextBuildAction) CanHandle(context *v1alpha1.IntegrationContext) bool { - // TODO: implement - return false + return context.Status.Phase == v1alpha1.IntegrationContextPhaseBuilding } -func (action *integrationContextBuildAction) Handle(integration *v1alpha1.IntegrationContext) error { - target := integration.DeepCopy() - // TODO: implement - return sdk.Update(target) +func (action *integrationContextBuildAction) Handle(context *v1alpha1.IntegrationContext) error { + buildIdentifier := api.BuildIdentifier{ + Name: "context-" + context.Name, + Qualifier: context.ResourceVersion, + } + + buildResult := action.buildManager.Get(buildIdentifier) + if buildResult.Status == api.BuildStatusNotRequested { + action.buildManager.Start(api.BuildSource{ + Identifier: buildIdentifier, + Dependencies: context.Spec.Dependencies, + }) + logrus.Info("Build started") + } else if buildResult.Status == api.BuildStatusError { + target := context.DeepCopy() + target.Status.Phase = v1alpha1.IntegrationContextPhaseError + return sdk.Update(target) + } else if buildResult.Status == api.BuildStatusCompleted { + target := context.DeepCopy() + target.Status.Image = buildResult.Image + target.Status.Phase = v1alpha1.IntegrationContextPhaseReady + return sdk.Update(target) + } + + return nil } diff --git a/pkg/stub/action/context/edit.go b/pkg/stub/action/context/initialize.go similarity index 56% rename from pkg/stub/action/context/edit.go rename to pkg/stub/action/context/initialize.go index 871d44b..5803b38 100644 --- a/pkg/stub/action/context/edit.go +++ b/pkg/stub/action/context/initialize.go @@ -19,28 +19,31 @@ package action import ( "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" + "github.com/apache/camel-k/pkg/util/digest" "github.com/operator-framework/operator-sdk/pkg/sdk" ) -func NewIntegrationContextEditAction() IntegrationContextAction { - return &integrationContextEditAction{} +func NewIntegrationContextInitializeAction() IntegrationContextAction { + return &integrationContexInitializeAction{} } -// start edit context -type integrationContextEditAction struct { +type integrationContexInitializeAction struct { } -func (action *integrationContextEditAction) Name() string { - return "Edit" +func (action *integrationContexInitializeAction) Name() string { + return "initialize" } -func (action *integrationContextEditAction) CanHandle(context *v1alpha1.IntegrationContext) bool { - // TODO: implement - return false +func (action *integrationContexInitializeAction) CanHandle(context *v1alpha1.IntegrationContext) bool { + return context.Status.Phase == "" } -func (action *integrationContextEditAction) Handle(integration *v1alpha1.IntegrationContext) error { - target := integration.DeepCopy() - // TODO: implement +func (action *integrationContexInitializeAction) Handle(context *v1alpha1.IntegrationContext) error { + target := context.DeepCopy() + + // update the status + target.Status.Phase = v1alpha1.IntegrationContextPhaseBuilding + target.Status.Digest = digest.ComputeForIntegrationContext(context) + return sdk.Update(target) } diff --git a/pkg/stub/action/context/monitor.go b/pkg/stub/action/context/monitor.go index 98843f7..138900b 100644 --- a/pkg/stub/action/context/monitor.go +++ b/pkg/stub/action/context/monitor.go @@ -19,28 +19,36 @@ package action import ( "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" + "github.com/apache/camel-k/pkg/util/digest" "github.com/operator-framework/operator-sdk/pkg/sdk" + "github.com/sirupsen/logrus" ) func NewIntegrationContextMonitorAction() IntegrationContextAction { - return &integrationContexMonitorAction{} + return &integrationContextMonitorAction{} } -// start edit context -type integrationContexMonitorAction struct { +type integrationContextMonitorAction struct { } -func (action *integrationContexMonitorAction) Name() string { - return "Monitor" +func (action *integrationContextMonitorAction) Name() string { + return "monitor" } -func (action *integrationContexMonitorAction) CanHandle(context *v1alpha1.IntegrationContext) bool { - // TODO: implement - return false +func (action *integrationContextMonitorAction) CanHandle(context *v1alpha1.IntegrationContext) bool { + return context.Status.Phase == v1alpha1.IntegrationContextPhaseReady || context.Status.Phase == v1alpha1.IntegrationContextPhaseError } -func (action *integrationContexMonitorAction) Handle(integration *v1alpha1.IntegrationContext) error { - target := integration.DeepCopy() - // TODO: implement - return sdk.Update(target) +func (action *integrationContextMonitorAction) Handle(context *v1alpha1.IntegrationContext) error { + hash := digest.ComputeForIntegrationContext(context) + if hash != context.Status.Digest { + logrus.Info("IntegrationContext ", context.Name, " needs a rebuild") + + target := context.DeepCopy() + target.Status.Digest = hash + target.Status.Phase = v1alpha1.IntegrationContextPhaseBuilding + return sdk.Update(target) + } + + return nil } diff --git a/pkg/stub/action/context/save.go b/pkg/stub/action/context/save.go deleted file mode 100644 index 16d3b0e..0000000 --- a/pkg/stub/action/context/save.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -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 action - -import ( - "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" - "github.com/operator-framework/operator-sdk/pkg/sdk" -) - -func NewIntegrationContextSaveAction() IntegrationContextAction { - return &integrationContexSaveAction{} -} - -// start edit context -type integrationContexSaveAction struct { -} - -func (action *integrationContexSaveAction) Name() string { - return "Edit" -} - -func (action *integrationContexSaveAction) CanHandle(context *v1alpha1.IntegrationContext) bool { - // TODO: implement - return false -} - -func (action *integrationContexSaveAction) Handle(integration *v1alpha1.IntegrationContext) error { - target := integration.DeepCopy() - // TODO: implement - return sdk.Update(target) -} diff --git a/pkg/stub/action/integration/build.go b/pkg/stub/action/integration/build.go index 373583a..f98930e 100644 --- a/pkg/stub/action/integration/build.go +++ b/pkg/stub/action/integration/build.go @@ -24,16 +24,17 @@ import ( "github.com/apache/camel-k/pkg/build" "github.com/apache/camel-k/pkg/build/api" "github.com/operator-framework/operator-sdk/pkg/sdk" + "github.com/pkg/errors" "github.com/sirupsen/logrus" ) type BuildAction struct { - buildManager *build.BuildManager + buildManager *build.Manager } func NewBuildAction(ctx context.Context, namespace string) IntegrationAction { return &BuildAction{ - buildManager: build.NewBuildManager(ctx, namespace), + buildManager: build.NewManager(ctx, namespace), } } @@ -46,9 +47,28 @@ func (b *BuildAction) CanHandle(integration *v1alpha1.Integration) bool { } func (b *BuildAction) Handle(integration *v1alpha1.Integration) error { + if integration.Spec.Context != "" { + name := integration.Spec.Context + ctx := v1alpha1.NewIntegrationContext(integration.Namespace, name) + + if err := sdk.Get(&ctx); err != nil { + //TODO: we may need to add a wait strategy, i.e give up after some time + return errors.Wrapf(err, "unable to find integration context %s, %s", ctx.Name, err) + } + + if ctx.Status.Phase == v1alpha1.IntegrationContextPhaseReady { + target := integration.DeepCopy() + target.Status.Image = ctx.Status.Image + target.Status.Phase = v1alpha1.IntegrationPhaseDeploying + return sdk.Update(target) + } + + return nil + } + buildIdentifier := api.BuildIdentifier{ - Name: integration.Name, - Digest: integration.Status.Digest, + Name: integration.Name, + Qualifier: integration.Status.Digest, } buildResult := b.buildManager.Get(buildIdentifier) if buildResult.Status == api.BuildStatusNotRequested { diff --git a/pkg/stub/action/integration/initialize.go b/pkg/stub/action/integration/initialize.go index 6caa1b3..f2747eb 100644 --- a/pkg/stub/action/integration/initialize.go +++ b/pkg/stub/action/integration/initialize.go @@ -48,6 +48,6 @@ func (b *InitializeAction) Handle(integration *v1alpha1.Integration) error { } // update the status target.Status.Phase = v1alpha1.IntegrationPhaseBuilding - target.Status.Digest = digest.Compute(integration) + target.Status.Digest = digest.ComputeForIntegration(integration) return sdk.Update(target) } diff --git a/pkg/stub/action/integration/monitor.go b/pkg/stub/action/integration/monitor.go index ad14532..b12600a 100644 --- a/pkg/stub/action/integration/monitor.go +++ b/pkg/stub/action/integration/monitor.go @@ -42,7 +42,7 @@ func (a *MonitorAction) CanHandle(integration *v1alpha1.Integration) bool { func (a *MonitorAction) Handle(integration *v1alpha1.Integration) error { - hash := digest.Compute(integration) + hash := digest.ComputeForIntegration(integration) if hash != integration.Status.Digest { logrus.Info("Integration ", integration.Name, " needs a rebuild") diff --git a/pkg/stub/handler.go b/pkg/stub/handler.go index 5c927cf..d90d776 100644 --- a/pkg/stub/handler.go +++ b/pkg/stub/handler.go @@ -36,7 +36,11 @@ func NewHandler(ctx context.Context, namespace string) sdk.Handler { iaction.NewDeployAction(), iaction.NewMonitorAction(), }, - integrationContextActionPool: []caction.IntegrationContextAction{}, + integrationContextActionPool: []caction.IntegrationContextAction{ + caction.NewIntegrationContextInitializeAction(), + caction.NewIntegrationContextBuildAction(ctx, namespace), + caction.NewIntegrationContextMonitorAction(), + }, } } diff --git a/pkg/util/digest/digest.go b/pkg/util/digest/digest.go index 6f84744..103a6f1 100644 --- a/pkg/util/digest/digest.go +++ b/pkg/util/digest/digest.go @@ -27,16 +27,45 @@ import ( "github.com/apache/camel-k/version" ) -// Compute a digest of the fields that are relevant for the deployment +// ComputeForIntegration a digest of the fields that are relevant for the deployment // Produces a digest that can be used as docker image tag -func Compute(integration *v1alpha1.Integration) string { +func ComputeForIntegration(integration *v1alpha1.Integration) string { hash := sha256.New() // Operator version is relevant hash.Write([]byte(version.Version)) - // Integration relevant fields + // Integration Context is relevant + hash.Write([]byte(integration.Spec.Context)) + + // Integration code if integration.Spec.Source.Content != "" { hash.Write([]byte(integration.Spec.Source.Content)) } + // Integration dependencies + for _, item := range integration.Spec.Dependencies { + hash.Write([]byte(item)) + } + + // Add a letter at the beginning and use URL safe encoding + return "v" + base64.RawURLEncoding.EncodeToString(hash.Sum(nil)) +} + +// ComputeForIntegrationContext a digest of the fields that are relevant for the deployment +// Produces a digest that can be used as docker image tag +func ComputeForIntegrationContext(integration *v1alpha1.IntegrationContext) string { + hash := sha256.New() + // Operator version is relevant + hash.Write([]byte(version.Version)) + + for _, item := range integration.Spec.Dependencies { + hash.Write([]byte(item)) + } + for _, item := range integration.Spec.Environment { + hash.Write([]byte(item.String())) + } + for _, item := range integration.Spec.Properties { + hash.Write([]byte(item.String())) + } + // Add a letter at the beginning and use URL safe encoding return "v" + base64.RawURLEncoding.EncodeToString(hash.Sum(nil)) } ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org With regards, Apache Git Services