diff --git a/pkg/controller/integrationplatform/initialize.go b/pkg/controller/integrationplatform/initialize.go index ec700f16fc..e4a329e91d 100644 --- a/pkg/controller/integrationplatform/initialize.go +++ b/pkg/controller/integrationplatform/initialize.go @@ -19,6 +19,7 @@ package integrationplatform import ( "context" + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" platformutils "github.com/apache/camel-k/pkg/platform" "github.com/apache/camel-k/pkg/util/openshift" diff --git a/pkg/metadata/metadata.go b/pkg/metadata/metadata.go index 5b2940c58c..cec6c07787 100644 --- a/pkg/metadata/metadata.go +++ b/pkg/metadata/metadata.go @@ -21,7 +21,9 @@ import ( "sort" "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" + "github.com/apache/camel-k/pkg/gzip" src "github.com/apache/camel-k/pkg/util/source" + "github.com/sirupsen/logrus" ) // ExtractAll returns metadata information from all listed source codes @@ -68,6 +70,12 @@ func merge(m1 IntegrationMetadata, m2 IntegrationMetadata) IntegrationMetadata { // Extract returns metadata information from the source code func Extract(source v1alpha1.SourceSpec) IntegrationMetadata { + var err error + source, err = uncompress(source) + if err != nil { + logrus.Errorf("unable to uncompress source %s: %v", source.Name, err) + } + language := source.InferLanguage() m := IntegrationMetadata{} @@ -91,3 +99,19 @@ func Each(sources []v1alpha1.SourceSpec, consumer func(int, IntegrationMetadata) } } } + +func uncompress(spec v1alpha1.SourceSpec) (v1alpha1.SourceSpec, error) { + if spec.Compression { + data := []byte(spec.Content) + var uncompressed []byte + var err error + if uncompressed, err = gzip.UncompressBase64(data); err != nil { + return spec, err + } + newSpec := spec + newSpec.Compression = false + newSpec.Content = string(uncompressed) + return newSpec, nil + } + return spec, nil +} diff --git a/pkg/trait/catalog.go b/pkg/trait/catalog.go index 3f98d347db..3714eb7909 100644 --- a/pkg/trait/catalog.go +++ b/pkg/trait/catalog.go @@ -30,41 +30,43 @@ import ( // Catalog collects all information about traits in one place type Catalog struct { - tDebug Trait - tDependencies Trait - tDeployment Trait - tKnative Trait - tService Trait - tRoute Trait - tIngress Trait - tOwner Trait - tImages Trait - tBuilder Trait - tSpringBoot Trait - tIstio Trait - tEnvironment Trait - tClasspath Trait - tRest Trait + tDebug Trait + tDependencies Trait + tDeployment Trait + tKnativeService Trait + tKnative Trait + tService Trait + tRoute Trait + tIngress Trait + tOwner Trait + tImages Trait + tBuilder Trait + tSpringBoot Trait + tIstio Trait + tEnvironment Trait + tClasspath Trait + tRest Trait } // NewCatalog creates a new trait Catalog func NewCatalog(ctx context.Context, c client.Client) *Catalog { catalog := Catalog{ - tDebug: newDebugTrait(), - tRest: newRestTrait(), - tDependencies: newDependenciesTrait(), - tDeployment: newDeploymentTrait(), - tKnative: newKnativeTrait(), - tService: newServiceTrait(), - tRoute: newRouteTrait(), - tIngress: newIngressTrait(), - tOwner: newOwnerTrait(), - tImages: newImagesTrait(), - tBuilder: newBuilderTrait(), - tSpringBoot: newSpringBootTrait(), - tIstio: newIstioTrait(), - tEnvironment: newEnvironmentTrait(), - tClasspath: newClasspathTrait(), + tDebug: newDebugTrait(), + tRest: newRestTrait(), + tKnative: newKnativeTrait(), + tDependencies: newDependenciesTrait(), + tDeployment: newDeploymentTrait(), + tKnativeService: newKnativeServiceTrait(), + tService: newServiceTrait(), + tRoute: newRouteTrait(), + tIngress: newIngressTrait(), + tOwner: newOwnerTrait(), + tImages: newImagesTrait(), + tBuilder: newBuilderTrait(), + tSpringBoot: newSpringBootTrait(), + tIstio: newIstioTrait(), + tEnvironment: newEnvironmentTrait(), + tClasspath: newClasspathTrait(), } for _, t := range catalog.allTraits() { @@ -82,9 +84,10 @@ func (c *Catalog) allTraits() []Trait { return []Trait{ c.tDebug, c.tRest, + c.tKnative, c.tDependencies, c.tDeployment, - c.tKnative, + c.tKnativeService, c.tService, c.tRoute, c.tIngress, @@ -134,14 +137,15 @@ func (c *Catalog) traitsFor(environment *Environment) []Trait { return []Trait{ c.tDebug, c.tRest, + c.tKnative, c.tDependencies, c.tImages, c.tBuilder, c.tEnvironment, c.tClasspath, c.tSpringBoot, - c.tKnative, c.tDeployment, + c.tKnativeService, c.tIstio, c.tOwner, } diff --git a/pkg/trait/controller.go b/pkg/trait/controller.go new file mode 100644 index 0000000000..5194f1438e --- /dev/null +++ b/pkg/trait/controller.go @@ -0,0 +1,55 @@ +/* +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 trait + +import ( + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" + "github.com/apache/camel-k/pkg/client" + "github.com/apache/camel-k/pkg/metadata" + "golang.org/x/net/context" +) + +// ControllerStrategy determines the kind of controller that needs to be created for the integration +type ControllerStrategy string + +// List of controller strategies +const ( + ControllerStrategyDeployment = "deployment" + ControllerStrategyKnativeService = "knative-service" +) + +// DetermineControllerStrategy determines the type of controller that should be used for the integration +func DetermineControllerStrategy(ctx context.Context, c client.Client, e *Environment) (ControllerStrategy, error) { + if e.DetermineProfile() != v1alpha1.TraitProfileKnative { + return ControllerStrategyDeployment, nil + } + + sources := e.Integration.Sources() + var err error + if sources, err = GetEnrichedSources(ctx, c, e, sources); err != nil { + return "", err + } + + // In Knative profile: use knative service only if needed + meta := metadata.ExtractAll(sources) + if !meta.RequiresHTTPService { + return ControllerStrategyDeployment, nil + } + + return ControllerStrategyKnativeService, nil +} diff --git a/pkg/trait/deployment.go b/pkg/trait/deployment.go index 23f4cdee47..327164fa69 100644 --- a/pkg/trait/deployment.go +++ b/pkg/trait/deployment.go @@ -52,9 +52,14 @@ func (t *deploymentTrait) Configure(e *Environment) (bool, error) { if e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) { // - // Don't deploy on knative + // Don't deploy when a different strategy is needed (e.g. Knative) // - return e.DetermineProfile() != v1alpha1.TraitProfileKnative, nil + var strategy ControllerStrategy + var err error + if strategy, err = DetermineControllerStrategy(t.ctx, t.client, e); err != nil { + return false, err + } + return strategy == ControllerStrategyDeployment, nil } if t.ContainerImage && e.InPhase(v1alpha1.IntegrationContextPhaseReady, v1alpha1.IntegrationPhaseBuildingContext) { diff --git a/pkg/trait/istio.go b/pkg/trait/istio.go index 20732204b4..bd65738a84 100644 --- a/pkg/trait/istio.go +++ b/pkg/trait/istio.go @@ -29,7 +29,8 @@ type istioTrait struct { } const ( - istioIncludeAnnotation = "traffic.sidecar.istio.io/includeOutboundIPRanges" + istioSidecarInjectAnnotation = "sidecar.istio.io/inject" + istioOutboundIPRangesAnnotation = "traffic.sidecar.istio.io/includeOutboundIPRanges" ) func newIstioTrait() *istioTrait { @@ -52,19 +53,22 @@ func (t *istioTrait) Configure(e *Environment) (bool, error) { func (t *istioTrait) Apply(e *Environment) error { if t.Allow != "" { e.Resources.VisitDeployment(func(d *appsv1.Deployment) { - d.Spec.Template.Annotations = t.injectIstioAnnotation(d.Spec.Template.Annotations) + d.Spec.Template.Annotations = t.injectIstioAnnotation(d.Spec.Template.Annotations, true) }) e.Resources.VisitKnativeConfigurationSpec(func(cs *serving.ConfigurationSpec) { - cs.RevisionTemplate.Annotations = t.injectIstioAnnotation(cs.RevisionTemplate.Annotations) + cs.RevisionTemplate.Annotations = t.injectIstioAnnotation(cs.RevisionTemplate.Annotations, false) }) } return nil } -func (t *istioTrait) injectIstioAnnotation(annotations map[string]string) map[string]string { +func (t *istioTrait) injectIstioAnnotation(annotations map[string]string, includeInject bool) map[string]string { if annotations == nil { annotations = make(map[string]string) } - annotations[istioIncludeAnnotation] = t.Allow + annotations[istioOutboundIPRangesAnnotation] = t.Allow + if includeInject { + annotations[istioSidecarInjectAnnotation] = "true" + } return annotations } diff --git a/pkg/trait/knative.go b/pkg/trait/knative.go index 60e3813b32..a7bc62234e 100644 --- a/pkg/trait/knative.go +++ b/pkg/trait/knative.go @@ -18,12 +18,8 @@ limitations under the License. package trait import ( - "fmt" - "strconv" "strings" - "github.com/sirupsen/logrus" - "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" knativeapi "github.com/apache/camel-k/pkg/apis/camel/v1alpha1/knative" "github.com/apache/camel-k/pkg/metadata" @@ -37,18 +33,11 @@ import ( k8sclient "sigs.k8s.io/controller-runtime/pkg/client" ) -const ( - knativeMinScaleAnnotation = "autoscaling.knative.dev/minScale" - knativeMaxScaleAnnotation = "autoscaling.knative.dev/maxScale" -) - type knativeTrait struct { BaseTrait `property:",squash"` Configuration string `property:"configuration"` Sources string `property:"sources"` Sinks string `property:"sinks"` - MinScale *int `property:"minScale"` - MaxScale *int `property:"maxScale"` Auto *bool `property:"auto"` } @@ -78,14 +67,6 @@ func (t *knativeTrait) Configure(e *Environment) (bool, error) { channels := t.getSinkChannels(e) t.Sinks = strings.Join(channels, ",") } - // Check the right value for minScale, as not all services are allowed to scale down to 0 - if t.MinScale == nil { - meta := metadata.ExtractAll(e.Integration.Spec.Sources) - if !meta.RequiresHTTPService || !meta.PassiveEndpoints { - single := 1 - t.MinScale = &single - } - } } return true, nil @@ -99,13 +80,6 @@ func (t *knativeTrait) Apply(e *Environment) error { e.Resources.Add(sub) } - svc, err := t.getServiceFor(e) - if err != nil { - return err - } - - e.Resources.Add(svc) - return nil } @@ -121,187 +95,6 @@ func (t *knativeTrait) prepareEnvVars(e *Environment) error { return nil } -func (t *knativeTrait) getServiceFor(e *Environment) (*serving.Service, error) { - // combine properties of integration with context, integration - // properties have the priority - properties := "" - - VisitKeyValConfigurations("property", e.Context, e.Integration, func(key string, val string) { - properties += fmt.Sprintf("%s=%s\n", key, val) - }) - - environment := make([]corev1.EnvVar, 0) - - // combine Environment of integration with context, integration - // Environment has the priority - VisitKeyValConfigurations("env", e.Context, e.Integration, func(key string, value string) { - envvar.SetVal(&environment, key, value) - }) - - sources := make([]string, 0, len(e.Integration.Spec.Sources)) - for i, s := range e.Integration.Sources() { - - content := s.Content - if s.ContentRef != "" { - // - // Try to check if the config map is among the one - // creates for the deployment - // - cm := e.Resources.RemoveConfigMap(func(m *corev1.ConfigMap) bool { - return m.Name == s.ContentRef - }) - - // - // if not, try to get it from the kubernetes cluster - // - if cm == nil { - key := k8sclient.ObjectKey{ - Name: s.ContentRef, - Namespace: e.Integration.Namespace, - } - - cm = &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{ - Kind: "ConfigMap", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: s.ContentRef, - Namespace: e.Integration.Namespace, - }, - } - - if err := t.client.Get(t.ctx, key, cm); err != nil { - return nil, err - } - } - - if cm == nil { - return nil, fmt.Errorf("unable to find a COnfigMap with name: %s in the namespace: %s", s.ContentRef, e.Integration.Namespace) - } - - content = cm.Data["content"] - } - - if content == "" { - logrus.Warnf("Source %s has empty content", s.Name) - } - - envName := fmt.Sprintf("CAMEL_K_ROUTE_%03d", i) - envvar.SetVal(&environment, envName, content) - - params := make([]string, 0) - if s.InferLanguage() != "" { - params = append(params, "language="+string(s.InferLanguage())) - } - if s.Compression { - params = append(params, "compression=true") - } - - src := fmt.Sprintf("env:%s", envName) - if len(params) > 0 { - src = fmt.Sprintf("%s?%s", src, strings.Join(params, "&")) - } - - sources = append(sources, src) - } - - for i, r := range e.Integration.Spec.Resources { - if r.Type != v1alpha1.ResourceTypeData { - continue - } - - envName := fmt.Sprintf("CAMEL_K_RESOURCE_%03d", i) - envvar.SetVal(&environment, envName, r.Content) - - params := make([]string, 0) - if r.Compression { - params = append(params, "compression=true") - } - - envValue := fmt.Sprintf("env:%s", envName) - if len(params) > 0 { - envValue = fmt.Sprintf("%s?%s", envValue, strings.Join(params, "&")) - } - - envName = r.Name - envName = strings.ToUpper(envName) - envName = strings.Replace(envName, "-", "_", -1) - envName = strings.Replace(envName, ".", "_", -1) - envName = strings.Replace(envName, " ", "_", -1) - - envvar.SetVal(&environment, envName, envValue) - } - - // set env vars needed by the runtime - envvar.SetVal(&environment, "JAVA_MAIN_CLASS", "org.apache.camel.k.jvm.Application") - - // camel-k runtime - envvar.SetVal(&environment, "CAMEL_K_ROUTES", strings.Join(sources, ",")) - envvar.SetVal(&environment, "CAMEL_K_CONF", "env:CAMEL_K_PROPERTIES") - envvar.SetVal(&environment, "CAMEL_K_PROPERTIES", properties) - - // add a dummy env var to trigger deployment if everything but the code - // has been changed - envvar.SetVal(&environment, "CAMEL_K_DIGEST", e.Integration.Status.Digest) - - // optimizations - envvar.SetVal(&environment, "AB_JOLOKIA_OFF", True) - - // add env vars from traits - for _, envVar := range e.EnvVars { - envvar.SetVar(&environment, envVar) - } - - labels := map[string]string{ - "camel.apache.org/integration": e.Integration.Name, - } - - annotations := make(map[string]string) - // Resolve registry host names when used - annotations["alpha.image.policy.openshift.io/resolve-names"] = "*" - if t.MinScale != nil { - annotations[knativeMinScaleAnnotation] = strconv.Itoa(*t.MinScale) - } - if t.MaxScale != nil { - annotations[knativeMaxScaleAnnotation] = strconv.Itoa(*t.MaxScale) - } - - svc := serving.Service{ - TypeMeta: metav1.TypeMeta{ - Kind: "Service", - APIVersion: serving.SchemeGroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Name: e.Integration.Name, - Namespace: e.Integration.Namespace, - Labels: labels, - Annotations: e.Integration.Annotations, - }, - Spec: serving.ServiceSpec{ - RunLatest: &serving.RunLatestType{ - Configuration: serving.ConfigurationSpec{ - RevisionTemplate: serving.RevisionTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: labels, - Annotations: annotations, - }, - Spec: serving.RevisionSpec{ - ServiceAccountName: e.Integration.Spec.ServiceAccountName, - Container: corev1.Container{ - Image: e.Integration.Status.Image, - Env: environment, - }, - }, - }, - }, - }, - }, - } - - return &svc, nil -} - func (t *knativeTrait) getSubscriptionsFor(e *Environment) []*eventing.Subscription { channels := t.getConfiguredSourceChannels() subs := make([]*eventing.Subscription, 0) diff --git a/pkg/trait/knative_service.go b/pkg/trait/knative_service.go new file mode 100644 index 0000000000..a9d5b5adb0 --- /dev/null +++ b/pkg/trait/knative_service.go @@ -0,0 +1,262 @@ +/* +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 trait + +import ( + "fmt" + "strconv" + "strings" + + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" + "github.com/apache/camel-k/pkg/metadata" + "github.com/apache/camel-k/pkg/util/envvar" + serving "github.com/knative/serving/pkg/apis/serving/v1alpha1" + "github.com/sirupsen/logrus" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + knativeMinScaleAnnotation = "autoscaling.knative.dev/minScale" + knativeMaxScaleAnnotation = "autoscaling.knative.dev/maxScale" +) + +type knativeServiceTrait struct { + BaseTrait `property:",squash"` + MinScale *int `property:"minScale"` + MaxScale *int `property:"maxScale"` + Auto *bool `property:"auto"` +} + +func newKnativeServiceTrait() *knativeServiceTrait { + return &knativeServiceTrait{ + BaseTrait: BaseTrait{ + id: ID("knative-service"), + }, + } +} + +func (t *knativeServiceTrait) Configure(e *Environment) (bool, error) { + if t.Enabled != nil && !*t.Enabled { + return false, nil + } + + if !e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) { + return false, nil + } + + var strategy ControllerStrategy + var err error + if strategy, err = DetermineControllerStrategy(t.ctx, t.client, e); err != nil { + return false, err + } + if strategy != ControllerStrategyKnativeService { + return false, nil + } + + deployment := e.Resources.GetDeployment(func(d *appsv1.Deployment) bool { + if name, ok := d.ObjectMeta.Labels["camel.apache.org/integration"]; ok { + return name == e.Integration.Name + } + return false + }) + if deployment != nil { + // A controller is already present for the integration + return false, nil + } + + if t.Auto == nil || *t.Auto { + // Check the right value for minScale, as not all services are allowed to scale down to 0 + if t.MinScale == nil { + sources := e.Integration.Sources() + var err error + if sources, err = GetEnrichedSources(t.ctx, t.client, e, sources); err != nil { + return false, err + } + meta := metadata.ExtractAll(sources) + if !meta.RequiresHTTPService || !meta.PassiveEndpoints { + single := 1 + t.MinScale = &single + } + } + } + + return true, nil +} + +func (t *knativeServiceTrait) Apply(e *Environment) error { + svc, err := t.getServiceFor(e) + if err != nil { + return err + } + + e.Resources.Add(svc) + + return nil +} + +func (t *knativeServiceTrait) getServiceFor(e *Environment) (*serving.Service, error) { + // combine properties of integration with context, integration + // properties have the priority + properties := "" + + VisitKeyValConfigurations("property", e.Context, e.Integration, func(key string, val string) { + properties += fmt.Sprintf("%s=%s\n", key, val) + }) + + environment := make([]corev1.EnvVar, 0) + + // combine Environment of integration with context, integration + // Environment has the priority + VisitKeyValConfigurations("env", e.Context, e.Integration, func(key string, value string) { + envvar.SetVal(&environment, key, value) + }) + + sourceSpecs, err := GetEnrichedSources(t.ctx, t.client, e, e.Integration.Sources()) + if err != nil { + return nil, err + } + sources := make([]string, 0, len(sourceSpecs)) + for i, s := range sourceSpecs { + if s.ContentRef != "" { + // Remove the configmap if present among the ones created for the deployment + sourceSpec := s + e.Resources.RemoveConfigMap(func(m *corev1.ConfigMap) bool { + return m.Name == sourceSpec.ContentRef + }) + } + + if s.Content == "" { + logrus.Warnf("Source %s has empty content", s.Name) + } + + envName := fmt.Sprintf("CAMEL_K_ROUTE_%03d", i) + envvar.SetVal(&environment, envName, s.Content) + + params := make([]string, 0) + if s.InferLanguage() != "" { + params = append(params, "language="+string(s.InferLanguage())) + } + if s.Compression { + params = append(params, "compression=true") + } + + src := fmt.Sprintf("env:%s", envName) + if len(params) > 0 { + src = fmt.Sprintf("%s?%s", src, strings.Join(params, "&")) + } + + sources = append(sources, src) + } + + for i, r := range e.Integration.Spec.Resources { + if r.Type != v1alpha1.ResourceTypeData { + continue + } + + envName := fmt.Sprintf("CAMEL_K_RESOURCE_%03d", i) + envvar.SetVal(&environment, envName, r.Content) + + params := make([]string, 0) + if r.Compression { + params = append(params, "compression=true") + } + + envValue := fmt.Sprintf("env:%s", envName) + if len(params) > 0 { + envValue = fmt.Sprintf("%s?%s", envValue, strings.Join(params, "&")) + } + + envName = r.Name + envName = strings.ToUpper(envName) + envName = strings.Replace(envName, "-", "_", -1) + envName = strings.Replace(envName, ".", "_", -1) + envName = strings.Replace(envName, " ", "_", -1) + + envvar.SetVal(&environment, envName, envValue) + } + + // set env vars needed by the runtime + envvar.SetVal(&environment, "JAVA_MAIN_CLASS", "org.apache.camel.k.jvm.Application") + + // camel-k runtime + envvar.SetVal(&environment, "CAMEL_K_ROUTES", strings.Join(sources, ",")) + envvar.SetVal(&environment, "CAMEL_K_CONF", "env:CAMEL_K_PROPERTIES") + envvar.SetVal(&environment, "CAMEL_K_PROPERTIES", properties) + + // add a dummy env var to trigger deployment if everything but the code + // has been changed + envvar.SetVal(&environment, "CAMEL_K_DIGEST", e.Integration.Status.Digest) + + // optimizations + envvar.SetVal(&environment, "AB_JOLOKIA_OFF", True) + + // add env vars from traits + for _, envVar := range e.EnvVars { + envvar.SetVar(&environment, envVar) + } + + labels := map[string]string{ + "camel.apache.org/integration": e.Integration.Name, + } + + annotations := make(map[string]string) + // Resolve registry host names when used + annotations["alpha.image.policy.openshift.io/resolve-names"] = "*" + if t.MinScale != nil { + annotations[knativeMinScaleAnnotation] = strconv.Itoa(*t.MinScale) + } + if t.MaxScale != nil { + annotations[knativeMaxScaleAnnotation] = strconv.Itoa(*t.MaxScale) + } + + svc := serving.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + APIVersion: serving.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: e.Integration.Name, + Namespace: e.Integration.Namespace, + Labels: labels, + Annotations: e.Integration.Annotations, + }, + Spec: serving.ServiceSpec{ + RunLatest: &serving.RunLatestType{ + Configuration: serving.ConfigurationSpec{ + RevisionTemplate: serving.RevisionTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: labels, + Annotations: annotations, + }, + Spec: serving.RevisionSpec{ + ServiceAccountName: e.Integration.Spec.ServiceAccountName, + Container: corev1.Container{ + Image: e.Integration.Status.Image, + Env: environment, + }, + }, + }, + }, + }, + }, + } + + return &svc, nil +} diff --git a/pkg/trait/knative_test.go b/pkg/trait/knative_test.go index 216a952593..3137658b78 100644 --- a/pkg/trait/knative_test.go +++ b/pkg/trait/knative_test.go @@ -37,7 +37,7 @@ import ( ) func TestKnativeTraitWithCompressedSources(t *testing.T) { - content := "H4sIAAAAAAAA/+JKK8rP1VAvycxNLbIqyUzOVtfkUlBQUNAryddQz8lPt8rMS8tX1+QCAAAA//8BAAD//3wZ4pUoAAAA" + content := "H4sIAOJoQFwAA+NKK8rP1VAqzUtJLSrJL7fKKCkpsNLXN9ADQysLAwsD/YLEkgwlTS4FINAryddQz8lPt8rMS8tX1+TiAgAya2XzQAAAAA==" environment := Environment{ Integration: &v1alpha1.Integration{ @@ -137,7 +137,7 @@ func TestKnativeTraitWithCompressedSources(t *testing.T) { } func TestKnativeTraitWithConfigMapSources(t *testing.T) { - content := "H4sIAAAAAAAA/+JKK8rP1VAvycxNLbIqyUzOVtfkUlBQUNAryddQz8lPt8rMS8tX1+QCAAAA//8BAAD//3wZ4pUoAAAA" + content := "H4sIAOJoQFwAA+NKK8rP1VAqzUtJLSrJL7fKKCkpsNLXN9ADQysLAwsD/YLEkgwlTS4FINAryddQz8lPt8rMS8tX1+TiAgAya2XzQAAAAA==" environment := Environment{ Integration: &v1alpha1.Integration{ diff --git a/pkg/trait/util.go b/pkg/trait/util.go index 14554e7776..c29d80afc1 100644 --- a/pkg/trait/util.go +++ b/pkg/trait/util.go @@ -19,10 +19,13 @@ package trait import ( "context" + "fmt" "strings" "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" "github.com/apache/camel-k/pkg/client" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8sclient "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -99,3 +102,57 @@ func VisitKeyValConfigurations( } } } + +// GetEnrichedSources returns an enriched version of the sources, with all the external content injected +func GetEnrichedSources(ctx context.Context, c client.Client, e *Environment, sources []v1alpha1.SourceSpec) ([]v1alpha1.SourceSpec, error) { + enriched := make([]v1alpha1.SourceSpec, 0, len(sources)) + + for _, s := range sources { + content := s.Content + if content == "" && s.ContentRef != "" { + // + // Try to check if the config map is among the one + // creates for the deployment + // + sourceRef := s + cm := e.Resources.GetConfigMap(func(m *corev1.ConfigMap) bool { + return m.Name == sourceRef.ContentRef + }) + + // + // if not, try to get it from the kubernetes cluster + // + if cm == nil { + key := k8sclient.ObjectKey{ + Name: s.ContentRef, + Namespace: e.Integration.Namespace, + } + + cm = &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: s.ContentRef, + Namespace: e.Integration.Namespace, + }, + } + + if err := c.Get(ctx, key, cm); err != nil { + return nil, err + } + } + + if cm == nil { + return nil, fmt.Errorf("unable to find a ConfigMap with name: %s in the namespace: %s", s.ContentRef, e.Integration.Namespace) + } + + content = cm.Data["content"] + } + newSource := s + newSource.Content = content + enriched = append(enriched, newSource) + } + return enriched, nil +}