Skip to content

Commit dd37168

Browse files
Fix file browser pods being scheduled on incorrect nodes when volume to mount is in RWO mode
1 parent 52ff419 commit dd37168

2 files changed

Lines changed: 58 additions & 7 deletions

File tree

backend/src/service/files.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import type { ApiException, V1Job } from "@kubernetes/client-node";
1+
import type {
2+
ApiException,
3+
V1Job,
4+
V1PodAffinity,
5+
} from "@kubernetes/client-node";
26
import crypto, { randomBytes } from "node:crypto";
37
import { setTimeout } from "node:timers/promises";
48
import type { AppRepo } from "../db/repo/app.ts";
@@ -124,6 +128,45 @@ export class FileBrowserService {
124128
}
125129
}
126130

131+
// In RWO mode, the file browser pod needs to be on the same node as the user's pod (since it already has the volume mounted).
132+
// It seems like K8s isn't smart enough to do this automatically while scheduling, so we need to explicitly request that the
133+
// file browser pod is scheduled on the same node as the user's pod.
134+
const affinityRequired = volume.spec.accessModes?.includes("ReadWriteOnce");
135+
136+
let affinity: V1PodAffinity = {};
137+
138+
if (affinityRequired) {
139+
const pods = await this.kubernetesService.listNamespacedPod({
140+
namespace,
141+
labelSelector: "anvilops.rcac.purdue.edu/deployment-id",
142+
});
143+
144+
const matchingPod = pods.items.find((pod) =>
145+
pod.spec.volumes.some(
146+
(it) => it.persistentVolumeClaim?.claimName === volumeClaimName,
147+
),
148+
);
149+
150+
if (matchingPod) {
151+
// This is the pod we're looking for, since it mounts the volume we want to mount in our new file browser pod
152+
affinity = {
153+
requiredDuringSchedulingIgnoredDuringExecution: [
154+
{
155+
topologyKey: "kubernetes.io/hostname",
156+
labelSelector: {
157+
matchLabels: matchingPod.metadata.labels,
158+
},
159+
},
160+
],
161+
};
162+
} else {
163+
logger.warn(
164+
{ namespace, volumeName: volume.spec.volumeName },
165+
"Affinity required for file browser pod, but target pod not found",
166+
);
167+
}
168+
}
169+
127170
let job: V1Job;
128171
try {
129172
job = await this.kubernetesService.createNamespacedJob({
@@ -138,6 +181,7 @@ export class FileBrowserService {
138181
activeDeadlineSeconds: 30 * 60, // Kill after 30 minutes
139182
template: {
140183
spec: {
184+
affinity: { podAffinity: affinity },
141185
containers: [
142186
{
143187
name: "file-browser",

frontend/src/pages/app/FilesTab.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -704,12 +704,19 @@ const FileUpload = ({
704704
const promise = fetch(uploadURL, {
705705
method: "POST",
706706
body: formData,
707-
})
708-
.then(() => {
709-
refresh();
710-
setOpen(false);
711-
})
712-
.catch(console.error);
707+
}).then((response) => {
708+
if (!response.ok) {
709+
throw new Error(
710+
"Unexpected status " +
711+
response.status +
712+
": " +
713+
response.statusText,
714+
);
715+
}
716+
refresh();
717+
setOpen(false);
718+
});
719+
promise.catch(console.error);
713720
toast.promise(promise, {
714721
success: "Files uploaded successfully!",
715722
error: "There was a problem uploading files.",

0 commit comments

Comments
 (0)