This is an automated email from the ASF dual-hosted git repository.
squakez 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 212ceb295 fix(platform): load profile in Integration namespace
212ceb295 is described below
commit 212ceb2955056addfb192b85fa0c66136d9b4aa6
Author: Pasquale Congiusti <[email protected]>
AuthorDate: Sat Oct 17 11:57:57 2026 +0200
fix(platform): load profile in Integration namespace
Closes #6864
---
pkg/apis/camel/v1/common_types.go | 3 +
pkg/apis/camel/v1/common_types_support.go | 5 --
pkg/controller/integration/monitor.go | 4 +-
pkg/controller/integrationkit/build.go | 3 +
pkg/controller/pipe/monitor.go | 5 +-
pkg/platform/operator.go | 3 +-
pkg/platform/profile.go | 4 +-
pkg/platform/profile_test.go | 79 +++++++++++++++++++++-
pkg/trait/quarkus.go | 1 +
pkg/trait/quarkus_test.go | 16 +++++
pkg/util/digest/digest.go | 3 +-
pkg/util/kubernetes/client.go | 42 +++++++-----
.../kubernetes/client_test.go} | 58 +++++++++++-----
13 files changed, 176 insertions(+), 50 deletions(-)
diff --git a/pkg/apis/camel/v1/common_types.go
b/pkg/apis/camel/v1/common_types.go
index cd484d465..521641624 100644
--- a/pkg/apis/camel/v1/common_types.go
+++ b/pkg/apis/camel/v1/common_types.go
@@ -36,6 +36,9 @@ const (
PlatformSelectorAnnotation = "camel.apache.org/platform.id"
// IntegrationProfileAnnotation integration profile id annotation label.
IntegrationProfileAnnotation = "camel.apache.org/integration-profile.id"
+ // IntegrationProfileNamespaceAnnotation integration profile namespace
annotation label.
+ // NOTE: this is an internal annotation only valid for IntegrationKits
and Builds.
+ IntegrationProfileNamespaceAnnotation =
"camel.apache.org/integration-profile.namespace"
// IntegrationDontRunAfterBuildAnnotation -- .
IntegrationDontRunAfterBuildAnnotation =
"camel.apache.org/dont-run-after-build"
// IntegrationDontRunAfterBuildAnnotationTrueValue -- .
diff --git a/pkg/apis/camel/v1/common_types_support.go
b/pkg/apis/camel/v1/common_types_support.go
index bfee3e035..88004ff57 100644
--- a/pkg/apis/camel/v1/common_types_support.go
+++ b/pkg/apis/camel/v1/common_types_support.go
@@ -171,11 +171,6 @@ func GetOperatorIDAnnotation(obj metav1.Object) string {
return GetAnnotation(OperatorIDAnnotation, obj)
}
-// GetIntegrationProfileAnnotation to safely get the integration profile
annotation value.
-func GetIntegrationProfileAnnotation(obj metav1.Object) string {
- return GetAnnotation(IntegrationProfileAnnotation, obj)
-}
-
// GetAnnotation safely get the annotation value.
func GetAnnotation(name string, obj metav1.Object) string {
if obj == nil || obj.GetAnnotations() == nil {
diff --git a/pkg/controller/integration/monitor.go
b/pkg/controller/integration/monitor.go
index ad7ff3c99..48f7ac63b 100644
--- a/pkg/controller/integration/monitor.go
+++ b/pkg/controller/integration/monitor.go
@@ -301,8 +301,8 @@ func isIntegrationKitResetRequired(integration
*v1.Integration, kit *v1.Integrat
return true
}
- if v1.GetIntegrationProfileAnnotation(integration) != "" &&
- v1.GetIntegrationProfileAnnotation(integration) !=
v1.GetIntegrationProfileAnnotation(kit) {
+ if kubernetes.GetIntegrationProfileResource(integration) != "" &&
+ kubernetes.GetIntegrationProfileResource(integration) !=
kubernetes.GetIntegrationProfileResource(kit) {
// Integration profile for the integration has changed. Reset
integration kit
// so new profile can be applied
return true
diff --git a/pkg/controller/integrationkit/build.go
b/pkg/controller/integrationkit/build.go
index 371fc4d45..161122862 100644
--- a/pkg/controller/integrationkit/build.go
+++ b/pkg/controller/integrationkit/build.go
@@ -120,6 +120,9 @@ func (action *buildAction) createBuild(ctx context.Context,
kit *v1.IntegrationK
if v, ok := kit.Annotations[v1.IntegrationProfileAnnotation]; ok {
annotations[v1.IntegrationProfileAnnotation] = v
}
+ if v, ok := kit.Annotations[v1.IntegrationProfileNamespaceAnnotation];
ok {
+ annotations[v1.IntegrationProfileNamespaceAnnotation] = v
+ }
operatorID := defaults.OperatorID()
if operatorID != "" {
diff --git a/pkg/controller/pipe/monitor.go b/pkg/controller/pipe/monitor.go
index 26e513826..53e29d77d 100644
--- a/pkg/controller/pipe/monitor.go
+++ b/pkg/controller/pipe/monitor.go
@@ -30,6 +30,7 @@ import (
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/kubernetes"
)
// NewMonitorAction returns an action that monitors the Pipe after it's fully
initialized.
@@ -69,8 +70,8 @@ func (action *monitorAction) Handle(ctx context.Context, pipe
*v1.Pipe) (*v1.Pip
operatorIDChanged := v1.GetOperatorIDAnnotation(pipe) != "" &&
(v1.GetOperatorIDAnnotation(pipe) !=
v1.GetOperatorIDAnnotation(&it))
- integrationProfileChanged := v1.GetIntegrationProfileAnnotation(pipe)
!= "" &&
- (v1.GetIntegrationProfileAnnotation(pipe) !=
v1.GetIntegrationProfileAnnotation(&it))
+ integrationProfileChanged :=
kubernetes.GetIntegrationProfileResource(pipe) != "" &&
+ (kubernetes.GetIntegrationProfileResource(pipe) !=
kubernetes.GetIntegrationProfileResource(&it))
sameTraits, err := trait.IntegrationAndPipeSameTraits(action.client,
&it, pipe)
if err != nil {
diff --git a/pkg/platform/operator.go b/pkg/platform/operator.go
index a13dc7421..df2ef9c74 100644
--- a/pkg/platform/operator.go
+++ b/pkg/platform/operator.go
@@ -24,6 +24,7 @@ import (
camelv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
"github.com/apache/camel-k/v2/pkg/util/defaults"
+ "github.com/apache/camel-k/v2/pkg/util/kubernetes"
coordination "k8s.io/api/coordination/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
"sigs.k8s.io/controller-runtime/pkg/event"
@@ -222,7 +223,7 @@ func (f FilteringFuncs[T]) Update(e
event.TypedUpdateEvent[T]) bool {
// Always force reconciliation when the object becomes managed
by the current operator
return true
}
- if camelv1.GetIntegrationProfileAnnotation(e.ObjectOld) !=
camelv1.GetIntegrationProfileAnnotation(e.ObjectNew) {
+ if kubernetes.GetIntegrationProfileResource(e.ObjectOld) !=
kubernetes.GetIntegrationProfileResource(e.ObjectNew) {
// Always force reconciliation when the object gets attached to
a new integration profile
return true
}
diff --git a/pkg/platform/profile.go b/pkg/platform/profile.go
index e6ac23f5e..0d3642494 100644
--- a/pkg/platform/profile.go
+++ b/pkg/platform/profile.go
@@ -40,8 +40,8 @@ func ApplyIntegrationProfile(ctx context.Context, c
k8sclient.Reader, o k8sclien
// findIntegrationProfile finds profile from given resource annotations
// and resolves the profile in given resource namespace.
func findIntegrationProfile(ctx context.Context, c k8sclient.Reader, o
k8sclient.Object) (*v1.IntegrationProfile, error) {
- if profileName := v1.GetIntegrationProfileAnnotation(o); profileName !=
"" {
- return kubernetes.GetIntegrationProfile(ctx, c, profileName,
o.GetNamespace())
+ if profileResource := kubernetes.GetIntegrationProfileResource(o);
profileResource != "" {
+ return kubernetes.GetIntegrationProfile(ctx, c, profileResource)
}
return nil, nil
diff --git a/pkg/platform/profile_test.go b/pkg/platform/profile_test.go
index 6fba44146..4f5cdcb4c 100644
--- a/pkg/platform/profile_test.go
+++ b/pkg/platform/profile_test.go
@@ -29,7 +29,7 @@ import (
"github.com/apache/camel-k/v2/pkg/internal"
)
-func TestFindIntegrationProfile(t *testing.T) {
+func TestFindOrApplyIntegrationProfile(t *testing.T) {
profile := v1.IntegrationProfile{
ObjectMeta: metav1.ObjectMeta{
Name: "custom",
@@ -41,6 +41,9 @@ func TestFindIntegrationProfile(t *testing.T) {
require.NoError(t, err)
integration := v1.Integration{
+ TypeMeta: metav1.TypeMeta{
+ Kind: v1.IntegrationKind,
+ },
ObjectMeta: metav1.ObjectMeta{
Name: "test",
Namespace: "ns",
@@ -56,4 +59,78 @@ func TestFindIntegrationProfile(t *testing.T) {
found, err := findIntegrationProfile(context.TODO(), c, &integration)
require.NoError(t, err)
assert.NotNil(t, found)
+
+ found, err = ApplyIntegrationProfile(context.TODO(), c, &integration)
+ require.NoError(t, err)
+ assert.NotNil(t, found)
+}
+
+func TestFindOrApplyIntegrationMissing(t *testing.T) {
+ profile := v1.IntegrationProfile{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "custom",
+ Namespace: "ns",
+ },
+ }
+
+ c, err := internal.NewFakeClient(&profile)
+ require.NoError(t, err)
+
+ integration := v1.Integration{
+ TypeMeta: metav1.TypeMeta{
+ Kind: v1.IntegrationKind,
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ Namespace: "ns",
+ Annotations: map[string]string{
+ v1.IntegrationProfileAnnotation: "missing",
+ },
+ },
+ Status: v1.IntegrationStatus{
+ Phase: v1.IntegrationPhaseRunning,
+ },
+ }
+
+ found, err := findIntegrationProfile(context.TODO(), c, &integration)
+ require.Error(t, err)
+ assert.Nil(t, found)
+ assert.Contains(t, err.Error(), "\"missing\" not found")
+
+ found, err = ApplyIntegrationProfile(context.TODO(), c, &integration)
+ require.NoError(t, err)
+ assert.Nil(t, found)
+}
+
+func TestFindOrApplyIntegrationProfileError(t *testing.T) {
+ profile := v1.IntegrationProfile{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "custom",
+ Namespace: "ns",
+ },
+ }
+
+ c, err := internal.NewFakeClient(&profile)
+ require.NoError(t, err)
+
+ integrationKit := v1.IntegrationKit{
+ TypeMeta: metav1.TypeMeta{
+ Kind: v1.IntegrationKitKind,
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ Namespace: "ns",
+ Annotations: map[string]string{
+ v1.IntegrationProfileAnnotation: "custom",
+ },
+ },
+ }
+
+ _, err = findIntegrationProfile(context.TODO(), c, &integrationKit)
+ require.Error(t, err)
+ assert.Equal(t, "wrong format for IntegrationProfile, expected
profile-namespace/profile-id", err.Error())
+
+ _, err = ApplyIntegrationProfile(context.TODO(), c, &integrationKit)
+ require.Error(t, err)
+ assert.Equal(t, "wrong format for IntegrationProfile, expected
profile-namespace/profile-id", err.Error())
}
diff --git a/pkg/trait/quarkus.go b/pkg/trait/quarkus.go
index 45f581bdc..dff8b1290 100644
--- a/pkg/trait/quarkus.go
+++ b/pkg/trait/quarkus.go
@@ -304,6 +304,7 @@ func (t *quarkusTrait) newIntegrationKit(e *Environment,
packageType quarkusPack
if v, ok := integration.Annotations[v1.IntegrationProfileAnnotation];
ok {
v1.SetAnnotation(&kit.ObjectMeta,
v1.IntegrationProfileAnnotation, v)
+ v1.SetAnnotation(&kit.ObjectMeta,
v1.IntegrationProfileNamespaceAnnotation, integration.Namespace)
}
operatorID := defaults.OperatorID()
if operatorID != "" {
diff --git a/pkg/trait/quarkus_test.go b/pkg/trait/quarkus_test.go
index 7aafe861d..aedcf7c9d 100644
--- a/pkg/trait/quarkus_test.go
+++ b/pkg/trait/quarkus_test.go
@@ -289,3 +289,19 @@ func TestFailNewIntegrationKit(t *testing.T) {
require.Error(t, err)
assert.Equal(t, "cannot create a Kit outside Integration or Catalog
namespaces", err.Error())
}
+
+func TestNewIntegrationKitWithProfile(t *testing.T) {
+ quarkusTrait, environment := createNominalQuarkusTest()
+ environment.Integration.Namespace = "it-ns"
+ environment.Integration.Annotations = map[string]string{
+ v1.IntegrationProfileAnnotation: "my-profile",
+ }
+ environment.Platform.CatalogNamespace = "op-ns"
+
+ kit, err := quarkusTrait.newIntegrationKit(environment,
fastJarPackageType)
+ require.NoError(t, err)
+ require.NotNil(t, kit)
+ require.NotEmpty(t, kit.Annotations)
+ assert.Equal(t, "my-profile",
kit.Annotations[v1.IntegrationProfileAnnotation])
+ assert.Equal(t, "it-ns",
kit.Annotations[v1.IntegrationProfileNamespaceAnnotation])
+}
diff --git a/pkg/util/digest/digest.go b/pkg/util/digest/digest.go
index 8ee6b7cf9..54aeb56b3 100644
--- a/pkg/util/digest/digest.go
+++ b/pkg/util/digest/digest.go
@@ -34,6 +34,7 @@ import (
v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
"github.com/apache/camel-k/v2/pkg/util"
"github.com/apache/camel-k/v2/pkg/util/defaults"
+ "github.com/apache/camel-k/v2/pkg/util/kubernetes"
"fmt"
)
@@ -58,7 +59,7 @@ func ComputeForIntegration(integration *v1.Integration,
configmapVersions []stri
}
// Integration profile is relevant
- if _, err :=
hash.Write([]byte(v1.GetIntegrationProfileAnnotation(integration))); err != nil
{
+ if _, err :=
hash.Write([]byte(kubernetes.GetIntegrationProfileResource(integration))); err
!= nil {
return "", err
}
diff --git a/pkg/util/kubernetes/client.go b/pkg/util/kubernetes/client.go
index 501018d84..aade17949 100644
--- a/pkg/util/kubernetes/client.go
+++ b/pkg/util/kubernetes/client.go
@@ -21,6 +21,7 @@ import (
"context"
"errors"
"fmt"
+ "strings"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -30,40 +31,51 @@ import (
v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
"github.com/apache/camel-k/v2/pkg/client"
- "github.com/apache/camel-k/v2/pkg/util/log"
)
//nolint:staticcheck
func GetIntegrationPlatform(context context.Context, client ctrl.Reader, name
string, namespace string) (*v1.IntegrationPlatform, error) {
platform := v1.NewIntegrationPlatform(namespace, name)
- log.Debugf("Integration Platform [name: %s], [namespace: %s],
[objectkey: %s]", name, namespace, ctrl.ObjectKeyFromObject(&platform))
if err := client.Get(context, ctrl.ObjectKeyFromObject(&platform),
&platform); err != nil {
- log.Debugf("Integration platform Error: %v", err)
-
return nil, err
}
return &platform, nil
}
-func GetIntegrationProfile(context context.Context, client ctrl.Reader, name
string, namespace string) (*v1.IntegrationProfile, error) {
- integrationProfile := v1.NewIntegrationProfile(namespace, name)
- log.Debugf("Integration Profile [name: %s], [namespace: %s],
[objectkey: %s]", name, namespace,
ctrl.ObjectKeyFromObject(&integrationProfile))
+// GetIntegrationProfile return an integration profile provided as the
resource parameter (ie, profile-namespace/profile-id).
+func GetIntegrationProfile(context context.Context, client ctrl.Reader,
resource string) (*v1.IntegrationProfile, error) {
+ resourceSplit := strings.Split(resource, "/")
+ if len(resourceSplit) != 2 || resourceSplit[0] == "" ||
resourceSplit[1] == "" {
+ return nil, errors.New("wrong format for IntegrationProfile,
expected profile-namespace/profile-id")
+ }
+ integrationProfile := v1.NewIntegrationProfile(resourceSplit[0],
resourceSplit[1])
if err := client.Get(context,
ctrl.ObjectKeyFromObject(&integrationProfile), &integrationProfile); err != nil
{
- log.Debugf("Integration profile Error: %v", err)
-
return nil, err
}
return &integrationProfile, nil
}
+// GetIntegrationProfileResource to safely get the integration profile
namespace and id.
+// NOTE: IntegrationKit and Build use an operator generated annotation value
to calculate the namespace.
+// User should never be able to set the profile namespace annotation.
+func GetIntegrationProfileResource(o ctrl.Object) string {
+ if v1.GetAnnotation(v1.IntegrationProfileAnnotation, o) == "" {
+ return ""
+ }
+
+ namespace := o.GetNamespace()
+ if o.GetObjectKind().GroupVersionKind().Kind == v1.IntegrationKitKind
|| o.GetObjectKind().GroupVersionKind().Kind == v1.BuildKind {
+ namespace =
v1.GetAnnotation(v1.IntegrationProfileNamespaceAnnotation, o)
+ }
+
+ return namespace + "/" +
v1.GetAnnotation(v1.IntegrationProfileAnnotation, o)
+}
+
func GetCamelCatalog(context context.Context, client ctrl.Reader, name string,
namespace string) (*v1.CamelCatalog, error) {
catalog := v1.NewCamelCatalog(namespace, name)
- log.Debugf("Camel Catalog [name: %s], [namespace: %s], [objectkey:
%s]", name, namespace, ctrl.ObjectKeyFromObject(&catalog))
if err := client.Get(context, ctrl.ObjectKeyFromObject(&catalog),
&catalog); err != nil {
- log.Debugf("Camel catalog Error: %v", err)
-
return nil, err
}
@@ -72,10 +84,7 @@ func GetCamelCatalog(context context.Context, client
ctrl.Reader, name string, n
func GetIntegrationKit(context context.Context, client ctrl.Reader, name
string, namespace string) (*v1.IntegrationKit, error) {
kit := v1.NewIntegrationKit(namespace, name)
- log.Debugf("Integration Kit [name: %s], [namespace: %s], [objectkey:
%s]", name, namespace, ctrl.ObjectKeyFromObject(kit))
if err := client.Get(context, ctrl.ObjectKeyFromObject(kit), kit); err
!= nil {
- log.Debugf("Integration kit Error: %v", err)
-
return nil, err
}
@@ -84,10 +93,7 @@ func GetIntegrationKit(context context.Context, client
ctrl.Reader, name string,
func GetBuild(context context.Context, client client.Client, name string,
namespace string) (*v1.Build, error) {
build := v1.NewBuild(namespace, name)
- log.Debugf("Build [name: %s], [namespace: %s], [objectkey: %s]", name,
namespace, ctrl.ObjectKeyFromObject(build))
if err := client.Get(context, ctrl.ObjectKeyFromObject(build), build);
err != nil {
- log.Debugf("Integration build Error: %v", err)
-
return nil, err
}
diff --git a/pkg/platform/profile_test.go b/pkg/util/kubernetes/client_test.go
similarity index 54%
copy from pkg/platform/profile_test.go
copy to pkg/util/kubernetes/client_test.go
index 6fba44146..dd2e61c88 100644
--- a/pkg/platform/profile_test.go
+++ b/pkg/util/kubernetes/client_test.go
@@ -15,45 +15,67 @@ See the License for the specific language governing
permissions and
limitations under the License.
*/
-package platform
+package kubernetes
import (
- "context"
"testing"
+ v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-
- v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
- "github.com/apache/camel-k/v2/pkg/internal"
)
-func TestFindIntegrationProfile(t *testing.T) {
- profile := v1.IntegrationProfile{
+func TestFindIntegrationProfileResource(t *testing.T) {
+ integration := v1.Integration{
+ TypeMeta: metav1.TypeMeta{
+ Kind: v1.IntegrationKind,
+ },
ObjectMeta: metav1.ObjectMeta{
- Name: "custom",
+ Name: "test",
Namespace: "ns",
+ Annotations: map[string]string{
+ v1.IntegrationProfileAnnotation: "custom",
+ },
+ },
+ Status: v1.IntegrationStatus{
+ Phase: v1.IntegrationPhaseRunning,
},
}
- c, err := internal.NewFakeClient(&profile)
- require.NoError(t, err)
+ profileResource := GetIntegrationProfileResource(&integration)
+ assert.Equal(t, "ns/custom", profileResource)
- integration := v1.Integration{
+ kit := v1.IntegrationKit{
+ TypeMeta: metav1.TypeMeta{
+ Kind: v1.IntegrationKitKind,
+ },
ObjectMeta: metav1.ObjectMeta{
Name: "test",
Namespace: "ns",
Annotations: map[string]string{
- v1.IntegrationProfileAnnotation: "custom",
+ v1.IntegrationProfileAnnotation:
"custom",
+ v1.IntegrationProfileNamespaceAnnotation:
"it-ns",
},
},
- Status: v1.IntegrationStatus{
- Phase: v1.IntegrationPhaseRunning,
+ }
+
+ profileResource = GetIntegrationProfileResource(&kit)
+ assert.Equal(t, "it-ns/custom", profileResource)
+
+ build := v1.Build{
+ TypeMeta: metav1.TypeMeta{
+ Kind: v1.BuildKind,
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ Namespace: "ns",
+ Annotations: map[string]string{
+ v1.IntegrationProfileAnnotation:
"custom",
+ v1.IntegrationProfileNamespaceAnnotation:
"it-ns",
+ },
},
}
- found, err := findIntegrationProfile(context.TODO(), c, &integration)
- require.NoError(t, err)
- assert.NotNil(t, found)
+ profileResource = GetIntegrationProfileResource(&build)
+ assert.Equal(t, "it-ns/custom", profileResource)
}