Skip to content

Commit a162a1d

Browse files
jlpetterssonScott
authored andcommitted
Add support for repeated PVC-claim but using subPath in AA-validation
The validation for compatibility with the Affinity Assistant does not support the same PVC repeated, but using different subPaths. This patch adds support for this case and tests for the validation. Co-authored-by: Scott <sbws@google.com>
1 parent 8c302b7 commit a162a1d

File tree

3 files changed

+116
-27
lines changed

3 files changed

+116
-27
lines changed

pkg/reconciler/taskrun/taskrun.go

+6-27
Original file line numberDiff line numberDiff line change
@@ -302,10 +302,12 @@ func (c *Reconciler) prepare(ctx context.Context, tr *v1beta1.TaskRun) (*v1beta1
302302
return nil, nil, controller.NewPermanentError(err)
303303
}
304304

305-
if err := validateWorkspaceCompatibilityWithAffinityAssistant(tr); err != nil {
306-
logger.Errorf("TaskRun %q workspaces are invalid: %v", tr.Name, err)
307-
tr.Status.MarkResourceFailed(podconvert.ReasonFailedValidation, err)
308-
return nil, nil, controller.NewPermanentError(err)
305+
if _, usesAssistant := tr.Annotations[workspace.AnnotationAffinityAssistantName]; usesAssistant {
306+
if err := workspace.ValidateOnlyOnePVCIsUsed(tr.Spec.Workspaces); err != nil {
307+
logger.Errorf("TaskRun %q workspaces incompatible with Affinity Assistant: %v", tr.Name, err)
308+
tr.Status.MarkResourceFailed(podconvert.ReasonFailedValidation, err)
309+
return nil, nil, controller.NewPermanentError(err)
310+
}
309311
}
310312

311313
// Initialize the cloud events if at least a CloudEventResource is defined
@@ -775,26 +777,3 @@ func storeTaskSpec(ctx context.Context, tr *v1beta1.TaskRun, ts *v1beta1.TaskSpe
775777
}
776778
return nil
777779
}
778-
779-
// validateWorkspaceCompatibilityWithAffinityAssistant validates the TaskRun's compatibility
780-
// with the Affinity Assistant - if associated with an Affinity Assistant.
781-
// No more than one PVC-backed workspace can be used for a TaskRun that is associated with an
782-
// Affinity Assistant.
783-
func validateWorkspaceCompatibilityWithAffinityAssistant(tr *v1beta1.TaskRun) error {
784-
_, isAssociatedWithAnAffinityAssistant := tr.Annotations[workspace.AnnotationAffinityAssistantName]
785-
if !isAssociatedWithAnAffinityAssistant {
786-
return nil
787-
}
788-
789-
pvcWorkspaces := 0
790-
for _, w := range tr.Spec.Workspaces {
791-
if w.PersistentVolumeClaim != nil || w.VolumeClaimTemplate != nil {
792-
pvcWorkspaces++
793-
}
794-
}
795-
796-
if pvcWorkspaces > 1 {
797-
return fmt.Errorf("TaskRun mounts more than one PersistentVolumeClaim - that is forbidden when the Affinity Assistant is enabled")
798-
}
799-
return nil
800-
}

pkg/workspace/validate.go

+22
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,25 @@ func ValidateBindings(w []v1beta1.WorkspaceDeclaration, wb []v1beta1.WorkspaceBi
4848
}
4949
return nil
5050
}
51+
52+
// ValidateOnlyOnePVCIsUsed checks that a list of WorkspaceBinding uses only one
53+
// persistent volume claim.
54+
//
55+
// This is only useful to validate that WorkspaceBindings in TaskRuns are compatible
56+
// with affinity rules enforced by the AffinityAssistant.
57+
func ValidateOnlyOnePVCIsUsed(wb []v1beta1.WorkspaceBinding) error {
58+
workspaceVolumes := make(map[string]bool)
59+
for _, w := range wb {
60+
if w.PersistentVolumeClaim != nil {
61+
workspaceVolumes[w.PersistentVolumeClaim.ClaimName] = true
62+
}
63+
if w.VolumeClaimTemplate != nil {
64+
workspaceVolumes[w.Name] = true
65+
}
66+
}
67+
68+
if len(workspaceVolumes) > 1 {
69+
return fmt.Errorf("more than one PersistentVolumeClaim is bound")
70+
}
71+
return nil
72+
}

