Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/modules/ROOT/partials/apis/camel-k-crds.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -8318,6 +8318,15 @@ It's activated automatically when using the master endpoint in a route, e.g. `fr
NOTE: this trait adds special permissions to the integration service account in order to read/write configmaps and read pods.
It's recommended to use a different service account than "default" when running the integration.

WARNING: The Master trait is **deprecated** and will be removed in future release versions.
This trait requires the operator to manage RBAC explicitly, which should be avoided for security
and simplicity reasons. Users should manually create the required Role and RoleBinding, then configure
Quarkus properties directly:

-p quarkus.camel.cluster.kubernetes.resource-name=<integration>-lock
-p quarkus.camel.cluster.kubernetes.resource-type=Lease
-p quarkus.camel.cluster.kubernetes.labels."camel.apache.org/integration"=<integration-name>


[cols="2,2a",options="header"]
|===
Expand Down
123 changes: 123 additions & 0 deletions docs/modules/traits/pages/master.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
= Master Trait

// Start of autogenerated code - DO NOT EDIT! (badges)
[.badges]
[.badge-key]##Deprecated since##[.badge-unsupported]##2.9.0##
// End of autogenerated code - DO NOT EDIT! (badges)
// Start of autogenerated code - DO NOT EDIT! (description)
The Master trait allows to configure the integration to automatically leverage Kubernetes resources for doing
Expand All @@ -11,6 +13,15 @@ It's activated automatically when using the master endpoint in a route, e.g. `fr
NOTE: this trait adds special permissions to the integration service account in order to read/write configmaps and read pods.
It's recommended to use a different service account than "default" when running the integration.

WARNING: The Master trait is **deprecated** and will be removed in future release versions.
This trait requires the operator to manage RBAC explicitly, which should be avoided for security
and simplicity reasons. Users should manually create the required Role and RoleBinding, then configure
Quarkus properties directly:

-p quarkus.camel.cluster.kubernetes.resource-name=<integration>-lock
-p quarkus.camel.cluster.kubernetes.resource-type=Lease
-p quarkus.camel.cluster.kubernetes.labels."camel.apache.org/integration"=<integration-name>


This trait is available in the following profiles: **Kubernetes, Knative, OpenShift**.

Expand Down Expand Up @@ -63,3 +74,115 @@ Name of the configmap/lease resource that will be used to store the lock. Defaul
|===

// End of autogenerated code - DO NOT EDIT! (configuration)

== Migration Guide

The Master trait is deprecated and will be removed in a future release. This trait requires the operator to manage RBAC explicitly, which should be avoided for security and simplicity reasons.

=== Why Migrate?

* **Reduced Operator Permissions**: The operator no longer needs permissions to create Role and RoleBinding resources
* **Explicit RBAC Control**: Users have full control over the RBAC configuration
* **Simplified Security Model**: Better separation of concerns between integration and infrastructure

=== Migration Steps

==== Step 1: Create RBAC Resources Manually

Apply the following RBAC resources to your namespace:

[source,yaml]
----
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: <integration-name>-master
rules:
- apiGroups:
- coordination.k8s.io
resources:
- leases
verbs:
- create
- delete
- deletecollection
- get
- list
- patch
- update
- watch
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: <integration-name>-master
subjects:
- kind: ServiceAccount
name: <service-account-name>
roleRef:
kind: Role
name: <integration-name>-master
apiGroup: rbac.authorization.k8s.io
----

==== Step 2: Configure Quarkus Properties

Instead of using the trait properties, configure the equivalent Quarkus properties directly:

[cols="2m,2m,3a"]
|===
|Master Trait Property | Quarkus Property | Description

| master.resource-name
| quarkus.camel.cluster.kubernetes.resource-name
| Name of the lock resource

| master.resource-type
| quarkus.camel.cluster.kubernetes.resource-type
| Type of Kubernetes resource (Lease or ConfigMap)

| master.label-key / master.label-value
| quarkus.camel.cluster.kubernetes.labels."<key>"
| Labels for pod identification

|===

==== Example Migration

**Before (using deprecated trait):**
[source,console]
----
$ kamel run MyRoute.java \
-t master.resource-name=my-lock \
-t master.resource-type=Lease \
-t master.label-key=leader-group \
-t master.label-value=my-group
----

