Skip to content

Commit d9d1237

Browse files
authored
fix: workload rollout spec is invalid template is not empty (argoproj#1224)
Signed-off-by: Hui Kang <[email protected]>
1 parent c68f3df commit d9d1237

File tree

6 files changed

+160
-0
lines changed

6 files changed

+160
-0
lines changed

pkg/apis/rollouts/v1alpha1/types.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,16 @@ func (s *RolloutSpec) SetResolvedTemplate(template corev1.PodTemplateSpec) {
8383
s.Template = template
8484
}
8585

86+
func (s *RolloutSpec) EmptyTemplate() bool {
87+
if len(s.Template.Labels) > 0 {
88+
return false
89+
}
90+
if len(s.Template.Annotations) > 0 {
91+
return false
92+
}
93+
return true
94+
}
95+
8696
func (s *RolloutSpec) MarshalJSON() ([]byte, error) {
8797
type Alias RolloutSpec
8898

pkg/apis/rollouts/validation/validation.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,13 @@ func ValidateRolloutSpec(rollout *v1alpha1.Rollout, fldPath *field.Path) field.E
8383
}
8484
}
8585

86+
if !rollout.Spec.TemplateResolvedFromRef && (spec.WorkloadRef != nil && !spec.EmptyTemplate()) {
87+
// WorkloadRef and template can not be set at the same time for lint plugin
88+
// During reconciliation, TemplateResolvedFromRef is true and will not reach here
89+
allErrs = append(allErrs, field.InternalError(fldPath.Child("template"),
90+
fmt.Errorf("template must be empty for workload reference rollout")))
91+
}
92+
8693
selector, err := metav1.LabelSelectorAsSelector(spec.Selector)
8794
if err != nil {
8895
allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "invalid label selector"))

pkg/apis/rollouts/validation/validation_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,37 @@ func TestCanaryScaleDownDelaySeconds(t *testing.T) {
312312
})
313313

314314
}
315+
316+
func TestWorkloadRefWithTemplate(t *testing.T) {
317+
selector := &metav1.LabelSelector{
318+
MatchLabels: map[string]string{"key": "value"},
319+
}
320+
ro := &v1alpha1.Rollout{
321+
Spec: v1alpha1.RolloutSpec{
322+
WorkloadRef: &v1alpha1.ObjectRef{
323+
Name: "my-deployment",
324+
Kind: "Deployment",
325+
APIVersion: "apps/v1",
326+
},
327+
Selector: selector,
328+
Strategy: v1alpha1.RolloutStrategy{
329+
Canary: &v1alpha1.CanaryStrategy{
330+
StableService: "stable",
331+
CanaryService: "canary",
332+
},
333+
},
334+
Template: corev1.PodTemplateSpec{
335+
ObjectMeta: metav1.ObjectMeta{
336+
Labels: selector.MatchLabels,
337+
},
338+
},
339+
},
340+
}
341+
t.Run("workload reference with template", func(t *testing.T) {
342+
ro := ro.DeepCopy()
343+
allErrs := ValidateRollout(ro)
344+
assert.Equal(t, 2, len(allErrs))
345+
assert.EqualError(t, allErrs[0], "spec.template: Internal error: template must be empty for workload reference rollout")
346+
assert.EqualError(t, allErrs[1], "spec.template.spec.containers: Required value")
347+
})
348+
}

rollout/temlateref.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ func (r *informerBasedTemplateResolver) Resolve(rollout *v1alpha1.Rollout) error
125125
return nil
126126
}
127127

128+
// When workloadRef is resolved for the first time, TemplateResolvedFromRef = false.
129+
// In this case, template must not be set
130+
if !rollout.Spec.TemplateResolvedFromRef && !rollout.Spec.EmptyTemplate() {
131+
return fmt.Errorf("template must be empty for workload reference rollout")
132+
}
133+
128134
gvk := schema.FromAPIVersionAndKind(rollout.Spec.WorkloadRef.APIVersion, rollout.Spec.WorkloadRef.Kind)
129135

130136
info, ok := infoByGroupKind[gvk.GroupKind()]

