-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/k8s executor #87
Open
thiagoyeds
wants to merge
44
commits into
feature/remote-worker
Choose a base branch
from
feature/k8s-executor
base: feature/remote-worker
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
44 commits
Select commit
Hold shift + click to select a range
9a002c4
New K8s worker creator added
thiagoyeds b89f64f
Kubernetes Java library dependency added
thiagoyeds 7a3962a
New worker type added to handle K8s
thiagoyeds 85d7f51
Case dealing with added K8s
thiagoyeds a63d3bf
K8s constant class added
thiagoyeds aea8eea
K8s client class added
thiagoyeds 2b2625d
K8s configuration class added
thiagoyeds f1917e1
New task executor added to handle K8s
thiagoyeds d2371c5
Arrebol K8s json example added
thiagoyeds d93d673
Fixed constants value
thiagoyeds db048f3
Confusion fixed when obtaining property
thiagoyeds 2c8cdcf
New constant added to inform k8s cluster namespace to be used
thiagoyeds 944a535
Fixed code to get new k8s cluster namespace info
thiagoyeds d98f19f
Arrebol k8s json example updated
thiagoyeds 09bfcce
Class changed to K8s client interface
thiagoyeds 2fe20a8
Default K8s client classe impl added
thiagoyeds b086adf
K8s cluster resource interface added
thiagoyeds 0a49e47
Default K8s cluster resource class added
thiagoyeds 88dc8e4
Method impl of create workers added
thiagoyeds 515ce57
New constructors added in K8s task executor class
thiagoyeds 2aa9b23
List jobs method removed from K8s client interface
thiagoyeds 746ddc5
Command parameter type changed
thiagoyeds 4ea7904
Api exception added in interface methods
thiagoyeds 0352546
Worker type changed to K8sTaskExecutor
thiagoyeds c7305a6
New constant added to handle successful execution
thiagoyeds 54eb4f8
Hibernate notations removed
thiagoyeds 4b027e3
K8s task executor class impl added
thiagoyeds 82f02a1
Fixed class for changes made to the K8s client interface
thiagoyeds fd1e1aa
New constant to volume name for used in jobs (opcional)
thiagoyeds e4e6188
Changed code to check new constant of volume name (opcional)
thiagoyeds 9eb014e
New key added in k8s json example about volume name constant
thiagoyeds 342a55d
Changed code to handle with new constant to volume name (opcional)
thiagoyeds ce9978c
Changed code to handle with new volume name key
thiagoyeds 5109569
Unused imports removed
thiagoyeds 968e02f
Fixed bug to handle get queue info (classes persistence)
thiagoyeds 4d09024
BackoffLimit attribute added to the job pod
thiagoyeds b72eaaf
Fixed backoffLimit attibute value to 2
thiagoyeds 8f90457
Implemented method of create workers with worker node param
thiagoyeds 5f3f00a
TODO added in k8s worker specification about requeriments (CPU and RAM)
thiagoyeds fd932ac
Improved methods name for better
thiagoyeds 3d2100f
Improved var name for better
thiagoyeds 9b1deb0
Fixed broken indentation
thiagoyeds 1e49733
Removed unused method
thiagoyeds 4993498
Improved constants name for better
thiagoyeds File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,8 @@ | |
|
||
public enum WorkerTypes { | ||
DOCKER("docker"), | ||
RAW("raw"); | ||
RAW("raw"), | ||
K8S("k8s"); | ||
|
||
private String type; | ||
|
||
|
79 changes: 79 additions & 0 deletions
79
src/main/java/org/fogbowcloud/arrebol/execution/creator/K8sWorkerCreator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package org.fogbowcloud.arrebol.execution.creator; | ||
|
||
import java.io.IOException; | ||
import java.util.Collection; | ||
import java.util.LinkedList; | ||
import java.util.UUID; | ||
|
||
import org.apache.log4j.Logger; | ||
import org.fogbowcloud.arrebol.execution.Worker; | ||
import org.fogbowcloud.arrebol.execution.k8s.K8sConfiguration; | ||
import org.fogbowcloud.arrebol.execution.k8s.K8sTaskExecutor; | ||
import org.fogbowcloud.arrebol.execution.k8s.client.DefaultK8sClient; | ||
import org.fogbowcloud.arrebol.execution.k8s.client.K8sClient; | ||
import org.fogbowcloud.arrebol.execution.k8s.resource.DefaultK8sClusterResource; | ||
import org.fogbowcloud.arrebol.execution.k8s.resource.K8sClusterResource; | ||
import org.fogbowcloud.arrebol.models.configuration.Configuration; | ||
import org.fogbowcloud.arrebol.models.specification.Specification; | ||
import org.fogbowcloud.arrebol.processor.spec.WorkerNode; | ||
import org.fogbowcloud.arrebol.resource.MatchAnyWorker; | ||
import org.fogbowcloud.arrebol.utils.AppUtil; | ||
|
||
public class K8sWorkerCreator implements WorkerCreator { | ||
|
||
private final K8sConfiguration configuration; | ||
private final Logger LOGGER = Logger.getLogger(K8sWorkerCreator.class); | ||
|
||
public K8sWorkerCreator(Configuration configuration) throws Exception { | ||
this.configuration = new K8sConfiguration(configuration); | ||
} | ||
|
||
@Override | ||
public Collection<Worker> createWorkers(Integer poolId) { | ||
Collection<Worker> workers = new LinkedList<>(); | ||
String address = configuration.getAddress(); | ||
int poolSize = configuration.getCapacity(); | ||
String namespace = configuration.getNamespace(); | ||
String volumeName = configuration.getVolumeName(); | ||
for (int i = 0; i < poolSize; i++) { | ||
LOGGER.info("Creating k8s worker with address=" + address); | ||
Worker worker = createK8sWorker(poolId, address, namespace, volumeName); | ||
workers.add(worker); | ||
} | ||
|
||
return workers; | ||
} | ||
|
||
@Override | ||
public Collection<Worker> createWorkers(Integer poolId, WorkerNode workerNode) { | ||
Collection<Worker> workers = new LinkedList<>(); | ||
String namespace = configuration.getNamespace(); | ||
String volumeName = configuration.getVolumeName(); | ||
for(int i = 0; i < workerNode.getPoolSize(); i++){ | ||
LOGGER.info("Creating k8s worker with address=" + workerNode.getAddress()); | ||
Worker worker = createK8sWorker(poolId, workerNode.getAddress(), namespace, volumeName); | ||
workers.add(worker); | ||
} | ||
return workers; | ||
} | ||
|
||
private Worker createK8sWorker(Integer poolId, String address, String namespace, String volumeName) { | ||
String id = "k8s-executor-" + UUID.randomUUID().toString(); | ||
K8sClusterResource k8sClusterResource = new DefaultK8sClusterResource(id, address); | ||
//TODO Add some specification here, something about the CPU and RAM requirements maybe | ||
Specification resourceSpec = null; | ||
|
||
K8sClient k8sClient = null; | ||
try { | ||
k8sClient = new DefaultK8sClient(address, namespace, volumeName); | ||
} catch (IOException e) { | ||
LOGGER.error("Error while create k8s client in worker [" + id + "]", e); | ||
} | ||
|
||
K8sTaskExecutor k8sTaskExecutor = new K8sTaskExecutor(k8sClusterResource, k8sClient); | ||
Worker worker = new MatchAnyWorker(AppUtil.generateUniqueStringId(), resourceSpec, poolId, k8sTaskExecutor); | ||
LOGGER.info("Created K8s Worker [" + worker.getId() + "]"); | ||
return worker; | ||
} | ||
|
||
} |
60 changes: 60 additions & 0 deletions
60
src/main/java/org/fogbowcloud/arrebol/execution/k8s/K8sConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package org.fogbowcloud.arrebol.execution.k8s; | ||
|
||
import org.fogbowcloud.arrebol.execution.k8s.constants.K8sConstants; | ||
import org.fogbowcloud.arrebol.models.configuration.Configuration; | ||
import org.fogbowcloud.arrebol.models.configuration.Property; | ||
|
||
public class K8sConfiguration { | ||
private final Integer capacity; | ||
private final String address; | ||
private final String namespace; | ||
private final String volumeName; | ||
|
||
public K8sConfiguration(Configuration configuration) throws Exception { | ||
checkK8sConfigurationProperties(configuration); | ||
Property<Double> capacity = configuration.getProperty(K8sConstants.K8S_CLUSTER_CAPACITY); | ||
Property<String> address = configuration.getProperty(K8sConstants.K8S_CLUSTER_ADDRESS); | ||
Property<String> namespace = configuration.getProperty(K8sConstants.K8S_CLUSTER_NAMESPACE); | ||
Property<String> volumeName = configuration.getProperty(K8sConstants.K8S_CLUSTER_VOLUME_NAME); | ||
this.capacity = capacity.getValue().intValue(); | ||
this.address = address.getValue(); | ||
this.namespace = namespace.getValue(); | ||
this.volumeName = volumeName.getValue(); | ||
} | ||
|
||
private void checkK8sConfigurationProperties(Configuration configuration) throws Exception { | ||
String verifyMsg = " Please, verify your configuration file."; | ||
String capacityMsg = "K8s cluster capacity configuration property wrong or missing." + verifyMsg; | ||
String addressMsg = "K8s cluster address configuration property wrong or missing." + verifyMsg; | ||
String namespaceMsg = "K8s cluster namespace configuration property wrong or missing." + verifyMsg; | ||
|
||
Property<Double> capacity = configuration.getProperty(K8sConstants.K8S_CLUSTER_CAPACITY); | ||
Property<String> address = configuration.getProperty(K8sConstants.K8S_CLUSTER_ADDRESS); | ||
Property<String> namespace = configuration.getProperty(K8sConstants.K8S_CLUSTER_NAMESPACE); | ||
|
||
if (address.getValue() == null || address.getValue().trim().isEmpty()) { | ||
throw new Exception(addressMsg); | ||
} else if (capacity.getValue() == null || capacity.getValue().intValue() == 0) { | ||
throw new Exception(capacityMsg); | ||
} else if (namespace.getValue() == null || namespace.getValue().trim().isEmpty()) { | ||
throw new Exception(namespaceMsg); | ||
} | ||
} | ||
|
||
public Integer getCapacity() { | ||
return capacity; | ||
} | ||
|
||
public String getAddress() { | ||
return address; | ||
} | ||
|
||
public String getNamespace() { | ||
return namespace; | ||
} | ||
|
||
public String getVolumeName() { | ||
return volumeName; | ||
} | ||
|
||
} |
166 changes: 166 additions & 0 deletions
166
src/main/java/org/fogbowcloud/arrebol/execution/k8s/K8sTaskExecutor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
package org.fogbowcloud.arrebol.execution.k8s; | ||
|
||
import static java.lang.Thread.sleep; | ||
import static org.fogbowcloud.arrebol.execution.docker.constants.DockerConstants.ADDRESS_METADATA_KEY; | ||
|
||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
|
||
import javax.persistence.CascadeType; | ||
import javax.persistence.Entity; | ||
import javax.persistence.GeneratedValue; | ||
import javax.persistence.GenerationType; | ||
import javax.persistence.Id; | ||
import javax.persistence.OneToOne; | ||
import javax.persistence.Transient; | ||
|
||
import org.apache.log4j.Logger; | ||
import org.fogbowcloud.arrebol.execution.TaskExecutionResult; | ||
import org.fogbowcloud.arrebol.execution.TaskExecutor; | ||
import org.fogbowcloud.arrebol.execution.TaskExecutionResult.RESULT; | ||
import org.fogbowcloud.arrebol.execution.k8s.client.DefaultK8sClient; | ||
import org.fogbowcloud.arrebol.execution.k8s.client.K8sClient; | ||
import org.fogbowcloud.arrebol.execution.k8s.resource.DefaultK8sClusterResource; | ||
import org.fogbowcloud.arrebol.execution.k8s.resource.K8sClusterResource; | ||
import org.fogbowcloud.arrebol.models.command.Command; | ||
import org.fogbowcloud.arrebol.models.command.CommandState; | ||
import org.fogbowcloud.arrebol.models.task.RequirementsContants; | ||
import org.fogbowcloud.arrebol.models.task.Task; | ||
|
||
import io.kubernetes.client.openapi.ApiException; | ||
import io.kubernetes.client.openapi.models.V1Job; | ||
|
||
@Entity | ||
public class K8sTaskExecutor implements TaskExecutor { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Integer id; | ||
@Transient | ||
private final Logger LOGGER = Logger.getLogger(K8sTaskExecutor.class); | ||
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, targetEntity = DefaultK8sClusterResource.class) | ||
private K8sClusterResource k8sClusterResource; | ||
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, targetEntity = DefaultK8sClient.class) | ||
private K8sClient k8sClient; | ||
@Transient | ||
private static final long POOLING_PERIOD_TIME_MS = 5 * 1000; | ||
|
||
public K8sTaskExecutor(K8sClusterResource k8sClusterResource, K8sClient k8sClient) { | ||
this.k8sClusterResource = k8sClusterResource; | ||
this.k8sClient = k8sClient; | ||
} | ||
|
||
public K8sTaskExecutor() { | ||
} | ||
|
||
@Override | ||
public TaskExecutionResult execute(Task task) { | ||
TaskExecutionResult taskExecutionResult = null; | ||
|
||
String jobName = task.getId(); | ||
|
||
List<Command> commands = task.getTaskSpec().getCommands(); | ||
String command = joinCommands(commands); | ||
|
||
Map<String, String> requirements = task.getTaskSpec().getRequirements(); | ||
String imageId = getImageId(requirements); | ||
|
||
LOGGER.debug("Image id: " + imageId); | ||
LOGGER.debug("Command: " + command); | ||
|
||
int tasksListSize = task.getTaskSpec().getCommands().size(); | ||
|
||
try { | ||
V1Job job = k8sClient.createJob(jobName, imageId, command); | ||
boolean jobIsRunning = true; | ||
|
||
while (jobIsRunning) { | ||
try { | ||
sleep(POOLING_PERIOD_TIME_MS); | ||
} catch (InterruptedException e) { | ||
LOGGER.error(e.getMessage(), e); | ||
} | ||
|
||
job = k8sClient.getJob(jobName); | ||
jobIsRunning = job.getStatus().getActive() != null; | ||
} | ||
|
||
if (wasSuccessful(job)) { | ||
finishSuccessfulExecution(task); | ||
taskExecutionResult = getSuccessResultInstance(tasksListSize); | ||
} else { | ||
finishFailExecution(task); | ||
taskExecutionResult = getFailResultInstance(tasksListSize); | ||
} | ||
} catch (ApiException e) { | ||
LOGGER.error("Error while call K8s client API. " + e.getMessage(), e); | ||
finishFailExecution(task); | ||
taskExecutionResult = getFailResultInstance(tasksListSize); | ||
return taskExecutionResult; | ||
} | ||
|
||
return taskExecutionResult; | ||
} | ||
|
||
@Override | ||
public Map<String, String> getMetadata() { | ||
Map<String, String> metadata = new HashMap<>(); | ||
String address = this.k8sClusterResource.getApiAddress(); | ||
metadata.put(ADDRESS_METADATA_KEY, address); | ||
return metadata; | ||
} | ||
|
||
private String joinCommands(List<Command> commands) { | ||
String result = ""; | ||
for (Command command : commands) | ||
result += command.getCommand() + " && "; | ||
result = result.substring(0, result.length() - 4); | ||
|
||
return result; | ||
} | ||
|
||
private String getImageId(Map<String, String> requirements) throws IllegalArgumentException { | ||
String imageId = null; | ||
if (Objects.nonNull(requirements)) | ||
imageId = requirements.get(RequirementsContants.IMAGE_KEY); | ||
|
||
return imageId; | ||
} | ||
|
||
private boolean wasSuccessful(V1Job job) { | ||
Integer succeededAmount = job.getStatus().getSucceeded(); | ||
return succeededAmount != null && succeededAmount > 0; | ||
} | ||
|
||
private void finishSuccessfulExecution(Task task) { | ||
for (Command c : task.getTaskSpec().getCommands()) { | ||
c.setState(CommandState.FINISHED); | ||
c.setExitcode(TaskExecutionResult.SUCCESS_RESULT); | ||
} | ||
} | ||
|
||
private void finishFailExecution(Task task) { | ||
for (Command c : task.getTaskSpec().getCommands()) { | ||
c.setState(CommandState.FAILED); | ||
c.setExitcode(TaskExecutionResult.UNDETERMINED_RESULT); | ||
} | ||
} | ||
|
||
private TaskExecutionResult getFailResultInstance(int size) { | ||
int[] exitCodes = new int[size]; | ||
Arrays.fill(exitCodes, TaskExecutionResult.UNDETERMINED_RESULT); | ||
TaskExecutionResult taskExecutionResult = new TaskExecutionResult(RESULT.FAILURE, exitCodes); | ||
return taskExecutionResult; | ||
} | ||
|
||
private TaskExecutionResult getSuccessResultInstance(int size) { | ||
int[] exitCodes = new int[size]; | ||
Arrays.fill(exitCodes, TaskExecutionResult.SUCCESS_RESULT); | ||
TaskExecutionResult taskExecutionResult = new TaskExecutionResult(RESULT.SUCCESS, exitCodes); | ||
return taskExecutionResult; | ||
} | ||
|
||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just to make a note. Maybe we want it to be filled with the requirements we talked about, right? I mean, I think that this is the place we want to specify them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we discussed before, there are two ways to specify the amount of CPU and RAM consumed by the POD container in the k8s cluster worker, the minimum (
requests
) and the maximum (limits
), I believe that here can enter the maximum information, but the minimum does not fit, as the client that details this information. I will add a TODO to add something to that specification in the future.