This is an automated email from the ASF dual-hosted git repository. nferraro pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel-k.git
commit 3676bb5175a33b094a216d3b9bb8ddb8135f5129 Author: lburgazzoli <lburgazz...@gmail.com> AuthorDate: Thu Sep 13 16:07:28 2018 +0200 Support for properties #62 --- pkg/apis/camel/v1alpha1/types.go | 43 ++++++++++++---- pkg/apis/camel/v1alpha1/types_support.go | 2 + pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go | 5 ++ pkg/client/cmd/context_create.go | 2 +- pkg/client/cmd/run.go | 43 ++++++++++------ pkg/stub/action/integration/build.go | 35 +++++++------ pkg/stub/action/integration/deploy.go | 58 ++++++++++++++++------ pkg/stub/action/integration/util.go | 56 +++++++++++++++++++++ runtime/examples/props.js | 9 ++++ .../java/org/apache/camel/k/jvm/Application.java | 7 +++ .../main/java/org/apache/camel/k/jvm/Routes.java | 1 + 11 files changed, 199 insertions(+), 62 deletions(-) diff --git a/pkg/apis/camel/v1alpha1/types.go b/pkg/apis/camel/v1alpha1/types.go index 72da35a..e59a3e6 100644 --- a/pkg/apis/camel/v1alpha1/types.go +++ b/pkg/apis/camel/v1alpha1/types.go @@ -23,6 +23,7 @@ import ( // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// IntegrationList -- type IntegrationList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata"` @@ -31,6 +32,7 @@ type IntegrationList struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// Integration -- type Integration struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata"` @@ -38,36 +40,49 @@ type Integration struct { Status IntegrationStatus `json:"status,omitempty"` } +// IntegrationSpec -- type IntegrationSpec struct { - Replicas *int32 `json:"replicas,omitempty"` - Source SourceSpec `json:"source,omitempty"` - Context string `json:"context,omitempty"` - Dependencies []string `json:"dependencies,omitempty"` + Replicas *int32 `json:"replicas,omitempty"` + Source SourceSpec `json:"source,omitempty"` + Context string `json:"context,omitempty"` + Dependencies []string `json:"dependencies,omitempty"` + Properties []PropertySpec `json:"properties,omitempty"` } +// SourceSpec -- type SourceSpec struct { Name string `json:"name,omitempty"` Content string `json:"content,omitempty"` Language string `json:"language,omitempty"` } +// IntegrationStatus -- type IntegrationStatus struct { Phase IntegrationPhase `json:"phase,omitempty"` Digest string `json:"digest,omitempty"` Image string `json:"image,omitempty"` } +// IntegrationPhase -- type IntegrationPhase string const ( - IntegrationPhaseBuilding IntegrationPhase = "Building" + // IntegrationKind -- + IntegrationKind string = "Integration" + + // IntegrationPhaseBuilding -- + IntegrationPhaseBuilding IntegrationPhase = "Building" + // IntegrationPhaseDeploying -- IntegrationPhaseDeploying IntegrationPhase = "Deploying" - IntegrationPhaseRunning IntegrationPhase = "Running" - IntegrationPhaseError IntegrationPhase = "Error" + // IntegrationPhaseRunning -- + IntegrationPhaseRunning IntegrationPhase = "Running" + // IntegrationPhaseError -- + IntegrationPhaseError IntegrationPhase = "Error" ) // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// IntegrationContextList -- type IntegrationContextList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata"` @@ -76,6 +91,7 @@ type IntegrationContextList struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// IntegrationContext -- type IntegrationContext struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata"` @@ -83,28 +99,33 @@ type IntegrationContext struct { Status IntegrationContextStatus `json:"status,omitempty"` } +// IntegrationContextSpec -- type IntegrationContextSpec struct { Dependencies []string `json:"dependencies,omitempty"` Properties []PropertySpec `json:"properties,omitempty"` Environment []EnvironmentSpec `json:"environment,omitempty"` } +// PropertySpec -- type PropertySpec struct { - Name string - Value string + Name string `json:"name"` + Value string `json:"value"` } +// EnvironmentSpec -- type EnvironmentSpec struct { - Name string - Value string + Name string `json:"name"` + Value string `json:"value"` } +// IntegrationContextStatus -- type IntegrationContextStatus struct { Phase IntegrationContextPhase `json:"phase,omitempty"` Image string `json:"image,omitempty"` Digest string `json:"digest,omitempty"` } +// IntegrationContextPhase -- type IntegrationContextPhase string const ( diff --git a/pkg/apis/camel/v1alpha1/types_support.go b/pkg/apis/camel/v1alpha1/types_support.go index fe93a18..b1cd02e 100644 --- a/pkg/apis/camel/v1alpha1/types_support.go +++ b/pkg/apis/camel/v1alpha1/types_support.go @@ -43,6 +43,7 @@ func (spec EnvironmentSpec) String() string { // // ********************************** +// NewIntegrationContext -- func NewIntegrationContext(namespace string, name string) IntegrationContext { return IntegrationContext{ TypeMeta: metav1.TypeMeta{ @@ -56,6 +57,7 @@ func NewIntegrationContext(namespace string, name string) IntegrationContext { } } +// NewIntegrationContextList -- func NewIntegrationContextList() IntegrationContextList { return IntegrationContextList{ TypeMeta: metav1.TypeMeta{ diff --git a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go index 826918a..1da7ad7 100644 --- a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go @@ -224,6 +224,11 @@ func (in *IntegrationSpec) DeepCopyInto(out *IntegrationSpec) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.Properties != nil { + in, out := &in.Properties, &out.Properties + *out = make([]PropertySpec, len(*in)) + copy(*out, *in) + } return } diff --git a/pkg/client/cmd/context_create.go b/pkg/client/cmd/context_create.go index de2c1a5..75b63d7 100644 --- a/pkg/client/cmd/context_create.go +++ b/pkg/client/cmd/context_create.go @@ -89,7 +89,7 @@ func (command *contextCreateCommand) run(cmd *cobra.Command, args []string) erro 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]}) + ctx.Spec.Properties = append(ctx.Spec.Properties, v1alpha1.PropertySpec{Name: pair[0], Value: pair[1]}) } } diff --git a/pkg/client/cmd/run.go b/pkg/client/cmd/run.go index 312f49b..ff66631 100644 --- a/pkg/client/cmd/run.go +++ b/pkg/client/cmd/run.go @@ -34,17 +34,9 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1" ) -type RunCmdOptions struct { - *RootCmdOptions - IntegrationContext string - Language string - IntegrationName string - Dependencies []string - Wait bool -} - +// NewCmdRun -- func NewCmdRun(rootCmdOptions *RootCmdOptions) *cobra.Command { - options := RunCmdOptions{ + options := runCmdOptions{ RootCmdOptions: rootCmdOptions, } @@ -61,11 +53,22 @@ func NewCmdRun(rootCmdOptions *RootCmdOptions) *cobra.Command { 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.Flags().StringVarP(&options.IntegrationContext, "context", "x", "", "The contex used to run the integration") + cmd.Flags().StringSliceVarP(&options.Properties, "property", "p", nil, "Add a system property") return &cmd } -func (*RunCmdOptions) validateArgs(cmd *cobra.Command, args []string) error { +type runCmdOptions struct { + *RootCmdOptions + IntegrationContext string + Language string + IntegrationName string + Dependencies []string + Properties []string + Wait bool +} + +func (*runCmdOptions) validateArgs(cmd *cobra.Command, args []string) error { if len(args) != 1 { return errors.New("accepts 1 arg, received " + strconv.Itoa(len(args))) } @@ -78,7 +81,7 @@ func (*RunCmdOptions) validateArgs(cmd *cobra.Command, args []string) error { return nil } -func (o *RunCmdOptions) run(cmd *cobra.Command, args []string) error { +func (o *runCmdOptions) run(cmd *cobra.Command, args []string) error { integration, err := o.createIntegration(cmd, args) if err != nil { return err @@ -92,7 +95,7 @@ func (o *RunCmdOptions) run(cmd *cobra.Command, args []string) error { return nil } -func (o *RunCmdOptions) waitForIntegrationReady(integration *v1alpha1.Integration) error { +func (o *runCmdOptions) waitForIntegrationReady(integration *v1alpha1.Integration) error { // Block this goroutine until the integration is in a final status changes, err := watch.WatchStateChanges(o.Context, integration) if err != nil { @@ -130,7 +133,7 @@ watcher: return nil } -func (o *RunCmdOptions) createIntegration(cmd *cobra.Command, args []string) (*v1alpha1.Integration, error) { +func (o *runCmdOptions) createIntegration(cmd *cobra.Command, args []string) (*v1alpha1.Integration, error) { code, err := o.loadCode(args[0]) if err != nil { return nil, err @@ -157,7 +160,7 @@ func (o *RunCmdOptions) createIntegration(cmd *cobra.Command, args []string) (*v integration := v1alpha1.Integration{ TypeMeta: v1.TypeMeta{ - Kind: "Integration", + Kind: v1alpha1.IntegrationKind, APIVersion: v1alpha1.SchemeGroupVersion.String(), }, ObjectMeta: v1.ObjectMeta{ @@ -175,6 +178,14 @@ func (o *RunCmdOptions) createIntegration(cmd *cobra.Command, args []string) (*v }, } + integration.Spec.Properties = make([]v1alpha1.PropertySpec, 0) + for _, item := range o.Properties { + pair := strings.Split(item, "=") + if len(pair) == 2 { + integration.Spec.Properties = append(integration.Spec.Properties, v1alpha1.PropertySpec{Name: pair[0], Value: pair[1]}) + } + } + existed := false err = sdk.Create(&integration) if err != nil && k8serrors.IsAlreadyExists(err) { @@ -200,7 +211,7 @@ func (o *RunCmdOptions) createIntegration(cmd *cobra.Command, args []string) (*v return &integration, nil } -func (*RunCmdOptions) loadCode(fileName string) (string, error) { +func (*runCmdOptions) loadCode(fileName string) (string, error) { content, err := ioutil.ReadFile(fileName) if err != nil { return "", err diff --git a/pkg/stub/action/integration/build.go b/pkg/stub/action/integration/build.go index f98930e..ba24358 100644 --- a/pkg/stub/action/integration/build.go +++ b/pkg/stub/action/integration/build.go @@ -24,38 +24,37 @@ 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.Manager -} - +// NewBuildAction create an action that handles integration build func NewBuildAction(ctx context.Context, namespace string) IntegrationAction { - return &BuildAction{ + return &buildAction{ buildManager: build.NewManager(ctx, namespace), } } -func (b *BuildAction) Name() string { +type buildAction struct { + buildManager *build.Manager +} + +func (action *buildAction) Name() string { return "build" } -func (b *BuildAction) CanHandle(integration *v1alpha1.Integration) bool { +func (action *buildAction) CanHandle(integration *v1alpha1.Integration) bool { return integration.Status.Phase == v1alpha1.IntegrationPhaseBuilding } -func (b *BuildAction) Handle(integration *v1alpha1.Integration) error { - if integration.Spec.Context != "" { - name := integration.Spec.Context - ctx := v1alpha1.NewIntegrationContext(integration.Namespace, name) +func (action *buildAction) Handle(integration *v1alpha1.Integration) error { + ctx, err := LookupContextForIntegration(integration) + if err != nil { + //TODO: we may need to add a wait strategy, i.e give up after some time + return err + } - 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 != nil { if ctx.Status.Phase == v1alpha1.IntegrationContextPhaseReady { target := integration.DeepCopy() target.Status.Image = ctx.Status.Image @@ -70,9 +69,9 @@ func (b *BuildAction) Handle(integration *v1alpha1.Integration) error { Name: integration.Name, Qualifier: integration.Status.Digest, } - buildResult := b.buildManager.Get(buildIdentifier) + buildResult := action.buildManager.Get(buildIdentifier) if buildResult.Status == api.BuildStatusNotRequested { - b.buildManager.Start(api.BuildSource{ + action.buildManager.Start(api.BuildSource{ Identifier: buildIdentifier, Code: api.Code{ Name: integration.Spec.Source.Name, diff --git a/pkg/stub/action/integration/deploy.go b/pkg/stub/action/integration/deploy.go index 3403928..9acd9d8 100644 --- a/pkg/stub/action/integration/deploy.go +++ b/pkg/stub/action/integration/deploy.go @@ -29,22 +29,23 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -type DeployAction struct { +// NewDeployAction create an action that handles integration deploy +func NewDeployAction() IntegrationAction { + return &deployAction{} } -func NewDeployAction() IntegrationAction { - return &DeployAction{} +type deployAction struct { } -func (b *DeployAction) Name() string { +func (action *deployAction) Name() string { return "deploy" } -func (a *DeployAction) CanHandle(integration *v1alpha1.Integration) bool { +func (action *deployAction) CanHandle(integration *v1alpha1.Integration) bool { return integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying } -func (a *DeployAction) Handle(integration *v1alpha1.Integration) error { +func (action *deployAction) Handle(integration *v1alpha1.Integration) error { if err := createOrUpdateConfigMap(integration); err != nil { return err } @@ -61,11 +62,20 @@ func (a *DeployAction) Handle(integration *v1alpha1.Integration) error { // // ********************************** -func getConfigMapFor(integration *v1alpha1.Integration) *corev1.ConfigMap { +func getConfigMapFor(integration *v1alpha1.Integration) (*corev1.ConfigMap, error) { controller := true blockOwnerDeletion := true - return &corev1.ConfigMap{ + ctx, err := LookupContextForIntegration(integration) + if err != nil { + return nil, err + } + + // combine properties of integration with context, integration + // properties have the priority + properties := CombinePropertiesAsMap(ctx, integration) + + cm := corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{ Kind: "ConfigMap", APIVersion: "v1", @@ -91,14 +101,20 @@ func getConfigMapFor(integration *v1alpha1.Integration) *corev1.ConfigMap { }, Data: map[string]string{ "integration": integration.Spec.Source.Content, + "properties": PropertiesString(properties), }, } + + return &cm, nil } func createOrUpdateConfigMap(integration *v1alpha1.Integration) error { - cm := getConfigMapFor(integration) + cm, err := getConfigMapFor(integration) + if err != nil { + return err + } - err := sdk.Create(cm) + err = sdk.Create(cm) if err != nil && k8serrors.IsAlreadyExists(err) { err = sdk.Update(cm) } @@ -115,7 +131,7 @@ func createOrUpdateConfigMap(integration *v1alpha1.Integration) error { // // ********************************** -func getDeploymentFor(integration *v1alpha1.Integration) *appsv1.Deployment { +func getDeploymentFor(integration *v1alpha1.Integration) (*appsv1.Deployment, error) { controller := true blockOwnerDeletion := true integrationName := strings.TrimPrefix(integration.Spec.Source.Name, "/") @@ -160,7 +176,7 @@ func getDeploymentFor(integration *v1alpha1.Integration) *appsv1.Deployment { Image: integration.Status.Image, VolumeMounts: []corev1.VolumeMount{ { - Name: "integration", + Name: "integration-volume", MountPath: "/etc/camel", }, }, @@ -177,12 +193,16 @@ func getDeploymentFor(integration *v1alpha1.Integration) *appsv1.Deployment { Name: "CAMEL_K_ROUTES_LANGUAGE", Value: integration.Spec.Source.Language, }, + { + Name: "CAMEL_K_PROPERTIES", + Value: "file:/etc/camel/application.properties", + }, }, }, }, Volumes: []corev1.Volume{ { - Name: "integration", + Name: "integration-volume", VolumeSource: corev1.VolumeSource{ ConfigMap: &corev1.ConfigMapVolumeSource{ LocalObjectReference: corev1.LocalObjectReference{ @@ -192,6 +212,9 @@ func getDeploymentFor(integration *v1alpha1.Integration) *appsv1.Deployment { { Key: "integration", Path: integrationName, + }, { + Key: "properties", + Path: "application.properties", }, }, }, @@ -203,13 +226,16 @@ func getDeploymentFor(integration *v1alpha1.Integration) *appsv1.Deployment { }, } - return &deployment + return &deployment, nil } func createOrUpdateDeployment(integration *v1alpha1.Integration) error { - deployment := getDeploymentFor(integration) + deployment, err := getDeploymentFor(integration) + if err != nil { + return err + } - err := sdk.Create(deployment) + err = sdk.Create(deployment) if err != nil && k8serrors.IsAlreadyExists(err) { err = sdk.Update(deployment) } diff --git a/pkg/stub/action/integration/util.go b/pkg/stub/action/integration/util.go new file mode 100644 index 0000000..339a996 --- /dev/null +++ b/pkg/stub/action/integration/util.go @@ -0,0 +1,56 @@ +package action + +import ( + "fmt" + + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" + "github.com/operator-framework/operator-sdk/pkg/sdk" + + "github.com/pkg/errors" +) + +// LookupContextForIntegration -- +func LookupContextForIntegration(integration *v1alpha1.Integration) (*v1alpha1.IntegrationContext, error) { + if integration.Spec.Context != "" { + name := integration.Spec.Context + ctx := v1alpha1.NewIntegrationContext(integration.Namespace, name) + + if err := sdk.Get(&ctx); err != nil { + return nil, errors.Wrapf(err, "unable to find integration context %s, %s", ctx.Name, err) + } + + return &ctx, nil + } + + return nil, nil +} + +// PropertiesString -- +func PropertiesString(m map[string]string) string { + properties := "" + for k, v := range m { + properties += fmt.Sprintf("%s=%s\n", k, v) + } + + return properties +} + +// CombinePropertiesAsMap -- +func CombinePropertiesAsMap(context *v1alpha1.IntegrationContext, integration *v1alpha1.Integration) map[string]string { + properties := make(map[string]string) + if context != nil { + // Add context properties first so integrations can + // override it + for _, p := range context.Spec.Properties { + properties[p.Name] = p.Value + } + } + + if integration != nil { + for _, p := range integration.Spec.Properties { + properties[p.Name] = p.Value + } + } + + return properties +} diff --git a/runtime/examples/props.js b/runtime/examples/props.js new file mode 100644 index 0000000..a89b6ae --- /dev/null +++ b/runtime/examples/props.js @@ -0,0 +1,9 @@ +// +// To run this integrations use: +// +// kamel run -p my.message=test-props runtime/examples/props.js +// + +from('timer:props?period=1s') + .routeId('props') + .log('{{my.message}}') \ No newline at end of file diff --git a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Application.java b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Application.java index bec284f..8a394ea 100644 --- a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Application.java +++ b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Application.java @@ -25,6 +25,7 @@ public class Application { public static void main(String[] args) throws Exception { final String resource = System.getenv(Routes.ENV_CAMEL_K_ROUTES_URI); final String language = System.getenv(Routes.ENV_CAMEL_K_ROUTES_LANGUAGE); + final String properties = System.getenv(Routes.ENV_CAMEL_K_PROPERTIES); if (ObjectHelper.isEmpty(resource)) { throw new IllegalStateException("No valid resource found in " + Routes.ENV_CAMEL_K_ROUTES_URI + " environment variable"); @@ -38,6 +39,12 @@ public class Application { } Main main = new Main(); + + // Load properties + if (ObjectHelper.isNotEmpty(properties)) { + main.setPropertyPlaceholderLocations(properties); + } + main.addRouteBuilder(routes); main.run(); } diff --git a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Routes.java b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Routes.java index b2c87d4..308b838 100644 --- a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Routes.java +++ b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Routes.java @@ -27,6 +27,7 @@ import org.apache.commons.lang3.StringUtils; public final class Routes { public static final String ENV_CAMEL_K_ROUTES_URI = "CAMEL_K_ROUTES_URI"; public static final String ENV_CAMEL_K_ROUTES_LANGUAGE = "CAMEL_K_ROUTES_LANGUAGE"; + public static final String ENV_CAMEL_K_PROPERTIES = "CAMEL_K_PROPERTIES"; public static final String SCHEME_CLASSPATH = "classpath:"; public static final String SCHEME_FILE = "file:";