rollout/temlateref_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,3 +406,52 @@ func TestRemashalMapFails(t *testing.T) {
406406
err := remarshalMap(nil, struct{}{})
407407
assert.Error(t, err)
408408
}
409+
410+
func TestResolve_WorkloadWithTemplate(t *testing.T) {
411+
rollout := v1alpha1.Rollout{
412+
ObjectMeta: v1.ObjectMeta{
413+
Namespace: "default",
414+
},
415+
Spec: v1alpha1.RolloutSpec{
416+
WorkloadRef: &v1alpha1.ObjectRef{
417+
Name: "my-deployment",
418+
Kind: "Deployment",
419+
APIVersion: "apps/v1",
420+
},
421+
Template: corev1.PodTemplateSpec{
422+
ObjectMeta: metav1.ObjectMeta{
423+
Labels: map[string]string{
424+
"app": "deploy",
425+
},
426+
},
427+
},
428+
},
429+
}
430+
431+
deployment := &appsv1.Deployment{
432+
TypeMeta: metav1.TypeMeta{
433+
APIVersion: appsv1.SchemeGroupVersion.String(),
434+
Kind: "Deployment",
435+
},
436+
ObjectMeta: metav1.ObjectMeta{
437+
Name: "my-deployment",
438+
Namespace: "default",
439+
},
440+
Spec: appsv1.DeploymentSpec{
441+
Template: corev1.PodTemplateSpec{
442+
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"test-label": "test-label-val"}},
443+
},
444+
},
445+
}
446+
447+
discoveryClient := newFakeDiscoClient()
448+
dynamicClient := dynamicfake.NewSimpleDynamicClient(scheme.Scheme, deployment)
449+
450+
resolver, cancel := newResolver(dynamicClient, discoveryClient, fake.NewSimpleClientset())
451+
defer cancel()
452+
453+
err := resolver.Resolve(&rollout)
454+
455+
assert.Error(t, err)
456+
assert.Equal(t, "template must be empty for workload reference rollout", err.Error())
457+
}

test/e2e/functional_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,60 @@ spec:
10631063
ExpectActiveRevision("2")
10641064
}
10651065

1066+
func (s *FunctionalSuite) TestWorkloadRefTemplate() {
1067+
s.Given().
1068+
RolloutObjects(`
1069+
---
1070+
apiVersion: apps/v1
1071+
kind: Deployment
1072+
metadata:
1073+
labels:
1074+
app.kubernetes.io/instance: rollout-canary
1075+
name: rollout-ref-deployment
1076+
spec:
1077+
replicas: 0
1078+
selector:
1079+
matchLabels:
1080+
app: rollout-ref-deployment
1081+
template:
1082+
metadata:
1083+
labels:
1084+
app: rollout-ref-deployment
1085+
spec:
1086+
containers:
1087+
- name: rollouts-demo
1088+
image: argoproj/rollouts-demo:green
1089+
---
1090+
apiVersion: argoproj.io/v1alpha1
1091+
kind: Rollout
1092+
metadata:
1093+
name: rollout-ref-deployment
1094+
spec:
1095+
replicas: 1
1096+
workloadRef:
1097+
apiVersion: apps/v1
1098+
kind: Deployment
1099+
name: rollout-ref-deployment
1100+
selector:
1101+
matchLabels:
1102+
app: rollout-demo-deploy
1103+
template:
1104+
metadata:
1105+
labels:
1106+
app: rollout-ref-deployment
1107+
strategy:
1108+
blueGreen:
1109+
activeService: rollout-bluegreen-active
1110+
`).
1111+
When().
1112+
ApplyManifests().
1113+
WaitForRolloutStatus("Degraded").
1114+
Then().
1115+
ExpectRollout("error due to workload ref and template", func(r *v1alpha1.Rollout) bool {
1116+
return len(r.Status.Conditions) == 1 && strings.Contains(r.Status.Conditions[0].Message, "template must be empty for workload reference rollout")
1117+
})
1118+
}
1119+
10661120
func (s *FunctionalSuite) TestWorkloadRef() {
10671121
s.Given().
10681122
RolloutObjects(`

0 commit comments

Comments
 (0)