diff --git a/README.md b/README.md index 034aacc8..b06b9d4a 100644 --- a/README.md +++ b/README.md @@ -16,16 +16,16 @@ DH Core offers an API interface that give us the possibility to formalize the en 3. **Tasks (TASK)** are the logical representations of an action which is performed via a given function, by a runtime, within a framework. As such, they define both the actual scope of the execution and its context, in terms of environment, dependencies, resources etc. 4. **Runs (RUN)** are the representation of the execution of a given task with the given function, on (a set of) inputs to deliver (a set of) outputs. At a high level, they can be seen both as a summary of the union between a function and a task in a single instance, and as the representation of the actual execution phase, with parameters, status, results etc. -5. Dataitems -6. Artifacts -7. Models -8. Workflows +5. **Dataitems** +6. **Artifacts** +7. **Models** +8. **Workflows** The project is essentially divided into the following parts: -1. Core -2. Modules -3. Frontend +1. **Core** +2. **Modules** +3. **Frontend** Core manages all the basic elements that are the foundation of the project. Modules include: @@ -46,7 +46,7 @@ Modules include: ## Configuration -You can locate the project configuration file in the **application [core]** module, named **_resources/application.yml_**. This file specifies configurations for various sections. +You can locate the project configuration file in the **application [core]** module, named **_resources/application.yml_**. This file specifies configurations for various sections. Each section allows configurations to be written directly in the YAML file or through environment variables (ENV). @@ -231,9 +231,9 @@ To start the database for Core using Docker, execute the following command: ```bash docker run -d --name -p 5434:5432 -e POSTGRES_PASSWORD= postgres ``` -Once the database container is started, you can launch your application. +Once the database container is started, you can launch your application. -It's important to configure the database parameters either via ENV variables or directly in the application.yml file. You can find those settings in the SpringBoot configuration section +It's important to configure the database parameters either via ENV variables or directly in the application.yml file. You can find those settings in the SpringBoot configuration section **IMPORTANT:** If no database is created locally or through Docker, an H2 database will be automatically created when the application starts. @@ -243,12 +243,12 @@ To run core there are different possibilities: 1. You can download directly the image from GitHub and run it ```bash docker pull ghcr.io/scc-digitalhub/digitalhub-core: -docker run digitalhub-core: +docker run ghcr.io/scc-digitalhub/digitalhub-core/digitalhub-core: ``` 2. You can build your own image and run it ```bash -docker build -t ghcr.io/scc-digitalhub/digitalhub-core: . +docker build -t digitalhub-core: . docker run digitalhub-core: ``` @@ -264,8 +264,8 @@ git sumodule update # Run the project mvn spring-boot:run -pl application ``` -In case of problem once you have updated all submodules you can clean and install all dependencies -running the following command +In case of problem once you have updated all submodules you can clean and install all dependencies +running the following command ```bash mvn clean install -DskipTests ``` diff --git a/application/src/main/resources/application.yml b/application/src/main/resources/application.yml index 55af327f..1be8ab7d 100644 --- a/application/src/main/resources/application.yml +++ b/application/src/main/resources/application.yml @@ -67,7 +67,10 @@ runtime: kfp: image: ${RUNTIME_KFP_IMAGE:ghcr.io/scc-digitalhub/digitalhub-core-wrapper-kfp:latest} python: - image: ${RUNTIME_PYTHON_IMAGE:ghcr.io/scc-digitalhub/digitalhub-serverless/python-runtime-3.9:latest} + images: + python-3.9: ${RUNTIME_PYTHON_IMAGE_3_9:ghcr.io/scc-digitalhub/digitalhub-serverless/python-runtime-3.9:latest} + python-3.10: ${RUNTIME_PYTHON_IMAGE_3_10:ghcr.io/scc-digitalhub/digitalhub-serverless/python-runtime-3.10:latest} + command: /usr/local/bin/processor diff --git a/modules/runtime-container/src/main/java/it/smartcommunitylabdhub/runtime/container/runners/ContainerBuildRunner.java b/modules/runtime-container/src/main/java/it/smartcommunitylabdhub/runtime/container/runners/ContainerBuildRunner.java index 56e078cd..d8de0520 100644 --- a/modules/runtime-container/src/main/java/it/smartcommunitylabdhub/runtime/container/runners/ContainerBuildRunner.java +++ b/modules/runtime-container/src/main/java/it/smartcommunitylabdhub/runtime/container/runners/ContainerBuildRunner.java @@ -50,7 +50,6 @@ public ContainerBuildRunner(FunctionContainerSpec functionContainerSpec, Map coreEnvList = new ArrayList<>( List.of(new CoreEnv("PROJECT_NAME", run.getProject()), new CoreEnv("RUN_ID", run.getId())) @@ -110,7 +109,7 @@ public K8sKanikoRunnable produce(Run run) { // Task specific .dockerFile(dockerfile) // specific - .backoffLimit(1) + .backoffLimit(0) .build(); } } diff --git a/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/PythonRuntime.java b/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/PythonRuntime.java index 8305ea58..b08c6f28 100644 --- a/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/PythonRuntime.java +++ b/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/PythonRuntime.java @@ -28,12 +28,17 @@ import it.smartcommunitylabdhub.framework.k8s.runnables.K8sJobRunnable; import it.smartcommunitylabdhub.framework.k8s.runnables.K8sRunnable; import it.smartcommunitylabdhub.framework.k8s.runnables.K8sServeRunnable; +import it.smartcommunitylabdhub.framework.kaniko.runnables.K8sKanikoRunnable; +import it.smartcommunitylabdhub.runtime.python.builders.PythonBuildBuilder; import it.smartcommunitylabdhub.runtime.python.builders.PythonJobBuilder; import it.smartcommunitylabdhub.runtime.python.builders.PythonServeBuilder; +import it.smartcommunitylabdhub.runtime.python.configs.PythonImage; +import it.smartcommunitylabdhub.runtime.python.runners.PythonBuildRunner; import it.smartcommunitylabdhub.runtime.python.runners.PythonJobRunner; import it.smartcommunitylabdhub.runtime.python.runners.PythonServeRunner; import it.smartcommunitylabdhub.runtime.python.specs.function.PythonFunctionSpec; import it.smartcommunitylabdhub.runtime.python.specs.run.PythonRunSpec; +import it.smartcommunitylabdhub.runtime.python.specs.task.PythonBuildTaskSpec; import it.smartcommunitylabdhub.runtime.python.specs.task.PythonJobTaskSpec; import it.smartcommunitylabdhub.runtime.python.specs.task.PythonServeTaskSpec; import it.smartcommunitylabdhub.runtime.python.status.PythonRunStatus; @@ -63,6 +68,7 @@ public class PythonRuntime implements Runtime serveRunnableStore; + @Autowired(required = false) + private RunnableStore buildRunnableStore; + @Autowired private FunctionService functionService; @Autowired private LogService logService; - @Value("${runtime.python.image}") - private String image; + @Autowired + PythonImage images; @Value("${runtime.python.command}") private String command; @@ -109,6 +118,10 @@ public PythonRunSpec build(@NotNull Executable function, @NotNull Task task, @No PythonServeTaskSpec taskServeSpec = new PythonServeTaskSpec(task.getSpec()); return serveBuilder.build(funSpec, taskServeSpec, runSpec); } + case PythonBuildTaskSpec.KIND -> { + PythonBuildTaskSpec taskBuildSpec = new PythonBuildTaskSpec(task.getSpec()); + return buildBuilder.build(funSpec, taskBuildSpec, runSpec); + } default -> throw new IllegalArgumentException( "Kind not recognized. Cannot retrieve the right builder or specialize Spec for Run and Task." ); @@ -131,19 +144,26 @@ public RunRunnable run(@NotNull Run run) { return switch (runAccessor.getTask()) { case PythonJobTaskSpec.KIND -> new PythonJobRunner( - image, + images.getImage("python-" + runPythonSpec.getFunctionSpec().getPythonVersion().getVersion()), command, runPythonSpec.getFunctionSpec(), secretService.groupSecrets(run.getProject(), runPythonSpec.getTaskJobSpec().getSecrets()) ) .produce(run); case PythonServeTaskSpec.KIND -> new PythonServeRunner( - image, + images.getImage("python-" + runPythonSpec.getFunctionSpec().getPythonVersion().getVersion()), command, runPythonSpec.getFunctionSpec(), secretService.groupSecrets(run.getProject(), runPythonSpec.getTaskJobSpec().getSecrets()) ) .produce(run); + case PythonBuildTaskSpec.KIND -> new PythonBuildRunner( + images.getImage("python-" + runPythonSpec.getFunctionSpec().getPythonVersion().getVersion()), + command, + runPythonSpec.getFunctionSpec(), + secretService.groupSecrets(run.getProject(), runPythonSpec.getTaskBuildSpec().getSecrets()) + ) + .produce(run); default -> throw new IllegalArgumentException("Kind not recognized. Cannot retrieve the right Runner"); }; } @@ -186,6 +206,17 @@ public RunRunnable stop(Run run) throws NoSuchEntityException { } throw new CoreRuntimeException("Store is not available"); } + case PythonBuildTaskSpec.KIND -> { + if (jobRunnableStore != null) { + K8sKanikoRunnable k8sKanikoRunnable = buildRunnableStore.find(run.getId()); + if (k8sKanikoRunnable == null) { + throw new NoSuchEntityException("JobDeployment not found"); + } + k8sKanikoRunnable.setState(State.STOP.name()); + yield k8sKanikoRunnable; + } + throw new CoreRuntimeException("Build Store is not available"); + } default -> throw new IllegalArgumentException("Kind not recognized. Cannot retrieve the right Runner"); }; } catch (StoreException e) { @@ -222,6 +253,18 @@ public RunRunnable delete(Run run) throws NoSuchEntityException { } throw new CoreRuntimeException("Job Store is not available"); } + case PythonBuildTaskSpec.KIND -> { + if (jobRunnableStore != null) { + K8sKanikoRunnable k8sKanikoRunnable = buildRunnableStore.find(run.getId()); + if (k8sKanikoRunnable == null) { + //not in store, either not existent or already removed, nothing to do + yield null; + } + k8sKanikoRunnable.setState(State.DELETING.name()); + yield k8sKanikoRunnable; + } + throw new CoreRuntimeException("Build Store is not available"); + } case PythonServeTaskSpec.KIND -> { if (jobRunnableStore != null) { K8sServeRunnable k8sServeRunnable = serveRunnableStore.find(run.getId()); diff --git a/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/builders/PythonBuildBuilder.java b/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/builders/PythonBuildBuilder.java new file mode 100644 index 00000000..3c496188 --- /dev/null +++ b/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/builders/PythonBuildBuilder.java @@ -0,0 +1,26 @@ +package it.smartcommunitylabdhub.runtime.python.builders; + +import it.smartcommunitylabdhub.commons.infrastructure.Builder; +import it.smartcommunitylabdhub.runtime.python.specs.function.PythonFunctionSpec; +import it.smartcommunitylabdhub.runtime.python.specs.run.PythonRunSpec; +import it.smartcommunitylabdhub.runtime.python.specs.task.PythonBuildTaskSpec; +import it.smartcommunitylabdhub.runtime.python.specs.task.PythonJobTaskSpec; + +import java.util.Optional; + +public class PythonBuildBuilder implements Builder { + + @Override + public PythonRunSpec build(PythonFunctionSpec funSpec, PythonBuildTaskSpec taskSpec, PythonRunSpec runSpec) { + PythonRunSpec pythonSpec = new PythonRunSpec(runSpec.toMap()); + pythonSpec.setTaskBuildSpec(taskSpec); + pythonSpec.setFunctionSpec(funSpec); + + //let run override k8s specs + Optional + .ofNullable(runSpec.getTaskJobSpec()) + .ifPresent(k8sSpec -> pythonSpec.getTaskJobSpec().configure(k8sSpec.toMap())); + + return pythonSpec; + } +} diff --git a/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/configs/PythonImage.java b/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/configs/PythonImage.java new file mode 100644 index 00000000..8f432c6e --- /dev/null +++ b/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/configs/PythonImage.java @@ -0,0 +1,27 @@ +package it.smartcommunitylabdhub.runtime.python.configs; + + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.Map; + + +@Component +@ConfigurationProperties(prefix = "runtime.python") +public class PythonImage { + + private Map images; + + public void setImages(Map images) { + this.images = images; + } + + public Map getImages() { + return images; + } + + public String getImage(String version) { + return images.get(version); + } +} diff --git a/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/model/PythonVersion.java b/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/model/PythonVersion.java new file mode 100644 index 00000000..bf43d2ce --- /dev/null +++ b/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/model/PythonVersion.java @@ -0,0 +1,23 @@ +package it.smartcommunitylabdhub.runtime.python.model; + + + +public enum PythonVersion { + PYTHON_3_9("3.9"), + PYTHON_3_10("3.10"), + PYTHON_3_11("3.11"), + PYTHON_3_12("3.12"), + PYTHON_3_13("3.13"); + + private final String version; + + PythonVersion(String version) { + this.version = version; + } + + + public String getVersion() { + return version; + } + +} diff --git a/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/runners/PythonBuildRunner.java b/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/runners/PythonBuildRunner.java new file mode 100644 index 00000000..1065eda2 --- /dev/null +++ b/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/runners/PythonBuildRunner.java @@ -0,0 +1,192 @@ +package it.smartcommunitylabdhub.runtime.python.runners; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import it.smartcommunitylabdhub.commons.infrastructure.Runner; +import it.smartcommunitylabdhub.commons.jackson.JacksonMapper; +import it.smartcommunitylabdhub.commons.models.entities.run.Run; +import it.smartcommunitylabdhub.commons.models.enums.State; +import it.smartcommunitylabdhub.commons.models.objects.SourceCode; +import it.smartcommunitylabdhub.framework.k8s.model.ContextRef; +import it.smartcommunitylabdhub.framework.k8s.model.ContextSource; +import it.smartcommunitylabdhub.framework.k8s.objects.CoreEnv; +import it.smartcommunitylabdhub.framework.k8s.runnables.K8sJobRunnable; +import it.smartcommunitylabdhub.framework.k8s.runnables.K8sRunnable; +import it.smartcommunitylabdhub.framework.kaniko.infrastructure.docker.DockerfileGenerator; +import it.smartcommunitylabdhub.framework.kaniko.infrastructure.docker.DockerfileGeneratorFactory; +import it.smartcommunitylabdhub.framework.kaniko.runnables.K8sKanikoRunnable; +import it.smartcommunitylabdhub.runtime.python.PythonRuntime; +import it.smartcommunitylabdhub.runtime.python.model.NuclioFunctionBuilder; +import it.smartcommunitylabdhub.runtime.python.model.NuclioFunctionSpec; +import it.smartcommunitylabdhub.runtime.python.specs.function.PythonFunctionSpec; +import it.smartcommunitylabdhub.runtime.python.specs.function.PythonFunctionSpec.PythonSourceCodeLanguages; +import it.smartcommunitylabdhub.runtime.python.specs.run.PythonRunSpec; +import it.smartcommunitylabdhub.runtime.python.specs.task.PythonBuildTaskSpec; +import it.smartcommunitylabdhub.runtime.python.specs.task.PythonJobTaskSpec; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.StringUtils; +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; + +import java.io.Serializable; +import java.nio.charset.StandardCharsets; +import java.util.*; + +@Slf4j +public class PythonBuildRunner implements Runner { + + private static final String RUN_DEBUG = + "PWD=`pwd`;echo \"DEBUG: dir ${PWD}\";LS=`ls -R`;echo \"DEBUG: dir content:\" && echo \"${LS}\";"; + + private static final String TASK = "job"; + + private static ObjectMapper jsonMapper = JacksonMapper.CUSTOM_OBJECT_MAPPER; + + private final String image; + private final String command; + private final PythonFunctionSpec functionSpec; + private final Map> groupedSecrets; + + public PythonBuildRunner( + String image, + String command, + PythonFunctionSpec functionPythonSpec, + Map> groupedSecrets + ) { + this.image = image; + this.command = command; + this.functionSpec = functionPythonSpec; + this.groupedSecrets = groupedSecrets; + } + + @Override + public K8sKanikoRunnable produce(Run run) { + PythonRunSpec runSpec = new PythonRunSpec(run.getSpec()); + PythonBuildTaskSpec taskSpec = runSpec.getTaskBuildSpec(); + + try { + List coreEnvList = new ArrayList<>( + List.of(new CoreEnv("PROJECT_NAME", run.getProject()), new CoreEnv("RUN_ID", run.getId())) + ); + + Optional.ofNullable(taskSpec.getEnvs()).ifPresent(coreEnvList::addAll); + + // Generate docker file + DockerfileGeneratorFactory dockerfileGenerator = DockerfileGenerator.factory(); + + + if (!StringUtils.hasText(functionSpec.getBaseImage())) { + throw new IllegalArgumentException("invalid or missing baseImage"); + } + + dockerfileGenerator.from(functionSpec.getBaseImage()); + dockerfileGenerator.copy(".", "./build"); + dockerfileGenerator.workdir("/build"); + if (log.isDebugEnabled()) { + //add debug instructions to docker file + dockerfileGenerator.run(RUN_DEBUG); + } + + //build nuclio definition + HashMap event = new HashMap<>(); + + // Add Instructions to docker file + Optional + .ofNullable(taskSpec.getInstructions()) + .ifPresent(instructions -> instructions.forEach(i -> dockerfileGenerator.run(i))); + + dockerfileGenerator.entrypoint(List.of(command)); + // Generate string docker file + String dockerfile = dockerfileGenerator.build().generate(); + + + event.put("body", jsonMapper.writeValueAsString(run)); + + NuclioFunctionSpec nuclio = NuclioFunctionSpec + .builder() + .runtime("python") + //invoke user code wrapped via default handler + .handler("run_handler:handler") + //directly invoke user code + // .handler("main:" + runSpec.getFunctionSpec().getSource().getHandler()) + .event(event) + .build(); + + String nuclioFunction = NuclioFunctionBuilder.write(nuclio); + + //read source and build context + List contextRefs = null; + List contextSources = new ArrayList<>(); + ContextSource fn = ContextSource + .builder() + .name("function.yaml") + .base64(Base64.getUrlEncoder().encodeToString(nuclioFunction.getBytes(StandardCharsets.UTF_8))) + .build(); + contextSources.add(fn); + + if (functionSpec.getSource() != null) { + SourceCode source = functionSpec.getSource(); + String path = "main.py"; + + if (StringUtils.hasText(source.getSource())) { + try { + //evaluate if local path (no scheme) + UriComponents uri = UriComponentsBuilder.fromUriString(source.getSource()).build(); + String scheme = uri.getScheme(); + + if (scheme != null) { + //write as ref + contextRefs = Collections.singletonList(ContextRef.from(source.getSource())); + } else { + if (StringUtils.hasText(path)) { + //override path for local src + path = uri.getPath(); + if (path.startsWith(".")) { + path = path.substring(1); + } + } + } + } catch (IllegalArgumentException e) { + //skip invalid source + } + } + + if (StringUtils.hasText(source.getBase64())) { + contextSources.add(ContextSource.builder().name(path).base64(source.getBase64()).build()); + } + } + + //merge env with PYTHON path override + coreEnvList.add(new CoreEnv("PYTHONPATH", "${PYTHONPATH}:/shared/")); + + return K8sKanikoRunnable + .builder() + .id(run.getId()) + .project(run.getProject()) + .runtime(PythonRuntime.RUNTIME) + .task(PythonBuildTaskSpec.KIND) + .state(State.READY.name()) + //base + .image(StringUtils.hasText(functionSpec.getImage()) ? functionSpec.getImage() : image) + + .contextRefs(contextRefs) + .contextSources(contextSources) + .envs(coreEnvList) + .secrets(groupedSecrets) + .resources(taskSpec.getResources()) + .volumes(taskSpec.getVolumes()) + .nodeSelector(taskSpec.getNodeSelector()) + .affinity(taskSpec.getAffinity()) + .tolerations(taskSpec.getTolerations()) + + // Task Specific + .dockerFile(dockerfile) + //specific + .backoffLimit(0) + .build(); + + } catch (JsonProcessingException e) { + throw new IllegalArgumentException(e.getMessage()); + } + } +} diff --git a/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/runners/PythonJobRunner.java b/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/runners/PythonJobRunner.java index 5cc5f65c..91cee589 100644 --- a/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/runners/PythonJobRunner.java +++ b/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/runners/PythonJobRunner.java @@ -134,7 +134,7 @@ public K8sRunnable produce(Run run) { .task(PythonJobTaskSpec.KIND) .state(State.READY.name()) //base - .image(StringUtils.hasText(functionSpec.getImage()) ? functionSpec.getImage() : image) + .image(StringUtils.hasText(functionSpec.getImage()) ? functionSpec.getImage() : image) // TODO check that functionSpec.getImage() has our prefix otherwise ERROR .command(command) // .args(new String[] { run.getProject(), run.getId() }) .args(new String[] { "--config", "/shared/function.yaml" }) diff --git a/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/specs/function/PythonFunctionSpec.java b/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/specs/function/PythonFunctionSpec.java index a541da2a..5e0d4a78 100644 --- a/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/specs/function/PythonFunctionSpec.java +++ b/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/specs/function/PythonFunctionSpec.java @@ -7,6 +7,7 @@ import it.smartcommunitylabdhub.commons.models.enums.EntityName; import it.smartcommunitylabdhub.commons.models.objects.SourceCode; import it.smartcommunitylabdhub.runtime.python.PythonRuntime; +import it.smartcommunitylabdhub.runtime.python.model.PythonVersion; import jakarta.validation.constraints.NotNull; import java.io.Serializable; import java.util.List; @@ -34,6 +35,10 @@ public class PythonFunctionSpec extends FunctionBaseSpec { @Schema(title = "fields.container.baseImage.title", description = "fields.container.baseImage.description") private String baseImage; + @JsonProperty("python_version") + @Schema(title = "fields.python.version.title", description = "fields.python.version.description") + private PythonVersion pythonVersion; + @Schema(title = "fields.python.requirements.title", description = "fields.python.requirements.description") private List requirements; @@ -50,6 +55,7 @@ public void configure(Map data) { this.source = spec.getSource(); this.image = spec.getImage(); this.baseImage = spec.getBaseImage(); + this.pythonVersion = spec.getPythonVersion(); this.requirements = spec.getRequirements(); } diff --git a/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/specs/run/PythonRunSpec.java b/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/specs/run/PythonRunSpec.java index 83ccdec2..9b6a62dd 100644 --- a/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/specs/run/PythonRunSpec.java +++ b/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/specs/run/PythonRunSpec.java @@ -6,6 +6,7 @@ import it.smartcommunitylabdhub.commons.models.enums.EntityName; import it.smartcommunitylabdhub.runtime.python.PythonRuntime; import it.smartcommunitylabdhub.runtime.python.specs.function.PythonFunctionSpec; +import it.smartcommunitylabdhub.runtime.python.specs.task.PythonBuildTaskSpec; import it.smartcommunitylabdhub.runtime.python.specs.task.PythonJobTaskSpec; import it.smartcommunitylabdhub.runtime.python.specs.task.PythonServeTaskSpec; import java.io.Serializable; @@ -29,6 +30,9 @@ public class PythonRunSpec extends RunBaseSpec { @JsonUnwrapped private PythonServeTaskSpec taskServeSpec; + @JsonUnwrapped + private PythonBuildTaskSpec taskBuildSpec; + @JsonUnwrapped private PythonFunctionSpec functionSpec; @@ -51,6 +55,7 @@ public void configure(Map data) { this.taskJobSpec = spec.getTaskJobSpec(); this.taskServeSpec = spec.getTaskServeSpec(); this.functionSpec = spec.getFunctionSpec(); + this.taskBuildSpec = spec.getTaskBuildSpec(); this.inputs = spec.getInputs(); this.outputs = spec.getOutputs(); @@ -65,6 +70,10 @@ public void setTaskServeSpec(PythonServeTaskSpec taskServeSpec) { this.taskServeSpec = taskServeSpec; } + public void setTaskBuildSpec(PythonBuildTaskSpec buildTaskSpec) { + this.taskBuildSpec = buildTaskSpec; + } + public void setFunctionSpec(PythonFunctionSpec functionSpec) { this.functionSpec = functionSpec; } diff --git a/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/specs/task/PythonBuildTaskSpec.java b/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/specs/task/PythonBuildTaskSpec.java new file mode 100644 index 00000000..475097a8 --- /dev/null +++ b/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/specs/task/PythonBuildTaskSpec.java @@ -0,0 +1,38 @@ +package it.smartcommunitylabdhub.runtime.python.specs.task; + +import io.swagger.v3.oas.annotations.media.Schema; +import it.smartcommunitylabdhub.commons.annotations.common.SpecType; +import it.smartcommunitylabdhub.commons.models.enums.EntityName; +import it.smartcommunitylabdhub.framework.k8s.base.K8sTaskBaseSpec; +import it.smartcommunitylabdhub.runtime.python.PythonRuntime; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +@Getter +@Setter +@NoArgsConstructor +@SpecType(runtime = PythonRuntime.RUNTIME, kind = PythonBuildTaskSpec.KIND, entity = EntityName.TASK) +public class PythonBuildTaskSpec extends K8sTaskBaseSpec { + + public static final String KIND = "python+build"; + + @Schema(title = "fields.container.instructions.title", description = "fields.container.instructions.description") + private List instructions; + + public PythonBuildTaskSpec(Map data) { + configure(data); + } + + @Override + public void configure(Map data) { + super.configure(data); + + PythonBuildTaskSpec spec = mapper.convertValue(data, PythonBuildTaskSpec.class); + this.instructions = spec.getInstructions(); + } +} diff --git a/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/specs/task/PythonBuildTaskSpecFactory.java b/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/specs/task/PythonBuildTaskSpecFactory.java new file mode 100644 index 00000000..457371f1 --- /dev/null +++ b/modules/runtime-python/src/main/java/it/smartcommunitylabdhub/runtime/python/specs/task/PythonBuildTaskSpecFactory.java @@ -0,0 +1,21 @@ +package it.smartcommunitylabdhub.runtime.python.specs.task; + +import it.smartcommunitylabdhub.commons.infrastructure.SpecFactory; +import org.springframework.stereotype.Component; + +import java.io.Serializable; +import java.util.Map; + +@Component +public class PythonBuildTaskSpecFactory implements SpecFactory { + + @Override + public PythonBuildTaskSpec create() { + return new PythonBuildTaskSpec(); + } + + @Override + public PythonBuildTaskSpec create(Map data) { + return new PythonBuildTaskSpec(data); + } +}