Skip to content

Commit 4eafd3e

Browse files
pkalsi97squakez
authored andcommitted
feat: Add default container annotation for kubectl commands
1 parent 674118d commit 4eafd3e

File tree

4 files changed

+355
-0
lines changed

4 files changed

+355
-0
lines changed

pkg/trait/cron.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,9 @@ func (t *cronTrait) getCronJobFor(e *Environment) *batchv1.CronJob {
240240
}
241241
}
242242

243+
// Set the default container annotation for kubectl commands
244+
annotations["kubectl.kubernetes.io/default-container"] = defaultContainerName
245+
243246
activeDeadline := defaultCronActiveDeadlineSeconds
244247
if t.ActiveDeadlineSeconds != nil {
245248
activeDeadline = *t.ActiveDeadlineSeconds
Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package trait
19+
20+
import (
21+
"testing"
22+
23+
"github.com/stretchr/testify/assert"
24+
"github.com/stretchr/testify/require"
25+
26+
appsv1 "k8s.io/api/apps/v1"
27+
batchv1 "k8s.io/api/batch/v1"
28+
corev1 "k8s.io/api/core/v1"
29+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30+
"k8s.io/utils/ptr"
31+
32+
serving "knative.dev/serving/pkg/apis/serving/v1"
33+
34+
v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
35+
traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait"
36+
"github.com/apache/camel-k/v2/pkg/internal"
37+
"github.com/apache/camel-k/v2/pkg/util/camel"
38+
"github.com/apache/camel-k/v2/pkg/util/kubernetes"
39+
)
40+
41+
const (
42+
defaultContainerAnnotationKey = "kubectl.kubernetes.io/default-container"
43+
testNamespace = "test-ns"
44+
testIntegrationName = "test-integration"
45+
)
46+
47+
func TestDeploymentHasDefaultContainerAnnotation(t *testing.T) {
48+
deploymentTrait, environment := createNominalDeploymentTest()
49+
err := deploymentTrait.Apply(environment)
50+
require.NoError(t, err)
51+
52+
deployment := environment.Resources.GetDeployment(func(deployment *appsv1.Deployment) bool {
53+
return true
54+
})
55+
require.NotNil(t, deployment, "Deployment should not be nil")
56+
57+
podTemplateAnnotations := deployment.Spec.Template.ObjectMeta.Annotations
58+
59+
t.Logf("Pod Template Annotations: %v", podTemplateAnnotations)
60+
61+
value, exists := podTemplateAnnotations[defaultContainerAnnotationKey]
62+
63+
assert.True(t, exists,
64+
"Expected annotation '%s' to be present on pod template, but it was not found. "+
65+
"Current annotations: %v", defaultContainerAnnotationKey, podTemplateAnnotations)
66+
67+
if exists {
68+
assert.Equal(t, defaultContainerName, value,
69+
"Expected default container annotation to point to '%s', got '%s'",
70+
defaultContainerName, value)
71+
}
72+
}
73+
74+
func TestDeploymentDefaultContainerAnnotationWithUserAnnotations(t *testing.T) {
75+
deploymentTrait, environment := createNominalDeploymentTest()
76+
77+
environment.Integration.Annotations = map[string]string{
78+
"user-annotation-1": "value1",
79+
"user-annotation-2": "value2",
80+
}
81+
82+
err := deploymentTrait.Apply(environment)
83+
require.NoError(t, err)
84+
85+
deployment := environment.Resources.GetDeployment(func(d *appsv1.Deployment) bool { return true })
86+
require.NotNil(t, deployment)
87+
88+
annotations := deployment.Spec.Template.ObjectMeta.Annotations
89+
90+
assert.Equal(t, defaultContainerName, annotations[defaultContainerAnnotationKey],
91+
"Default container annotation should be set to '%s'", defaultContainerName)
92+
93+
t.Logf("All pod template annotations: %v", annotations)
94+
}
95+
96+
func TestDeploymentDefaultContainerAnnotationValue(t *testing.T) {
97+
deploymentTrait, environment := createNominalDeploymentTest()
98+
99+
err := deploymentTrait.Apply(environment)
100+
require.NoError(t, err)
101+
102+
deployment := environment.Resources.GetDeployment(func(d *appsv1.Deployment) bool { return true })
103+
require.NotNil(t, deployment)
104+
105+
value := deployment.Spec.Template.ObjectMeta.Annotations[defaultContainerAnnotationKey]
106+
107+
assert.Equal(t, "integration", value,
108+
"Annotation value should be 'integration' to match the default container name")
109+
}
110+
111+
func TestKnativeServiceHasDefaultContainerAnnotation(t *testing.T) {
112+
environment := createKnativeServiceTestEnvironment(t, &traitv1.KnativeServiceTrait{
113+
Trait: traitv1.Trait{
114+
Enabled: ptr.To(true),
115+
},
116+
})
117+
118+
service := environment.Resources.GetKnativeService(func(s *serving.Service) bool {
119+
return true
120+
})
121+
require.NotNil(t, service, "Knative Service should not be nil")
122+
123+
revisionAnnotations := service.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations
124+
125+
t.Logf("Knative Revision Template Annotations: %v", revisionAnnotations)
126+
127+
value, exists := revisionAnnotations[defaultContainerAnnotationKey]
128+
129+
assert.True(t, exists,
130+
"Expected annotation '%s' to be present on Knative revision template, but it was not found. "+
131+
"Current annotations: %v", defaultContainerAnnotationKey, revisionAnnotations)
132+
133+
if exists {
134+
assert.Equal(t, defaultContainerName, value,
135+
"Expected default container annotation to point to '%s', got '%s'",
136+
defaultContainerName, value)
137+
}
138+
}
139+
140+
func TestKnativeServiceDefaultContainerAnnotationWithAutoScaling(t *testing.T) {
141+
minScale := 1
142+
maxScale := 5
143+
environment := createKnativeServiceTestEnvironment(t, &traitv1.KnativeServiceTrait{
144+
Trait: traitv1.Trait{
145+
Enabled: ptr.To(true),
146+
},
147+
MinScale: &minScale,
148+
MaxScale: &maxScale,
149+
})
150+
151+
service := environment.Resources.GetKnativeService(func(s *serving.Service) bool { return true })
152+
require.NotNil(t, service)
153+
154+
annotations := service.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations
155+
156+
assert.Equal(t, defaultContainerName, annotations[defaultContainerAnnotationKey],
157+
"Default container annotation should be present")
158+
assert.Equal(t, "1", annotations["autoscaling.knative.dev/minScale"],
159+
"Min scale annotation should be present")
160+
assert.Equal(t, "5", annotations["autoscaling.knative.dev/maxScale"],
161+
"Max scale annotation should be present")
162+
163+
t.Logf("All Knative revision annotations: %v", annotations)
164+
}
165+
166+
func TestCronJobHasDefaultContainerAnnotation(t *testing.T) {
167+
environment := createCronJobTestEnvironment(t)
168+
169+
cronJob := environment.Resources.GetCronJob(func(c *batchv1.CronJob) bool {
170+
return true
171+
})
172+
require.NotNil(t, cronJob, "CronJob should not be nil")
173+
174+
podTemplateAnnotations := cronJob.Spec.JobTemplate.Spec.Template.ObjectMeta.Annotations
175+
176+
t.Logf("CronJob Pod Template Annotations: %v", podTemplateAnnotations)
177+
178+
value, exists := podTemplateAnnotations[defaultContainerAnnotationKey]
179+
180+
assert.True(t, exists,
181+
"Expected annotation '%s' to be present on CronJob pod template, but it was not found. "+
182+
"Current annotations: %v", defaultContainerAnnotationKey, podTemplateAnnotations)
183+
184+
if exists {
185+
assert.Equal(t, defaultContainerName, value,
186+
"Expected default container annotation to point to '%s', got '%s'",
187+
defaultContainerName, value)
188+
}
189+
}
190+
191+
func TestCronJobDefaultContainerAnnotationWithUserAnnotations(t *testing.T) {
192+
environment := createCronJobTestEnvironmentWithAnnotations(t, map[string]string{
193+
"custom.annotation/key": "custom-value",
194+
})
195+
196+
cronJob := environment.Resources.GetCronJob(func(c *batchv1.CronJob) bool { return true })
197+
require.NotNil(t, cronJob)
198+
199+
annotations := cronJob.Spec.JobTemplate.Spec.Template.ObjectMeta.Annotations
200+
201+
assert.Equal(t, defaultContainerName, annotations[defaultContainerAnnotationKey],
202+
"Default container annotation should be set to '%s'", defaultContainerName)
203+
204+
t.Logf("All CronJob pod template annotations: %v", annotations)
205+
}
206+
207+
func TestAllControllerStrategiesHaveDefaultContainerAnnotation(t *testing.T) {
208+
t.Run("Deployment", func(t *testing.T) {
209+
deploymentTrait, environment := createNominalDeploymentTest()
210+
err := deploymentTrait.Apply(environment)
211+
require.NoError(t, err)
212+
213+
deployment := environment.Resources.GetDeployment(func(d *appsv1.Deployment) bool { return true })
214+
require.NotNil(t, deployment)
215+
216+
assert.Equal(t, defaultContainerName,
217+
deployment.Spec.Template.ObjectMeta.Annotations[defaultContainerAnnotationKey],
218+
"Deployment should have default-container annotation")
219+
})
220+
221+
t.Run("KnativeService", func(t *testing.T) {
222+
environment := createKnativeServiceTestEnvironment(t, &traitv1.KnativeServiceTrait{
223+
Trait: traitv1.Trait{Enabled: ptr.To(true)},
224+
})
225+
226+
service := environment.Resources.GetKnativeService(func(s *serving.Service) bool { return true })
227+
require.NotNil(t, service)
228+
229+
assert.Equal(t, defaultContainerName,
230+
service.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations[defaultContainerAnnotationKey],
231+
"Knative Service should have default-container annotation")
232+
})
233+
234+
t.Run("CronJob", func(t *testing.T) {
235+
environment := createCronJobTestEnvironment(t)
236+
237+
cronJob := environment.Resources.GetCronJob(func(c *batchv1.CronJob) bool { return true })
238+
require.NotNil(t, cronJob)
239+
240+
assert.Equal(t, defaultContainerName,
241+
cronJob.Spec.JobTemplate.Spec.Template.ObjectMeta.Annotations[defaultContainerAnnotationKey],
242+
"CronJob should have default-container annotation")
243+
})
244+
}
245+
246+
func TestDefaultContainerAnnotationValueIsConsistent(t *testing.T) {
247+
expectedValue := "integration"
248+
249+
t.Run("Deployment uses correct value", func(t *testing.T) {
250+
deploymentTrait, environment := createNominalDeploymentTest()
251+
_ = deploymentTrait.Apply(environment)
252+
deployment := environment.Resources.GetDeployment(func(d *appsv1.Deployment) bool { return true })
253+
require.NotNil(t, deployment)
254+
assert.Equal(t, expectedValue, deployment.Spec.Template.ObjectMeta.Annotations[defaultContainerAnnotationKey])
255+
})
256+
257+
t.Run("KnativeService uses correct value", func(t *testing.T) {
258+
environment := createKnativeServiceTestEnvironment(t, &traitv1.KnativeServiceTrait{
259+
Trait: traitv1.Trait{Enabled: ptr.To(true)},
260+
})
261+
service := environment.Resources.GetKnativeService(func(s *serving.Service) bool { return true })
262+
require.NotNil(t, service)
263+
assert.Equal(t, expectedValue, service.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations[defaultContainerAnnotationKey])
264+
})
265+
266+
t.Run("CronJob uses correct value", func(t *testing.T) {
267+
environment := createCronJobTestEnvironment(t)
268+
cronJob := environment.Resources.GetCronJob(func(c *batchv1.CronJob) bool { return true })
269+
require.NotNil(t, cronJob)
270+
assert.Equal(t, expectedValue, cronJob.Spec.JobTemplate.Spec.Template.ObjectMeta.Annotations[defaultContainerAnnotationKey])
271+
})
272+
}
273+
274+
func createCronJobTestEnvironment(t *testing.T) *Environment {
275+
t.Helper()
276+
return createCronJobTestEnvironmentWithAnnotations(t, nil)
277+
}
278+
279+
func createCronJobTestEnvironmentWithAnnotations(t *testing.T, annotations map[string]string) *Environment {
280+
t.Helper()
281+
282+
catalog, err := camel.DefaultCatalog()
283+
require.NoError(t, err)
284+
285+
client, _ := internal.NewFakeClient()
286+
traitCatalog := NewCatalog(nil)
287+
288+
environment := &Environment{
289+
CamelCatalog: catalog,
290+
Catalog: traitCatalog,
291+
Client: client,
292+
Integration: &v1.Integration{
293+
ObjectMeta: metav1.ObjectMeta{
294+
Name: testIntegrationName,
295+
Namespace: testNamespace,
296+
Annotations: annotations,
297+
},
298+
Status: v1.IntegrationStatus{
299+
Phase: v1.IntegrationPhaseDeploying,
300+
},
301+
Spec: v1.IntegrationSpec{
302+
Sources: []v1.SourceSpec{
303+
{
304+
DataSpec: v1.DataSpec{
305+
Name: "routes.java",
306+
Content: `from("cron:tab?schedule=0 0/2 * * ?").to("log:test")`,
307+
},
308+
Language: v1.LanguageJavaSource,
309+
},
310+
},
311+
Traits: v1.Traits{
312+
Cron: &traitv1.CronTrait{},
313+
},
314+
},
315+
},
316+
IntegrationKit: &v1.IntegrationKit{
317+
Status: v1.IntegrationKitStatus{
318+
Phase: v1.IntegrationKitPhaseReady,
319+
},
320+
},
321+
Platform: &v1.IntegrationPlatform{
322+
Spec: v1.IntegrationPlatformSpec{
323+
Build: v1.IntegrationPlatformBuildSpec{
324+
RuntimeVersion: catalog.Runtime.Version,
325+
},
326+
},
327+
Status: v1.IntegrationPlatformStatus{
328+
Phase: v1.IntegrationPlatformPhaseReady,
329+
},
330+
},
331+
EnvVars: make([]corev1.EnvVar, 0),
332+
ExecutedTraits: make([]Trait, 0),
333+
Resources: kubernetes.NewCollection(),
334+
}
335+
environment.Platform.ResyncStatusFullConfig()
336+
337+
c, err := newFakeClient(testNamespace)
338+
require.NoError(t, err)
339+
340+
tc := NewCatalog(c)
341+
_, _, err = tc.apply(environment)
342+
require.NoError(t, err)
343+
344+
return environment
345+
}

pkg/trait/deployment.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ func (t *deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
118118
}
119119
}
120120

121+
// Set the default container annotation for kubectl commands
122+
annotations["kubectl.kubernetes.io/default-container"] = defaultContainerName
123+
121124
deadline := defaultProgressDeadline
122125
if t.ProgressDeadlineSeconds != nil {
123126
deadline = *t.ProgressDeadlineSeconds

pkg/trait/knative_service.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ func (t *knativeServiceTrait) getServiceFor(e *Environment) (*serving.Service, e
193193
revisionAnnotations[k] = v
194194
}
195195
}
196+
197+
// Set the default container annotation for kubectl
198+
revisionAnnotations["kubectl.kubernetes.io/default-container"] = defaultContainerName
199+
196200
// Set Knative auto-scaling
197201
if t.Class != "" {
198202
revisionAnnotations[knativeServingClassAnnotation] = t.Class

0 commit comments

Comments
 (0)