Skip to content

Commit f0704e4

Browse files
[TEP-0135] Revert PVC creation - e2e
Part of [tektoncd#6740] and unblocks [tektoncd#6635]. `PVCs` are created if a user specifies `VolumeClaimTemplate` as the source of a `pipelinerun workspace`. Prior to this change, such `pvcs` are created via `affinity assistant statefulset` when `affinity assistant` is enabled (in both `workspaces` or `pipelineruns` coschedule mode). To delete such pvcs when the owning `pipelinerun` is completed, we must explicitly delete those pvcs in the reconciler since the owner of such pvcs is the `affinity assistant statefulset` instead of the `pipelinerun`. The problem of such deletion mechanism is that such `pvcs` are left in `terminating` state when the owning `pipelinerun` is `completed` but not `deleted`. This is because the pvcs are protected by `kubernetes.io/pvc-protection` `finalizer`, which does not allow the `pvcs` to be deleted until the `pipelinerun` consuming the `pvc` is deleted. Leaving pvcs in `terminating` state is confusing to cluster operators. Instead of changing the pvc deletion behavior in such backward incompatible way, it is better to make it configurable so that it is backward compatible, as prototyped in [tektoncd#6635]. This commit reverts the pvc creation behavior `per-workspace` coschedule mode, which changes the owner of the `pvcs` back to the `pipelinerun` instead of the `affinity assistant statefulset`. After the commit, the pvcs created by specifying `VolumeClaimTemplate` are left in `bounded` state instead of `terminating`. This commit is the prerequisite of [tektoncd#6635]. This commit does NOT reverts the pvc creation behavior `per-pipelinerun` coschedule mode as we will enforce the deletion of pvcs when the owning `pipelinerun` is completed. This is a better practice and there is no backward compatability concern since `per-pipelinerun` coschedule mode is a new feature. [tektoncd#6740]: tektoncd#6740 [tektoncd#6635]: tektoncd#6635
1 parent b769b56 commit f0704e4

File tree

5 files changed

+196
-116
lines changed

5 files changed

+196
-116
lines changed

pkg/reconciler/pipelinerun/affinity_assistant.go

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ import (
4040
)
4141

4242
const (
43-
// ReasonCouldntCreateOrUpdateAffinityAssistantStatefulSetPerWorkspace indicates that a PipelineRun uses workspaces with PersistentVolumeClaim
43+
// ReasonCouldntCreateOrUpdateAffinityAssistantStatefulSet indicates that a PipelineRun uses workspaces with PersistentVolumeClaim
4444
// as a volume source and expect an Assistant StatefulSet in AffinityAssistantPerWorkspace behavior, but couldn't create a StatefulSet.
45-
ReasonCouldntCreateOrUpdateAffinityAssistantStatefulSetPerWorkspace = "ReasonCouldntCreateOrUpdateAffinityAssistantStatefulSetPerWorkspace"
45+
ReasonCouldntCreateOrUpdateAffinityAssistantStatefulSet = "ReasonCouldntCreateOrUpdateAffinityAssistantStatefulSet"
4646

4747
featureFlagDisableAffinityAssistantKey = "disable-affinity-assistant"
4848
)
@@ -103,18 +103,21 @@ func (c *Reconciler) createOrUpdateAffinityAssistantsAndPVCs(ctx context.Context
103103
errs = append(errs, err...)
104104
}
105105
case aa.AffinityAssistantPerPipelineRun, aa.AffinityAssistantPerPipelineRunWithIsolation:
106-
if claims != nil || claimTemplates != nil {
107-
aaName := GetAffinityAssistantName("", pr.Name)
108-
// In AffinityAssistantPerPipelineRun or AffinityAssistantPerPipelineRunWithIsolation modes, the PVCs are created via StatefulSet for volume scheduling.
109-
// PVCs from pipelinerun's VolumeClaimTemplate are enforced to be deleted at pipelinerun completion time,
110-
// so we don't need to worry the OwnerReference of the PVCs
111-
err := c.createOrUpdateAffinityAssistant(ctx, aaName, pr, claimTemplates, claims, unschedulableNodes)
112-
errs = append(errs, err...)
113-
}
106+
aaName := GetAffinityAssistantName("", pr.Name)
107+
// In AffinityAssistantPerPipelineRun or AffinityAssistantPerPipelineRunWithIsolation modes, the PVCs are created via StatefulSet for volume scheduling.
108+
// PVCs from pipelinerun's VolumeClaimTemplate are enforced to be deleted at pipelinerun completion time,
109+
// so we don't need to worry the OwnerReference of the PVCs
110+
err := c.createOrUpdateAffinityAssistant(ctx, aaName, pr, claimTemplates, claims, unschedulableNodes)
111+
errs = append(errs, err...)
114112
case aa.AffinityAssistantDisabled:
115113
}
116114