**After (using Quarkus properties):**
[source,console]
----
# First apply the RBAC resources
$ kubectl apply -f master-rbac.yaml

# Then run with Quarkus properties
$ kamel run MyRoute.java \
--service-account my-integration-sa \
-p quarkus.camel.cluster.kubernetes.resource-name=my-lock \
-p quarkus.camel.cluster.kubernetes.resource-type=Lease \
-p quarkus.camel.cluster.kubernetes.labels."leader-group"=my-group
----

=== Notes

* The `master:` component in Camel routes continues to work; only the automatic RBAC creation is removed
* Ensure the service account used by the integration has the necessary permissions
* For existing integrations, create the RBAC resources before removing the trait configuration
91 changes: 81 additions & 10 deletions e2e/common/traits/master_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import (
. "github.com/onsi/gomega"

corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

. "github.com/apache/camel-k/v2/e2e/support"
v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
Expand All @@ -39,29 +41,98 @@ import (
func TestMasterTrait(t *testing.T) {
t.Parallel()
WithNewTestNamespace(t, func(ctx context.Context, g *WithT, ns string) {
t.Run("master works", func(t *testing.T) {
g.Expect(KamelRun(t, ctx, ns, "files/Master.java").Execute()).To(Succeed())
g.Eventually(IntegrationPodPhase(t, ctx, ns, "master"), TestTimeoutLong).Should(Equal(corev1.PodRunning))
g.Eventually(IntegrationLogs(t, ctx, ns, "master"), TestTimeoutShort).Should(ContainSubstring("Magicstring!"))
t.Run("master works with properties", func(t *testing.T) {
name := "master"
// Create RBAC resources for the master component
CreateMasterRBAC(t, ctx, g, ns, name, "default")

// Run using Quarkus properties instead of deprecated trait
g.Expect(KamelRun(t, ctx, ns, "files/Master.java",
"-d", "camel:kubernetes",
"-p", fmt.Sprintf("quarkus.camel.cluster.kubernetes.resource-name=%s-lock", name),
"-p", "quarkus.camel.cluster.kubernetes.resource-type=Lease",
"-p", fmt.Sprintf("quarkus.camel.cluster.kubernetes.labels.\"camel.apache.org/integration\"=%s", name),
).Execute()).To(Succeed())
g.Eventually(IntegrationPodPhase(t, ctx, ns, name), TestTimeoutLong).Should(Equal(corev1.PodRunning))
g.Eventually(IntegrationLogs(t, ctx, ns, name), TestTimeoutShort).Should(ContainSubstring("Magicstring!"))
g.Expect(Kamel(t, ctx, "delete", "--all", "-n", ns).Execute()).To(Succeed())
})

t.Run("only one integration with master runs", func(t *testing.T) {
t.Run("only one integration with master runs using properties", func(t *testing.T) {
nameFirst := RandomizedSuffixName("first")
nameSecond := RandomizedSuffixName("second")
lockName := nameFirst + "-lock"

CreateMasterRBAC(t, ctx, g, ns, nameFirst, "default")
CreateMasterRBAC(t, ctx, g, ns, nameSecond, "default")

g.Expect(KamelRun(t, ctx, ns, "files/Master.java", "--name", nameFirst,
"--label", "leader-group=same", "-t", "master.label-key=leader-group", "-t", "master.label-value=same", "-t", "owner.target-labels=leader-group",
"-d", "camel:kubernetes",
"--label", "leader-group=same",
"-t", "owner.target-labels=leader-group",
"-p", fmt.Sprintf("quarkus.camel.cluster.kubernetes.resource-name=%s", lockName),
"-p", "quarkus.camel.cluster.kubernetes.resource-type=Lease",
"-p", "quarkus.camel.cluster.kubernetes.labels.\"leader-group\"=same",
).Execute()).To(Succeed())
g.Eventually(IntegrationConditionStatus(t, ctx, ns, nameFirst, v1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
g.Eventually(IntegrationLogs(t, ctx, ns, nameFirst), TestTimeoutShort).Should(ContainSubstring("Magicstring!"))
// Start a second integration with the same lock (it should not start the route before 15 seconds)
nameSecond := RandomizedSuffixName("second")

g.Expect(KamelRun(t, ctx, ns, "files/Master.java", "--name", nameSecond,
"--label", "leader-group=same", "-t", "master.label-key=leader-group", "-t", "master.label-value=same", "-t", "owner.target-labels=leader-group",
"-t", fmt.Sprintf("master.resource-name=%s-lock", nameFirst),
"-d", "camel:kubernetes",
"--label", "leader-group=same",
"-t", "owner.target-labels=leader-group",
"-p", fmt.Sprintf("quarkus.camel.cluster.kubernetes.resource-name=%s", lockName),
"-p", "quarkus.camel.cluster.kubernetes.resource-type=Lease",
"-p", "quarkus.camel.cluster.kubernetes.labels.\"leader-group\"=same",
).Execute()).To(Succeed())
g.Eventually(IntegrationLogs(t, ctx, ns, nameSecond), TestTimeoutShort).Should(ContainSubstring("started in"))
g.Eventually(IntegrationLogs(t, ctx, ns, nameSecond), 15*time.Second).ShouldNot(ContainSubstring("Magicstring!"))
g.Eventually(IntegrationConditionStatus(t, ctx, ns, nameSecond, v1.IntegrationConditionReady), TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
})
})
}

// CreateMasterRBAC creates the Role and RoleBinding
func CreateMasterRBAC(t *testing.T, ctx context.Context, g *WithT, ns string, name string, serviceAccount string) {
t.Helper()

role := &rbacv1.Role{
ObjectMeta: metav1.ObjectMeta{
Name: name + "-master",
Namespace: ns,
},
Rules: []rbacv1.PolicyRule{
{
APIGroups: []string{"coordination.k8s.io"},
Resources: []string{"leases"},
Verbs: []string{"create", "delete", "deletecollection", "get", "list", "patch", "update", "watch"},
},
{
APIGroups: []string{""},
Resources: []string{"pods"},
Verbs: []string{"get", "list", "watch"},
},
},
}
g.Expect(TestClient(t).Create(ctx, role)).To(Succeed())

roleBinding := &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: name + "-master",
Namespace: ns,
},
Subjects: []rbacv1.Subject{
{
Kind: "ServiceAccount",
Name: serviceAccount,
Namespace: ns,
},
},
RoleRef: rbacv1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "Role",
Name: name + "-master",
},
}
g.Expect(TestClient(t).Create(ctx, roleBinding)).To(Succeed())
}
10 changes: 10 additions & 0 deletions pkg/apis/camel/v1/trait/master.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,17 @@ package trait
// NOTE: this trait adds special permissions to the integration service account in order to read/write configmaps and read pods.
// It's recommended to use a different service account than "default" when running the integration.
//
// WARNING: The Master trait is **deprecated** and will be removed in future release versions.
// This trait requires the operator to manage RBAC explicitly, which should be avoided for security
// and simplicity reasons. Users should manually create the required Role and RoleBinding, then configure
// Quarkus properties directly:
//
// -p quarkus.camel.cluster.kubernetes.resource-name=<integration>-lock
// -p quarkus.camel.cluster.kubernetes.resource-type=Lease
// -p quarkus.camel.cluster.kubernetes.labels."camel.apache.org/integration"=<integration-name>
//
// +camel-k:trait=master.
// +camel-k:deprecated=2.9.0.
type MasterTrait struct {
Trait `json:",inline" property:",squash"`

Expand Down
17 changes: 16 additions & 1 deletion pkg/trait/master.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"fmt"
"strings"

corev1 "k8s.io/api/core/v1"
"k8s.io/utils/ptr"
ctrl "sigs.k8s.io/controller-runtime/pkg/client"

Expand Down Expand Up @@ -100,7 +101,21 @@ func (t *masterTrait) Configure(e *Environment) (bool, *TraitCondition, error) {
}
}

return enabled, nil, nil
// Add deprecation warning condition when the trait is enabled
var condition *TraitCondition
if enabled {
condition = NewIntegrationCondition(
"Master",
v1.IntegrationConditionTraitInfo,
corev1.ConditionTrue,
TraitConfigurationReason,
"The master trait is deprecated and will be removed in a future release. "+
"Please manually create the required Role and RoleBinding, then configure Quarkus properties directly. "+
"See documentation for migration guide.",
)
}

return enabled, condition, nil
}

func (t *masterTrait) Apply(e *Environment) error {
Expand Down
Loading
Loading