pkg/workspace/validate_test.go

+88
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ limitations under the License.
1717
package workspace
1818

1919
import (
20+
"errors"
2021
"testing"
2122

2223
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1"
24+
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
2325
corev1 "k8s.io/api/core/v1"
2426
)
2527

@@ -131,3 +133,89 @@ func TestValidateBindingsInvalid(t *testing.T) {
131133
})
132134
}
133135
}
136+
137+
func TestValidateOnlyOnePVCIsUsed_Valid(t *testing.T) {
138+
for _, tc := range []struct {
139+
name string
140+
bindings []v1beta1.WorkspaceBinding
141+
}{{
142+
name: "an error is not returned when no bindings are given",
143+
bindings: []v1beta1.WorkspaceBinding{},
144+
}, {
145+
name: "an error is not returned when volume claims are not used",
146+
bindings: []v1beta1.WorkspaceBinding{{
147+
EmptyDir: &corev1.EmptyDirVolumeSource{},
148+
}, {
149+
Secret: &corev1.SecretVolumeSource{},
150+
}},
151+
}, {
152+
name: "an error is not returned when one PV claim is used in two bindings",
153+
bindings: []v1beta1.WorkspaceBinding{{
154+
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
155+
ClaimName: "foo",
156+
},
157+
}, {
158+
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
159+
ClaimName: "foo",
160+
},
161+
}},
162+
}, {
163+
name: "an error is not returned when one PV claim is used in two bindings with different subpaths",
164+
bindings: []v1beta1.WorkspaceBinding{{
165+
SubPath: "/pathA",
166+
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
167+
ClaimName: "foo",
168+
},
169+
}, {
170+
SubPath: "/pathB",
171+
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
172+
ClaimName: "foo",
173+
},
174+
}},
175+
}} {
176+
t.Run(tc.name, func(t *testing.T) {
177+
if err := ValidateOnlyOnePVCIsUsed(tc.bindings); err != nil {
178+
t.Errorf("unexpected error: %v", err)
179+
}
180+
})
181+
}
182+
}
183+
184+
func TestValidateOnlyOnePVCIsUsed_Invalid(t *testing.T) {
185+
validationError := errors.New("more than one PersistentVolumeClaim is bound")
186+
for _, tc := range []struct {
187+
name string
188+
bindings []v1beta1.WorkspaceBinding
189+
wantErr error
190+
}{{
191+
name: "an error is returned when two different PV claims are used",
192+
bindings: []v1beta1.WorkspaceBinding{{
193+
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
194+
ClaimName: "foo",
195+
},
196+
}, {
197+
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
198+
ClaimName: "bar",
199+
},
200+
}},
201+
wantErr: validationError,
202+
}, {
203+
name: "an error is returned when a PVC and volume claim template are mixed",
204+
bindings: []v1beta1.WorkspaceBinding{{
205+
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
206+
ClaimName: "foo",
207+
},
208+
}, {
209+
Name: "bar",
210+
VolumeClaimTemplate: &corev1.PersistentVolumeClaim{},
211+
}},
212+
wantErr: validationError,
213+
}} {
214+
t.Run(tc.name, func(t *testing.T) {
215+
err := ValidateOnlyOnePVCIsUsed(tc.bindings)
216+
if err == nil || (tc.wantErr.Error() != err.Error()) {
217+
t.Errorf("expected %v received %v", tc.wantErr, err)
218+
}
219+
})
220+
}
221+
}

0 commit comments

Comments
 (0)