117-
return errorutils.NewAggregate(errs)
115+
failReason := ""
116+
if errs != nil {
117+
failReason = ReasonCouldntCreateOrUpdateAffinityAssistantStatefulSet
118+
}
119+
120+
return errorutils.NewAggregate(errs), failReason
118121
}
119122

120123
// createOrUpdateAffinityAssistant creates an Affinity Assistant Statefulset with the provided affinityAssistantName and pipelinerun information.
@@ -178,29 +181,39 @@ func (c *Reconciler) createOrUpdateAffinityAssistant(ctx context.Context, affini
178181
return errs
179182
}
180183

181-
// TODO(#6740)(WIP) implement cleanupAffinityAssistants for AffinityAssistantPerPipelineRun and AffinityAssistantPerPipelineRunWithIsolation affinity assistant modes
184+
// cleanupAffinityAssistants deletes affinity assistant StatefulSet
182185
func (c *Reconciler) cleanupAffinityAssistants(ctx context.Context, pr *v1.PipelineRun) error {
183-
// omit cleanup if the feature is disabled
184-
if c.isAffinityAssistantDisabled(ctx) {
185-
return nil
186+
aaBehavior, err := aa.GetAffinityAssistantBehavior(ctx)
187+
if err != nil {
188+
return err
186189
}
187190

188191
var errs []error
189-
for _, w := range pr.Spec.Workspaces {
190-
if w.PersistentVolumeClaim != nil || w.VolumeClaimTemplate != nil {
191-
affinityAssistantStsName := GetAffinityAssistantName(w.Name, pr.Name)
192-
if err := c.KubeClientSet.AppsV1().StatefulSets(pr.Namespace).Delete(ctx, affinityAssistantStsName, metav1.DeleteOptions{}); err != nil && !apierrors.IsNotFound(err) {
193-
errs = append(errs, fmt.Errorf("failed to delete StatefulSet %s: %w", affinityAssistantStsName, err))
192+
switch aaBehavior {
193+
case aa.AffinityAssistantPerWorkspace:
194+
for _, w := range pr.Spec.Workspaces {
195+
if w.PersistentVolumeClaim != nil || w.VolumeClaimTemplate != nil {
196+
affinityAssistantStsName := GetAffinityAssistantName(w.Name, pr.Name)
197+
if err := c.KubeClientSet.AppsV1().StatefulSets(pr.Namespace).Delete(ctx, affinityAssistantStsName, metav1.DeleteOptions{}); err != nil && !apierrors.IsNotFound(err) {
198+
errs = append(errs, fmt.Errorf("failed to delete StatefulSet %s: %w", affinityAssistantStsName, err))
199+
}
194200
}
195201
}
202+
case aa.AffinityAssistantPerPipelineRun, aa.AffinityAssistantPerPipelineRunWithIsolation:
203+
affinityAssistantStsName := GetAffinityAssistantName("", pr.Name)
204+
if err := c.KubeClientSet.AppsV1().StatefulSets(pr.Namespace).Delete(ctx, affinityAssistantStsName, metav1.DeleteOptions{}); err != nil && !apierrors.IsNotFound(err) {
205+
errs = append(errs, fmt.Errorf("failed to delete StatefulSet %s: %w", affinityAssistantStsName, err))
206+
}
207+
case aa.AffinityAssistantDisabled:
208+
return nil
196209
}
210+
197211
return errorutils.NewAggregate(errs)
198212
}
199213

200214
// getPersistentVolumeClaimNameWithAffinityAssistant returns the PersistentVolumeClaim name that is
201215
// created by the Affinity Assistant StatefulSet VolumeClaimTemplate when Affinity Assistant is enabled.
202216
// The PVCs created by StatefulSet VolumeClaimTemplates follow the format `<pvcName>-<affinityAssistantName>-0`
203-
// TODO(#6740)(WIP): use this function when adding end-to-end support for AffinityAssistantPerPipelineRun mode
204217
func getPersistentVolumeClaimNameWithAffinityAssistant(pipelineWorkspaceName, prName string, wb v1.WorkspaceBinding, owner metav1.OwnerReference) string {
205218
pvcName := volumeclaim.GetPVCNameWithoutAffinityAssistant(wb.VolumeClaimTemplate.Name, wb, owner)
206219
affinityAssistantName := GetAffinityAssistantName(pipelineWorkspaceName, prName)
@@ -322,17 +335,6 @@ func affinityAssistantStatefulSet(name string, pr *v1.PipelineRun, claimTemplate
322335
}
323336
}
324337

325-
// isAffinityAssistantDisabled returns a bool indicating whether an Affinity Assistant should
326-
// be created for each PipelineRun that use workspaces with PersistentVolumeClaims
327-
// as volume source. The default behaviour is to enable the Affinity Assistant to
328-
// provide Node Affinity for TaskRuns that share a PVC workspace.
329-
//
330-
// TODO(#6740)(WIP): replace this function with GetAffinityAssistantBehavior
331-
func (c *Reconciler) isAffinityAssistantDisabled(ctx context.Context) bool {
332-
cfg := config.FromContextOrDefaults(ctx)
333-
return cfg.FeatureFlags.DisableAffinityAssistant
334-
}
335-
336338
// getAssistantAffinityMergedWithPodTemplateAffinity return the affinity that merged with PipelineRun PodTemplate affinity.
337339
func getAssistantAffinityMergedWithPodTemplateAffinity(pr *v1.PipelineRun) *corev1.Affinity {
338340
// use podAntiAffinity to repel other affinity assistants

pkg/reconciler/pipelinerun/affinity_assistant_test.go

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -754,53 +754,6 @@ func TestThatCleanupIsAvoidedIfAssistantIsDisabled(t *testing.T) {
754754
}
755755
}
756756

757-
func TestDisableAffinityAssistant(t *testing.T) {
758-
for _, tc := range []struct {
759-
description string
760-
configMap *corev1.ConfigMap
761-
expected bool
762-
}{{
763-
description: "Default behaviour: A missing disable-affinity-assistant flag should result in false",
764-
configMap: &corev1.ConfigMap{
765-
ObjectMeta: metav1.ObjectMeta{Name: config.GetFeatureFlagsConfigName(), Namespace: system.Namespace()},
766-
Data: map[string]string{},
767-
},
768-
expected: false,
769-
}, {
770-
description: "Setting disable-affinity-assistant to false should result in false",
771-
configMap: &corev1.ConfigMap{
772-
ObjectMeta: metav1.ObjectMeta{Name: config.GetFeatureFlagsConfigName(), Namespace: system.Namespace()},
773-
Data: map[string]string{
774-
featureFlagDisableAffinityAssistantKey: "false",
775-
},
776-
},
777-
expected: false,
778-
}, {
779-
description: "Setting disable-affinity-assistant to true should result in true",
780-
configMap: &corev1.ConfigMap{
781-
ObjectMeta: metav1.ObjectMeta{Name: config.GetFeatureFlagsConfigName(), Namespace: system.Namespace()},
782-
Data: map[string]string{
783-
featureFlagDisableAffinityAssistantKey: "true",
784-
},
785-
},
786-
expected: true,
787-
}} {
788-
t.Run(tc.description, func(t *testing.T) {
789-
c := Reconciler{
790-
KubeClientSet: fakek8s.NewSimpleClientset(
791-
tc.configMap,
792-
),
793-
Images: pipeline.Images{},
794-
}
795-
store := config.NewStore(logtesting.TestLogger(t))
796-
store.OnConfigChanged(tc.configMap)
797-
if result := c.isAffinityAssistantDisabled(store.ToContext(context.Background())); result != tc.expected {
798-
t.Errorf("Expected %t Received %t", tc.expected, result)
799-
}
800-
})
801-
}
802-
}
803-
804757
func TestGetAssistantAffinityMergedWithPodTemplateAffinity(t *testing.T) {
805758
assistantPodAffinityTerm := corev1.WeightedPodAffinityTerm{
806759
Weight: 100,

pkg/reconciler/pipelinerun/pipelinerun.go

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -611,25 +611,12 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1.PipelineRun, getPipel
611611
return controller.NewPermanentError(err)
612612
}
613613

614-
switch aaBehavior {
615-
case affinityassistant.AffinityAssistantPerWorkspace, affinityassistant.AffinityAssistantDisabled:
616-
if err = c.createOrUpdateAffinityAssistantsAndPVCs(ctx, pr, aaBehavior); err != nil {
617-
logger.Errorf("Failed to create PVC or affinity assistant StatefulSet for PipelineRun %s: %v", pr.Name, err)
618-
if aaBehavior == affinityassistant.AffinityAssistantPerWorkspace {
619-
pr.Status.MarkFailed(ReasonCouldntCreateOrUpdateAffinityAssistantStatefulSetPerWorkspace,
620-
"Failed to create StatefulSet for PipelineRun %s/%s correctly: %s",
621-
pr.Namespace, pr.Name, err)
622-
} else {
623-
pr.Status.MarkFailed(volumeclaim.ReasonCouldntCreateWorkspacePVC,
624-
"Failed to create PVC for PipelineRun %s/%s Workspaces correctly: %s",
625-
pr.Namespace, pr.Name, err)
626-
}
627-
628-
return controller.NewPermanentError(err)
629-
}
630-
case affinityassistant.AffinityAssistantPerPipelineRun, affinityassistant.AffinityAssistantPerPipelineRunWithIsolation:
631-
// TODO(#6740)(WIP): implement end-to-end support for AffinityAssistantPerPipelineRun and AffinityAssistantPerPipelineRunWithIsolation modes
632-
return controller.NewPermanentError(fmt.Errorf("affinity assistant behavior: %v is not implemented", aaBehavior))
614+
// TODO: better error message
615+
if err, reason := c.createOrUpdateAffinityAssistantsAndPVCsPerAABehavior(ctx, pr, aaBehavior); err != nil {
616+
logger.Errorf("Failed to create PVC or affinity assistant StatefulSet for PipelineRun %s: %v", pr.Name, err)
617+
pr.Status.MarkFailed(reason,
618+
"Failed to create PVC or StatefulSet for PipelineRun %s/%s correctly: %s",
619+
pr.Namespace, pr.Name, err)
633620
}
634621
}
635622

@@ -876,8 +863,20 @@ func (c *Reconciler) createTaskRun(ctx context.Context, taskRunName string, para
876863
return nil, err
877864
}
878865

879-
if !c.isAffinityAssistantDisabled(ctx) && pipelinePVCWorkspaceName != "" {
880-
tr.Annotations[workspace.AnnotationAffinityAssistantName] = GetAffinityAssistantName(pipelinePVCWorkspaceName, pr.Name)
866+
aaBehavior, err := affinityassistant.GetAffinityAssistantBehavior(ctx)
867+
if err != nil {
868+
return nil, err
869+
}
870+
871+
switch aaBehavior {
872+
case affinityassistant.AffinityAssistantPerWorkspace:
873+
if pipelinePVCWorkspaceName != "" {
874+
tr.Annotations[workspace.AnnotationAffinityAssistantName] = GetAffinityAssistantName(pipelinePVCWorkspaceName, pr.Name)
875+
}
876+
case affinityassistant.AffinityAssistantPerPipelineRun, affinityassistant.AffinityAssistantPerPipelineRunWithIsolation:
877+
tr.Annotations[workspace.AnnotationAffinityAssistantName] = GetAffinityAssistantName("", pr.Name)
878+
879+
case affinityassistant.AffinityAssistantDisabled:
881880
}
882881

883882
logger.Infof("Creating a new TaskRun object %s for pipeline task %s", taskRunName, rpt.PipelineTask.Name)
@@ -997,10 +996,23 @@ func (c *Reconciler) createCustomRun(ctx context.Context, runName string, params
997996
},
998997
}
999998
}
999+
10001000
// Set the affinity assistant annotation in case the custom task creates TaskRuns or Pods
10011001
// that can take advantage of it.
1002-
if !c.isAffinityAssistantDisabled(ctx) && pipelinePVCWorkspaceName != "" {
1003-
r.Annotations[workspace.AnnotationAffinityAssistantName] = GetAffinityAssistantName(pipelinePVCWorkspaceName, pr.Name)
1002+
aaBehavior, err := affinityassistant.GetAffinityAssistantBehavior(ctx)
1003+
if err != nil {
1004+
return nil, err
1005+
}
1006+
1007+
switch aaBehavior {
1008+
case affinityassistant.AffinityAssistantPerWorkspace:
1009+
if pipelinePVCWorkspaceName != "" {
1010+
r.Annotations[workspace.AnnotationAffinityAssistantName] = GetAffinityAssistantName(pipelinePVCWorkspaceName, pr.Name)
1011+
}
1012+
case affinityassistant.AffinityAssistantPerPipelineRun, affinityassistant.AffinityAssistantPerPipelineRunWithIsolation:
1013+
r.Annotations[workspace.AnnotationAffinityAssistantName] = GetAffinityAssistantName("", pr.Name)
1014+
1015+
case affinityassistant.AffinityAssistantDisabled:
10041016
}
10051017

10061018
logger.Infof("Creating a new CustomRun object %s", runName)
@@ -1116,9 +1128,11 @@ func (c *Reconciler) taskWorkspaceByWorkspaceVolumeSource(ctx context.Context, p
11161128
}
11171129
binding.Name = taskWorkspaceName
11181130

1119-
// TODO(#6740)(WIP): get binding for AffinityAssistantPerPipelineRun and AffinityAssistantPerPipelineRunWithIsolation mode
1120-
if aaBehavior == affinityassistant.AffinityAssistantDisabled || aaBehavior == affinityassistant.AffinityAssistantPerWorkspace {
1131+
switch aaBehavior {
1132+
case affinityassistant.AffinityAssistantPerWorkspace, affinityassistant.AffinityAssistantDisabled:
11211133
binding.PersistentVolumeClaim.ClaimName = volumeclaim.GetPVCNameWithoutAffinityAssistant(wb.VolumeClaimTemplate.Name, wb, owner)
1134+
case affinityassistant.AffinityAssistantPerPipelineRun, affinityassistant.AffinityAssistantPerPipelineRunWithIsolation:
1135+
binding.PersistentVolumeClaim.ClaimName = getPersistentVolumeClaimNameWithAffinityAssistant("", prName, wb, owner)
11221136
}
11231137

11241138
return binding

pkg/reconciler/taskrun/taskrun.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,11 @@ func (c *Reconciler) prepare(ctx context.Context, tr *v1.TaskRun) (*v1.TaskSpec,
418418
return nil, nil, controller.NewPermanentError(err)
419419
}
420420

421-
if _, usesAssistant := tr.Annotations[workspace.AnnotationAffinityAssistantName]; usesAssistant {
421+
aaBehavior, err := affinityassistant.GetAffinityAssistantBehavior(ctx)
422+
if err != nil {
423+
return nil, nil, controller.NewPermanentError(err)
424+
}
425+
if aaBehavior == affinityassistant.AffinityAssistantPerWorkspace {
422426
if err := workspace.ValidateOnlyOnePVCIsUsed(tr.Spec.Workspaces); err != nil {
423427
logger.Errorf("TaskRun %q workspaces incompatible with Affinity Assistant: %v", tr.Name, err)
424428
tr.Status.MarkResourceFailed(podconvert.ReasonFailedValidation, err)

0 commit comments

Comments
 (0)