From 4046ee82ecbc0327902197aa11f6394abae1de6e Mon Sep 17 00:00:00 2001 From: Pasquale Congiusti Date: Tue, 24 Oct 2023 13:19:18 +0200 Subject: [PATCH 1/2] feat(trait): expose conditions Add a TraitCondition to be returned by each trait configuration. With this we harmonize the way we collect and expose informations about traits configuration. --- addons/keda/keda.go | 11 ++- addons/keda/keda_test.go | 21 +++-- addons/master/master.go | 22 +++-- addons/resume/resume.go | 13 ++- addons/telemetry/telemetry.go | 17 +++- addons/telemetry/telemetry_test.go | 6 +- addons/threescale/3scale.go | 10 +-- addons/threescale/3scale_test.go | 9 ++- addons/tracing/tracing.go | 8 +- addons/tracing/tracing_test.go | 3 +- addons/vault/aws/aws_secrets_manager.go | 10 +-- addons/vault/aws/aws_secrets_manager_test.go | 12 ++- addons/vault/azure/azure_key_vault.go | 10 +-- addons/vault/azure/azure_key_vault_test.go | 9 ++- addons/vault/gcp/gcp_secret_manager.go | 10 +-- addons/vault/gcp/gcp_secret_manager_test.go | 6 +- addons/vault/hashicorp/hashicorp_vault.go | 10 +-- .../vault/hashicorp/hashicorp_vault_test.go | 9 ++- pkg/apis/camel/v1/integration_types.go | 3 +- pkg/apis/camel/v1/integrationkit_types.go | 2 + pkg/trait/affinity.go | 8 +- pkg/trait/affinity_test.go | 9 ++- pkg/trait/builder.go | 50 +++++++++--- pkg/trait/builder_test.go | 19 +++-- pkg/trait/camel.go | 4 +- pkg/trait/camel_test.go | 15 ++-- pkg/trait/container.go | 19 +++-- pkg/trait/container_probes_test.go | 28 +++++-- pkg/trait/container_test.go | 35 +++++--- pkg/trait/cron.go | 65 ++++++--------- pkg/trait/cron_test.go | 31 ++++--- pkg/trait/dependencies.go | 6 +- pkg/trait/dependencies_test.go | 27 ++++--- pkg/trait/deployer.go | 4 +- pkg/trait/deployer_test.go | 8 +- pkg/trait/deployment.go | 22 +++-- pkg/trait/deployment_test.go | 30 +++---- pkg/trait/environment.go | 6 +- pkg/trait/environment_test.go | 16 ++-- pkg/trait/error_handler.go | 8 +- pkg/trait/error_handler_test.go | 18 +++-- pkg/trait/gc.go | 11 ++- pkg/trait/gc_test.go | 16 +++- pkg/trait/health.go | 9 +-- pkg/trait/ingress.go | 28 +++---- pkg/trait/ingress_test.go | 38 ++++----- pkg/trait/init.go | 6 +- pkg/trait/istio.go | 6 +- pkg/trait/istio_test.go | 16 ++-- pkg/trait/jolokia.go | 6 +- pkg/trait/jolokia_test.go | 3 +- pkg/trait/jvm.go | 11 ++- pkg/trait/jvm_test.go | 9 ++- pkg/trait/kamelets.go | 16 ++-- pkg/trait/kamelets_test.go | 30 ++++--- pkg/trait/knative.go | 39 ++++----- pkg/trait/knative_service.go | 52 ++++++------ pkg/trait/knative_service_test.go | 17 ++-- pkg/trait/knative_test.go | 16 ++-- pkg/trait/logging.go | 12 ++- pkg/trait/logging_test.go | 6 +- pkg/trait/mount.go | 12 +-- pkg/trait/mount_test.go | 9 ++- pkg/trait/openapi.go | 10 +-- pkg/trait/openapi_test.go | 12 ++- pkg/trait/owner.go | 11 ++- pkg/trait/pdb.go | 12 +-- pkg/trait/pdb_test.go | 6 +- pkg/trait/platform.go | 12 +-- pkg/trait/platform_test.go | 9 ++- pkg/trait/pod.go | 14 ++-- pkg/trait/pod_test.go | 9 ++- pkg/trait/prometheus.go | 6 +- pkg/trait/prometheus_test.go | 6 +- pkg/trait/pull_secret.go | 18 +++-- pkg/trait/pull_secret_test.go | 12 ++- pkg/trait/quarkus.go | 14 ++-- pkg/trait/quarkus_test.go | 9 ++- pkg/trait/registry.go | 6 +- pkg/trait/route.go | 39 ++++----- pkg/trait/route_test.go | 56 +++++++------ pkg/trait/service.go | 46 +++++------ pkg/trait/service_binding.go | 14 ++-- pkg/trait/service_test.go | 56 +++++++++++-- pkg/trait/toleration.go | 8 +- pkg/trait/toleration_test.go | 3 +- pkg/trait/trait.go | 15 +++- pkg/trait/trait_catalog.go | 23 +++--- pkg/trait/trait_condition_types.go | 80 +++++++++++++++++++ pkg/trait/trait_test.go | 2 +- pkg/trait/trait_types.go | 2 +- pkg/trait/util.go | 7 -- 92 files changed, 905 insertions(+), 609 deletions(-) create mode 100644 pkg/trait/trait_condition_types.go diff --git a/addons/keda/keda.go b/addons/keda/keda.go index 8fa069c723..ce4b56ab9a 100644 --- a/addons/keda/keda.go +++ b/addons/keda/keda.go @@ -116,22 +116,21 @@ func NewKedaTrait() trait.Trait { } } -func (t *kedaTrait) Configure(e *trait.Environment) (bool, error) { +func (t *kedaTrait) Configure(e *trait.Environment) (bool, *trait.TraitCondition, error) { if e.Integration == nil || !pointer.BoolDeref(t.Enabled, false) { - return false, nil + return false, nil, nil } - if !e.IntegrationInPhase(camelv1.IntegrationPhaseInitialization) && !e.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil } if t.Auto == nil || *t.Auto { if err := t.populateTriggersFromKamelets(e); err != nil { - return false, err + return false, nil, err } } - return len(t.Triggers) > 0, nil + return len(t.Triggers) > 0, nil, nil } func (t *kedaTrait) Apply(e *trait.Environment) error { diff --git a/addons/keda/keda_test.go b/addons/keda/keda_test.go index 73d60d4eaa..d43ed1f63b 100644 --- a/addons/keda/keda_test.go +++ b/addons/keda/keda_test.go @@ -52,9 +52,10 @@ func TestManualConfig(t *testing.T) { }) env := createBasicTestEnvironment() - res, err := keda.Configure(env) + res, condition, err := keda.Configure(env) assert.NoError(t, err) assert.True(t, res) + assert.Nil(t, condition) assert.NoError(t, keda.Apply(env)) so := getScaledObject(env) assert.NotNil(t, so) @@ -90,9 +91,10 @@ func TestConfigFromSecret(t *testing.T) { }, }) - res, err := keda.Configure(env) + res, condition, err := keda.Configure(env) assert.NoError(t, err) assert.True(t, res) + assert.Nil(t, condition) assert.NoError(t, keda.Apply(env)) so := getScaledObject(env) assert.NotNil(t, so) @@ -177,9 +179,10 @@ func TestKameletAutoDetection(t *testing.T) { }, }) - res, err := keda.Configure(env) + res, condition, err := keda.Configure(env) assert.NoError(t, err) assert.True(t, res) + assert.Nil(t, condition) assert.NoError(t, keda.Apply(env)) so := getScaledObject(env) assert.NotNil(t, so) @@ -284,15 +287,17 @@ func TestPipeAutoDetection(t *testing.T) { it.Status.Phase = camelv1.IntegrationPhaseInitialization init := trait.NewInitTrait() - ok, err := init.Configure(env) + ok, condition, err := init.Configure(env) assert.NoError(t, err) assert.True(t, ok) + assert.Nil(t, condition) assert.NoError(t, init.Apply(env)) it.Status.Phase = camelv1.IntegrationPhaseDeploying - res, err := keda.Configure(env) + res, condition, err := keda.Configure(env) assert.NoError(t, err) assert.True(t, res) + assert.Nil(t, condition) assert.NoError(t, keda.Apply(env)) so := getScaledObject(env) assert.NotNil(t, so) @@ -339,9 +344,10 @@ func TestHackReplicas(t *testing.T) { }, ) - res, err := keda.Configure(env) + res, condition, err := keda.Configure(env) assert.NoError(t, err) assert.True(t, res) + assert.Nil(t, condition) assert.NoError(t, keda.Apply(env)) scalesClient, err := env.Client.ScalesClient() assert.NoError(t, err) @@ -386,9 +392,10 @@ func TestHackKLBReplicas(t *testing.T) { }, ) - res, err := keda.Configure(env) + res, condition, err := keda.Configure(env) assert.NoError(t, err) assert.True(t, res) + assert.Nil(t, condition) assert.NoError(t, keda.Apply(env)) scalesClient, err := env.Client.ScalesClient() assert.NoError(t, err) diff --git a/addons/master/master.go b/addons/master/master.go index aad5a8af59..c60733341a 100644 --- a/addons/master/master.go +++ b/addons/master/master.go @@ -84,25 +84,27 @@ var ( configMapResourceType = "ConfigMap" ) -func (t *masterTrait) Configure(e *trait.Environment) (bool, error) { +func (t *masterTrait) Configure(e *trait.Environment) (bool, *trait.TraitCondition, error) { if e.Integration == nil || !pointer.BoolDeref(t.Enabled, true) { - return false, nil + return false, nil, nil } - if !e.IntegrationInPhase(v1.IntegrationPhaseInitialization) && !e.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil + } + if !pointer.BoolDeref(t.Enabled, false) { + return false, trait.NewIntegrationConditionUserDisabled(), nil } if pointer.BoolDeref(t.Auto, true) { // Check if the master component has been used sources, err := kubernetes.ResolveIntegrationSources(e.Ctx, t.Client, e.Integration, e.Resources) if err != nil { - return false, err + return false, nil, err } meta, err := metadata.ExtractAll(e.CamelCatalog, sources) if err != nil { - return false, err + return false, nil, err } if t.Enabled == nil { @@ -114,10 +116,6 @@ func (t *masterTrait) Configure(e *trait.Environment) (bool, error) { } } - if !pointer.BoolDeref(t.Enabled, false) { - return false, nil - } - if t.IncludeDelegateDependencies == nil || *t.IncludeDelegateDependencies { t.delegateDependencies = findAdditionalDependencies(e, meta) } @@ -130,7 +128,7 @@ func (t *masterTrait) Configure(e *trait.Environment) (bool, error) { if t.ResourceType == nil { canUseLeases, err := t.canUseLeases(e) if err != nil { - return false, err + return false, nil, err } if canUseLeases { t.ResourceType = &leaseResourceType @@ -149,7 +147,7 @@ func (t *masterTrait) Configure(e *trait.Environment) (bool, error) { } } - return pointer.BoolDeref(t.Enabled, true), nil + return pointer.BoolDeref(t.Enabled, true), nil, nil } func (t *masterTrait) Apply(e *trait.Environment) error { diff --git a/addons/resume/resume.go b/addons/resume/resume.go index f593b9fc88..221ae0364a 100644 --- a/addons/resume/resume.go +++ b/addons/resume/resume.go @@ -75,25 +75,24 @@ func NewResumeTrait() trait.Trait { } } -func (r *resumeTrait) Configure(environment *trait.Environment) (bool, error) { +func (r *resumeTrait) Configure(environment *trait.Environment) (bool, *trait.TraitCondition, error) { if !pointer.BoolDeref(r.Enabled, false) { - return false, nil + return false, nil, nil } - if !environment.IntegrationInPhase(v1.IntegrationPhaseInitialization) && !environment.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil } if pointer.BoolDeref(r.Auto, true) { // Check which components have been used sources, err := kubernetes.ResolveIntegrationSources(environment.Ctx, r.Client, environment.Integration, environment.Resources) if err != nil { - return false, err + return false, nil, err } meta, err := metadata.ExtractAll(environment.CamelCatalog, sources) if err != nil { - return false, err + return false, nil, err } for _, endpoint := range meta.FromURIs { @@ -109,7 +108,7 @@ func (r *resumeTrait) Configure(environment *trait.Environment) (bool, error) { } } - return r.Enabled != nil && *r.Enabled, nil + return r.Enabled != nil && *r.Enabled, nil, nil } func (r *resumeTrait) Apply(environment *trait.Environment) error { diff --git a/addons/telemetry/telemetry.go b/addons/telemetry/telemetry.go index 0ad3035dee..2d830d9911 100644 --- a/addons/telemetry/telemetry.go +++ b/addons/telemetry/telemetry.go @@ -20,6 +20,8 @@ package telemetry import ( "k8s.io/utils/pointer" + corev1 "k8s.io/api/core/v1" + "github.com/apache/camel-k/v2/addons/telemetry/discovery" v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait" @@ -85,20 +87,27 @@ func NewTelemetryTrait() trait.Trait { } } -func (t *telemetryTrait) Configure(e *trait.Environment) (bool, error) { +func (t *telemetryTrait) Configure(e *trait.Environment) (bool, *trait.TraitCondition, error) { if e.Integration == nil || !pointer.BoolDeref(t.Enabled, false) { - return false, nil + return false, nil, nil } + var condition *trait.TraitCondition if pointer.BoolDeref(t.Auto, true) { if t.Endpoint == "" { for _, locator := range discovery.TelemetryLocators { endpoint, err := locator.FindEndpoint(e.Ctx, t.Client, t.L, e) if err != nil { - return false, err + return false, nil, err } if endpoint != "" { t.L.Infof("Using tracing endpoint: %s", endpoint) + condition = trait.NewIntegrationCondition( + v1.IntegrationConditionTraitInfo, + corev1.ConditionTrue, + "Tracing endpoint", + endpoint, + ) t.Endpoint = endpoint break } @@ -114,7 +123,7 @@ func (t *telemetryTrait) Configure(e *trait.Environment) (bool, error) { } } - return true, nil + return true, condition, nil } func (t *telemetryTrait) Apply(e *trait.Environment) error { diff --git a/addons/telemetry/telemetry_test.go b/addons/telemetry/telemetry_test.go index 76bea8696e..c15af2894d 100644 --- a/addons/telemetry/telemetry_test.go +++ b/addons/telemetry/telemetry_test.go @@ -36,9 +36,10 @@ func TestTelemetryTraitOnDefaultQuarkus(t *testing.T) { tt, _ := telemetry.(*telemetryTrait) tt.Enabled = pointer.Bool(true) tt.Endpoint = "http://endpoint3" - ok, err := telemetry.Configure(e) + ok, condition, err := telemetry.Configure(e) assert.Nil(t, err) assert.True(t, ok) + assert.Nil(t, condition) err = telemetry.Apply(e) assert.Nil(t, err) @@ -61,9 +62,10 @@ func TestTelemetryTraitWithValues(t *testing.T) { tt.Sampler = "ratio" tt.SamplerRatio = "0.001" tt.SamplerParentBased = pointer.Bool(false) - ok, err := telemetry.Configure(e) + ok, condition, err := telemetry.Configure(e) assert.Nil(t, err) assert.True(t, ok) + assert.Nil(t, condition) err = telemetry.Apply(e) assert.Nil(t, err) diff --git a/addons/threescale/3scale.go b/addons/threescale/3scale.go index a75fa460f1..2bab8ea219 100644 --- a/addons/threescale/3scale.go +++ b/addons/threescale/3scale.go @@ -87,14 +87,12 @@ func NewThreeScaleTrait() trait.Trait { } } -func (t *threeScaleTrait) Configure(e *trait.Environment) (bool, error) { +func (t *threeScaleTrait) Configure(e *trait.Environment) (bool, *trait.TraitCondition, error) { if e.Integration == nil || !pointer.BoolDeref(t.Enabled, false) { - // disabled by default - return false, nil + return false, nil, nil } - if !e.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil } if pointer.BoolDeref(t.Auto, true) { @@ -113,7 +111,7 @@ func (t *threeScaleTrait) Configure(e *trait.Environment) (bool, error) { } } - return true, nil + return true, nil, nil } func (t *threeScaleTrait) Apply(e *trait.Environment) error { diff --git a/addons/threescale/3scale_test.go b/addons/threescale/3scale_test.go index cfd8025143..d7a3a35c2c 100644 --- a/addons/threescale/3scale_test.go +++ b/addons/threescale/3scale_test.go @@ -39,9 +39,10 @@ func TestThreeScaleDisabled(t *testing.T) { } threeScale := NewThreeScaleTrait() - enabled, err := threeScale.Configure(e) + enabled, condition, err := threeScale.Configure(e) assert.Nil(t, err) assert.False(t, enabled) + assert.Nil(t, condition) } func TestThreeScaleInjection(t *testing.T) { @@ -49,9 +50,10 @@ func TestThreeScaleInjection(t *testing.T) { threeScale := NewThreeScaleTrait() tst, _ := threeScale.(*threeScaleTrait) tst.Enabled = pointer.Bool(true) - ok, err := threeScale.Configure(e) + ok, condition, err := threeScale.Configure(e) assert.Nil(t, err) assert.True(t, ok) + assert.Nil(t, condition) err = threeScale.Apply(e) assert.Nil(t, err) @@ -69,9 +71,10 @@ func TestThreeScaleInjectionNoAPIPath(t *testing.T) { tst, _ := threeScale.(*threeScaleTrait) tst.Enabled = pointer.Bool(true) tst.DescriptionPath = pointer.String("") - ok, err := threeScale.Configure(e) + ok, condition, err := threeScale.Configure(e) assert.Nil(t, err) assert.True(t, ok) + assert.Nil(t, condition) err = threeScale.Apply(e) assert.Nil(t, err) diff --git a/addons/tracing/tracing.go b/addons/tracing/tracing.go index caca01841e..0cf3852e09 100644 --- a/addons/tracing/tracing.go +++ b/addons/tracing/tracing.go @@ -86,9 +86,9 @@ func NewTracingTrait() trait.Trait { } } -func (t *tracingTrait) Configure(e *trait.Environment) (bool, error) { +func (t *tracingTrait) Configure(e *trait.Environment) (bool, *trait.TraitCondition, error) { if e.Integration == nil || !pointer.BoolDeref(t.Enabled, false) { - return false, nil + return false, nil, nil } if pointer.BoolDeref(t.Auto, true) { @@ -96,7 +96,7 @@ func (t *tracingTrait) Configure(e *trait.Environment) (bool, error) { for _, locator := range discovery.TracingLocators { endpoint, err := locator.FindEndpoint(e.Ctx, t.Client, t.L, e) if err != nil { - return false, err + return false, nil, err } if endpoint != "" { t.L.Infof("Using tracing endpoint: %s", endpoint) @@ -119,7 +119,7 @@ func (t *tracingTrait) Configure(e *trait.Environment) (bool, error) { } } - return true, nil + return true, nil, nil } func (t *tracingTrait) Apply(e *trait.Environment) error { diff --git a/addons/tracing/tracing_test.go b/addons/tracing/tracing_test.go index 0cdaef089f..25865c02be 100644 --- a/addons/tracing/tracing_test.go +++ b/addons/tracing/tracing_test.go @@ -36,9 +36,10 @@ func TestTracingTraitOnQuarkus(t *testing.T) { tt, _ := tracing.(*tracingTrait) tt.Enabled = pointer.Bool(true) tt.Endpoint = "http://endpoint3" - ok, err := tracing.Configure(e) + ok, condition, err := tracing.Configure(e) assert.Nil(t, err) assert.True(t, ok) + assert.Nil(t, condition) err = tracing.Apply(e) assert.Nil(t, err) diff --git a/addons/vault/aws/aws_secrets_manager.go b/addons/vault/aws/aws_secrets_manager.go index 81aa15c1b3..1a98e03241 100644 --- a/addons/vault/aws/aws_secrets_manager.go +++ b/addons/vault/aws/aws_secrets_manager.go @@ -82,13 +82,13 @@ func NewAwsSecretsManagerTrait() trait.Trait { } } -func (t *awsSecretsManagerTrait) Configure(environment *trait.Environment) (bool, error) { - if !pointer.BoolDeref(t.Enabled, false) { - return false, nil +func (t *awsSecretsManagerTrait) Configure(environment *trait.Environment) (bool, *trait.TraitCondition, error) { + if environment.Integration == nil || !pointer.BoolDeref(t.Enabled, false) { + return false, nil, nil } if !environment.IntegrationInPhase(v1.IntegrationPhaseInitialization) && !environment.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil } if t.UseDefaultCredentialsProvider == nil { @@ -101,7 +101,7 @@ func (t *awsSecretsManagerTrait) Configure(environment *trait.Environment) (bool t.RefreshEnabled = pointer.Bool(false) } - return true, nil + return true, nil, nil } func (t *awsSecretsManagerTrait) Apply(environment *trait.Environment) error { diff --git a/addons/vault/aws/aws_secrets_manager_test.go b/addons/vault/aws/aws_secrets_manager_test.go index aeb1e86f49..ce48f5ab34 100644 --- a/addons/vault/aws/aws_secrets_manager_test.go +++ b/addons/vault/aws/aws_secrets_manager_test.go @@ -43,9 +43,10 @@ func TestAwsSecretsManagerTraitApply(t *testing.T) { secrets.Region = "eu-west-1" secrets.AccessKey = "access-key" secrets.SecretKey = "secret-key" - ok, err := secrets.Configure(e) + ok, condition, err := secrets.Configure(e) assert.Nil(t, err) assert.True(t, ok) + assert.Nil(t, condition) err = secrets.Apply(e) assert.Nil(t, err) @@ -65,9 +66,10 @@ func TestAwsSecretsManagerTraitNoDefaultCreds(t *testing.T) { secrets.Region = "eu-west-1" secrets.AccessKey = "access-key" secrets.SecretKey = "secret-key" - ok, err := secrets.Configure(e) + ok, condition, err := secrets.Configure(e) assert.Nil(t, err) assert.True(t, ok) + assert.Nil(t, condition) err = secrets.Apply(e) assert.Nil(t, err) @@ -104,9 +106,10 @@ func TestAwsSecretsManagerTraitWithSecrets(t *testing.T) { secrets.Region = "eu-west-1" secrets.AccessKey = "secret:my-secret2/aws-access-key" secrets.SecretKey = "secret:my-secret1/aws-secret-key" - ok, err := secrets.Configure(e) + ok, condition, err := secrets.Configure(e) assert.Nil(t, err) assert.True(t, ok) + assert.Nil(t, condition) err = secrets.Apply(e) assert.Nil(t, err) @@ -143,9 +146,10 @@ func TestAwsSecretsManagerTraitWithConfigMap(t *testing.T) { secrets.Region = "eu-west-1" secrets.AccessKey = "configmap:my-configmap2/aws-access-key" secrets.SecretKey = "configmap:my-configmap1/aws-secret-key" - ok, err := secrets.Configure(e) + ok, condition, err := secrets.Configure(e) assert.Nil(t, err) assert.True(t, ok) + assert.Nil(t, condition) err = secrets.Apply(e) assert.Nil(t, err) diff --git a/addons/vault/azure/azure_key_vault.go b/addons/vault/azure/azure_key_vault.go index 5ab1ab0f66..04281fe04a 100644 --- a/addons/vault/azure/azure_key_vault.go +++ b/addons/vault/azure/azure_key_vault.go @@ -90,13 +90,13 @@ func NewAzureKeyVaultTrait() trait.Trait { } } -func (t *azureKeyVaultTrait) Configure(environment *trait.Environment) (bool, error) { - if !pointer.BoolDeref(t.Enabled, false) { - return false, nil +func (t *azureKeyVaultTrait) Configure(environment *trait.Environment) (bool, *trait.TraitCondition, error) { + if environment.Integration == nil || !pointer.BoolDeref(t.Enabled, false) { + return false, nil, nil } if !environment.IntegrationInPhase(v1.IntegrationPhaseInitialization) && !environment.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil } if t.ContextReloadEnabled == nil { @@ -107,7 +107,7 @@ func (t *azureKeyVaultTrait) Configure(environment *trait.Environment) (bool, er t.RefreshEnabled = pointer.Bool(false) } - return true, nil + return true, nil, nil } func (t *azureKeyVaultTrait) Apply(environment *trait.Environment) error { diff --git a/addons/vault/azure/azure_key_vault_test.go b/addons/vault/azure/azure_key_vault_test.go index 3886436059..7f3299ba40 100644 --- a/addons/vault/azure/azure_key_vault_test.go +++ b/addons/vault/azure/azure_key_vault_test.go @@ -43,9 +43,10 @@ func TestAzureKeyVaultTraitApply(t *testing.T) { secrets.ClientID = "client-id" secrets.ClientSecret = "secret" secrets.VaultName = "my-vault" - ok, err := secrets.Configure(e) + ok, condition, err := secrets.Configure(e) assert.Nil(t, err) assert.True(t, ok) + assert.Nil(t, condition) err = secrets.Apply(e) assert.Nil(t, err) @@ -85,9 +86,10 @@ func TestAzureKeyVaultTraitApplyWithConfigmapAndRefresh(t *testing.T) { secrets.BlobAccessKey = "configmap:my-configmap2/azure-storage-blob-key" secrets.BlobAccountName = "camel-k" secrets.BlobContainerName = "camel-k-container" - ok, err := secrets.Configure(e) + ok, condition, err := secrets.Configure(e) assert.Nil(t, err) assert.True(t, ok) + assert.Nil(t, condition) err = secrets.Apply(e) assert.Nil(t, err) @@ -131,9 +133,10 @@ func TestAzureKeyVaultTraitApplyWithSecretAndRefresh(t *testing.T) { secrets.BlobAccessKey = "secret:my-secret2/azure-storage-blob-key" secrets.BlobAccountName = "camel-k" secrets.BlobContainerName = "camel-k-container" - ok, err := secrets.Configure(e) + ok, condition, err := secrets.Configure(e) assert.Nil(t, err) assert.True(t, ok) + assert.Nil(t, condition) err = secrets.Apply(e) assert.Nil(t, err) diff --git a/addons/vault/gcp/gcp_secret_manager.go b/addons/vault/gcp/gcp_secret_manager.go index b3784d89ed..7f9810dbcc 100644 --- a/addons/vault/gcp/gcp_secret_manager.go +++ b/addons/vault/gcp/gcp_secret_manager.go @@ -75,13 +75,13 @@ func NewGcpSecretManagerTrait() trait.Trait { } } -func (t *gcpSecretManagerTrait) Configure(environment *trait.Environment) (bool, error) { - if !pointer.BoolDeref(t.Enabled, false) { - return false, nil +func (t *gcpSecretManagerTrait) Configure(environment *trait.Environment) (bool, *trait.TraitCondition, error) { + if environment.Integration == nil || !pointer.BoolDeref(t.Enabled, false) { + return false, nil, nil } if !environment.IntegrationInPhase(v1.IntegrationPhaseInitialization) && !environment.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil } if t.UseDefaultInstance == nil { @@ -96,7 +96,7 @@ func (t *gcpSecretManagerTrait) Configure(environment *trait.Environment) (bool, t.RefreshEnabled = pointer.Bool(false) } - return true, nil + return true, nil, nil } func (t *gcpSecretManagerTrait) Apply(environment *trait.Environment) error { diff --git a/addons/vault/gcp/gcp_secret_manager_test.go b/addons/vault/gcp/gcp_secret_manager_test.go index a7e88ad7c0..2d01c57cb0 100644 --- a/addons/vault/gcp/gcp_secret_manager_test.go +++ b/addons/vault/gcp/gcp_secret_manager_test.go @@ -38,9 +38,10 @@ func TestGcpSecretManagerTraitApply(t *testing.T) { secrets.UseDefaultInstance = pointer.Bool(false) secrets.ProjectID = "project-gcp" secrets.ServiceAccountKey = "file:////usr/local/serviceaccount.json" - ok, err := secrets.Configure(e) + ok, condition, err := secrets.Configure(e) assert.Nil(t, err) assert.True(t, ok) + assert.Nil(t, condition) err = secrets.Apply(e) assert.Nil(t, err) @@ -58,9 +59,10 @@ func TestGcpSecretManagerTraitNoDefaultCreds(t *testing.T) { secrets.UseDefaultInstance = pointer.Bool(false) secrets.ProjectID = "project-gcp" secrets.ServiceAccountKey = "file:////usr/local/serviceaccount.json" - ok, err := secrets.Configure(e) + ok, condition, err := secrets.Configure(e) assert.Nil(t, err) assert.True(t, ok) + assert.Nil(t, condition) err = secrets.Apply(e) assert.Nil(t, err) diff --git a/addons/vault/hashicorp/hashicorp_vault.go b/addons/vault/hashicorp/hashicorp_vault.go index 058b18cc99..616409a176 100644 --- a/addons/vault/hashicorp/hashicorp_vault.go +++ b/addons/vault/hashicorp/hashicorp_vault.go @@ -68,16 +68,16 @@ func NewHashicorpVaultTrait() trait.Trait { } } -func (t *hashicorpVaultTrait) Configure(environment *trait.Environment) (bool, error) { - if !pointer.BoolDeref(t.Enabled, false) { - return false, nil +func (t *hashicorpVaultTrait) Configure(environment *trait.Environment) (bool, *trait.TraitCondition, error) { + if environment.Integration == nil || !pointer.BoolDeref(t.Enabled, false) { + return false, nil, nil } if !environment.IntegrationInPhase(v1.IntegrationPhaseInitialization) && !environment.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil } - return true, nil + return true, nil, nil } func (t *hashicorpVaultTrait) Apply(environment *trait.Environment) error { diff --git a/addons/vault/hashicorp/hashicorp_vault_test.go b/addons/vault/hashicorp/hashicorp_vault_test.go index 86cb702297..975cc3be13 100644 --- a/addons/vault/hashicorp/hashicorp_vault_test.go +++ b/addons/vault/hashicorp/hashicorp_vault_test.go @@ -44,9 +44,10 @@ func TestHashicorpVaultTraitApply(t *testing.T) { secrets.Host = "localhost" secrets.Port = "9091" secrets.Scheme = "http" - ok, err := secrets.Configure(e) + ok, condition, err := secrets.Configure(e) assert.Nil(t, err) assert.True(t, ok) + assert.Nil(t, condition) err = secrets.Apply(e) assert.Nil(t, err) @@ -77,9 +78,10 @@ func TestHashicorpVaultTraitWithSecretApply(t *testing.T) { secrets.Host = "localhost" secrets.Port = "9091" secrets.Scheme = "http" - ok, err := secrets.Configure(e) + ok, condition, err := secrets.Configure(e) assert.Nil(t, err) assert.True(t, ok) + assert.Nil(t, condition) err = secrets.Apply(e) assert.Nil(t, err) @@ -110,9 +112,10 @@ func TestHashicorpVaultTraitWithConfigMapApply(t *testing.T) { secrets.Host = "localhost" secrets.Port = "9091" secrets.Scheme = "http" - ok, err := secrets.Configure(e) + ok, condition, err := secrets.Configure(e) assert.Nil(t, err) assert.True(t, ok) + assert.Nil(t, condition) err = secrets.Apply(e) assert.Nil(t, err) diff --git a/pkg/apis/camel/v1/integration_types.go b/pkg/apis/camel/v1/integration_types.go index 86cf7a1aea..78dd40a8cd 100644 --- a/pkg/apis/camel/v1/integration_types.go +++ b/pkg/apis/camel/v1/integration_types.go @@ -180,7 +180,8 @@ const ( IntegrationConditionProbesAvailable IntegrationConditionType = "ProbesAvailable" // IntegrationConditionReady --. IntegrationConditionReady IntegrationConditionType = "Ready" - + // IntegrationConditionTraitInfo --. + IntegrationConditionTraitInfo IntegrationConditionType = "TraitInfo" // IntegrationConditionKitAvailableReason --. IntegrationConditionKitAvailableReason string = "IntegrationKitAvailable" // IntegrationConditionPlatformAvailableReason --. diff --git a/pkg/apis/camel/v1/integrationkit_types.go b/pkg/apis/camel/v1/integrationkit_types.go index 2ddca51791..8ee233dee6 100644 --- a/pkg/apis/camel/v1/integrationkit_types.go +++ b/pkg/apis/camel/v1/integrationkit_types.go @@ -187,6 +187,8 @@ const ( IntegrationKitConditionCatalogAvailable IntegrationKitConditionType = "CamelCatalogAvailable" // IntegrationKitConditionPlatformAvailableReason --. IntegrationKitConditionPlatformAvailableReason string = "IntegrationPlatformAvailable" + // IntegrationKitConditionTraitInfo --. + IntegrationKitConditionTraitInfo IntegrationKitConditionType = "TraitInfo" ) // IntegrationKitCondition describes the state of a resource at a certain point. diff --git a/pkg/trait/affinity.go b/pkg/trait/affinity.go index 509d196c13..d3fad6cead 100644 --- a/pkg/trait/affinity.go +++ b/pkg/trait/affinity.go @@ -46,16 +46,16 @@ func newAffinityTrait() Trait { } } -func (t *affinityTrait) Configure(e *Environment) (bool, error) { +func (t *affinityTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if e.Integration == nil || !pointer.BoolDeref(t.Enabled, false) { - return false, nil + return false, nil, nil } if pointer.BoolDeref(t.PodAffinity, false) && pointer.BoolDeref(t.PodAntiAffinity, false) { - return false, fmt.Errorf("both pod affinity and pod anti-affinity can't be set simultaneously") + return false, nil, fmt.Errorf("both pod affinity and pod anti-affinity can't be set simultaneously") } - return e.IntegrationInRunningPhases(), nil + return e.IntegrationInRunningPhases(), nil, nil } func (t *affinityTrait) Apply(e *Environment) error { diff --git a/pkg/trait/affinity_test.go b/pkg/trait/affinity_test.go index 86eaa74d6a..35a27e9bcb 100644 --- a/pkg/trait/affinity_test.go +++ b/pkg/trait/affinity_test.go @@ -32,9 +32,10 @@ import ( func TestConfigureAffinityTraitDoesSucceed(t *testing.T) { affinityTrait := createNominalAffinityTest() environment, _ := createNominalDeploymentTraitTest() - configured, err := affinityTrait.Configure(environment) + configured, condition, err := affinityTrait.Configure(environment) assert.True(t, configured) + assert.Nil(t, condition) assert.Nil(t, err) } @@ -43,9 +44,10 @@ func TestConfigureAffinityTraitWithConflictingAffinitiesFails(t *testing.T) { environment, _ := createNominalDeploymentTraitTest() affinityTrait.PodAffinity = pointer.Bool(true) affinityTrait.PodAntiAffinity = pointer.Bool(true) - configured, err := affinityTrait.Configure(environment) + configured, condition, err := affinityTrait.Configure(environment) assert.False(t, configured) + assert.Nil(t, condition) assert.NotNil(t, err) } @@ -53,10 +55,11 @@ func TestConfigureDisabledAffinityTraitFails(t *testing.T) { affinityTrait := createNominalAffinityTest() affinityTrait.Enabled = pointer.Bool(false) environment, _ := createNominalDeploymentTraitTest() - configured, err := affinityTrait.Configure(environment) + configured, condition, err := affinityTrait.Configure(environment) assert.False(t, configured) assert.Nil(t, err) + assert.Nil(t, condition) } func TestApplyAffinityMissingDeployment(t *testing.T) { diff --git a/pkg/trait/builder.go b/pkg/trait/builder.go index 58d5790d55..64b3144cc2 100644 --- a/pkg/trait/builder.go +++ b/pkg/trait/builder.go @@ -60,12 +60,12 @@ func (t *builderTrait) InfluencesBuild(this, prev map[string]interface{}) bool { return true } -func (t *builderTrait) Configure(e *Environment) (bool, error) { +func (t *builderTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if e.IntegrationKit == nil { - return false, nil + return false, nil, nil } - t.adaptDeprecatedFields() + condition := t.adaptDeprecatedFields() if e.IntegrationKitInPhase(v1.IntegrationKitPhaseBuildSubmitted) { if trait := e.Catalog.GetTrait(quarkusTraitID); trait != nil { @@ -73,7 +73,7 @@ func (t *builderTrait) Configure(e *Environment) (bool, error) { isNativeIntegration := quarkus.isNativeIntegration(e) isNativeKit, err := quarkus.isNativeKit(e) if err != nil { - return false, err + return false, condition, err } if ok && (isNativeIntegration || isNativeKit) { // TODO expect maven repository in local repo (need to change builder pod accordingly!) @@ -86,7 +86,9 @@ func (t *builderTrait) Configure(e *Environment) (bool, error) { // it should be performed as the last custom task t.Tasks = append(t.Tasks, fmt.Sprintf(`quarkus-native;%s;/bin/bash -c "%s"`, nativeBuilderImage, command)) // Force the build to run in a separate Pod and strictly sequential - t.L.Info("This is a Quarkus native build: setting build configuration with build Pod strategy, and native container with 1 CPU core and 4 GiB memory. Make sure your cluster can handle it.") + m := "This is a Quarkus native build: setting build configuration with build Pod strategy, and native container with 1 CPU core and 4 GiB memory. Make sure your cluster can handle it." + t.L.Info(m) + condition = newOrAppend(condition, m) t.Strategy = string(v1.BuildStrategyPod) t.OrderStrategy = string(v1.BuildOrderStrategySequential) t.TasksRequestCPU = append(t.TasksRequestCPU, "quarkus-native:1000m") @@ -94,29 +96,53 @@ func (t *builderTrait) Configure(e *Environment) (bool, error) { } } - return true, nil + return true, condition, nil } - return false, nil + return false, condition, nil } -func (t *builderTrait) adaptDeprecatedFields() { +func (t *builderTrait) adaptDeprecatedFields() *TraitCondition { + var condition *TraitCondition if t.RequestCPU != "" { - t.L.Info("The request-cpu parameter is deprecated and may be removed in future releases. Make sure to use tasks-request-cpu parameter instead.") + m := "The request-cpu parameter is deprecated and may be removed in future releases. Make sure to use tasks-request-cpu parameter instead." + t.L.Info(m) + condition = newOrAppend(condition, m) t.TasksRequestCPU = append(t.TasksRequestCPU, fmt.Sprintf("builder:%s", t.RequestCPU)) } if t.LimitCPU != "" { - t.L.Info("The limit-cpu parameter is deprecated and may be removed in future releases. Make sure to use tasks-limit-cpu parameter instead.") + m := "The limit-cpu parameter is deprecated and may be removed in future releases. Make sure to use tasks-limit-cpu parameter instead." + t.L.Info(m) + condition = newOrAppend(condition, m) t.TasksLimitCPU = append(t.TasksLimitCPU, fmt.Sprintf("builder:%s", t.LimitCPU)) } if t.RequestMemory != "" { - t.L.Info("The request-memory parameter is deprecated and may be removed in future releases. Make sure to use tasks-request-memory parameter instead.") + m := "The request-memory parameter is deprecated and may be removed in future releases. Make sure to use tasks-request-memory parameter instead." + t.L.Info(m) + condition = newOrAppend(condition, m) t.TasksRequestMemory = append(t.TasksRequestMemory, fmt.Sprintf("builder:%s", t.RequestMemory)) } if t.LimitMemory != "" { - t.L.Info("The limit-memory parameter is deprecated and may be removed in future releases. Make sure to use tasks-limit-memory parameter instead.") + m := "The limit-memory parameter is deprecated and may be removed in future releases. Make sure to use tasks-limit-memory parameter instead." + t.L.Info(m) + if condition == nil { + condition = NewIntegrationCondition(v1.IntegrationConditionTraitInfo, corev1.ConditionTrue, traitConfigurationMessage, "") + } + condition = newOrAppend(condition, m) t.TasksLimitMemory = append(t.TasksLimitMemory, fmt.Sprintf("builder:%s", t.LimitMemory)) } + + return condition +} + +func newOrAppend(condition *TraitCondition, reason string) *TraitCondition { + if condition == nil { + condition = NewIntegrationCondition(v1.IntegrationConditionTraitInfo, corev1.ConditionTrue, traitConfigurationMessage, reason) + } else { + condition.reason += "; " + reason + } + + return condition } func (t *builderTrait) Apply(e *Environment) error { diff --git a/pkg/trait/builder_test.go b/pkg/trait/builder_test.go index 66a5eab2a5..21c7829d4b 100644 --- a/pkg/trait/builder_test.go +++ b/pkg/trait/builder_test.go @@ -44,9 +44,10 @@ func TestBuilderTraitNotAppliedBecauseOfNilKit(t *testing.T) { e.IntegrationKit = nil t.Run(string(e.Platform.Status.Cluster), func(t *testing.T) { - err := NewBuilderTestCatalog().apply(e) + conditions, err := NewBuilderTestCatalog().apply(e) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, e.ExecutedTraits) assert.Nil(t, e.GetTrait("builder")) assert.Empty(t, e.Pipeline) @@ -65,9 +66,10 @@ func TestBuilderTraitNotAppliedBecauseOfNilPhase(t *testing.T) { e.IntegrationKit.Status.Phase = v1.IntegrationKitPhaseInitialization t.Run(string(e.Platform.Status.Cluster), func(t *testing.T) { - err := NewBuilderTestCatalog().apply(e) + conditions, err := NewBuilderTestCatalog().apply(e) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, e.ExecutedTraits) assert.Nil(t, e.GetTrait("builder")) assert.Empty(t, e.Pipeline) @@ -77,9 +79,10 @@ func TestBuilderTraitNotAppliedBecauseOfNilPhase(t *testing.T) { func TestS2IBuilderTrait(t *testing.T) { env := createBuilderTestEnv(v1.IntegrationPlatformClusterOpenShift, v1.IntegrationPlatformBuildPublishStrategyS2I, v1.BuildStrategyRoutine) - err := NewBuilderTestCatalog().apply(env) + conditions, err := NewBuilderTestCatalog().apply(env) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, env.ExecutedTraits) assert.NotNil(t, env.GetTrait("builder")) assert.NotEmpty(t, env.Pipeline) @@ -91,9 +94,10 @@ func TestS2IBuilderTrait(t *testing.T) { func TestKanikoBuilderTrait(t *testing.T) { env := createBuilderTestEnv(v1.IntegrationPlatformClusterKubernetes, v1.IntegrationPlatformBuildPublishStrategyKaniko, v1.BuildStrategyRoutine) - err := NewBuilderTestCatalog().apply(env) + conditions, err := NewBuilderTestCatalog().apply(env) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, env.ExecutedTraits) assert.NotNil(t, env.GetTrait("builder")) assert.NotEmpty(t, env.Pipeline) @@ -420,10 +424,15 @@ func TestBuilderDeprecatedParams(t *testing.T) { builderTrait.LimitMemory = "100Mi" builderTrait.RequestMemory = "100Mi" - active, err := builderTrait.Configure(env) + active, condition, err := builderTrait.Configure(env) assert.Nil(t, err) assert.True(t, active) + assert.NotNil(t, condition) + assert.Contains(t, condition.reason, "The request-cpu parameter is deprecated and may be removed in future releases") + assert.Contains(t, condition.reason, "The limit-cpu parameter is deprecated and may be removed in future releases") + assert.Contains(t, condition.reason, "The request-memory parameter is deprecated and may be removed in future releases") + assert.Contains(t, condition.reason, "The limit-memory parameter is deprecated and may be removed in future releases") assert.Len(t, builderTrait.TasksLimitCPU, 1) assert.Len(t, builderTrait.TasksRequestCPU, 1) assert.Len(t, builderTrait.TasksLimitMemory, 1) diff --git a/pkg/trait/camel.go b/pkg/trait/camel.go index 36e3d74b49..9842ea934f 100644 --- a/pkg/trait/camel.go +++ b/pkg/trait/camel.go @@ -59,12 +59,12 @@ func (t *camelTrait) InfluencesBuild(this, prev map[string]interface{}) bool { return this["runtimeVersion"] != prev["runtimeVersion"] } -func (t *camelTrait) Configure(e *Environment) (bool, error) { +func (t *camelTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if t.RuntimeVersion == "" { t.RuntimeVersion = determineRuntimeVersion(e) } - return true, nil + return true, nil, nil } func (t *camelTrait) Apply(e *Environment) error { diff --git a/pkg/trait/camel_test.go b/pkg/trait/camel_test.go index 692c5df214..e300291da4 100644 --- a/pkg/trait/camel_test.go +++ b/pkg/trait/camel_test.go @@ -35,16 +35,18 @@ import ( func TestConfigureEnabledCamelTraitSucceeds(t *testing.T) { trait, environment := createNominalCamelTest(false) - configured, err := trait.Configure(environment) + configured, condition, err := trait.Configure(environment) assert.Nil(t, err) + assert.Nil(t, condition) assert.True(t, configured) } func TestApplyCamelTraitSucceeds(t *testing.T) { trait, environment := createNominalCamelTest(false) - configured, err := trait.Configure(environment) + configured, condition, err := trait.Configure(environment) assert.Nil(t, err) + assert.Nil(t, condition) assert.True(t, configured) err = trait.Apply(environment) @@ -65,8 +67,9 @@ func TestApplyCamelTraitWithoutEnvironmentCatalogAndUnmatchableVersionFails(t *t environment.Integration.Status.RuntimeVersion = "Unmatchable version" environment.Integration.Status.RuntimeProvider = v1.RuntimeProviderQuarkus - configured, err := trait.Configure(environment) + configured, condition, err := trait.Configure(environment) assert.Nil(t, err) + assert.Nil(t, condition) assert.True(t, configured) err = trait.Apply(environment) @@ -167,8 +170,9 @@ func TestApplyCamelTraitWithProperties(t *testing.T) { trait, environment := createNominalCamelTest(false) trait.Properties = []string{"a=b", "c=d"} - configured, err := trait.Configure(environment) + configured, condition, err := trait.Configure(environment) assert.Nil(t, err) + assert.Nil(t, condition) assert.True(t, configured) err = trait.Apply(environment) @@ -186,8 +190,9 @@ func TestApplyCamelTraitWithProperties(t *testing.T) { func TestApplyCamelTraitWithSources(t *testing.T) { trait, environment := createNominalCamelTest(true) - configured, err := trait.Configure(environment) + configured, condition, err := trait.Configure(environment) assert.Nil(t, err) + assert.Nil(t, condition) assert.True(t, configured) err = trait.Apply(environment) diff --git a/pkg/trait/container.go b/pkg/trait/container.go index 62b905eb03..6f6bb462cd 100644 --- a/pkg/trait/container.go +++ b/pkg/trait/container.go @@ -67,33 +67,32 @@ func newContainerTrait() Trait { } } -func (t *containerTrait) Configure(e *Environment) (bool, error) { +func (t *containerTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if e.Integration == nil { - return false, nil + return false, nil, nil } if !e.IntegrationInPhase(v1.IntegrationPhaseInitialization) && !e.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil } knativeInstalled, _ := knative.IsInstalled(e.Client) if e.IntegrationInPhase(v1.IntegrationPhaseInitialization) && !knativeInstalled { hasKnativeEndpoint, err := containsEndpoint("knative", e, t.Client) if err != nil { - return false, err + return false, nil, err } if hasKnativeEndpoint { // fail fast the integration as there is no knative installed in the cluster t.L.ForIntegration(e.Integration).Infof("Integration %s/%s contains knative endpoint that cannot run, as knative is not installed in the cluster.", e.Integration.Namespace, e.Integration.Name) err := errors.New("integration cannot run, as knative is not installed in the cluster") - e.Integration.Status.SetCondition( + return false, NewIntegrationCondition( v1.IntegrationConditionKnativeAvailable, corev1.ConditionFalse, v1.IntegrationConditionKnativeNotInstalledReason, - err.Error()) - e.Integration.Status.Phase = v1.IntegrationPhaseError - return false, err + err.Error(), + ), err } } @@ -105,10 +104,10 @@ func (t *containerTrait) Configure(e *Environment) (bool, error) { } if !isValidPullPolicy(t.ImagePullPolicy) { - return false, fmt.Errorf("unsupported pull policy %s", t.ImagePullPolicy) + return false, nil, fmt.Errorf("unsupported pull policy %s", t.ImagePullPolicy) } - return true, nil + return true, nil, nil } func isValidPullPolicy(policy corev1.PullPolicy) bool { diff --git a/pkg/trait/container_probes_test.go b/pkg/trait/container_probes_test.go index f7d58a4f0e..772dfd46c8 100644 --- a/pkg/trait/container_probes_test.go +++ b/pkg/trait/container_probes_test.go @@ -78,9 +78,9 @@ func TestProbesDependencies(t *testing.T) { env := newTestProbesEnv(t, integration) env.Integration.Status.Phase = v1.IntegrationPhaseInitialization - err := env.Catalog.apply(&env) + conditions, err := env.Catalog.apply(&env) assert.Nil(t, err) - + assert.Empty(t, conditions) assert.Contains(t, env.Integration.Status.Dependencies, "mvn:org.apache.camel.quarkus:camel-quarkus-microprofile-health") } @@ -103,8 +103,9 @@ func TestProbesOnDeployment(t *testing.T) { env := newTestProbesEnv(t, integration) env.Integration.Status.Phase = v1.IntegrationPhaseDeploying - err := env.Catalog.apply(&env) + conditions, err := env.Catalog.apply(&env) assert.Nil(t, err) + assert.Empty(t, conditions) container := env.GetIntegrationContainer() @@ -140,8 +141,9 @@ func TestProbesOnDeploymentWithCustomScheme(t *testing.T) { env := newTestProbesEnv(t, integration) env.Integration.Status.Phase = v1.IntegrationPhaseDeploying - err := env.Catalog.apply(&env) + conditions, err := env.Catalog.apply(&env) assert.Nil(t, err) + assert.Empty(t, conditions) container := env.GetIntegrationContainer() @@ -181,8 +183,24 @@ func TestProbesOnKnativeService(t *testing.T) { env := newTestProbesEnv(t, integration) env.Integration.Status.Phase = v1.IntegrationPhaseDeploying - err := env.Catalog.apply(&env) + serviceOverrideCondition := NewIntegrationCondition( + v1.IntegrationConditionTraitInfo, + corev1.ConditionTrue, + "service trait configuration", + "explicitly disabled by the platform: knative-service trait has priority over this trait", + ) + ctrlStrategyCondition := NewIntegrationCondition( + v1.IntegrationConditionDeploymentAvailable, + corev1.ConditionFalse, + "deployment trait configuration", + "controller strategy: knative-service", + ) + + conditions, err := env.Catalog.apply(&env) assert.Nil(t, err) + assert.Len(t, conditions, 2) + assert.Contains(t, conditions, ctrlStrategyCondition) + assert.Contains(t, conditions, serviceOverrideCondition) container := env.GetIntegrationContainer() diff --git a/pkg/trait/container_test.go b/pkg/trait/container_test.go index 78abb84cd8..c483eedaa3 100644 --- a/pkg/trait/container_test.go +++ b/pkg/trait/container_test.go @@ -84,9 +84,10 @@ func TestContainerWithDefaults(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - err = traitCatalog.apply(&environment) + conditions, err := traitCatalog.apply(&environment) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("deployment")) assert.NotNil(t, environment.GetTrait("container")) @@ -150,9 +151,10 @@ func TestContainerWithCustomName(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - err = traitCatalog.apply(&environment) + conditions, err := traitCatalog.apply(&environment) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("deployment")) assert.NotNil(t, environment.GetTrait("container")) @@ -215,8 +217,10 @@ func TestContainerWithCustomImage(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - err = traitCatalog.apply(&environment) + conditions, err := traitCatalog.apply(&environment) + assert.Nil(t, err) + assert.Empty(t, conditions) for _, postAction := range environment.PostActions { assert.Nil(t, postAction(&environment)) @@ -294,8 +298,9 @@ func TestContainerWithCustomImageAndIntegrationKit(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - err = traitCatalog.apply(&environment) + conditions, err := traitCatalog.apply(&environment) assert.NotNil(t, err) + assert.Empty(t, conditions) assert.Contains(t, err.Error(), "unsupported configuration: a container image has been set in conjunction with an IntegrationKit") } @@ -336,8 +341,10 @@ func TestContainerWithImagePullPolicy(t *testing.T) { environment.Integration.Status.Phase = v1.IntegrationPhaseDeploying environment.Platform.ResyncStatusFullConfig() - err = traitCatalog.apply(&environment) + conditions, err := traitCatalog.apply(&environment) + assert.Nil(t, err) + assert.Empty(t, conditions) container := environment.GetIntegrationContainer() @@ -345,11 +352,8 @@ func TestContainerWithImagePullPolicy(t *testing.T) { } func TestRunKnativeEndpointWithKnativeNotInstalled(t *testing.T) { - environment := createEnvironment() - trait, _ := newContainerTrait().(*containerTrait) - environment.Integration.Spec.Sources = []v1.SourceSpec{ { DataSpec: v1.DataSpec{ @@ -361,12 +365,16 @@ func TestRunKnativeEndpointWithKnativeNotInstalled(t *testing.T) { Language: v1.LanguageJavaSource, }, } - configured, err := trait.Configure(environment) + expectedCondition := NewIntegrationCondition( + v1.IntegrationConditionKnativeAvailable, + corev1.ConditionFalse, + v1.IntegrationConditionKnativeNotInstalledReason, + "integration cannot run, as knative is not installed in the cluster", + ) + configured, condition, err := trait.Configure(environment) assert.NotNil(t, err) + assert.Equal(t, expectedCondition, condition) assert.False(t, configured) - conditions := environment.Integration.Status.Conditions - assert.Len(t, conditions, 1) - assert.Equal(t, v1.IntegrationConditionKnativeNotInstalledReason, conditions[0].Reason) } func TestRunNonKnativeEndpointWithKnativeNotInstalled(t *testing.T) { @@ -385,8 +393,9 @@ func TestRunNonKnativeEndpointWithKnativeNotInstalled(t *testing.T) { }, } - configured, err := trait.Configure(environment) + configured, condition, err := trait.Configure(environment) assert.Nil(t, err) + assert.Nil(t, condition) assert.True(t, configured) conditions := environment.Integration.Status.Conditions assert.Len(t, conditions, 0) diff --git a/pkg/trait/cron.go b/pkg/trait/cron.go index a5926c50a1..0a7a69cb0f 100644 --- a/pkg/trait/cron.go +++ b/pkg/trait/cron.go @@ -73,44 +73,35 @@ func newCronTrait() Trait { } } -func (t *cronTrait) Configure(e *Environment) (bool, error) { - if e.Integration == nil || !pointer.BoolDeref(t.Enabled, true) { - if e.Integration != nil { - e.Integration.Status.SetCondition( - v1.IntegrationConditionCronJobAvailable, - corev1.ConditionFalse, - v1.IntegrationConditionCronJobNotAvailableReason, - "explicitly disabled", - ) - } - - return false, nil +func (t *cronTrait) Configure(e *Environment) (bool, *TraitCondition, error) { + if e.Integration == nil { + return false, nil, nil + } + if !pointer.BoolDeref(t.Enabled, true) { + return false, NewIntegrationConditionUserDisabled(), nil } - if !e.IntegrationInPhase(v1.IntegrationPhaseInitialization) && !e.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil } if _, ok := e.CamelCatalog.Runtime.Capabilities[v1.CapabilityCron]; !ok { - e.Integration.Status.SetCondition( + return false, NewIntegrationCondition( v1.IntegrationConditionCronJobAvailable, corev1.ConditionFalse, v1.IntegrationConditionCronJobNotAvailableReason, "the runtime provider %s does not declare 'cron' capability", - ) - - return false, nil + ), nil } if pointer.BoolDeref(t.Auto, true) { globalCron, err := t.getGlobalCron(e) if err != nil { - e.Integration.Status.SetErrorCondition( + return false, NewIntegrationCondition( v1.IntegrationConditionCronJobAvailable, + corev1.ConditionFalse, v1.IntegrationConditionCronJobNotAvailableReason, - err, - ) - return false, err + err.Error(), + ), err } if t.Schedule == "" && globalCron != nil { @@ -133,7 +124,7 @@ func (t *cronTrait) Configure(e *Environment) (bool, error) { // If there's at least a `cron` endpoint, add a fallback implementation fromURIs, err := t.getSourcesFromURIs(e) if err != nil { - return false, err + return false, nil, err } for _, fromURI := range fromURIs { if uri.GetComponent(fromURI) == genericCronComponent { @@ -146,40 +137,33 @@ func (t *cronTrait) Configure(e *Environment) (bool, error) { // Fallback strategy can be implemented in any other controller if pointer.BoolDeref(t.Fallback, false) { + var condition *TraitCondition if e.IntegrationInPhase(v1.IntegrationPhaseDeploying) { - e.Integration.Status.SetCondition( + condition = NewIntegrationCondition( v1.IntegrationConditionCronJobAvailable, corev1.ConditionFalse, v1.IntegrationConditionCronJobNotAvailableReason, "fallback strategy selected", ) } - return true, nil + return true, condition, nil } // CronJob strategy requires common schedule strategy, err := e.DetermineControllerStrategy() if err != nil { - e.Integration.Status.SetErrorCondition( + return false, NewIntegrationCondition( v1.IntegrationConditionCronJobAvailable, + corev1.ConditionFalse, v1.IntegrationConditionCronJobNotAvailableReason, - err, - ) - return false, err + err.Error(), + ), err } if strategy != ControllerStrategyCronJob { - if e.IntegrationInPhase(v1.IntegrationPhaseDeploying) { - e.Integration.Status.SetCondition( - v1.IntegrationConditionCronJobAvailable, - corev1.ConditionFalse, - v1.IntegrationConditionCronJobNotAvailableReason, - fmt.Sprintf("different controller strategy used (%s)", string(strategy)), - ) - } - return false, nil + return false, nil, nil } - return t.Schedule != "", nil + return t.Schedule != "", nil, nil } func (t *cronTrait) Apply(e *Environment) error { @@ -280,9 +264,6 @@ func (t *cronTrait) getCronJobFor(e *Environment) *batchv1.CronJob { // SelectControllerStrategy can be used to check if a CronJob can be generated given the integration and trait settings. func (t *cronTrait) SelectControllerStrategy(e *Environment) (*ControllerStrategy, error) { cronStrategy := ControllerStrategyCronJob - if !pointer.BoolDeref(t.Enabled, true) { - return nil, nil - } if pointer.BoolDeref(t.Fallback, false) { return nil, nil } diff --git a/pkg/trait/cron_test.go b/pkg/trait/cron_test.go index dc00b2032c..78964e527f 100644 --- a/pkg/trait/cron_test.go +++ b/pkg/trait/cron_test.go @@ -284,10 +284,10 @@ func TestCronDeps(t *testing.T) { assert.Nil(t, err) tc := NewCatalog(c) - - err = tc.apply(&environment) + conditions, err := tc.apply(&environment) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) ct, _ := environment.GetTrait("cron").(*cronTrait) @@ -364,9 +364,10 @@ func TestCronDepsFallback(t *testing.T) { tc := NewCatalog(c) - err = tc.apply(&environment) + conditions, err := tc.apply(&environment) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) ct, _ := environment.GetTrait("cron").(*cronTrait) @@ -397,7 +398,6 @@ func TestCronWithActiveDeadline(t *testing.T) { Phase: v1.IntegrationPhaseDeploying, }, Spec: v1.IntegrationSpec{ - Profile: v1.TraitProfileKnative, Sources: []v1.SourceSpec{ { DataSpec: v1.DataSpec{ @@ -440,9 +440,16 @@ func TestCronWithActiveDeadline(t *testing.T) { tc := NewCatalog(c) - err = tc.apply(&environment) - + expectedCondition := NewIntegrationCondition( + v1.IntegrationConditionDeploymentAvailable, + corev1.ConditionFalse, + "deployment trait configuration", + "controller strategy: cron-job", + ) + conditions, err := tc.apply(&environment) assert.Nil(t, err) + assert.Len(t, conditions, 1) + assert.Contains(t, conditions, expectedCondition) assert.NotEmpty(t, environment.ExecutedTraits) ct, _ := environment.GetTrait("cron").(*cronTrait) @@ -480,7 +487,6 @@ func TestCronWithBackoffLimit(t *testing.T) { Phase: v1.IntegrationPhaseDeploying, }, Spec: v1.IntegrationSpec{ - Profile: v1.TraitProfileKnative, Sources: []v1.SourceSpec{ { DataSpec: v1.DataSpec{ @@ -523,9 +529,16 @@ func TestCronWithBackoffLimit(t *testing.T) { tc := NewCatalog(c) - err = tc.apply(&environment) - + expectedCondition := NewIntegrationCondition( + v1.IntegrationConditionDeploymentAvailable, + corev1.ConditionFalse, + "deployment trait configuration", + "controller strategy: cron-job", + ) + conditions, err := tc.apply(&environment) assert.Nil(t, err) + assert.Len(t, conditions, 1) + assert.Contains(t, conditions, expectedCondition) assert.NotEmpty(t, environment.ExecutedTraits) ct, _ := environment.GetTrait("cron").(*cronTrait) diff --git a/pkg/trait/dependencies.go b/pkg/trait/dependencies.go index 3aefec8bdd..e8d6e5d0f7 100644 --- a/pkg/trait/dependencies.go +++ b/pkg/trait/dependencies.go @@ -38,12 +38,12 @@ func newDependenciesTrait() Trait { } } -func (t *dependenciesTrait) Configure(e *Environment) (bool, error) { +func (t *dependenciesTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if e.Integration == nil { - return false, nil + return false, nil, nil } - return e.IntegrationInPhase(v1.IntegrationPhaseInitialization), nil + return e.IntegrationInPhase(v1.IntegrationPhaseInitialization), nil, nil } func (t *dependenciesTrait) Apply(e *Environment) error { diff --git a/pkg/trait/dependencies_test.go b/pkg/trait/dependencies_test.go index 35ba5ea167..1f39204532 100644 --- a/pkg/trait/dependencies_test.go +++ b/pkg/trait/dependencies_test.go @@ -33,18 +33,21 @@ func TestDependenciesTraitApplicability(t *testing.T) { } trait := newDependenciesTrait() - enabled, err := trait.Configure(e) + enabled, condition, err := trait.Configure(e) assert.Nil(t, err) + assert.Nil(t, condition) assert.False(t, enabled) e.Integration.Status.Phase = v1.IntegrationPhaseNone - enabled, err = trait.Configure(e) + enabled, condition, err = trait.Configure(e) assert.Nil(t, err) + assert.Nil(t, condition) assert.False(t, enabled) e.Integration.Status.Phase = v1.IntegrationPhaseInitialization - enabled, err = trait.Configure(e) + enabled, condition, err = trait.Configure(e) assert.Nil(t, err) + assert.Nil(t, condition) assert.True(t, enabled) } @@ -74,8 +77,9 @@ func TestIntegrationDefaultDeps(t *testing.T) { } trait := newDependenciesTrait() - enabled, err := trait.Configure(e) + enabled, condition, err := trait.Configure(e) assert.Nil(t, err) + assert.Nil(t, condition) assert.True(t, enabled) err = trait.Apply(e) @@ -122,8 +126,9 @@ func TestIntegrationCustomDeps(t *testing.T) { } trait := newDependenciesTrait() - enabled, err := trait.Configure(e) + enabled, condition, err := trait.Configure(e) assert.Nil(t, err) + assert.Nil(t, condition) assert.True(t, enabled) err = trait.Apply(e) @@ -176,8 +181,9 @@ func TestIntegrationAutoGeneratedDeps(t *testing.T) { } for _, trait := range []Trait{NewInitTrait(), newDependenciesTrait()} { - enabled, err := trait.Configure(e) + enabled, condition, err := trait.Configure(e) assert.Nil(t, err) + assert.Nil(t, condition) assert.True(t, enabled) assert.Nil(t, trait.Apply(e)) } @@ -231,8 +237,9 @@ func TestIntegrationCustomLoader(t *testing.T) { } trait := newDependenciesTrait() - enabled, err := trait.Configure(e) + enabled, condition, err := trait.Configure(e) assert.Nil(t, err) + assert.Nil(t, condition) assert.True(t, enabled) err = trait.Apply(e) @@ -274,8 +281,9 @@ func TestRestDeps(t *testing.T) { } trait := newDependenciesTrait() - enabled, err := trait.Configure(e) + enabled, condition, err := trait.Configure(e) assert.Nil(t, err) + assert.Nil(t, condition) assert.True(t, enabled) err = trait.Apply(e) @@ -316,8 +324,9 @@ func TestRestDepsQuarkus(t *testing.T) { } trait := newDependenciesTrait() - enabled, err := trait.Configure(e) + enabled, condition, err := trait.Configure(e) assert.Nil(t, err) + assert.Nil(t, condition) assert.True(t, enabled) err = trait.Apply(e) diff --git a/pkg/trait/deployer.go b/pkg/trait/deployer.go index 6ec537b8d5..6cab160782 100644 --- a/pkg/trait/deployer.go +++ b/pkg/trait/deployer.go @@ -50,8 +50,8 @@ func newDeployerTrait() Trait { } } -func (t *deployerTrait) Configure(e *Environment) (bool, error) { - return e.Integration != nil, nil +func (t *deployerTrait) Configure(e *Environment) (bool, *TraitCondition, error) { + return e.Integration != nil, nil, nil } func (t *deployerTrait) Apply(e *Environment) error { diff --git a/pkg/trait/deployer_test.go b/pkg/trait/deployer_test.go index 91b05f7f56..5809e16ecc 100644 --- a/pkg/trait/deployer_test.go +++ b/pkg/trait/deployer_test.go @@ -29,20 +29,20 @@ import ( func TestConfigureDeployerTraitDoesSucceed(t *testing.T) { deployerTrait, environment := createNominalDeployerTest() - configured, err := deployerTrait.Configure(environment) - + configured, condition, err := deployerTrait.Configure(environment) assert.True(t, configured) assert.Nil(t, err) + assert.Nil(t, condition) } func TestConfigureDeployerTraitInWrongPhaseDoesNotSucceed(t *testing.T) { deployerTrait, environment := createNominalDeployerTest() environment.Integration.Status.Phase = v1.IntegrationPhaseError - configured, err := deployerTrait.Configure(environment) - + configured, condition, err := deployerTrait.Configure(environment) assert.True(t, configured) assert.Nil(t, err) + assert.Nil(t, condition) } func TestApplyDeployerTraitDoesSucceed(t *testing.T) { diff --git a/pkg/trait/deployment.go b/pkg/trait/deployment.go index 9424f46054..c11fff7fb8 100644 --- a/pkg/trait/deployment.go +++ b/pkg/trait/deployment.go @@ -43,39 +43,37 @@ func newDeploymentTrait() Trait { } } -func (t *deploymentTrait) Configure(e *Environment) (bool, error) { +func (t *deploymentTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if !e.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil } if e.IntegrationInPhase(v1.IntegrationPhaseRunning, v1.IntegrationPhaseError) { condition := e.Integration.Status.GetCondition(v1.IntegrationConditionDeploymentAvailable) - return condition != nil && condition.Status == corev1.ConditionTrue, nil + return condition != nil && condition.Status == corev1.ConditionTrue, nil, nil } // Don't deploy when a different strategy is needed (e.g. Knative, Cron) strategy, err := e.DetermineControllerStrategy() if err != nil { - e.Integration.Status.SetErrorCondition( + return false, NewIntegrationCondition( v1.IntegrationConditionDeploymentAvailable, + corev1.ConditionFalse, v1.IntegrationConditionDeploymentAvailableReason, - err, - ) - - return false, err + err.Error(), + ), err } if strategy != ControllerStrategyDeployment { - e.Integration.Status.SetCondition( + return false, NewIntegrationCondition( v1.IntegrationConditionDeploymentAvailable, corev1.ConditionFalse, v1.IntegrationConditionDeploymentAvailableReason, "controller strategy: "+string(strategy), - ) - return false, nil + ), nil } - return e.IntegrationInPhase(v1.IntegrationPhaseDeploying), nil + return e.IntegrationInPhase(v1.IntegrationPhaseDeploying), nil, nil } func (t *deploymentTrait) SelectControllerStrategy(e *Environment) (*ControllerStrategy, error) { diff --git a/pkg/trait/deployment_test.go b/pkg/trait/deployment_test.go index 018db540d2..9539cddecd 100644 --- a/pkg/trait/deployment_test.go +++ b/pkg/trait/deployment_test.go @@ -42,29 +42,29 @@ func TestConfigureDeploymentTraitWhileIntegrationIsRunningDoesSucceed(t *testing ) environment.Integration.Status.Phase = v1.IntegrationPhaseRunning - configured, err := deploymentTrait.Configure(environment) - - assert.Nil(t, err) + configured, condition, err := deploymentTrait.Configure(environment) assert.True(t, configured) + assert.Nil(t, err) + assert.Nil(t, condition) } func TestConfigureDeploymentTraitDoesSucceed(t *testing.T) { deploymentTrait, environment := createNominalDeploymentTest() - configured, err := deploymentTrait.Configure(environment) - - assert.Nil(t, err) + configured, condition, err := deploymentTrait.Configure(environment) assert.True(t, configured) + assert.Nil(t, err) + assert.Nil(t, condition) } func TestConfigureDeploymentTraitWhileBuildingKitDoesNotSucceed(t *testing.T) { deploymentTrait, environment := createNominalDeploymentTest() environment.Integration.Status.Phase = v1.IntegrationPhaseBuildingKit - configured, err := deploymentTrait.Configure(environment) - - assert.Nil(t, err) + configured, condition, err := deploymentTrait.Configure(environment) assert.False(t, configured) + assert.Nil(t, err) + assert.Nil(t, condition) } func TestConfigureDeploymentTraitWhileWaitingPlatformDoesNotSucceed(t *testing.T) { @@ -72,20 +72,20 @@ func TestConfigureDeploymentTraitWhileWaitingPlatformDoesNotSucceed(t *testing.T environment.Integration.Status.Phase = v1.IntegrationPhaseBuildingKit environment.IntegrationKit.Status.Phase = v1.IntegrationKitPhaseWaitingForPlatform - configured, err := deploymentTrait.Configure(environment) - - assert.Nil(t, err) + configured, condition, err := deploymentTrait.Configure(environment) assert.False(t, configured) + assert.Nil(t, err) + assert.Nil(t, condition) } func TestApplyDeploymentTraitWhileResolvingKitDoesNotSucceed(t *testing.T) { deploymentTrait, environment := createNominalDeploymentTest() environment.Integration.Status.Phase = v1.IntegrationPhaseBuildingKit - configured, err := deploymentTrait.Configure(environment) - - assert.Nil(t, err) + configured, condition, err := deploymentTrait.Configure(environment) assert.False(t, configured) + assert.Nil(t, err) + assert.Nil(t, condition) } func TestApplyDeploymentTraitWhileDeployingIntegrationDoesSucceed(t *testing.T) { diff --git a/pkg/trait/environment.go b/pkg/trait/environment.go index 4569c38a49..760f6e34eb 100644 --- a/pkg/trait/environment.go +++ b/pkg/trait/environment.go @@ -61,12 +61,12 @@ func newEnvironmentTrait() Trait { } } -func (t *environmentTrait) Configure(e *Environment) (bool, error) { +func (t *environmentTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if e.Integration == nil { - return false, nil + return false, nil, nil } - return e.IntegrationInRunningPhases(), nil + return e.IntegrationInRunningPhases(), nil, nil } func (t *environmentTrait) Apply(e *Environment) error { diff --git a/pkg/trait/environment_test.go b/pkg/trait/environment_test.go index 75f2d421d9..63f72e38fc 100644 --- a/pkg/trait/environment_test.go +++ b/pkg/trait/environment_test.go @@ -41,9 +41,9 @@ func TestDefaultEnvironment(t *testing.T) { env := mockEnvironment(catalog) env.Platform.ResyncStatusFullConfig() - err = NewEnvironmentTestCatalog().apply(&env) - + conditions, err := NewEnvironmentTestCatalog().apply(&env) assert.Nil(t, err) + assert.Empty(t, conditions) ns := false name := false @@ -90,9 +90,9 @@ func TestEnabledContainerMetaDataEnvVars(t *testing.T) { } env.Platform.ResyncStatusFullConfig() - err = NewEnvironmentTestCatalog().apply(&env) - + conditions, err := NewEnvironmentTestCatalog().apply(&env) assert.Nil(t, err) + assert.Empty(t, conditions) ns := false name := false @@ -130,9 +130,9 @@ func TestDisabledContainerMetaDataEnvVars(t *testing.T) { env.Platform.ResyncStatusFullConfig() - err = NewEnvironmentTestCatalog().apply(&env) - + conditions, err := NewEnvironmentTestCatalog().apply(&env) assert.Nil(t, err) + assert.Empty(t, conditions) ns := false name := false @@ -169,9 +169,9 @@ func TestCustomEnvVars(t *testing.T) { } env.Platform.ResyncStatusFullConfig() - err = NewEnvironmentTestCatalog().apply(&env) - + conditions, err := NewEnvironmentTestCatalog().apply(&env) assert.Nil(t, err) + assert.Empty(t, conditions) userK1 := false userK2 := false diff --git a/pkg/trait/error_handler.go b/pkg/trait/error_handler.go index 3da5efff1c..116dd3883b 100644 --- a/pkg/trait/error_handler.go +++ b/pkg/trait/error_handler.go @@ -41,20 +41,20 @@ func newErrorHandlerTrait() Trait { } } -func (t *errorHandlerTrait) Configure(e *Environment) (bool, error) { +func (t *errorHandlerTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if e.Integration == nil { - return false, nil + return false, nil, nil } if !e.IntegrationInPhase(v1.IntegrationPhaseInitialization) && !e.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil } if t.ErrorHandlerRef == "" { t.ErrorHandlerRef = e.Integration.Spec.GetConfigurationProperty(v1.ErrorHandlerRefName) } - return t.ErrorHandlerRef != "", nil + return t.ErrorHandlerRef != "", nil, nil } func (t *errorHandlerTrait) Apply(e *Environment) error { diff --git a/pkg/trait/error_handler_test.go b/pkg/trait/error_handler_test.go index d18fce211a..6c93206825 100644 --- a/pkg/trait/error_handler_test.go +++ b/pkg/trait/error_handler_test.go @@ -36,19 +36,23 @@ func TestErrorHandlerConfigureFromIntegrationProperty(t *testing.T) { e.Integration.Spec.AddConfiguration("property", fmt.Sprintf("%v = %s", v1.ErrorHandlerRefName, "defaultErrorHandler")) trait := newErrorHandlerTrait() - enabled, err := trait.Configure(e) + enabled, condition, err := trait.Configure(e) assert.Nil(t, err) assert.False(t, enabled) + assert.Nil(t, condition) e.Integration.Status.Phase = v1.IntegrationPhaseNone - enabled, err = trait.Configure(e) + enabled, condition, err = trait.Configure(e) assert.Nil(t, err) assert.False(t, enabled) + assert.Nil(t, condition) e.Integration.Status.Phase = v1.IntegrationPhaseInitialization - enabled, err = trait.Configure(e) + enabled, condition, err = trait.Configure(e) assert.Nil(t, err) assert.True(t, enabled) + assert.Nil(t, condition) + } func TestErrorHandlerApplySource(t *testing.T) { @@ -60,9 +64,11 @@ func TestErrorHandlerApplySource(t *testing.T) { e.Integration.Status.Phase = v1.IntegrationPhaseInitialization trait := newErrorHandlerTrait() - enabled, err := trait.Configure(e) + enabled, condition, err := trait.Configure(e) assert.Nil(t, err) assert.True(t, enabled) + assert.Nil(t, condition) + err = trait.Apply(e) assert.Nil(t, err) assert.Equal(t, `- error-handler: @@ -84,9 +90,11 @@ func TestErrorHandlerApplyDependency(t *testing.T) { e.Integration.Status.Phase = v1.IntegrationPhaseInitialization trait := newErrorHandlerTrait() - enabled, err := trait.Configure(e) + enabled, condition, err := trait.Configure(e) assert.Nil(t, err) assert.True(t, enabled) + assert.Nil(t, condition) + err = trait.Apply(e) assert.Nil(t, err) assert.Equal(t, "camel:log", e.Integration.Status.Dependencies[0]) diff --git a/pkg/trait/gc.go b/pkg/trait/gc.go index 3beeb88d9b..ef309c9ebb 100644 --- a/pkg/trait/gc.go +++ b/pkg/trait/gc.go @@ -61,12 +61,15 @@ func newGCTrait() Trait { } } -func (t *gcTrait) Configure(e *Environment) (bool, error) { - if e.Integration == nil || !pointer.BoolDeref(t.Enabled, true) { - return false, nil +func (t *gcTrait) Configure(e *Environment) (bool, *TraitCondition, error) { + if e.Integration == nil { + return false, nil, nil + } + if !pointer.BoolDeref(t.Enabled, true) { + return false, NewIntegrationConditionUserDisabled(), nil } - return e.IntegrationInPhase(v1.IntegrationPhaseInitialization) || e.IntegrationInRunningPhases(), nil + return e.IntegrationInPhase(v1.IntegrationPhaseInitialization) || e.IntegrationInRunningPhases(), nil, nil } func (t *gcTrait) Apply(e *Environment) error { diff --git a/pkg/trait/gc_test.go b/pkg/trait/gc_test.go index aad15f1a2c..463af2f5a9 100644 --- a/pkg/trait/gc_test.go +++ b/pkg/trait/gc_test.go @@ -23,26 +23,36 @@ import ( v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/pointer" ) func TestConfigureGCTraitDoesSucceed(t *testing.T) { gcTrait, environment := createNominalGCTest() - configured, err := gcTrait.Configure(environment) + configured, condition, err := gcTrait.Configure(environment) assert.True(t, configured) assert.Nil(t, err) + assert.Nil(t, condition) + } func TestConfigureDisabledGCTraitDoesNotSucceed(t *testing.T) { gcTrait, environment := createNominalGCTest() gcTrait.Enabled = pointer.Bool(false) - configured, err := gcTrait.Configure(environment) - + expectedCondition := NewIntegrationCondition( + v1.IntegrationConditionTraitInfo, + corev1.ConditionTrue, + "Trait configuration", + "explicitly disabled by the user", + ) + configured, condition, err := gcTrait.Configure(environment) assert.False(t, configured) assert.Nil(t, err) + assert.NotNil(t, condition) + assert.Equal(t, expectedCondition, condition) } func TestApplyGarbageCollectorTraitFirstGenerationDoesSucceed(t *testing.T) { diff --git a/pkg/trait/health.go b/pkg/trait/health.go index bf8350e4b7..cb357a39ec 100644 --- a/pkg/trait/health.go +++ b/pkg/trait/health.go @@ -52,17 +52,16 @@ func newHealthTrait() Trait { } } -func (t *healthTrait) Configure(e *Environment) (bool, error) { +func (t *healthTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if e.Integration == nil || !e.IntegrationInPhase(v1.IntegrationPhaseInitialization) && !e.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil } - if !pointer.BoolDeref(t.Enabled, false) { - return false, nil + return false, nil, nil } - return true, nil + return true, nil, nil } func (t *healthTrait) Apply(e *Environment) error { diff --git a/pkg/trait/ingress.go b/pkg/trait/ingress.go index f611e86677..f240f10d98 100644 --- a/pkg/trait/ingress.go +++ b/pkg/trait/ingress.go @@ -24,6 +24,7 @@ import ( corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/pointer" v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait" @@ -51,34 +52,29 @@ func (t *ingressTrait) IsAllowedInProfile(profile v1.TraitProfile) bool { return profile.Equal(v1.TraitProfileKubernetes) } -func (t *ingressTrait) Configure(e *Environment) (bool, error) { +func (t *ingressTrait) Configure(e *Environment) (bool, *TraitCondition, error) { + if e.Integration == nil { + return false, nil, nil + } if !e.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil } - - if !ptrDerefOr(t.Enabled, true) { - e.Integration.Status.SetCondition( + if !pointer.BoolDeref(t.Enabled, true) { + return false, NewIntegrationCondition( v1.IntegrationConditionExposureAvailable, corev1.ConditionFalse, v1.IntegrationConditionIngressNotAvailableReason, "explicitly disabled", - ) - return false, nil + ), nil } - if ptrDerefOr(t.Auto, true) { + if pointer.BoolDeref(t.Auto, true) { if e.Resources.GetUserServiceForIntegration(e.Integration) == nil { - e.Integration.Status.SetCondition( - v1.IntegrationConditionExposureAvailable, - corev1.ConditionFalse, - v1.IntegrationConditionIngressNotAvailableReason, - "no service defined", - ) - return false, nil + return false, nil, nil } } - return true, nil + return true, nil, nil } func (t *ingressTrait) Apply(e *Environment) error { diff --git a/pkg/trait/ingress_test.go b/pkg/trait/ingress_test.go index c190323762..b299f8f46d 100644 --- a/pkg/trait/ingress_test.go +++ b/pkg/trait/ingress_test.go @@ -34,59 +34,55 @@ import ( func TestConfigureIngressTraitDoesSucceed(t *testing.T) { ingressTrait, environment := createNominalIngressTest() - configured, err := ingressTrait.Configure(environment) + configured, condition, err := ingressTrait.Configure(environment) assert.True(t, configured) assert.Nil(t, err) + assert.Nil(t, condition) assert.Len(t, environment.Integration.Status.Conditions, 0) + assert.Nil(t, condition) + } func TestConfigureDisabledIngressTraitDoesNotSucceed(t *testing.T) { ingressTrait, environment := createNominalIngressTest() ingressTrait.Enabled = pointer.Bool(false) - configured, err := ingressTrait.Configure(environment) + expectedCondition := NewIntegrationCondition( + v1.IntegrationConditionExposureAvailable, + corev1.ConditionFalse, + v1.IntegrationConditionIngressNotAvailableReason, + "explicitly disabled", + ) + configured, condition, err := ingressTrait.Configure(environment) assert.False(t, configured) assert.Nil(t, err) - conditions := environment.Integration.Status.Conditions - assert.Len(t, conditions, 1) - assert.Equal(t, "explicitly disabled", conditions[0].Message) + assert.NotNil(t, condition) + assert.Equal(t, expectedCondition, condition) } func TestConfigureIngressTraitInWrongPhaseDoesNotSucceed(t *testing.T) { ingressTrait, environment := createNominalIngressTest() environment.Integration.Status.Phase = v1.IntegrationPhaseError - configured, err := ingressTrait.Configure(environment) + configured, condition, err := ingressTrait.Configure(environment) assert.True(t, configured) assert.Nil(t, err) + assert.Nil(t, condition) assert.Len(t, environment.Integration.Status.Conditions, 0) } -func TestConfigureAutoIngressTraitWithoutUserServiceDoesNotSucceed(t *testing.T) { - ingressTrait, environment := createNominalIngressTest() - ingressTrait.Auto = pointer.Bool(true) - environment.Resources = kubernetes.NewCollection() - - configured, err := ingressTrait.Configure(environment) - - assert.False(t, configured) - assert.Nil(t, err) - conditions := environment.Integration.Status.Conditions - assert.Len(t, conditions, 1) - assert.Equal(t, "no service defined", conditions[0].Message) -} - func TestConfigureAutoIngressTraitWithUserServiceDoesSucceed(t *testing.T) { ingressTrait, environment := createNominalIngressTest() ingressTrait.Auto = nil - configured, err := ingressTrait.Configure(environment) + configured, condition, err := ingressTrait.Configure(environment) assert.True(t, configured) assert.Nil(t, err) + assert.Nil(t, condition) assert.Len(t, environment.Integration.Status.Conditions, 0) } diff --git a/pkg/trait/init.go b/pkg/trait/init.go index d941af7c18..a365f344e3 100644 --- a/pkg/trait/init.go +++ b/pkg/trait/init.go @@ -42,12 +42,12 @@ func NewInitTrait() Trait { } } -func (t *initTrait) Configure(e *Environment) (bool, error) { +func (t *initTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if !pointer.BoolDeref(t.Enabled, true) { - return false, errors.New("trait init cannot be disabled") + return false, nil, errors.New("trait init cannot be disabled") } - return e.IntegrationInPhase(v1.IntegrationPhaseInitialization), nil + return e.IntegrationInPhase(v1.IntegrationPhaseInitialization), nil, nil } func (t *initTrait) Apply(e *Environment) error { diff --git a/pkg/trait/istio.go b/pkg/trait/istio.go index 26e35540b4..86d4ea268b 100644 --- a/pkg/trait/istio.go +++ b/pkg/trait/istio.go @@ -47,12 +47,12 @@ func newIstioTrait() Trait { } } -func (t *istioTrait) Configure(e *Environment) (bool, error) { +func (t *istioTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if e.Integration == nil || !pointer.BoolDeref(t.Enabled, false) { - return false, nil + return false, nil, nil } - return e.IntegrationInRunningPhases(), nil + return e.IntegrationInRunningPhases(), nil, nil } func (t *istioTrait) Apply(e *Environment) error { diff --git a/pkg/trait/istio_test.go b/pkg/trait/istio_test.go index d3cf638f46..6c7d5b69bd 100644 --- a/pkg/trait/istio_test.go +++ b/pkg/trait/istio_test.go @@ -60,7 +60,6 @@ func NewIstioTestEnv(t *testing.T, d *appsv1.Deployment, s *serving.Service, ena }, Spec: v1.IntegrationPlatformSpec{ Cluster: v1.IntegrationPlatformClusterOpenShift, - Profile: v1.TraitProfileKnative, Build: v1.IntegrationPlatformBuildSpec{ RuntimeVersion: catalog.Runtime.Version, }, @@ -100,9 +99,9 @@ func TestIstioInject(t *testing.T) { } env := NewIstioTestEnv(t, &d, &s, true) - err := env.Catalog.apply(&env) + conditions, err := env.Catalog.apply(&env) assert.Nil(t, err) - + assert.Empty(t, conditions) assert.Empty(t, s.Spec.ConfigurationSpec.Template.Annotations[istioSidecarInjectAnnotation]) assert.NotEmpty(t, d.Spec.Template.Annotations[istioSidecarInjectAnnotation]) } @@ -125,9 +124,9 @@ func TestIstioForcedInjectTrue(t *testing.T) { env.Integration.Spec.Traits.Istio.Enabled = pointer.Bool(true) env.Integration.Spec.Traits.Istio.Inject = pointer.Bool(true) - err := env.Catalog.apply(&env) + conditions, err := env.Catalog.apply(&env) assert.Nil(t, err) - + assert.Empty(t, conditions) assert.Equal(t, "true", s.Spec.ConfigurationSpec.Template.Annotations[istioSidecarInjectAnnotation]) assert.Equal(t, "true", d.Spec.Template.Annotations[istioSidecarInjectAnnotation]) } @@ -150,9 +149,9 @@ func TestIstioForcedInjectFalse(t *testing.T) { env.Integration.Spec.Traits.Istio.Enabled = pointer.Bool(true) env.Integration.Spec.Traits.Istio.Inject = pointer.Bool(false) - err := env.Catalog.apply(&env) + conditions, err := env.Catalog.apply(&env) assert.Nil(t, err) - + assert.Empty(t, conditions) assert.Equal(t, "false", s.Spec.ConfigurationSpec.Template.Annotations[istioSidecarInjectAnnotation]) assert.Equal(t, "false", d.Spec.Template.Annotations[istioSidecarInjectAnnotation]) } @@ -173,7 +172,8 @@ func TestIstioDisabled(t *testing.T) { env := NewIstioTestEnv(t, &d, &s, false) - err := env.Catalog.apply(&env) + conditions, err := env.Catalog.apply(&env) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotContains(t, env.ExecutedTraits, "istio") } diff --git a/pkg/trait/jolokia.go b/pkg/trait/jolokia.go index 871c257ee1..057fa71e83 100644 --- a/pkg/trait/jolokia.go +++ b/pkg/trait/jolokia.go @@ -44,12 +44,12 @@ func newJolokiaTrait() Trait { } } -func (t *jolokiaTrait) Configure(e *Environment) (bool, error) { +func (t *jolokiaTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if e.Integration == nil || !pointer.BoolDeref(t.Enabled, false) { - return false, nil + return false, nil, nil } - return e.IntegrationInPhase(v1.IntegrationPhaseInitialization) || e.IntegrationInRunningPhases(), nil + return e.IntegrationInPhase(v1.IntegrationPhaseInitialization) || e.IntegrationInRunningPhases(), nil, nil } func (t *jolokiaTrait) Apply(e *Environment) error { diff --git a/pkg/trait/jolokia_test.go b/pkg/trait/jolokia_test.go index 4b7bc9a8a6..f0210e5d03 100644 --- a/pkg/trait/jolokia_test.go +++ b/pkg/trait/jolokia_test.go @@ -34,10 +34,11 @@ func TestConfigureJolokiaTraitInRunningPhaseDoesSucceed(t *testing.T) { trait, environment := createNominalJolokiaTest() environment.Integration.Status.Phase = v1.IntegrationPhaseRunning - configured, err := trait.Configure(environment) + configured, condition, err := trait.Configure(environment) assert.Nil(t, err) assert.True(t, configured) + assert.Nil(t, condition) } func TestApplyJolokiaTraitNominalShouldSucceed(t *testing.T) { diff --git a/pkg/trait/jvm.go b/pkg/trait/jvm.go index 8a69687368..9223cfef6e 100644 --- a/pkg/trait/jvm.go +++ b/pkg/trait/jvm.go @@ -55,23 +55,22 @@ func newJvmTrait() Trait { } } -func (t *jvmTrait) Configure(e *Environment) (bool, error) { +func (t *jvmTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if !pointer.BoolDeref(t.Enabled, true) { - return false, nil + return false, NewIntegrationConditionUserDisabled(), nil } - if !e.IntegrationKitInPhase(v1.IntegrationKitPhaseReady) || !e.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil } if trait := e.Catalog.GetTrait(quarkusTraitID); trait != nil { // The JVM trait must be disabled in case the current IntegrationKit corresponds to a native build if quarkus, ok := trait.(*quarkusTrait); ok && quarkus.isNativeIntegration(e) { - return false, nil + return false, newIntegrationConditionPlatformDisabledWithReason("Quarkus native build"), nil } } - return true, nil + return true, nil, nil } // nolint: maintidx // TODO: refactor the code diff --git a/pkg/trait/jvm_test.go b/pkg/trait/jvm_test.go index 41c3d82809..610d5000ba 100644 --- a/pkg/trait/jvm_test.go +++ b/pkg/trait/jvm_test.go @@ -49,27 +49,30 @@ var ( func TestConfigureJvmTraitInRightPhasesDoesSucceed(t *testing.T) { trait, environment := createNominalJvmTest(v1.IntegrationKitTypePlatform) - configured, err := trait.Configure(environment) + configured, condition, err := trait.Configure(environment) assert.Nil(t, err) assert.True(t, configured) + assert.Nil(t, condition) } func TestConfigureJvmTraitInWrongIntegrationPhaseDoesNotSucceed(t *testing.T) { trait, environment := createNominalJvmTest(v1.IntegrationKitTypePlatform) environment.Integration.Status.Phase = v1.IntegrationPhaseError - configured, err := trait.Configure(environment) + configured, condition, err := trait.Configure(environment) assert.Nil(t, err) assert.True(t, configured) + assert.Nil(t, condition) } func TestConfigureJvmTraitInWrongIntegrationKitPhaseDoesNotSucceed(t *testing.T) { trait, environment := createNominalJvmTest(v1.IntegrationKitTypePlatform) environment.IntegrationKit.Status.Phase = v1.IntegrationKitPhaseWaitingForPlatform - configured, err := trait.Configure(environment) + configured, condition, err := trait.Configure(environment) assert.Nil(t, err) assert.False(t, configured) + assert.Nil(t, condition) } func TestApplyJvmTraitWithDeploymentResource(t *testing.T) { diff --git a/pkg/trait/kamelets.go b/pkg/trait/kamelets.go index 012e178356..d54878b165 100644 --- a/pkg/trait/kamelets.go +++ b/pkg/trait/kamelets.go @@ -71,19 +71,21 @@ func newKameletsTrait() Trait { } } -func (t *kameletsTrait) Configure(e *Environment) (bool, error) { - if e.Integration == nil || !pointer.BoolDeref(t.Enabled, true) { - return false, nil +func (t *kameletsTrait) Configure(e *Environment) (bool, *TraitCondition, error) { + if e.Integration == nil { + return false, nil, nil + } + if !pointer.BoolDeref(t.Enabled, true) { + return false, NewIntegrationConditionUserDisabled(), nil } - if !e.IntegrationInPhase(v1.IntegrationPhaseInitialization) && !e.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil } if pointer.BoolDeref(t.Auto, true) { kamelets, err := kamelets.ExtractKameletFromSources(e.Ctx, e.Client, e.CamelCatalog, e.Resources, e.Integration) if err != nil { - return false, err + return false, nil, err } if len(kamelets) > 0 { @@ -96,7 +98,7 @@ func (t *kameletsTrait) Configure(e *Environment) (bool, error) { } } - return len(t.getKameletKeys()) > 0, nil + return len(t.getKameletKeys()) > 0, nil, nil } func (t *kameletsTrait) Apply(e *Environment) error { diff --git a/pkg/trait/kamelets_test.go b/pkg/trait/kamelets_test.go index ea2bf56907..e3cf4fd09a 100644 --- a/pkg/trait/kamelets_test.go +++ b/pkg/trait/kamelets_test.go @@ -40,9 +40,10 @@ func TestConfigurationNoKameletsUsed(t *testing.T) { steps: - to: log:info `) - enabled, err := trait.Configure(environment) + enabled, condition, err := trait.Configure(environment) require.NoError(t, err) assert.False(t, enabled) + assert.Nil(t, condition) assert.Equal(t, "", trait.List) } @@ -60,9 +61,10 @@ func TestConfigurationWithKamelets(t *testing.T) { - to: kamelet://complex-.-.-1b/a - to: kamelet://complex-.-.-1c/b `) - enabled, err := trait.Configure(environment) + enabled, condition, err := trait.Configure(environment) require.NoError(t, err) assert.True(t, enabled) + assert.Nil(t, condition) assert.Equal(t, []string{"c0", "c1", "c2", "complex-.-.-1a", "complex-.-.-1b", "complex-.-.-1c"}, trait.getKameletKeys()) assert.Equal(t, []configurationKey{ newConfigurationKey("c0", ""), @@ -99,9 +101,10 @@ func TestKameletLookup(t *testing.T) { }, }, }) - enabled, err := trait.Configure(environment) + enabled, condition, err := trait.Configure(environment) require.NoError(t, err) assert.True(t, enabled) + assert.Nil(t, condition) assert.Equal(t, []string{"timer"}, trait.getKameletKeys()) err = trait.Apply(environment) @@ -147,9 +150,10 @@ func TestKameletSecondarySourcesLookup(t *testing.T) { }, }, }) - enabled, err := trait.Configure(environment) + enabled, condition, err := trait.Configure(environment) require.NoError(t, err) assert.True(t, enabled) + assert.Nil(t, condition) assert.Equal(t, []string{"timer"}, trait.getKameletKeys()) err = trait.Apply(environment) @@ -197,9 +201,10 @@ func TestNonYAMLKameletLookup(t *testing.T) { }, }, }) - enabled, err := trait.Configure(environment) + enabled, condition, err := trait.Configure(environment) require.NoError(t, err) assert.True(t, enabled) + assert.Nil(t, condition) assert.Equal(t, []string{"timer"}, trait.getKameletKeys()) err = trait.Apply(environment) @@ -271,9 +276,10 @@ func TestMultipleKamelets(t *testing.T) { }, }, }) - enabled, err := trait.Configure(environment) + enabled, condition, err := trait.Configure(environment) require.NoError(t, err) assert.True(t, enabled) + assert.Nil(t, condition) assert.Equal(t, []string{"logger", "timer"}, trait.getKameletKeys()) err = trait.Apply(environment) @@ -357,9 +363,10 @@ func TestKameletConfigLookup(t *testing.T) { }, }, }) - enabled, err := trait.Configure(environment) + enabled, condition, err := trait.Configure(environment) require.NoError(t, err) assert.True(t, enabled) + assert.Nil(t, condition) assert.Equal(t, []string{"timer"}, trait.getKameletKeys()) assert.Equal(t, []configurationKey{newConfigurationKey("timer", "")}, trait.getConfigurationKeys()) @@ -420,9 +427,10 @@ func TestKameletNamedConfigLookup(t *testing.T) { }, }, }) - enabled, err := trait.Configure(environment) + enabled, condition, err := trait.Configure(environment) require.NoError(t, err) assert.True(t, enabled) + assert.Nil(t, condition) assert.Equal(t, []string{"timer"}, trait.getKameletKeys()) assert.Equal(t, []configurationKey{ newConfigurationKey("timer", ""), @@ -460,9 +468,10 @@ func TestKameletConditionFalse(t *testing.T) { }, }) - enabled, err := trait.Configure(environment) + enabled, condition, err := trait.Configure(environment) require.NoError(t, err) assert.True(t, enabled) + assert.Nil(t, condition) err = trait.Apply(environment) assert.Error(t, err) @@ -511,9 +520,10 @@ func TestKameletConditionTrue(t *testing.T) { }, }) - enabled, err := trait.Configure(environment) + 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) diff --git a/pkg/trait/knative.go b/pkg/trait/knative.go index 5e09bcd44f..91bb7b7bca 100644 --- a/pkg/trait/knative.go +++ b/pkg/trait/knative.go @@ -66,27 +66,28 @@ func (t *knativeTrait) IsAllowedInProfile(profile v1.TraitProfile) bool { return profile.Equal(v1.TraitProfileKnative) } -func (t *knativeTrait) Configure(e *Environment) (bool, error) { - if e.Integration == nil || !pointer.BoolDeref(t.Enabled, true) { - return false, nil +func (t *knativeTrait) Configure(e *Environment) (bool, *TraitCondition, error) { + if e.Integration == nil { + return false, nil, nil + } + if !pointer.BoolDeref(t.Enabled, true) { + return false, NewIntegrationConditionUserDisabled(), nil } - if !e.IntegrationInPhase(v1.IntegrationPhaseInitialization) && !e.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil } - if pointer.BoolDeref(t.Auto, true) { if len(t.ChannelSources) == 0 { items := make([]string, 0) sources, err := kubernetes.ResolveIntegrationSources(e.Ctx, e.Client, e.Integration, e.Resources) if err != nil { - return false, err + return false, nil, err } if err := metadata.Each(e.CamelCatalog, sources, func(_ int, meta metadata.IntegrationMetadata) bool { items = append(items, knativeutil.FilterURIs(meta.FromURIs, knativeapi.CamelServiceTypeChannel)...) return true }); err != nil { - return false, err + return false, nil, err } t.ChannelSources = items @@ -96,13 +97,13 @@ func (t *knativeTrait) Configure(e *Environment) (bool, error) { items := make([]string, 0) sources, err := kubernetes.ResolveIntegrationSources(e.Ctx, e.Client, e.Integration, e.Resources) if err != nil { - return false, err + return false, nil, err } if err := metadata.Each(e.CamelCatalog, sources, func(_ int, meta metadata.IntegrationMetadata) bool { items = append(items, knativeutil.FilterURIs(meta.ToURIs, knativeapi.CamelServiceTypeChannel)...) return true }); err != nil { - return false, err + return false, nil, err } t.ChannelSinks = items @@ -112,13 +113,13 @@ func (t *knativeTrait) Configure(e *Environment) (bool, error) { items := make([]string, 0) sources, err := kubernetes.ResolveIntegrationSources(e.Ctx, e.Client, e.Integration, e.Resources) if err != nil { - return false, err + return false, nil, err } if err := metadata.Each(e.CamelCatalog, sources, func(_ int, meta metadata.IntegrationMetadata) bool { items = append(items, knativeutil.FilterURIs(meta.FromURIs, knativeapi.CamelServiceTypeEndpoint)...) return true }); err != nil { - return false, err + return false, nil, err } t.EndpointSources = items @@ -128,13 +129,13 @@ func (t *knativeTrait) Configure(e *Environment) (bool, error) { items := make([]string, 0) sources, err := kubernetes.ResolveIntegrationSources(e.Ctx, e.Client, e.Integration, e.Resources) if err != nil { - return false, err + return false, nil, err } if err := metadata.Each(e.CamelCatalog, sources, func(_ int, meta metadata.IntegrationMetadata) bool { items = append(items, knativeutil.FilterURIs(meta.ToURIs, knativeapi.CamelServiceTypeEndpoint)...) return true }); err != nil { - return false, err + return false, nil, err } t.EndpointSinks = items @@ -144,13 +145,13 @@ func (t *knativeTrait) Configure(e *Environment) (bool, error) { items := make([]string, 0) sources, err := kubernetes.ResolveIntegrationSources(e.Ctx, e.Client, e.Integration, e.Resources) if err != nil { - return false, err + return false, nil, err } if err := metadata.Each(e.CamelCatalog, sources, func(_ int, meta metadata.IntegrationMetadata) bool { items = append(items, knativeutil.FilterURIs(meta.FromURIs, knativeapi.CamelServiceTypeEvent)...) return true }); err != nil { - return false, err + return false, nil, err } t.EventSources = items @@ -160,13 +161,13 @@ func (t *knativeTrait) Configure(e *Environment) (bool, error) { items := make([]string, 0) sources, err := kubernetes.ResolveIntegrationSources(e.Ctx, e.Client, e.Integration, e.Resources) if err != nil { - return false, err + return false, nil, err } if err := metadata.Each(e.CamelCatalog, sources, func(_ int, meta metadata.IntegrationMetadata) bool { items = append(items, knativeutil.FilterURIs(meta.ToURIs, knativeapi.CamelServiceTypeEvent)...) return true }); err != nil { - return false, err + return false, nil, err } t.EventSinks = items @@ -182,7 +183,7 @@ func (t *knativeTrait) Configure(e *Environment) (bool, error) { } } - return true, nil + return true, nil, nil } func (t *knativeTrait) Apply(e *Environment) error { diff --git a/pkg/trait/knative_service.go b/pkg/trait/knative_service.go index 63b8f231ed..7d667881a9 100644 --- a/pkg/trait/knative_service.go +++ b/pkg/trait/knative_service.go @@ -69,63 +69,57 @@ func (t *knativeServiceTrait) IsAllowedInProfile(profile v1.TraitProfile) bool { return profile.Equal(v1.TraitProfileKnative) } -func (t *knativeServiceTrait) Configure(e *Environment) (bool, error) { - if e.Integration == nil || !pointer.BoolDeref(t.Enabled, true) { - if e.Integration != nil { - e.Integration.Status.SetCondition( - v1.IntegrationConditionKnativeServiceAvailable, - corev1.ConditionFalse, - v1.IntegrationConditionKnativeServiceNotAvailableReason, - "explicitly disabled", - ) - } - - return false, nil +func (t *knativeServiceTrait) Configure(e *Environment) (bool, *TraitCondition, error) { + if e.Integration == nil { + return false, nil, nil + } + if !pointer.BoolDeref(t.Enabled, true) { + return false, NewIntegrationCondition( + v1.IntegrationConditionKnativeServiceAvailable, + corev1.ConditionFalse, + v1.IntegrationConditionKnativeServiceNotAvailableReason, + "explicitly disabled", + ), nil } if !e.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil } if e.Resources.GetDeploymentForIntegration(e.Integration) != nil { - e.Integration.Status.SetCondition( + // A controller is already present for the integration + return false, NewIntegrationCondition( v1.IntegrationConditionKnativeServiceAvailable, corev1.ConditionFalse, v1.IntegrationConditionKnativeServiceNotAvailableReason, fmt.Sprintf("different controller strategy used (%s)", string(ControllerStrategyDeployment)), - ) - - // A controller is already present for the integration - return false, nil + ), nil } strategy, err := e.DetermineControllerStrategy() if err != nil { - e.Integration.Status.SetErrorCondition( + return false, NewIntegrationCondition( v1.IntegrationConditionKnativeServiceAvailable, + corev1.ConditionFalse, v1.IntegrationConditionKnativeServiceNotAvailableReason, - err, - ) - - return false, err + err.Error(), + ), err } if strategy != ControllerStrategyKnativeService { - e.Integration.Status.SetCondition( + return false, NewIntegrationCondition( v1.IntegrationConditionKnativeServiceAvailable, corev1.ConditionFalse, v1.IntegrationConditionKnativeServiceNotAvailableReason, fmt.Sprintf("different controller strategy used (%s)", string(strategy)), - ) - - return false, nil + ), nil } if e.IntegrationInPhase(v1.IntegrationPhaseRunning, v1.IntegrationPhaseError) { condition := e.Integration.Status.GetCondition(v1.IntegrationConditionKnativeServiceAvailable) - return condition != nil && condition.Status == corev1.ConditionTrue, nil + return condition != nil && condition.Status == corev1.ConditionTrue, nil, nil } - return true, nil + return true, 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 ccf49b3d83..6367c13f63 100644 --- a/pkg/trait/knative_service_test.go +++ b/pkg/trait/knative_service_test.go @@ -118,7 +118,8 @@ func TestKnativeService(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - err = traitCatalog.apply(&environment) + // don't care about conditions in this unit test + _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -246,7 +247,8 @@ func TestKnativeServiceWithCustomContainerName(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - err = traitCatalog.apply(&environment) + // don't care about conditions in this unit test + _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -330,7 +332,8 @@ func TestKnativeServiceWithRest(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - err = traitCatalog.apply(&environment) + // don't care about conditions in this unit test + _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -397,7 +400,8 @@ func TestKnativeServiceNotApplicable(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - err = traitCatalog.apply(&environment) + // don't care about conditions in this unit test + _, err = traitCatalog.apply(&environment) require.NoError(t, err) assert.NotEmpty(t, environment.ExecutedTraits) @@ -507,8 +511,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 @@ -521,7 +524,7 @@ func TestServiceAnnotation(t *testing.T) { }) traitsCatalog := environment.Catalog - err := traitsCatalog.apply(environment) + _, err := traitsCatalog.apply(environment) assert.Nil(t, err) diff --git a/pkg/trait/knative_test.go b/pkg/trait/knative_test.go index 134819436e..0d5a339fbe 100644 --- a/pkg/trait/knative_test.go +++ b/pkg/trait/knative_test.go @@ -109,9 +109,10 @@ func TestKnativeEnvConfigurationFromTrait(t *testing.T) { assert.Nil(t, err) tr, _ := tc.GetTrait("knative").(*knativeTrait) - ok, err := tr.Configure(&environment) + ok, condition, err := tr.Configure(&environment) assert.Nil(t, err) assert.True(t, ok) + assert.Nil(t, condition) err = tr.Apply(&environment) assert.Nil(t, err) @@ -230,9 +231,10 @@ func TestKnativeEnvConfigurationFromSource(t *testing.T) { tr, _ := tc.GetTrait("knative").(*knativeTrait) - ok, err := tr.Configure(&environment) + ok, condition, err := tr.Configure(&environment) assert.Nil(t, err) assert.True(t, ok) + assert.Nil(t, condition) err = tr.Apply(&environment) assert.Nil(t, err) @@ -296,9 +298,8 @@ func TestKnativePlatformHttpConfig(t *testing.T) { err = tc.Configure(&environment) assert.Nil(t, err) - err = tc.apply(&environment) + _, err = tc.apply(&environment) assert.Nil(t, err) - assert.Contains(t, environment.Integration.Status.Capabilities, v1.CapabilityPlatformHTTP) }) } @@ -343,9 +344,9 @@ func TestKnativePlatformHttpDependencies(t *testing.T) { err = tc.Configure(&environment) assert.Nil(t, err) - err = tc.apply(&environment) + conditions, err := tc.apply(&environment) assert.Nil(t, err) - + assert.Empty(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") }) @@ -394,7 +395,8 @@ func TestKnativeConfigurationSorting(t *testing.T) { tc := NewCatalog(c) err = tc.Configure(&environment) assert.Nil(t, err) - _ = tc.apply(&environment) + conditions, _ := tc.apply(&environment) + assert.Empty(t, conditions) // no matter if there is any other trait error camelEnv := knativeapi.NewCamelEnvironment() err = camelEnv.Deserialize(envvar.Get(environment.EnvVars, "CAMEL_KNATIVE_CONFIGURATION").Value) diff --git a/pkg/trait/logging.go b/pkg/trait/logging.go index 3e3590b78f..dafd331a76 100644 --- a/pkg/trait/logging.go +++ b/pkg/trait/logging.go @@ -48,12 +48,16 @@ func newLoggingTraitTrait() Trait { } } -func (l loggingTrait) Configure(e *Environment) (bool, error) { - if e.Integration == nil || !pointer.BoolDeref(l.Enabled, true) { - return false, nil +func (l loggingTrait) Configure(e *Environment) (bool, *TraitCondition, error) { + if e.Integration == nil { + return false, nil, nil } - return e.IntegrationInRunningPhases(), nil + if !pointer.BoolDeref(l.Enabled, true) { + return false, NewIntegrationConditionUserDisabled(), nil + } + + return e.IntegrationInRunningPhases(), nil, nil } func (l loggingTrait) Apply(e *Environment) error { diff --git a/pkg/trait/logging_test.go b/pkg/trait/logging_test.go index 2dad88db33..6b2846e626 100644 --- a/pkg/trait/logging_test.go +++ b/pkg/trait/logging_test.go @@ -110,9 +110,10 @@ func NewLoggingTestCatalog() *Catalog { func TestEmptyLoggingTrait(t *testing.T) { env := createDefaultLoggingTestEnv(t) - err := NewLoggingTestCatalog().apply(env) + conditions, err := NewLoggingTestCatalog().apply(env) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, env.ExecutedTraits) quarkusConsoleColor := false @@ -162,9 +163,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, false, "TRACE", "%d{HH:mm:ss} %-5p (%t) %s%e%n") - err := NewLoggingTestCatalog().apply(env) + conditions, err := NewLoggingTestCatalog().apply(env) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, env.ExecutedTraits) quarkusConsoleColor := false diff --git a/pkg/trait/mount.go b/pkg/trait/mount.go index a99e3c9b0c..ccb3b344b2 100644 --- a/pkg/trait/mount.go +++ b/pkg/trait/mount.go @@ -46,29 +46,29 @@ func newMountTrait() Trait { } } -func (t *mountTrait) Configure(e *Environment) (bool, error) { +func (t *mountTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if e.Integration == nil { - return false, nil + return false, nil, nil } if e.IntegrationInPhase(v1.IntegrationPhaseInitialization) || (!e.IntegrationInPhase(v1.IntegrationPhaseInitialization) && !e.IntegrationInRunningPhases()) { - return false, nil + return false, nil, nil } // Validate resources and pvcs for _, c := range t.Configs { if !strings.HasPrefix(c, "configmap:") && !strings.HasPrefix(c, "secret:") { - return false, fmt.Errorf("unsupported config %s, must be a configmap or secret resource", c) + return false, nil, fmt.Errorf("unsupported config %s, must be a configmap or secret resource", c) } } for _, r := range t.Resources { if !strings.HasPrefix(r, "configmap:") && !strings.HasPrefix(r, "secret:") { - return false, fmt.Errorf("unsupported resource %s, must be a configmap or secret resource", r) + return false, nil, fmt.Errorf("unsupported resource %s, must be a configmap or secret resource", r) } } - return true, nil + return true, nil, nil } func (t *mountTrait) Apply(e *Environment) error { diff --git a/pkg/trait/mount_test.go b/pkg/trait/mount_test.go index 199597d9f0..6191b2ae69 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() - err := traitCatalog.apply(environment) + conditions, err := traitCatalog.apply(environment) require.NoError(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("mount")) @@ -65,9 +66,10 @@ func TestMountVolumesIntegrationPhaseDeploying(t *testing.T) { environment := getNominalEnv(t, traitCatalog) environment.Platform.ResyncStatusFullConfig() - err := traitCatalog.apply(environment) + conditions, err := traitCatalog.apply(environment) require.NoError(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("mount")) @@ -113,9 +115,10 @@ func TestMountVolumesIntegrationPhaseInitialization(t *testing.T) { environment.Integration.Status.Phase = v1.IntegrationPhaseInitialization environment.Platform.ResyncStatusFullConfig() - err := traitCatalog.apply(environment) + conditions, err := traitCatalog.apply(environment) require.NoError(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.Nil(t, environment.GetTrait("mount")) diff --git a/pkg/trait/openapi.go b/pkg/trait/openapi.go index 50ab972d5d..0da314b96d 100644 --- a/pkg/trait/openapi.go +++ b/pkg/trait/openapi.go @@ -55,21 +55,21 @@ func newOpenAPITrait() Trait { } } -func (t *openAPITrait) Configure(e *Environment) (bool, error) { +func (t *openAPITrait) Configure(e *Environment) (bool, *TraitCondition, error) { if !e.IntegrationInPhase(v1.IntegrationPhaseInitialization) { - return false, nil + return false, nil, nil } // check if the runtime provides 'rest' capabilities if _, ok := e.CamelCatalog.Runtime.Capabilities[v1.CapabilityRest]; !ok { - return false, fmt.Errorf("the runtime provider %s does not declare 'rest' capability", e.CamelCatalog.Runtime.Provider) + return false, nil, fmt.Errorf("the runtime provider %s does not declare 'rest' capability", e.CamelCatalog.Runtime.Provider) } if t.Configmaps != nil { - return e.IntegrationInPhase(v1.IntegrationPhaseInitialization), nil + return e.IntegrationInPhase(v1.IntegrationPhaseInitialization), nil, nil } - return false, nil + return false, nil, nil } func (t *openAPITrait) Apply(e *Environment) error { diff --git a/pkg/trait/openapi_test.go b/pkg/trait/openapi_test.go index d90a27ac11..05e710dc7f 100644 --- a/pkg/trait/openapi_test.go +++ b/pkg/trait/openapi_test.go @@ -36,27 +36,31 @@ func TestRestDslTraitApplicability(t *testing.T) { } trait, _ := newOpenAPITrait().(*openAPITrait) - enabled, err := trait.Configure(e) + enabled, condition, err := trait.Configure(e) assert.Nil(t, err) assert.False(t, enabled) + assert.Nil(t, condition) e.Integration = &v1.Integration{ Status: v1.IntegrationStatus{ Phase: v1.IntegrationPhaseNone, }, } - enabled, err = trait.Configure(e) + enabled, condition, err = trait.Configure(e) assert.Nil(t, err) assert.False(t, enabled) + assert.Nil(t, condition) trait.Configmaps = []string{"my-configmap"} - enabled, err = trait.Configure(e) + enabled, condition, err = trait.Configure(e) assert.Nil(t, err) assert.False(t, enabled) + assert.Nil(t, condition) e.Integration.Status.Phase = v1.IntegrationPhaseInitialization - enabled, err = trait.Configure(e) + enabled, condition, err = trait.Configure(e) assert.Nil(t, err) assert.True(t, enabled) + assert.Nil(t, condition) } diff --git a/pkg/trait/owner.go b/pkg/trait/owner.go index 31066f1a20..20bab1952d 100644 --- a/pkg/trait/owner.go +++ b/pkg/trait/owner.go @@ -39,12 +39,15 @@ func newOwnerTrait() Trait { } } -func (t *ownerTrait) Configure(e *Environment) (bool, error) { - if e.Integration == nil || !pointer.BoolDeref(t.Enabled, true) { - return false, nil +func (t *ownerTrait) Configure(e *Environment) (bool, *TraitCondition, error) { + if e.Integration == nil { + return false, nil, nil + } + if !pointer.BoolDeref(t.Enabled, true) { + return false, NewIntegrationConditionUserDisabled(), nil } - return e.IntegrationInPhase(v1.IntegrationPhaseInitialization) || e.IntegrationInRunningPhases(), nil + return e.IntegrationInPhase(v1.IntegrationPhaseInitialization) || e.IntegrationInRunningPhases(), nil, nil } func (t *ownerTrait) Apply(e *Environment) error { diff --git a/pkg/trait/pdb.go b/pkg/trait/pdb.go index 50093faf11..94532a6986 100644 --- a/pkg/trait/pdb.go +++ b/pkg/trait/pdb.go @@ -40,25 +40,25 @@ func newPdbTrait() Trait { } } -func (t *pdbTrait) Configure(e *Environment) (bool, error) { +func (t *pdbTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if e.Integration == nil || !pointer.BoolDeref(t.Enabled, false) { - return false, nil + return false, nil, nil } strategy, err := e.DetermineControllerStrategy() if err != nil { - return false, fmt.Errorf("unable to determine the controller stratedy") + return false, nil, fmt.Errorf("unable to determine the controller strategy") } if strategy == ControllerStrategyCronJob { - return false, fmt.Errorf("poddisruptionbudget isn't supported with cron-job controller strategy") + return false, nil, fmt.Errorf("poddisruptionbudget isn't supported with cron-job controller strategy") } if t.MaxUnavailable != "" && t.MinAvailable != "" { - return false, fmt.Errorf("both minAvailable and maxUnavailable can't be set simultaneously") + return false, nil, fmt.Errorf("both minAvailable and maxUnavailable can't be set simultaneously") } - return e.IntegrationInRunningPhases(), nil + return e.IntegrationInRunningPhases(), nil, nil } func (t *pdbTrait) Apply(e *Environment) error { diff --git a/pkg/trait/pdb_test.go b/pkg/trait/pdb_test.go index 65fec620b5..17518c4f95 100644 --- a/pkg/trait/pdb_test.go +++ b/pkg/trait/pdb_test.go @@ -34,10 +34,11 @@ import ( func TestConfigurePdbTraitDoesSucceed(t *testing.T) { pdbTrait, environment, _ := createPdbTest() - configured, err := pdbTrait.Configure(environment) + configured, condition, err := pdbTrait.Configure(environment) assert.True(t, configured) assert.Nil(t, err) + assert.Nil(t, condition) } func TestConfigurePdbTraitDoesNotSucceed(t *testing.T) { @@ -45,9 +46,10 @@ func TestConfigurePdbTraitDoesNotSucceed(t *testing.T) { pdbTrait.MinAvailable = "1" pdbTrait.MaxUnavailable = "2" - configured, err := pdbTrait.Configure(environment) + configured, condition, err := pdbTrait.Configure(environment) assert.NotNil(t, err) assert.False(t, configured) + assert.Nil(t, condition) } func TestPdbIsCreatedWithoutParametersEnabled(t *testing.T) { diff --git a/pkg/trait/platform.go b/pkg/trait/platform.go index 3401d481a3..58e5455977 100644 --- a/pkg/trait/platform.go +++ b/pkg/trait/platform.go @@ -43,13 +43,13 @@ func newPlatformTrait() Trait { } } -func (t *platformTrait) Configure(e *Environment) (bool, error) { +func (t *platformTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if e.Integration == nil { - return false, nil + return false, nil, nil } if !e.IntegrationInPhase(v1.IntegrationPhaseNone, v1.IntegrationPhaseWaitingForPlatform) { - return false, nil + return false, nil, nil } if !pointer.BoolDeref(t.Auto, false) { @@ -57,11 +57,11 @@ func (t *platformTrait) Configure(e *Environment) (bool, error) { if t.CreateDefault == nil { // Calculate if the platform should be automatically created when missing. if ocp, err := openshift.IsOpenShift(t.Client); err != nil { - return false, err + return false, nil, err } else if ocp { t.CreateDefault = &ocp } else if addr, err := image.GetRegistryAddress(e.Ctx, t.Client); err != nil { - return false, err + return false, nil, err } else if addr != nil { t.CreateDefault = pointer.Bool(true) } @@ -73,7 +73,7 @@ func (t *platformTrait) Configure(e *Environment) (bool, error) { } } - return true, nil + return true, nil, nil } func (t *platformTrait) Apply(e *Environment) error { diff --git a/pkg/trait/platform_test.go b/pkg/trait/platform_test.go index 2907680a4c..7db06c3bd3 100644 --- a/pkg/trait/platform_test.go +++ b/pkg/trait/platform_test.go @@ -65,9 +65,10 @@ func TestPlatformTraitChangeStatus(t *testing.T) { trait.Client, err = test.NewFakeClient() assert.Nil(t, err) - enabled, err := trait.Configure(&e) + enabled, condition, err := trait.Configure(&e) assert.Nil(t, err) assert.True(t, enabled) + assert.Nil(t, condition) err = trait.Apply(&e) assert.Nil(t, err) @@ -99,9 +100,10 @@ func TestPlatformTraitCreatesDefaultPlatform(t *testing.T) { trait.Client, err = test.NewFakeClient() assert.Nil(t, err) - enabled, err := trait.Configure(&e) + enabled, condition, err := trait.Configure(&e) assert.Nil(t, err) assert.True(t, enabled) + assert.Nil(t, condition) err = trait.Apply(&e) assert.Nil(t, err) @@ -156,9 +158,10 @@ func TestPlatformTraitExisting(t *testing.T) { trait.Client, err = test.NewFakeClient(&existingPlatform) assert.Nil(t, err) - enabled, err := trait.Configure(&e) + enabled, condition, err := trait.Configure(&e) assert.Nil(t, err) assert.True(t, enabled) + assert.Nil(t, condition) err = trait.Apply(&e) assert.Nil(t, err) diff --git a/pkg/trait/pod.go b/pkg/trait/pod.go index aa440548d7..5233bd513a 100644 --- a/pkg/trait/pod.go +++ b/pkg/trait/pod.go @@ -44,16 +44,18 @@ func newPodTrait() Trait { } } -func (t *podTrait) Configure(e *Environment) (bool, error) { - if e.Integration == nil || !pointer.BoolDeref(t.Enabled, true) { - return false, nil +func (t *podTrait) Configure(e *Environment) (bool, *TraitCondition, error) { + if e.Integration == nil { + return false, nil, nil + } + if !pointer.BoolDeref(t.Enabled, true) { + return false, NewIntegrationConditionUserDisabled(), nil } - if e.Integration.Spec.PodTemplate == nil { - return false, nil + return false, nil, nil } - return e.IntegrationInRunningPhases(), nil + return e.IntegrationInRunningPhases(), nil, nil } func (t *podTrait) Apply(e *Environment) error { diff --git a/pkg/trait/pod_test.go b/pkg/trait/pod_test.go index 0ecef59c30..5a994cb508 100755 --- a/pkg/trait/pod_test.go +++ b/pkg/trait/pod_test.go @@ -36,14 +36,16 @@ import ( func TestConfigurePodTraitDoesSucceed(t *testing.T) { trait, environment, _ := createPodTest("") - configured, err := trait.Configure(environment) + configured, condition, err := trait.Configure(environment) assert.True(t, configured) + assert.Nil(t, condition) assert.Nil(t, err) - configured, err = trait.Configure(environment) + configured, condition, err = trait.Configure(environment) assert.True(t, configured) + assert.Nil(t, condition) assert.Nil(t, err) } @@ -203,8 +205,9 @@ func testPodTemplateSpec(t *testing.T, template string) corev1.PodTemplateSpec { trait, environment, _ := createPodTest(template) - _, err := trait.Configure(environment) + _, condition, err := trait.Configure(environment) assert.Nil(t, err) + assert.Nil(t, condition) err = trait.Apply(environment) assert.Nil(t, err) diff --git a/pkg/trait/prometheus.go b/pkg/trait/prometheus.go index 620b32293e..e0870a0417 100644 --- a/pkg/trait/prometheus.go +++ b/pkg/trait/prometheus.go @@ -45,12 +45,12 @@ func newPrometheusTrait() Trait { } } -func (t *prometheusTrait) Configure(e *Environment) (bool, error) { +func (t *prometheusTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if e.Integration == nil || !pointer.BoolDeref(t.Enabled, false) { - return false, nil + return false, nil, nil } - return e.IntegrationInPhase(v1.IntegrationPhaseInitialization) || e.IntegrationInRunningPhases(), nil + return e.IntegrationInPhase(v1.IntegrationPhaseInitialization) || e.IntegrationInRunningPhases(), nil, nil } func (t *prometheusTrait) Apply(e *Environment) error { diff --git a/pkg/trait/prometheus_test.go b/pkg/trait/prometheus_test.go index 8fe005233e..71c097f3f5 100644 --- a/pkg/trait/prometheus_test.go +++ b/pkg/trait/prometheus_test.go @@ -36,20 +36,22 @@ import ( func TestConfigurePrometheusTraitInRightPhaseDoesSucceed(t *testing.T) { trait, environment := createNominalPrometheusTest() - configured, err := trait.Configure(environment) + configured, condition, err := trait.Configure(environment) assert.Nil(t, err) assert.True(t, configured) + assert.Nil(t, condition) } func TestConfigurePrometheusTraitInWrongPhaseDoesNotSucceed(t *testing.T) { trait, environment := createNominalPrometheusTest() environment.Integration.Status.Phase = v1.IntegrationPhaseBuildingKit - configured, err := trait.Configure(environment) + configured, condition, err := trait.Configure(environment) assert.Nil(t, err) assert.False(t, configured) + assert.Nil(t, condition) } func TestApplyNominalPrometheusTraitDoesSucceed(t *testing.T) { diff --git a/pkg/trait/pull_secret.go b/pkg/trait/pull_secret.go index 640d9a3416..829a7920fc 100644 --- a/pkg/trait/pull_secret.go +++ b/pkg/trait/pull_secret.go @@ -44,13 +44,15 @@ func newPullSecretTrait() Trait { } } -func (t *pullSecretTrait) Configure(e *Environment) (bool, error) { - if e.Integration == nil || !pointer.BoolDeref(t.Enabled, true) { - return false, nil +func (t *pullSecretTrait) Configure(e *Environment) (bool, *TraitCondition, error) { + if e.Integration == nil { + return false, nil, nil + } + if !pointer.BoolDeref(t.Enabled, true) { + return false, NewIntegrationConditionUserDisabled(), nil } - if !e.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil } if pointer.BoolDeref(t.Auto, true) { @@ -60,7 +62,7 @@ func (t *pullSecretTrait) Configure(e *Environment) (bool, error) { key := ctrl.ObjectKey{Namespace: e.Platform.Namespace, Name: secret} obj := corev1.Secret{} if err := t.Client.Get(e.Ctx, key, &obj); err != nil { - return false, err + return false, nil, err } if obj.Type == corev1.SecretTypeDockerConfigJson { t.SecretName = secret @@ -73,7 +75,7 @@ func (t *pullSecretTrait) Configure(e *Environment) (bool, error) { var err error isOpenShift, err = openshift.IsOpenShift(t.Client) if err != nil { - return false, err + return false, nil, err } } isOperatorGlobal := platform.IsCurrentOperatorGlobal() @@ -83,7 +85,7 @@ func (t *pullSecretTrait) Configure(e *Environment) (bool, error) { } } - return t.SecretName != "" || pointer.BoolDeref(t.ImagePullerDelegation, false), nil + return t.SecretName != "" || pointer.BoolDeref(t.ImagePullerDelegation, false), nil, nil } func (t *pullSecretTrait) Apply(e *Environment) error { diff --git a/pkg/trait/pull_secret_test.go b/pkg/trait/pull_secret_test.go index 4eddbbccb0..ded0281d59 100644 --- a/pkg/trait/pull_secret_test.go +++ b/pkg/trait/pull_secret_test.go @@ -40,9 +40,10 @@ func TestPullSecret(t *testing.T) { trait, _ := newPullSecretTrait().(*pullSecretTrait) trait.SecretName = "xxxy" - enabled, err := trait.Configure(e) + enabled, condition, err := trait.Configure(e) assert.Nil(t, err) assert.True(t, enabled) + assert.Nil(t, condition) err = trait.Apply(e) assert.Nil(t, err) @@ -54,9 +55,10 @@ func TestPullSecretDoesNothingWhenNotSetOnPlatform(t *testing.T) { e.Platform = &v1.IntegrationPlatform{} trait := newPullSecretTrait() - enabled, err := trait.Configure(e) + enabled, condition, err := trait.Configure(e) assert.Nil(t, err) assert.False(t, enabled) + assert.Nil(t, condition) } func TestPullSecretAuto(t *testing.T) { @@ -64,9 +66,10 @@ func TestPullSecretAuto(t *testing.T) { trait, _ := newPullSecretTrait().(*pullSecretTrait) trait.Auto = pointer.Bool(false) - enabled, err := trait.Configure(e) + enabled, condition, err := trait.Configure(e) assert.Nil(t, err) assert.False(t, enabled) + assert.Nil(t, condition) } func TestPullSecretImagePullerDelegation(t *testing.T) { @@ -75,9 +78,10 @@ func TestPullSecretImagePullerDelegation(t *testing.T) { trait, _ := newPullSecretTrait().(*pullSecretTrait) trait.Auto = pointer.Bool(false) trait.ImagePullerDelegation = pointer.Bool(true) - enabled, err := trait.Configure(e) + enabled, condition, err := trait.Configure(e) assert.Nil(t, err) assert.True(t, enabled) + assert.Nil(t, condition) assert.True(t, *trait.ImagePullerDelegation) err = trait.Apply(e) diff --git a/pkg/trait/quarkus.go b/pkg/trait/quarkus.go index fbe0618e1f..3127c679f3 100644 --- a/pkg/trait/quarkus.go +++ b/pkg/trait/quarkus.go @@ -140,18 +140,19 @@ func (t *quarkusTrait) Matches(trait Trait) bool { return true } -func (t *quarkusTrait) Configure(e *Environment) (bool, error) { - t.adaptDeprecatedFields() +func (t *quarkusTrait) Configure(e *Environment) (bool, *TraitCondition, error) { + condition := t.adaptDeprecatedFields() return e.IntegrationInPhase(v1.IntegrationPhaseBuildingKit) || e.IntegrationKitInPhase(v1.IntegrationKitPhaseBuildSubmitted) || e.IntegrationKitInPhase(v1.IntegrationKitPhaseReady) && e.IntegrationInRunningPhases(), - nil + condition, nil } -func (t *quarkusTrait) adaptDeprecatedFields() { +func (t *quarkusTrait) adaptDeprecatedFields() *TraitCondition { if t.PackageTypes != nil { - t.L.Info("The package-type parameter is deprecated and may be removed in future releases. Make sure to use mode parameter instead.") + message := "The package-type parameter is deprecated and may be removed in future releases. Make sure to use mode parameter instead." + t.L.Info(message) for _, pt := range t.PackageTypes { if pt == traitv1.NativePackageType { t.Modes = append(t.Modes, traitv1.NativeQuarkusMode) @@ -161,7 +162,10 @@ func (t *quarkusTrait) adaptDeprecatedFields() { t.Modes = append(t.Modes, traitv1.JvmQuarkusMode) } } + return NewIntegrationCondition(v1.IntegrationConditionTraitInfo, corev1.ConditionTrue, traitConfigurationMessage, message) } + + return nil } func (t *quarkusTrait) Apply(e *Environment) error { diff --git a/pkg/trait/quarkus_test.go b/pkg/trait/quarkus_test.go index e4a48d791e..4b9312c0be 100644 --- a/pkg/trait/quarkus_test.go +++ b/pkg/trait/quarkus_test.go @@ -32,10 +32,11 @@ func TestConfigureQuarkusTraitBuildSubmitted(t *testing.T) { quarkusTrait, environment := createNominalQuarkusTest() environment.IntegrationKit.Status.Phase = v1.IntegrationKitPhaseBuildSubmitted - configured, err := quarkusTrait.Configure(environment) + configured, condition, err := quarkusTrait.Configure(environment) assert.True(t, configured) assert.Nil(t, err) + assert.Nil(t, condition) err = quarkusTrait.Apply(environment) assert.Nil(t, err) @@ -53,9 +54,10 @@ func TestApplyQuarkusTraitDefaultKitLayout(t *testing.T) { quarkusTrait, environment := createNominalQuarkusTest() environment.Integration.Status.Phase = v1.IntegrationPhaseBuildingKit - configured, err := quarkusTrait.Configure(environment) + configured, condition, err := quarkusTrait.Configure(environment) assert.True(t, configured) assert.Nil(t, err) + assert.Nil(t, condition) err = quarkusTrait.Apply(environment) assert.Nil(t, err) @@ -69,9 +71,10 @@ func TestApplyQuarkusTraitAnnotationKitConfiguration(t *testing.T) { v1.SetAnnotation(&environment.Integration.ObjectMeta, v1.TraitAnnotationPrefix+"quarkus.foo", "camel-k") - configured, err := quarkusTrait.Configure(environment) + configured, condition, err := quarkusTrait.Configure(environment) assert.True(t, configured) assert.Nil(t, err) + assert.Nil(t, condition) err = quarkusTrait.Apply(environment) assert.Nil(t, err) diff --git a/pkg/trait/registry.go b/pkg/trait/registry.go index 27aed1355c..daa1bb8ffb 100644 --- a/pkg/trait/registry.go +++ b/pkg/trait/registry.go @@ -58,13 +58,13 @@ func (t *registryTrait) InfluencesBuild(this, prev map[string]interface{}) bool return true } -func (t *registryTrait) Configure(e *Environment) (bool, error) { +func (t *registryTrait) Configure(e *Environment) (bool, *TraitCondition, error) { // disabled by default if e.IntegrationKit == nil || !pointer.BoolDeref(t.Enabled, false) { - return false, nil + return false, nil, nil } - return e.IntegrationKitInPhase(v1.IntegrationKitPhaseBuildSubmitted), nil + return e.IntegrationKitInPhase(v1.IntegrationKitPhaseBuildSubmitted), nil, nil } func (t *registryTrait) Apply(e *Environment) error { diff --git a/pkg/trait/route.go b/pkg/trait/route.go index 165e345b6c..101f5c326a 100644 --- a/pkg/trait/route.go +++ b/pkg/trait/route.go @@ -54,39 +54,28 @@ func (t *routeTrait) IsAllowedInProfile(profile v1.TraitProfile) bool { return profile.Equal(v1.TraitProfileOpenShift) } -func (t *routeTrait) Configure(e *Environment) (bool, error) { - if e.Integration == nil || !pointer.BoolDeref(t.Enabled, true) { - if e.Integration != nil { - e.Integration.Status.SetCondition( - v1.IntegrationConditionExposureAvailable, - corev1.ConditionFalse, - v1.IntegrationConditionRouteNotAvailableReason, - "explicitly disabled", - ) - } - - return false, nil +func (t *routeTrait) Configure(e *Environment) (bool, *TraitCondition, error) { + if e.Integration == nil { + return false, nil, nil + } + if !pointer.BoolDeref(t.Enabled, true) { + return false, NewIntegrationCondition( + v1.IntegrationConditionExposureAvailable, + corev1.ConditionFalse, + v1.IntegrationConditionRouteNotAvailableReason, + "explicitly disabled", + ), nil } - if !e.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil } t.service = e.Resources.GetUserServiceForIntegration(e.Integration) if t.service == nil { - if e.Integration != nil { - e.Integration.Status.SetCondition( - v1.IntegrationConditionExposureAvailable, - corev1.ConditionFalse, - v1.IntegrationConditionRouteNotAvailableReason, - "no target service found", - ) - } - - return false, nil + return false, nil, nil } - return true, nil + return true, nil, nil } func (t *routeTrait) Apply(e *Environment) error { diff --git a/pkg/trait/route_test.go b/pkg/trait/route_test.go index ae8d335a93..a4ecdc2dd8 100644 --- a/pkg/trait/route_test.go +++ b/pkg/trait/route_test.go @@ -205,9 +205,9 @@ func TestRoute_Default(t *testing.T) { environment := createTestRouteEnvironment(t, name) traitsCatalog := environment.Catalog - err := traitsCatalog.apply(environment) - + conditions, err := traitsCatalog.apply(environment) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("container")) assert.NotNil(t, environment.GetTrait("route")) @@ -233,10 +233,17 @@ func TestRoute_Disabled(t *testing.T) { }, } + expectedCondition := NewIntegrationCondition( + v1.IntegrationConditionExposureAvailable, + corev1.ConditionFalse, + "route trait configuration", + "explicitly disabled", + ) traitsCatalog := environment.Catalog - err := traitsCatalog.apply(environment) - + conditions, err := traitsCatalog.apply(environment) assert.Nil(t, err) + assert.Len(t, conditions, 1) + assert.Contains(t, conditions, expectedCondition) assert.NotEmpty(t, environment.ExecutedTraits) assert.Nil(t, environment.GetTrait("route")) @@ -256,9 +263,10 @@ func TestRoute_Configure_IntegrationKitOnly(t *testing.T) { enabled := false routeTrait.Enabled = &enabled - result, err := routeTrait.Configure(environment) + result, condition, err := routeTrait.Configure(environment) assert.False(t, result) assert.Nil(t, err) + assert.Nil(t, condition) } func TestRoute_Host(t *testing.T) { @@ -272,9 +280,10 @@ func TestRoute_Host(t *testing.T) { }, } - err := traitsCatalog.apply(environment) + conditions, err := traitsCatalog.apply(environment) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("route")) @@ -302,9 +311,10 @@ func TestRoute_TLS_From_Secret_reencrypt(t *testing.T) { TLSDestinationCACertificateSecret: tlsMultipleSecretsName + "/" + tlsMultipleSecretsCert3Key, }, } - err := traitsCatalog.apply(environment) + conditions, err := traitsCatalog.apply(environment) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("route")) @@ -337,8 +347,8 @@ func TestRoute_TLS_wrong_secret(t *testing.T) { TLSDestinationCACertificateSecret: "404", }, } - err := traitsCatalog.apply(environment) - + conditions, err := traitsCatalog.apply(environment) + assert.Empty(t, conditions) // there must be errors as the trait has wrong configuration assert.NotNil(t, err) assert.Nil(t, environment.GetTrait("route")) @@ -365,8 +375,8 @@ func TestRoute_TLS_secret_wrong_key(t *testing.T) { TLSCACertificateSecret: tlsMultipleSecretsName + "/foo", }, } - err := traitsCatalog.apply(environment) - + conditions, err := traitsCatalog.apply(environment) + assert.Empty(t, conditions) // there must be errors as the trait has wrong configuration assert.NotNil(t, err) assert.Nil(t, environment.GetTrait("route")) @@ -393,8 +403,8 @@ func TestRoute_TLS_secret_missing_key(t *testing.T) { TLSCACertificateSecret: tlsMultipleSecretsName, }, } - err := traitsCatalog.apply(environment) - + conditions, err := traitsCatalog.apply(environment) + assert.Empty(t, conditions) // there must be errors as the trait has wrong configuration assert.NotNil(t, err) assert.Nil(t, environment.GetTrait("route")) @@ -422,9 +432,9 @@ func TestRoute_TLS_reencrypt(t *testing.T) { TLSDestinationCACertificate: destinationCaCert, }, } - err := traitsCatalog.apply(environment) - + conditions, err := traitsCatalog.apply(environment) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("route")) @@ -456,9 +466,9 @@ func TestRoute_TLS_edge(t *testing.T) { TLSCACertificate: caCert, }, } - err := traitsCatalog.apply(environment) - + conditions, err := traitsCatalog.apply(environment) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("route")) @@ -488,9 +498,9 @@ func TestRoute_TLS_passthrough(t *testing.T) { TLSInsecureEdgeTerminationPolicy: string(routev1.InsecureEdgeTerminationPolicyAllow), }, } - err := traitsCatalog.apply(environment) - + conditions, err := traitsCatalog.apply(environment) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("route")) @@ -518,9 +528,9 @@ func TestRoute_WithCustomServicePort(t *testing.T) { } traitsCatalog := environment.Catalog - err := traitsCatalog.apply(environment) - + conditions, err := traitsCatalog.apply(environment) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("container")) assert.NotNil(t, environment.GetTrait("route")) @@ -552,9 +562,9 @@ func TestRouteAnnotation(t *testing.T) { } traitsCatalog := environment.Catalog - err := traitsCatalog.apply(environment) - + conditions, err := traitsCatalog.apply(environment) assert.Nil(t, err) + assert.Empty(t, conditions) route := environment.Resources.GetRoute(func(r *routev1.Route) bool { return r.ObjectMeta.Name == name diff --git a/pkg/trait/service.go b/pkg/trait/service.go index 2ae926a780..8f431f036e 100644 --- a/pkg/trait/service.go +++ b/pkg/trait/service.go @@ -43,18 +43,17 @@ func newServiceTrait() Trait { } } -func (t *serviceTrait) Configure(e *Environment) (bool, error) { - if e.Integration == nil || !pointer.BoolDeref(t.Enabled, true) { - if e.Integration != nil { - e.Integration.Status.SetCondition( - v1.IntegrationConditionServiceAvailable, - corev1.ConditionFalse, - v1.IntegrationConditionServiceNotAvailableReason, - "explicitly disabled", - ) - } - - return false, nil +func (t *serviceTrait) Configure(e *Environment) (bool, *TraitCondition, error) { + if e.Integration == nil { + return false, nil, nil + } + if !pointer.BoolDeref(t.Enabled, true) { + return false, NewIntegrationCondition( + v1.IntegrationConditionServiceAvailable, + corev1.ConditionFalse, + v1.IntegrationConditionServiceNotAvailableReason, + "explicitly disabled", + ), nil } // in case the knative-service and service trait are enabled, the knative-service has priority @@ -62,43 +61,36 @@ func (t *serviceTrait) Configure(e *Environment) (bool, error) { if e.GetTrait(knativeServiceTraitID) != nil { knativeServiceTrait, _ := e.GetTrait(knativeServiceTraitID).(*knativeServiceTrait) if pointer.BoolDeref(knativeServiceTrait.Enabled, true) { - return false, nil + return false, newIntegrationConditionPlatformDisabledWithReason("knative-service trait has priority over this trait"), nil } } if !e.IntegrationInRunningPhases() { - return false, nil + return false, nil, nil } if pointer.BoolDeref(t.Auto, true) { sources, err := kubernetes.ResolveIntegrationSources(e.Ctx, t.Client, e.Integration, e.Resources) + var condition *TraitCondition if err != nil { - e.Integration.Status.SetCondition( + condition = NewIntegrationCondition( v1.IntegrationConditionServiceAvailable, corev1.ConditionFalse, v1.IntegrationConditionServiceNotAvailableReason, err.Error(), ) - - return false, err + return false, condition, err } meta, err := metadata.ExtractAll(e.CamelCatalog, sources) if err != nil { - return false, err + return false, nil, err } if !meta.ExposesHTTPServices { - e.Integration.Status.SetCondition( - v1.IntegrationConditionServiceAvailable, - corev1.ConditionFalse, - v1.IntegrationConditionServiceNotAvailableReason, - "no http service required", - ) - - return false, nil + return false, nil, nil } } - return true, nil + return true, nil, nil } func (t *serviceTrait) Apply(e *Environment) error { diff --git a/pkg/trait/service_binding.go b/pkg/trait/service_binding.go index f586851663..dabea74816 100644 --- a/pkg/trait/service_binding.go +++ b/pkg/trait/service_binding.go @@ -48,16 +48,18 @@ func newServiceBindingTrait() Trait { } } -func (t *serviceBindingTrait) Configure(e *Environment) (bool, error) { - if e.Integration == nil || !pointer.BoolDeref(t.Enabled, true) { - return false, nil +func (t *serviceBindingTrait) Configure(e *Environment) (bool, *TraitCondition, error) { + if e.Integration == nil { + return false, nil, nil + } + if !pointer.BoolDeref(t.Enabled, true) { + return false, NewIntegrationConditionUserDisabled(), nil } - if len(t.Services) == 0 { - return false, nil + return false, nil, nil } - return e.IntegrationInPhase(v1.IntegrationPhaseInitialization) || e.IntegrationInRunningPhases(), nil + return e.IntegrationInPhase(v1.IntegrationPhaseInitialization) || e.IntegrationInRunningPhases(), nil, nil } func (t *serviceBindingTrait) Apply(e *Environment) error { diff --git a/pkg/trait/service_test.go b/pkg/trait/service_test.go index a1aefa7794..cbb856856d 100644 --- a/pkg/trait/service_test.go +++ b/pkg/trait/service_test.go @@ -108,9 +108,10 @@ func TestServiceWithDefaults(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - err = traitCatalog.apply(&environment) + conditions, err := traitCatalog.apply(&environment) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("deployment")) assert.NotNil(t, environment.GetTrait("service")) @@ -215,9 +216,10 @@ func TestService(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - err = traitCatalog.apply(&environment) + conditions, err := traitCatalog.apply(&environment) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("deployment")) assert.NotNil(t, environment.GetTrait("service")) @@ -302,9 +304,10 @@ func TestServiceWithCustomContainerName(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - err = traitCatalog.apply(&environment) + conditions, err := traitCatalog.apply(&environment) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("deployment")) assert.NotNil(t, environment.GetTrait("service")) @@ -393,9 +396,10 @@ func TestServiceWithNodePort(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - err = traitCatalog.apply(&environment) + conditions, err := traitCatalog.apply(&environment) assert.Nil(t, err) + assert.Empty(t, conditions) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait("deployment")) assert.NotNil(t, environment.GetTrait("service")) @@ -487,9 +491,24 @@ func TestServiceWithKnativeServiceEnabled(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - err = traitCatalog.apply(&environment) + deploymentCondition := NewIntegrationCondition( + v1.IntegrationConditionDeploymentAvailable, + corev1.ConditionFalse, + "deployment trait configuration", + "controller strategy: knative-service", + ) + serviceCondition := NewIntegrationCondition( + v1.IntegrationConditionTraitInfo, + corev1.ConditionTrue, + "service trait configuration", + "explicitly disabled by the platform: knative-service trait has priority over this trait", + ) + conditions, err := traitCatalog.apply(&environment) assert.Nil(t, err) + assert.Len(t, conditions, 2) + assert.Contains(t, conditions, deploymentCondition) + assert.Contains(t, conditions, serviceCondition) assert.NotEmpty(t, environment.ExecutedTraits) assert.Nil(t, environment.GetTrait(serviceTraitID)) assert.NotNil(t, environment.GetTrait(knativeServiceTraitID)) @@ -550,9 +569,24 @@ func TestServicesWithKnativeProfile(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - err = traitCatalog.apply(&environment) + deploymentCondition := NewIntegrationCondition( + v1.IntegrationConditionDeploymentAvailable, + corev1.ConditionFalse, + "deployment trait configuration", + "controller strategy: knative-service", + ) + serviceCondition := NewIntegrationCondition( + v1.IntegrationConditionTraitInfo, + corev1.ConditionTrue, + "service trait configuration", + "explicitly disabled by the platform: knative-service trait has priority over this trait", + ) + conditions, err := traitCatalog.apply(&environment) assert.Nil(t, err) + assert.Len(t, conditions, 2) + assert.Contains(t, conditions, deploymentCondition) + assert.Contains(t, conditions, serviceCondition) assert.NotEmpty(t, environment.ExecutedTraits) assert.Nil(t, environment.GetTrait(serviceTraitID)) assert.NotNil(t, environment.GetTrait(knativeServiceTraitID)) @@ -621,9 +655,17 @@ func TestServiceWithKnativeServiceDisabledInIntegrationPlatform(t *testing.T) { } environment.Platform.ResyncStatusFullConfig() - err = traitCatalog.apply(&environment) + expectedCondition := NewIntegrationCondition( + v1.IntegrationConditionKnativeServiceAvailable, + corev1.ConditionFalse, + "knative-service trait configuration", + "explicitly disabled", + ) + conditions, err := traitCatalog.apply(&environment) assert.Nil(t, err) + assert.Len(t, conditions, 1) + assert.Contains(t, conditions, expectedCondition) assert.NotEmpty(t, environment.ExecutedTraits) assert.NotNil(t, environment.GetTrait(serviceTraitID)) assert.Nil(t, environment.GetTrait(knativeServiceTraitID)) diff --git a/pkg/trait/toleration.go b/pkg/trait/toleration.go index 73303a293b..8d58e75efa 100644 --- a/pkg/trait/toleration.go +++ b/pkg/trait/toleration.go @@ -38,16 +38,16 @@ func newTolerationTrait() Trait { } } -func (t *tolerationTrait) Configure(e *Environment) (bool, error) { +func (t *tolerationTrait) Configure(e *Environment) (bool, *TraitCondition, error) { if e.Integration == nil || !pointer.BoolDeref(t.Enabled, false) { - return false, nil + return false, nil, nil } if len(t.Taints) == 0 { - return false, fmt.Errorf("no taint was provided") + return false, nil, fmt.Errorf("no taint was provided") } - return e.IntegrationInRunningPhases(), nil + return e.IntegrationInRunningPhases(), nil, nil } func (t *tolerationTrait) Apply(e *Environment) error { diff --git a/pkg/trait/toleration_test.go b/pkg/trait/toleration_test.go index 6b0cd66956..48b472113d 100644 --- a/pkg/trait/toleration_test.go +++ b/pkg/trait/toleration_test.go @@ -30,10 +30,11 @@ func TestConfigureTolerationTraitMissingTaint(t *testing.T) { environment, _ := createNominalDeploymentTraitTest() tolerationTrait := createNominalTolerationTrait() - success, err := tolerationTrait.Configure(environment) + success, condition, err := tolerationTrait.Configure(environment) assert.Equal(t, false, success) assert.NotNil(t, err) + assert.Nil(t, condition) } func TestApplyTolerationTraitMalformedTaint(t *testing.T) { diff --git a/pkg/trait/trait.go b/pkg/trait/trait.go index ec4a9ca3cf..33676616f1 100644 --- a/pkg/trait/trait.go +++ b/pkg/trait/trait.go @@ -55,7 +55,20 @@ func Apply(ctx context.Context, c client.Client, integration *v1.Integration, ki environment.Catalog = catalog // invoke the trait framework to determine the needed resources - if err := catalog.apply(environment); err != nil { + conditions, err := catalog.apply(environment) + // 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) } diff --git a/pkg/trait/trait_catalog.go b/pkg/trait/trait_catalog.go index 025a9cc989..dc11607092 100644 --- a/pkg/trait/trait_catalog.go +++ b/pkg/trait/trait_catalog.go @@ -85,9 +85,10 @@ func (c *Catalog) TraitsForProfile(profile v1.TraitProfile) []Trait { return res } -func (c *Catalog) apply(environment *Environment) error { +func (c *Catalog) apply(environment *Environment) ([]*TraitCondition, error) { + traitsConditions := []*TraitCondition{} if err := c.Configure(environment); err != nil { - return err + return traitsConditions, err } traits := c.traitsFor(environment) environment.ConfiguredTraits = traits @@ -100,15 +101,19 @@ func (c *Catalog) apply(environment *Environment) error { continue } applicable = true - enabled, err := trait.Configure(environment) + enabled, condition, err := trait.Configure(environment) + if condition != nil { + condition.message = fmt.Sprintf("%s trait configuration", trait.ID()) + traitsConditions = append(traitsConditions, condition) + } if err != nil { - return fmt.Errorf("%s trait configuration failed: %w", trait.ID(), err) + return traitsConditions, fmt.Errorf("%s trait configuration failed: %w", trait.ID(), err) } if enabled { err = trait.Apply(environment) if err != nil { - return fmt.Errorf("%s trait execution failed: %w", trait.ID(), err) + return traitsConditions, fmt.Errorf("%s trait execution failed: %w", trait.ID(), err) } environment.ExecutedTraits = append(environment.ExecutedTraits, trait) @@ -117,7 +122,7 @@ func (c *Catalog) apply(environment *Environment) error { for _, processor := range environment.PostStepProcessors { err := processor(environment) if err != nil { - return fmt.Errorf("%s trait executing post step action failed: %w", trait.ID(), err) + return traitsConditions, fmt.Errorf("%s trait executing post step action failed: %w", trait.ID(), err) } } } @@ -130,17 +135,17 @@ func (c *Catalog) apply(environment *Environment) error { c.L.Debugf("Applied traits: %s", strings.Join(traitIds, ",")) if !applicable && environment.PlatformInPhase(v1.IntegrationPlatformPhaseReady) { - return errors.New("no trait can be executed because of no ready platform found") + return traitsConditions, 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 fmt.Errorf("error executing post processor: %w", err) + return traitsConditions, fmt.Errorf("error executing post processor: %w", err) } } - return nil + return traitsConditions, nil } // GetTrait returns the trait with the given ID. diff --git a/pkg/trait/trait_condition_types.go b/pkg/trait/trait_condition_types.go new file mode 100644 index 0000000000..9b35647c99 --- /dev/null +++ b/pkg/trait/trait_condition_types.go @@ -0,0 +1,80 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package trait + +import ( + "fmt" + + corev1 "k8s.io/api/core/v1" + + v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" +) + +const ( + traitConfigurationMessage = "Trait configuration" + userDisabledMessage = "explicitly disabled by the user" + platformDisabledMessage = "explicitly disabled by the platform" +) + +// TraitCondition is used to get all information/warning about a trait configuration. +// It should either use an IntegrationConditionType or IntegrationKitConditionType. +type TraitCondition struct { + integrationConditionType v1.IntegrationConditionType + integrationKitConditionType v1.IntegrationKitConditionType + conditionStatus corev1.ConditionStatus + message string + reason string +} + +func NewIntegrationCondition(ict v1.IntegrationConditionType, cs corev1.ConditionStatus, message, reason string) *TraitCondition { + return &TraitCondition{ + integrationConditionType: ict, + conditionStatus: cs, + message: message, + reason: reason, + } +} + +func NewIntegrationConditionUserDisabled() *TraitCondition { + return NewIntegrationCondition(v1.IntegrationConditionTraitInfo, corev1.ConditionTrue, traitConfigurationMessage, userDisabledMessage) +} + +func newIntegrationConditionPlatformDisabled() *TraitCondition { + return NewIntegrationCondition(v1.IntegrationConditionTraitInfo, corev1.ConditionTrue, traitConfigurationMessage, platformDisabledMessage) +} + +func newIntegrationConditionPlatformDisabledWithReason(reason string) *TraitCondition { + return NewIntegrationCondition(v1.IntegrationConditionTraitInfo, corev1.ConditionTrue, traitConfigurationMessage, fmt.Sprintf("%s: %s", platformDisabledMessage, reason)) +} + +func newIntegrationKitCondition(ikct v1.IntegrationKitConditionType, cs corev1.ConditionStatus, message, reason string) *TraitCondition { + return &TraitCondition{ + integrationKitConditionType: ikct, + conditionStatus: cs, + message: message, + reason: reason, + } +} + +func (tc *TraitCondition) integrationCondition() (v1.IntegrationConditionType, corev1.ConditionStatus, string, string) { + return tc.integrationConditionType, tc.conditionStatus, tc.message, tc.reason +} + +func (tc *TraitCondition) integrationKitCondition() (v1.IntegrationKitConditionType, corev1.ConditionStatus, string, string) { + return tc.integrationKitConditionType, tc.conditionStatus, tc.message, tc.reason +} diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go index edefb85869..1acb07f5ae 100644 --- a/pkg/trait/trait_test.go +++ b/pkg/trait/trait_test.go @@ -485,7 +485,7 @@ func processTestEnv(t *testing.T, env *Environment) *kubernetes.Collection { t.Helper() catalog := NewTraitTestCatalog() - err := catalog.apply(env) + _, err := catalog.apply(env) assert.Nil(t, err) return env.Resources } diff --git a/pkg/trait/trait_types.go b/pkg/trait/trait_types.go index 9471ddb666..9d838a6886 100644 --- a/pkg/trait/trait_types.go +++ b/pkg/trait/trait_types.go @@ -61,7 +61,7 @@ type Trait interface { client.Injectable // Configure the trait - Configure(environment *Environment) (bool, error) + Configure(environment *Environment) (bool, *TraitCondition, error) // Apply executes a customization of the Environment Apply(environment *Environment) error diff --git a/pkg/trait/util.go b/pkg/trait/util.go index e047576eb3..59445247c8 100644 --- a/pkg/trait/util.go +++ b/pkg/trait/util.go @@ -47,13 +47,6 @@ func ptrFrom[T any](value T) *T { return &value } -func ptrDerefOr[T any](value *T, def T) T { - if value != nil { - return *value - } - return def -} - type Options map[string]map[string]interface{} func (u Options) Get(id string) (map[string]interface{}, bool) { From 86a7e5151cfe406ef5e8123007f80bc8b770db93 Mon Sep 17 00:00:00 2001 From: Pasquale Congiusti Date: Tue, 24 Oct 2023 13:22:30 +0200 Subject: [PATCH 2/2] doc(trait): TraitCondition explaination --- addons/master/master.go | 8 +++----- docs/modules/ROOT/pages/architecture/traits.adoc | 4 ++-- pkg/trait/trait_condition_types.go | 13 ------------- 3 files changed, 5 insertions(+), 20 deletions(-) diff --git a/addons/master/master.go b/addons/master/master.go index c60733341a..e2002e806d 100644 --- a/addons/master/master.go +++ b/addons/master/master.go @@ -91,10 +91,6 @@ func (t *masterTrait) Configure(e *trait.Environment) (bool, *trait.TraitConditi if !e.IntegrationInPhase(v1.IntegrationPhaseInitialization) && !e.IntegrationInRunningPhases() { return false, nil, nil } - if !pointer.BoolDeref(t.Enabled, false) { - return false, trait.NewIntegrationConditionUserDisabled(), nil - } - if pointer.BoolDeref(t.Auto, true) { // Check if the master component has been used sources, err := kubernetes.ResolveIntegrationSources(e.Ctx, t.Client, e.Integration, e.Resources) @@ -115,7 +111,9 @@ func (t *masterTrait) Configure(e *trait.Environment) (bool, *trait.TraitConditi } } } - + if !pointer.BoolDeref(t.Enabled, false) { + return false, trait.NewIntegrationConditionUserDisabled(), nil + } if t.IncludeDelegateDependencies == nil || *t.IncludeDelegateDependencies { t.delegateDependencies = findAdditionalDependencies(e, meta) } diff --git a/docs/modules/ROOT/pages/architecture/traits.adoc b/docs/modules/ROOT/pages/architecture/traits.adoc index 3457b3b4ec..fc56b11928 100644 --- a/docs/modules/ROOT/pages/architecture/traits.adoc +++ b/docs/modules/ROOT/pages/architecture/traits.adoc @@ -40,7 +40,7 @@ type Trait interface { Identifiable client.Injectable InjectContext(context.Context) - Configure(environment *Environment) (bool, error) + Configure(environment *Environment) (bool, TraitCondition, error) Apply(environment *Environment) error InfluencesKit() bool InfluencesBuild(this, prev map[string]interface{}) bool @@ -51,7 +51,7 @@ type Trait interface { } ---- -Each trait will implement this interface. The most important methods that will be invoked by the xref:architecture/operator.adoc[Operator] are `Configure()` and `Apply()`. Basically, the `Configure()` method will set those inputs aforementioned (each trait has its own). The method is in charge to verify also the correctness of those expected parameters, where it makes sense (i.e., a well expected `Kubernetes` resource name). +Each trait will implement this interface. The most important methods that will be invoked by the xref:architecture/operator.adoc[Operator] are `Configure()` and `Apply()`. Basically, the `Configure()` method will set those inputs aforementioned (each trait has its own). The method is in charge to verify also the correctness of those expected parameters, where it makes sense (i.e., a well expected `Kubernetes` resource name). The function can return a `TraitCondition` object containing any informative or warning condition to be attached to the resulting Integration (for instance, traits forcefully altered by the platform, or deprecation notices). Once configured, the `Apply()` method will be called along the build or initialization phase in order to do the business logic expected for it. The `environment` variable will give you all the below resources you will need to perform your operation (ie, the `Integration` or any Kubernetes resource attached to it). You can have a deeper look at the `https://github.com/apache/camel-k/blob/main/pkg/trait/trait_types.go#L188[Environment]` struct. diff --git a/pkg/trait/trait_condition_types.go b/pkg/trait/trait_condition_types.go index 9b35647c99..6acf12dcf3 100644 --- a/pkg/trait/trait_condition_types.go +++ b/pkg/trait/trait_condition_types.go @@ -54,23 +54,10 @@ func NewIntegrationConditionUserDisabled() *TraitCondition { return NewIntegrationCondition(v1.IntegrationConditionTraitInfo, corev1.ConditionTrue, traitConfigurationMessage, userDisabledMessage) } -func newIntegrationConditionPlatformDisabled() *TraitCondition { - return NewIntegrationCondition(v1.IntegrationConditionTraitInfo, corev1.ConditionTrue, traitConfigurationMessage, platformDisabledMessage) -} - func newIntegrationConditionPlatformDisabledWithReason(reason string) *TraitCondition { return NewIntegrationCondition(v1.IntegrationConditionTraitInfo, corev1.ConditionTrue, traitConfigurationMessage, fmt.Sprintf("%s: %s", platformDisabledMessage, reason)) } -func newIntegrationKitCondition(ikct v1.IntegrationKitConditionType, cs corev1.ConditionStatus, message, reason string) *TraitCondition { - return &TraitCondition{ - integrationKitConditionType: ikct, - conditionStatus: cs, - message: message, - reason: reason, - } -} - func (tc *TraitCondition) integrationCondition() (v1.IntegrationConditionType, corev1.ConditionStatus, string, string) { return tc.integrationConditionType, tc.conditionStatus, tc.message, tc.reason }