From 4154069db39e61c04d1769bb5132dd37926bde22 Mon Sep 17 00:00:00 2001 From: Pasquale Congiusti Date: Wed, 18 Sep 2024 08:18:43 +0200 Subject: [PATCH] feat(api): store executed traits in status --- addons/master/master.go | 82 +- addons/master/master_test.go | 78 + addons/resume/resume.go | 50 +- .../ROOT/partials/apis/camel-k-crds.adoc | 8 + docs/modules/traits/pages/resume.adoc | 1 + helm/camel-k/crds/camel-k-crds.yaml | 1693 +++++++++++++++++ pkg/apis/camel/v1/integration_types.go | 2 + pkg/apis/camel/v1/zz_generated.deepcopy.go | 5 + .../camel/v1/integrationstatus.go | 9 + .../bases/camel.apache.org_integrations.yaml | 1693 +++++++++++++++++ pkg/trait/affinity.go | 4 - pkg/trait/builder_test.go | 9 +- pkg/trait/camel.go | 10 +- pkg/trait/container.go | 147 +- pkg/trait/container_probes_test.go | 12 +- pkg/trait/container_test.go | 34 +- pkg/trait/cron.go | 29 +- pkg/trait/cron_test.go | 87 +- pkg/trait/environment.go | 3 - pkg/trait/environment_test.go | 15 +- pkg/trait/health.go | 35 +- pkg/trait/health_test.go | 4 +- pkg/trait/ingress.go | 34 +- pkg/trait/ingress_test.go | 2 +- pkg/trait/istio.go | 17 +- pkg/trait/istio_test.go | 12 +- pkg/trait/jolokia.go | 15 +- pkg/trait/jolokia_test.go | 5 +- pkg/trait/jvm.go | 4 +- pkg/trait/kamelets.go | 15 +- pkg/trait/kamelets_test.go | 53 + pkg/trait/knative.go | 72 +- pkg/trait/knative_service.go | 28 +- pkg/trait/knative_service_test.go | 86 +- pkg/trait/knative_test.go | 83 +- pkg/trait/logging.go | 23 +- pkg/trait/logging_test.go | 9 +- pkg/trait/mount_test.go | 12 +- pkg/trait/platform.go | 29 +- pkg/trait/prometheus.go | 5 +- pkg/trait/route.go | 3 - pkg/trait/route_test.go | 36 +- pkg/trait/security_context.go | 28 +- pkg/trait/security_context_test.go | 15 +- pkg/trait/service.go | 3 +- pkg/trait/service_test.go | 82 +- pkg/trait/trait.go | 16 +- pkg/trait/trait_catalog.go | 51 +- pkg/trait/trait_catalog_test.go | 89 + pkg/trait/trait_test.go | 194 +- pkg/trait/trait_types.go | 7 +- pkg/trait/trait_types_test.go | 6 +- pkg/trait/util.go | 4 - 53 files changed, 4667 insertions(+), 381 deletions(-) create mode 100644 pkg/trait/trait_catalog_test.go diff --git a/addons/master/master.go b/addons/master/master.go index f893e19bc3..242c7ee40b 100644 --- a/addons/master/master.go +++ b/addons/master/master.go @@ -66,7 +66,7 @@ type Trait struct { type masterTrait struct { trait.BaseTrait Trait `property:",squash"` - delegateDependencies []string `json:"-"` + delegateDependencies []string } // NewMasterTrait --. @@ -105,7 +105,7 @@ func (t *masterTrait) Configure(e *trait.Environment) (bool, *trait.TraitConditi } } if found { - if t.IncludeDelegateDependencies == nil || *t.IncludeDelegateDependencies { + if ptr.Deref(t.IncludeDelegateDependencies, true) { t.delegateDependencies = findAdditionalDependencies(e, meta) } } @@ -117,28 +117,17 @@ func (t *masterTrait) Configure(e *trait.Environment) (bool, *trait.TraitConditi return false, nil, err } if enabled { + t.Enabled = ptr.To(enabled) if t.ResourceName == nil { val := e.Integration.Name + "-lock" t.ResourceName = &val } - - if t.ResourceType == nil { - t.ResourceType = ptr.To(leaseResourceType) - } - - if t.LabelKey == nil { - val := v1.IntegrationLabel - t.LabelKey = &val - } - if t.LabelValue == nil { t.LabelValue = &e.Integration.Name } - - return true, nil, nil } - return false, nil, nil + return enabled, nil, nil } func (t *masterTrait) Apply(e *trait.Environment) error { @@ -172,49 +161,50 @@ func (t *masterTrait) setCustomizerConfiguration(e *trait.Environment) { e.Integration.Status.Configuration = append(e.Integration.Status.Configuration, v1.ConfigurationSpec{Type: "property", Value: "customizer.master.enabled=true"}, ) - if t.ResourceName != nil { - resourceName := t.ResourceName - e.Integration.Status.Configuration = append(e.Integration.Status.Configuration, - v1.ConfigurationSpec{Type: "property", Value: "customizer.master.kubernetesResourceNames=" + *resourceName}, - ) - } - if t.ResourceType != nil { - e.Integration.Status.Configuration = append(e.Integration.Status.Configuration, - v1.ConfigurationSpec{Type: "property", Value: "customizer.master.leaseResourceType" + *t.ResourceType}, - ) - } - if t.LabelKey != nil { - e.Integration.Status.Configuration = append(e.Integration.Status.Configuration, - v1.ConfigurationSpec{Type: "property", Value: "customizer.master.labelKey=" + *t.LabelKey}, - ) - } - if t.LabelValue != nil { - e.Integration.Status.Configuration = append(e.Integration.Status.Configuration, - v1.ConfigurationSpec{Type: "property", Value: "customizer.master.labelValue=" + *t.LabelValue}, - ) - } + e.Integration.Status.Configuration = append(e.Integration.Status.Configuration, + v1.ConfigurationSpec{Type: "property", Value: "customizer.master.kubernetesResourceNames=" + *t.ResourceName}, + ) + e.Integration.Status.Configuration = append(e.Integration.Status.Configuration, + v1.ConfigurationSpec{Type: "property", Value: "customizer.master.leaseResourceType" + t.getResourceKey()}, + ) + e.Integration.Status.Configuration = append(e.Integration.Status.Configuration, + v1.ConfigurationSpec{Type: "property", Value: "customizer.master.labelKey=" + t.getLabelKey()}, + ) + e.Integration.Status.Configuration = append(e.Integration.Status.Configuration, + v1.ConfigurationSpec{Type: "property", Value: "customizer.master.labelValue=" + *t.LabelValue}, + ) } func (t *masterTrait) setCatalogConfiguration(e *trait.Environment) { if e.ApplicationProperties == nil { e.ApplicationProperties = make(map[string]string) } - if t.ResourceName != nil { - e.ApplicationProperties["camel.k.master.resourceName"] = *t.ResourceName - } - if t.ResourceType != nil { - e.ApplicationProperties["camel.k.master.resourceType"] = *t.ResourceType - } - if t.LabelKey != nil && t.LabelValue != nil { - e.ApplicationProperties["camel.k.master.labelKey"] = *t.LabelKey - e.ApplicationProperties["camel.k.master.labelValue"] = *t.LabelValue - } + e.ApplicationProperties["camel.k.master.resourceName"] = *t.ResourceName + e.ApplicationProperties["camel.k.master.resourceType"] = t.getResourceKey() + e.ApplicationProperties["camel.k.master.labelKey"] = t.getLabelKey() + e.ApplicationProperties["camel.k.master.labelValue"] = *t.LabelValue for _, cp := range e.CamelCatalog.Runtime.Capabilities["master"].RuntimeProperties { e.ApplicationProperties[trait.CapabilityPropertyKey(cp.Key, e.ApplicationProperties)] = cp.Value } } +func (t *masterTrait) getResourceKey() string { + if t.ResourceType == nil { + return leaseResourceType + } + + return *t.ResourceType +} + +func (t *masterTrait) getLabelKey() string { + if t.LabelKey == nil { + return v1.IntegrationLabel + } + + return *t.LabelKey +} + func findAdditionalDependencies(e *trait.Environment, meta metadata.IntegrationMetadata) []string { var dependencies []string for _, endpoint := range meta.FromURIs { diff --git a/addons/master/master_test.go b/addons/master/master_test.go index c80b2abd6d..0754331b8f 100644 --- a/addons/master/master_test.go +++ b/addons/master/master_test.go @@ -21,6 +21,7 @@ import ( "testing" v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" + traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait" "github.com/apache/camel-k/v2/pkg/trait" "github.com/apache/camel-k/v2/pkg/util/camel" "github.com/apache/camel-k/v2/pkg/util/kubernetes" @@ -29,6 +30,7 @@ import ( "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" ) func TestMasterOn(t *testing.T) { @@ -184,3 +186,79 @@ func TestMasterOff(t *testing.T) { assert.Empty(t, conditions) assert.False(t, configured) } + +func TestMasterAuto(t *testing.T) { + catalog, err := camel.DefaultCatalog() + require.NoError(t, err) + + client, err := test.NewFakeClient() + require.NoError(t, err) + traitCatalog := trait.NewCatalog(nil) + + environment := trait.Environment{ + CamelCatalog: catalog, + Catalog: traitCatalog, + Client: client, + Integration: &v1.Integration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "ns", + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseInitialization, + }, + Spec: v1.IntegrationSpec{ + Profile: v1.TraitProfileKnative, + Sources: []v1.SourceSpec{ + { + DataSpec: v1.DataSpec{ + Name: "Master.java", + Content: `from("master:lock:timer:tick").to("log:test")`, + }, + Language: v1.LanguageJavaSource, + }, + }, + }, + }, + Platform: &v1.IntegrationPlatform{ + Spec: v1.IntegrationPlatformSpec{ + Cluster: v1.IntegrationPlatformClusterOpenShift, + Build: v1.IntegrationPlatformBuildSpec{ + PublishStrategy: v1.IntegrationPlatformBuildPublishStrategyS2I, + Registry: v1.RegistrySpec{Address: "registry"}, + RuntimeVersion: catalog.Runtime.Version, + }, + Profile: v1.TraitProfileKnative, + }, + Status: v1.IntegrationPlatformStatus{ + Phase: v1.IntegrationPlatformPhaseReady, + }, + }, + EnvVars: make([]corev1.EnvVar, 0), + ExecutedTraits: make([]trait.Trait, 0), + Resources: kubernetes.NewCollection(), + } + environment.Platform.ResyncStatusFullConfig() + + mt := NewMasterTrait() + mt.InjectClient(client) + trait, _ := mt.(*masterTrait) + // Initialization phase + configured, conditions, err := trait.Configure(&environment) + require.NoError(t, err) + assert.Empty(t, conditions) + assert.True(t, configured) + err = trait.Apply(&environment) + require.NoError(t, err) + + expectedTrait := &masterTrait{ + Trait: Trait{ + Trait: traitv1.Trait{ + Enabled: ptr.To(true), + }, + ResourceName: ptr.To("test-lock"), + LabelValue: ptr.To("test"), + }, + } + assert.Equal(t, expectedTrait.Trait, trait.Trait) +} diff --git a/addons/resume/resume.go b/addons/resume/resume.go index 45d56756c7..36cb9fdded 100644 --- a/addons/resume/resume.go +++ b/addons/resume/resume.go @@ -20,10 +20,8 @@ package resume import ( v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait" - "github.com/apache/camel-k/v2/pkg/metadata" "github.com/apache/camel-k/v2/pkg/trait" "github.com/apache/camel-k/v2/pkg/util" - "github.com/apache/camel-k/v2/pkg/util/log" "k8s.io/utils/ptr" ) @@ -46,6 +44,7 @@ import ( type Trait struct { traitv1.Trait `property:",squash"` // Enables automatic configuration of the trait. + // Deprecated: not in use. Auto *bool `property:"auto" json:"auto,omitempty"` // The type of the resume strategy to use ResumeStrategy string `property:"resume-strategy,omitempty"` @@ -64,8 +63,8 @@ type resumeTrait struct { } const ( - KafkaSingle = "org.apache.camel.processor.resume.kafka.SingleNodeKafkaResumeStrategy" - StrategyPath = "camel-k-offsets" + kafkaSingle = "org.apache.camel.processor.resume.kafka.SingleNodeKafkaResumeStrategy" + strategyPath = "camel-k-offsets" ) func NewResumeTrait() trait.Trait { @@ -82,28 +81,7 @@ func (r *resumeTrait) Configure(environment *trait.Environment) (bool, *trait.Tr return false, nil, nil } - if ptr.Deref(r.Auto, true) { - _, err := environment.ConsumeMeta(func(meta metadata.IntegrationMetadata) bool { - for _, endpoint := range meta.FromURIs { - log.Infof("Processing component %s", endpoint) - } - - return true - }) - if err != nil { - return false, nil, err - } - - if r.ResumeStrategy == "" { - r.ResumeStrategy = KafkaSingle - } - - if r.ResumePath == "" { - r.ResumePath = StrategyPath - } - } - - return r.Enabled != nil && *r.Enabled, nil, nil + return ptr.Deref(r.Enabled, false), nil, nil } func (r *resumeTrait) Apply(environment *trait.Environment) error { @@ -113,11 +91,27 @@ func (r *resumeTrait) Apply(environment *trait.Environment) error { if environment.IntegrationInRunningPhases() { environment.ApplicationProperties["customizer.resume.enabled"] = "true" - environment.ApplicationProperties["customizer.resume.resumeStrategy"] = r.ResumeStrategy - environment.ApplicationProperties["customizer.resume.resumePath"] = r.ResumePath + environment.ApplicationProperties["customizer.resume.resumeStrategy"] = r.getResumeStrategy() + environment.ApplicationProperties["customizer.resume.resumePath"] = r.getResumePath() environment.ApplicationProperties["customizer.resume.resumeServer"] = r.ResumeServer environment.ApplicationProperties["customizer.resume.cacheFillPolicy"] = r.CacheFillPolicy } return nil } + +func (r *resumeTrait) getResumeStrategy() string { + if r.ResumeStrategy == "" { + return kafkaSingle + } + + return r.ResumeStrategy +} + +func (r *resumeTrait) getResumePath() string { + if r.ResumePath == "" { + return strategyPath + } + + return r.ResumePath +} diff --git a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc index 77c844a218..a9199401aa 100644 --- a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc +++ b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc @@ -3533,6 +3533,13 @@ a list of dependencies needed by the application the profile needed to run this Integration +|`traits` + +*xref:#_camel_apache_org_v1_Traits[Traits]* +| + + +the traits executed for the Integration + |`integrationKit` + *https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#objectreference-v1-core[Kubernetes core/v1.ObjectReference]* | @@ -5817,6 +5824,7 @@ TraitConfiguration parameters configuration * <<#_camel_apache_org_v1_IntegrationPlatformSpec, IntegrationPlatformSpec>> * <<#_camel_apache_org_v1_IntegrationProfileSpec, IntegrationProfileSpec>> * <<#_camel_apache_org_v1_IntegrationSpec, IntegrationSpec>> +* <<#_camel_apache_org_v1_IntegrationStatus, IntegrationStatus>> Traits represents the collection of trait configurations. diff --git a/docs/modules/traits/pages/resume.adoc b/docs/modules/traits/pages/resume.adoc index 894fe4d12c..779b9f1af7 100644 --- a/docs/modules/traits/pages/resume.adoc +++ b/docs/modules/traits/pages/resume.adoc @@ -43,6 +43,7 @@ The following configuration options are available: | resume.auto | bool | Enables automatic configuration of the trait. +Deprecated: not in use. | resume.resume-strategy,omitempty | string diff --git a/helm/camel-k/crds/camel-k-crds.yaml b/helm/camel-k/crds/camel-k-crds.yaml index 449de8c308..ee85c65f6b 100644 --- a/helm/camel-k/crds/camel-k-crds.yaml +++ b/helm/camel-k/crds/camel-k-crds.yaml @@ -19892,6 +19892,1699 @@ spec: selector: description: label selector type: string + traits: + description: the traits executed for the Integration + properties: + 3scale: + description: 'Deprecated: for backward compatibility.' + properties: + configuration: + description: TraitConfiguration parameters configuration + type: object + x-kubernetes-preserve-unknown-fields: true + required: + - configuration + type: object + addons: + additionalProperties: + description: AddonTrait represents the configuration of an addon + trait. + type: object + x-kubernetes-preserve-unknown-fields: true + description: The extension point with addon traits + type: object + affinity: + description: The configuration of Affinity trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + nodeAffinityLabels: + description: Defines a set of nodes the integration pod(s) + are eligible to be scheduled on, based on labels on the + node. + items: + type: string + type: array + podAffinity: + description: Always co-locates multiple replicas of the integration + in the same node (default `false`). + type: boolean + podAffinityLabels: + description: |- + Defines a set of pods (namely those matching the label selector, relative to the given namespace) that the + integration pod(s) should be co-located with. + items: + type: string + type: array + podAntiAffinity: + description: Never co-locates multiple replicas of the integration + in the same node (default `false`). + type: boolean + podAntiAffinityLabels: + description: |- + Defines a set of pods (namely those matching the label selector, relative to the given namespace) that the + integration pod(s) should not be co-located with. + items: + type: string + type: array + type: object + builder: + description: The configuration of Builder trait + properties: + annotations: + additionalProperties: + type: string + description: When using `pod` strategy, annotation to use + for the builder pod. + type: object + baseImage: + description: |- + Specify a base image. In order to have the application working properly it must be a container image which has a Java JDK + installed and ready to use on path (ie `/usr/bin/java`). + type: string + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + incrementalImageBuild: + description: Use the incremental image build option, to reuse + existing containers (default `true`) + type: boolean + limitCPU: + description: |- + When using `pod` strategy, the maximum amount of CPU required by the pod builder. + Deprecated: use TasksRequestCPU instead with task name `builder`. + type: string + limitMemory: + description: |- + When using `pod` strategy, the maximum amount of memory required by the pod builder. + Deprecated: use TasksRequestCPU instead with task name `builder`. + type: string + mavenProfiles: + description: |- + A list of references pointing to configmaps/secrets that contains a maven profile. + This configmap/secret is a resource of the IntegrationKit created, therefore it needs to be present in the namespace where the operator is going to create the IntegrationKit. + The content of the maven profile is expected to be a text containing a valid maven profile starting with `` and ending with `` that will be integrated as an inline profile in the POM. + Syntax: [configmap|secret]:name[/key], where name represents the resource name, key optionally represents the resource key to be filtered (default key value = profile.xml). + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: Defines a set of nodes the builder pod is eligible + to be scheduled on, based on labels on the node. + type: object + orderStrategy: + description: The build order strategy to use, either `dependencies`, + `fifo` or `sequential` (default is the platform default) + enum: + - dependencies + - fifo + - sequential + type: string + platforms: + description: The list of manifest platforms to use to build + a container image (default `linux/amd64`). + items: + type: string + type: array + properties: + description: A list of properties to be provided to the build + task + items: + type: string + type: array + requestCPU: + description: |- + When using `pod` strategy, the minimum amount of CPU required by the pod builder. + Deprecated: use TasksRequestCPU instead with task name `builder`. + type: string + requestMemory: + description: |- + When using `pod` strategy, the minimum amount of memory required by the pod builder. + Deprecated: use TasksRequestCPU instead with task name `builder`. + type: string + strategy: + description: The strategy to use, either `pod` or `routine` + (default `routine`) + enum: + - pod + - routine + type: string + tasks: + description: A list of tasks to be executed (available only + when using `pod` strategy) with format `;;`. + items: + type: string + type: array + tasksFilter: + description: |- + A list of tasks sorted by the order of execution in a csv format, ie, `,,...`. + Mind that you must include also the operator tasks (`builder`, `quarkus-native`, `package`, `jib`, `s2i`) + if you need to execute them. Useful only with `pod` strategy. + type: string + tasksLimitCPU: + description: A list of limit cpu configuration for the specific + task with format `:`. + items: + type: string + type: array + tasksLimitMemory: + description: A list of limit memory configuration for the + specific task with format `:`. + items: + type: string + type: array + tasksRequestCPU: + description: A list of request cpu configuration for the specific + task with format `:`. + items: + type: string + type: array + tasksRequestMemory: + description: A list of request memory configuration for the + specific task with format `:`. + items: + type: string + type: array + verbose: + description: |- + Enable verbose logging on build components that support it (e.g. Kaniko build pod). + Deprecated no longer in use + type: boolean + type: object + camel: + description: The configuration of Camel trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + properties: + description: A list of properties to be provided to the Integration + runtime + items: + type: string + type: array + runtimeVersion: + description: |- + The camel-k-runtime version to use for the integration. It overrides the default version set in the Integration Platform. + You can use a fixed version (for example "3.2.3") or a semantic version (for example "3.x") which will try to resolve + to the best matching Catalog existing on the cluster. + type: string + type: object + container: + description: The configuration of Container trait + properties: + allowPrivilegeEscalation: + description: Security Context AllowPrivilegeEscalation configuration + (default false). + type: boolean + auto: + description: To automatically enable the trait + type: boolean + capabilitiesAdd: + description: Security Context Capabilities Add configuration + (default none). + items: + description: Capability represent POSIX capabilities type + type: string + type: array + capabilitiesDrop: + description: Security Context Capabilities Drop configuration + (default ALL). + items: + description: Capability represent POSIX capabilities type + type: string + type: array + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + expose: + description: Can be used to enable/disable exposure via kubernetes + Service. + type: boolean + image: + description: |- + The main container image to use for the Integration. When using this parameter the operator will create a synthetic IntegrationKit which + won't be able to execute traits requiring CamelCatalog. If the container image you're using is coming from an IntegrationKit, use instead + Integration `.spec.integrationKit` parameter. If you're moving the Integration across environments, you will also need to create an "external" IntegrationKit. + type: string + imagePullPolicy: + description: 'The pull policy: Always|Never|IfNotPresent' + enum: + - Always + - Never + - IfNotPresent + type: string + limitCPU: + description: The maximum amount of CPU to be provided (default + 500 millicores). + type: string + limitMemory: + description: The maximum amount of memory to be provided (default + 512 Mi). + type: string + name: + description: The main container name. It's named `integration` + by default. + type: string + port: + description: To configure a different port exposed by the + container (default `8080`). + type: integer + portName: + description: To configure a different port name for the port + exposed by the container. It defaults to `http` only when + the `expose` parameter is true. + type: string + requestCPU: + description: The minimum amount of CPU required (default 125 + millicores). + type: string + requestMemory: + description: The minimum amount of memory required (default + 128 Mi). + type: string + runAsNonRoot: + description: Security Context RunAsNonRoot configuration (default + false). + type: boolean + runAsUser: + description: 'Security Context RunAsUser configuration (default + none): this value is automatically retrieved in Openshift + clusters when not explicitly set.' + format: int64 + type: integer + seccompProfileType: + description: Security Context SeccompProfileType configuration + (default RuntimeDefault). + enum: + - Unconfined + - RuntimeDefault + type: string + servicePort: + description: To configure under which service port the container + port is to be exposed (default `80`). + type: integer + servicePortName: + description: To configure under which service port name the + container port is to be exposed (default `http`). + type: string + type: object + cron: + description: The configuration of Cron trait + properties: + activeDeadlineSeconds: + description: |- + Specifies the duration in seconds, relative to the start time, that the job + may be continuously active before it is considered to be failed. + It defaults to 60s. + format: int64 + type: integer + auto: + description: |- + Automatically deploy the integration as CronJob when all routes are + either starting from a periodic consumer (only `cron`, `timer` and `quartz` are supported) or a passive consumer (e.g. `direct` is a passive consumer). + + + It's required that all periodic consumers have the same period, and it can be expressed as cron schedule (e.g. `1m` can be expressed as `0/1 * * * *`, + while `35m` or `50s` cannot). + type: boolean + backoffLimit: + description: |- + Specifies the number of retries before marking the job failed. + It defaults to 2. + format: int32 + type: integer + components: + description: |- + A comma separated list of the Camel components that need to be customized in order for them to work when the schedule is triggered externally by Kubernetes. + A specific customizer is activated for each specified component. E.g. for the `timer` component, the `cron-timer` customizer is + activated (it's present in the `org.apache.camel.k:camel-k-cron` library). + + + Supported components are currently: `cron`, `timer` and `quartz`. + type: string + concurrencyPolicy: + description: |- + Specifies how to treat concurrent executions of a Job. + Valid values are: + - "Allow": allows CronJobs to run concurrently; + - "Forbid" (default): forbids concurrent runs, skipping next run if previous run hasn't finished yet; + - "Replace": cancels currently running job and replaces it with a new one + enum: + - Allow + - Forbid + - Replace + type: string + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + fallback: + description: |- + Use the default Camel implementation of the `cron` endpoint (`quartz`) instead of trying to materialize the integration + as Kubernetes CronJob. + type: boolean + schedule: + description: |- + The CronJob schedule for the whole integration. If multiple routes are declared, they must have the same schedule for this + mechanism to work correctly. + type: string + startingDeadlineSeconds: + description: |- + Optional deadline in seconds for starting the job if it misses scheduled + time for any reason. Missed jobs executions will be counted as failed ones. + format: int64 + type: integer + timeZone: + description: The timezone that the CronJob will run on + type: string + type: object + dependencies: + description: The configuration of Dependencies trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + type: object + deployer: + description: The configuration of Deployer trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + kind: + description: Allows to explicitly select the desired deployment + kind between `deployment`, `cron-job` or `knative-service` + when creating the resources for running the integration. + enum: + - deployment + - cron-job + - knative-service + type: string + useSSA: + description: |- + Deprecated: won't be able to enforce client side update in the future. + Use server-side apply to update the owned resources (default `true`). + Note that it automatically falls back to client-side patching, if SSA is not available, e.g., on old Kubernetes clusters. + type: boolean + type: object + deployment: + description: The configuration of Deployment trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + progressDeadlineSeconds: + description: |- + The maximum time in seconds for the deployment to make progress before it + is considered to be failed. It defaults to `60s`. + format: int32 + type: integer + rollingUpdateMaxSurge: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of pods that can be scheduled above the desired number of + pods. + Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). + This can not be 0 if MaxUnavailable is 0. + Absolute number is calculated from percentage by rounding up. + Defaults to `25%`. + x-kubernetes-int-or-string: true + rollingUpdateMaxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of pods that can be unavailable during the update. + Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). + Absolute number is calculated from percentage by rounding down. + This can not be 0 if MaxSurge is 0. + Defaults to `25%`. + x-kubernetes-int-or-string: true + strategy: + description: The deployment strategy to use to replace existing + pods with new ones. + enum: + - Recreate + - RollingUpdate + type: string + type: object + environment: + description: The configuration of Environment trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + containerMeta: + description: Enables injection of `NAMESPACE` and `POD_NAME` + environment variables (default `true`) + type: boolean + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + httpProxy: + description: Propagates the `HTTP_PROXY`, `HTTPS_PROXY` and + `NO_PROXY` environment variables (default `true`) + type: boolean + vars: + description: |- + A list of environment variables to be added to the integration container. + The syntax is either VAR=VALUE or VAR=[configmap|secret]:name/key, where name represents the resource name, + and key represents the resource key to be mapped as and environment variable. + These take precedence over any previously defined environment variables. + items: + type: string + type: array + type: object + error-handler: + description: The configuration of Error Handler trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + ref: + description: The error handler ref name provided or found + in application properties + type: string + type: object + gc: + description: The configuration of GC trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + discoveryCache: + description: |- + Discovery client cache to be used, either `disabled`, `disk` or `memory` (default `memory`). + Deprecated: to be removed from trait configuration. + enum: + - disabled + - disk + - memory + type: string + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + type: object + health: + description: The configuration of Health trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + livenessFailureThreshold: + description: Minimum consecutive failures for the liveness + probe to be considered failed after having succeeded. + format: int32 + type: integer + livenessInitialDelay: + description: Number of seconds after the container has started + before the liveness probe is initiated. + format: int32 + type: integer + livenessPeriod: + description: How often to perform the liveness probe. + format: int32 + type: integer + livenessProbe: + description: The liveness probe path to use (default provided + by the Catalog runtime used). + type: string + livenessProbeEnabled: + description: Configures the liveness probe for the integration + container (default `false`). + type: boolean + livenessScheme: + description: Scheme to use when connecting to the liveness + probe (default `HTTP`). + type: string + livenessSuccessThreshold: + description: Minimum consecutive successes for the liveness + probe to be considered successful after having failed. + format: int32 + type: integer + livenessTimeout: + description: Number of seconds after which the liveness probe + times out. + format: int32 + type: integer + readinessFailureThreshold: + description: Minimum consecutive failures for the readiness + probe to be considered failed after having succeeded. + format: int32 + type: integer + readinessInitialDelay: + description: Number of seconds after the container has started + before the readiness probe is initiated. + format: int32 + type: integer + readinessPeriod: + description: How often to perform the readiness probe. + format: int32 + type: integer + readinessProbe: + description: The readiness probe path to use (default provided + by the Catalog runtime used). + type: string + readinessProbeEnabled: + description: Configures the readiness probe for the integration + container (default `true`). + type: boolean + readinessScheme: + description: Scheme to use when connecting to the readiness + probe (default `HTTP`). + type: string + readinessSuccessThreshold: + description: Minimum consecutive successes for the readiness + probe to be considered successful after having failed. + format: int32 + type: integer + readinessTimeout: + description: Number of seconds after which the readiness probe + times out. + format: int32 + type: integer + startupFailureThreshold: + description: Minimum consecutive failures for the startup + probe to be considered failed after having succeeded. + format: int32 + type: integer + startupInitialDelay: + description: Number of seconds after the container has started + before the startup probe is initiated. + format: int32 + type: integer + startupPeriod: + description: How often to perform the startup probe. + format: int32 + type: integer + startupProbe: + description: The startup probe path to use (default provided + by the Catalog runtime used). + type: string + startupProbeEnabled: + description: Configures the startup probe for the integration + container (default `false`). + type: boolean + startupScheme: + description: Scheme to use when connecting to the startup + probe (default `HTTP`). + type: string + startupSuccessThreshold: + description: Minimum consecutive successes for the startup + probe to be considered successful after having failed. + format: int32 + type: integer + startupTimeout: + description: Number of seconds after which the startup probe + times out. + format: int32 + type: integer + type: object + ingress: + description: The configuration of Ingress trait + properties: + annotations: + additionalProperties: + type: string + description: |- + The annotations added to the ingress. + This can be used to set controller specific annotations, e.g., when using the NGINX Ingress controller: + See https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/annotations.md + type: object + auto: + description: To automatically add an ingress whenever the + integration uses an HTTP endpoint consumer. + type: boolean + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + host: + description: To configure the host exposed by the ingress. + type: string + ingressClassName: + description: |- + The Ingress class name as defined by the Ingress spec + See https://kubernetes.io/docs/concepts/services-networking/ingress/ + type: string + path: + description: To configure the path exposed by the ingress + (default `/`). + type: string + pathType: + description: |- + To configure the path type exposed by the ingress. + One of `Exact`, `Prefix`, `ImplementationSpecific` (default to `Prefix`). + enum: + - Exact + - Prefix + - ImplementationSpecific + type: string + tlsHosts: + description: To configure tls hosts + items: + type: string + type: array + tlsSecretName: + description: To configure tls secret name + type: string + type: object + istio: + description: The configuration of Istio trait + properties: + allow: + description: Configures a (comma-separated) list of CIDR subnets + that should not be intercepted by the Istio proxy (`10.0.0.0/8,172.16.0.0/12,192.168.0.0/16` + by default). + type: string + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + inject: + description: Forces the value for labels `sidecar.istio.io/inject`. + By default the label is set to `true` on deployment and + not set on Knative Service. + type: boolean + type: object + jolokia: + description: The configuration of Jolokia trait + properties: + CACert: + description: |- + The PEM encoded CA certification file path, used to verify client certificates, + applicable when `protocol` is `https` and `use-ssl-client-authentication` is `true` + (default `/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt` for OpenShift). + type: string + clientPrincipal: + description: |- + The principal(s) which must be given in a client certificate to allow access to the Jolokia endpoint, + applicable when `protocol` is `https` and `use-ssl-client-authentication` is `true` + (default `clientPrincipal=cn=system:master-proxy`, `cn=hawtio-online.hawtio.svc` and `cn=fuse-console.fuse.svc` for OpenShift). + items: + type: string + type: array + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + discoveryEnabled: + description: Listen for multicast requests (default `false`) + type: boolean + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + extendedClientCheck: + description: |- + Mandate the client certificate contains a client flag in the extended key usage section, + applicable when `protocol` is `https` and `use-ssl-client-authentication` is `true` + (default `true` for OpenShift). + type: boolean + host: + description: |- + The Host address to which the Jolokia agent should bind to. If `"\*"` or `"0.0.0.0"` is given, + the servers binds to every network interface (default `"*"`). + type: string + options: + description: |- + A list of additional Jolokia options as defined + in https://jolokia.org/reference/html/agents.html#agent-jvm-config[JVM agent configuration options] + items: + type: string + type: array + password: + description: The password used for authentication, applicable + when the `user` option is set. + type: string + port: + description: The Jolokia endpoint port (default `8778`). + type: integer + protocol: + description: The protocol to use, either `http` or `https` + (default `https` for OpenShift) + type: string + useSSLClientAuthentication: + description: Whether client certificates should be used for + authentication (default `true` for OpenShift). + type: boolean + user: + description: The user to be used for authentication + type: string + type: object + jvm: + description: The configuration of JVM trait + properties: + classpath: + description: Additional JVM classpath (use `Linux` classpath + separator) + type: string + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + debug: + description: Activates remote debugging, so that a debugger + can be attached to the JVM, e.g., using port-forwarding + type: boolean + debugAddress: + description: Transport address at which to listen for the + newly launched JVM (default `*:5005`) + type: string + debugSuspend: + description: Suspends the target JVM immediately before the + main class is loaded + type: boolean + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + jar: + description: The Jar dependency which will run the application. + Leave it empty for managed Integrations. + type: string + options: + description: A list of JVM options + items: + type: string + type: array + printCommand: + description: |- + Prints the command used the start the JVM in the container logs (default `true`) + Deprecated: no longer in use. + type: boolean + type: object + kamelets: + description: The configuration of Kamelets trait + properties: + auto: + description: Automatically inject all referenced Kamelets + and their default configuration (enabled by default) + type: boolean + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + list: + description: Comma separated list of Kamelet names to load + into the current integration + type: string + mountPoint: + description: The directory where the application mounts and + reads Kamelet spec (default `/etc/camel/kamelets`) + type: string + type: object + keda: + description: 'Deprecated: for backward compatibility.' + properties: + configuration: + description: TraitConfiguration parameters configuration + type: object + x-kubernetes-preserve-unknown-fields: true + required: + - configuration + type: object + knative: + description: The configuration of Knative trait + properties: + auto: + description: Enable automatic discovery of all trait properties. + type: boolean + channelSinks: + description: |- + List of channels used as destination of integration routes. + Can contain simple channel names or full Camel URIs. + items: + type: string + type: array + channelSources: + description: |- + List of channels used as source of integration routes. + Can contain simple channel names or full Camel URIs. + items: + type: string + type: array + config: + description: Can be used to inject a Knative complete configuration + in JSON format. + type: string + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + endpointSinks: + description: |- + List of endpoints used as destination of integration routes. + Can contain simple endpoint names or full Camel URIs. + items: + type: string + type: array + endpointSources: + description: List of channels used as source of integration + routes. + items: + type: string + type: array + eventSinks: + description: |- + List of event types that the integration will produce. + Can contain simple event types or full Camel URIs (to use a specific broker). + items: + type: string + type: array + eventSources: + description: |- + List of event types that the integration will be subscribed to. + Can contain simple event types or full Camel URIs (to use a specific broker different from "default"). + items: + type: string + type: array + filterEventType: + description: |- + Enables the default filtering for the Knative trigger using the event type + If this is true, the created Knative trigger uses the event type as a filter on the event stream when no other filter criteria is given. (default: true) + type: boolean + filterSourceChannels: + description: |- + Enables filtering on events based on the header "ce-knativehistory". Since this header has been removed in newer versions of + Knative, filtering is disabled by default. + type: boolean + filters: + description: |- + Sets filter attributes on the event stream (such as event type, source, subject and so on). + A list of key-value pairs that represent filter attributes and its values. + The syntax is KEY=VALUE, e.g., `source="my.source"`. + Filter attributes get set on the Knative trigger that is being created as part of this integration. + items: + type: string + type: array + namespaceLabel: + description: |- + Enables the camel-k-operator to set the "bindings.knative.dev/include=true" label to the namespace + As Knative requires this label to perform injection of K_SINK URL into the service. + If this is false, the integration pod may start and fail, read the SinkBinding Knative documentation. (default: true) + type: boolean + sinkBinding: + description: |- + Allows binding the integration to a sink via a Knative SinkBinding resource. + This can be used when the integration targets a single sink. + It's enabled by default when the integration targets a single sink + (except when the integration is owned by a Knative source). + type: boolean + type: object + knative-service: + description: The configuration of Knative Service trait + properties: + annotations: + additionalProperties: + type: string + description: |- + The annotations added to route. + This can be used to set knative service specific annotations + CLI usage example: -t "knative-service.annotations.'haproxy.router.openshift.io/balance'=true" + type: object + auto: + description: |- + Automatically deploy the integration as Knative service when all conditions hold: + + + * Integration is using the Knative profile + * All routes are either starting from an HTTP based consumer or a passive consumer (e.g. `direct` is a passive consumer) + type: boolean + autoscalingMetric: + description: |- + Configures the Knative autoscaling metric property (e.g. to set `concurrency` based or `cpu` based autoscaling). + + + Refer to the Knative documentation for more information. + type: string + autoscalingTarget: + description: |- + Sets the allowed concurrency level or CPU percentage (depending on the autoscaling metric) for each Pod. + + + Refer to the Knative documentation for more information. + type: integer + class: + description: |- + Configures the Knative autoscaling class property (e.g. to set `hpa.autoscaling.knative.dev` or `kpa.autoscaling.knative.dev` autoscaling). + + + Refer to the Knative documentation for more information. + enum: + - kpa.autoscaling.knative.dev + - hpa.autoscaling.knative.dev + type: string + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + maxScale: + description: |- + An upper bound for the number of Pods that can be running in parallel for the integration. + Knative has its own cap value that depends on the installation. + + + Refer to the Knative documentation for more information. + type: integer + minScale: + description: |- + The minimum number of Pods that should be running at any time for the integration. It's **zero** by default, meaning that + the integration is scaled down to zero when not used for a configured amount of time. + + + Refer to the Knative documentation for more information. + type: integer + rolloutDuration: + description: |- + Enables to gradually shift traffic to the latest Revision and sets the rollout duration. + It's disabled by default and must be expressed as a Golang `time.Duration` string representation, + rounded to a second precision. + type: string + timeoutSeconds: + description: |- + The maximum duration in seconds that the request instance is allowed to respond to a request. + This field propagates to the integration pod's terminationGracePeriodSeconds + + + Refer to the Knative documentation for more information. + format: int64 + type: integer + visibility: + description: |- + Setting `cluster-local`, Knative service becomes a private service. + Specifically, this option applies the `networking.knative.dev/visibility` label to Knative service. + + + Refer to the Knative documentation for more information. + enum: + - cluster-local + type: string + type: object + logging: + description: The configuration of Logging trait + properties: + color: + description: Colorize the log output + type: boolean + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + format: + description: Logs message format + type: string + json: + description: Output the logs in JSON + type: boolean + jsonPrettyPrint: + description: Enable "pretty printing" of the JSON logs + type: boolean + level: + description: Adjust the logging level (defaults to `INFO`) + enum: + - FATAL + - WARN + - INFO + - DEBUG + - TRACE + type: string + type: object + master: + description: 'Deprecated: for backward compatibility.' + properties: + configuration: + description: TraitConfiguration parameters configuration + type: object + x-kubernetes-preserve-unknown-fields: true + required: + - configuration + type: object + mount: + description: The configuration of Mount trait + properties: + configs: + description: |- + A list of configuration pointing to configmap/secret. + The configuration are expected to be UTF-8 resources as they are processed by runtime Camel Context and tried to be parsed as property files. + They are also made available on the classpath in order to ease their usage directly from the Route. + Syntax: [configmap|secret]:name[/key], where name represents the resource name and key optionally represents the resource key to be filtered + items: + type: string + type: array + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + emptyDirs: + description: 'A list of EmptyDir volumes to be mounted. Syntax: + [name:/container/path]' + items: + type: string + type: array + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + hotReload: + description: |- + Enable "hot reload" when a secret/configmap mounted is edited (default `false`). The configmap/secret must be + marked with `camel.apache.org/integration` label to be taken in account. The resource will be watched for any kind change, also for + changes in metadata. + type: boolean + resources: + description: |- + A list of resources (text or binary content) pointing to configmap/secret. + The resources are expected to be any resource type (text or binary content). + The destination path can be either a default location or any path specified by the user. + Syntax: [configmap|secret]:name[/key][@path], where name represents the resource name, key optionally represents the resource key to be filtered and path represents the destination path + items: + type: string + type: array + scanKameletsImplicitLabelSecrets: + description: 'Deprecated: no longer available since version + 2.5.' + type: boolean + volumes: + description: 'A list of Persistent Volume Claims to be mounted. + Syntax: [pvcname:/container/path]' + items: + type: string + type: array + type: object + openapi: + description: The configuration of OpenAPI trait + properties: + configmaps: + description: The configmaps holding the spec of the OpenAPI + (compatible with > 3.0 spec only). + items: + type: string + type: array + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + type: object + owner: + description: The configuration of Owner trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + targetAnnotations: + description: The set of annotations to be transferred + items: + type: string + type: array + targetLabels: + description: The set of labels to be transferred + items: + type: string + type: array + type: object + pdb: + description: The configuration of PDB trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + maxUnavailable: + description: |- + The number of pods for the Integration that can be unavailable after an eviction. + It can be either an absolute number or a percentage (default `1` if `min-available` is also not set). + Only one of `max-unavailable` and `min-available` can be specified. + type: string + minAvailable: + description: |- + The number of pods for the Integration that must still be available after an eviction. + It can be either an absolute number or a percentage. + Only one of `min-available` and `max-unavailable` can be specified. + type: string + type: object + platform: + description: The configuration of Platform trait + properties: + auto: + description: |- + To automatically detect from the environment if a default platform can be created (it will be created on OpenShift or when a registry address is set). + Deprecated: Platform is auto generated by the operator install procedure - maintained for backward compatibility + type: boolean + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + createDefault: + description: |- + To create a default (empty) platform when the platform is missing. + Deprecated: Platform is auto generated by the operator install procedure - maintained for backward compatibility + type: boolean + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + global: + description: |- + Indicates if the platform should be created globally in the case of global operator (default true). + Deprecated: Platform is auto generated by the operator install procedure - maintained for backward compatibility + type: boolean + type: object + pod: + description: The configuration of Pod trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + type: object + prometheus: + description: The configuration of Prometheus trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + podMonitor: + description: Whether a `PodMonitor` resource is created (default + `true`). + type: boolean + podMonitorLabels: + description: The `PodMonitor` resource labels, applicable + when `pod-monitor` is `true`. + items: + type: string + type: array + type: object + pull-secret: + description: The configuration of Pull Secret trait + properties: + auto: + description: Automatically configures the platform registry + secret on the pod if it is of type `kubernetes.io/dockerconfigjson`. + type: boolean + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + imagePullerDelegation: + description: When using a global operator with a shared platform, + this enables delegation of the `system:image-puller` cluster + role on the operator namespace to the integration service + account. + type: boolean + secretName: + description: The pull secret name to set on the Pod. If left + empty this is automatically taken from the `IntegrationPlatform` + registry configuration. + type: string + type: object + quarkus: + description: The configuration of Quarkus trait + properties: + buildMode: + description: |- + The Quarkus mode to run: either `jvm` or `native` (default `jvm`). + In case both `jvm` and `native` are specified, two `IntegrationKit` resources are created, + with the `native` kit having precedence over the `jvm` one once ready. + items: + description: QuarkusMode is the type of Quarkus build packaging. + enum: + - jvm + - native + type: string + type: array + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + nativeBaseImage: + description: The base image to use when running a native build + (default `quay.io/quarkus/quarkus-micro-image:2.0`) + type: string + nativeBuilderImage: + description: The image containing the tooling required for + a native build (by default it will use the one provided + in the runtime catalog) + type: string + packageTypes: + description: |- + The Quarkus package types, `fast-jar` or `native` (default `fast-jar`). + In case both `fast-jar` and `native` are specified, two `IntegrationKit` resources are created, + with the native kit having precedence over the `fast-jar` one once ready. + The order influences the resolution of the current kit for the integration. + The kit corresponding to the first package type will be assigned to the + integration in case no existing kit that matches the integration exists. + Deprecated: use `build-mode` instead. + items: + description: |- + QuarkusPackageType is the type of Quarkus build packaging. + Deprecated: use `QuarkusMode` instead. + enum: + - fast-jar + - native + type: string + type: array + type: object + registry: + description: |- + The configuration of Registry trait (support removed since version 2.5.0). + Deprecated: use jvm trait or read documentation. + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + type: object + route: + description: The configuration of Route trait + properties: + annotations: + additionalProperties: + type: string + description: |- + The annotations added to route. + This can be used to set route specific annotations + For annotations options see https://docs.openshift.com/container-platform/3.11/architecture/networking/routes.html#route-specific-annotations + CLI usage example: -t "route.annotations.'haproxy.router.openshift.io/balance'=true" + type: object + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + host: + description: To configure the host exposed by the route. + type: string + tlsCACertificate: + description: |- + The TLS CA certificate contents. + + + Refer to the OpenShift route documentation for additional information. + type: string + tlsCACertificateSecret: + description: |- + The secret name and key reference to the TLS CA certificate. The format is "secret-name[/key-name]", the value represents the secret name, if there is only one key in the secret it will be read, otherwise you can set a key name separated with a "/". + + + Refer to the OpenShift route documentation for additional information. + type: string + tlsCertificate: + description: |- + The TLS certificate contents. + + + Refer to the OpenShift route documentation for additional information. + type: string + tlsCertificateSecret: + description: |- + The secret name and key reference to the TLS certificate. The format is "secret-name[/key-name]", the value represents the secret name, if there is only one key in the secret it will be read, otherwise you can set a key name separated with a "/". + + + Refer to the OpenShift route documentation for additional information. + type: string + tlsDestinationCACertificate: + description: |- + The destination CA certificate provides the contents of the ca certificate of the final destination. When using reencrypt + termination this file should be provided in order to have routers use it for health checks on the secure connection. + If this field is not specified, the router may provide its own destination CA and perform hostname validation using + the short service name (service.namespace.svc), which allows infrastructure generated certificates to automatically + verify. + + + Refer to the OpenShift route documentation for additional information. + type: string + tlsDestinationCACertificateSecret: + description: |- + The secret name and key reference to the destination CA certificate. The format is "secret-name[/key-name]", the value represents the secret name, if there is only one key in the secret it will be read, otherwise you can set a key name separated with a "/". + + + Refer to the OpenShift route documentation for additional information. + type: string + tlsInsecureEdgeTerminationPolicy: + description: |- + To configure how to deal with insecure traffic, e.g. `Allow`, `Disable` or `Redirect` traffic. + + + Refer to the OpenShift route documentation for additional information. + enum: + - None + - Allow + - Redirect + type: string + tlsKey: + description: |- + The TLS certificate key contents. + + + Refer to the OpenShift route documentation for additional information. + type: string + tlsKeySecret: + description: |- + The secret name and key reference to the TLS certificate key. The format is "secret-name[/key-name]", the value represents the secret name, if there is only one key in the secret it will be read, otherwise you can set a key name separated with a "/". + + + Refer to the OpenShift route documentation for additional information. + type: string + tlsTermination: + description: |- + The TLS termination type, like `edge`, `passthrough` or `reencrypt`. + + + Refer to the OpenShift route documentation for additional information. + enum: + - edge + - reencrypt + - passthrough + type: string + type: object + security-context: + description: The configuration of Security Context trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + runAsNonRoot: + description: Security Context RunAsNonRoot configuration (default + false). + type: boolean + runAsUser: + description: 'Security Context RunAsUser configuration (default + none): this value is automatically retrieved in Openshift + clusters when not explicitly set.' + format: int64 + type: integer + seccompProfileType: + description: Security Context SeccompProfileType configuration + (default RuntimeDefault). + enum: + - Unconfined + - RuntimeDefault + type: string + type: object + service: + description: The configuration of Service trait + properties: + auto: + description: To automatically detect from the code if a Service + needs to be created. + type: boolean + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + nodePort: + description: |- + Enable Service to be exposed as NodePort (default `false`). + Deprecated: Use service type instead. + type: boolean + type: + description: The type of service to be used, either 'ClusterIP', + 'NodePort' or 'LoadBalancer'. + enum: + - ClusterIP + - NodePort + - LoadBalancer + type: string + type: object + service-binding: + description: The configuration of Service Binding trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + services: + description: List of Services in the form [[apigroup/]version:]kind:[namespace/]name + items: + type: string + type: array + type: object + strimzi: + description: 'Deprecated: for backward compatibility.' + properties: + configuration: + description: TraitConfiguration parameters configuration + type: object + x-kubernetes-preserve-unknown-fields: true + required: + - configuration + type: object + telemetry: + description: The configuration of Telemetry trait + properties: + auto: + description: Enables automatic configuration of the trait, + including automatic discovery of the telemetry endpoint. + type: boolean + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + endpoint: + description: The target endpoint of the Telemetry service + (automatically discovered by default) + type: string + sampler: + description: The sampler of the telemetry used for tracing + (default "on") + type: string + sampler-parent-based: + description: The sampler of the telemetry used for tracing + is parent based (default "true") + type: boolean + sampler-ratio: + description: The sampler ratio of the telemetry used for tracing + type: string + serviceName: + description: The name of the service that publishes telemetry + data (defaults to the integration name) + type: string + type: object + toleration: + description: The configuration of Toleration trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + taints: + description: The list of taints to tolerate, in the form `Key[=Value]:Effect[:Seconds]` + items: + type: string + type: array + type: object + tracing: + description: 'Deprecated: for backward compatibility.' + properties: + configuration: + description: TraitConfiguration parameters configuration + type: object + x-kubernetes-preserve-unknown-fields: true + required: + - configuration + type: object + type: object version: description: the operator version type: string diff --git a/pkg/apis/camel/v1/integration_types.go b/pkg/apis/camel/v1/integration_types.go index 96e429bd1a..d31db2cb27 100644 --- a/pkg/apis/camel/v1/integration_types.go +++ b/pkg/apis/camel/v1/integration_types.go @@ -97,6 +97,8 @@ type IntegrationStatus struct { Dependencies []string `json:"dependencies,omitempty"` // the profile needed to run this Integration Profile TraitProfile `json:"profile,omitempty"` + // the traits executed for the Integration + Traits *Traits `json:"traits,omitempty"` // the reference of the `IntegrationKit` which is used for this Integration IntegrationKit *corev1.ObjectReference `json:"integrationKit,omitempty"` // The IntegrationPlatform watching this Integration diff --git a/pkg/apis/camel/v1/zz_generated.deepcopy.go b/pkg/apis/camel/v1/zz_generated.deepcopy.go index 64fa10e2bd..a4362f7409 100644 --- a/pkg/apis/camel/v1/zz_generated.deepcopy.go +++ b/pkg/apis/camel/v1/zz_generated.deepcopy.go @@ -1838,6 +1838,11 @@ func (in *IntegrationStatus) DeepCopyInto(out *IntegrationStatus) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.Traits != nil { + in, out := &in.Traits, &out.Traits + *out = new(Traits) + (*in).DeepCopyInto(*out) + } if in.IntegrationKit != nil { in, out := &in.IntegrationKit, &out.IntegrationKit *out = new(corev1.ObjectReference) diff --git a/pkg/client/camel/applyconfiguration/camel/v1/integrationstatus.go b/pkg/client/camel/applyconfiguration/camel/v1/integrationstatus.go index 851decd192..12a1980cb8 100644 --- a/pkg/client/camel/applyconfiguration/camel/v1/integrationstatus.go +++ b/pkg/client/camel/applyconfiguration/camel/v1/integrationstatus.go @@ -34,6 +34,7 @@ type IntegrationStatusApplyConfiguration struct { Image *string `json:"image,omitempty"` Dependencies []string `json:"dependencies,omitempty"` Profile *v1.TraitProfile `json:"profile,omitempty"` + Traits *TraitsApplyConfiguration `json:"traits,omitempty"` IntegrationKit *corev1.ObjectReference `json:"integrationKit,omitempty"` Platform *string `json:"platform,omitempty"` GeneratedSources []SourceSpecApplyConfiguration `json:"generatedSources,omitempty"` @@ -105,6 +106,14 @@ func (b *IntegrationStatusApplyConfiguration) WithProfile(value v1.TraitProfile) return b } +// WithTraits sets the Traits field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Traits field is set to the value of the last call. +func (b *IntegrationStatusApplyConfiguration) WithTraits(value *TraitsApplyConfiguration) *IntegrationStatusApplyConfiguration { + b.Traits = value + return b +} + // WithIntegrationKit sets the IntegrationKit field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the IntegrationKit field is set to the value of the last call. diff --git a/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml b/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml index 20be245607..a89e55bd13 100644 --- a/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml +++ b/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml @@ -8357,6 +8357,1699 @@ spec: selector: description: label selector type: string + traits: + description: the traits executed for the Integration + properties: + 3scale: + description: 'Deprecated: for backward compatibility.' + properties: + configuration: + description: TraitConfiguration parameters configuration + type: object + x-kubernetes-preserve-unknown-fields: true + required: + - configuration + type: object + addons: + additionalProperties: + description: AddonTrait represents the configuration of an addon + trait. + type: object + x-kubernetes-preserve-unknown-fields: true + description: The extension point with addon traits + type: object + affinity: + description: The configuration of Affinity trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + nodeAffinityLabels: + description: Defines a set of nodes the integration pod(s) + are eligible to be scheduled on, based on labels on the + node. + items: + type: string + type: array + podAffinity: + description: Always co-locates multiple replicas of the integration + in the same node (default `false`). + type: boolean + podAffinityLabels: + description: |- + Defines a set of pods (namely those matching the label selector, relative to the given namespace) that the + integration pod(s) should be co-located with. + items: + type: string + type: array + podAntiAffinity: + description: Never co-locates multiple replicas of the integration + in the same node (default `false`). + type: boolean + podAntiAffinityLabels: + description: |- + Defines a set of pods (namely those matching the label selector, relative to the given namespace) that the + integration pod(s) should not be co-located with. + items: + type: string + type: array + type: object + builder: + description: The configuration of Builder trait + properties: + annotations: + additionalProperties: + type: string + description: When using `pod` strategy, annotation to use + for the builder pod. + type: object + baseImage: + description: |- + Specify a base image. In order to have the application working properly it must be a container image which has a Java JDK + installed and ready to use on path (ie `/usr/bin/java`). + type: string + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + incrementalImageBuild: + description: Use the incremental image build option, to reuse + existing containers (default `true`) + type: boolean + limitCPU: + description: |- + When using `pod` strategy, the maximum amount of CPU required by the pod builder. + Deprecated: use TasksRequestCPU instead with task name `builder`. + type: string + limitMemory: + description: |- + When using `pod` strategy, the maximum amount of memory required by the pod builder. + Deprecated: use TasksRequestCPU instead with task name `builder`. + type: string + mavenProfiles: + description: |- + A list of references pointing to configmaps/secrets that contains a maven profile. + This configmap/secret is a resource of the IntegrationKit created, therefore it needs to be present in the namespace where the operator is going to create the IntegrationKit. + The content of the maven profile is expected to be a text containing a valid maven profile starting with `` and ending with `` that will be integrated as an inline profile in the POM. + Syntax: [configmap|secret]:name[/key], where name represents the resource name, key optionally represents the resource key to be filtered (default key value = profile.xml). + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: Defines a set of nodes the builder pod is eligible + to be scheduled on, based on labels on the node. + type: object + orderStrategy: + description: The build order strategy to use, either `dependencies`, + `fifo` or `sequential` (default is the platform default) + enum: + - dependencies + - fifo + - sequential + type: string + platforms: + description: The list of manifest platforms to use to build + a container image (default `linux/amd64`). + items: + type: string + type: array + properties: + description: A list of properties to be provided to the build + task + items: + type: string + type: array + requestCPU: + description: |- + When using `pod` strategy, the minimum amount of CPU required by the pod builder. + Deprecated: use TasksRequestCPU instead with task name `builder`. + type: string + requestMemory: + description: |- + When using `pod` strategy, the minimum amount of memory required by the pod builder. + Deprecated: use TasksRequestCPU instead with task name `builder`. + type: string + strategy: + description: The strategy to use, either `pod` or `routine` + (default `routine`) + enum: + - pod + - routine + type: string + tasks: + description: A list of tasks to be executed (available only + when using `pod` strategy) with format `;;`. + items: + type: string + type: array + tasksFilter: + description: |- + A list of tasks sorted by the order of execution in a csv format, ie, `,,...`. + Mind that you must include also the operator tasks (`builder`, `quarkus-native`, `package`, `jib`, `s2i`) + if you need to execute them. Useful only with `pod` strategy. + type: string + tasksLimitCPU: + description: A list of limit cpu configuration for the specific + task with format `:`. + items: + type: string + type: array + tasksLimitMemory: + description: A list of limit memory configuration for the + specific task with format `:`. + items: + type: string + type: array + tasksRequestCPU: + description: A list of request cpu configuration for the specific + task with format `:`. + items: + type: string + type: array + tasksRequestMemory: + description: A list of request memory configuration for the + specific task with format `:`. + items: + type: string + type: array + verbose: + description: |- + Enable verbose logging on build components that support it (e.g. Kaniko build pod). + Deprecated no longer in use + type: boolean + type: object + camel: + description: The configuration of Camel trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + properties: + description: A list of properties to be provided to the Integration + runtime + items: + type: string + type: array + runtimeVersion: + description: |- + The camel-k-runtime version to use for the integration. It overrides the default version set in the Integration Platform. + You can use a fixed version (for example "3.2.3") or a semantic version (for example "3.x") which will try to resolve + to the best matching Catalog existing on the cluster. + type: string + type: object + container: + description: The configuration of Container trait + properties: + allowPrivilegeEscalation: + description: Security Context AllowPrivilegeEscalation configuration + (default false). + type: boolean + auto: + description: To automatically enable the trait + type: boolean + capabilitiesAdd: + description: Security Context Capabilities Add configuration + (default none). + items: + description: Capability represent POSIX capabilities type + type: string + type: array + capabilitiesDrop: + description: Security Context Capabilities Drop configuration + (default ALL). + items: + description: Capability represent POSIX capabilities type + type: string + type: array + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + expose: + description: Can be used to enable/disable exposure via kubernetes + Service. + type: boolean + image: + description: |- + The main container image to use for the Integration. When using this parameter the operator will create a synthetic IntegrationKit which + won't be able to execute traits requiring CamelCatalog. If the container image you're using is coming from an IntegrationKit, use instead + Integration `.spec.integrationKit` parameter. If you're moving the Integration across environments, you will also need to create an "external" IntegrationKit. + type: string + imagePullPolicy: + description: 'The pull policy: Always|Never|IfNotPresent' + enum: + - Always + - Never + - IfNotPresent + type: string + limitCPU: + description: The maximum amount of CPU to be provided (default + 500 millicores). + type: string + limitMemory: + description: The maximum amount of memory to be provided (default + 512 Mi). + type: string + name: + description: The main container name. It's named `integration` + by default. + type: string + port: + description: To configure a different port exposed by the + container (default `8080`). + type: integer + portName: + description: To configure a different port name for the port + exposed by the container. It defaults to `http` only when + the `expose` parameter is true. + type: string + requestCPU: + description: The minimum amount of CPU required (default 125 + millicores). + type: string + requestMemory: + description: The minimum amount of memory required (default + 128 Mi). + type: string + runAsNonRoot: + description: Security Context RunAsNonRoot configuration (default + false). + type: boolean + runAsUser: + description: 'Security Context RunAsUser configuration (default + none): this value is automatically retrieved in Openshift + clusters when not explicitly set.' + format: int64 + type: integer + seccompProfileType: + description: Security Context SeccompProfileType configuration + (default RuntimeDefault). + enum: + - Unconfined + - RuntimeDefault + type: string + servicePort: + description: To configure under which service port the container + port is to be exposed (default `80`). + type: integer + servicePortName: + description: To configure under which service port name the + container port is to be exposed (default `http`). + type: string + type: object + cron: + description: The configuration of Cron trait + properties: + activeDeadlineSeconds: + description: |- + Specifies the duration in seconds, relative to the start time, that the job + may be continuously active before it is considered to be failed. + It defaults to 60s. + format: int64 + type: integer + auto: + description: |- + Automatically deploy the integration as CronJob when all routes are + either starting from a periodic consumer (only `cron`, `timer` and `quartz` are supported) or a passive consumer (e.g. `direct` is a passive consumer). + + + It's required that all periodic consumers have the same period, and it can be expressed as cron schedule (e.g. `1m` can be expressed as `0/1 * * * *`, + while `35m` or `50s` cannot). + type: boolean + backoffLimit: + description: |- + Specifies the number of retries before marking the job failed. + It defaults to 2. + format: int32 + type: integer + components: + description: |- + A comma separated list of the Camel components that need to be customized in order for them to work when the schedule is triggered externally by Kubernetes. + A specific customizer is activated for each specified component. E.g. for the `timer` component, the `cron-timer` customizer is + activated (it's present in the `org.apache.camel.k:camel-k-cron` library). + + + Supported components are currently: `cron`, `timer` and `quartz`. + type: string + concurrencyPolicy: + description: |- + Specifies how to treat concurrent executions of a Job. + Valid values are: + - "Allow": allows CronJobs to run concurrently; + - "Forbid" (default): forbids concurrent runs, skipping next run if previous run hasn't finished yet; + - "Replace": cancels currently running job and replaces it with a new one + enum: + - Allow + - Forbid + - Replace + type: string + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + fallback: + description: |- + Use the default Camel implementation of the `cron` endpoint (`quartz`) instead of trying to materialize the integration + as Kubernetes CronJob. + type: boolean + schedule: + description: |- + The CronJob schedule for the whole integration. If multiple routes are declared, they must have the same schedule for this + mechanism to work correctly. + type: string + startingDeadlineSeconds: + description: |- + Optional deadline in seconds for starting the job if it misses scheduled + time for any reason. Missed jobs executions will be counted as failed ones. + format: int64 + type: integer + timeZone: + description: The timezone that the CronJob will run on + type: string + type: object + dependencies: + description: The configuration of Dependencies trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + type: object + deployer: + description: The configuration of Deployer trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + kind: + description: Allows to explicitly select the desired deployment + kind between `deployment`, `cron-job` or `knative-service` + when creating the resources for running the integration. + enum: + - deployment + - cron-job + - knative-service + type: string + useSSA: + description: |- + Deprecated: won't be able to enforce client side update in the future. + Use server-side apply to update the owned resources (default `true`). + Note that it automatically falls back to client-side patching, if SSA is not available, e.g., on old Kubernetes clusters. + type: boolean + type: object + deployment: + description: The configuration of Deployment trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + progressDeadlineSeconds: + description: |- + The maximum time in seconds for the deployment to make progress before it + is considered to be failed. It defaults to `60s`. + format: int32 + type: integer + rollingUpdateMaxSurge: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of pods that can be scheduled above the desired number of + pods. + Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). + This can not be 0 if MaxUnavailable is 0. + Absolute number is calculated from percentage by rounding up. + Defaults to `25%`. + x-kubernetes-int-or-string: true + rollingUpdateMaxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of pods that can be unavailable during the update. + Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). + Absolute number is calculated from percentage by rounding down. + This can not be 0 if MaxSurge is 0. + Defaults to `25%`. + x-kubernetes-int-or-string: true + strategy: + description: The deployment strategy to use to replace existing + pods with new ones. + enum: + - Recreate + - RollingUpdate + type: string + type: object + environment: + description: The configuration of Environment trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + containerMeta: + description: Enables injection of `NAMESPACE` and `POD_NAME` + environment variables (default `true`) + type: boolean + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + httpProxy: + description: Propagates the `HTTP_PROXY`, `HTTPS_PROXY` and + `NO_PROXY` environment variables (default `true`) + type: boolean + vars: + description: |- + A list of environment variables to be added to the integration container. + The syntax is either VAR=VALUE or VAR=[configmap|secret]:name/key, where name represents the resource name, + and key represents the resource key to be mapped as and environment variable. + These take precedence over any previously defined environment variables. + items: + type: string + type: array + type: object + error-handler: + description: The configuration of Error Handler trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + ref: + description: The error handler ref name provided or found + in application properties + type: string + type: object + gc: + description: The configuration of GC trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + discoveryCache: + description: |- + Discovery client cache to be used, either `disabled`, `disk` or `memory` (default `memory`). + Deprecated: to be removed from trait configuration. + enum: + - disabled + - disk + - memory + type: string + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + type: object + health: + description: The configuration of Health trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + livenessFailureThreshold: + description: Minimum consecutive failures for the liveness + probe to be considered failed after having succeeded. + format: int32 + type: integer + livenessInitialDelay: + description: Number of seconds after the container has started + before the liveness probe is initiated. + format: int32 + type: integer + livenessPeriod: + description: How often to perform the liveness probe. + format: int32 + type: integer + livenessProbe: + description: The liveness probe path to use (default provided + by the Catalog runtime used). + type: string + livenessProbeEnabled: + description: Configures the liveness probe for the integration + container (default `false`). + type: boolean + livenessScheme: + description: Scheme to use when connecting to the liveness + probe (default `HTTP`). + type: string + livenessSuccessThreshold: + description: Minimum consecutive successes for the liveness + probe to be considered successful after having failed. + format: int32 + type: integer + livenessTimeout: + description: Number of seconds after which the liveness probe + times out. + format: int32 + type: integer + readinessFailureThreshold: + description: Minimum consecutive failures for the readiness + probe to be considered failed after having succeeded. + format: int32 + type: integer + readinessInitialDelay: + description: Number of seconds after the container has started + before the readiness probe is initiated. + format: int32 + type: integer + readinessPeriod: + description: How often to perform the readiness probe. + format: int32 + type: integer + readinessProbe: + description: The readiness probe path to use (default provided + by the Catalog runtime used). + type: string + readinessProbeEnabled: + description: Configures the readiness probe for the integration + container (default `true`). + type: boolean + readinessScheme: + description: Scheme to use when connecting to the readiness + probe (default `HTTP`). + type: string + readinessSuccessThreshold: + description: Minimum consecutive successes for the readiness + probe to be considered successful after having failed. + format: int32 + type: integer + readinessTimeout: + description: Number of seconds after which the readiness probe + times out. + format: int32 + type: integer + startupFailureThreshold: + description: Minimum consecutive failures for the startup + probe to be considered failed after having succeeded. + format: int32 + type: integer + startupInitialDelay: + description: Number of seconds after the container has started + before the startup probe is initiated. + format: int32 + type: integer + startupPeriod: + description: How often to perform the startup probe. + format: int32 + type: integer + startupProbe: + description: The startup probe path to use (default provided + by the Catalog runtime used). + type: string + startupProbeEnabled: + description: Configures the startup probe for the integration + container (default `false`). + type: boolean + startupScheme: + description: Scheme to use when connecting to the startup + probe (default `HTTP`). + type: string + startupSuccessThreshold: + description: Minimum consecutive successes for the startup + probe to be considered successful after having failed. + format: int32 + type: integer + startupTimeout: + description: Number of seconds after which the startup probe + times out. + format: int32 + type: integer + type: object + ingress: + description: The configuration of Ingress trait + properties: + annotations: + additionalProperties: + type: string + description: |- + The annotations added to the ingress. + This can be used to set controller specific annotations, e.g., when using the NGINX Ingress controller: + See https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/annotations.md + type: object + auto: + description: To automatically add an ingress whenever the + integration uses an HTTP endpoint consumer. + type: boolean + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + host: + description: To configure the host exposed by the ingress. + type: string + ingressClassName: + description: |- + The Ingress class name as defined by the Ingress spec + See https://kubernetes.io/docs/concepts/services-networking/ingress/ + type: string + path: + description: To configure the path exposed by the ingress + (default `/`). + type: string + pathType: + description: |- + To configure the path type exposed by the ingress. + One of `Exact`, `Prefix`, `ImplementationSpecific` (default to `Prefix`). + enum: + - Exact + - Prefix + - ImplementationSpecific + type: string + tlsHosts: + description: To configure tls hosts + items: + type: string + type: array + tlsSecretName: + description: To configure tls secret name + type: string + type: object + istio: + description: The configuration of Istio trait + properties: + allow: + description: Configures a (comma-separated) list of CIDR subnets + that should not be intercepted by the Istio proxy (`10.0.0.0/8,172.16.0.0/12,192.168.0.0/16` + by default). + type: string + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + inject: + description: Forces the value for labels `sidecar.istio.io/inject`. + By default the label is set to `true` on deployment and + not set on Knative Service. + type: boolean + type: object + jolokia: + description: The configuration of Jolokia trait + properties: + CACert: + description: |- + The PEM encoded CA certification file path, used to verify client certificates, + applicable when `protocol` is `https` and `use-ssl-client-authentication` is `true` + (default `/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt` for OpenShift). + type: string + clientPrincipal: + description: |- + The principal(s) which must be given in a client certificate to allow access to the Jolokia endpoint, + applicable when `protocol` is `https` and `use-ssl-client-authentication` is `true` + (default `clientPrincipal=cn=system:master-proxy`, `cn=hawtio-online.hawtio.svc` and `cn=fuse-console.fuse.svc` for OpenShift). + items: + type: string + type: array + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + discoveryEnabled: + description: Listen for multicast requests (default `false`) + type: boolean + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + extendedClientCheck: + description: |- + Mandate the client certificate contains a client flag in the extended key usage section, + applicable when `protocol` is `https` and `use-ssl-client-authentication` is `true` + (default `true` for OpenShift). + type: boolean + host: + description: |- + The Host address to which the Jolokia agent should bind to. If `"\*"` or `"0.0.0.0"` is given, + the servers binds to every network interface (default `"*"`). + type: string + options: + description: |- + A list of additional Jolokia options as defined + in https://jolokia.org/reference/html/agents.html#agent-jvm-config[JVM agent configuration options] + items: + type: string + type: array + password: + description: The password used for authentication, applicable + when the `user` option is set. + type: string + port: + description: The Jolokia endpoint port (default `8778`). + type: integer + protocol: + description: The protocol to use, either `http` or `https` + (default `https` for OpenShift) + type: string + useSSLClientAuthentication: + description: Whether client certificates should be used for + authentication (default `true` for OpenShift). + type: boolean + user: + description: The user to be used for authentication + type: string + type: object + jvm: + description: The configuration of JVM trait + properties: + classpath: + description: Additional JVM classpath (use `Linux` classpath + separator) + type: string + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + debug: + description: Activates remote debugging, so that a debugger + can be attached to the JVM, e.g., using port-forwarding + type: boolean + debugAddress: + description: Transport address at which to listen for the + newly launched JVM (default `*:5005`) + type: string + debugSuspend: + description: Suspends the target JVM immediately before the + main class is loaded + type: boolean + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + jar: + description: The Jar dependency which will run the application. + Leave it empty for managed Integrations. + type: string + options: + description: A list of JVM options + items: + type: string + type: array + printCommand: + description: |- + Prints the command used the start the JVM in the container logs (default `true`) + Deprecated: no longer in use. + type: boolean + type: object + kamelets: + description: The configuration of Kamelets trait + properties: + auto: + description: Automatically inject all referenced Kamelets + and their default configuration (enabled by default) + type: boolean + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + list: + description: Comma separated list of Kamelet names to load + into the current integration + type: string + mountPoint: + description: The directory where the application mounts and + reads Kamelet spec (default `/etc/camel/kamelets`) + type: string + type: object + keda: + description: 'Deprecated: for backward compatibility.' + properties: + configuration: + description: TraitConfiguration parameters configuration + type: object + x-kubernetes-preserve-unknown-fields: true + required: + - configuration + type: object + knative: + description: The configuration of Knative trait + properties: + auto: + description: Enable automatic discovery of all trait properties. + type: boolean + channelSinks: + description: |- + List of channels used as destination of integration routes. + Can contain simple channel names or full Camel URIs. + items: + type: string + type: array + channelSources: + description: |- + List of channels used as source of integration routes. + Can contain simple channel names or full Camel URIs. + items: + type: string + type: array + config: + description: Can be used to inject a Knative complete configuration + in JSON format. + type: string + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + endpointSinks: + description: |- + List of endpoints used as destination of integration routes. + Can contain simple endpoint names or full Camel URIs. + items: + type: string + type: array + endpointSources: + description: List of channels used as source of integration + routes. + items: + type: string + type: array + eventSinks: + description: |- + List of event types that the integration will produce. + Can contain simple event types or full Camel URIs (to use a specific broker). + items: + type: string + type: array + eventSources: + description: |- + List of event types that the integration will be subscribed to. + Can contain simple event types or full Camel URIs (to use a specific broker different from "default"). + items: + type: string + type: array + filterEventType: + description: |- + Enables the default filtering for the Knative trigger using the event type + If this is true, the created Knative trigger uses the event type as a filter on the event stream when no other filter criteria is given. (default: true) + type: boolean + filterSourceChannels: + description: |- + Enables filtering on events based on the header "ce-knativehistory". Since this header has been removed in newer versions of + Knative, filtering is disabled by default. + type: boolean + filters: + description: |- + Sets filter attributes on the event stream (such as event type, source, subject and so on). + A list of key-value pairs that represent filter attributes and its values. + The syntax is KEY=VALUE, e.g., `source="my.source"`. + Filter attributes get set on the Knative trigger that is being created as part of this integration. + items: + type: string + type: array + namespaceLabel: + description: |- + Enables the camel-k-operator to set the "bindings.knative.dev/include=true" label to the namespace + As Knative requires this label to perform injection of K_SINK URL into the service. + If this is false, the integration pod may start and fail, read the SinkBinding Knative documentation. (default: true) + type: boolean + sinkBinding: + description: |- + Allows binding the integration to a sink via a Knative SinkBinding resource. + This can be used when the integration targets a single sink. + It's enabled by default when the integration targets a single sink + (except when the integration is owned by a Knative source). + type: boolean + type: object + knative-service: + description: The configuration of Knative Service trait + properties: + annotations: + additionalProperties: + type: string + description: |- + The annotations added to route. + This can be used to set knative service specific annotations + CLI usage example: -t "knative-service.annotations.'haproxy.router.openshift.io/balance'=true" + type: object + auto: + description: |- + Automatically deploy the integration as Knative service when all conditions hold: + + + * Integration is using the Knative profile + * All routes are either starting from an HTTP based consumer or a passive consumer (e.g. `direct` is a passive consumer) + type: boolean + autoscalingMetric: + description: |- + Configures the Knative autoscaling metric property (e.g. to set `concurrency` based or `cpu` based autoscaling). + + + Refer to the Knative documentation for more information. + type: string + autoscalingTarget: + description: |- + Sets the allowed concurrency level or CPU percentage (depending on the autoscaling metric) for each Pod. + + + Refer to the Knative documentation for more information. + type: integer + class: + description: |- + Configures the Knative autoscaling class property (e.g. to set `hpa.autoscaling.knative.dev` or `kpa.autoscaling.knative.dev` autoscaling). + + + Refer to the Knative documentation for more information. + enum: + - kpa.autoscaling.knative.dev + - hpa.autoscaling.knative.dev + type: string + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + maxScale: + description: |- + An upper bound for the number of Pods that can be running in parallel for the integration. + Knative has its own cap value that depends on the installation. + + + Refer to the Knative documentation for more information. + type: integer + minScale: + description: |- + The minimum number of Pods that should be running at any time for the integration. It's **zero** by default, meaning that + the integration is scaled down to zero when not used for a configured amount of time. + + + Refer to the Knative documentation for more information. + type: integer + rolloutDuration: + description: |- + Enables to gradually shift traffic to the latest Revision and sets the rollout duration. + It's disabled by default and must be expressed as a Golang `time.Duration` string representation, + rounded to a second precision. + type: string + timeoutSeconds: + description: |- + The maximum duration in seconds that the request instance is allowed to respond to a request. + This field propagates to the integration pod's terminationGracePeriodSeconds + + + Refer to the Knative documentation for more information. + format: int64 + type: integer + visibility: + description: |- + Setting `cluster-local`, Knative service becomes a private service. + Specifically, this option applies the `networking.knative.dev/visibility` label to Knative service. + + + Refer to the Knative documentation for more information. + enum: + - cluster-local + type: string + type: object + logging: + description: The configuration of Logging trait + properties: + color: + description: Colorize the log output + type: boolean + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + format: + description: Logs message format + type: string + json: + description: Output the logs in JSON + type: boolean + jsonPrettyPrint: + description: Enable "pretty printing" of the JSON logs + type: boolean + level: + description: Adjust the logging level (defaults to `INFO`) + enum: + - FATAL + - WARN + - INFO + - DEBUG + - TRACE + type: string + type: object + master: + description: 'Deprecated: for backward compatibility.' + properties: + configuration: + description: TraitConfiguration parameters configuration + type: object + x-kubernetes-preserve-unknown-fields: true + required: + - configuration + type: object + mount: + description: The configuration of Mount trait + properties: + configs: + description: |- + A list of configuration pointing to configmap/secret. + The configuration are expected to be UTF-8 resources as they are processed by runtime Camel Context and tried to be parsed as property files. + They are also made available on the classpath in order to ease their usage directly from the Route. + Syntax: [configmap|secret]:name[/key], where name represents the resource name and key optionally represents the resource key to be filtered + items: + type: string + type: array + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + emptyDirs: + description: 'A list of EmptyDir volumes to be mounted. Syntax: + [name:/container/path]' + items: + type: string + type: array + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + hotReload: + description: |- + Enable "hot reload" when a secret/configmap mounted is edited (default `false`). The configmap/secret must be + marked with `camel.apache.org/integration` label to be taken in account. The resource will be watched for any kind change, also for + changes in metadata. + type: boolean + resources: + description: |- + A list of resources (text or binary content) pointing to configmap/secret. + The resources are expected to be any resource type (text or binary content). + The destination path can be either a default location or any path specified by the user. + Syntax: [configmap|secret]:name[/key][@path], where name represents the resource name, key optionally represents the resource key to be filtered and path represents the destination path + items: + type: string + type: array + scanKameletsImplicitLabelSecrets: + description: 'Deprecated: no longer available since version + 2.5.' + type: boolean + volumes: + description: 'A list of Persistent Volume Claims to be mounted. + Syntax: [pvcname:/container/path]' + items: + type: string + type: array + type: object + openapi: + description: The configuration of OpenAPI trait + properties: + configmaps: + description: The configmaps holding the spec of the OpenAPI + (compatible with > 3.0 spec only). + items: + type: string + type: array + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + type: object + owner: + description: The configuration of Owner trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + targetAnnotations: + description: The set of annotations to be transferred + items: + type: string + type: array + targetLabels: + description: The set of labels to be transferred + items: + type: string + type: array + type: object + pdb: + description: The configuration of PDB trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + maxUnavailable: + description: |- + The number of pods for the Integration that can be unavailable after an eviction. + It can be either an absolute number or a percentage (default `1` if `min-available` is also not set). + Only one of `max-unavailable` and `min-available` can be specified. + type: string + minAvailable: + description: |- + The number of pods for the Integration that must still be available after an eviction. + It can be either an absolute number or a percentage. + Only one of `min-available` and `max-unavailable` can be specified. + type: string + type: object + platform: + description: The configuration of Platform trait + properties: + auto: + description: |- + To automatically detect from the environment if a default platform can be created (it will be created on OpenShift or when a registry address is set). + Deprecated: Platform is auto generated by the operator install procedure - maintained for backward compatibility + type: boolean + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + createDefault: + description: |- + To create a default (empty) platform when the platform is missing. + Deprecated: Platform is auto generated by the operator install procedure - maintained for backward compatibility + type: boolean + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + global: + description: |- + Indicates if the platform should be created globally in the case of global operator (default true). + Deprecated: Platform is auto generated by the operator install procedure - maintained for backward compatibility + type: boolean + type: object + pod: + description: The configuration of Pod trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + type: object + prometheus: + description: The configuration of Prometheus trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + podMonitor: + description: Whether a `PodMonitor` resource is created (default + `true`). + type: boolean + podMonitorLabels: + description: The `PodMonitor` resource labels, applicable + when `pod-monitor` is `true`. + items: + type: string + type: array + type: object + pull-secret: + description: The configuration of Pull Secret trait + properties: + auto: + description: Automatically configures the platform registry + secret on the pod if it is of type `kubernetes.io/dockerconfigjson`. + type: boolean + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + imagePullerDelegation: + description: When using a global operator with a shared platform, + this enables delegation of the `system:image-puller` cluster + role on the operator namespace to the integration service + account. + type: boolean + secretName: + description: The pull secret name to set on the Pod. If left + empty this is automatically taken from the `IntegrationPlatform` + registry configuration. + type: string + type: object + quarkus: + description: The configuration of Quarkus trait + properties: + buildMode: + description: |- + The Quarkus mode to run: either `jvm` or `native` (default `jvm`). + In case both `jvm` and `native` are specified, two `IntegrationKit` resources are created, + with the `native` kit having precedence over the `jvm` one once ready. + items: + description: QuarkusMode is the type of Quarkus build packaging. + enum: + - jvm + - native + type: string + type: array + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + nativeBaseImage: + description: The base image to use when running a native build + (default `quay.io/quarkus/quarkus-micro-image:2.0`) + type: string + nativeBuilderImage: + description: The image containing the tooling required for + a native build (by default it will use the one provided + in the runtime catalog) + type: string + packageTypes: + description: |- + The Quarkus package types, `fast-jar` or `native` (default `fast-jar`). + In case both `fast-jar` and `native` are specified, two `IntegrationKit` resources are created, + with the native kit having precedence over the `fast-jar` one once ready. + The order influences the resolution of the current kit for the integration. + The kit corresponding to the first package type will be assigned to the + integration in case no existing kit that matches the integration exists. + Deprecated: use `build-mode` instead. + items: + description: |- + QuarkusPackageType is the type of Quarkus build packaging. + Deprecated: use `QuarkusMode` instead. + enum: + - fast-jar + - native + type: string + type: array + type: object + registry: + description: |- + The configuration of Registry trait (support removed since version 2.5.0). + Deprecated: use jvm trait or read documentation. + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + type: object + route: + description: The configuration of Route trait + properties: + annotations: + additionalProperties: + type: string + description: |- + The annotations added to route. + This can be used to set route specific annotations + For annotations options see https://docs.openshift.com/container-platform/3.11/architecture/networking/routes.html#route-specific-annotations + CLI usage example: -t "route.annotations.'haproxy.router.openshift.io/balance'=true" + type: object + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + host: + description: To configure the host exposed by the route. + type: string + tlsCACertificate: + description: |- + The TLS CA certificate contents. + + + Refer to the OpenShift route documentation for additional information. + type: string + tlsCACertificateSecret: + description: |- + The secret name and key reference to the TLS CA certificate. The format is "secret-name[/key-name]", the value represents the secret name, if there is only one key in the secret it will be read, otherwise you can set a key name separated with a "/". + + + Refer to the OpenShift route documentation for additional information. + type: string + tlsCertificate: + description: |- + The TLS certificate contents. + + + Refer to the OpenShift route documentation for additional information. + type: string + tlsCertificateSecret: + description: |- + The secret name and key reference to the TLS certificate. The format is "secret-name[/key-name]", the value represents the secret name, if there is only one key in the secret it will be read, otherwise you can set a key name separated with a "/". + + + Refer to the OpenShift route documentation for additional information. + type: string + tlsDestinationCACertificate: + description: |- + The destination CA certificate provides the contents of the ca certificate of the final destination. When using reencrypt + termination this file should be provided in order to have routers use it for health checks on the secure connection. + If this field is not specified, the router may provide its own destination CA and perform hostname validation using + the short service name (service.namespace.svc), which allows infrastructure generated certificates to automatically + verify. + + + Refer to the OpenShift route documentation for additional information. + type: string + tlsDestinationCACertificateSecret: + description: |- + The secret name and key reference to the destination CA certificate. The format is "secret-name[/key-name]", the value represents the secret name, if there is only one key in the secret it will be read, otherwise you can set a key name separated with a "/". + + + Refer to the OpenShift route documentation for additional information. + type: string + tlsInsecureEdgeTerminationPolicy: + description: |- + To configure how to deal with insecure traffic, e.g. `Allow`, `Disable` or `Redirect` traffic. + + + Refer to the OpenShift route documentation for additional information. + enum: + - None + - Allow + - Redirect + type: string + tlsKey: + description: |- + The TLS certificate key contents. + + + Refer to the OpenShift route documentation for additional information. + type: string + tlsKeySecret: + description: |- + The secret name and key reference to the TLS certificate key. The format is "secret-name[/key-name]", the value represents the secret name, if there is only one key in the secret it will be read, otherwise you can set a key name separated with a "/". + + + Refer to the OpenShift route documentation for additional information. + type: string + tlsTermination: + description: |- + The TLS termination type, like `edge`, `passthrough` or `reencrypt`. + + + Refer to the OpenShift route documentation for additional information. + enum: + - edge + - reencrypt + - passthrough + type: string + type: object + security-context: + description: The configuration of Security Context trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: 'Deprecated: no longer in use.' + type: boolean + runAsNonRoot: + description: Security Context RunAsNonRoot configuration (default + false). + type: boolean + runAsUser: + description: 'Security Context RunAsUser configuration (default + none): this value is automatically retrieved in Openshift + clusters when not explicitly set.' + format: int64 + type: integer + seccompProfileType: + description: Security Context SeccompProfileType configuration + (default RuntimeDefault). + enum: + - Unconfined + - RuntimeDefault + type: string + type: object + service: + description: The configuration of Service trait + properties: + auto: + description: To automatically detect from the code if a Service + needs to be created. + type: boolean + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + nodePort: + description: |- + Enable Service to be exposed as NodePort (default `false`). + Deprecated: Use service type instead. + type: boolean + type: + description: The type of service to be used, either 'ClusterIP', + 'NodePort' or 'LoadBalancer'. + enum: + - ClusterIP + - NodePort + - LoadBalancer + type: string + type: object + service-binding: + description: The configuration of Service Binding trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + services: + description: List of Services in the form [[apigroup/]version:]kind:[namespace/]name + items: + type: string + type: array + type: object + strimzi: + description: 'Deprecated: for backward compatibility.' + properties: + configuration: + description: TraitConfiguration parameters configuration + type: object + x-kubernetes-preserve-unknown-fields: true + required: + - configuration + type: object + telemetry: + description: The configuration of Telemetry trait + properties: + auto: + description: Enables automatic configuration of the trait, + including automatic discovery of the telemetry endpoint. + type: boolean + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + endpoint: + description: The target endpoint of the Telemetry service + (automatically discovered by default) + type: string + sampler: + description: The sampler of the telemetry used for tracing + (default "on") + type: string + sampler-parent-based: + description: The sampler of the telemetry used for tracing + is parent based (default "true") + type: boolean + sampler-ratio: + description: The sampler ratio of the telemetry used for tracing + type: string + serviceName: + description: The name of the service that publishes telemetry + data (defaults to the integration name) + type: string + type: object + toleration: + description: The configuration of Toleration trait + properties: + configuration: + description: |- + Legacy trait configuration parameters. + Deprecated: for backward compatibility. + type: object + x-kubernetes-preserve-unknown-fields: true + enabled: + description: Can be used to enable or disable a trait. All + traits share this common property. + type: boolean + taints: + description: The list of taints to tolerate, in the form `Key[=Value]:Effect[:Seconds]` + items: + type: string + type: array + type: object + tracing: + description: 'Deprecated: for backward compatibility.' + properties: + configuration: + description: TraitConfiguration parameters configuration + type: object + x-kubernetes-preserve-unknown-fields: true + required: + - configuration + type: object + type: object version: description: the operator version type: string diff --git a/pkg/trait/affinity.go b/pkg/trait/affinity.go index caa9278350..43f0485237 100644 --- a/pkg/trait/affinity.go +++ b/pkg/trait/affinity.go @@ -44,10 +44,6 @@ type affinityTrait struct { func newAffinityTrait() Trait { return &affinityTrait{ BaseTrait: NewBaseTrait(affinityTraitID, affinityTraitOrder), - AffinityTrait: traitv1.AffinityTrait{ - PodAffinity: ptr.To(false), - PodAntiAffinity: ptr.To(false), - }, } } diff --git a/pkg/trait/builder_test.go b/pkg/trait/builder_test.go index a096c6e7c8..aaa30c00f7 100644 --- a/pkg/trait/builder_test.go +++ b/pkg/trait/builder_test.go @@ -67,10 +67,11 @@ func TestBuilderTraitNotAppliedBecauseOfNilPhase(t *testing.T) { e.IntegrationKit.Status.Phase = v1.IntegrationKitPhaseInitialization t.Run(string(e.Platform.Status.Cluster), func(t *testing.T) { - conditions, err := NewBuilderTestCatalog().apply(e) + conditions, traits, err := NewBuilderTestCatalog().apply(e) require.NoError(t, err) assert.NotEmpty(t, conditions) + assert.Empty(t, traits) assert.NotEmpty(t, e.ExecutedTraits) assert.Nil(t, e.GetTrait("builder")) assert.Empty(t, e.Pipeline) @@ -80,9 +81,10 @@ func TestBuilderTraitNotAppliedBecauseOfNilPhase(t *testing.T) { func TestS2IBuilderTrait(t *testing.T) { env := createBuilderTestEnv(v1.IntegrationPlatformClusterOpenShift, v1.IntegrationPlatformBuildPublishStrategyS2I, v1.BuildStrategyRoutine) - conditions, err := NewBuilderTestCatalog().apply(env) + conditions, traits, err := NewBuilderTestCatalog().apply(env) require.NoError(t, err) + assert.Empty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, env.ExecutedTraits) assert.NotNil(t, env.GetTrait("builder")) @@ -97,9 +99,10 @@ func TestS2IBuilderTrait(t *testing.T) { func TestJibBuilderTrait(t *testing.T) { env := createBuilderTestEnv(v1.IntegrationPlatformClusterOpenShift, v1.IntegrationPlatformBuildPublishStrategyJib, v1.BuildStrategyRoutine) - conditions, err := NewBuilderTestCatalog().apply(env) + conditions, traits, err := NewBuilderTestCatalog().apply(env) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, env.ExecutedTraits) assert.NotNil(t, env.GetTrait("builder")) diff --git a/pkg/trait/camel.go b/pkg/trait/camel.go index 698f360514..df94312aec 100644 --- a/pkg/trait/camel.go +++ b/pkg/trait/camel.go @@ -42,6 +42,8 @@ const ( type camelTrait struct { BasePlatformTrait traitv1.CamelTrait `property:",squash"` + // private configuration used only internally + runtimeVersion string } func newCamelTrait() Trait { @@ -73,8 +75,10 @@ func (t *camelTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if runtimeVersion, err := determineRuntimeVersion(e); err != nil { return false, nil, err } else { - t.RuntimeVersion = runtimeVersion + t.runtimeVersion = runtimeVersion } + } else { + t.runtimeVersion = t.RuntimeVersion } var cond *TraitCondition @@ -89,7 +93,7 @@ func (t *camelTrait) Configure(e *Environment) (bool, *TraitCondition, error) { traitConfigurationReason, fmt.Sprintf( "Operated with CamelCatalog version %s which may be different from the runtime used in the container", - t.RuntimeVersion, + t.runtimeVersion, ), ) } @@ -102,7 +106,7 @@ func (t *camelTrait) Apply(e *Environment) error { // expects a CamelCatalog to be loaded regardless it's a managed or // non managed build Integration if e.CamelCatalog == nil { - if err := t.loadOrCreateCatalog(e, t.RuntimeVersion); err != nil { + if err := t.loadOrCreateCatalog(e, t.runtimeVersion); err != nil { return err } } diff --git a/pkg/trait/container.go b/pkg/trait/container.go index 80c116991d..f3f24deb3b 100644 --- a/pkg/trait/container.go +++ b/pkg/trait/container.go @@ -66,20 +66,6 @@ type containerTrait struct { func newContainerTrait() Trait { return &containerTrait{ BasePlatformTrait: NewBasePlatformTrait(containerTraitID, containerTraitOrder), - ContainerTrait: traitv1.ContainerTrait{ - Port: defaultContainerPort, - ServicePort: defaultServicePort, - ServicePortName: defaultContainerPortName, - Name: defaultContainerName, - RunAsNonRoot: ptr.To(defaultContainerRunAsNonRoot), - SeccompProfileType: defaultContainerSeccompProfileType, - AllowPrivilegeEscalation: ptr.To(defaultContainerAllowPrivilegeEscalation), - CapabilitiesDrop: []corev1.Capability{defaultContainerCapabilitiesDrop}, - RequestCPU: defaultContainerResourceCPU, - RequestMemory: defaultContainerResourceMemory, - LimitCPU: defaultContainerLimitCPU, - LimitMemory: defaultContainerLimitMemory, - }, } } @@ -94,8 +80,9 @@ func (t *containerTrait) Configure(e *Environment) (bool, *TraitCondition, error if ptr.Deref(t.Auto, true) { if t.Expose == nil { - e := e.Resources.GetServiceForIntegration(e.Integration) != nil - t.Expose = &e + if e.Resources.GetServiceForIntegration(e.Integration) != nil { + t.Expose = ptr.To(true) + } } } @@ -138,7 +125,7 @@ func (t *containerTrait) configureContainer(e *Environment) error { e.ApplicationProperties = make(map[string]string) } container := corev1.Container{ - Name: t.Name, + Name: t.getContainerName(), Image: e.Integration.Status.Image, Env: make([]corev1.EnvVar, 0), } @@ -230,7 +217,7 @@ func (t *containerTrait) configureService(e *Environment, container *corev1.Cont } containerPort := corev1.ContainerPort{ Name: name, - ContainerPort: int32(t.Port), + ContainerPort: int32(t.getPort()), Protocol: corev1.ProtocolTCP, } if !isKnative { @@ -238,8 +225,8 @@ func (t *containerTrait) configureService(e *Environment, container *corev1.Cont service := e.Resources.GetServiceForIntegration(e.Integration) if service != nil { servicePort := corev1.ServicePort{ - Name: t.ServicePortName, - Port: int32(t.ServicePort), + Name: t.getServicePortName(), + Port: int32(t.getServicePort()), Protocol: corev1.ProtocolTCP, TargetPort: intstr.FromString(name), } @@ -271,21 +258,21 @@ func (t *containerTrait) configureResources(container *corev1.Container) { limitsList = make(corev1.ResourceList) } - requestsList, err = kubernetes.ConfigureResource(t.RequestCPU, requestsList, corev1.ResourceCPU) + requestsList, err = kubernetes.ConfigureResource(t.getRequestCPU(), requestsList, corev1.ResourceCPU) if err != nil { - t.L.Error(err, "unable to parse quantity", "request-cpu", t.RequestCPU) + t.L.Error(err, "unable to parse quantity", "request-cpu", t.getRequestCPU()) } - requestsList, err = kubernetes.ConfigureResource(t.RequestMemory, requestsList, corev1.ResourceMemory) + requestsList, err = kubernetes.ConfigureResource(t.getRequestMemory(), requestsList, corev1.ResourceMemory) if err != nil { - t.L.Error(err, "unable to parse quantity", "request-memory", t.RequestMemory) + t.L.Error(err, "unable to parse quantity", "request-memory", t.getRequestMemory()) } - limitsList, err = kubernetes.ConfigureResource(t.LimitCPU, limitsList, corev1.ResourceCPU) + limitsList, err = kubernetes.ConfigureResource(t.getLimitCPU(), limitsList, corev1.ResourceCPU) if err != nil { - t.L.Error(err, "unable to parse quantity", "limit-cpu", t.LimitCPU) + t.L.Error(err, "unable to parse quantity", "limit-cpu", t.getLimitCPU()) } - limitsList, err = kubernetes.ConfigureResource(t.LimitMemory, limitsList, corev1.ResourceMemory) + limitsList, err = kubernetes.ConfigureResource(t.getLimitMemory(), limitsList, corev1.ResourceMemory) if err != nil { - t.L.Error(err, "unable to parse quantity", "limit-memory", t.LimitMemory) + t.L.Error(err, "unable to parse quantity", "limit-memory", t.getLimitMemory()) } container.Resources.Requests = requestsList @@ -300,12 +287,12 @@ func (t *containerTrait) configureCapabilities(e *Environment) { func (t *containerTrait) setSecurityContext(e *Environment, container *corev1.Container) error { sc := corev1.SecurityContext{ - RunAsNonRoot: t.RunAsNonRoot, + RunAsNonRoot: t.getRunAsNonRoot(), SeccompProfile: &corev1.SeccompProfile{ - Type: t.SeccompProfileType, + Type: t.getSeccompProfileType(), }, - AllowPrivilegeEscalation: t.AllowPrivilegeEscalation, - Capabilities: &corev1.Capabilities{Drop: t.CapabilitiesDrop, Add: t.CapabilitiesAdd}, + AllowPrivilegeEscalation: t.getAllowPrivilegeEscalation(), + Capabilities: &corev1.Capabilities{Drop: t.getCapabilitiesDrop(), Add: t.CapabilitiesAdd}, } runAsUser, err := t.getUser(e) @@ -342,3 +329,99 @@ func (t *containerTrait) getUser(e *Environment) (*int64, error) { return runAsUser, nil } + +func (t *containerTrait) getPort() int { + if t.Port == 0 { + return defaultContainerPort + } + + return t.Port +} + +func (t *containerTrait) getServicePort() int { + if t.ServicePort == 0 { + return defaultServicePort + } + + return t.ServicePort +} + +func (t *containerTrait) getServicePortName() string { + if t.ServicePortName == "" { + return defaultContainerPortName + } + + return t.ServicePortName +} + +func (t *containerTrait) getContainerName() string { + if t.Name == "" { + return defaultContainerName + } + + return t.Name +} + +func (t *containerTrait) getRunAsNonRoot() *bool { + if t.RunAsNonRoot == nil { + return ptr.To(defaultContainerRunAsNonRoot) + } + + return t.RunAsNonRoot +} + +func (t *containerTrait) getSeccompProfileType() corev1.SeccompProfileType { + if t.SeccompProfileType == "" { + return defaultContainerSeccompProfileType + } + + return t.SeccompProfileType +} + +func (t *containerTrait) getAllowPrivilegeEscalation() *bool { + if t.AllowPrivilegeEscalation == nil { + return ptr.To(defaultContainerAllowPrivilegeEscalation) + } + + return t.AllowPrivilegeEscalation +} + +func (t *containerTrait) getCapabilitiesDrop() []corev1.Capability { + if t.CapabilitiesDrop == nil { + return []corev1.Capability{defaultContainerCapabilitiesDrop} + } + + return t.CapabilitiesDrop +} + +func (t *containerTrait) getRequestCPU() string { + if t.RequestCPU == "" { + return defaultContainerResourceCPU + } + + return t.RequestCPU +} + +func (t *containerTrait) getRequestMemory() string { + if t.RequestMemory == "" { + return defaultContainerResourceMemory + } + + return t.RequestMemory +} + +func (t *containerTrait) getLimitCPU() string { + if t.LimitCPU == "" { + return defaultContainerLimitCPU + } + + return t.LimitCPU +} + +func (t *containerTrait) getLimitMemory() string { + if t.LimitMemory == "" { + return defaultContainerLimitMemory + } + + return t.LimitMemory +} diff --git a/pkg/trait/container_probes_test.go b/pkg/trait/container_probes_test.go index 4b128e9c2a..0d17162a3b 100644 --- a/pkg/trait/container_probes_test.go +++ b/pkg/trait/container_probes_test.go @@ -80,8 +80,9 @@ func TestProbesDependencies(t *testing.T) { env := newTestProbesEnv(t, integration) env.Integration.Status.Phase = v1.IntegrationPhaseInitialization - conditions, err := env.Catalog.apply(&env) + conditions, traits, err := env.Catalog.apply(&env) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.Contains(t, env.Integration.Status.Dependencies, "mvn:org.apache.camel.quarkus:camel-quarkus-microprofile-health") } @@ -105,8 +106,9 @@ func TestProbesOnDeployment(t *testing.T) { env := newTestProbesEnv(t, integration) env.Integration.Status.Phase = v1.IntegrationPhaseDeploying - conditions, err := env.Catalog.apply(&env) + conditions, traits, err := env.Catalog.apply(&env) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) container := env.GetIntegrationContainer() @@ -143,8 +145,9 @@ func TestProbesOnDeploymentWithCustomScheme(t *testing.T) { env := newTestProbesEnv(t, integration) env.Integration.Status.Phase = v1.IntegrationPhaseDeploying - conditions, err := env.Catalog.apply(&env) + conditions, traits, err := env.Catalog.apply(&env) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) container := env.GetIntegrationContainer() @@ -200,8 +203,9 @@ func TestProbesOnKnativeService(t *testing.T) { "controller strategy: knative-service", ) - conditions, err := env.Catalog.apply(&env) + conditions, traits, err := env.Catalog.apply(&env) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.Contains(t, conditions, ctrlStrategyCondition) assert.Contains(t, conditions, serviceOverrideCondition) diff --git a/pkg/trait/container_test.go b/pkg/trait/container_test.go index db41467a84..ff52d6e8b5 100644 --- a/pkg/trait/container_test.go +++ b/pkg/trait/container_test.go @@ -86,9 +86,10 @@ func TestContainerWithDefaults(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - conditions, err := traitCatalog.apply(&environment) + conditions, traits, err := traitCatalog.apply(&environment) require.NoError(t, err) + assert.Empty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("deployment")) @@ -167,9 +168,10 @@ func TestContainerWithOpenshift(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - conditions, err := traitCatalog.apply(&environment) + conditions, traits, err := traitCatalog.apply(&environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("deployment")) @@ -235,9 +237,10 @@ func TestContainerWithCustomName(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - conditions, err := traitCatalog.apply(&environment) + conditions, traits, err := traitCatalog.apply(&environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("deployment")) @@ -301,9 +304,10 @@ func TestContainerWithCustomImage(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - conditions, err := traitCatalog.apply(&environment) + conditions, traits, err := traitCatalog.apply(&environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) for _, postAction := range environment.PostActions { @@ -370,8 +374,9 @@ func TestContainerWithCustomImageAndIntegrationKit(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - conditions, err := traitCatalog.apply(&environment) + conditions, traits, err := traitCatalog.apply(&environment) require.Error(t, err) + assert.Empty(t, traits) assert.NotEmpty(t, conditions) assert.Contains(t, err.Error(), "unsupported configuration: a container image has been set in conjunction with an IntegrationKit") } @@ -414,9 +419,10 @@ func TestContainerWithImagePullPolicy(t *testing.T) { environment.Integration.Status.Phase = v1.IntegrationPhaseDeploying environment.Platform.ResyncStatusFullConfig() - conditions, err := traitCatalog.apply(&environment) + conditions, traits, err := traitCatalog.apply(&environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) container := environment.GetIntegrationContainer() @@ -479,7 +485,7 @@ func TestDeploymentContainerPorts(t *testing.T) { environment.Integration.Status.Phase = v1.IntegrationPhaseDeploying environment.Platform.ResyncStatusFullConfig() - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) container := environment.GetIntegrationContainer() assert.Len(t, container.Ports, 1) @@ -545,7 +551,7 @@ func TestKnativeServiceContainerPorts(t *testing.T) { environment.Integration.Status.Phase = v1.IntegrationPhaseDeploying environment.Platform.ResyncStatusFullConfig() - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) container := environment.GetIntegrationContainer() assert.Len(t, container.Ports, 1) @@ -557,9 +563,10 @@ func TestDefaultKubernetesSecurityContext(t *testing.T) { environment := createSettingContextEnvironment(t, v1.TraitProfileKubernetes) traitCatalog := NewCatalog(nil) - conditions, err := traitCatalog.apply(environment) + conditions, traits, err := traitCatalog.apply(environment) require.NoError(t, err) + assert.Empty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("deployment")) @@ -587,9 +594,10 @@ func TestDefaultKnativeSecurityContext(t *testing.T) { } traitCatalog := NewCatalog(nil) - conditions, err := traitCatalog.apply(environment) + conditions, traits, err := traitCatalog.apply(environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.Nil(t, environment.GetTrait("deployment")) @@ -625,9 +633,10 @@ func TestUserSecurityContext(t *testing.T) { } traitCatalog := NewCatalog(nil) - conditions, err := traitCatalog.apply(environment) + conditions, traits, err := traitCatalog.apply(environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("deployment")) @@ -648,9 +657,10 @@ func TestUserSecurityContext(t *testing.T) { func TestUserDefaultResources(t *testing.T) { environment := createSettingContextEnvironment(t, v1.TraitProfileKubernetes) traitCatalog := NewCatalog(nil) - conditions, err := traitCatalog.apply(environment) + conditions, traits, err := traitCatalog.apply(environment) require.NoError(t, err) + assert.Empty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("deployment")) diff --git a/pkg/trait/cron.go b/pkg/trait/cron.go index 7fbb35d9ea..80da757783 100644 --- a/pkg/trait/cron.go +++ b/pkg/trait/cron.go @@ -88,15 +88,6 @@ func (t *cronTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if !e.IntegrationInPhase(v1.IntegrationPhaseInitialization) && !e.IntegrationInRunningPhases() { return false, nil, nil } - if _, ok := e.CamelCatalog.Runtime.Capabilities[v1.CapabilityCron]; !ok { - return false, NewIntegrationCondition( - "Cron", - v1.IntegrationConditionCronJobAvailable, - corev1.ConditionFalse, - v1.IntegrationConditionCronJobNotAvailableReason, - "the runtime provider %s does not declare 'cron' capability", - ), nil - } if ptr.Deref(t.Auto, true) { err := t.autoConfigure(e) @@ -143,7 +134,11 @@ func (t *cronTrait) Configure(e *Environment) (bool, *TraitCondition, error) { return false, nil, nil } - return t.Schedule != "", nil, nil + if t.Schedule != "" { + t.Enabled = ptr.To(true) + } + + return ptr.Deref(t.Enabled, false), nil, nil } func (t *cronTrait) autoConfigure(e *Environment) error { @@ -164,10 +159,6 @@ func (t *cronTrait) autoConfigure(e *Environment) error { t.Components = strings.Join(configuredComponents, ",") } - if t.ConcurrencyPolicy == "" { - t.ConcurrencyPolicy = string(batchv1.ForbidConcurrent) - } - if (t.Schedule == "" && t.Components == "") && t.Fallback == nil { // If there's at least a `cron` endpoint, add a fallback implementation fromURIs, err := t.getSourcesFromURIs(e) @@ -254,7 +245,7 @@ func (t *cronTrait) getCronJobFor(e *Environment) *batchv1.CronJob { Spec: batchv1.CronJobSpec{ Schedule: t.Schedule, TimeZone: t.TimeZone, - ConcurrencyPolicy: batchv1.ConcurrencyPolicy(t.ConcurrencyPolicy), + ConcurrencyPolicy: t.getConcurrentPolicy(), StartingDeadlineSeconds: t.StartingDeadlineSeconds, JobTemplate: batchv1.JobTemplateSpec{ Spec: batchv1.JobSpec{ @@ -361,6 +352,14 @@ func (t *cronTrait) getSourcesFromURIs(e *Environment) ([]string, error) { return fromUris, nil } +func (t *cronTrait) getConcurrentPolicy() batchv1.ConcurrencyPolicy { + if t.ConcurrencyPolicy == "" { + return batchv1.ForbidConcurrent + } + + return batchv1.ConcurrencyPolicy(t.ConcurrencyPolicy) +} + func getCronForURIs(camelURIs []string) *cronInfo { var globalCron *cronInfo for _, camelURI := range camelURIs { diff --git a/pkg/trait/cron_test.go b/pkg/trait/cron_test.go index 178f22d082..e09eaeff21 100644 --- a/pkg/trait/cron_test.go +++ b/pkg/trait/cron_test.go @@ -285,9 +285,10 @@ func TestCronDeps(t *testing.T) { require.NoError(t, err) tc := NewCatalog(c) - conditions, err := tc.apply(&environment) + conditions, traits, err := tc.apply(&environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) @@ -367,9 +368,10 @@ func TestCronMultipleScheduleFallback(t *testing.T) { assert.Nil(t, err) tc := NewCatalog(c) - conditions, err := tc.apply(&environment) + conditions, traits, err := tc.apply(&environment) assert.Nil(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) @@ -445,9 +447,10 @@ func TestCronDepsFallback(t *testing.T) { tc := NewCatalog(c) - conditions, err := tc.apply(&environment) + conditions, traits, err := tc.apply(&environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) @@ -528,8 +531,9 @@ func TestCronWithActiveDeadline(t *testing.T) { "DeploymentAvailable", "controller strategy: cron-job", ) - conditions, err := tc.apply(&environment) + conditions, traits, err := tc.apply(&environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.Contains(t, conditions, expectedCondition) assert.NotEmpty(t, environment.ExecutedTraits) @@ -617,8 +621,9 @@ func TestCronWithBackoffLimit(t *testing.T) { "DeploymentAvailable", "controller strategy: cron-job", ) - conditions, err := tc.apply(&environment) + conditions, traits, err := tc.apply(&environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.Contains(t, conditions, expectedCondition) assert.NotEmpty(t, environment.ExecutedTraits) @@ -710,8 +715,9 @@ func TestCronWithTimeZone(t *testing.T) { "DeploymentAvailable", "controller strategy: cron-job", ) - conditions, err := tc.apply(&environment) + conditions, traits, err := tc.apply(&environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.Contains(t, conditions, expectedCondition) assert.NotEmpty(t, environment.ExecutedTraits) @@ -726,3 +732,72 @@ func TestCronWithTimeZone(t *testing.T) { assert.NotNil(t, cronJob.Spec.TimeZone) assert.EqualValues(t, *cronJob.Spec.TimeZone, "America/Sao_Paulo") } + +func TestCronAuto(t *testing.T) { + catalog, err := camel.DefaultCatalog() + require.NoError(t, err) + + client, _ := test.NewFakeClient() + traitCatalog := NewCatalog(nil) + + environment := Environment{ + CamelCatalog: catalog, + Catalog: traitCatalog, + Client: client, + Integration: &v1.Integration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "ns", + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseDeploying, + }, + Spec: v1.IntegrationSpec{ + Sources: []v1.SourceSpec{ + { + DataSpec: v1.DataSpec{ + Name: "routes.java", + Content: `from("cron:tab?schedule=0 0/2 * * ?").to("log:test")`, + }, + Language: v1.LanguageJavaSource, + }, + }, + }, + }, + IntegrationKit: &v1.IntegrationKit{ + Status: v1.IntegrationKitStatus{ + Phase: v1.IntegrationKitPhaseReady, + }, + }, + Platform: &v1.IntegrationPlatform{ + Spec: v1.IntegrationPlatformSpec{ + Build: v1.IntegrationPlatformBuildSpec{ + RuntimeVersion: catalog.Runtime.Version, + }, + }, + Status: v1.IntegrationPlatformStatus{ + Phase: v1.IntegrationPlatformPhaseReady, + }, + }, + EnvVars: make([]corev1.EnvVar, 0), + ExecutedTraits: make([]Trait, 0), + Resources: kubernetes.NewCollection(), + } + environment.Platform.ResyncStatusFullConfig() + + c, err := NewFakeClient("ns") + require.NoError(t, err) + + tc := NewCatalog(c) + _, traits, err := tc.apply(&environment) + require.NoError(t, err) + assert.NotEmpty(t, traits) + + assert.Equal(t, &traitv1.CronTrait{ + Trait: traitv1.Trait{ + Enabled: ptr.To(true), + }, + Schedule: "0 0/2 * * ?", + Components: "cron", + }, traits.Cron) +} diff --git a/pkg/trait/environment.go b/pkg/trait/environment.go index c1db2aec84..da49b26f76 100644 --- a/pkg/trait/environment.go +++ b/pkg/trait/environment.go @@ -59,9 +59,6 @@ const ( func newEnvironmentTrait() Trait { return &environmentTrait{ BasePlatformTrait: NewBasePlatformTrait(environmentTraitID, environmentTraitOrder), - EnvironmentTrait: traitv1.EnvironmentTrait{ - ContainerMeta: ptr.To(true), - }, } } diff --git a/pkg/trait/environment_test.go b/pkg/trait/environment_test.go index 05bf6cd8e6..d7ffdf0c84 100644 --- a/pkg/trait/environment_test.go +++ b/pkg/trait/environment_test.go @@ -43,8 +43,9 @@ func TestDefaultEnvironment(t *testing.T) { env := mockEnvironment(catalog) env.Platform.ResyncStatusFullConfig() - conditions, err := NewEnvironmentTestCatalog().apply(&env) + conditions, traits, err := NewEnvironmentTestCatalog().apply(&env) require.NoError(t, err) + assert.Empty(t, traits) assert.NotEmpty(t, conditions) ns := false @@ -92,8 +93,9 @@ func TestEnabledContainerMetaDataEnvVars(t *testing.T) { } env.Platform.ResyncStatusFullConfig() - conditions, err := NewEnvironmentTestCatalog().apply(&env) + conditions, traits, err := NewEnvironmentTestCatalog().apply(&env) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) ns := false @@ -132,8 +134,9 @@ func TestDisabledContainerMetaDataEnvVars(t *testing.T) { env.Platform.ResyncStatusFullConfig() - conditions, err := NewEnvironmentTestCatalog().apply(&env) + conditions, traits, err := NewEnvironmentTestCatalog().apply(&env) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) ns := false @@ -171,8 +174,9 @@ func TestCustomEnvVars(t *testing.T) { } env.Platform.ResyncStatusFullConfig() - conditions, err := NewEnvironmentTestCatalog().apply(&env) + conditions, traits, err := NewEnvironmentTestCatalog().apply(&env) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) userK1 := false @@ -205,8 +209,9 @@ func TestValueSourceEnvVars(t *testing.T) { } env.Platform.ResyncStatusFullConfig() - conditions, err := NewEnvironmentTestCatalog().apply(&env) + conditions, traits, err := NewEnvironmentTestCatalog().apply(&env) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) userK1 := false diff --git a/pkg/trait/health.go b/pkg/trait/health.go index 98d87b046d..fe4e4bc0dd 100644 --- a/pkg/trait/health.go +++ b/pkg/trait/health.go @@ -47,11 +47,6 @@ type healthTrait struct { func newHealthTrait() Trait { return &healthTrait{ BaseTrait: NewBaseTrait(healthTraitID, healthTraitOrder), - HealthTrait: traitv1.HealthTrait{ - LivenessScheme: string(corev1.URISchemeHTTP), - ReadinessScheme: string(corev1.URISchemeHTTP), - StartupScheme: string(corev1.URISchemeHTTP), - }, } } @@ -156,12 +151,36 @@ func (t *healthTrait) setProbes(container *corev1.Container, port *intstr.IntOrS return nil } +func (t *healthTrait) getLivenessScheme() corev1.URIScheme { + if t.LivenessScheme == "" { + return corev1.URISchemeHTTP + } + + return corev1.URIScheme(t.LivenessScheme) +} + +func (t *healthTrait) getReadinessScheme() corev1.URIScheme { + if t.ReadinessScheme == "" { + return corev1.URISchemeHTTP + } + + return corev1.URIScheme(t.ReadinessScheme) +} + +func (t *healthTrait) getStartupScheme() corev1.URIScheme { + if t.StartupScheme == "" { + return corev1.URISchemeHTTP + } + + return corev1.URIScheme(t.StartupScheme) +} + func (t *healthTrait) newLivenessProbe(port *intstr.IntOrString, path string) *corev1.Probe { p := corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Path: path, - Scheme: corev1.URIScheme(t.LivenessScheme), + Scheme: t.getLivenessScheme(), }, }, InitialDelaySeconds: t.LivenessInitialDelay, @@ -183,7 +202,7 @@ func (t *healthTrait) newReadinessProbe(port *intstr.IntOrString, path string) * ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Path: path, - Scheme: corev1.URIScheme(t.ReadinessScheme), + Scheme: t.getReadinessScheme(), }, }, InitialDelaySeconds: t.ReadinessInitialDelay, @@ -205,7 +224,7 @@ func (t *healthTrait) newStartupProbe(port *intstr.IntOrString, path string) *co ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Path: path, - Scheme: corev1.URIScheme(t.StartupScheme), + Scheme: t.getStartupScheme(), }, }, InitialDelaySeconds: t.StartupInitialDelay, diff --git a/pkg/trait/health_test.go b/pkg/trait/health_test.go index e60e8fd150..c9092ef6b4 100644 --- a/pkg/trait/health_test.go +++ b/pkg/trait/health_test.go @@ -90,7 +90,7 @@ func TestHealthTrait(t *testing.T) { Resources: kubernetes.NewCollection(), } environment.Platform.ResyncStatusFullConfig() - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) d := environment.Resources.GetDeploymentForIntegration(environment.Integration) @@ -108,7 +108,7 @@ func TestHealthTrait(t *testing.T) { environment.Integration.Spec.Traits.Health.StartupProbeEnabled = ptr.To(true) environment.Platform.ResyncStatusFullConfig() - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) d = environment.Resources.GetDeploymentForIntegration(environment.Integration) assert.NotNil(t, d) diff --git a/pkg/trait/ingress.go b/pkg/trait/ingress.go index 6ee6a4c1d8..d78a6248e6 100644 --- a/pkg/trait/ingress.go +++ b/pkg/trait/ingress.go @@ -32,6 +32,9 @@ import ( const ( ingressTraitID = "ingress" ingressTraitOrder = 2400 + + defaultPath = "/" + defaultPathTypePrefix = networkingv1.PathTypePrefix ) type ingressTrait struct { @@ -42,15 +45,6 @@ type ingressTrait struct { func newIngressTrait() Trait { return &ingressTrait{ BaseTrait: NewBaseTrait(ingressTraitID, ingressTraitOrder), - IngressTrait: traitv1.IngressTrait{ - IngressClassName: "", - Annotations: map[string]string{}, - Host: "", - Path: "/", - PathType: ptrFrom(networkingv1.PathTypePrefix), - TLSHosts: []string{}, - TLSSecretName: "", - }, } } @@ -88,7 +82,7 @@ func (t *ingressTrait) Configure(e *Environment) (bool, *TraitCondition, error) func (t *ingressTrait) Apply(e *Environment) error { service := e.Resources.GetUserServiceForIntegration(e.Integration) if service == nil { - return errors.New("cannot Apply ingress trait: no target service") + return errors.New("cannot apply ingress trait: no target service") } ingress := networkingv1.Ingress{ @@ -109,8 +103,8 @@ func (t *ingressTrait) Apply(e *Environment) error { HTTP: &networkingv1.HTTPIngressRuleValue{ Paths: []networkingv1.HTTPIngressPath{ { - Path: t.Path, - PathType: t.PathType, + Path: t.getPath(), + PathType: t.getPathType(), Backend: networkingv1.IngressBackend{ Service: &networkingv1.IngressServiceBackend{ Name: service.Name, @@ -153,3 +147,19 @@ func (t *ingressTrait) Apply(e *Environment) error { return nil } + +func (t *ingressTrait) getPath() string { + if t.Path == "" { + return defaultPath + } + + return t.Path +} + +func (t *ingressTrait) getPathType() *networkingv1.PathType { + if t.PathType == nil { + return ptr.To(defaultPathTypePrefix) + } + + return t.PathType +} diff --git a/pkg/trait/ingress_test.go b/pkg/trait/ingress_test.go index d07aa57ba6..6635bd1249 100644 --- a/pkg/trait/ingress_test.go +++ b/pkg/trait/ingress_test.go @@ -95,7 +95,7 @@ func TestApplyIngressTraitWithoutUserServiceDoesNotSucceed(t *testing.T) { err := ingressTrait.Apply(environment) require.Error(t, err) - assert.Equal(t, "cannot Apply ingress trait: no target service", err.Error()) + assert.Equal(t, "cannot apply ingress trait: no target service", err.Error()) assert.Len(t, environment.Resources.Items(), 0) } diff --git a/pkg/trait/istio.go b/pkg/trait/istio.go index 93169ddbc1..b61a4d1dd5 100644 --- a/pkg/trait/istio.go +++ b/pkg/trait/istio.go @@ -43,14 +43,13 @@ type istioTrait struct { const ( istioSidecarInjectAnnotation = "sidecar.istio.io/inject" istioOutboundIPRangesAnnotation = "traffic.sidecar.istio.io/includeOutboundIPRanges" + + defaultAllow = "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16" ) func newIstioTrait() Trait { return &istioTrait{ BaseTrait: NewBaseTrait(istioTraitID, istioTraitOrder), - IstioTrait: traitv1.IstioTrait{ - Allow: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16", - }, } } @@ -63,7 +62,7 @@ func (t *istioTrait) Configure(e *Environment) (bool, *TraitCondition, error) { } func (t *istioTrait) Apply(e *Environment) error { - if t.Allow != "" { + if t.getAllow() != "" { e.Resources.VisitDeployment(func(d *appsv1.Deployment) { d.Spec.Template.Annotations = t.injectIstioAnnotation(d.Spec.Template.Annotations, true) }) @@ -78,7 +77,7 @@ func (t *istioTrait) injectIstioAnnotation(annotations map[string]string, includ if annotations == nil { annotations = make(map[string]string) } - annotations[istioOutboundIPRangesAnnotation] = t.Allow + annotations[istioOutboundIPRangesAnnotation] = t.getAllow() if includeInject { annotations[istioSidecarInjectAnnotation] = boolean.TrueString } @@ -87,3 +86,11 @@ func (t *istioTrait) injectIstioAnnotation(annotations map[string]string, includ } return annotations } + +func (t *istioTrait) getAllow() string { + if t.Allow == "" { + return defaultAllow + } + + return t.Allow +} diff --git a/pkg/trait/istio_test.go b/pkg/trait/istio_test.go index e6f63df4c5..13d2369e23 100644 --- a/pkg/trait/istio_test.go +++ b/pkg/trait/istio_test.go @@ -102,8 +102,9 @@ func TestIstioInject(t *testing.T) { } env := NewIstioTestEnv(t, &d, &s, true) - conditions, err := env.Catalog.apply(&env) + conditions, traits, err := env.Catalog.apply(&env) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.Empty(t, s.Spec.ConfigurationSpec.Template.Annotations[istioSidecarInjectAnnotation]) assert.NotEmpty(t, d.Spec.Template.Annotations[istioSidecarInjectAnnotation]) @@ -127,8 +128,9 @@ func TestIstioForcedInjectTrue(t *testing.T) { env.Integration.Spec.Traits.Istio.Enabled = ptr.To(true) env.Integration.Spec.Traits.Istio.Inject = ptr.To(true) - conditions, err := env.Catalog.apply(&env) + conditions, traits, err := env.Catalog.apply(&env) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.Equal(t, boolean.TrueString, s.Spec.ConfigurationSpec.Template.Annotations[istioSidecarInjectAnnotation]) assert.Equal(t, boolean.TrueString, d.Spec.Template.Annotations[istioSidecarInjectAnnotation]) @@ -152,8 +154,9 @@ func TestIstioForcedInjectFalse(t *testing.T) { env.Integration.Spec.Traits.Istio.Enabled = ptr.To(true) env.Integration.Spec.Traits.Istio.Inject = ptr.To(false) - conditions, err := env.Catalog.apply(&env) + conditions, traits, err := env.Catalog.apply(&env) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.Equal(t, boolean.FalseString, s.Spec.ConfigurationSpec.Template.Annotations[istioSidecarInjectAnnotation]) assert.Equal(t, boolean.FalseString, d.Spec.Template.Annotations[istioSidecarInjectAnnotation]) @@ -175,8 +178,9 @@ func TestIstioDisabled(t *testing.T) { env := NewIstioTestEnv(t, &d, &s, false) - conditions, err := env.Catalog.apply(&env) + conditions, traits, err := env.Catalog.apply(&env) require.NoError(t, err) + assert.Empty(t, traits) assert.NotEmpty(t, conditions) assert.NotContains(t, env.ExecutedTraits, "istio") } diff --git a/pkg/trait/jolokia.go b/pkg/trait/jolokia.go index d5f602ea66..cf4dc6959e 100644 --- a/pkg/trait/jolokia.go +++ b/pkg/trait/jolokia.go @@ -45,9 +45,6 @@ type jolokiaTrait struct { func newJolokiaTrait() Trait { return &jolokiaTrait{ BaseTrait: NewBaseTrait(jolokiaTraitID, jolokiaTraitOrder), - JolokiaTrait: traitv1.JolokiaTrait{ - Port: defaultJolokiaPort, - }, } } @@ -107,7 +104,7 @@ func (t *jolokiaTrait) Apply(e *Environment) error { t.addToJolokiaOptions(options, "extendedClientCheck", t.ExtendedClientCheck) t.addToJolokiaOptions(options, "host", t.Host) t.addToJolokiaOptions(options, "password", t.Password) - t.addToJolokiaOptions(options, "port", t.Port) + t.addToJolokiaOptions(options, "port", t.getPort()) t.addToJolokiaOptions(options, "protocol", t.Protocol) t.addToJolokiaOptions(options, "user", t.User) t.addToJolokiaOptions(options, "useSslClientAuthentication", t.UseSslClientAuthentication) @@ -131,7 +128,7 @@ func (t *jolokiaTrait) Apply(e *Environment) error { containerPort := corev1.ContainerPort{ Name: "jolokia", - ContainerPort: int32(t.Port), + ContainerPort: int32(t.getPort()), Protocol: corev1.ProtocolTCP, } @@ -147,6 +144,14 @@ func (t *jolokiaTrait) Apply(e *Environment) error { return nil } +func (t *jolokiaTrait) getPort() int { + if t.Port == 0 { + return defaultJolokiaPort + } + + return t.Port +} + func (t *jolokiaTrait) setDefaultJolokiaOption(options map[string]string, option interface{}, key string, value interface{}) { // Do not override existing option if _, ok := options[key]; ok { diff --git a/pkg/trait/jolokia_test.go b/pkg/trait/jolokia_test.go index b9ffa492b2..cf9b06fdf0 100644 --- a/pkg/trait/jolokia_test.go +++ b/pkg/trait/jolokia_test.go @@ -53,10 +53,11 @@ func TestApplyJolokiaTraitNominalShouldSucceed(t *testing.T) { container := environment.Resources.GetContainerByName(defaultContainerName) assert.NotNil(t, container) - assert.Equal(t, container.Args, []string{ + assert.Equal(t, []string{ "-javaagent:dependencies/lib/main/org.jolokia.jolokia-agent-jvm-1.7.1.jar=discoveryEnabled=false,host=*,port=8778", "-cp", "dependencies/lib/main/org.jolokia.jolokia-agent-jvm-1.7.1.jar", - }) + }, + container.Args) assert.Len(t, container.Ports, 1) containerPort := container.Ports[0] diff --git a/pkg/trait/jvm.go b/pkg/trait/jvm.go index 0a28ba036d..096d315308 100644 --- a/pkg/trait/jvm.go +++ b/pkg/trait/jvm.go @@ -57,9 +57,6 @@ type jvmTrait struct { func newJvmTrait() Trait { return &jvmTrait{ BaseTrait: NewBaseTrait(jvmTraitID, jvmTraitOrder), - JVMTrait: traitv1.JVMTrait{ - DebugAddress: "*:5005", - }, } } @@ -230,6 +227,7 @@ func (t *jvmTrait) enableDebug(e *Environment) string { } meta.Labels["camel.apache.org/debug"] = "true" }) + t.DebugAddress = "*:5005" return fmt.Sprintf("-agentlib:jdwp=transport=dt_socket,server=y,suspend=%s,address=%s", suspend, t.DebugAddress) diff --git a/pkg/trait/kamelets.go b/pkg/trait/kamelets.go index fd764264b6..328b432664 100644 --- a/pkg/trait/kamelets.go +++ b/pkg/trait/kamelets.go @@ -48,8 +48,6 @@ const ( contentKey = "content" KameletLocationProperty = "camel.component.kamelet.location" - kameletLabel = "camel.apache.org/kamelet" - kameletConfigurationLabel = "camel.apache.org/kamelet.configuration" kameletMountPointAnnotation = "camel.apache.org/kamelet.mount-point" ) @@ -76,9 +74,6 @@ func (t *kameletsTrait) Configure(e *Environment) (bool, *TraitCondition, error) if !e.IntegrationInPhase(v1.IntegrationPhaseInitialization) && !e.IntegrationInRunningPhases() { return false, nil, nil } - if t.MountPoint == "" { - t.MountPoint = filepath.Join(camel.BasePath, "kamelets") - } if ptr.Deref(t.Auto, true) { var kamelets []string _, err := e.ConsumeMeta(func(meta metadata.IntegrationMetadata) bool { @@ -223,7 +218,7 @@ func (t *kameletsTrait) addKamelets(e *Environment) error { e.ApplicationProperties = map[string]string{} } for _, cm := range bundleConfigmaps { - kameletMountPoint := fmt.Sprintf("%s/%s", t.MountPoint, cm.Name) + kameletMountPoint := fmt.Sprintf("%s/%s", t.getMountPoint(), cm.Name) cm.Annotations[kameletMountPointAnnotation] = kameletMountPoint e.Resources.Add(cm) if e.ApplicationProperties[KameletLocationProperty] == "" { @@ -300,6 +295,14 @@ func (t *kameletsTrait) getKameletKeys(withVersion bool) []string { return answer } +func (t *kameletsTrait) getMountPoint() string { + if t.MountPoint == "" { + return filepath.Join(camel.BasePath, "kamelets") + } + + return t.MountPoint +} + func getKameletKey(item string, withVersion bool) string { i := strings.Trim(item, " \t\"") if strings.Contains(i, "/") { diff --git a/pkg/trait/kamelets_test.go b/pkg/trait/kamelets_test.go index ac7ad9411c..2b43d40afa 100644 --- a/pkg/trait/kamelets_test.go +++ b/pkg/trait/kamelets_test.go @@ -22,6 +22,7 @@ import ( "testing" v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" + traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait" "github.com/apache/camel-k/v2/pkg/util/camel" "github.com/apache/camel-k/v2/pkg/util/kubernetes" @@ -695,3 +696,55 @@ func TestKameletSyntheticKitAutoConditionFalse(t *testing.T) { }) assert.Nil(t, kameletsBundle) } + +func TestKameletAuto(t *testing.T) { + flow := ` +- from: + uri: kamelet:timer + steps: + - to: kamelet:none +` + trait, environment := createKameletsTestEnvironment( + flow, + &v1.Kamelet{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "timer", + }, + Spec: v1.KameletSpec{ + KameletSpecBase: v1.KameletSpecBase{ + Template: templateOrFail(map[string]interface{}{ + "from": map[string]interface{}{ + "uri": "timer:tick", + }, + }), + }, + }, + }, + &v1.Kamelet{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "none", + }, + Spec: v1.KameletSpec{ + KameletSpecBase: v1.KameletSpecBase{ + Template: templateOrFail(map[string]interface{}{ + "from": map[string]interface{}{ + "uri": "timer:tick", + }, + }), + }, + }, + }) + + enabled, condition, err := trait.Configure(environment) + require.NoError(t, err) + assert.True(t, enabled) + assert.Nil(t, condition) + + err = trait.Apply(environment) + require.NoError(t, err) + assert.Equal(t, traitv1.KameletsTrait{ + List: "none,timer", + }, trait.KameletsTrait) +} diff --git a/pkg/trait/knative.go b/pkg/trait/knative.go index 9f52337284..19439453d3 100644 --- a/pkg/trait/knative.go +++ b/pkg/trait/knative.go @@ -80,25 +80,7 @@ func (t *knativeTrait) Configure(e *Environment) (bool, *TraitCondition, error) return false, nil, nil } - knativeInstalled, err := knativeutil.IsEventingInstalled(e.Client) - if err != nil { - return false, nil, err - } - - if !ptr.Deref(t.Auto, true) { - if !knativeInstalled { - return false, NewIntegrationCondition( - "Knative", - v1.IntegrationConditionKnativeAvailable, - corev1.ConditionFalse, - v1.IntegrationConditionKnativeNotInstalledReason, - "integration cannot run. Knative is not installed in the cluster", - ), fmt.Errorf("integration cannot run. Knative is not installed in the cluster") - } - return true, nil, nil - } - - _, err = e.ConsumeMeta(func(meta metadata.IntegrationMetadata) bool { + _, err := e.ConsumeMeta(func(meta metadata.IntegrationMetadata) bool { if len(t.ChannelSources) == 0 { t.ChannelSources = filterMetaItems(meta, knativeapi.CamelServiceTypeChannel, "from") } @@ -123,30 +105,37 @@ func (t *knativeTrait) Configure(e *Environment) (bool, *TraitCondition, error) return false, nil, err } - hasKnativeEndpoint := len(t.ChannelSources) > 0 || len(t.ChannelSinks) > 0 || len(t.EndpointSources) > 0 || len(t.EndpointSinks) > 0 || len(t.EventSources) > 0 || len(t.EventSinks) > 0 - - if !hasKnativeEndpoint && !ptr.Deref(t.Enabled, false) { - return false, nil, nil - } - if !knativeInstalled { - return false, NewIntegrationCondition( - "Knative", - v1.IntegrationConditionKnativeAvailable, - corev1.ConditionFalse, - v1.IntegrationConditionKnativeNotInstalledReason, - "integration cannot run. Knative is not installed in the cluster", - ), fmt.Errorf("integration cannot run. Knative is not installed in the cluster") + if t.Enabled == nil { + // If the trait is enabled, then, we can skip this optimization + hasKnativeEndpoint := len(t.ChannelSources) > 0 || len(t.ChannelSinks) > 0 || + len(t.EndpointSources) > 0 || len(t.EndpointSinks) > 0 || + len(t.EventSources) > 0 || len(t.EventSinks) > 0 + t.Enabled = ptr.To(hasKnativeEndpoint) } - if t.FilterSourceChannels == nil { - // Filtering is no longer used by default - t.FilterSourceChannels = ptr.To(false) - } - if t.SinkBinding == nil { - allowed := t.isSinkBindingAllowed(e) - t.SinkBinding = &allowed + + if ptr.Deref(t.Enabled, false) { + // Verify if Knative eventing is installed + knativeInstalled, err := knativeutil.IsEventingInstalled(e.Client) + if err != nil { + return false, nil, err + } + if !knativeInstalled { + return false, NewIntegrationCondition( + "Knative", + v1.IntegrationConditionKnativeAvailable, + corev1.ConditionFalse, + v1.IntegrationConditionKnativeNotInstalledReason, + "integration cannot run. Knative is not installed in the cluster", + ), fmt.Errorf("integration cannot run. Knative is not installed in the cluster") + } + + if t.SinkBinding == nil { + allowed := t.isSinkBindingAllowed(e) + t.SinkBinding = &allowed + } } - return true, nil, nil + return ptr.Deref(t.Enabled, false), nil, nil } func filterMetaItems(meta metadata.IntegrationMetadata, cst knativeapi.CamelServiceType, uriType string) []string { @@ -158,6 +147,9 @@ func filterMetaItems(meta metadata.IntegrationMetadata, cst knativeapi.CamelServ uris = meta.ToURIs } items = append(items, knativeutil.FilterURIs(uris, cst)...) + if len(items) == 0 { + return nil + } sort.Strings(items) return items } diff --git a/pkg/trait/knative_service.go b/pkg/trait/knative_service.go index 3d55db707a..0a53459923 100644 --- a/pkg/trait/knative_service.go +++ b/pkg/trait/knative_service.go @@ -61,9 +61,6 @@ var _ ControllerStrategySelector = &knativeServiceTrait{} func newKnativeServiceTrait() Trait { return &knativeServiceTrait{ BaseTrait: NewBaseTrait(knativeServiceTraitID, knativeServiceTraitOrder), - KnativeServiceTrait: traitv1.KnativeServiceTrait{ - Annotations: map[string]string{}, - }, } } @@ -92,13 +89,7 @@ func (t *knativeServiceTrait) Configure(e *Environment) (bool, *TraitCondition, if e.Resources.GetDeploymentForIntegration(e.Integration) != nil { // A controller is already present for the integration - return false, NewIntegrationCondition( - "KnativeService", - v1.IntegrationConditionKnativeServiceAvailable, - corev1.ConditionFalse, - v1.IntegrationConditionKnativeServiceNotAvailableReason, - fmt.Sprintf("different controller strategy used (%s)", string(ControllerStrategyDeployment)), - ), nil + return false, nil, nil } strategy, err := e.DetermineControllerStrategy() @@ -111,22 +102,15 @@ func (t *knativeServiceTrait) Configure(e *Environment) (bool, *TraitCondition, err.Error(), ), err } - if strategy != ControllerStrategyKnativeService { - return false, NewIntegrationCondition( - "KnativeService", - v1.IntegrationConditionKnativeServiceAvailable, - corev1.ConditionFalse, - v1.IntegrationConditionKnativeServiceNotAvailableReason, - fmt.Sprintf("different controller strategy used (%s)", string(strategy)), - ), nil - } - if e.IntegrationInPhase(v1.IntegrationPhaseRunning, v1.IntegrationPhaseError) { + if strategy == ControllerStrategyKnativeService { + t.Enabled = ptr.To(true) + } else if e.IntegrationInPhase(v1.IntegrationPhaseRunning, v1.IntegrationPhaseError) { condition := e.Integration.Status.GetCondition(v1.IntegrationConditionKnativeServiceAvailable) - return condition != nil && condition.Status == corev1.ConditionTrue, nil, nil + t.Enabled = ptr.To(condition != nil && condition.Status == corev1.ConditionTrue) } - return true, nil, nil + return ptr.Deref(t.Enabled, false), nil, nil } func (t *knativeServiceTrait) Apply(e *Environment) error { diff --git a/pkg/trait/knative_service_test.go b/pkg/trait/knative_service_test.go index 24eae32635..80cf2628cb 100644 --- a/pkg/trait/knative_service_test.go +++ b/pkg/trait/knative_service_test.go @@ -124,7 +124,7 @@ func TestKnativeService(t *testing.T) { environment.Platform.ResyncStatusFullConfig() // don't care about conditions in this unit test - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -252,7 +252,7 @@ func TestKnativeServiceWithCustomContainerName(t *testing.T) { environment.Platform.ResyncStatusFullConfig() // don't care about conditions in this unit test - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -337,7 +337,7 @@ func TestKnativeServiceWithRest(t *testing.T) { environment.Platform.ResyncStatusFullConfig() // don't care about conditions in this unit test - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -405,7 +405,7 @@ func TestKnativeServiceNotApplicable(t *testing.T) { environment.Platform.ResyncStatusFullConfig() // don't care about conditions in this unit test - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -480,7 +480,7 @@ func TestKnativeServiceNoServingAvailable(t *testing.T) { environment.Platform.ResyncStatusFullConfig() // don't care about conditions in this unit test - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -606,7 +606,7 @@ func createKnativeServiceTestEnvironment(t *testing.T, trait *traitv1.KnativeSer environment.Platform.ResyncStatusFullConfig() - _, err = traitCatalog.apply(environment) + _, _, err = traitCatalog.apply(environment) require.NoError(t, err) return environment @@ -619,7 +619,7 @@ func TestServiceAnnotation(t *testing.T) { }) traitsCatalog := environment.Catalog - _, err := traitsCatalog.apply(environment) + _, _, err := traitsCatalog.apply(environment) require.NoError(t, err) @@ -629,5 +629,77 @@ func TestServiceAnnotation(t *testing.T) { assert.NotNil(t, service) assert.True(t, reflect.DeepEqual(service.GetAnnotations(), annotationsTest)) +} +func TestKnativeServiceAuto(t *testing.T) { + catalog, err := camel.DefaultCatalog() + require.NoError(t, err) + + client, _ := test.NewFakeClient() + traitCatalog := NewCatalog(nil) + + compressedRoute, err := gzip.CompressBase64([]byte(`from("platform-http:test").log("hello")`)) + require.NoError(t, err) + + environment := Environment{ + CamelCatalog: catalog, + Catalog: traitCatalog, + Client: client, + Integration: &v1.Integration{ + ObjectMeta: metav1.ObjectMeta{ + Name: KnativeServiceTestName, + Namespace: KnativeServiceTestNamespace, + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseDeploying, + }, + Spec: v1.IntegrationSpec{ + Profile: v1.TraitProfileKnative, + Sources: []v1.SourceSpec{ + { + DataSpec: v1.DataSpec{ + Name: "routes.js", + Content: string(compressedRoute), + Compression: true, + }, + Language: v1.LanguageJavaScript, + }, + }, + }, + }, + IntegrationKit: &v1.IntegrationKit{ + Status: v1.IntegrationKitStatus{ + Phase: v1.IntegrationKitPhaseReady, + }, + }, + Platform: &v1.IntegrationPlatform{ + Spec: v1.IntegrationPlatformSpec{ + Cluster: v1.IntegrationPlatformClusterOpenShift, + Build: v1.IntegrationPlatformBuildSpec{ + PublishStrategy: v1.IntegrationPlatformBuildPublishStrategyS2I, + Registry: v1.RegistrySpec{Address: "registry"}, + RuntimeVersion: catalog.Runtime.Version, + }, + }, + Status: v1.IntegrationPlatformStatus{ + Phase: v1.IntegrationPlatformPhaseReady, + }, + }, + EnvVars: make([]corev1.EnvVar, 0), + ExecutedTraits: make([]Trait, 0), + Resources: kubernetes.NewCollection(), + } + environment.Platform.ResyncStatusFullConfig() + + // don't care about conditions in this unit test + _, traits, err := traitCatalog.apply(&environment) + + require.NoError(t, err) + assert.NotEmpty(t, traits) + assert.NotEmpty(t, environment.ExecutedTraits) + assert.Equal(t, &traitv1.KnativeServiceTrait{ + Trait: traitv1.Trait{ + Enabled: ptr.To(true), + }, + }, traits.KnativeService) } diff --git a/pkg/trait/knative_test.go b/pkg/trait/knative_test.go index 7a8a7e45f0..7a6ebe49f0 100644 --- a/pkg/trait/knative_test.go +++ b/pkg/trait/knative_test.go @@ -355,7 +355,7 @@ func TestKnativeTriggerExplicitFilterConfig(t *testing.T) { environment.Platform.ResyncStatusFullConfig() // don't care about conditions in this unit test - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -455,7 +455,7 @@ func TestKnativeTriggerExplicitFilterConfigNoEventTypeFilter(t *testing.T) { environment.Platform.ResyncStatusFullConfig() // don't care about conditions in this unit test - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -552,7 +552,7 @@ func TestKnativeTriggerDefaultEventTypeFilter(t *testing.T) { environment.Platform.ResyncStatusFullConfig() // don't care about conditions in this unit test - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -649,7 +649,7 @@ func TestKnativeTriggerDefaultEventTypeFilterDisabled(t *testing.T) { environment.Platform.ResyncStatusFullConfig() // don't care about conditions in this unit test - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -749,7 +749,7 @@ func TestKnativeMultipleTrigger(t *testing.T) { environment.Platform.ResyncStatusFullConfig() // don't care about conditions in this unit test - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -887,7 +887,7 @@ func TestKnativeMultipleTriggerAdditionalFilterConfig(t *testing.T) { environment.Platform.ResyncStatusFullConfig() // don't care about conditions in this unit test - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -1022,7 +1022,7 @@ func TestKnativeTriggerNoEventType(t *testing.T) { environment.Platform.ResyncStatusFullConfig() // don't care about conditions in this unit test - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -1119,7 +1119,7 @@ func TestKnativeTriggerNoServingAvailable(t *testing.T) { environment.Platform.ResyncStatusFullConfig() // don't care about conditions in this unit test - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -1180,7 +1180,7 @@ func TestKnativePlatformHttpConfig(t *testing.T) { err = tc.Configure(&environment) require.NoError(t, err) - _, err = tc.apply(&environment) + _, _, err = tc.apply(&environment) require.NoError(t, err) assert.Contains(t, environment.Integration.Status.Capabilities, v1.CapabilityPlatformHTTP) }) @@ -1226,8 +1226,9 @@ func TestKnativePlatformHttpDependencies(t *testing.T) { err = tc.Configure(&environment) require.NoError(t, err) - conditions, err := tc.apply(&environment) + conditions, traits, err := tc.apply(&environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.Contains(t, environment.Integration.Status.Capabilities, v1.CapabilityPlatformHTTP) assert.Contains(t, environment.Integration.Status.Dependencies, "mvn:org.apache.camel.quarkus:camel-quarkus-platform-http") @@ -1653,7 +1654,7 @@ func TestKnativeSinkBinding(t *testing.T) { err = tc.Configure(&environment) require.NoError(t, err) - _, err = tc.apply(&environment) + _, _, err = tc.apply(&environment) require.NoError(t, err) baseProp := "camel.component.knative.environment.resources[0]" assert.Equal(t, "channel-sink-1", environment.ApplicationProperties[baseProp+".name"]) @@ -1806,3 +1807,63 @@ func createEnvironmentMissingEventingCRDs() *Environment { return environment } + +func TestKnativeAutoConfiguration(t *testing.T) { + client, _ := test.NewFakeClient() + replicas := int32(3) + catalog, _ := camel.QuarkusCatalog() + environment := &Environment{ + CamelCatalog: catalog, + Catalog: NewCatalog(nil), + Client: client, + Integration: &v1.Integration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "integration-name", + }, + Spec: v1.IntegrationSpec{ + Replicas: &replicas, + Traits: v1.Traits{}, + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseInitialization, + }, + }, + Platform: &v1.IntegrationPlatform{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "namespace", + }, + Spec: v1.IntegrationPlatformSpec{ + Cluster: v1.IntegrationPlatformClusterKubernetes, + Profile: v1.TraitProfileKubernetes, + }, + }, + Resources: kubernetes.NewCollection(), + ApplicationProperties: make(map[string]string), + } + environment.Platform.ResyncStatusFullConfig() + + trait, _ := newKnativeTrait().(*knativeTrait) + environment.Integration.Spec.Sources = []v1.SourceSpec{ + { + DataSpec: v1.DataSpec{ + Name: "test.java", + Content: ` + from("knative:channel/test").to("log:${body}; + `, + }, + Language: v1.LanguageJavaSource, + }, + } + + configured, condition, err := trait.Configure(environment) + require.NoError(t, err) + assert.Nil(t, condition) + assert.True(t, configured) + err = trait.Apply(environment) + require.NoError(t, err) + expectedTrait, _ := newKnativeTrait().(*knativeTrait) + expectedTrait.Enabled = ptr.To(true) + expectedTrait.SinkBinding = ptr.To(false) + expectedTrait.ChannelSources = []string{"knative:channel/test"} + assert.Equal(t, expectedTrait, trait) +} diff --git a/pkg/trait/logging.go b/pkg/trait/logging.go index fdd6656a47..8d88d21fcc 100644 --- a/pkg/trait/logging.go +++ b/pkg/trait/logging.go @@ -44,13 +44,10 @@ type loggingTrait struct { func newLoggingTraitTrait() Trait { return &loggingTrait{ BaseTrait: NewBaseTrait(loggingTraitID, loggingTraitOrder), - LoggingTrait: traitv1.LoggingTrait{ - Level: defaultLogLevel, - }, } } -func (l loggingTrait) Configure(e *Environment) (bool, *TraitCondition, error) { +func (l *loggingTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if e.Integration == nil { return false, nil, nil } @@ -61,7 +58,7 @@ func (l loggingTrait) Configure(e *Environment) (bool, *TraitCondition, error) { return e.IntegrationInRunningPhases(), nil, nil } -func (l loggingTrait) Apply(e *Environment) error { +func (l *loggingTrait) Apply(e *Environment) error { if e.CamelCatalog.Runtime.Capabilities["logging"].RuntimeProperties != nil { l.setCatalogConfiguration(e) } else { @@ -72,8 +69,8 @@ func (l loggingTrait) Apply(e *Environment) error { } // Deprecated: to be removed in future release in favor of func setCatalogConfiguration(). -func (l loggingTrait) setEnvConfiguration(e *Environment) { - envvar.SetVal(&e.EnvVars, envVarQuarkusLogLevel, l.Level) +func (l *loggingTrait) setEnvConfiguration(e *Environment) { + envvar.SetVal(&e.EnvVars, envVarQuarkusLogLevel, l.getLevel()) if l.Format != "" { envvar.SetVal(&e.EnvVars, envVarQuarkusLogConsoleFormat, l.Format) @@ -94,11 +91,11 @@ func (l loggingTrait) setEnvConfiguration(e *Environment) { } } -func (l loggingTrait) setCatalogConfiguration(e *Environment) { +func (l *loggingTrait) setCatalogConfiguration(e *Environment) { if e.ApplicationProperties == nil { e.ApplicationProperties = make(map[string]string) } - e.ApplicationProperties["camel.k.logging.level"] = l.Level + e.ApplicationProperties["camel.k.logging.level"] = l.getLevel() if l.Format != "" { e.ApplicationProperties["camel.k.logging.format"] = l.Format } @@ -121,3 +118,11 @@ func (l loggingTrait) setCatalogConfiguration(e *Environment) { } } } + +func (l *loggingTrait) getLevel() string { + if l.Level == "" { + return defaultLogLevel + } + + return l.Level +} diff --git a/pkg/trait/logging_test.go b/pkg/trait/logging_test.go index 7d77ff7e07..49a4ec878d 100644 --- a/pkg/trait/logging_test.go +++ b/pkg/trait/logging_test.go @@ -113,9 +113,10 @@ func NewLoggingTestCatalog() *Catalog { func TestEmptyLoggingTrait(t *testing.T) { env := createDefaultLoggingTestEnv(t) - conditions, err := NewLoggingTestCatalog().apply(env) + conditions, traits, err := NewLoggingTestCatalog().apply(env) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, env.ExecutedTraits) @@ -135,9 +136,10 @@ func TestEmptyLoggingTrait(t *testing.T) { func TestJsonLoggingTrait(t *testing.T) { // When running, this log should look like "09:07:00 INFO (main) Profile prod activated." env := createLoggingTestEnv(t, true, true, true, "TRACE", "%d{HH:mm:ss} %-5p (%t) %s%e%n") - conditions, err := NewLoggingTestCatalog().apply(env) + conditions, traits, err := NewLoggingTestCatalog().apply(env) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, env.ExecutedTraits) @@ -162,9 +164,10 @@ func TestDefaultQuarkusLogging(t *testing.T) { RuntimeProperties: nil, } env.EnvVars = []corev1.EnvVar{} - conditions, err := NewLoggingTestCatalog().apply(env) + conditions, traits, err := NewLoggingTestCatalog().apply(env) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, env.ExecutedTraits) diff --git a/pkg/trait/mount_test.go b/pkg/trait/mount_test.go index f23e1bbbc9..0a480e3147 100644 --- a/pkg/trait/mount_test.go +++ b/pkg/trait/mount_test.go @@ -43,9 +43,10 @@ func TestMountVolumesEmpty(t *testing.T) { environment.Integration.Spec.Traits = v1.Traits{} // empty traits environment.Platform.ResyncStatusFullConfig() - conditions, err := traitCatalog.apply(environment) + conditions, traits, err := traitCatalog.apply(environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("mount")) @@ -66,9 +67,10 @@ func TestMountVolumesIntegrationPhaseDeploying(t *testing.T) { environment := getNominalEnv(t, traitCatalog) environment.Platform.ResyncStatusFullConfig() - conditions, err := traitCatalog.apply(environment) + conditions, traits, err := traitCatalog.apply(environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("mount")) @@ -116,9 +118,10 @@ func TestEmptyDirVolumeIntegrationPhaseDeploying(t *testing.T) { EmptyDirs: []string{"my-empty-dir:/some/path"}, } environment.Platform.ResyncStatusFullConfig() - conditions, err := traitCatalog.apply(environment) + conditions, traits, err := traitCatalog.apply(environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("mount")) @@ -161,9 +164,10 @@ func TestMountVolumesIntegrationPhaseInitialization(t *testing.T) { environment.Integration.Status.Phase = v1.IntegrationPhaseInitialization environment.Platform.ResyncStatusFullConfig() - conditions, err := traitCatalog.apply(environment) + conditions, traits, err := traitCatalog.apply(environment) require.NoError(t, err) + assert.Empty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.Nil(t, environment.GetTrait("mount")) diff --git a/pkg/trait/platform.go b/pkg/trait/platform.go index cc9a04bbbb..d129e4bca6 100644 --- a/pkg/trait/platform.go +++ b/pkg/trait/platform.go @@ -39,6 +39,9 @@ const ( type platformTrait struct { BasePlatformTrait traitv1.PlatformTrait `property:",squash"` + // Parameters to be used internally + createDefault *bool + global *bool } func newPlatformTrait() Trait { @@ -62,17 +65,17 @@ func (t *platformTrait) Configure(e *Environment) (bool, *TraitCondition, error) if ocp, err := openshift.IsOpenShift(t.Client); err != nil { return false, nil, err } else if ocp { - t.CreateDefault = ptr.To(true) + t.createDefault = ptr.To(true) } else if addr, err := image.GetRegistryAddress(e.Ctx, t.Client); err != nil { return false, nil, err } else if addr != nil { - t.CreateDefault = ptr.To(true) + t.createDefault = ptr.To(true) } } if t.Global == nil { globalOperator := platform.IsCurrentOperatorGlobal() - t.Global = &globalOperator + t.global = &globalOperator } return true, nil, nil @@ -117,7 +120,7 @@ func (t *platformTrait) getOrCreatePlatform(e *Environment) (*v1.IntegrationPlat if err != nil && !apierrors.IsNotFound(err) { return nil, err } - if apierrors.IsNotFound(err) && ptr.Deref(t.CreateDefault, false) { + if apierrors.IsNotFound(err) && ptr.Deref(t.getCreateDefault(), false) { pl = t.createDefaultPlatform(e) e.Resources.Add(pl) @@ -146,7 +149,7 @@ func (t *platformTrait) createDefaultPlatform(e *Environment) *v1.IntegrationPla platformName = platform.DefaultPlatformName } namespace := e.Integration.Namespace - if ptr.Deref(t.Global, false) { + if ptr.Deref(t.getGlobal(), false) { operatorNamespace := platform.GetOperatorNamespace() if operatorNamespace != "" { namespace = operatorNamespace @@ -175,3 +178,19 @@ func (t *platformTrait) installViewerRole(e *Environment, itp *v1.IntegrationPla t.L.Infof("Cannot install global IntegrationPlatform viewer role in namespace '%s': skipping.", itp.Namespace) } } + +func (t *platformTrait) getCreateDefault() *bool { + if t.CreateDefault == nil { + return t.createDefault + } + + return t.CreateDefault +} + +func (t *platformTrait) getGlobal() *bool { + if t.Global == nil { + return t.global + } + + return t.Global +} diff --git a/pkg/trait/prometheus.go b/pkg/trait/prometheus.go index 4138ff8fd5..ba11aaa55a 100644 --- a/pkg/trait/prometheus.go +++ b/pkg/trait/prometheus.go @@ -44,9 +44,6 @@ type prometheusTrait struct { func newPrometheusTrait() Trait { return &prometheusTrait{ BaseTrait: NewBaseTrait(prometheusTraitID, prometheusTraitOrder), - PrometheusTrait: traitv1.PrometheusTrait{ - PodMonitor: ptr.To(true), - }, } } @@ -89,7 +86,7 @@ func (t *prometheusTrait) Apply(e *Environment) error { condition.Message = fmt.Sprintf("%s(%d)", container.Name, containerPort.ContainerPort) // Add the PodMonitor resource - if ptr.Deref(t.PodMonitor, false) { + if ptr.Deref(t.PodMonitor, true) { portName := containerPort.Name podMonitor, err := t.getPodMonitorFor(e, portName) if err != nil { diff --git a/pkg/trait/route.go b/pkg/trait/route.go index c35b83c879..710bb3a368 100644 --- a/pkg/trait/route.go +++ b/pkg/trait/route.go @@ -48,9 +48,6 @@ type routeTrait struct { func newRouteTrait() Trait { return &routeTrait{ BaseTrait: NewBaseTrait(routeTraitID, routeTraitOrder), - RouteTrait: traitv1.RouteTrait{ - Annotations: map[string]string{}, - }, } } diff --git a/pkg/trait/route_test.go b/pkg/trait/route_test.go index 376464fa68..2ab1b31065 100644 --- a/pkg/trait/route_test.go +++ b/pkg/trait/route_test.go @@ -207,8 +207,9 @@ func TestRoute_Default(t *testing.T) { environment := createTestRouteEnvironment(t, name) traitsCatalog := environment.Catalog - conditions, err := traitsCatalog.apply(environment) + conditions, traits, err := traitsCatalog.apply(environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("container")) @@ -243,8 +244,9 @@ func TestRoute_Disabled(t *testing.T) { "explicitly disabled", ) traitsCatalog := environment.Catalog - conditions, err := traitsCatalog.apply(environment) + conditions, traits, err := traitsCatalog.apply(environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.Contains(t, conditions, expectedCondition) assert.NotEmpty(t, environment.ExecutedTraits) assert.Nil(t, environment.GetTrait("route")) @@ -282,9 +284,10 @@ func TestRoute_Host(t *testing.T) { }, } - conditions, err := traitsCatalog.apply(environment) + conditions, traits, err := traitsCatalog.apply(environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("route")) @@ -313,9 +316,10 @@ func TestRoute_TLS_From_Secret_reencrypt(t *testing.T) { TLSDestinationCACertificateSecret: tlsMultipleSecretsName + "/" + tlsMultipleSecretsCert3Key, }, } - conditions, err := traitsCatalog.apply(environment) + conditions, traits, err := traitsCatalog.apply(environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("route")) @@ -349,7 +353,8 @@ func TestRoute_TLS_wrong_secret(t *testing.T) { TLSDestinationCACertificateSecret: "404", }, } - conditions, err := traitsCatalog.apply(environment) + conditions, traits, err := traitsCatalog.apply(environment) + assert.Empty(t, traits) assert.Empty(t, conditions) // there must be errors as the trait has wrong configuration require.Error(t, err) @@ -377,7 +382,8 @@ func TestRoute_TLS_secret_wrong_key(t *testing.T) { TLSCACertificateSecret: tlsMultipleSecretsName + "/foo", }, } - conditions, err := traitsCatalog.apply(environment) + conditions, traits, err := traitsCatalog.apply(environment) + assert.Empty(t, traits) assert.Empty(t, conditions) // there must be errors as the trait has wrong configuration require.Error(t, err) @@ -405,7 +411,8 @@ func TestRoute_TLS_secret_missing_key(t *testing.T) { TLSCACertificateSecret: tlsMultipleSecretsName, }, } - conditions, err := traitsCatalog.apply(environment) + conditions, traits, err := traitsCatalog.apply(environment) + assert.Empty(t, traits) assert.Empty(t, conditions) // there must be errors as the trait has wrong configuration require.Error(t, err) @@ -434,8 +441,9 @@ func TestRoute_TLS_reencrypt(t *testing.T) { TLSDestinationCACertificate: destinationCaCert, }, } - conditions, err := traitsCatalog.apply(environment) + conditions, traits, err := traitsCatalog.apply(environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("route")) @@ -468,8 +476,9 @@ func TestRoute_TLS_edge(t *testing.T) { TLSCACertificate: caCert, }, } - conditions, err := traitsCatalog.apply(environment) + conditions, traits, err := traitsCatalog.apply(environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("route")) @@ -500,8 +509,9 @@ func TestRoute_TLS_passthrough(t *testing.T) { TLSInsecureEdgeTerminationPolicy: string(routev1.InsecureEdgeTerminationPolicyAllow), }, } - conditions, err := traitsCatalog.apply(environment) + conditions, traits, err := traitsCatalog.apply(environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("route")) @@ -530,8 +540,9 @@ func TestRoute_WithCustomServicePort(t *testing.T) { } traitsCatalog := environment.Catalog - conditions, err := traitsCatalog.apply(environment) + conditions, traits, err := traitsCatalog.apply(environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("container")) @@ -564,8 +575,9 @@ func TestRouteAnnotation(t *testing.T) { } traitsCatalog := environment.Catalog - conditions, err := traitsCatalog.apply(environment) + conditions, traits, err := traitsCatalog.apply(environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) route := environment.Resources.GetRoute(func(r *routev1.Route) bool { diff --git a/pkg/trait/security_context.go b/pkg/trait/security_context.go index c43d90f980..d02c58a3f0 100644 --- a/pkg/trait/security_context.go +++ b/pkg/trait/security_context.go @@ -44,10 +44,6 @@ type securityContextTrait struct { func newSecurityContextTrait() Trait { return &securityContextTrait{ BasePlatformTrait: NewBasePlatformTrait(securityContextTraitID, securityContextTraitOder), - SecurityContextTrait: traitv1.SecurityContextTrait{ - RunAsNonRoot: ptr.To(defaultPodRunAsNonRoot), - SeccompProfileType: defaultPodSeccompProfileType, - }, } } @@ -66,7 +62,9 @@ func (t *securityContextTrait) Configure(e *Environment) (bool, *TraitCondition, if condition != nil && condition.Status == corev1.ConditionTrue { return false, NewIntegrationConditionPlatformDisabledWithMessage( "SecurityContext", - "pod security context is disabled for Knative Service. PodSecurityContext properties can affect non-user sidecar containers that come from Knative or your service mesh. Use container security context instead.", + "pod security context is disabled for Knative Service. "+ + "PodSecurityContext properties can affect non-user sidecar containers that come from Knative or your service mesh. "+ + "Use container security context instead.", ), nil } @@ -83,9 +81,9 @@ func (t *securityContextTrait) Apply(e *Environment) error { func (t *securityContextTrait) setSecurityContext(e *Environment, podSpec *corev1.PodSpec) error { sc := corev1.PodSecurityContext{ - RunAsNonRoot: t.RunAsNonRoot, + RunAsNonRoot: t.getRunAsNonRoot(), SeccompProfile: &corev1.SeccompProfile{ - Type: t.SeccompProfileType, + Type: t.getSeccompProfileType(), }, } @@ -123,3 +121,19 @@ func (t *securityContextTrait) getUser(e *Environment) (*int64, error) { return runAsUser, nil } + +func (t *securityContextTrait) getRunAsNonRoot() *bool { + if t.RunAsNonRoot == nil { + return ptr.To(defaultPodRunAsNonRoot) + } + + return t.RunAsNonRoot +} + +func (t *securityContextTrait) getSeccompProfileType() corev1.SeccompProfileType { + if t.SeccompProfileType == "" { + return defaultPodSeccompProfileType + } + + return t.SeccompProfileType +} diff --git a/pkg/trait/security_context_test.go b/pkg/trait/security_context_test.go index 9315657fa0..b9a49a2436 100644 --- a/pkg/trait/security_context_test.go +++ b/pkg/trait/security_context_test.go @@ -40,9 +40,10 @@ func TestDefaultPodKubernetesSecurityContextInitializationPhase(t *testing.T) { environment.Integration.Status.Phase = v1.IntegrationPhaseInitialization traitCatalog := NewCatalog(nil) - conditions, err := traitCatalog.apply(environment) + conditions, traits, err := traitCatalog.apply(environment) require.NoError(t, err) + assert.Empty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.Nil(t, environment.GetTrait("security-context")) @@ -52,9 +53,10 @@ func TestDefaultPodKubernetesSecurityContext(t *testing.T) { environment := createPodSettingContextEnvironment(t, v1.TraitProfileKubernetes) traitCatalog := NewCatalog(nil) - conditions, err := traitCatalog.apply(environment) + conditions, traits, err := traitCatalog.apply(environment) require.NoError(t, err) + assert.Empty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("deployment")) @@ -72,9 +74,10 @@ func TestDefaultPodOpenshiftSecurityContext(t *testing.T) { environment := createOpenshiftPodSettingContextEnvironment(t, v1.TraitProfileOpenShift) traitCatalog := NewCatalog(nil) - conditions, err := traitCatalog.apply(environment) + conditions, traits, err := traitCatalog.apply(environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("deployment")) @@ -97,9 +100,10 @@ func TestDefaultPodKnativeSecurityContext(t *testing.T) { } traitCatalog := NewCatalog(nil) - conditions, err := traitCatalog.apply(environment) + conditions, traits, err := traitCatalog.apply(environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.Nil(t, environment.GetTrait("deployment")) @@ -125,9 +129,10 @@ func TestUserPodSecurityContext(t *testing.T) { } traitCatalog := NewCatalog(nil) - conditions, err := traitCatalog.apply(environment) + conditions, traits, err := traitCatalog.apply(environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.NotEmpty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("deployment")) diff --git a/pkg/trait/service.go b/pkg/trait/service.go index 087f289dba..b114115f63 100644 --- a/pkg/trait/service.go +++ b/pkg/trait/service.go @@ -86,7 +86,8 @@ func (t *serviceTrait) Configure(e *Environment) (bool, *TraitCondition, error) ) return false, condition, err } - return exposeHTTPServices, nil, nil + + t.Enabled = ptr.To(exposeHTTPServices) } return ptr.Deref(t.Enabled, false), nil, nil diff --git a/pkg/trait/service_test.go b/pkg/trait/service_test.go index b61149115f..4d0f3f0859 100644 --- a/pkg/trait/service_test.go +++ b/pkg/trait/service_test.go @@ -109,7 +109,7 @@ func TestServiceWithDefaults(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -216,7 +216,7 @@ func TestService(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -303,7 +303,7 @@ func TestServiceWithCustomContainerName(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -394,7 +394,7 @@ func TestServiceWithNodePort(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - _, err = traitCatalog.apply(&environment) + _, _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -503,9 +503,10 @@ func TestServiceWithKnativeServiceEnabled(t *testing.T) { "TraitConfiguration", "explicitly disabled by the platform: knative-service trait has priority over this trait", ) - conditions, err := traitCatalog.apply(&environment) + conditions, traits, err := traitCatalog.apply(&environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.Contains(t, conditions, deploymentCondition) assert.Contains(t, conditions, serviceCondition) assert.NotEmpty(t, environment.ExecutedTraits) @@ -583,9 +584,10 @@ func TestServicesWithKnativeProfile(t *testing.T) { "TraitConfiguration", "explicitly disabled by the platform: knative-service trait has priority over this trait", ) - conditions, err := traitCatalog.apply(&environment) + conditions, traits, err := traitCatalog.apply(&environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.Contains(t, conditions, deploymentCondition) assert.Contains(t, conditions, serviceCondition) assert.NotEmpty(t, environment.ExecutedTraits) @@ -664,11 +666,77 @@ func TestServiceWithKnativeServiceDisabledInIntegrationPlatform(t *testing.T) { "KnativeServiceNotAvailable", "explicitly disabled", ) - conditions, err := traitCatalog.apply(&environment) + conditions, traits, err := traitCatalog.apply(&environment) require.NoError(t, err) + assert.NotEmpty(t, traits) assert.Contains(t, conditions, expectedCondition) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait(serviceTraitID)) assert.Nil(t, environment.GetTrait(knativeServiceTraitID)) } + +func TestServiceAutoConfiguration(t *testing.T) { + catalog, err := camel.DefaultCatalog() + require.NoError(t, err) + + client, _ := test.NewFakeClient() + traitCatalog := NewCatalog(nil) + + compressedRoute, err := gzip.CompressBase64([]byte(`from("netty-http:test").log("hello")`)) + require.NoError(t, err) + + environment := Environment{ + CamelCatalog: catalog, + Catalog: traitCatalog, + Client: client, + Integration: &v1.Integration{ + ObjectMeta: metav1.ObjectMeta{ + Name: ServiceTestName, + Namespace: "ns", + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseDeploying, + }, + Spec: v1.IntegrationSpec{ + Profile: v1.TraitProfileKubernetes, + Sources: []v1.SourceSpec{ + { + DataSpec: v1.DataSpec{ + Name: "routes.js", + Content: string(compressedRoute), + Compression: true, + }, + Language: v1.LanguageJavaScript, + }, + }, + }, + }, + IntegrationKit: &v1.IntegrationKit{ + Status: v1.IntegrationKitStatus{ + Phase: v1.IntegrationKitPhaseReady, + }, + }, + Platform: &v1.IntegrationPlatform{ + Spec: v1.IntegrationPlatformSpec{ + Cluster: v1.IntegrationPlatformClusterOpenShift, + Build: v1.IntegrationPlatformBuildSpec{ + PublishStrategy: v1.IntegrationPlatformBuildPublishStrategyS2I, + Registry: v1.RegistrySpec{Address: "registry"}, + RuntimeVersion: catalog.Runtime.Version, + }, + }, + Status: v1.IntegrationPlatformStatus{ + Phase: v1.IntegrationPlatformPhaseReady, + }, + }, + EnvVars: make([]corev1.EnvVar, 0), + ExecutedTraits: make([]Trait, 0), + Resources: kubernetes.NewCollection(), + } + environment.Platform.ResyncStatusFullConfig() + + _, traits, err := traitCatalog.apply(&environment) + require.NoError(t, err) + assert.Equal(t, ptr.To(true), traits.Service.Enabled) +} diff --git a/pkg/trait/trait.go b/pkg/trait/trait.go index 5d9076c135..93c10a74aa 100644 --- a/pkg/trait/trait.go +++ b/pkg/trait/trait.go @@ -57,22 +57,28 @@ func Apply(ctx context.Context, c client.Client, integration *v1.Integration, ki environment.Catalog = catalog // invoke the trait framework to determine the needed resources - conditions, err := catalog.apply(environment) - // Conditions contains informative message coming from the trait execution and useful to be reported into it or ik CR + conditions, traits, err := catalog.apply(environment) + // WARNING: Conditions contains informative message coming from the trait execution and useful to be reported into it or ik CR // they must be applied before returning after an error for _, tc := range conditions { switch { case integration != nil: - // set an Integration condition integration.Status.SetCondition(tc.integrationCondition()) case kit != nil: - // set an IntegrationKit condition kit.Status.SetCondition(tc.integrationKitCondition()) } } if err != nil { return nil, fmt.Errorf("error during trait customization: %w", err) } + // Set the executed traits taking care to merge in order to avoid the distinct execution + // phase to clean up any previous executed trait + if integration != nil { + integration.Status.Traits = integration.Spec.Traits.DeepCopy() + if err := integration.Status.Traits.Merge(*traits); err != nil { + return nil, fmt.Errorf("error setting status traits: %w", err) + } + } postActionErrors := make([]error, 0) // execute post actions registered by traits @@ -172,7 +178,7 @@ func NewSyntheticEnvironment(ctx context.Context, c client.Client, integration * // set the catalog env.Catalog = catalog // we need to simulate the execution of the traits to fill certain values used later by monitoring - _, err := catalog.apply(&env) + _, _, err := catalog.apply(&env) if err != nil { return nil, fmt.Errorf("error during trait customization: %w", err) } diff --git a/pkg/trait/trait_catalog.go b/pkg/trait/trait_catalog.go index 867c758143..b59b3b5b0d 100644 --- a/pkg/trait/trait_catalog.go +++ b/pkg/trait/trait_catalog.go @@ -27,6 +27,7 @@ import ( "github.com/apache/camel-k/v2/pkg/client" "github.com/apache/camel-k/v2/pkg/util/log" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/json" ) // Catalog collects all information about traits in one place. @@ -86,10 +87,10 @@ func (c *Catalog) TraitsForProfile(profile v1.TraitProfile) []Trait { return res } -func (c *Catalog) apply(environment *Environment) ([]*TraitCondition, error) { +func (c *Catalog) apply(environment *Environment) ([]*TraitCondition, *v1.Traits, error) { traitsConditions := []*TraitCondition{} if err := c.Configure(environment); err != nil { - return traitsConditions, err + return traitsConditions, nil, err } traits := c.traitsFor(environment) environment.ConfiguredTraits = traits @@ -107,49 +108,75 @@ func (c *Catalog) apply(environment *Environment) ([]*TraitCondition, error) { traitsConditions = append(traitsConditions, condition) } if err != nil { - return traitsConditions, fmt.Errorf("%s trait configuration failed: %w", trait.ID(), err) + return traitsConditions, nil, fmt.Errorf("%s trait configuration failed: %w", trait.ID(), err) } - if enabled { err = trait.Apply(environment) if err != nil { - return traitsConditions, fmt.Errorf("%s trait execution failed: %w", trait.ID(), err) + return traitsConditions, nil, fmt.Errorf("%s trait execution failed: %w", trait.ID(), err) } environment.ExecutedTraits = append(environment.ExecutedTraits, trait) // execute post step processors for _, processor := range environment.PostStepProcessors { err := processor(environment) if err != nil { - return traitsConditions, fmt.Errorf("%s trait executing post step action failed: %w", trait.ID(), err) + return traitsConditions, nil, fmt.Errorf("%s trait executing post step action failed: %w", trait.ID(), err) } } } } - traitsConditions = append(traitsConditions, c.executedTraitCondition(environment.ExecutedTraits)) + cs, ts, err := c.executedTraitCondition(environment.ExecutedTraits) + if err != nil { + return traitsConditions, &ts, err + } + traitsConditions = append(traitsConditions, cs) if !applicable && environment.PlatformInPhase(v1.IntegrationPlatformPhaseReady) { - return traitsConditions, errors.New("no trait can be executed because of no ready platform found") + return traitsConditions, nil, errors.New("no trait can be executed because of no ready platform found") } for _, processor := range environment.PostProcessors { err := processor(environment) if err != nil { - return traitsConditions, fmt.Errorf("error executing post processor: %w", err) + return traitsConditions, nil, fmt.Errorf("error executing post processor: %w", err) } } - return traitsConditions, nil + return traitsConditions, &ts, nil } -func (c *Catalog) executedTraitCondition(executedTrait []Trait) *TraitCondition { +func (c *Catalog) executedTraitCondition(executedTrait []Trait) (*TraitCondition, v1.Traits, error) { + var traits v1.Traits + var traitMap = make(map[string]map[string]interface{}) traitIds := make([]string, 0) for _, trait := range executedTrait { + data, err := json.Marshal(trait) + if err != nil { + return nil, traits, err + } + var traitIDMap map[string]interface{} + if err := json.Unmarshal(data, &traitIDMap); err != nil { + return nil, traits, err + } + if len(traitIDMap) > 0 { + traitMap[string(trait.ID())] = traitIDMap + } + traitIds = append(traitIds, string(trait.ID())) } + + traitData, err := json.Marshal(traitMap) + if err != nil { + return nil, traits, err + } + if err := json.Unmarshal(traitData, &traits); err != nil { + return nil, traits, err + } + message := fmt.Sprintf("Applied traits: %s", strings.Join(traitIds, ",")) c.L.Debugf(message) - return NewIntegrationCondition("", v1.IntegrationConditionTraitInfo, corev1.ConditionTrue, traitConfigurationReason, message) + return NewIntegrationCondition("", v1.IntegrationConditionTraitInfo, corev1.ConditionTrue, traitConfigurationReason, message), traits, nil } // GetTrait returns the trait with the given ID. diff --git a/pkg/trait/trait_catalog_test.go b/pkg/trait/trait_catalog_test.go new file mode 100644 index 0000000000..3475ae2f0e --- /dev/null +++ b/pkg/trait/trait_catalog_test.go @@ -0,0 +1,89 @@ +/* +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 ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" + "github.com/apache/camel-k/v2/pkg/util/camel" + "github.com/apache/camel-k/v2/pkg/util/defaults" + "github.com/apache/camel-k/v2/pkg/util/kubernetes" + "github.com/apache/camel-k/v2/pkg/util/test" +) + +func TestIntegrationExecutedTrait(t *testing.T) { + ip := v1.IntegrationPlatform{} + ip.Namespace = "ns" + ip.Name = "ck" + ip.Status = v1.IntegrationPlatformStatus{ + IntegrationPlatformSpec: v1.IntegrationPlatformSpec{ + Build: v1.IntegrationPlatformBuildSpec{ + RuntimeProvider: v1.RuntimeProviderQuarkus, + RuntimeVersion: defaults.DefaultRuntimeVersion, + }, + }, + Phase: v1.IntegrationPlatformPhaseReady, + } + c, err := test.NewFakeClient(&ip) + require.NoError(t, err) + catalog := NewCatalog(c) + env := Environment{ + CamelCatalog: &camel.RuntimeCatalog{ + CamelCatalogSpec: v1.CamelCatalogSpec{ + Runtime: v1.RuntimeSpec{ + Version: defaults.DefaultRuntimeVersion, + Provider: v1.RuntimeProviderQuarkus, + }, + }, + }, + Catalog: catalog, + Ctx: context.Background(), + Client: c, + Integration: &v1.Integration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "it", + Namespace: "ns", + }, + Spec: v1.IntegrationSpec{ + Profile: v1.TraitProfileKubernetes, + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseRunning, + Conditions: []v1.IntegrationCondition{ + { + Type: v1.IntegrationConditionDeploymentAvailable, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + Platform: &ip, + Resources: kubernetes.NewCollection(), + } + + _, ts, err := catalog.apply(&env) + require.NoError(t, err) + assert.Equal(t, &v1.Traits{}, ts) +} diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go index 1bbf4d06dd..4803d5719c 100644 --- a/pkg/trait/trait_test.go +++ b/pkg/trait/trait_test.go @@ -18,10 +18,13 @@ limitations under the License. package trait import ( + "context" + "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "gopkg.in/yaml.v2" routev1 "github.com/openshift/api/route/v1" @@ -32,8 +35,10 @@ import ( v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait" + "github.com/apache/camel-k/v2/pkg/resources" "github.com/apache/camel-k/v2/pkg/util/boolean" "github.com/apache/camel-k/v2/pkg/util/camel" + "github.com/apache/camel-k/v2/pkg/util/defaults" "github.com/apache/camel-k/v2/pkg/util/kubernetes" "github.com/apache/camel-k/v2/pkg/util/test" ) @@ -43,7 +48,7 @@ const ( ) func TestOpenShiftTraits(t *testing.T) { - env := createTestEnv(t, v1.IntegrationPlatformClusterOpenShift, "camel:core") + env := createTestEnv(t, v1.IntegrationPlatformClusterOpenShift, "from('timer:my-timer').to('log:info')") res := processTestEnv(t, env) assert.NotEmpty(t, env.ExecutedTraits) @@ -413,7 +418,7 @@ func processTestEnv(t *testing.T, env *Environment) *kubernetes.Collection { t.Helper() catalog := NewTraitTestCatalog() - _, err := catalog.apply(env) + _, _, err := catalog.apply(env) require.NoError(t, err) return env.Resources } @@ -478,11 +483,11 @@ func NewTraitTestCatalog() *Catalog { } func TestExecutedTraitsCondition(t *testing.T) { - env := createTestEnv(t, v1.IntegrationPlatformClusterOpenShift, "camel:core") + env := createTestEnv(t, v1.IntegrationPlatformClusterOpenShift, "from('timer:test').to('log:info')") catalog := NewTraitTestCatalog() - conditions, err := catalog.apply(env) + conditions, traits, err := catalog.apply(env) require.NoError(t, err) - + assert.Empty(t, traits) expectedCondition := NewIntegrationCondition( "", v1.IntegrationConditionTraitInfo, @@ -492,3 +497,182 @@ func TestExecutedTraitsCondition(t *testing.T) { ) assert.Contains(t, conditions, expectedCondition) } + +func testDefaultIntegrationPhaseTraitsSetting(t *testing.T, phase v1.IntegrationPhase) { + it := &v1.Integration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-it", + Namespace: "ns", + }, + Spec: v1.IntegrationSpec{ + Sources: []v1.SourceSpec{ + { + DataSpec: v1.DataSpec{ + Name: "file.groovy", + Content: "from('timer:test').to('log:info')", + }, + Language: v1.LanguageGroovy, + }, + }, + }, + Status: v1.IntegrationStatus{ + Phase: phase, + Conditions: []v1.IntegrationCondition{ + { + Type: v1.IntegrationConditionDeploymentAvailable, + Status: corev1.ConditionTrue, + }, + }, + }, + } + platform := &v1.IntegrationPlatform{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-platform", + Namespace: "ns", + }, + Status: v1.IntegrationPlatformStatus{ + IntegrationPlatformSpec: v1.IntegrationPlatformSpec{ + Build: v1.IntegrationPlatformBuildSpec{ + RuntimeProvider: v1.RuntimeProviderQuarkus, + RuntimeVersion: defaults.DefaultRuntimeVersion, + }, + }, + Phase: v1.IntegrationPlatformPhaseReady, + }, + } + // Load the default catalog + camelCatalogData, err := resources.Resource(fmt.Sprintf("/resources/camel-catalog-%s.yaml", defaults.DefaultRuntimeVersion)) + require.NoError(t, err) + var cat v1.CamelCatalog + err = yaml.Unmarshal(camelCatalogData, &cat) + require.NoError(t, err) + cat.SetAnnotations(platform.Annotations) + cat.SetNamespace(platform.Namespace) + + client, err := test.NewFakeClient(platform, &cat) + require.NoError(t, err) + env, err := Apply(context.Background(), client, it, nil) + require.NoError(t, err) + assert.NotEmpty(t, env.Integration) + // We should get no values slicking from the spec + // only traits marked as "auto" should be able to scan + // the sources and set values. + // Neither default traits values should not be included here. + assert.Equal(t, &v1.Traits{}, env.Integration.Status.Traits) +} + +func TestDefaultIntegrationRunningTraitsSetting(t *testing.T) { + testDefaultIntegrationPhaseTraitsSetting(t, v1.IntegrationPhaseRunning) +} + +func TestDefaultIntegrationNoneTraitsSetting(t *testing.T) { + testDefaultIntegrationPhaseTraitsSetting(t, v1.IntegrationPhaseNone) +} + +func TestDefaultIntegrationInitTraitsSetting(t *testing.T) { + testDefaultIntegrationPhaseTraitsSetting(t, v1.IntegrationPhaseInitialization) +} + +func TestDefaultIntegrationWaitingPlatformTraitsSetting(t *testing.T) { + testDefaultIntegrationPhaseTraitsSetting(t, v1.IntegrationPhaseWaitingForPlatform) +} + +func TestDefaultIntegrationBuildingKitTraitsSetting(t *testing.T) { + testDefaultIntegrationPhaseTraitsSetting(t, v1.IntegrationPhaseBuildingKit) +} + +func TestDefaultIntegrationDeployingTraitsSetting(t *testing.T) { + testDefaultIntegrationPhaseTraitsSetting(t, v1.IntegrationPhaseDeploying) +} + +func TestDefaultIntegrationErrorTraitsSetting(t *testing.T) { + testDefaultIntegrationPhaseTraitsSetting(t, v1.IntegrationPhaseError) +} + +func TestIntegrationTraitsSetting(t *testing.T) { + it := &v1.Integration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-it", + Namespace: "ns", + }, + Spec: v1.IntegrationSpec{ + Sources: []v1.SourceSpec{ + { + DataSpec: v1.DataSpec{ + Name: "file.groovy", + Content: "from('timer:test').to('log:info')", + }, + Language: v1.LanguageGroovy, + }, + }, + Traits: v1.Traits{ + Builder: &traitv1.BuilderTrait{ + Properties: []string{ + "building=yes", + }, + }, + Camel: &traitv1.CamelTrait{ + Properties: []string{ + "hello=world", + }, + }, + Container: &traitv1.ContainerTrait{ + Name: "my-container-name", + }, + }, + }, + Status: v1.IntegrationStatus{ + Phase: v1.IntegrationPhaseRunning, + Conditions: []v1.IntegrationCondition{ + { + Type: v1.IntegrationConditionDeploymentAvailable, + Status: corev1.ConditionTrue, + }, + }, + }, + } + platform := &v1.IntegrationPlatform{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-platform", + Namespace: "ns", + }, + Status: v1.IntegrationPlatformStatus{ + IntegrationPlatformSpec: v1.IntegrationPlatformSpec{ + Build: v1.IntegrationPlatformBuildSpec{ + RuntimeProvider: v1.RuntimeProviderQuarkus, + RuntimeVersion: defaults.DefaultRuntimeVersion, + }, + }, + Phase: v1.IntegrationPlatformPhaseReady, + }, + } + // Load the default catalog + camelCatalogData, err := resources.Resource(fmt.Sprintf("/resources/camel-catalog-%s.yaml", defaults.DefaultRuntimeVersion)) + require.NoError(t, err) + var cat v1.CamelCatalog + err = yaml.Unmarshal(camelCatalogData, &cat) + require.NoError(t, err) + cat.SetAnnotations(platform.Annotations) + cat.SetNamespace(platform.Namespace) + + client, err := test.NewFakeClient(platform, &cat) + require.NoError(t, err) + env, err := Apply(context.Background(), client, it, nil) + require.NoError(t, err) + assert.NotEmpty(t, env.Integration) + assert.Equal(t, &v1.Traits{ + Builder: &traitv1.BuilderTrait{ + Properties: []string{ + "building=yes", + }, + }, + Camel: &traitv1.CamelTrait{ + Properties: []string{ + "hello=world", + }, + }, + Container: &traitv1.ContainerTrait{ + Name: "my-container-name", + }, + }, env.Integration.Status.Traits) +} diff --git a/pkg/trait/trait_types.go b/pkg/trait/trait_types.go index 7a890af40e..5b6ac50b96 100644 --- a/pkg/trait/trait_types.go +++ b/pkg/trait/trait_types.go @@ -670,7 +670,7 @@ func (e *Environment) GetIntegrationContainerName() string { if dt := e.Catalog.GetTrait(containerTraitID); dt != nil { if ct, ok := dt.(*containerTrait); ok { - containerName = ct.Name + containerName = ct.getContainerName() } } return containerName @@ -727,7 +727,7 @@ func (e *Environment) createContainerPort() *corev1.ContainerPort { if t := e.Catalog.GetTrait(containerTraitID); t != nil { if ct, ok := t.(*containerTrait); ok { name = ct.PortName - port = ct.Port + port = ct.getPort() } } @@ -757,7 +757,8 @@ func CapabilityPropertyKey(camelPropertyKey string, vars map[string]string) stri } // ConsumeMeta is used to consume metadata information coming from Integration sources. If no sources available, -// would return false. +// would return false. When consuming from meta you should make sure that the configuration is stored in the +// status traits by setting each trait configuration when in "auto" mode. func (e *Environment) ConsumeMeta(consumeMeta func(metadata.IntegrationMetadata) bool) (bool, error) { return e.consumeSourcesMeta(nil, consumeMeta) } diff --git a/pkg/trait/trait_types_test.go b/pkg/trait/trait_types_test.go index 8f945397f8..608fc3d076 100644 --- a/pkg/trait/trait_types_test.go +++ b/pkg/trait/trait_types_test.go @@ -198,7 +198,7 @@ func TestDetermineControllerStrategySyntheticKitForceKnative(t *testing.T) { Auto: ptr.To(false), } e.Platform.ResyncStatusFullConfig() - _, err := e.Catalog.apply(e) + _, _, err := e.Catalog.apply(e) require.NoError(t, err) strategy, err := e.DetermineControllerStrategy() @@ -270,7 +270,7 @@ func createTestEnvironment(t *testing.T, profile v1.TraitProfile) *Environment { environment.Platform.ResyncStatusFullConfig() - _, err = traitCatalog.apply(environment) + _, _, err = traitCatalog.apply(environment) require.NoError(t, err) return environment @@ -321,7 +321,7 @@ func createNonManagedBuildTestEnvironment(t *testing.T, profile v1.TraitProfile) environment.Platform.ResyncStatusFullConfig() - _, err = traitCatalog.apply(environment) + _, _, err = traitCatalog.apply(environment) require.NoError(t, err) return environment diff --git a/pkg/trait/util.go b/pkg/trait/util.go index 6a8083b59c..ec1f62c6fd 100644 --- a/pkg/trait/util.go +++ b/pkg/trait/util.go @@ -37,10 +37,6 @@ import ( "github.com/apache/camel-k/v2/pkg/util/sets" ) -func ptrFrom[T any](value T) *T { - return &value -} - type Options map[string]map[string]interface{} func (u Options) Get(id string) (map[string]interface{}, bool) {