From 13bd5e5e2c5b5c5e7812c2ea95375674daee0096 Mon Sep 17 00:00:00 2001 From: Liam Beckman Date: Thu, 21 Nov 2024 19:17:01 -0800 Subject: [PATCH 1/8] Add initial support for S3 backed PVCs --- compute/kubernetes/backend.go | 108 +++++++++++++++++- config/internal/bundle.go | 16 +-- config/kubernetes-executor-template.yaml | 14 +-- config/kubernetes-template.yaml | 9 +- deployments/kubernetes/helm/Chart.yaml | 2 +- .../kubernetes/helm/templates/NOTES.txt | 4 +- .../helm/templates/clusterrole.yaml | 15 +++ .../helm/templates/clusterrolebinding.yaml | 12 ++ .../kubernetes/helm/templates/role.yaml | 2 +- examples/internal/bundle.go | 8 +- 10 files changed, 153 insertions(+), 37 deletions(-) create mode 100644 deployments/kubernetes/helm/templates/clusterrole.yaml create mode 100644 deployments/kubernetes/helm/templates/clusterrolebinding.yaml diff --git a/compute/kubernetes/backend.go b/compute/kubernetes/backend.go index ba7ea4590..bc14b7a3a 100644 --- a/compute/kubernetes/backend.go +++ b/compute/kubernetes/backend.go @@ -21,6 +21,7 @@ import ( batchv1 "k8s.io/client-go/kubernetes/typed/batch/v1" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" + "k8s.io/utils/ptr" "github.com/ohsu-comp-bio/funnel/config" "github.com/ohsu-comp-bio/funnel/events" @@ -172,19 +173,67 @@ func (b *Backend) createJob(task *tes.Task) (*v1.Job, error) { } func (b *Backend) createPVC(ctx context.Context, taskID string, resources *tes.Resources) error { - clientset, err := kubernetes.NewForConfig(b.config) // You'll need to store the config during NewBackend + clientset, err := kubernetes.NewForConfig(b.config) if err != nil { return fmt.Errorf("getting kubernetes client: %v", err) } - storageSize := resource.NewQuantity(1024*1024*1024, resource.BinarySI) // 1Gi default + // Define storage size (ignored by S3 CSI driver but required by the API) + storageSize := resource.NewQuantity(1024*1024*1024, resource.BinarySI) // Default 1Gi if resources != nil && resources.DiskGb > 0 { storageSize = resource.NewQuantity(int64(resources.DiskGb*1024*1024*1024), resource.BinarySI) } + pvName := fmt.Sprintf("funnel-pv-%s", taskID) + pvcName := fmt.Sprintf("funnel-pvc-%s", taskID) + bucketName := "funnel-testing" + + // Step 1: Create the PersistentVolume + pv := &corev1.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{ + Name: pvName, + Labels: map[string]string{ + "app": "funnel", + "taskId": taskID, + }, + }, + Spec: corev1.PersistentVolumeSpec{ + Capacity: corev1.ResourceList{ + corev1.ResourceStorage: *storageSize, + }, + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteMany, // S3 CSI supports RWX + }, + PersistentVolumeReclaimPolicy: corev1.PersistentVolumeReclaimRetain, + StorageClassName: "", + ClaimRef: &corev1.ObjectReference{ + Namespace: b.namespace, + Name: pvcName, + }, + MountOptions: []string{ + "allow-delete", + "region=us-west-2", + }, + PersistentVolumeSource: corev1.PersistentVolumeSource{ + CSI: &corev1.CSIPersistentVolumeSource{ + Driver: "s3.csi.aws.com", + VolumeHandle: fmt.Sprintf("s3-csi-%s", taskID), + VolumeAttributes: map[string]string{ + "bucketName": bucketName, + }, + }, + }, + }, + } + + if _, err := clientset.CoreV1().PersistentVolumes().Create(ctx, pv, metav1.CreateOptions{}); err != nil { + return fmt.Errorf("creating PersistentVolume: %v", err) + } + + // Step 2: Create the PersistentVolumeClaim pvc := &corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("funnel-pvc-%s", taskID), + Name: pvcName, Labels: map[string]string{ "app": "funnel", "taskId": taskID, @@ -192,19 +241,61 @@ func (b *Backend) createPVC(ctx context.Context, taskID string, resources *tes.R }, Spec: corev1.PersistentVolumeClaimSpec{ AccessModes: []corev1.PersistentVolumeAccessMode{ - corev1.ReadWriteOnce, + corev1.ReadWriteMany, }, Resources: corev1.VolumeResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceStorage: *storageSize, }, }, + StorageClassName: func() *string { s := ""; return &s }(), + VolumeName: pvName, }, } - _, err = clientset.CoreV1().PersistentVolumeClaims(b.namespace).Create(ctx, pvc, metav1.CreateOptions{}) + if _, err := clientset.CoreV1().PersistentVolumeClaims(b.namespace).Create(ctx, pvc, metav1.CreateOptions{}); err != nil { + return fmt.Errorf("creating PersistentVolumeClaim: %v", err) + } + + return nil +} + +func (b *Backend) addOwnerReference(ctx context.Context, taskID string, job *v1.Job) error { + // Fetch the Job that will own the PVC and PV + jobName := fmt.Sprintf("funnel-job-%s", taskID) + _, err := b.client.Get(ctx, jobName, metav1.GetOptions{}) if err != nil { - return fmt.Errorf("creating shared PVC: %v", err) + return fmt.Errorf("fetching Job for owner reference: %v", err) + } + + // Fetch the PVC + pvcName := fmt.Sprintf("funnel-pvc-%s", taskID) + clientset, err := kubernetes.NewForConfig(b.config) + if err != nil { + return fmt.Errorf("getting kubernetes client: %v", err) + } + pvc, err := clientset.CoreV1().PersistentVolumeClaims(b.namespace).Get(ctx, pvcName, metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("fetching PVC: %v", err) + } + + // Add OwnerReference for the Job + ownerRef := metav1.OwnerReference{ + APIVersion: "batch/v1", + Kind: "Job", + Name: job.Name, + UID: job.UID, + BlockOwnerDeletion: ptr.To(true), + Controller: ptr.To(true), + } + + // Append the OwnerReference to the PVC + pvc.OwnerReferences = append(pvc.OwnerReferences, ownerRef) + + // Update the PVC + _, err = clientset.CoreV1().PersistentVolumeClaims(b.namespace).Update(ctx, pvc, metav1.UpdateOptions{}) + if err != nil { + return fmt.Errorf("updating PVC: %v", err) } return nil @@ -257,6 +348,11 @@ func (b *Backend) Submit(ctx context.Context, task *tes.Task) error { return fmt.Errorf("creating job in backend: %v", err) } + err = b.addOwnerReference(submitCtx, task.Id, job) + if err != nil { + return fmt.Errorf("updating PVC with OwnerReference: %v", err) + } + return nil } diff --git a/config/internal/bundle.go b/config/internal/bundle.go index c4f9a5310..59e082e62 100644 --- a/config/internal/bundle.go +++ b/config/internal/bundle.go @@ -3,10 +3,10 @@ // config/gridengine-template.txt (346B) // config/pbs-template.txt (361B) // config/slurm-template.txt (415B) -// config/kubernetes-executor-template.yaml (1.764kB) +// config/kubernetes-executor-template.yaml (1.232kB) // config/default-config.yaml (11.655kB) // config/htcondor-template.txt (505B) -// config/kubernetes-template.yaml (1.34kB) +// config/kubernetes-template.yaml (1.483kB) package config @@ -134,7 +134,7 @@ func configSlurmTemplateTxt() (*asset, error) { return a, nil } -var _configKubernetesExecutorTemplateYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x95\xc1\x8e\xdb\x36\x10\x86\xef\x7e\x8a\x81\xbc\x45\x2f\x91\x57\x7b\xe9\x41\x40\x0f\x0b\x6f\xb0\xd9\xa0\x71\x16\xc9\x36\x3d\x14\x3d\x50\xd4\xc8\x66\x2d\x92\xea\xcc\xd0\x59\x43\xf5\xbb\x17\x94\x64\x45\xda\x38\x29\xa2\x13\xc5\x19\x7e\xf3\xcf\xaf\xa1\xbd\x84\x27\xc5\x7b\x78\xfd\x8c\x3a\x88\xa7\x85\x6a\xcc\x27\x24\x36\xde\xe5\x50\x28\xd1\xbb\xeb\xc3\xcd\x62\x6f\x5c\x99\xc3\x5b\x5f\x2c\x2c\x8a\x2a\x95\xa8\x7c\x01\xe0\x94\xc5\x1c\xda\x76\x15\x09\x0f\xe5\xe9\x94\xb6\xed\xea\xad\x2f\xe2\x72\x08\x73\xa3\x74\x9f\xb3\x39\xbf\x75\xb1\x5a\x15\x58\x73\x84\x00\xfc\xed\x8b\xf4\xbb\x28\x6e\x50\xc7\xd4\x42\xe9\xbd\xaf\xaa\xdf\x8c\x35\x92\x43\xb6\x00\xd0\xde\x36\x35\x8a\xf1\x8e\x73\xb8\x59\x00\x08\xda\xa6\x56\x82\x3d\xf9\x7c\x30\x3e\x84\x2c\x8a\xe4\xd1\xd7\x46\x1f\x73\xd8\xe0\x01\x69\x08\x31\xd2\xc1\x68\xbc\xd5\xda\x07\x27\x9b\x4e\x4a\x15\x9c\xc3\x3a\x65\x35\xe4\x2c\xe1\x23\x4a\x68\x80\x8f\xb6\x36\x6e\xcf\x50\x91\xb7\xf0\xd9\xd3\x1e\x4a\x43\x20\x1e\x44\xd1\x16\x05\x1a\x25\x3b\x1e\x0e\x19\x67\x64\xed\x9d\x28\xe3\x90\xf8\xac\x24\x1d\x8c\xe3\x08\x4c\x4b\x43\xe7\x74\x00\x63\xd5\x16\x73\x28\x02\x1f\x0b\xff\x3c\x6e\x6b\x6f\xad\x8a\x5f\xe0\xcf\xe4\xba\x30\xee\x9a\x77\xc9\x2b\x48\x52\x9d\xfc\x35\xa6\x28\xda\x8e\x05\xfa\x22\xff\x4e\xde\xa2\xfe\x35\xa1\x12\x04\x15\xf5\xa2\x16\x4f\xc7\xa8\xba\x30\xae\x04\x1b\xfb\x06\xd9\x61\xd7\x10\xd2\xcf\x0c\x95\xa9\x91\x2f\x13\x1a\x45\xe8\x64\xc4\x18\x64\xa8\x3c\x81\x72\xc7\xae\xf9\xce\x75\x53\x19\x2c\xc1\xb8\x0e\x2a\x8a\xf7\x97\x51\x31\x3a\x38\xfa\xc5\x50\xa4\x0b\x96\xce\xce\xa3\xde\x79\x48\x66\xee\xe6\x3d\xd2\xb8\xed\x4c\x98\x72\xe5\xf8\xc9\x92\x91\xc1\xa8\x03\x19\x39\xc6\xd3\xf8\x2c\x53\xdf\x28\xb8\x5b\xfe\x9d\x23\x30\x8b\x42\x3f\x04\x07\x8a\x81\xbc\x97\xa8\x08\x1d\x07\xc2\x89\x85\xba\xab\xea\x5d\xa7\xfc\x6c\x98\xfe\xe6\x37\x1f\xc6\xaa\x6f\x33\x9d\xcc\xfb\xcb\x11\x68\xdb\xd5\x43\x5c\xbd\x8c\x3c\x86\xba\x3e\xcf\xf0\x6d\xfd\x59\x1d\xf9\x87\xa7\x24\xb2\xd7\x7d\xee\x84\x1e\x25\x19\xb7\xbd\x33\xd4\x25\xfc\xe1\x69\x5f\x1a\x9a\x24\x10\xb2\x0f\xa4\x71\x36\x66\x84\xff\x04\x64\x99\xed\x01\xe8\x26\x44\x88\xa9\xc0\x21\xac\xd6\x4d\x60\xc8\x20\x3d\x9d\x62\xe1\x26\x70\x5c\x00\xd6\x8c\x10\x57\xc9\x4d\x96\xd9\x24\xae\x70\x26\x28\x3e\x16\xad\xa7\xe3\x84\xf5\x41\xd9\xfb\x02\xb2\xd5\x80\x6b\xc8\x38\xa9\x20\xf9\x69\x95\x55\xf7\xc9\x10\xee\x50\x35\x63\x0f\xff\xe5\xdd\x37\xd8\xd8\xec\xd0\x22\xa9\x3a\x65\xf1\x34\x98\x3e\x94\xb9\x33\xbc\xff\x5e\x9d\x3e\x3e\x2f\x94\x65\x97\x2a\x8d\x8b\x83\xaf\x83\xc5\x77\xf1\xa6\x4d\xdc\x5a\x2e\x97\x70\xf7\x1e\x36\xef\x9f\x60\xfd\xe6\x76\x73\xff\x1a\x9e\xde\x3c\x7c\x1c\xc3\x6d\x4b\xca\x6d\x11\xae\x4c\xf9\xfc\x0a\xae\x8c\xa0\x85\xfc\x57\x58\x7d\xea\x60\x3c\xa9\xf3\x62\xc0\x86\x96\xd2\xb6\xbd\xfa\x7a\xc4\xa0\xbf\xf0\x8f\x4a\x76\xb1\xe7\x0e\xbb\x1a\x6f\x52\xdc\x9e\x25\x73\x28\xc6\xd4\x11\xf6\x7f\xc7\xce\x3e\x2c\xa6\xdd\x73\x0e\x97\xef\xc3\x17\xb9\x5f\xab\x6d\xe2\xbf\x11\x0b\x3a\xe9\xbb\x5e\xd7\xca\xd8\xe9\xbc\xe9\xb8\x31\xfb\xd5\x6e\x0e\x7a\x86\xfa\x2f\x00\x00\xff\xff\x63\xd1\xdc\x25\xe4\x06\x00\x00") +var _configKubernetesExecutorTemplateYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x54\xcd\x4f\xdb\x4e\x10\xbd\xfb\xaf\x18\x39\xfc\x6e\xd8\x98\xcb\xef\x60\xa9\x07\x14\x10\x1f\x2a\x01\xb5\x88\x1e\xaa\x1e\xc6\x9b\x49\xb2\xcd\x7e\x75\x67\x1d\x40\x56\xfe\xf7\x6a\xfd\x55\x07\x28\xf5\x69\xb2\xef\xf9\xbd\x37\x93\x59\xcf\xe0\x01\x79\x0b\x17\xcf\x24\xea\x60\x7d\x82\x4e\x3e\x92\x67\x69\x4d\x09\x15\x06\xb1\x39\xd9\x9d\x26\x5b\x69\x96\x25\xdc\xd8\x2a\xd1\x14\x70\x89\x01\xcb\x04\xc0\xa0\xa6\x12\x9a\x26\x8f\x0a\xd7\xcb\xfd\x3e\x6b\x9a\xfc\xc6\x56\xb1\xec\x61\x76\x28\x3a\xce\x62\xf8\xd5\x62\x0a\x2b\x52\x1c\x45\x00\xd0\xb9\x12\x56\xb5\x31\xa4\x32\x1a\x62\x44\xe0\xa7\xad\xb2\x0f\x3d\xd8\x91\x88\x1a\x15\x8a\xad\x5d\xad\x3e\x4b\x2d\x43\x09\x45\x02\x20\xac\x76\x8a\x82\xb4\x86\x4b\x38\x4d\x00\x02\x69\xa7\x30\x50\x67\x39\xbc\x18\x1f\x4f\x1c\xd0\x87\x7b\xab\xa4\x78\x29\x61\x41\x3b\xf2\x3d\xc4\xe4\x77\x52\xd0\x99\x10\xb6\x36\x61\xd1\x46\xe9\x83\x32\xf6\x1c\x61\x4d\x40\x69\xc8\xf3\x20\x98\xf5\x83\xe9\x99\x4f\xd6\x6f\xc9\x67\x93\x16\x7a\x1e\x80\xd4\xb8\xee\xba\xbb\x8e\xd5\x6b\xe4\xbe\x56\x6a\x88\x75\xa6\x9e\xf0\x85\x47\x5c\x58\xad\x31\xfe\x27\xdf\xd3\x93\x4a\x9a\x13\xde\xa4\xc7\x90\x66\x22\xfd\x31\x52\xd0\xaf\xb9\xd5\x9e\x77\xdc\x89\x7a\x8c\x24\xcd\xfa\x5c\xfa\x96\xf0\xcd\xfa\xed\x52\xfa\x09\xc1\x13\xdb\xda\x0b\x1a\x7b\xea\x0e\x7f\xd5\xc4\xe1\xe0\x0c\x40\xb8\x3a\x8a\xc8\x15\x18\x82\x7c\xee\x6a\x86\x02\xb2\xfd\x3e\x1a\xbb\x9a\x63\x01\xa4\x98\x20\x56\xe9\x69\x51\xe8\x34\x56\x74\x10\x28\x3e\x9a\xb4\xf5\x2f\x13\xad\x2f\xa8\x2f\x2b\x28\xf2\x5e\xce\x79\x69\xc2\x0a\xd2\xff\xf2\x62\x75\x99\xf6\x70\x2b\xa5\x98\x3a\xf1\xff\x6f\xff\xa2\x4d\x6e\x43\x9a\x3c\xaa\x8c\x83\xf5\xfd\xd0\x7b\x9b\x73\xc9\xdb\x8f\x7c\x3a\xfc\xd0\xa8\x28\xde\x73\x1a\x8b\x9d\x55\xb5\xa6\xdb\xb8\x34\x93\x69\xcd\x66\x33\x38\xbf\x83\xc5\xdd\x03\xcc\xaf\xce\x16\x97\x17\xf0\x70\x75\xfd\x75\x84\x9b\xc6\xa3\x59\x13\x1c\xc9\xe5\xf3\x31\x1c\xc9\x40\x1a\xca\x4f\x90\x3f\xb6\x62\x3c\xf1\x79\xb5\x60\x7d\x4b\x59\xd3\x1c\xbd\x5d\x31\x00\x1d\x63\xdc\x63\xd8\xc4\x9e\x5b\xd9\x7c\x3e\xec\x6c\x3c\x3e\x20\x73\x5d\x8d\xd4\x51\xec\x5f\xaf\x0d\x73\x48\xa6\xdd\x73\x09\xef\xdf\x87\x3f\x71\xdf\xa6\x75\xf1\xcb\xc3\x81\x4c\xe8\xba\x9e\x2b\x94\x7a\xba\x6f\x22\x1e\x1c\x5c\x44\xb7\x13\x07\x52\xbf\x03\x00\x00\xff\xff\x00\x13\xe6\xe4\xd0\x04\x00\x00") func configKubernetesExecutorTemplateYamlBytes() ([]byte, error) { return bindataRead( @@ -149,8 +149,8 @@ func configKubernetesExecutorTemplateYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "config/kubernetes-executor-template.yaml", size: 1764, mode: os.FileMode(0644), modTime: time.Unix(1732069321, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x25, 0x2, 0xdd, 0x4b, 0xb1, 0xe0, 0x8, 0x1f, 0xee, 0xb8, 0xec, 0x74, 0x27, 0xdb, 0xee, 0x63, 0x56, 0x82, 0x78, 0xa4, 0x2a, 0x2b, 0x8c, 0x98, 0x5c, 0xdd, 0x1c, 0xf6, 0x19, 0xd6, 0x9a, 0xb2}} + info := bindataFileInfo{name: "config/kubernetes-executor-template.yaml", size: 1232, mode: os.FileMode(0644), modTime: time.Unix(1732240371, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x28, 0xc4, 0x2b, 0x17, 0x62, 0x5d, 0x51, 0xe4, 0x35, 0x84, 0xde, 0x2a, 0x3, 0x35, 0x7d, 0x71, 0x79, 0x5a, 0xe0, 0xbd, 0x90, 0xcd, 0x65, 0x9d, 0xe8, 0x11, 0x72, 0x90, 0x35, 0xe7, 0x36, 0x7a}} return a, nil } @@ -194,7 +194,7 @@ func configHtcondorTemplateTxt() (*asset, error) { return a, nil } -var _configKubernetesTemplateYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x54\xc9\x6e\xdb\x30\x10\xbd\xeb\x2b\x06\x2a\x7a\xd4\x92\x4b\x0f\xbc\x05\x09\x1a\xb4\x68\x82\xa0\x28\xd2\xf3\x88\x1e\xd9\x84\xb9\x85\x8b\x02\xc3\xd0\xbf\x17\x14\xe5\x56\x72\xec\x00\xe5\x89\x9c\xe5\x2d\xe4\x48\x9f\xe0\x6b\xd4\x9a\x24\xfc\x36\x6e\x4f\xae\x40\x2b\x5e\xc8\x79\x61\x34\x83\x0e\x03\xdf\x35\xc3\x4d\xb1\x17\x7a\xc3\xe0\xbb\xe9\x0a\x45\x01\x37\x18\x90\x15\x00\x1a\x15\x31\x38\x1e\xeb\x5f\xe8\xf7\xdf\x36\xe3\x38\xc7\xbc\x45\x9e\x13\x4f\xa7\xd3\x38\x16\xde\x12\x67\x50\x00\x74\xc8\xf7\xa6\xef\x7f\x08\x25\x02\x83\xb6\x00\xe0\x46\x59\x49\x41\x18\xed\x19\xdc\x14\x00\x81\x94\x95\x18\x28\xb1\x00\x4c\x9d\xd3\x0e\xc0\x93\x1b\x04\xa7\x5b\xce\x4d\xd4\xe1\x69\x52\xd0\x4f\x06\x2a\x8f\x73\x8d\x23\x1f\xd0\x85\x67\x23\x05\x3f\x30\x78\xa2\x81\xdc\x9c\xe2\x46\x07\x14\x9a\x9c\x9f\xa4\xe4\x55\xcd\x4e\x66\x9c\xb7\xe9\x22\xaa\xb5\xaf\xd3\x12\x0a\xb7\xc4\xe0\x35\xe2\xa1\x16\xa6\x31\x3b\x1f\xab\x24\xbf\xea\x84\x69\x32\x00\xdb\xd0\x40\xd2\x58\x45\x3a\x9c\x77\x3e\x47\x29\x4f\xba\x6e\xe5\x1b\x1e\xfc\xa2\x02\xdd\xd6\xb3\xc5\x39\x29\x2b\xb3\x9a\xf2\x3c\xec\xa2\x7e\x17\xab\x2a\x6e\x74\x2f\xb6\xef\x12\x0d\x05\xde\xe4\x5c\xb3\x32\x59\x1f\x50\xc9\x0b\x38\x21\x19\xbf\x3f\x4f\x5c\xbe\x11\x47\xde\x44\xc7\xe9\x4c\xba\xa3\xd7\x48\x3e\x9c\x45\x01\xb8\x8d\x69\x34\x44\x0f\x9a\xa0\xbe\xb3\xd1\x43\x0b\xd5\x38\x1e\x8f\xd3\x21\x6d\x80\xa4\x27\x48\xbb\xf2\xa6\x6d\x55\x99\x76\xa4\xd7\xac\x69\x29\x52\xc6\x1d\x16\x68\x3f\x51\x3d\x74\xd0\xd6\x33\xa0\x75\x42\x87\x1e\xca\xcf\x75\xdb\x3f\x94\x73\x7a\x02\x93\x9e\x32\xfc\x97\xc7\xab\xe8\x64\x77\xa4\xc8\xa1\xac\x7c\x30\x6e\x7a\xf5\xbf\x44\xf7\xc2\xef\x3f\x62\xca\xf9\x35\x55\xdb\x5e\xe6\x1a\x8c\x8c\x8a\x1e\xd3\x38\xaf\x2e\xeb\x34\x95\xf9\xdd\xaa\x5c\xb6\xd2\xa8\x52\xcf\x33\x86\x1d\x83\xc5\x0b\x2f\x4a\x2e\xa0\x9d\xbe\x95\x6c\xe9\xca\x90\xaf\xa1\x8d\x0d\xf3\xd4\x2c\x87\xa7\xda\x08\xd7\x5c\x6b\xf7\xb1\xcb\xcd\xcb\x82\x62\xe9\xf7\xdf\xf7\xf7\xb1\xcf\x1c\x7d\x44\xbb\xbc\x9a\x95\x95\xd9\x75\xf1\xdf\x3e\x6d\xfa\xcd\xf9\x40\x3a\xbc\x4c\x9c\x77\x12\x85\x5a\xd2\xf0\x14\x58\xfd\x62\xec\xc0\x97\x50\x7f\x02\x00\x00\xff\xff\x58\xbe\x1f\x47\x3c\x05\x00\x00") +var _configKubernetesTemplateYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x54\xc9\x6e\xdb\x30\x10\xbd\xeb\x2b\x06\x2e\x7a\xa4\xa5\x5c\x7a\xe0\x2d\x48\xd0\x20\x45\x13\x04\x45\x91\x9e\x47\xf4\x28\x26\xcc\x2d\x5c\x1c\x18\x86\xff\xbd\xa0\x28\xa7\x94\x63\x07\x28\x4f\xd4\x2c\xef\xbd\x59\xc4\x2f\xf0\x3d\x19\x43\x0a\xfe\x58\xbf\x21\xdf\xa0\x93\xcf\xe4\x83\xb4\x86\x43\x8f\x51\xac\xdb\xed\x55\xb3\x91\x66\xc5\xe1\x87\xed\x1b\x4d\x11\x57\x18\x91\x37\x00\x06\x35\x71\xd8\xef\x97\xbf\x31\x6c\xee\x57\x87\xc3\x64\x0b\x0e\x45\x71\x3c\x1e\xbf\x46\x9f\xc2\x9e\x54\xc8\x99\x00\xe8\x1c\x87\x61\x64\x66\x6f\x85\x39\x9b\x23\x86\x0d\x93\xab\x39\x6a\x70\x24\x38\x34\x00\x3d\x8a\x8d\x1d\x86\x9f\x52\xcb\xc8\xa1\x6b\x00\x84\xd5\x4e\x51\x94\xd6\x04\x0e\x57\x0d\x40\x24\xed\x14\x46\x2a\x2c\xb5\xda\x7c\x6a\x05\x9f\xa8\xb8\xa8\x24\x3b\x46\x35\x53\x58\x20\xbf\x95\x82\xae\x85\xb0\xc9\xc4\xc7\xb1\x1f\x13\x5c\xc0\x29\xc6\x53\x88\xe8\xe3\x93\x55\x52\xec\x38\x3c\xd2\xf6\x9d\x45\x58\x13\x51\x1a\xf2\x61\x2c\xaf\x1c\x36\xf5\x75\x26\x8b\x9d\xaa\x28\x47\x6a\x7c\x21\x0e\xaf\x09\x77\x4b\x69\x5b\xbb\x0e\x89\xe5\x96\xb0\x5e\xda\xb6\x00\xf0\x15\x6d\x49\x59\xa7\xc9\xc4\xd3\xcc\xa7\xa4\xd4\x51\xd7\xb5\x7a\xc3\x5d\xa8\x22\xd0\xbf\x54\x9d\x2a\xca\x16\x45\xcd\xe2\xd4\xec\x93\xf9\x60\x63\x4c\x58\x33\xc8\x97\x0f\x8e\x96\xa2\x68\x8b\xaf\x9d\x15\xb9\xdc\xa1\x56\x67\x70\xf2\x28\xee\x6f\x4f\x1d\xe7\x3b\xe2\x29\xd8\xe4\x05\x9d\x48\xf7\xf4\x9a\x28\xc4\x13\x2b\x80\x70\x29\x4f\x58\x0e\x60\x08\x96\x37\x2e\x05\xe8\x80\x1d\x0e\xfb\xfd\xf8\x91\x2f\x40\x2a\x10\xe4\xdb\xe2\xaa\xeb\xf4\x22\xdf\xc8\xcc\x59\x61\xdc\x35\x6d\xfd\xae\x42\xfb\x85\xfa\xae\x87\x6e\x39\x01\x3a\x2f\x4d\x1c\x60\xf1\x75\xd9\x0d\x77\x8b\xc9\x3d\x82\xa9\x40\x05\xfe\xdb\xc3\x45\x74\x72\x6b\xd2\xe4\x51\xb1\x10\xad\x1f\xa7\xfe\x4e\x74\x2b\xc3\xe6\x33\xa6\xe2\x9f\x53\x75\xdd\x79\xae\xad\x55\x49\xd3\x43\x5e\xe7\x59\xb3\x8e\x5b\x59\xe6\xc6\x4a\xd8\x4c\xa3\xce\x39\x4f\x18\xd7\x1c\xaa\x09\x57\x21\x67\xd0\x8e\xff\x4a\x29\xe9\xc2\x92\xcf\xa1\xad\x8b\xd3\xd6\xd4\xcb\xc3\x56\xd2\xb7\x97\xd2\x43\xea\x4b\x72\x1d\xd0\xd4\xf5\xfe\xfb\xff\x3e\xaf\xb3\x58\x1f\xd0\xd5\xad\x99\x95\x32\x55\xdd\xfc\x77\x9d\x2e\x3f\xba\x21\x92\x89\xcf\x23\xe7\x8d\x42\xa9\x6b\x1a\x91\x0d\xb3\x27\xc6\x6d\xc5\x0c\xea\x6f\x00\x00\x00\xff\xff\x08\x8f\xc3\x13\xcb\x05\x00\x00") func configKubernetesTemplateYamlBytes() ([]byte, error) { return bindataRead( @@ -209,8 +209,8 @@ func configKubernetesTemplateYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "config/kubernetes-template.yaml", size: 1340, mode: os.FileMode(0644), modTime: time.Unix(1732069183, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd, 0x9f, 0xc2, 0x40, 0x16, 0xb2, 0xe4, 0xc1, 0xca, 0x91, 0x63, 0x8c, 0xd5, 0x75, 0x4a, 0x48, 0x1f, 0x5b, 0x6a, 0xca, 0xcd, 0x8d, 0x20, 0x7d, 0x76, 0xa4, 0x7, 0x7f, 0xf3, 0xe5, 0x87, 0xe5}} + info := bindataFileInfo{name: "config/kubernetes-template.yaml", size: 1483, mode: os.FileMode(0644), modTime: time.Unix(1732239949, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x2c, 0x8c, 0x8e, 0x18, 0x35, 0xe0, 0x7c, 0x96, 0x55, 0x8e, 0x9a, 0x5a, 0x33, 0xde, 0x4f, 0x66, 0x80, 0xae, 0x95, 0xb, 0x6a, 0x60, 0xf3, 0xb4, 0x8d, 0xd9, 0xa9, 0xf4, 0x6b, 0x90, 0xbc, 0x91}} return a, nil } diff --git a/config/kubernetes-executor-template.yaml b/config/kubernetes-executor-template.yaml index 8e10d2546..b87f8ef7f 100644 --- a/config/kubernetes-executor-template.yaml +++ b/config/kubernetes-executor-template.yaml @@ -5,6 +5,7 @@ metadata: name: {{.TaskId}}-{{.JobId}} namespace: {{.Namespace}} labels: + app: funnel-executor job-name: {{.TaskId}}-{{.JobId}} spec: backoffLimit: 0 @@ -13,19 +14,6 @@ spec: spec: restartPolicy: Never serviceAccountName: funnel-sa - # Setup symlinks from work dir to target paths - initContainers: - - name: setup-dirs - image: busybox - command: ["/bin/sh", "-c"] - args: - - | - # Create a directory to bind mount the worker's files - # Create parent directories for any path specified in the task - # Create the symlink from worker dir to target path - echo "initContainer: Creating directories and symlinks" - securityContext: - runAsUser: 0 # Run as root to ensure directory creation works containers: - name: funnel-worker-{{.TaskId}} image: {{.Image}} diff --git a/config/kubernetes-template.yaml b/config/kubernetes-template.yaml index 81239b68a..97f8af013 100644 --- a/config/kubernetes-template.yaml +++ b/config/kubernetes-template.yaml @@ -4,10 +4,17 @@ kind: Job metadata: name: {{.TaskId}} namespace: {{.Namespace}} + labels: + app: funnel-worker + task-id: {{.TaskId}} spec: backoffLimit: 0 completions: 1 template: + metadata: + labels: + app: funnel-worker + task-id: {{.TaskId}} spec: serviceAccountName: funnel-sa restartPolicy: Never @@ -42,4 +49,4 @@ spec: - name: funnel-storage-{{.TaskId}} persistentVolumeClaim: - claimName: funnel-pvc-{{.TaskId}} \ No newline at end of file + claimName: funnel-pvc-{{.TaskId}} diff --git a/deployments/kubernetes/helm/Chart.yaml b/deployments/kubernetes/helm/Chart.yaml index 42db24fba..c93d104c8 100644 --- a/deployments/kubernetes/helm/Chart.yaml +++ b/deployments/kubernetes/helm/Chart.yaml @@ -17,7 +17,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.11 +version: 0.1.12 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/deployments/kubernetes/helm/templates/NOTES.txt b/deployments/kubernetes/helm/templates/NOTES.txt index 19666b112..34e155afb 100644 --- a/deployments/kubernetes/helm/templates/NOTES.txt +++ b/deployments/kubernetes/helm/templates/NOTES.txt @@ -1,5 +1,3 @@ -1. To access the Funnel application, use the following instructions: - -To access the service locally, use: +To access the Funnel service locally, use: kubectl --namespace {{ .Release.Namespace }} port-forward svc/{{ include "funnel.fullname" . }} 8000:8000 echo "Visit http://127.0.0.1:8000" diff --git a/deployments/kubernetes/helm/templates/clusterrole.yaml b/deployments/kubernetes/helm/templates/clusterrole.yaml new file mode 100644 index 000000000..39968966d --- /dev/null +++ b/deployments/kubernetes/helm/templates/clusterrole.yaml @@ -0,0 +1,15 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: funnel-clusterrole +rules: +- apiGroups: [""] + resources: + - persistentvolumes + verbs: + - get + - list + - watch + - create + - update + - delete diff --git a/deployments/kubernetes/helm/templates/clusterrolebinding.yaml b/deployments/kubernetes/helm/templates/clusterrolebinding.yaml new file mode 100644 index 000000000..672d4d661 --- /dev/null +++ b/deployments/kubernetes/helm/templates/clusterrolebinding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: funnel-clusterrolebinding +subjects: +- kind: ServiceAccount + name: funnel-sa + namespace: funnel +roleRef: + kind: ClusterRole + name: funnel-clusterrole + apiGroup: rbac.authorization.k8s.io diff --git a/deployments/kubernetes/helm/templates/role.yaml b/deployments/kubernetes/helm/templates/role.yaml index 64628d22e..32fa3b93c 100644 --- a/deployments/kubernetes/helm/templates/role.yaml +++ b/deployments/kubernetes/helm/templates/role.yaml @@ -6,7 +6,7 @@ metadata: namespace: {{ .Release.Namespace }} rules: - apiGroups: [""] - resources: ["pods", "pods/log", "persistentvolumeclaims"] # Added PVCs + resources: ["pods", "pods/log", "persistentvolumeclaims"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - apiGroups: ["batch", "extensions"] resources: ["jobs"] diff --git a/examples/internal/bundle.go b/examples/internal/bundle.go index b8c2533b5..4699b377d 100644 --- a/examples/internal/bundle.go +++ b/examples/internal/bundle.go @@ -10,7 +10,7 @@ // examples/s3-sleep.json (518B) // examples/md5sum.json (737B) // examples/nextflow.json (551B) -// examples/s3.json (524B) +// examples/s3.json (532B) // examples/malicious-tag.json (283B) // examples/full-hello.json (577B) @@ -280,7 +280,7 @@ func examplesNextflowJson() (*asset, error) { return a, nil } -var _examplesS3Json = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x64\x90\xcf\x4e\xf3\x30\x10\xc4\xef\x79\x8a\x95\x4f\xdf\x27\x35\xb1\x44\xc5\x25\xd7\xb4\xe2\x52\x71\xa0\x70\x01\xf5\xb0\x71\xb6\xa9\xc1\x7f\x22\xaf\x4d\x83\xaa\xbe\x3b\x8a\x0b\x2d\x85\x4b\x14\x69\xc6\x33\xbf\x9d\x43\x01\x20\x1c\x5a\x12\x35\x88\xf5\x1c\xd6\xd1\x07\xec\x09\x68\x44\x3b\x18\x12\xb3\x49\xef\x88\x55\xd0\x43\xd4\xde\x4d\xb6\x47\xe4\x37\xd0\x6e\x48\x91\x01\x5d\x07\x3e\xc5\xfc\xaf\xd0\x41\x4b\xd0\x18\x9f\xba\x73\xd0\xd3\xc3\x8a\xab\x53\x0c\x8d\xa4\x52\xf4\x81\x45\x0d\x2f\x05\x00\xc0\x21\x7f\x01\x84\xb6\xd8\x67\x84\xd4\x26\x17\x53\xf6\x67\x41\x79\x6b\xd1\x75\xd3\x0b\x61\xbb\x5b\x4e\x56\xcc\x40\xc8\x40\x86\x90\xa9\x7a\x65\xef\xc4\x26\x9b\x8f\x05\xc0\x26\xf7\x9c\xd0\xfe\x96\x7c\x9f\x99\xf5\x4b\xc5\xaf\xeb\x16\x7e\xef\x8c\xc7\x0e\x10\x86\xd4\x1a\xad\x60\xab\x0d\xc1\x36\x78\x0b\x3f\x06\xfa\xd7\x3c\x43\xb3\x5c\xad\xc6\xbb\xe5\xfd\x12\x16\x9a\x95\x7f\xa7\x00\x0d\x39\x4e\x0c\x0b\x8c\x58\xc3\x2e\xc6\x81\x6b\x29\x03\xf5\x9a\x63\xf8\xa8\xfc\x40\xae\xc3\x88\x15\xee\x59\x46\xec\x65\xab\xbd\x76\x5b\x1f\x2c\x46\xad\x58\xfe\xbf\x50\xa5\x60\x26\x1a\x9e\xd7\x52\x2a\x32\x66\xec\xc9\x51\xa9\x72\x7c\x79\x02\x2b\x13\x97\x7b\xe2\x58\xde\x64\xc7\x97\x78\xbd\xcd\x39\x6f\xc0\xb8\x9b\x02\xaf\xe5\xcb\x72\xc5\xb1\xf8\x0c\x00\x00\xff\xff\xc0\x06\x69\xef\x0c\x02\x00\x00") +var _examplesS3Json = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x64\x90\x4f\x8f\xd3\x30\x10\xc5\xef\xf9\x14\x23\x9f\x40\xda\xc4\x12\x2b\x2e\xb9\xa6\x15\x97\x8a\x03\x0b\x17\x50\x0f\x13\x67\x9a\x1a\xfc\x4f\x9e\x31\x0d\xaa\xfa\xdd\x51\x5c\x68\x61\x7b\xb1\x2c\xbd\x99\xf7\x7e\xf3\xce\x0d\x80\x0a\xe8\x49\xf5\xa0\x5e\x9e\xe1\x45\x62\xc6\x99\x80\x16\xf4\xc9\x91\x7a\x5a\xf5\x89\xd8\x64\x9b\xc4\xc6\xb0\x8e\x7d\x46\xfe\x01\x36\xa4\x22\x0c\x18\x26\x88\x45\xea\xdf\x60\x80\x91\x60\x70\xb1\x4c\x37\xa3\x2f\x9f\x76\xdc\x5d\x6d\x68\x21\x53\x24\x66\x56\x3d\x7c\x6b\x00\x00\xce\xf5\x05\x50\xd6\xe3\x5c\x11\xca\x58\x82\x94\x3a\x5f\x05\x13\xbd\xc7\x30\xad\x1b\xca\x4f\xef\xb9\x78\xf5\x04\x4a\x8b\x4f\x3a\x93\x23\x64\xea\xbe\x73\x0c\x6a\x5f\x17\x2e\x0d\xc0\xbe\x66\x5d\xf1\x1e\x83\xfe\x9e\x5a\xf5\x7b\xcc\xab\x0b\x37\xf1\x14\x5c\xc4\x09\x10\x52\x19\x9d\x35\x70\xb0\x8e\xe0\x90\xa3\x87\x7f\x4a\x7a\x33\x7c\x85\x61\xbb\xdb\x2d\x1f\xb6\x1f\xb7\xb0\xb1\x6c\xe2\x4f\xca\x30\x50\xe0\xc2\xb0\x41\xc1\x1e\x8e\x22\x89\x7b\xad\x33\xcd\x96\x25\xff\xea\x62\xa2\x30\xa1\x60\x87\x27\xd6\x82\xb3\x1e\x6d\xb4\xe1\x10\xb3\x47\xb1\x86\xf5\xdb\x3b\x55\xc9\x6e\xa5\xe1\xe7\x5e\x6b\x43\xce\x2d\x33\x05\x6a\x4d\xb5\x6f\xaf\x60\x6d\xe1\xf6\x44\x2c\xed\xbb\x3a\xf1\x47\xfc\xbf\x9b\x9b\x5f\x42\x39\xae\x86\x8f\xf5\xdd\xdb\x6b\x2e\xcd\xef\x00\x00\x00\xff\xff\x65\xd0\xc2\x9b\x14\x02\x00\x00") func examplesS3JsonBytes() ([]byte, error) { return bindataRead( @@ -295,8 +295,8 @@ func examplesS3Json() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "examples/s3.json", size: 524, mode: os.FileMode(0644), modTime: time.Unix(1732064231, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa5, 0x12, 0xab, 0xa6, 0x34, 0xd0, 0xb, 0xd6, 0x56, 0x79, 0x95, 0xa, 0x80, 0x42, 0x6e, 0x70, 0x53, 0xb8, 0xcb, 0x92, 0x4, 0xb6, 0x62, 0xe0, 0xb, 0x1c, 0xfb, 0xb1, 0xd5, 0x78, 0xa4, 0x37}} + info := bindataFileInfo{name: "examples/s3.json", size: 532, mode: os.FileMode(0644), modTime: time.Unix(1732133183, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xce, 0xcf, 0xc, 0x0, 0xf8, 0xd5, 0x2b, 0x3c, 0x3f, 0x6e, 0x32, 0xcb, 0x99, 0x40, 0x9, 0x3b, 0x20, 0xd4, 0xde, 0x90, 0xd6, 0x4b, 0x53, 0x91, 0xd3, 0x71, 0x9b, 0x4f, 0x31, 0x3b, 0x70, 0x94}} return a, nil } From 4f3fd5cddc0a86835ae6e5f14e82c66b8bf7fd03 Mon Sep 17 00:00:00 2001 From: Liam Beckman Date: Fri, 22 Nov 2024 19:21:27 -0800 Subject: [PATCH 2/8] Add support for S3 Mountpoint S3 Driver - Thanks for this @jawadqur and @paulineribeyre! --- compute/kubernetes/backend.go | 220 ++++++++---------- config/config.go | 8 + config/default.go | 8 +- config/internal/bundle.go | 48 +++- deployments/kubernetes/helm/Chart.yaml | 2 +- .../kubernetes/helm/config/funnel-server.yaml | 9 +- .../kubernetes/helm/config/funnel-worker.yaml | 9 +- deployments/kubernetes/helm/values.yaml | 6 +- worker/kubernetes.go | 2 +- worker/worker.go | 4 + 10 files changed, 181 insertions(+), 135 deletions(-) diff --git a/compute/kubernetes/backend.go b/compute/kubernetes/backend.go index bc14b7a3a..334f2abbb 100644 --- a/compute/kubernetes/backend.go +++ b/compute/kubernetes/backend.go @@ -14,14 +14,12 @@ import ( v1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" k8errors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" batchv1 "k8s.io/client-go/kubernetes/typed/batch/v1" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" - "k8s.io/utils/ptr" "github.com/ohsu-comp-bio/funnel/config" "github.com/ohsu-comp-bio/funnel/events" @@ -69,13 +67,17 @@ func NewBackend(ctx context.Context, conf config.Kubernetes, reader tes.ReadOnly } b := &Backend{ - client: clientset.BatchV1().Jobs(conf.Namespace), - namespace: conf.Namespace, - template: conf.Template, - event: writer, - database: reader, - log: log, - config: kubeconfig, + bucket: conf.Bucket, + region: conf.Region, + client: clientset.BatchV1().Jobs(conf.Namespace), + namespace: conf.Namespace, + template: conf.Template, + pvTemplate: conf.PVTemplate, + pvcTemplate: conf.PVCTemplate, + event: writer, + database: reader, + log: log, + config: kubeconfig, } if !conf.DisableReconciler { @@ -88,9 +90,13 @@ func NewBackend(ctx context.Context, conf config.Kubernetes, reader tes.ReadOnly // Backend represents the local backend. type Backend struct { + bucket string + region string client batchv1.JobInterface namespace string template string + pvTemplate string + pvcTemplate string event events.Writer database tes.ReadOnlyServer log *logger.Logger @@ -134,7 +140,7 @@ func (b *Backend) Close() { //TODO: close database? } -// Create the Funnel Worker job +// Create the Funnel Worker job from kubernetes-template.yaml // Executor job is created in worker/kubernetes.go#Run func (b *Backend) createJob(task *tes.Task) (*v1.Job, error) { submitTpl, err := template.New(task.Id).Parse(b.template) @@ -156,7 +162,7 @@ func (b *Backend) createJob(task *tes.Task) (*v1.Job, error) { "DiskGb": res.GetDiskGb(), }) if err != nil { - return nil, fmt.Errorf("executing template: %v", err) + return nil, fmt.Errorf("executing Worker template: %v", err) } decode := scheme.Codecs.UniversalDeserializer().Decode @@ -172,133 +178,74 @@ func (b *Backend) createJob(task *tes.Task) (*v1.Job, error) { return job, nil } -func (b *Backend) createPVC(ctx context.Context, taskID string, resources *tes.Resources) error { - clientset, err := kubernetes.NewForConfig(b.config) +// Create the Worker/Executor PVC from config/kubernetes-pvc.yaml +// TODO: Move this config file to Helm Charts so users can see/customize it +func (b *Backend) createPVC(task *tes.Task) (*corev1.PersistentVolumeClaim, error) { + // Load templates + pvcTpl, err := template.New(task.Id).Parse(b.pvcTemplate) if err != nil { - return fmt.Errorf("getting kubernetes client: %v", err) + return nil, fmt.Errorf("parsing template: %v", err) } - // Define storage size (ignored by S3 CSI driver but required by the API) - storageSize := resource.NewQuantity(1024*1024*1024, resource.BinarySI) // Default 1Gi - if resources != nil && resources.DiskGb > 0 { - storageSize = resource.NewQuantity(int64(resources.DiskGb*1024*1024*1024), resource.BinarySI) + // Template parameters + var buf bytes.Buffer + err = pvcTpl.Execute(&buf, map[string]interface{}{ + "TaskId": task.Id, + "Namespace": b.namespace, + "Bucket": b.bucket, + "Region": b.region, + }) + if err != nil { + return nil, fmt.Errorf("executing PVC template: %v", err) } - pvName := fmt.Sprintf("funnel-pv-%s", taskID) - pvcName := fmt.Sprintf("funnel-pvc-%s", taskID) - bucketName := "funnel-testing" - - // Step 1: Create the PersistentVolume - pv := &corev1.PersistentVolume{ - ObjectMeta: metav1.ObjectMeta{ - Name: pvName, - Labels: map[string]string{ - "app": "funnel", - "taskId": taskID, - }, - }, - Spec: corev1.PersistentVolumeSpec{ - Capacity: corev1.ResourceList{ - corev1.ResourceStorage: *storageSize, - }, - AccessModes: []corev1.PersistentVolumeAccessMode{ - corev1.ReadWriteMany, // S3 CSI supports RWX - }, - PersistentVolumeReclaimPolicy: corev1.PersistentVolumeReclaimRetain, - StorageClassName: "", - ClaimRef: &corev1.ObjectReference{ - Namespace: b.namespace, - Name: pvcName, - }, - MountOptions: []string{ - "allow-delete", - "region=us-west-2", - }, - PersistentVolumeSource: corev1.PersistentVolumeSource{ - CSI: &corev1.CSIPersistentVolumeSource{ - Driver: "s3.csi.aws.com", - VolumeHandle: fmt.Sprintf("s3-csi-%s", taskID), - VolumeAttributes: map[string]string{ - "bucketName": bucketName, - }, - }, - }, - }, - } - - if _, err := clientset.CoreV1().PersistentVolumes().Create(ctx, pv, metav1.CreateOptions{}); err != nil { - return fmt.Errorf("creating PersistentVolume: %v", err) - } - - // Step 2: Create the PersistentVolumeClaim - pvc := &corev1.PersistentVolumeClaim{ - ObjectMeta: metav1.ObjectMeta{ - Name: pvcName, - Labels: map[string]string{ - "app": "funnel", - "taskId": taskID, - }, - }, - Spec: corev1.PersistentVolumeClaimSpec{ - AccessModes: []corev1.PersistentVolumeAccessMode{ - corev1.ReadWriteMany, - }, - Resources: corev1.VolumeResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceStorage: *storageSize, - }, - }, - StorageClassName: func() *string { s := ""; return &s }(), - VolumeName: pvName, - }, - } - - if _, err := clientset.CoreV1().PersistentVolumeClaims(b.namespace).Create(ctx, pvc, metav1.CreateOptions{}); err != nil { - return fmt.Errorf("creating PersistentVolumeClaim: %v", err) + decode := scheme.Codecs.UniversalDeserializer().Decode + obj, _, err := decode(buf.Bytes(), nil, nil) + if err != nil { + return nil, fmt.Errorf("decoding PVC spec: %v", err) } - return nil + fmt.Println("PVC spec: ", string(buf.Bytes())) + pvc, ok := obj.(*corev1.PersistentVolumeClaim) + if !ok { + return nil, fmt.Errorf("failed to decode PVC spec") + } + return pvc, nil } -func (b *Backend) addOwnerReference(ctx context.Context, taskID string, job *v1.Job) error { - // Fetch the Job that will own the PVC and PV - jobName := fmt.Sprintf("funnel-job-%s", taskID) - _, err := b.client.Get(ctx, jobName, metav1.GetOptions{}) +// Create the Worker/Executor PV from config/kubernetes-pv.yaml +// TODO: Move this config file to Helm Charts so users can see/customize it +func (b *Backend) createPV(task *tes.Task) (*corev1.PersistentVolume, error) { + // Load templates + pvTpl, err := template.New(task.Id).Parse(b.pvTemplate) if err != nil { - return fmt.Errorf("fetching Job for owner reference: %v", err) + return nil, fmt.Errorf("parsing template: %v", err) } - // Fetch the PVC - pvcName := fmt.Sprintf("funnel-pvc-%s", taskID) - clientset, err := kubernetes.NewForConfig(b.config) - if err != nil { - return fmt.Errorf("getting kubernetes client: %v", err) - } - pvc, err := clientset.CoreV1().PersistentVolumeClaims(b.namespace).Get(ctx, pvcName, metav1.GetOptions{}) + // Template parameters + var buf bytes.Buffer + err = pvTpl.Execute(&buf, map[string]interface{}{ + "TaskId": task.Id, + "Namespace": b.namespace, + "Bucket": b.bucket, + "Region": b.region, + }) if err != nil { - return fmt.Errorf("fetching PVC: %v", err) + return nil, fmt.Errorf("executing PV template: %v", err) } - // Add OwnerReference for the Job - ownerRef := metav1.OwnerReference{ - APIVersion: "batch/v1", - Kind: "Job", - Name: job.Name, - UID: job.UID, - BlockOwnerDeletion: ptr.To(true), - Controller: ptr.To(true), - } - - // Append the OwnerReference to the PVC - pvc.OwnerReferences = append(pvc.OwnerReferences, ownerRef) - - // Update the PVC - _, err = clientset.CoreV1().PersistentVolumeClaims(b.namespace).Update(ctx, pvc, metav1.UpdateOptions{}) + decode := scheme.Codecs.UniversalDeserializer().Decode + obj, _, err := decode(buf.Bytes(), nil, nil) if err != nil { - return fmt.Errorf("updating PVC: %v", err) + return nil, fmt.Errorf("decoding PV spec: %v", err) } - return nil + fmt.Println("PV spec: ", string(buf.Bytes())) + pv, ok := obj.(*corev1.PersistentVolume) + if !ok { + return nil, fmt.Errorf("failed to decode PV spec") + } + return pv, nil } // Add this helper function for PVC cleanup @@ -326,13 +273,35 @@ func (b *Backend) Submit(ctx context.Context, task *tes.Task) error { // Create a new background context instead of inheriting from the potentially canceled one submitCtx := context.Background() - // TODO: Update this so that a PVC is only created if the task has inputs or outputs + // TODO: Update this so that a PVC/PV is only created if the task has inputs or outputs // If the task has either inputs or outputs, then create a PVC // shared between the Funnel Worker and the Executor // e.g. `if len(task.Inputs) > 0 || len(task.Outputs) > 0 {}` - err := b.createPVC(submitCtx, task.Id, task.GetResources()) + pvc, err := b.createPVC(task) + if err != nil { + return fmt.Errorf("creating shared storage PVC: %v", err) + } + + pv, err := b.createPV(task) if err != nil { - return fmt.Errorf("creating shared storage: %v", err) + return fmt.Errorf("creating shared storage PV: %v", err) + } + + clientset, err := kubernetes.NewForConfig(b.config) + if err != nil { + return fmt.Errorf("getting kubernetes client: %v", err) + } + + // Create PVC + pvc, err = clientset.CoreV1().PersistentVolumeClaims(b.namespace).Create(context.Background(), pvc, metav1.CreateOptions{}) + if err != nil { + return fmt.Errorf("creating PVC: %v", err) + } + + // Create PV + pv, err = clientset.CoreV1().PersistentVolumes().Create(context.Background(), pv, metav1.CreateOptions{}) + if err != nil { + return fmt.Errorf("creating PV: %v", err) } // Create the worker job @@ -348,11 +317,6 @@ func (b *Backend) Submit(ctx context.Context, task *tes.Task) error { return fmt.Errorf("creating job in backend: %v", err) } - err = b.addOwnerReference(submitCtx, task.Id, job) - if err != nil { - return fmt.Errorf("updating PVC with OwnerReference: %v", err) - } - return nil } diff --git a/config/config.go b/config/config.go index 978aba8da..32ed83707 100644 --- a/config/config.go +++ b/config/config.go @@ -409,6 +409,10 @@ func (h FTPStorage) Valid() bool { // Kubernetes describes the configuration for the Kubernetes compute backend. type Kubernetes struct { + // The bucket to use for the task's Working Directory + Bucket string + // The region to use for the task's Bucket + Region string // The executor used to execute tasks. Available executors: docker, kubernetes Executor string // Turn off task state reconciler. When enabled, Funnel communicates with Kuberenetes @@ -428,6 +432,10 @@ type Kubernetes struct { ExecutorTemplate string // ExecutorTemplateFile is the path to the executor template. ExecutorTemplateFile string + // Worker/Executor PV job template. + PVTemplate string + // Worker/Executor PVC job template. + PVCTemplate string // Path to the Kubernetes configuration file, otherwise assumes the Funnel server is running in a pod and // attempts to use https://godoc.org/k8s.io/client-go/rest#InClusterConfig to infer configuration. ConfigFile string diff --git a/config/default.go b/config/default.go index 2dfde8f1c..13fbb47e8 100644 --- a/config/default.go +++ b/config/default.go @@ -164,11 +164,17 @@ func DefaultConfig() Config { kubernetesTemplate := intern.MustAsset("config/kubernetes-template.yaml") executorTemplate := intern.MustAsset("config/kubernetes-executor-template.yaml") - c.Kubernetes.Executor = "docker" + pvTemplate := intern.MustAsset("config/kubernetes-pv.yaml") + pvcTemplate := intern.MustAsset("config/kubernetes-pvc.yaml") + c.Kubernetes.Executor = "kubernetes" c.Kubernetes.Namespace = "default" c.Kubernetes.ServiceAccount = "funnel-sa" c.Kubernetes.Template = string(kubernetesTemplate) c.Kubernetes.ExecutorTemplate = string(executorTemplate) + c.Kubernetes.Bucket = "" + c.Kubernetes.Region = "" + c.Kubernetes.PVTemplate = string(pvTemplate) + c.Kubernetes.PVCTemplate = string(pvcTemplate) c.Kubernetes.ReconcileRate = reconcile return c diff --git a/config/internal/bundle.go b/config/internal/bundle.go index 59e082e62..0710d7ba9 100644 --- a/config/internal/bundle.go +++ b/config/internal/bundle.go @@ -1,8 +1,10 @@ // Code generated by go-bindata. DO NOT EDIT. // sources: +// config/kubernetes-pvc.yaml (309B) // config/gridengine-template.txt (346B) // config/pbs-template.txt (361B) // config/slurm-template.txt (415B) +// config/kubernetes-pv.yaml (517B) // config/kubernetes-executor-template.yaml (1.232kB) // config/default-config.yaml (11.655kB) // config/htcondor-template.txt (505B) @@ -74,6 +76,26 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } +var _configKubernetesPvcYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x8f\xc1\x6a\xf3\x30\x10\x84\xef\x7a\x8a\x81\xff\xec\xfc\xe4\xaa\x6b\x28\xa5\x87\x84\x50\x8a\x73\xde\xca\xd3\x22\x6c\x4b\xaa\x56\x32\x2d\x26\xef\x5e\x6c\x6c\x4a\x8e\xb3\x3b\xf3\xc1\xf7\x0f\xb7\x98\x7b\xe6\xff\x4f\xdf\x74\xb5\xc4\x8c\x6b\x7b\x32\x92\x7c\xcb\xac\x3e\x06\x8b\xe9\x68\x7a\x1f\x3a\x8b\xeb\x72\xd1\xc2\x50\xda\x38\xd4\x91\xa7\x41\xfc\x68\x46\x16\xe9\xa4\x88\x35\x40\x90\x91\x16\x1f\x35\x04\x0e\x4d\x9a\x5c\x33\xcf\x87\x37\xd1\xfe\xa5\xbb\xdf\xb7\xb7\x26\x71\xb4\x98\x67\x1c\x2e\x7b\xc4\xfa\x1d\xe4\x9d\x83\x2e\x18\x40\x52\xda\x39\x6b\x2e\x2b\x64\x99\xfd\xf1\x34\xd1\x2d\x6d\x71\x8e\xaa\xe7\xd8\x71\x1b\x37\x78\xa5\x74\xb7\xec\x0b\xcf\x12\x7e\x0c\x90\xa9\xb1\x66\xb7\x17\x32\xbf\x2a\xb5\x6c\x09\xd0\x12\xb3\x7c\xd2\xe2\xf8\xec\x0d\x30\xad\x76\x97\x47\x97\x07\x95\xdf\x00\x00\x00\xff\xff\x01\x27\x69\xed\x35\x01\x00\x00") + +func configKubernetesPvcYamlBytes() ([]byte, error) { + return bindataRead( + _configKubernetesPvcYaml, + "config/kubernetes-pvc.yaml", + ) +} + +func configKubernetesPvcYaml() (*asset, error) { + bytes, err := configKubernetesPvcYamlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "config/kubernetes-pvc.yaml", size: 309, mode: os.FileMode(0644), modTime: time.Unix(1732326881, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe9, 0xc, 0x1f, 0x92, 0xa1, 0xb3, 0x7, 0x8, 0xb0, 0x29, 0x2f, 0x8e, 0x77, 0x9d, 0x10, 0x18, 0xc6, 0xcd, 0x90, 0xbf, 0xdf, 0x92, 0xf6, 0xf5, 0x81, 0x70, 0x47, 0xb6, 0x35, 0x85, 0x1a, 0x9d}} + return a, nil +} + var _configGridengineTemplateTxt = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\x90\xcd\x4a\xc4\x30\x14\x85\xf7\x79\x8a\x6b\xc7\x59\x26\xed\x0b\xb8\xb2\x30\xb8\x71\x21\x82\x4b\x69\xc9\x0d\x13\x32\xf9\xe1\x26\x51\x30\xe4\xdd\xa5\x69\x11\x0a\x75\x76\x97\xc3\x77\x3e\xb8\xe7\xf4\xd0\xcf\xda\xf5\xf3\x14\xaf\xec\xf4\x08\xfc\x15\x4a\x11\xef\x53\x34\x2f\xb2\xd6\x96\xf8\x25\xf9\xf0\x64\x46\x4d\xb5\xf6\x2a\x3b\x87\x37\x1e\x93\xf4\x39\x35\x00\xff\x03\x90\x88\x95\xa2\x15\x38\x04\xf1\x1c\x72\x84\x01\x78\xad\xac\x94\x40\xda\x25\x05\xdd\x52\x0f\x08\x36\x68\x38\xcb\x6e\x85\x1a\xc0\x01\x9d\x6c\xd7\x56\x7f\x9b\xec\x65\x86\x41\x1c\x19\x6e\x70\xfd\xfc\xb2\x68\x9f\xce\x62\x50\x97\x6e\x83\x8f\x3d\xa3\x8e\xe6\xae\x48\x45\xfd\x83\x7f\xa6\x15\xdf\xa9\xd8\xfa\x20\x7c\x7b\x32\x48\x40\xd9\x01\xe7\x69\x59\x6c\xdc\x6d\xf7\x1b\x00\x00\xff\xff\xcf\x92\x30\x7f\x5a\x01\x00\x00") func configGridengineTemplateTxtBytes() ([]byte, error) { @@ -134,6 +156,26 @@ func configSlurmTemplateTxt() (*asset, error) { return a, nil } +var _configKubernetesPvYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x5c\x91\x41\x6b\xfb\x30\x0c\xc5\xef\xfe\x14\x82\xff\x39\xf9\x53\x76\x33\xec\xb0\xc1\xd8\x76\x68\x57\xc2\x68\xcf\xaa\xad\x16\x11\xc7\x36\xb6\x92\xae\x84\x7e\xf7\x11\xa7\x19\x5b\x6f\x91\xf2\xd3\x7b\x7e\xd2\x3f\xd8\x87\xd4\x52\xfa\xff\xf2\x45\xa6\x97\x90\x60\xbb\x53\x18\x79\x47\x29\x73\xf0\x1a\x86\x95\x6a\xd9\x5b\x0d\xdb\xa9\x93\x85\xbc\xec\x82\xeb\x3b\x52\x1d\x09\x5a\x14\xd4\x0a\xc0\x63\x47\x1a\x8e\xbd\xf7\xe4\xaa\x38\x54\xe3\x58\x7f\x62\x6e\xdf\xed\xf5\xaa\x00\x1c\x1e\xc8\xe5\x89\x03\xc0\x18\x17\xb0\xd4\x52\x30\x0d\xbf\x27\x72\x24\x33\xd1\x06\x23\x1a\x96\xcb\x3c\x99\x25\x24\x3c\x91\x86\xd5\x2b\x2b\x00\x34\x86\x72\x5e\x07\x4b\x37\xe5\x0a\x1a\x42\xbb\x4f\x2c\xb4\x46\x7f\x51\x00\xf1\xee\xc9\x0d\x19\x87\xdc\x6d\x83\x63\x73\xd1\xd0\x90\x20\x7b\x05\xd0\x85\xde\xcb\x47\x14\x0e\xfe\x47\x0b\x9d\x0b\xe7\xca\x92\x23\xa1\x5b\x2b\xd1\x89\x83\x7f\x1c\xc7\xba\x29\x5f\x25\x9b\xc9\x3c\x8f\xd8\xc4\x03\x25\x0d\xf9\xa1\x36\x99\x6b\x3c\xe7\xda\x84\xae\xfc\x1a\x8a\xfb\x1b\x7a\xeb\x68\x02\x2a\x93\xf9\x6e\x45\x0b\xf4\x24\x92\xf8\xd0\xcb\x12\x0a\xe0\xd0\x9b\x96\x64\x53\x16\x3c\x8e\xf5\x73\x29\x67\xeb\x29\x4c\x43\xc7\x99\x9c\x4e\x90\x23\x9a\x19\xdb\x2c\xd5\x4d\xfd\xee\x40\xe6\x8f\xfd\x77\x00\x00\x00\xff\xff\xbf\x6a\x00\x6b\x05\x02\x00\x00") + +func configKubernetesPvYamlBytes() ([]byte, error) { + return bindataRead( + _configKubernetesPvYaml, + "config/kubernetes-pv.yaml", + ) +} + +func configKubernetesPvYaml() (*asset, error) { + bytes, err := configKubernetesPvYamlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "config/kubernetes-pv.yaml", size: 517, mode: os.FileMode(0644), modTime: time.Unix(1732327506, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x62, 0xa5, 0x30, 0x54, 0x11, 0xf3, 0x98, 0xe3, 0xc3, 0x75, 0xf8, 0x72, 0x29, 0x3a, 0x5c, 0x73, 0x31, 0x5b, 0x86, 0xc4, 0x99, 0x1d, 0x1b, 0xe0, 0xb4, 0xde, 0xf1, 0x3a, 0x28, 0xfe, 0xba, 0xea}} + return a, nil +} + var _configKubernetesExecutorTemplateYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x54\xcd\x4f\xdb\x4e\x10\xbd\xfb\xaf\x18\x39\xfc\x6e\xd8\x98\xcb\xef\x60\xa9\x07\x14\x10\x1f\x2a\x01\xb5\x88\x1e\xaa\x1e\xc6\x9b\x49\xb2\xcd\x7e\x75\x67\x1d\x40\x56\xfe\xf7\x6a\xfd\x55\x07\x28\xf5\x69\xb2\xef\xf9\xbd\x37\x93\x59\xcf\xe0\x01\x79\x0b\x17\xcf\x24\xea\x60\x7d\x82\x4e\x3e\x92\x67\x69\x4d\x09\x15\x06\xb1\x39\xd9\x9d\x26\x5b\x69\x96\x25\xdc\xd8\x2a\xd1\x14\x70\x89\x01\xcb\x04\xc0\xa0\xa6\x12\x9a\x26\x8f\x0a\xd7\xcb\xfd\x3e\x6b\x9a\xfc\xc6\x56\xb1\xec\x61\x76\x28\x3a\xce\x62\xf8\xd5\x62\x0a\x2b\x52\x1c\x45\x00\xd0\xb9\x12\x56\xb5\x31\xa4\x32\x1a\x62\x44\xe0\xa7\xad\xb2\x0f\x3d\xd8\x91\x88\x1a\x15\x8a\xad\x5d\xad\x3e\x4b\x2d\x43\x09\x45\x02\x20\xac\x76\x8a\x82\xb4\x86\x4b\x38\x4d\x00\x02\x69\xa7\x30\x50\x67\x39\xbc\x18\x1f\x4f\x1c\xd0\x87\x7b\xab\xa4\x78\x29\x61\x41\x3b\xf2\x3d\xc4\xe4\x77\x52\xd0\x99\x10\xb6\x36\x61\xd1\x46\xe9\x83\x32\xf6\x1c\x61\x4d\x40\x69\xc8\xf3\x20\x98\xf5\x83\xe9\x99\x4f\xd6\x6f\xc9\x67\x93\x16\x7a\x1e\x80\xd4\xb8\xee\xba\xbb\x8e\xd5\x6b\xe4\xbe\x56\x6a\x88\x75\xa6\x9e\xf0\x85\x47\x5c\x58\xad\x31\xfe\x27\xdf\xd3\x93\x4a\x9a\x13\xde\xa4\xc7\x90\x66\x22\xfd\x31\x52\xd0\xaf\xb9\xd5\x9e\x77\xdc\x89\x7a\x8c\x24\xcd\xfa\x5c\xfa\x96\xf0\xcd\xfa\xed\x52\xfa\x09\xc1\x13\xdb\xda\x0b\x1a\x7b\xea\x0e\x7f\xd5\xc4\xe1\xe0\x0c\x40\xb8\x3a\x8a\xc8\x15\x18\x82\x7c\xee\x6a\x86\x02\xb2\xfd\x3e\x1a\xbb\x9a\x63\x01\xa4\x98\x20\x56\xe9\x69\x51\xe8\x34\x56\x74\x10\x28\x3e\x9a\xb4\xf5\x2f\x13\xad\x2f\xa8\x2f\x2b\x28\xf2\x5e\xce\x79\x69\xc2\x0a\xd2\xff\xf2\x62\x75\x99\xf6\x70\x2b\xa5\x98\x3a\xf1\xff\x6f\xff\xa2\x4d\x6e\x43\x9a\x3c\xaa\x8c\x83\xf5\xfd\xd0\x7b\x9b\x73\xc9\xdb\x8f\x7c\x3a\xfc\xd0\xa8\x28\xde\x73\x1a\x8b\x9d\x55\xb5\xa6\xdb\xb8\x34\x93\x69\xcd\x66\x33\x38\xbf\x83\xc5\xdd\x03\xcc\xaf\xce\x16\x97\x17\xf0\x70\x75\xfd\x75\x84\x9b\xc6\xa3\x59\x13\x1c\xc9\xe5\xf3\x31\x1c\xc9\x40\x1a\xca\x4f\x90\x3f\xb6\x62\x3c\xf1\x79\xb5\x60\x7d\x4b\x59\xd3\x1c\xbd\x5d\x31\x00\x1d\x63\xdc\x63\xd8\xc4\x9e\x5b\xd9\x7c\x3e\xec\x6c\x3c\x3e\x20\x73\x5d\x8d\xd4\x51\xec\x5f\xaf\x0d\x73\x48\xa6\xdd\x73\x09\xef\xdf\x87\x3f\x71\xdf\xa6\x75\xf1\xcb\xc3\x81\x4c\xe8\xba\x9e\x2b\x94\x7a\xba\x6f\x22\x1e\x1c\x5c\x44\xb7\x13\x07\x52\xbf\x03\x00\x00\xff\xff\x00\x13\xe6\xe4\xd0\x04\x00\x00") func configKubernetesExecutorTemplateYamlBytes() ([]byte, error) { @@ -169,7 +211,7 @@ func configDefaultConfigYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "config/default-config.yaml", size: 11655, mode: os.FileMode(0644), modTime: time.Unix(1732064227, 0)} + info := bindataFileInfo{name: "config/default-config.yaml", size: 11655, mode: os.FileMode(0644), modTime: time.Unix(1732326201, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x61, 0x5a, 0x7d, 0xb0, 0x9b, 0x9d, 0x13, 0x7, 0x49, 0xd0, 0x4c, 0xab, 0xc2, 0xe5, 0x37, 0xfa, 0x4a, 0x5a, 0x9a, 0x68, 0x89, 0x2b, 0x4b, 0x3e, 0xf3, 0x73, 0x35, 0x82, 0xe8, 0xf1, 0xbf, 0x3c}} return a, nil } @@ -305,9 +347,11 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ + "config/kubernetes-pvc.yaml": configKubernetesPvcYaml, "config/gridengine-template.txt": configGridengineTemplateTxt, "config/pbs-template.txt": configPbsTemplateTxt, "config/slurm-template.txt": configSlurmTemplateTxt, + "config/kubernetes-pv.yaml": configKubernetesPvYaml, "config/kubernetes-executor-template.yaml": configKubernetesExecutorTemplateYaml, "config/default-config.yaml": configDefaultConfigYaml, "config/htcondor-template.txt": configHtcondorTemplateTxt, @@ -365,6 +409,8 @@ var _bintree = &bintree{nil, map[string]*bintree{ "gridengine-template.txt": {configGridengineTemplateTxt, map[string]*bintree{}}, "htcondor-template.txt": {configHtcondorTemplateTxt, map[string]*bintree{}}, "kubernetes-executor-template.yaml": {configKubernetesExecutorTemplateYaml, map[string]*bintree{}}, + "kubernetes-pv.yaml": {configKubernetesPvYaml, map[string]*bintree{}}, + "kubernetes-pvc.yaml": {configKubernetesPvcYaml, map[string]*bintree{}}, "kubernetes-template.yaml": {configKubernetesTemplateYaml, map[string]*bintree{}}, "pbs-template.txt": {configPbsTemplateTxt, map[string]*bintree{}}, "slurm-template.txt": {configSlurmTemplateTxt, map[string]*bintree{}}, diff --git a/deployments/kubernetes/helm/Chart.yaml b/deployments/kubernetes/helm/Chart.yaml index c93d104c8..87938faf9 100644 --- a/deployments/kubernetes/helm/Chart.yaml +++ b/deployments/kubernetes/helm/Chart.yaml @@ -17,7 +17,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.12 +version: 0.1.13 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/deployments/kubernetes/helm/config/funnel-server.yaml b/deployments/kubernetes/helm/config/funnel-server.yaml index 29812b538..5deeb38fe 100644 --- a/deployments/kubernetes/helm/config/funnel-server.yaml +++ b/deployments/kubernetes/helm/config/funnel-server.yaml @@ -1,8 +1,15 @@ Compute: {{ .Values.Compute }} Kubernetes: - Executor: "kubernetes" + Executor: {{ .Values.Kubernetes.Executor }} Namespace: {{ .Release.Namespace }} + DisableReconciler: {{ .Values.Kubernetes.DisableReconciler }} + ReconcileRate: {{ .Values.Kubernetes.ReconcileRate }} + ServiceAccount: {{ .Values.Kubernetes.ServiceAccount }} + Template: {{ .Values.Kubernetes.Template }} + TemplateFile: {{ .Values.Kubernetes.TemplateFile }} + Bucket: {{ .Values.Kubernetes.Bucket }} + Region: {{ .Values.Kubernetes.Region }} Database: {{ .Values.Database }} diff --git a/deployments/kubernetes/helm/config/funnel-worker.yaml b/deployments/kubernetes/helm/config/funnel-worker.yaml index 5f2a3cb61..d8abf37d3 100644 --- a/deployments/kubernetes/helm/config/funnel-worker.yaml +++ b/deployments/kubernetes/helm/config/funnel-worker.yaml @@ -3,8 +3,15 @@ Database: {{ .Values.Database }} Compute: {{ .Values.Compute }} Kubernetes: - Executor: "kubernetes" + Executor: {{ .Values.Kubernetes.Executor }} Namespace: {{ .Release.Namespace }} + DisableReconciler: {{ .Values.Kubernetes.DisableReconciler }} + ReconcileRate: {{ .Values.Kubernetes.ReconcileRate }} + ServiceAccount: {{ .Values.Kubernetes.ServiceAccount }} + Template: {{ .Values.Kubernetes.Template }} + TemplateFile: {{ .Values.Kubernetes.TemplateFile }} + Bucket: {{ .Values.Kubernetes.Bucket }} + Region: {{ .Values.Kubernetes.Region }} Logger: Level: {{ .Values.Logger.level }} diff --git a/deployments/kubernetes/helm/values.yaml b/deployments/kubernetes/helm/values.yaml index 55ae99bf8..1860b2c60 100644 --- a/deployments/kubernetes/helm/values.yaml +++ b/deployments/kubernetes/helm/values.yaml @@ -349,7 +349,7 @@ AWSBatch: # Kubernetes describes the configuration for the Kubernetes compute backend. Kubernetes: # The executor used to execute tasks. Available executors: docker, kubernetes - Executor: "kubernetes" + Executor: "kubernetes" # Turn off task state reconciler. When enabled, Funnel communicates with Kubernetes # to find tasks that are stuck in a queued state or errored and # updates the task state accordingly. @@ -365,6 +365,10 @@ Kubernetes: Template: "" # TemplateFile is the path to the master job template. TemplateFile: "" + # The bucket to use for the task's Working Directory + Bucket: "" + # The region to use for the task's Bucket + Region: "" # Configuration of the Kubernetes executor. diff --git a/worker/kubernetes.go b/worker/kubernetes.go index 2ad7ca6b4..c68a7d1ef 100644 --- a/worker/kubernetes.go +++ b/worker/kubernetes.go @@ -29,7 +29,7 @@ type KubernetesCommand struct { Command } -// Create the Executor K8s job +// Create the Executor K8s job from kubernetes-executor-template.yaml // Funnel Worker job is created in compute/kubernetes/backend.go#createJob func (kcmd KubernetesCommand) Run(ctx context.Context) error { var taskId = kcmd.TaskId diff --git a/worker/worker.go b/worker/worker.go index ab4210202..93683ddf0 100644 --- a/worker/worker.go +++ b/worker/worker.go @@ -34,6 +34,10 @@ type Executor struct { Backend string // Kubernetes executor template Template string + // Kubernetes persistent volume template + PVTemplate string + // Kubernetes persistent volume claim template + PVCTemplate string // Kubernetes namespace Namespace string // Kubernetes service account name From 00a35f965ae070906f0db41774dbb5294765dbc0 Mon Sep 17 00:00:00 2001 From: Liam Beckman Date: Wed, 4 Dec 2024 17:56:21 -0800 Subject: [PATCH 3/8] Update command string handling in K8s templates --- config/internal/bundle.go | 12 ++++----- config/kubernetes-pv.yaml | 27 +++++++++++++++++++++ config/kubernetes-pvc.yaml | 16 ++++++++++++ funnel.config.yml | 8 ------ go.mod | 2 +- website/content/docs/compute/kubernetes.md | 4 ++- website/static/img/k8s-pvc.png | Bin 128139 -> 41441 bytes worker/kubernetes.go | 17 ++++++++++--- 8 files changed, 67 insertions(+), 19 deletions(-) create mode 100644 config/kubernetes-pv.yaml create mode 100644 config/kubernetes-pvc.yaml delete mode 100644 funnel.config.yml diff --git a/config/internal/bundle.go b/config/internal/bundle.go index 0710d7ba9..a707627bd 100644 --- a/config/internal/bundle.go +++ b/config/internal/bundle.go @@ -4,7 +4,7 @@ // config/gridengine-template.txt (346B) // config/pbs-template.txt (361B) // config/slurm-template.txt (415B) -// config/kubernetes-pv.yaml (517B) +// config/kubernetes-pv.yaml (560B) // config/kubernetes-executor-template.yaml (1.232kB) // config/default-config.yaml (11.655kB) // config/htcondor-template.txt (505B) @@ -91,7 +91,7 @@ func configKubernetesPvcYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "config/kubernetes-pvc.yaml", size: 309, mode: os.FileMode(0644), modTime: time.Unix(1732326881, 0)} + info := bindataFileInfo{name: "config/kubernetes-pvc.yaml", size: 309, mode: os.FileMode(0644), modTime: time.Unix(1732590327, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe9, 0xc, 0x1f, 0x92, 0xa1, 0xb3, 0x7, 0x8, 0xb0, 0x29, 0x2f, 0x8e, 0x77, 0x9d, 0x10, 0x18, 0xc6, 0xcd, 0x90, 0xbf, 0xdf, 0x92, 0xf6, 0xf5, 0x81, 0x70, 0x47, 0xb6, 0x35, 0x85, 0x1a, 0x9d}} return a, nil } @@ -156,7 +156,7 @@ func configSlurmTemplateTxt() (*asset, error) { return a, nil } -var _configKubernetesPvYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x5c\x91\x41\x6b\xfb\x30\x0c\xc5\xef\xfe\x14\x82\xff\x39\xf9\x53\x76\x33\xec\xb0\xc1\xd8\x76\x68\x57\xc2\x68\xcf\xaa\xad\x16\x11\xc7\x36\xb6\x92\xae\x84\x7e\xf7\x11\xa7\x19\x5b\x6f\x91\xf2\xd3\x7b\x7e\xd2\x3f\xd8\x87\xd4\x52\xfa\xff\xf2\x45\xa6\x97\x90\x60\xbb\x53\x18\x79\x47\x29\x73\xf0\x1a\x86\x95\x6a\xd9\x5b\x0d\xdb\xa9\x93\x85\xbc\xec\x82\xeb\x3b\x52\x1d\x09\x5a\x14\xd4\x0a\xc0\x63\x47\x1a\x8e\xbd\xf7\xe4\xaa\x38\x54\xe3\x58\x7f\x62\x6e\xdf\xed\xf5\xaa\x00\x1c\x1e\xc8\xe5\x89\x03\xc0\x18\x17\xb0\xd4\x52\x30\x0d\xbf\x27\x72\x24\x33\xd1\x06\x23\x1a\x96\xcb\x3c\x99\x25\x24\x3c\x91\x86\xd5\x2b\x2b\x00\x34\x86\x72\x5e\x07\x4b\x37\xe5\x0a\x1a\x42\xbb\x4f\x2c\xb4\x46\x7f\x51\x00\xf1\xee\xc9\x0d\x19\x87\xdc\x6d\x83\x63\x73\xd1\xd0\x90\x20\x7b\x05\xd0\x85\xde\xcb\x47\x14\x0e\xfe\x47\x0b\x9d\x0b\xe7\xca\x92\x23\xa1\x5b\x2b\xd1\x89\x83\x7f\x1c\xc7\xba\x29\x5f\x25\x9b\xc9\x3c\x8f\xd8\xc4\x03\x25\x0d\xf9\xa1\x36\x99\x6b\x3c\xe7\xda\x84\xae\xfc\x1a\x8a\xfb\x1b\x7a\xeb\x68\x02\x2a\x93\xf9\x6e\x45\x0b\xf4\x24\x92\xf8\xd0\xcb\x12\x0a\xe0\xd0\x9b\x96\x64\x53\x16\x3c\x8e\xf5\x73\x29\x67\xeb\x29\x4c\x43\xc7\x99\x9c\x4e\x90\x23\x9a\x19\xdb\x2c\xd5\x4d\xfd\xee\x40\xe6\x8f\xfd\x77\x00\x00\x00\xff\xff\xbf\x6a\x00\x6b\x05\x02\x00\x00") +var _configKubernetesPvYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x5c\x91\x41\xab\xdb\x30\x0c\xc7\xef\xfe\x14\x82\x9d\x93\xed\x31\x1e\x03\xc3\x3b\x6c\x30\xb6\x1d\xda\x95\x30\xda\xb3\x6a\xab\x45\xc4\xb1\x8d\xad\xa4\x2b\xa1\xdf\x7d\xd8\x69\xc6\x6b\x6f\x96\xf4\xd3\x5f\xfa\x5b\x1f\xe0\x10\x52\x4f\xe9\xe3\xf7\xbf\x64\x46\x09\x09\x76\x7b\x85\x91\xf7\x94\x32\x07\xaf\x61\x7a\x51\x3d\x7b\xab\x61\x57\x32\x59\xc8\xcb\x3e\xb8\x71\x20\x35\x90\xa0\x45\x41\xad\x00\x3c\x0e\xa4\xe1\x34\x7a\x4f\xae\x89\x53\x33\xcf\xed\x1f\xcc\xfd\x2f\x7b\xbb\x29\x00\x87\x47\x72\xb9\x70\x00\x18\xe3\x0a\xd6\x58\x2a\xa6\xe1\x7d\x47\x8e\x64\x0a\x6d\x30\xa2\x61\xb9\x2e\x9d\x59\x42\xc2\x33\x69\x78\xf9\xc1\x0a\x00\x8d\xa1\x9c\x37\xc1\xd2\x5d\xb9\x81\x8e\xd0\x1e\x12\x0b\x6d\xd0\x5f\x15\x40\x7c\x5a\xb9\x23\xe3\x90\x87\x5d\x70\x6c\xae\x1a\x3a\x12\x64\xaf\x00\x86\x30\x7a\xf9\x1d\x85\x83\xff\xaf\x85\xce\x85\x4b\x63\xc9\x91\xd0\x43\x2a\x4c\x94\x2e\x65\xc8\x3d\x9b\xe8\xcc\xc1\xbf\xcd\x73\xdb\xd5\x57\x75\x5c\x0a\x27\x76\xd4\x0c\xc1\xd2\xdb\xa7\x2f\xaf\xaf\xc5\x4e\xe6\x45\xdd\x26\x9e\x28\x69\xc8\x9f\x5b\x93\xb9\xc5\x4b\x6e\x4d\x18\x6a\x69\xaa\x8b\xfe\x44\x6f\x1d\x15\xa0\x31\x99\x9f\x7e\x73\x85\xbe\x8a\x24\x3e\x8e\xb2\xfa\x07\x38\x8e\xa6\x27\xd9\xd6\x5b\xcc\x73\xfb\xad\x86\xb5\xa7\xfa\xee\xe8\xb4\x90\xe5\x5a\x39\xa2\x59\xb0\xed\x1a\xdd\xd5\x9f\x6e\x69\x1e\xc6\xff\x0b\x00\x00\xff\xff\x33\x84\x08\xef\x30\x02\x00\x00") func configKubernetesPvYamlBytes() ([]byte, error) { return bindataRead( @@ -171,8 +171,8 @@ func configKubernetesPvYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "config/kubernetes-pv.yaml", size: 517, mode: os.FileMode(0644), modTime: time.Unix(1732327506, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x62, 0xa5, 0x30, 0x54, 0x11, 0xf3, 0x98, 0xe3, 0xc3, 0x75, 0xf8, 0x72, 0x29, 0x3a, 0x5c, 0x73, 0x31, 0x5b, 0x86, 0xc4, 0x99, 0x1d, 0x1b, 0xe0, 0xb4, 0xde, 0xf1, 0x3a, 0x28, 0xfe, 0xba, 0xea}} + info := bindataFileInfo{name: "config/kubernetes-pv.yaml", size: 560, mode: os.FileMode(0644), modTime: time.Unix(1732649398, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa9, 0x15, 0x2e, 0x42, 0x94, 0x68, 0xc1, 0xf2, 0x27, 0xfe, 0xb7, 0xa7, 0xe8, 0x8d, 0x53, 0x1a, 0x1b, 0x36, 0xb0, 0x5c, 0xc, 0x59, 0xb4, 0x7d, 0x16, 0x9c, 0x2b, 0x72, 0xfc, 0x17, 0x7c, 0x20}} return a, nil } @@ -191,7 +191,7 @@ func configKubernetesExecutorTemplateYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "config/kubernetes-executor-template.yaml", size: 1232, mode: os.FileMode(0644), modTime: time.Unix(1732240371, 0)} + info := bindataFileInfo{name: "config/kubernetes-executor-template.yaml", size: 1232, mode: os.FileMode(0644), modTime: time.Unix(1733362467, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x28, 0xc4, 0x2b, 0x17, 0x62, 0x5d, 0x51, 0xe4, 0x35, 0x84, 0xde, 0x2a, 0x3, 0x35, 0x7d, 0x71, 0x79, 0x5a, 0xe0, 0xbd, 0x90, 0xcd, 0x65, 0x9d, 0xe8, 0x11, 0x72, 0x90, 0x35, 0xe7, 0x36, 0x7a}} return a, nil } diff --git a/config/kubernetes-pv.yaml b/config/kubernetes-pv.yaml new file mode 100644 index 000000000..7bc53f64f --- /dev/null +++ b/config/kubernetes-pv.yaml @@ -0,0 +1,27 @@ +# Worker/Executor PV +apiVersion: v1 +kind: PersistentVolume +metadata: + name: funnel-pv-{{.TaskId}} + labels: + app: funnel + taskId: {{.TaskId}} +spec: + capacity: + storage: 1Gi + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + mountOptions: + - allow-delete + - allow-overwrite + - region={{.Region}} + - file-mode=0755 + csi: + driver: s3.csi.aws.com + volumeHandle: s3-csi-{{.TaskId}} + volumeAttributes: + bucketName: {{.Bucket}} + claimRef: + namespace: {{.Namespace}} + name: funnel-pvc-{{.TaskId}} diff --git a/config/kubernetes-pvc.yaml b/config/kubernetes-pvc.yaml new file mode 100644 index 000000000..183a87ba9 --- /dev/null +++ b/config/kubernetes-pvc.yaml @@ -0,0 +1,16 @@ +# Worker/Executor PVC +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: funnel-pvc-{{.TaskId}} + namespace: {{ .Namespace }} + labels: + app: funnel + taskId: {{.TaskId}} +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: 1Gi + volumeName: funnel-pv-{{.TaskId}} diff --git a/funnel.config.yml b/funnel.config.yml deleted file mode 100644 index caf984b06..000000000 --- a/funnel.config.yml +++ /dev/null @@ -1,8 +0,0 @@ -LocalStorage: - # Whitelist of local directory paths which Funnel is allowed to access. - AllowedDirs: - - ./ - - /tmp - -Worker: - LeaveWorkDir: true diff --git a/go.mod b/go.mod index cf441cb8d..c26f35ef8 100644 --- a/go.mod +++ b/go.mod @@ -169,7 +169,7 @@ require ( golang.org/x/sys v0.27.0 // indirect golang.org/x/term v0.26.0 // indirect golang.org/x/text v0.20.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/website/content/docs/compute/kubernetes.md b/website/content/docs/compute/kubernetes.md index 004a285fb..b484fda49 100644 --- a/website/content/docs/compute/kubernetes.md +++ b/website/content/docs/compute/kubernetes.md @@ -104,7 +104,9 @@ funnel task create hello-world.json # Storage Architecture -![K8s Storage](/img/k8s-pvc.png) + + + # Additional Resources 📚 diff --git a/website/static/img/k8s-pvc.png b/website/static/img/k8s-pvc.png index 9325fb5bc382489853c90d2b5a097fc65c1d7056..7a815d5b503686ad6b351dfd3828dd9200c8b176 100644 GIT binary patch literal 41441 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%0DwM0DYd!_Px`6){#zaD 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 ZKR+n+5D9+KxJENMYiM@5*uehIe*v8I$bSF; diff --git a/worker/kubernetes.go b/worker/kubernetes.go index c68a7d1ef..49987127c 100644 --- a/worker/kubernetes.go +++ b/worker/kubernetes.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "io" + "strings" "text/template" "github.com/ohsu-comp-bio/funnel/tes" @@ -26,6 +27,7 @@ type KubernetesCommand struct { Namespace string Resources *tes.Resources ServiceAccount string + Clientset kubernetes.Interface Command } @@ -43,6 +45,11 @@ func (kcmd KubernetesCommand) Run(ctx context.Context) error { if kcmd.StdinFile != "" { command = append(command, "<", kcmd.StdinFile) } + for i, v := range command { + if strings.Contains(v, " ") { + command[i] = fmt.Sprintf("'%s'", v) + } + } var buf bytes.Buffer err = tpl.Execute(&buf, map[string]interface{}{ @@ -74,9 +81,13 @@ func (kcmd KubernetesCommand) Run(ctx context.Context) error { return err } - clientset, err := getKubernetesClientset() - if err != nil { - return err + clientset := kcmd.Clientset + if clientset == nil { + var err error + clientset, err = getKubernetesClientset() + if err != nil { + return err + } } var client = clientset.BatchV1().Jobs(kcmd.Namespace) From 035e80e3eb76b3a7472ac393d5ee54516b59ff7d Mon Sep 17 00:00:00 2001 From: Liam Beckman Date: Thu, 5 Dec 2024 12:55:58 -0800 Subject: [PATCH 4/8] K8s Github Actions Debugging --- .github/workflows/k8s.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/k8s.yaml b/.github/workflows/k8s.yaml index 58d4e9b1c..a951a57ee 100644 --- a/.github/workflows/k8s.yaml +++ b/.github/workflows/k8s.yaml @@ -35,8 +35,9 @@ jobs: - name: Deploy Funnel run: | helm repo add ohsu https://ohsu-comp-bio.github.io/helm-charts - # 'local-path' is a k3d specific storage class - # Ref: https://k3d.io/v5.7.4/usage/k3s/#local-path-provisioner + + # 'local-path' is a k3d specific storage class used to automatically create a PersistentVolume + # - Ref: https://k3d.io/v5.7.4/usage/k3s/#local-path-provisioner helm upgrade --install funnel ohsu/funnel --set storage.className=local-path --set storage.provisioner=local-path # Wait for the Deployment to be available @@ -45,6 +46,9 @@ jobs: # Port-forward the service kubectl port-forward svc/funnel 8000:8000 & + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + - name: Submit Task run: | export PATH="$PATH:$(pwd)" From c4ed9b256192e63b94d4ea792ccf3f702651b49c Mon Sep 17 00:00:00 2001 From: Liam Beckman Date: Mon, 9 Dec 2024 18:40:26 -0800 Subject: [PATCH 5/8] Update Funnel Helm Chart version to 0.1.14 --- README.md | 25 +++++++++++++++---- deployments/kubernetes/helm/Chart.yaml | 2 +- .../helm/templates/clusterrolebinding.yaml | 2 +- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b00de8d85..2902a9668 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,23 @@ -[![Build Status](https://img.shields.io/github/actions/workflow/status/ohsu-comp-bio/funnel/tests.yaml)](https://github.com/ohsu-comp-bio/funnel/actions/workflows/tests.yaml) -[![Compliance Tests Status](https://img.shields.io/github/actions/workflow/status/ohsu-comp-bio/funnel/compliance-test.yaml?label=Compliance%20Tests)](https://github.com/ohsu-comp-bio/funnel/actions/workflows/compliance-test.yaml) -[![Gitter](https://badges.gitter.im/ohsu-comp-bio/funnel.svg)](https://gitter.im/ohsu-comp-bio/funnel) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Godoc](https://img.shields.io/badge/godoc-ref-blue.svg)](http://godoc.org/github.com/ohsu-comp-bio/funnel) +[![Build Status][build-badge]][build] +[![Compliance Tests Status][compliance-tests-badge]][compliance-tests] +[![Gitter][gitter-badge]][gitter] +[![License: MIT][license-badge]][license] +[![Godoc][godoc-badge]][godoc] + +[build-badge]: https://img.shields.io/github/actions/workflow/status/ohsu-comp-bio/funnel/tests.yaml +[build]: https://github.com/ohsu-comp-bio/funnel/actions/workflows/tests.yaml + +[compliance-tests]: https://github.com/ohsu-comp-bio/funnel/actions/workflows/compliance-test.yaml +[compliance-tests-badge]: https://img.shields.io/github/actions/workflow/status/ohsu-comp-bio/funnel/compliance-test.yaml?label=Compliance%20Tests + +[gitter-badge]: https://badges.gitter.im/ohsu-comp-bio/funnel.svg +[gitter]: https://gitter.im/ohsu-comp-bio/funnel + +[license-badge]: https://img.shields.io/badge/License-MIT-yellow.svg +[license]: https://opensource.org/licenses/MIT + +[godoc-badge]: https://img.shields.io/badge/godoc-ref-blue.svg +[godoc]: http://godoc.org/github.com/ohsu-comp-bio/funnel diff --git a/deployments/kubernetes/helm/Chart.yaml b/deployments/kubernetes/helm/Chart.yaml index 87938faf9..4982d262a 100644 --- a/deployments/kubernetes/helm/Chart.yaml +++ b/deployments/kubernetes/helm/Chart.yaml @@ -17,7 +17,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.13 +version: 0.1.14 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/deployments/kubernetes/helm/templates/clusterrolebinding.yaml b/deployments/kubernetes/helm/templates/clusterrolebinding.yaml index 672d4d661..939c36f09 100644 --- a/deployments/kubernetes/helm/templates/clusterrolebinding.yaml +++ b/deployments/kubernetes/helm/templates/clusterrolebinding.yaml @@ -5,7 +5,7 @@ metadata: subjects: - kind: ServiceAccount name: funnel-sa - namespace: funnel + namespace: {{ .Release.Namespace }} roleRef: kind: ClusterRole name: funnel-clusterrole From 2bbd26ebd0a7a20cb1a2aaa4041654271ffc86c9 Mon Sep 17 00:00:00 2001 From: Liam Beckman Date: Tue, 17 Dec 2024 15:30:56 -0800 Subject: [PATCH 6/8] railway.app experiment --- Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 71520ed07..32b259d93 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,6 @@ RUN --mount=type=cache,target=/root/.cache/go-build make build # final stage FROM alpine WORKDIR /opt/funnel -VOLUME /opt/funnel/funnel-work-dir EXPOSE 8000 9090 ENV PATH="/app:${PATH}" COPY --from=build-env /go/src/github.com/ohsu-comp-bio/funnel/funnel /app/ From dbc53496f99e4daf582e0bd653a904cbb6aafdcc Mon Sep 17 00:00:00 2001 From: Liam Beckman Date: Thu, 19 Dec 2024 15:08:31 -0800 Subject: [PATCH 7/8] Bump Helm Chart version to 0.1.15 --- .github/workflows/build.yml | 3 ++- deployments/kubernetes/helm/Chart.yaml | 2 +- .../kubernetes/helm/templates/clusterrole.yaml | 16 ++++++++-------- .../helm/templates/clusterrolebinding.yaml | 6 +++--- go.mod | 1 + go.sum | 2 ++ 6 files changed, 17 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b853e0630..707d40fe8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v5 with: - go-version: 1.21 + go-version: 1.22 - name: Check out code uses: actions/checkout@v2 @@ -23,6 +23,7 @@ jobs: key: ${{ runner.os }}-funnel-bin-${{ hashFiles('**/go.sum') }}-${{ github.ref }} restore-keys: | ${{ runner.os }}-funnel-bin-${{ github.ref }} + ${{ runner.os }}-funnel-bin- - name: Build Funnel (if cache doesn't exist) run: | diff --git a/deployments/kubernetes/helm/Chart.yaml b/deployments/kubernetes/helm/Chart.yaml index 4982d262a..d55e41595 100644 --- a/deployments/kubernetes/helm/Chart.yaml +++ b/deployments/kubernetes/helm/Chart.yaml @@ -17,7 +17,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.14 +version: 0.1.15 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/deployments/kubernetes/helm/templates/clusterrole.yaml b/deployments/kubernetes/helm/templates/clusterrole.yaml index 39968966d..31e4ff694 100644 --- a/deployments/kubernetes/helm/templates/clusterrole.yaml +++ b/deployments/kubernetes/helm/templates/clusterrole.yaml @@ -1,15 +1,15 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: funnel-clusterrole + name: {{ .Release.Namespace }}-{{ .Release.Name }}-clusterrole rules: - apiGroups: [""] resources: - - persistentvolumes + - persistentvolumes verbs: - - get - - list - - watch - - create - - update - - delete + - get + - list + - watch + - create + - update + - delete diff --git a/deployments/kubernetes/helm/templates/clusterrolebinding.yaml b/deployments/kubernetes/helm/templates/clusterrolebinding.yaml index 939c36f09..6c3584fa6 100644 --- a/deployments/kubernetes/helm/templates/clusterrolebinding.yaml +++ b/deployments/kubernetes/helm/templates/clusterrolebinding.yaml @@ -1,12 +1,12 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: funnel-clusterrolebinding + name: {{ .Release.Namespace }}-{{ .Release.Name }}-clusterrolebinding subjects: - kind: ServiceAccount name: funnel-sa - namespace: {{ .Release.Namespace }} + namespace: {{ .Release.Namespace }} roleRef: kind: ClusterRole - name: funnel-clusterrole + name: {{ .Release.Namespace }}-{{ .Release.Name }}-clusterrole apiGroup: rbac.authorization.k8s.io diff --git a/go.mod b/go.mod index c26f35ef8..d6234916a 100644 --- a/go.mod +++ b/go.mod @@ -135,6 +135,7 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/montanaflynn/stats v0.7.1 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/muonsoft/openapi-mock v0.3.9 // indirect github.com/nsf/termbox-go v1.1.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect diff --git a/go.sum b/go.sum index fe58414e9..493f098f5 100644 --- a/go.sum +++ b/go.sum @@ -316,6 +316,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/muonsoft/openapi-mock v0.3.9 h1:MNeqzwk6QzGTtnhvyRyDIywvt1YdCIvBEbtENiOnXmo= +github.com/muonsoft/openapi-mock v0.3.9/go.mod h1:M5zGO4+q2BSS+KKsZtHX+lBFKMJ5QTQ0PdK2SdhHHc4= github.com/ncw/swift v1.0.53 h1:luHjjTNtekIEvHg5KdAFIBaH7bWfNkefwFnpDffSIks= github.com/ncw/swift v1.0.53/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/nsf/termbox-go v1.1.1 h1:nksUPLCb73Q++DwbYUBEglYBRPZyoXJdrj5L+TkjyZY= From d1c0a887f4265e0b0945f90f1ed33bc31da749dd Mon Sep 17 00:00:00 2001 From: Liam Beckman Date: Wed, 8 Jan 2025 14:30:55 -0800 Subject: [PATCH 8/8] Revert change to default executor (from k8s to docker) --- config/default-config.yaml | 2 +- config/default.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/default-config.yaml b/config/default-config.yaml index 21b7b409a..15cf647f3 100644 --- a/config/default-config.yaml +++ b/config/default-config.yaml @@ -299,7 +299,7 @@ AWSBatch: # Kubernetes describes the configuration for the Kubernetes compute backend. Kubernetes: # The executor used to execute tasks. Available executors: docker, kubernetes - Executor: "kubernetes" + Executor: "docker" # Turn off task state reconciler. When enabled, Funnel communicates with Kubernetes # to find tasks that are stuck in a queued state or errored and # updates the task state accordingly. diff --git a/config/default.go b/config/default.go index 13fbb47e8..f873a859c 100644 --- a/config/default.go +++ b/config/default.go @@ -166,7 +166,7 @@ func DefaultConfig() Config { executorTemplate := intern.MustAsset("config/kubernetes-executor-template.yaml") pvTemplate := intern.MustAsset("config/kubernetes-pv.yaml") pvcTemplate := intern.MustAsset("config/kubernetes-pvc.yaml") - c.Kubernetes.Executor = "kubernetes" + c.Kubernetes.Executor = "docker" c.Kubernetes.Namespace = "default" c.Kubernetes.ServiceAccount = "funnel-sa" c.Kubernetes.Template = string(kubernetesTemplate)