diff --git a/nextflow/Dockerfile.dev b/nextflow/Dockerfile.dev new file mode 100644 index 0000000..77cd1be --- /dev/null +++ b/nextflow/Dockerfile.dev @@ -0,0 +1,12 @@ +FROM eclipse-temurin:17-jre + +WORKDIR /opt/nextflow + +COPY . /opt/nextflow + +RUN chmod +x /opt/nextflow/launch.sh /opt/nextflow/nextflow || true + +ENV PATH="/opt/nextflow:${PATH}" +ENV NXF_HOME="/opt/nextflow/.nextflow" + +ENTRYPOINT ["/opt/nextflow/launch.sh"] diff --git a/nextflow/modules/nextflow/src/main/resources/META-INF/build-info.properties b/nextflow/modules/nextflow/src/main/resources/META-INF/build-info.properties index 458b79d..00d4739 100644 --- a/nextflow/modules/nextflow/src/main/resources/META-INF/build-info.properties +++ b/nextflow/modules/nextflow/src/main/resources/META-INF/build-info.properties @@ -1,4 +1,4 @@ build=0 version=26.04.0 -timestamp=1777432778635 -commitId=f9ab19000 +timestamp=1778418181930 +commitId=ad0d91a diff --git a/nextflow/plugins/nf-k8s/src/main/nextflow/k8s/K8sConfig.groovy b/nextflow/plugins/nf-k8s/src/main/nextflow/k8s/K8sConfig.groovy index 9cfa544..6ef912b 100644 --- a/nextflow/plugins/nf-k8s/src/main/nextflow/k8s/K8sConfig.groovy +++ b/nextflow/plugins/nf-k8s/src/main/nextflow/k8s/K8sConfig.groovy @@ -212,10 +212,15 @@ class K8sConfig implements ConfigScope { """) final String workDir - @ConfigOption @Description("Node initialization config") final K8sNodeInitConfig nodeInit + @ConfigOption + @Description(""" + The image name of the nextflow launcher image + """) + final String nextflowImage + /* required by extension point -- do not remove */ K8sConfig() { this(Collections.emptyMap()) @@ -245,6 +250,7 @@ class K8sConfig implements ConfigScope { storageMountPath = opts.storageMountPath ?: '/workspace' storageSubPath = opts.storageSubPath userName = opts.userName + nextflowImage = opts.nextflowImage ?: "nextflow/nextflow:${BuildInfo.version}" launchDir = opts.launchDir ?: "${storageMountPath}/${getUserName()}" projectDir = opts.projectDir ?: "${storageMountPath}/projects" @@ -266,6 +272,7 @@ class K8sConfig implements ConfigScope { else if( securityContext ) pod.securityContext = new PodSecurityContext(securityContext) + log.info("Hello sailor") nodeInit = new K8sNodeInitConfig(opts.nodeInit as Map ?: Collections.emptyMap()) } @@ -327,7 +334,7 @@ class K8sConfig implements ConfigScope { boolean useJobResource() { ResourceType.Job.name() == computeResourceType } String getNextflowImageName() { - return "nextflow/nextflow:${BuildInfo.version}" + return nextflowImage } PodOptions getPodOptions() { diff --git a/nextflow/plugins/nf-k8s/src/main/nextflow/k8s/K8sNodeInitDeployer.groovy b/nextflow/plugins/nf-k8s/src/main/nextflow/k8s/K8sNodeInitDeployer.groovy index e02b2e1..30d55ec 100644 --- a/nextflow/plugins/nf-k8s/src/main/nextflow/k8s/K8sNodeInitDeployer.groovy +++ b/nextflow/plugins/nf-k8s/src/main/nextflow/k8s/K8sNodeInitDeployer.groovy @@ -1,9 +1,11 @@ package nextflow.k8s +import groovy.util.logging.Slf4j import nextflow.k8s.client.K8sClient import nextflow.k8s.model.PodHostMount import nextflow.k8s.model.PodSpecBuilder +@Slf4j class K8sNodeInitDeployer { private K8sClient client private K8sConfig config @@ -20,12 +22,16 @@ class K8sNodeInitDeployer { if ( !init?.enabled ) return + log.info("deploying init pods") + final nodes = getNodes() for ( String nodeName : nodes ) { + log.info(" ... deploying to " + nodeName) final spec = makePodSpec(init, nodeName) client.podCreate(spec) } + log.info("waiting for init pods") waitForPods(nodes) } diff --git a/nextflow/plugins/nf-k8s/src/main/nextflow/k8s/K8sTaskScheduler.groovy b/nextflow/plugins/nf-k8s/src/main/nextflow/k8s/K8sTaskScheduler.groovy index 57957c0..4d825ab 100644 --- a/nextflow/plugins/nf-k8s/src/main/nextflow/k8s/K8sTaskScheduler.groovy +++ b/nextflow/plugins/nf-k8s/src/main/nextflow/k8s/K8sTaskScheduler.groovy @@ -20,7 +20,10 @@ class K8sTaskScheduler { } void submit(K8sTaskHandler handler) { + log.debug "[K8s] received queued task ${handler.task.name}" queue.add(new K8sSchedulingRequest(handler)) + + drain() } protected synchronized void drain() { @@ -34,7 +37,7 @@ class K8sTaskScheduler { if ( !queue.remove(decision.request) ) continue - log.trace "[K8s] launching queued task ${decision.request.task.name} on node: ${decision.nodeName}" + log.debug "[K8s] launching queued task ${decision.request.task.name} on node: ${decision.nodeName}" decision.request.handler.submitNow(decision.nodeName) } } diff --git a/provision-volume-claim.sh b/provision-volume-claim.sh index 8e236bc..1fb1731 100755 --- a/provision-volume-claim.sh +++ b/provision-volume-claim.sh @@ -1,28 +1,31 @@ #!/bin/bash # This creates a persistent volume and claim. # Params: -# -p|--pv -c|--pvc -s|--nfs-server -n|--nfs-path -# pv-name - the name used for the persistent volume -# pvc-name - the name used for the claim +# -p|--pv -c|--pvc -s|--nfs-server -n|--nfs-path -N|--namespace +# pv-name - the name used for the persistent volume +# pvc-name - the name used for the claim # nfs-server - IP or hostname of the NFS server -# nfs-path - Path on the NFS server +# nfs-path - Path on the NFS server +# namespace - Kubernetes namespace PV_NAME= PVC_NAME= NFS_SERVER= NFS_PATH= SIZE= +NAMESPACE=default usage() { echo "Usage: provision-volume-claim.sh < -p | --pv-name name > < -c | --pvc-name name > < -n | --nfs-server server > < -m | --nfs-path path > - < -s | --size size >" + < -s | --size size > + < -N | --namespace name >" exit 2 } -PARSED_ARGUMENTS=$(getopt -a -n provision-volume-claim -o p:c:n:m:s: --long pv-name:,pvc-name:,nfs-server:,nfs-path:,size: -- "$@") +PARSED_ARGUMENTS=$(getopt -a -n provision-volume-claim -o p:c:n:m:s:N: --long pv-name:,pvc-name:,nfs-server:,nfs-path:,size:,namespace: -- "$@") VALID_ARGUMENTS=$? if [ "$VALID_ARGUMENTS" != "0" ]; then usage @@ -37,6 +40,7 @@ do -n | --nfs-server) NFS_SERVER="$2" ; shift 2 ;; -m | --nfs-path) NFS_PATH="$2" ; shift 2 ;; -s | --size) SIZE="$2" ; shift 2 ;; + -N | --namespace) NAMESPACE="$2" ; shift 2 ;; # -- means the end of the arguments; drop this, and break out of the while loop --) shift; break ;; *) echo "Unexpected option: $1 - this should not happen." @@ -69,7 +73,6 @@ spec: - ReadWriteMany persistentVolumeReclaimPolicy: Recycle storageClassName: slow - mountOptions: - hard - nfsvers=4.1 @@ -88,6 +91,7 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: name: $PVC_NAME + namespace: $NAMESPACE spec: accessModes: - ReadWriteMany @@ -106,7 +110,7 @@ cat $PVC_MANIFEST # Generate cleanup script cat > cleanup-volume-claim.sh << EOM #!/bin/bash -kubectl delete pvc $PVC_NAME +kubectl delete pvc $PVC_NAME -n $NAMESPACE kubectl delete pv $PV_NAME EOM chmod +x cleanup-volume-claim.sh diff --git a/test/main.nf b/test/main.nf index 32bf937..e88c16d 100644 --- a/test/main.nf +++ b/test/main.nf @@ -1,52 +1,18 @@ -params.str = "Hello, world!" +nextflow.enable.dsl = 2 -process split { - publishDir "results/lower" - - input: - val x +process hello { + container 'ubuntu:22.04' output: - path 'chunk_*' + path 'hello.txt' script: """ - printf '${x}' | split -b 6 - chunk_ - """ -} - -process to_upper { - tag "$y" - - input: - path y - - output: - path 'upper_*' - - script: - """ - cat $y | tr '[a-z]' '[A-Z]' > upper_${y} + echo "Hello, from kubernetes" > hello.txt + hostname >> hello.txt """ } workflow { - main: - ch_str = channel.of(params.str) - ch_chunks = split(ch_str) - ch_upper = to_upper(ch_chunks.flatten()) - - publish: - lower = ch_chunks.flatten() - upper = ch_upper -} - -output { - lower { - path 'lower' - } - - upper { - path 'upper' - } + hello() } diff --git a/test/nextflow.config b/test/nextflow.config new file mode 100644 index 0000000..32813ef --- /dev/null +++ b/test/nextflow.config @@ -0,0 +1,35 @@ +plugins { + id 'nf-k8s' +} + +workDir = '/workspace' + +process { + executor = 'k8s' + container = 'ubuntu:22.04' + + pod = [ + imagePullPolicy: 'IfNotPresent' + ] +} + +k8s { + namespace = 'default' + + storageClaimName = 'my-claim' + storageMountPath = '/workspace' + launchDir = '/workspace/launch' + projectDir = '/workspace/projects' + cleanup = false + + nextflowImage = 'gitea.kleine.eulenhexe.de/kevin/ma/nextflow-dvfs:0.1' + imagePullPolicy = 'IfNotPresent' + + nodeInit { + enabled = false + image = 'gitea.kleine.eulenhexe.de/kevin/ma/dvfs-agent:0.1' + command = ['/bin/agent'] + wait = 'Running' + cleanup = false + } +}