This is an automated email from the ASF dual-hosted git repository.
pcongiusti pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git
The following commit(s) were added to refs/heads/main by this push:
new 226eef200 feat(traits): master refactoring
226eef200 is described below
commit 226eef20024b3e675972b2f1b22dc4228d0ec1db
Author: Pasquale Congiusti <[email protected]>
AuthorDate: Fri Mar 22 09:58:29 2024 +0100
feat(traits): master refactoring
Closes #4048
---
addons/master/master.go | 166 +++++++++++++++++++++++---------------
addons/master/master_test.go | 186 +++++++++++++++++++++++++++++++++++++++++++
pkg/trait/builder.go | 10 ++-
3 files changed, 297 insertions(+), 65 deletions(-)
diff --git a/addons/master/master.go b/addons/master/master.go
index 476f68bf8..692bfb2c4 100644
--- a/addons/master/master.go
+++ b/addons/master/master.go
@@ -26,6 +26,7 @@ import (
v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait"
+ "github.com/apache/camel-k/v2/pkg/client"
"github.com/apache/camel-k/v2/pkg/metadata"
"github.com/apache/camel-k/v2/pkg/resources"
"github.com/apache/camel-k/v2/pkg/trait"
@@ -85,10 +86,13 @@ var (
)
func (t *masterTrait) Configure(e *trait.Environment) (bool,
*trait.TraitCondition, error) {
- if e.Integration == nil || !pointer.BoolDeref(t.Enabled, true) {
+ if e.Integration == nil {
return false, nil, nil
}
- if !e.IntegrationInPhase(v1.IntegrationPhaseInitialization) &&
!e.IntegrationInRunningPhases() {
+ if !pointer.BoolDeref(t.Enabled, true) {
+ return false,
trait.NewIntegrationConditionUserDisabled(masterComponent), nil
+ }
+ if !e.IntegrationInPhase(v1.IntegrationPhaseInitialization,
v1.IntegrationPhaseBuildingKit) && !e.IntegrationInRunningPhases() {
return false, nil, nil
}
if pointer.BoolDeref(t.Auto, true) {
@@ -110,9 +114,10 @@ func (t *masterTrait) Configure(e *trait.Environment)
(bool, *trait.TraitConditi
t.Enabled = &enabled
}
}
- }
- if !pointer.BoolDeref(t.Enabled, false) {
- return false,
trait.NewIntegrationConditionUserDisabled("Master"), nil
+ // No master component, can skip the trait execution
+ if !pointer.BoolDeref(t.Enabled, false) {
+ return false, nil, nil
+ }
}
if t.IncludeDelegateDependencies == nil ||
*t.IncludeDelegateDependencies {
t.delegateDependencies = findAdditionalDependencies(e,
meta)
@@ -145,83 +150,81 @@ func (t *masterTrait) Configure(e *trait.Environment)
(bool, *trait.TraitConditi
}
}
- return pointer.BoolDeref(t.Enabled, true), nil, nil
+ return pointer.BoolDeref(t.Enabled, false), nil, nil
}
func (t *masterTrait) Apply(e *trait.Environment) error {
if e.IntegrationInPhase(v1.IntegrationPhaseInitialization) {
util.StringSliceUniqueAdd(&e.Integration.Status.Capabilities,
v1.CapabilityMaster)
-
// Master sub endpoints need to be added to the list of
dependencies
for _, dep := range t.delegateDependencies {
util.StringSliceUniqueAdd(&e.Integration.Status.Dependencies, dep)
}
-
} else if e.IntegrationInRunningPhases() {
- serviceAccount := e.Integration.Spec.ServiceAccountName
- if serviceAccount == "" {
- serviceAccount = "default"
- }
-
- templateData := struct {
- Namespace string
- Name string
- ServiceAccount string
- }{
- Namespace: e.Integration.Namespace,
- Name: fmt.Sprintf("%s-master",
e.Integration.Name),
- ServiceAccount: serviceAccount,
- }
-
- roleSuffix := leaseResourceType
- if t.ResourceType != nil {
- roleSuffix = *t.ResourceType
- }
- roleSuffix = strings.ToLower(roleSuffix)
-
- role, err := loadResource(e, fmt.Sprintf("master-role-%s.tmpl",
roleSuffix), templateData)
+ // Master trait requires the ServiceAccount certain privileges
+ privileges, err := t.prepareRBAC(e.Client,
e.Integration.Spec.ServiceAccountName, e.Integration.Name,
e.Integration.Namespace)
if err != nil {
return err
}
- roleBinding, err := loadResource(e, "master-role-binding.tmpl",
templateData)
- if err != nil {
- return err
+ // Add the RBAC privileges
+ e.Resources.AddAll(privileges)
+
+ if
e.CamelCatalog.Runtime.Capabilities["master"].RuntimeProperties != nil {
+ t.setCatalogConfiguration(e)
+ } else {
+ t.setCustomizerConfiguration(e)
}
+ }
- e.Resources.Add(role)
- e.Resources.Add(roleBinding)
+ return nil
+}
+// Deprecated: to be removed in future release in favor of func
setCatalogConfiguration().
+func (t *masterTrait) setCustomizerConfiguration(e *trait.Environment) {
+ e.Integration.Status.Configuration =
append(e.Integration.Status.Configuration,
+ v1.ConfigurationSpec{Type: "property", Value:
"customizer.master.enabled=true"},
+ )
+ if t.ResourceName != nil {
+ resourceName := t.ResourceName
e.Integration.Status.Configuration =
append(e.Integration.Status.Configuration,
- v1.ConfigurationSpec{Type: "property", Value:
"customizer.master.enabled=true"},
+ v1.ConfigurationSpec{Type: "property", Value:
fmt.Sprintf("customizer.master.kubernetesResourceName=%s", *resourceName)},
)
+ }
+ if t.ResourceType != nil {
+ e.Integration.Status.Configuration =
append(e.Integration.Status.Configuration,
+ v1.ConfigurationSpec{Type: "property", Value:
fmt.Sprintf("customizer.master.leaseResourceType=%s", *t.ResourceType)},
+ )
+ }
+ if t.LabelKey != nil {
+ e.Integration.Status.Configuration =
append(e.Integration.Status.Configuration,
+ v1.ConfigurationSpec{Type: "property", Value:
fmt.Sprintf("customizer.master.labelKey=%s", *t.LabelKey)},
+ )
+ }
+ if t.LabelValue != nil {
+ e.Integration.Status.Configuration =
append(e.Integration.Status.Configuration,
+ v1.ConfigurationSpec{Type: "property", Value:
fmt.Sprintf("customizer.master.labelValue=%s", *t.LabelValue)},
+ )
+ }
+}
- if t.ResourceName != nil {
- resourceName := t.ResourceName
- e.Integration.Status.Configuration =
append(e.Integration.Status.Configuration,
- v1.ConfigurationSpec{Type: "property", Value:
fmt.Sprintf("customizer.master.kubernetesResourceName=%s", *resourceName)},
- )
- }
-
- if t.ResourceType != nil {
- e.Integration.Status.Configuration =
append(e.Integration.Status.Configuration,
- v1.ConfigurationSpec{Type: "property", Value:
fmt.Sprintf("customizer.master.leaseResourceType=%s", *t.ResourceType)},
- )
- }
-
- if t.LabelKey != nil {
- e.Integration.Status.Configuration =
append(e.Integration.Status.Configuration,
- v1.ConfigurationSpec{Type: "property", Value:
fmt.Sprintf("customizer.master.labelKey=%s", *t.LabelKey)},
- )
- }
-
- if t.LabelValue != nil {
- e.Integration.Status.Configuration =
append(e.Integration.Status.Configuration,
- v1.ConfigurationSpec{Type: "property", Value:
fmt.Sprintf("customizer.master.labelValue=%s", *t.LabelValue)},
- )
- }
+func (t *masterTrait) setCatalogConfiguration(e *trait.Environment) {
+ if e.ApplicationProperties == nil {
+ e.ApplicationProperties = make(map[string]string)
+ }
+ if t.ResourceName != nil {
+ e.ApplicationProperties["camel.k.master.resourceName"] =
*t.ResourceName
+ }
+ if t.ResourceType != nil {
+ e.ApplicationProperties["camel.k.master.resourceType"] =
*t.ResourceType
+ }
+ if t.LabelKey != nil && t.LabelValue != nil {
+ e.ApplicationProperties["camel.k.master.labelKey"] = *t.LabelKey
+ e.ApplicationProperties["camel.k.master.labelValue"] =
*t.LabelValue
}
- return nil
+ for _, cp := range
e.CamelCatalog.Runtime.Capabilities["master"].RuntimeProperties {
+ e.ApplicationProperties[trait.CapabilityPropertyKey(cp.Key,
e.ApplicationProperties)] = cp.Value
+ }
}
func (t *masterTrait) canUseLeases(e *trait.Environment) (bool, error) {
@@ -246,14 +249,49 @@ func findAdditionalDependencies(e *trait.Environment,
meta metadata.IntegrationM
return dependencies
}
-func loadResource(e *trait.Environment, name string, params interface{})
(ctrl.Object, error) {
- data, err :=
resources.TemplateResource(fmt.Sprintf("/resources/addons/master/%s", name),
params)
+func loadResource(cli client.Client, name string, params interface{})
(ctrl.Object, error) {
+ data, err :=
resources.TemplateResource(fmt.Sprintf("resources/addons/master/%s", name),
params)
if err != nil {
return nil, err
}
- obj, err := kubernetes.LoadResourceFromYaml(e.Client.GetScheme(), data)
+ obj, err := kubernetes.LoadResourceFromYaml(cli.GetScheme(), data)
if err != nil {
return nil, err
}
return obj, nil
}
+
+func (t *masterTrait) prepareRBAC(cli client.Client, serviceAccount, itName,
itNamespace string) ([]ctrl.Object, error) {
+ objs := make([]ctrl.Object, 0, 2)
+ if serviceAccount == "" {
+ serviceAccount = "default"
+ }
+
+ templateData := struct {
+ Namespace string
+ Name string
+ ServiceAccount string
+ }{
+ Namespace: itNamespace,
+ Name: fmt.Sprintf("%s-master", itName),
+ ServiceAccount: serviceAccount,
+ }
+
+ roleSuffix := leaseResourceType
+ if t.ResourceType != nil {
+ roleSuffix = *t.ResourceType
+ }
+ roleSuffix = strings.ToLower(roleSuffix)
+
+ role, err := loadResource(cli, fmt.Sprintf("master-role-%s.tmpl",
roleSuffix), templateData)
+ if err != nil {
+ return nil, err
+ }
+ objs = append(objs, role)
+ roleBinding, err := loadResource(cli, "master-role-binding.tmpl",
templateData)
+ if err != nil {
+ return nil, err
+ }
+ objs = append(objs, roleBinding)
+ return objs, nil
+}
diff --git a/addons/master/master_test.go b/addons/master/master_test.go
new file mode 100644
index 000000000..c396af1db
--- /dev/null
+++ b/addons/master/master_test.go
@@ -0,0 +1,186 @@
+/*
+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 master
+
+import (
+ "testing"
+
+ v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
+ "github.com/apache/camel-k/v2/pkg/trait"
+ "github.com/apache/camel-k/v2/pkg/util/camel"
+ "github.com/apache/camel-k/v2/pkg/util/kubernetes"
+ "github.com/apache/camel-k/v2/pkg/util/test"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func TestMasterOn(t *testing.T) {
+ catalog, err := camel.DefaultCatalog()
+ require.NoError(t, err)
+
+ client, err := test.NewFakeClient()
+ require.NoError(t, err)
+ traitCatalog := trait.NewCatalog(nil)
+
+ environment := trait.Environment{
+ CamelCatalog: catalog,
+ Catalog: traitCatalog,
+ Client: client,
+ Integration: &v1.Integration{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ Namespace: "ns",
+ },
+ Status: v1.IntegrationStatus{
+ Phase: v1.IntegrationPhaseInitialization,
+ },
+ Spec: v1.IntegrationSpec{
+ Profile: v1.TraitProfileKnative,
+ Sources: []v1.SourceSpec{
+ {
+ DataSpec: v1.DataSpec{
+ Name: "Master.java",
+ Content:
`from("master:lock:timer:tick").to("log:test")`,
+ },
+ Language: v1.LanguageJavaSource,
+ },
+ },
+ Traits: v1.Traits{},
+ },
+ },
+ Platform: &v1.IntegrationPlatform{
+ Spec: v1.IntegrationPlatformSpec{
+ Cluster: v1.IntegrationPlatformClusterOpenShift,
+ Build: v1.IntegrationPlatformBuildSpec{
+ PublishStrategy:
v1.IntegrationPlatformBuildPublishStrategyS2I,
+ Registry:
v1.RegistrySpec{Address: "registry"},
+ RuntimeVersion:
catalog.Runtime.Version,
+ },
+ Profile: v1.TraitProfileKnative,
+ },
+ Status: v1.IntegrationPlatformStatus{
+ Phase: v1.IntegrationPlatformPhaseReady,
+ },
+ },
+ EnvVars: make([]corev1.EnvVar, 0),
+ ExecutedTraits: make([]trait.Trait, 0),
+ Resources: kubernetes.NewCollection(),
+ }
+ environment.Platform.ResyncStatusFullConfig()
+
+ mt := NewMasterTrait()
+ mt.InjectClient(client)
+ // Initialization phase
+ configured, conditions, err := mt.Configure(&environment)
+ require.NoError(t, err)
+ assert.Empty(t, conditions)
+ assert.True(t, configured)
+ err = mt.Apply(&environment)
+ require.NoError(t, err)
+ assert.Len(t, environment.Integration.Status.Capabilities, 1)
+ assert.Equal(t, "master",
environment.Integration.Status.Capabilities[0])
+ // Running phase
+ environment.Integration.Status.Phase = v1.IntegrationPhaseRunning
+ err = mt.Apply(&environment)
+ require.NoError(t, err)
+ assert.Equal(t, "test-lock",
environment.ApplicationProperties["camel.k.master.resourceName"])
+ assert.Equal(t, "ConfigMap",
environment.ApplicationProperties["camel.k.master.resourceType"])
+ assert.Equal(t, "camel.apache.org/integration",
environment.ApplicationProperties["camel.k.master.labelKey"])
+ assert.Equal(t, "test",
environment.ApplicationProperties["camel.k.master.labelValue"])
+ assert.Equal(t, "${camel.k.master.resourceName}",
environment.ApplicationProperties["quarkus.camel.cluster.kubernetes.resource-name"])
+ assert.Equal(t, "${camel.k.master.resourceType}",
environment.ApplicationProperties["quarkus.camel.cluster.kubernetes.resource-type"])
+ assert.Equal(t, "${camel.k.master.labelValue}",
environment.ApplicationProperties["quarkus.camel.cluster.kubernetes.labels.\"camel.apache.org/integration\""])
+ roles := 0
+ roleBindings := 0
+ for _, obj := range environment.Resources.Items() {
+ // make sure it contains a Role and a RoleBinding
+ if obj.GetObjectKind().GroupVersionKind().Kind == "Role" {
+ roles++
+ }
+ if obj.GetObjectKind().GroupVersionKind().Kind == "RoleBinding"
{
+ roleBindings++
+ }
+ }
+ assert.Equal(t, 1, roles)
+ assert.Equal(t, 1, roleBindings)
+}
+
+func TestMasterOff(t *testing.T) {
+ catalog, err := camel.DefaultCatalog()
+ require.NoError(t, err)
+
+ client, err := test.NewFakeClient()
+ require.NoError(t, err)
+ traitCatalog := trait.NewCatalog(nil)
+
+ environment := trait.Environment{
+ CamelCatalog: catalog,
+ Catalog: traitCatalog,
+ Client: client,
+ Integration: &v1.Integration{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ Namespace: "ns",
+ },
+ Status: v1.IntegrationStatus{
+ Phase: v1.IntegrationPhaseInitialization,
+ },
+ Spec: v1.IntegrationSpec{
+ Profile: v1.TraitProfileKnative,
+ Sources: []v1.SourceSpec{
+ {
+ DataSpec: v1.DataSpec{
+ Name:
"NonMaster.java",
+ Content:
`from("timer:tick").to("log:test")`,
+ },
+ Language: v1.LanguageJavaSource,
+ },
+ },
+ Traits: v1.Traits{},
+ },
+ },
+ Platform: &v1.IntegrationPlatform{
+ Spec: v1.IntegrationPlatformSpec{
+ Cluster: v1.IntegrationPlatformClusterOpenShift,
+ Build: v1.IntegrationPlatformBuildSpec{
+ PublishStrategy:
v1.IntegrationPlatformBuildPublishStrategyS2I,
+ Registry:
v1.RegistrySpec{Address: "registry"},
+ RuntimeVersion:
catalog.Runtime.Version,
+ },
+ Profile: v1.TraitProfileKnative,
+ },
+ Status: v1.IntegrationPlatformStatus{
+ Phase: v1.IntegrationPlatformPhaseReady,
+ },
+ },
+ EnvVars: make([]corev1.EnvVar, 0),
+ ExecutedTraits: make([]trait.Trait, 0),
+ Resources: kubernetes.NewCollection(),
+ }
+ environment.Platform.ResyncStatusFullConfig()
+
+ mt := NewMasterTrait()
+ mt.InjectClient(client)
+ // Initialization phase
+ configured, conditions, err := mt.Configure(&environment)
+ require.NoError(t, err)
+ assert.Empty(t, conditions)
+ assert.False(t, configured)
+}
diff --git a/pkg/trait/builder.go b/pkg/trait/builder.go
index 48ad9dab0..b30fa0cbe 100644
--- a/pkg/trait/builder.go
+++ b/pkg/trait/builder.go
@@ -384,7 +384,7 @@ func (t *builderTrait) builderTask(e *Environment, taskConf
*v1.BuildConfigurati
if task.Maven.Properties == nil {
task.Maven.Properties = make(map[string]string)
}
- // User provided Maven properties
+ // User provided build-time properties
if t.Properties != nil {
for _, v := range t.Properties {
key, value := property.SplitPropertyFileEntry(v)
@@ -396,6 +396,14 @@ func (t *builderTrait) builderTask(e *Environment,
taskConf *v1.BuildConfigurati
}
}
+ // Build time property required by master capability
+ if e.IntegrationKit.HasCapability("master") &&
e.CamelCatalog.Runtime.Capabilities["master"].BuildTimeProperties != nil {
+ task.Maven.Properties["camel.k.master.enabled"] = "true"
+ for _, cp := range
e.CamelCatalog.Runtime.Capabilities["master"].BuildTimeProperties {
+ task.Maven.Properties[CapabilityPropertyKey(cp.Key,
task.Maven.Properties)] = cp.Value
+ }
+ }
+
if e.Platform.Status.Build.PublishStrategy ==
v1.IntegrationPlatformBuildPublishStrategyJib {
profile, err :=
jib.JibMavenProfile(e.CamelCatalog.GetJibMavenPluginVersion(),
e.CamelCatalog.GetJibLayerFilterExtensionMavenVersion())
if err != nil {