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)
 }

Reply via email to