diff --git a/Docs/about/requests-and-deploys.md b/Docs/about/requests-and-deploys.md index 84b0cff160..b6032bfefa 100644 --- a/Docs/about/requests-and-deploys.md +++ b/Docs/about/requests-and-deploys.md @@ -36,6 +36,7 @@ Now you want `TestService` to actually run. To do this, you need to create a `De "resources": { "cpus":1, "memoryMb":128, + "diskMb": 1024, "numPorts":2 }, "command":"java -Ddw.server.applicationConnectors[0].port=$PORT0 -Ddw.server.adminConnectors[0].port=$PORT1 -jar singularitytest-1.0-SNAPSHOT.jar server example.yml", diff --git a/Docs/getting-started/install.md b/Docs/getting-started/install.md index 4149a57c7c..c7614d5c1d 100644 --- a/Docs/getting-started/install.md +++ b/Docs/getting-started/install.md @@ -66,6 +66,7 @@ mesos: master: http://[mesos master hostname]/api/v1/scheduler defaultCpus: 1 defaultMemory: 128 + defaultDisk: 1024 frameworkName: Singularity frameworkId: Singularity frameworkFailoverTimeout: 1000000 diff --git a/Docs/reference/configuration.md b/Docs/reference/configuration.md index a5915e5156..036eb4485f 100644 --- a/Docs/reference/configuration.md +++ b/Docs/reference/configuration.md @@ -176,6 +176,7 @@ These settings should live under the "mesos" field inside the root configuration |-----------|---------|-------------|------| | defaultCpus | 1 | Number of CPUs to request for a task if none are specified | int | | defaultMemory | 64 | MB of memory to request for a task if none is specified | int | +| defaultDisk | 1024 | MB of disk to request for a task if none is specified | int | | maxNumInstancesPerRequest | 25 | Max instances (tasks) to allow for a request (requests using over this will return a 400) | int | | maxNumCpusPerInstance | 50 | Max number of CPUs allowed on a given task | int | | maxNumCpusPerRequest | 900 | Max number of CPUs allowed for a given request (cpus per task * task instance) | int | diff --git a/SingularityBase/pom.xml b/SingularityBase/pom.xml index 5c8f2fa8dd..7f24886428 100644 --- a/SingularityBase/pom.xml +++ b/SingularityBase/pom.xml @@ -41,11 +41,6 @@ jackson-datatype-guava - - org.apache.mesos - mesos - - org.apache.commons commons-lang3 diff --git a/SingularityBase/src/main/java/com/hubspot/mesos/JavaUtils.java b/SingularityBase/src/main/java/com/hubspot/mesos/JavaUtils.java index c2315afad0..aae04c2aaf 100644 --- a/SingularityBase/src/main/java/com/hubspot/mesos/JavaUtils.java +++ b/SingularityBase/src/main/java/com/hubspot/mesos/JavaUtils.java @@ -202,12 +202,4 @@ public static ThreadPoolExecutor newFixedTimingOutThreadPool(int maxThreads, lon public static String getReplaceHyphensWithUnderscores(String string) { return string.replace("-", "_"); } - - public static final Optional getUserEmail(Optional user) { - if (user.isPresent()) { - return user.get().getEmail(); - } else { - return Optional.absent(); - } - } } diff --git a/SingularityBase/src/main/java/com/hubspot/mesos/SingularityVolume.java b/SingularityBase/src/main/java/com/hubspot/mesos/SingularityVolume.java index 8536577ffd..af0dce2d82 100644 --- a/SingularityBase/src/main/java/com/hubspot/mesos/SingularityVolume.java +++ b/SingularityBase/src/main/java/com/hubspot/mesos/SingularityVolume.java @@ -2,8 +2,6 @@ import java.util.Objects; -import org.apache.mesos.v1.Protos; - import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.base.Optional; @@ -23,19 +21,6 @@ public SingularityVolume( this.mode = Optional.fromNullable(mode); } - @Deprecated - public SingularityVolume(String containerPath, Optional hostPath, Optional mode) { - this(containerPath, hostPath, convertedMode(mode)); - } - - private static SingularityDockerVolumeMode convertedMode(Optional mode) { - if (mode.isPresent()) { - return SingularityDockerVolumeMode.valueOf(mode.get().toString()); - } else { - return null; - } - } - public String getContainerPath() { return containerPath; } diff --git a/SingularityBase/src/main/java/com/hubspot/mesos/json/SingularityMesosOfferObject.java b/SingularityBase/src/main/java/com/hubspot/mesos/json/SingularityMesosOfferObject.java deleted file mode 100644 index 0d8643a805..0000000000 --- a/SingularityBase/src/main/java/com/hubspot/mesos/json/SingularityMesosOfferObject.java +++ /dev/null @@ -1,168 +0,0 @@ -package com.hubspot.mesos.json; - -import java.util.Collections; -import java.util.List; - -import org.apache.mesos.v1.Protos.AgentID; -import org.apache.mesos.v1.Protos.Attribute; -import org.apache.mesos.v1.Protos.ExecutorID; -import org.apache.mesos.v1.Protos.FrameworkID; -import org.apache.mesos.v1.Protos.Offer; -import org.apache.mesos.v1.Protos.OfferID; -import org.apache.mesos.v1.Protos.Resource; -import org.apache.mesos.v1.Protos.URL; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -/* - * Mirrors the mesos Offer object, with the exception that slaveId can be read into agentId - */ -public class SingularityMesosOfferObject { - private final List attributes; - private final List executorIds; - private final URL url; - private final AgentID agentId; - private final AgentID slaveId; - private final FrameworkID frameworkId; - private final String hostname; - private final List resources; - private final OfferID id; - - public static final SingularityMesosOfferObject fromProtos(Offer offer) { - return new SingularityMesosOfferObject( - offer.getAttributesList(), - offer.getExecutorIdsList(), - offer.getUrl(), - offer.getAgentId(), - null, - offer.getFrameworkId(), - offer.getHostname(), - offer.getResourcesList(), - offer.getId() - ); - } - - @JsonCreator - public SingularityMesosOfferObject(@JsonProperty("attributes") List attributes, - @JsonProperty("executorIds") List executorIds, - @JsonProperty("url") URL url, - @JsonProperty("agentId") AgentID agentId, - @JsonProperty("slaveId") AgentID slaveId, - @JsonProperty("frameworkId") FrameworkID frameworkId, - @JsonProperty("hostname") String hostname, - @JsonProperty("resources") List resources, - @JsonProperty("id") OfferID id) { - this.attributes = attributes; - this.executorIds = executorIds; - this.url = url; - this.agentId = agentId != null ? agentId : slaveId; - this.slaveId = agentId != null ? agentId : slaveId; - this.frameworkId = frameworkId; - this.hostname = hostname; - this.resources = resources; - this.id = id; - } - - public SingularityMesosOfferObject sizeOptimized() { - return new SingularityMesosOfferObject(attributes, Collections.emptyList(), url, agentId, null, frameworkId, hostname, Collections.emptyList(), id); - } - - public List getAttributes() { - return attributes; - } - - public List getExecutorIds() { - return executorIds; - } - - public URL getUrl() { - return url; - } - - public AgentID getAgentId() { - return agentId; - } - - public AgentID getSlaveId() { - return slaveId; - } - - public FrameworkID getFrameworkId() { - return frameworkId; - } - - public String getHostname() { - return hostname; - } - - public List getResources() { - return resources; - } - - public OfferID getId() { - return id; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - SingularityMesosOfferObject that = (SingularityMesosOfferObject) o; - - if (attributes != null ? !attributes.equals(that.attributes) : that.attributes != null) { - return false; - } - if (executorIds != null ? !executorIds.equals(that.executorIds) : that.executorIds != null) { - return false; - } - if (url != null ? !url.equals(that.url) : that.url != null) { - return false; - } - if (agentId != null ? !agentId.equals(that.agentId) : that.agentId != null) { - return false; - } - if (frameworkId != null ? !frameworkId.equals(that.frameworkId) : that.frameworkId != null) { - return false; - } - if (hostname != null ? !hostname.equals(that.hostname) : that.hostname != null) { - return false; - } - if (resources != null ? !resources.equals(that.resources) : that.resources != null) { - return false; - } - return id != null ? id.equals(that.id) : that.id == null; - } - - @Override - public int hashCode() { - int result = attributes != null ? attributes.hashCode() : 0; - result = 31 * result + (executorIds != null ? executorIds.hashCode() : 0); - result = 31 * result + (url != null ? url.hashCode() : 0); - result = 31 * result + (agentId != null ? agentId.hashCode() : 0); - result = 31 * result + (frameworkId != null ? frameworkId.hashCode() : 0); - result = 31 * result + (hostname != null ? hostname.hashCode() : 0); - result = 31 * result + (resources != null ? resources.hashCode() : 0); - result = 31 * result + (id != null ? id.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "SingularityMesosOfferObject{" + - "attributes=" + attributes + - ", executorIds=" + executorIds + - ", url=" + url + - ", agentId=" + agentId + - ", frameworkId=" + frameworkId + - ", hostname='" + hostname + '\'' + - ", resources=" + resources + - ", id=" + id + - '}'; - } -} diff --git a/SingularityBase/src/main/java/com/hubspot/mesos/json/SingularityMesosTaskObject.java b/SingularityBase/src/main/java/com/hubspot/mesos/json/SingularityMesosTaskObject.java deleted file mode 100644 index cf832b7a16..0000000000 --- a/SingularityBase/src/main/java/com/hubspot/mesos/json/SingularityMesosTaskObject.java +++ /dev/null @@ -1,237 +0,0 @@ -package com.hubspot.mesos.json; - -import java.util.Collections; -import java.util.List; - -import org.apache.mesos.v1.Protos.AgentID; -import org.apache.mesos.v1.Protos.CommandInfo; -import org.apache.mesos.v1.Protos.ContainerInfo; -import org.apache.mesos.v1.Protos.DiscoveryInfo; -import org.apache.mesos.v1.Protos.ExecutorInfo; -import org.apache.mesos.v1.Protos.HealthCheck; -import org.apache.mesos.v1.Protos.KillPolicy; -import org.apache.mesos.v1.Protos.Labels; -import org.apache.mesos.v1.Protos.Resource; -import org.apache.mesos.v1.Protos.TaskID; -import org.apache.mesos.v1.Protos.TaskInfo; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.base.Optional; - -/* - * Mimics the TaskInfo object from mesos, with the addition that we can read - * AgentID from either a field named slaveId or a field named agentId for - * better backwards compatibility - */ -public class SingularityMesosTaskObject { - private final TaskID taskId; - private final Optional executor; - private final Optional labels; - private final AgentID agentId; - private final AgentID slaveId; - private final List resources; - private final Optional command; - private final Optional container; - private final Optional discovery; - private final Optional healthCheck; - private final Optional killPolicy; - private final String name; - - public static SingularityMesosTaskObject fromProtos(TaskInfo taskInfo) { - return new SingularityMesosTaskObject( - taskInfo.getTaskId(), - taskInfo.hasExecutor() ? Optional.of(taskInfo.getExecutor()) : Optional.absent(), - taskInfo.hasLabels() ? Optional.of(taskInfo.getLabels()) : Optional.absent(), - taskInfo.getAgentId(), - null, - taskInfo.getResourcesList(), - taskInfo.hasCommand() ? Optional.of(taskInfo.getCommand()) : Optional.absent(), - taskInfo.hasContainer() ? Optional.of(taskInfo.getContainer()) : Optional.absent(), - taskInfo.hasDiscovery() ? Optional.of(taskInfo.getDiscovery()) : Optional.absent(), - taskInfo.hasHealthCheck() ? Optional.of(taskInfo.getHealthCheck()) : Optional.absent(), - taskInfo.hasKillPolicy() ? Optional.of(taskInfo.getKillPolicy()) : Optional.absent(), - taskInfo.getName() - ); - } - - @JsonCreator - public SingularityMesosTaskObject(@JsonProperty("taskId") TaskID taskId, - @JsonProperty("executor") Optional executor, - @JsonProperty("labels") Optional labels, - @JsonProperty("agentId") AgentID agentId, - @JsonProperty("slaveId") AgentID slaveId, - @JsonProperty("resources") List resources, - @JsonProperty("command") Optional command, - @JsonProperty("container") Optional container, - @JsonProperty("discovery") Optional discovery, - @JsonProperty("healthCheck") Optional healthCheck, - @JsonProperty("killPolicy") Optional killPolicy, - @JsonProperty("name") String name) { - this.taskId = taskId; - this.executor = executor; - this.labels = labels; - this.agentId = agentId != null ? agentId : slaveId; - this.slaveId = agentId != null ? agentId : slaveId; - this.resources = resources != null ? resources : Collections.emptyList(); - this.command = command; - this.container = container; - this.discovery = discovery; - this.healthCheck = healthCheck; - this.killPolicy = killPolicy; - this.name = name; - } - - public TaskID getTaskId() { - return taskId; - } - - public ExecutorInfo getExecutor() { - return executor.orNull(); - } - - public boolean hasExecutor() { - return executor.isPresent(); - } - - public Labels getLabels() { - return labels.orNull(); - } - - public boolean hasLabels() { - return labels.isPresent(); - } - - public AgentID getAgentId() { - return agentId; - } - - public AgentID getSlaveId() { - return slaveId; - } - - public List getResources() { - return resources; - } - - public CommandInfo getCommand() { - return command.orNull(); - } - - public boolean hasCommand() { - return command.isPresent(); - } - - public ContainerInfo getContainer() { - return container.orNull(); - } - - public boolean hasContainer() { - return container.isPresent(); - } - - public DiscoveryInfo getDiscovery() { - return discovery.orNull(); - } - - public boolean hasDiscovery() { - return discovery.isPresent(); - } - - public HealthCheck getHealthCheck() { - return healthCheck.orNull(); - } - - public boolean hasHealthCheck() { - return healthCheck.isPresent(); - } - - public KillPolicy getKillPolicy() { - return killPolicy.orNull(); - } - - public boolean hasKillPolicy() { - return killPolicy.isPresent(); - } - - public String getName() { - return name; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - SingularityMesosTaskObject that = (SingularityMesosTaskObject) o; - - if (taskId != null ? !taskId.equals(that.taskId) : that.taskId != null) { - return false; - } - if (executor != null ? !executor.equals(that.executor) : that.executor != null) { - return false; - } - if (labels != null ? !labels.equals(that.labels) : that.labels != null) { - return false; - } - if (agentId != null ? !agentId.equals(that.agentId) : that.agentId != null) { - return false; - } - if (resources != null ? !resources.equals(that.resources) : that.resources != null) { - return false; - } - if (command != null ? !command.equals(that.command) : that.command != null) { - return false; - } - if (container != null ? !container.equals(that.container) : that.container != null) { - return false; - } - if (discovery != null ? !discovery.equals(that.discovery) : that.discovery != null) { - return false; - } - if (healthCheck != null ? !healthCheck.equals(that.healthCheck) : that.healthCheck != null) { - return false; - } - if (killPolicy != null ? !killPolicy.equals(that.killPolicy) : that.killPolicy != null) { - return false; - } - return name != null ? name.equals(that.name) : that.name == null; - } - - @Override - public int hashCode() { - int result = taskId != null ? taskId.hashCode() : 0; - result = 31 * result + (executor != null ? executor.hashCode() : 0); - result = 31 * result + (labels != null ? labels.hashCode() : 0); - result = 31 * result + (agentId != null ? agentId.hashCode() : 0); - result = 31 * result + (resources != null ? resources.hashCode() : 0); - result = 31 * result + (command != null ? command.hashCode() : 0); - result = 31 * result + (container != null ? container.hashCode() : 0); - result = 31 * result + (discovery != null ? discovery.hashCode() : 0); - result = 31 * result + (healthCheck != null ? healthCheck.hashCode() : 0); - result = 31 * result + (killPolicy != null ? killPolicy.hashCode() : 0); - result = 31 * result + (name != null ? name.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "SingularityMesosTaskObject{" + - "taskId=" + taskId + - ", executor=" + executor + - ", labels=" + labels + - ", agentId=" + agentId + - ", resources=" + resources + - ", command=" + command + - ", container=" + container + - ", discovery=" + discovery + - ", healthCheck=" + healthCheck + - ", killPolicy=" + killPolicy + - ", name='" + name + '\'' + - '}'; - } -} diff --git a/SingularityBase/src/main/java/com/hubspot/mesos/json/SingularityMesosTaskStatusObject.java b/SingularityBase/src/main/java/com/hubspot/mesos/json/SingularityMesosTaskStatusObject.java deleted file mode 100644 index 50520d2d33..0000000000 --- a/SingularityBase/src/main/java/com/hubspot/mesos/json/SingularityMesosTaskStatusObject.java +++ /dev/null @@ -1,201 +0,0 @@ -package com.hubspot.mesos.json; - -import org.apache.mesos.v1.Protos.AgentID; -import org.apache.mesos.v1.Protos.ContainerStatus; -import org.apache.mesos.v1.Protos.ExecutorID; -import org.apache.mesos.v1.Protos.Labels; -import org.apache.mesos.v1.Protos.TaskID; -import org.apache.mesos.v1.Protos.TaskState; -import org.apache.mesos.v1.Protos.TaskStatus; -import org.apache.mesos.v1.Protos.TaskStatus.Reason; -import org.apache.mesos.v1.Protos.TaskStatus.Source; -import org.apache.mesos.v1.Protos.TimeInfo; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.base.Optional; - -public class SingularityMesosTaskStatusObject { - private final Optional agentId; - private final Optional slaveId; - private final Optional containerStatus; - private final Optional executorId; - private final Optional healthy; - private final Optional labels; - private final Optional message; - private final Optional reason; - private final Optional source; - private final Optional state; - private final Optional taskId; - private final Optional timestamp; - private final Optional unreachableTime; - - @JsonCreator - public SingularityMesosTaskStatusObject(@JsonProperty("agentId") Optional agentId, - @JsonProperty("slaveId") Optional slaveId, - @JsonProperty("containerStatus") Optional containerStatus, - @JsonProperty("executorID") Optional executorId, - @JsonProperty("healthy") Optional healthy, - @JsonProperty("labels") Optional labels, - @JsonProperty("message") Optional message, - @JsonProperty("reason") Optional reason, - @JsonProperty("source") Optional source, - @JsonProperty("state") Optional state, - @JsonProperty("taskId") Optional taskId, - @JsonProperty("timestamp") Optional timestamp, - @JsonProperty("unreachableTime") Optional unreachableTime) { - this.agentId = agentId.or(slaveId); - this.slaveId = agentId.or(slaveId); - this.containerStatus = containerStatus; - this.executorId = executorId; - this.healthy = healthy; - this.labels = labels; - this.message = message; - this.reason = reason; - this.source = source; - this.state = state; - this.taskId = taskId; - this.timestamp = timestamp; - this.unreachableTime = unreachableTime; - } - - public static SingularityMesosTaskStatusObject fromProtos(TaskStatus status) { - return new SingularityMesosTaskStatusObject( - status.hasAgentId() ? Optional.of(status.getAgentId()) : Optional.absent(), - status.hasAgentId() ? Optional.of(status.getAgentId()) : Optional.absent(), - status.hasContainerStatus() ? Optional.of(status.getContainerStatus()) : Optional.absent(), - status.hasExecutorId() ? Optional.of(status.getExecutorId()) : Optional.absent(), - status.hasHealthy() ? Optional.of(status.getHealthy()) : Optional.absent(), - status.hasLabels() ? Optional.of(status.getLabels()) : Optional.absent(), - status.hasMessage() ? Optional.of(status.getMessage()) : Optional.absent(), - status.hasReason() ? Optional.of(status.getReason()) : Optional.absent(), - status.hasSource() ? Optional.of(status.getSource()) : Optional.absent(), - status.hasState() ? Optional.of(status.getState()) : Optional.absent(), - status.hasTaskId() ? Optional.of(status.getTaskId()) : Optional.absent(), - status.hasTimestamp() ? Optional.of(status.getTimestamp()) : Optional.absent(), - status.hasUnreachableTime() ? Optional.of(status.getUnreachableTime()) : Optional.absent() - ); - } - - public AgentID getAgentId() { - return agentId.orNull(); - } - - public boolean hasAgentId() { - return agentId.isPresent(); - } - - public AgentID getSlaveId() { - return slaveId.orNull(); - } - public boolean hasSlaveId() { - return slaveId.isPresent(); - } - - public ContainerStatus getContainerStatus() { - return containerStatus.orNull(); - } - - public boolean hasContainerStatus() { - return containerStatus.isPresent(); - } - - public ExecutorID getExecutorId() { - return executorId.orNull(); - } - - public boolean hasExecutorId() { - return executorId.isPresent(); - } - - public Boolean getHealthy() { - return healthy.orNull(); - } - - public boolean hasHealthy() { - return healthy.isPresent(); - } - - public Labels getLabels() { - return labels.orNull(); - } - - public boolean hasLabels() { - return labels.isPresent(); - } - - public String getMessage() { - return message.orNull(); - } - - public boolean hasMessage() { - return message.isPresent(); - } - - public Reason getReason() { - return reason.orNull(); - } - - public boolean hasReason() { - return reason.isPresent(); - } - - public Source getSource() { - return source.orNull(); - } - - public boolean hasSource() { - return source.isPresent(); - } - - public TaskState getState() { - return state.orNull(); - } - - public boolean hasState() { - return state.isPresent(); - } - - public TaskID getTaskId() { - return taskId.orNull(); - } - - public boolean hasTaskId() { - return taskId.isPresent(); - } - - public Double getTimestamp() { - return timestamp.orNull(); - } - - public boolean hasTimestamp() { - return timestamp.isPresent(); - } - - public TimeInfo getUnreachableTime() { - return unreachableTime.orNull(); - } - - public boolean hasUnreachableTime() { - return unreachableTime.isPresent(); - } - - @Override - public String toString() { - return "SingularityMesosTaskStatusObject{" + - "agentId=" + agentId + - ", slaveId=" + slaveId + - ", containerStatus=" + containerStatus + - ", executorId=" + executorId + - ", healthy=" + healthy + - ", labels=" + labels + - ", message=" + message + - ", reason=" + reason + - ", source=" + source + - ", state=" + state + - ", taskId=" + taskId + - ", timestamp=" + timestamp + - ", unreachableTime=" + unreachableTime + - '}'; - } -} diff --git a/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosExecutorInfo.java b/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosExecutorInfo.java new file mode 100644 index 0000000000..0fe76b6ca4 --- /dev/null +++ b/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosExecutorInfo.java @@ -0,0 +1,67 @@ +package com.hubspot.mesos.protos; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Optional; + +public class MesosExecutorInfo { + private final Optional executorId; + private final Map allOtherFields; + + @JsonCreator + public MesosExecutorInfo(@JsonProperty("executorId") Optional executorId) { + this.executorId = executorId; + this.allOtherFields = new HashMap<>(); + } + + public MesosStringValue getExecutorId() { + return executorId.orNull(); + } + + public boolean hasExecutorId() { + return executorId.isPresent(); + } + + // Unknown fields + @JsonAnyGetter + public Map getUnknownFields() { + return allOtherFields; + } + + @JsonAnySetter + public void setUnknownFields(String name, Object value) { + allOtherFields.put(name, value); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof MesosExecutorInfo) { + final MesosExecutorInfo that = (MesosExecutorInfo) obj; + return Objects.equals(this.executorId, that.executorId) && + Objects.equals(this.allOtherFields, that.allOtherFields); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(executorId, allOtherFields); + } + + @Override + public String toString() { + return "MesosExecutorInfo{" + + "executorId=" + executorId + + ", allOtherFields=" + allOtherFields + + '}'; + } +} diff --git a/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosLabels.java b/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosLabels.java new file mode 100644 index 0000000000..c0878deead --- /dev/null +++ b/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosLabels.java @@ -0,0 +1,44 @@ +package com.hubspot.mesos.protos; + +import java.util.List; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class MesosLabels { + private final List labels; + + @JsonCreator + public MesosLabels(@JsonProperty("labels") List labels) { + this.labels = labels; + } + + public List getLabels() { + return labels; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof MesosLabels) { + final MesosLabels that = (MesosLabels) obj; + return Objects.equals(this.labels, that.labels); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(labels); + } + + @Override + public String toString() { + return "MesosLabels{" + + "labels=" + labels + + '}'; + } +} diff --git a/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosOfferObject.java b/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosOfferObject.java new file mode 100644 index 0000000000..1af025a366 --- /dev/null +++ b/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosOfferObject.java @@ -0,0 +1,109 @@ +package com.hubspot.mesos.protos; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; + +/* + * Mirrors the mesos Offer object, with the exception that slaveId can be read into agentId + */ +public class MesosOfferObject { + private final MesosStringValue agentId; + private final MesosStringValue slaveId; + private final MesosStringValue frameworkId; + private final String hostname; + private final MesosStringValue id; + private final Map allOtherFields; + + @JsonCreator + public MesosOfferObject(@JsonProperty("agentId") MesosStringValue agentId, + @JsonProperty("slaveId") MesosStringValue slaveId, + @JsonProperty("frameworkId") MesosStringValue frameworkId, + @JsonProperty("hostname") String hostname, + @JsonProperty("id") MesosStringValue id) { + this.agentId = agentId != null ? agentId : slaveId; + this.slaveId = agentId != null ? agentId : slaveId; + this.frameworkId = frameworkId; + this.hostname = hostname; + this.id = id; + this.allOtherFields = new HashMap<>(); + } + + @JsonIgnore + public MesosOfferObject sizeOptimized() { + MesosOfferObject optimized = new MesosOfferObject(agentId, null, frameworkId, hostname, id); + optimized.setAllOtherFields("url", allOtherFields.get("url")); + return optimized; + } + + public MesosStringValue getAgentId() { + return agentId; + } + + public MesosStringValue getSlaveId() { + return slaveId; + } + + public MesosStringValue getFrameworkId() { + return frameworkId; + } + + public String getHostname() { + return hostname; + } + + public MesosStringValue getId() { + return id; + } + + // Unknown fields + @JsonAnyGetter + public Map getAllOtherFields() { + return allOtherFields; + } + + @JsonAnySetter + public void setAllOtherFields(String name, Object value) { + allOtherFields.put(name, value); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof MesosOfferObject) { + final MesosOfferObject that = (MesosOfferObject) obj; + return Objects.equals(this.agentId, that.agentId) && + Objects.equals(this.slaveId, that.slaveId) && + Objects.equals(this.frameworkId, that.frameworkId) && + Objects.equals(this.hostname, that.hostname) && + Objects.equals(this.id, that.id) && + Objects.equals(this.allOtherFields, that.allOtherFields); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(agentId, slaveId, frameworkId, hostname, id, allOtherFields); + } + + @Override + public String toString() { + return "MesosOfferObject{" + + "agentId=" + agentId + + ", slaveId=" + slaveId + + ", frameworkId=" + frameworkId + + ", hostname='" + hostname + '\'' + + ", id=" + id + + ", allOtherFields=" + allOtherFields + + '}'; + } +} diff --git a/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosParameter.java b/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosParameter.java new file mode 100644 index 0000000000..a9972c9a03 --- /dev/null +++ b/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosParameter.java @@ -0,0 +1,64 @@ +package com.hubspot.mesos.protos; + +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Optional; + +public class MesosParameter { + private final Optional key; + private final Optional value; + + @JsonCreator + public MesosParameter(@JsonProperty("key") Optional key, + @JsonProperty("value") Optional value) { + this.key = key; + this.value = value; + } + + public String getKey() { + return key.orNull(); + } + + @JsonIgnore + public boolean hasKey() { + return key.isPresent(); + } + + public String getValue() { + return value.orNull(); + } + + @JsonIgnore + public boolean hasValue() { + return value.isPresent(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof MesosParameter) { + final MesosParameter that = (MesosParameter) obj; + return Objects.equals(this.key, that.key) && + Objects.equals(this.value, that.value); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(key, value); + } + + @Override + public String toString() { + return "MesosKeyValueObject{" + + "key=" + key + + ", value=" + value + + '}'; + } +} diff --git a/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosResourceObject.java b/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosResourceObject.java new file mode 100644 index 0000000000..f73fd227de --- /dev/null +++ b/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosResourceObject.java @@ -0,0 +1,70 @@ +package com.hubspot.mesos.protos; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Optional; + +public class MesosResourceObject { + private final Optional name; + private final Map allOtherFields; + + @JsonCreator + + public MesosResourceObject(@JsonProperty("name") Optional name) { + this.name = name; + this.allOtherFields = new HashMap<>(); + } + + public String getName() { + return name.orNull(); + } + + @JsonIgnore + public boolean hasName() { + return name.isPresent(); + } + + // Unknown fields + @JsonAnyGetter + public Map getUnknownFields() { + return allOtherFields; + } + + @JsonAnySetter + public void setUnknownFields(String name, Object value) { + allOtherFields.put(name, value); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof MesosResourceObject) { + final MesosResourceObject that = (MesosResourceObject) obj; + return Objects.equals(this.name, that.name) && + Objects.equals(this.allOtherFields, that.allOtherFields); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(name, allOtherFields); + } + + @Override + public String toString() { + return "MesosResourceObject{" + + "name=" + name + + ", allOtherFields=" + allOtherFields + + '}'; + } +} diff --git a/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosStringValue.java b/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosStringValue.java new file mode 100644 index 0000000000..6a4eaca139 --- /dev/null +++ b/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosStringValue.java @@ -0,0 +1,43 @@ +package com.hubspot.mesos.protos; + +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class MesosStringValue { + private final String value; + + @JsonCreator + public MesosStringValue(@JsonProperty("value") String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof MesosStringValue) { + final MesosStringValue that = (MesosStringValue) obj; + return Objects.equals(this.value, that.value); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + @Override + public String toString() { + return "SingularityMesosIdObject{" + + "value='" + value + '\'' + + '}'; + } +} diff --git a/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosTaskObject.java b/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosTaskObject.java new file mode 100644 index 0000000000..c6fc1d0518 --- /dev/null +++ b/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosTaskObject.java @@ -0,0 +1,128 @@ +package com.hubspot.mesos.protos; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Optional; + +/* + * Mimics the TaskInfo object from mesos, with the addition that we can read + * AgentID from either a field named slaveId or a field named agentId for + * better backwards compatibility + */ +public class MesosTaskObject { + private final MesosStringValue taskId; + private final Optional executor; + private final MesosLabels labels; + private final MesosStringValue agentId; + private final MesosStringValue slaveId; + private final List resources; + private final String name; + private final Map allOtherFields; + + @JsonCreator + public MesosTaskObject(@JsonProperty("taskId") MesosStringValue taskId, + @JsonProperty("executor") Optional executor, + @JsonProperty("labels") MesosLabels labels, + @JsonProperty("agentId") MesosStringValue agentId, + @JsonProperty("slaveId") MesosStringValue slaveId, + @JsonProperty("resources") List resources, + @JsonProperty("name") String name) { + this.taskId = taskId; + this.executor = executor; + this.labels = labels; + this.agentId = agentId != null ? agentId : slaveId; + this.slaveId = agentId != null ? agentId : slaveId; + this.resources = resources != null ? resources : Collections.emptyList(); + this.name = name; + this.allOtherFields = new HashMap<>(); + } + + public MesosStringValue getTaskId() { + return taskId; + } + + public MesosExecutorInfo getExecutor() { + return executor.orNull(); + } + + public boolean hasExecutor() { + return executor.isPresent(); + } + + public MesosLabels getLabels() { + return labels; + } + + public MesosStringValue getAgentId() { + return agentId; + } + + public MesosStringValue getSlaveId() { + return slaveId; + } + + public List getResources() { + return resources; + } + + // Unknown fields + @JsonAnyGetter + public Map getAllOtherFields() { + return allOtherFields; + } + + @JsonAnySetter + public void setAllOtherFields(String name, Object value) { + allOtherFields.put(name, value); + } + + public String getName() { + return name; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof MesosTaskObject) { + final MesosTaskObject that = (MesosTaskObject) obj; + return Objects.equals(this.taskId, that.taskId) && + Objects.equals(this.executor, that.executor) && + Objects.equals(this.labels, that.labels) && + Objects.equals(this.agentId, that.agentId) && + Objects.equals(this.slaveId, that.slaveId) && + Objects.equals(this.resources, that.resources) && + Objects.equals(this.name, that.name) && + Objects.equals(this.allOtherFields, that.allOtherFields); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(taskId, executor, labels, agentId, slaveId, resources, name, allOtherFields); + } + + @Override + public String toString() { + return "MesosTaskObject{" + + "taskId=" + taskId + + ", executor=" + executor + + ", labels=" + labels + + ", agentId=" + agentId + + ", slaveId=" + slaveId + + ", resources=" + resources + + ", name='" + name + '\'' + + ", allOtherFields=" + allOtherFields + + '}'; + } +} diff --git a/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosTaskState.java b/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosTaskState.java new file mode 100644 index 0000000000..0e5763c975 --- /dev/null +++ b/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosTaskState.java @@ -0,0 +1,7 @@ +package com.hubspot.mesos.protos; + +public enum MesosTaskState { + TASK_DROPPED, TASK_ERROR, TASK_FAILED, TASK_FINISHED, TASK_GONE, + TASK_GONE_BY_OPERATOR, TASK_KILLED, TASK_KILLING, TASK_LOST, + TASK_RUNNING, TASK_STAGING, TASK_STARTING, TASK_UNKNOWN, TASK_UNREACHABLE +} diff --git a/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosTaskStatusObject.java b/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosTaskStatusObject.java new file mode 100644 index 0000000000..9568888531 --- /dev/null +++ b/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosTaskStatusObject.java @@ -0,0 +1,132 @@ +package com.hubspot.mesos.protos; + +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Optional; + +public class MesosTaskStatusObject { + private final Optional agentId; + private final Optional slaveId; + private final Optional healthy; + private final Optional message; + private final Optional reason; + private final Optional state; + private final Optional taskId; + private final Optional timestamp; + private final Map allOtherFields; + + @JsonCreator + public MesosTaskStatusObject(@JsonProperty("agentId") Optional agentId, + @JsonProperty("slaveId") Optional slaveId, + @JsonProperty("healthy") Optional healthy, + @JsonProperty("message") Optional message, + @JsonProperty("reason") Optional reason, + @JsonProperty("state") Optional state, + @JsonProperty("taskId") Optional taskId, + @JsonProperty("timestamp") Optional timestamp) { + this.agentId = agentId.or(slaveId); + this.slaveId = agentId.or(slaveId); + this.healthy = healthy; + this.message = message; + this.reason = reason; + this.state = state; + this.taskId = taskId; + this.timestamp = timestamp; + this.allOtherFields = new HashMap<>(); + } + + public MesosStringValue getAgentId() { + return agentId.orNull(); + } + + public boolean hasAgentId() { + return agentId.isPresent(); + } + + public MesosStringValue getSlaveId() { + return slaveId.orNull(); + } + + public boolean hasSlaveId() { + return slaveId.isPresent(); + } + + public Boolean getHealthy() { + return healthy.orNull(); + } + + public boolean hasHealthy() { + return healthy.isPresent(); + } + + public String getMessage() { + return message.orNull(); + } + + public boolean hasMessage() { + return message.isPresent(); + } + + public MesosTaskStatusReason getReason() { + return reason.orNull(); + } + + public boolean hasReason() { + return reason.isPresent(); + } + + public MesosTaskState getState() { + return state.orNull(); + } + + public boolean hasState() { + return state.isPresent(); + } + + public MesosStringValue getTaskId() { + return taskId.orNull(); + } + + public boolean hasTaskId() { + return taskId.isPresent(); + } + + public Double getTimestamp() { + return timestamp.orNull(); + } + + public boolean hasTimestamp() { + return timestamp.isPresent(); + } + + // Unknown fields + @JsonAnyGetter + public Map getAllOtherFields() { + return allOtherFields; + } + + @JsonAnySetter + public void setAllOtherFields(String name, Object value) { + allOtherFields.put(name, value); + } + + + @Override + public String toString() { + return "SingularityMesosTaskStatusObject{" + + "agentId=" + agentId + + ", slaveId=" + slaveId + + ", healthy=" + healthy + + ", message=" + message + + ", reason=" + reason + + ", state=" + state + + ", taskId=" + taskId + + ", timestamp=" + timestamp + + '}'; + } +} diff --git a/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosTaskStatusReason.java b/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosTaskStatusReason.java new file mode 100644 index 0000000000..3494dce8f6 --- /dev/null +++ b/SingularityBase/src/main/java/com/hubspot/mesos/protos/MesosTaskStatusReason.java @@ -0,0 +1,12 @@ +package com.hubspot.mesos.protos; + +public enum MesosTaskStatusReason { + REASON_COMMAND_EXECUTOR_FAILED, REASON_CONTAINER_LAUNCH_FAILED, REASON_CONTAINER_LIMITATION, REASON_CONTAINER_LIMITATION_DISK, + REASON_CONTAINER_LIMITATION_MEMORY, REASON_CONTAINER_PREEMPTED, REASON_CONTAINER_UPDATE_FAILED, REASON_EXECUTOR_REGISTRATION_TIMEOUT, + REASON_EXECUTOR_REREGISTRATION_TIMEOUT, REASON_EXECUTOR_TERMINATED, REASON_EXECUTOR_UNREGISTERED, REASON_FRAMEWORK_REMOVED, REASON_GC_ERROR, + REASON_INVALID_FRAMEWORKID, REASON_INVALID_OFFERS, REASON_IO_SWITCHBOARD_EXITED, REASON_MASTER_DISCONNECTED, REASON_RECONCILIATION, + REASON_RESOURCES_UNKNOWN, REASON_SLAVE_DISCONNECTED, REASON_SLAVE_REMOVED, REASON_SLAVE_REMOVED_BY_OPERATOR, REASON_SLAVE_RESTARTED, + REASON_SLAVE_UNKNOWN, REASON_TASK_CHECK_STATUS_UPDATED, REASON_TASK_GROUP_INVALID, REASON_TASK_GROUP_UNAUTHORIZED, + REASON_TASK_HEALTH_CHECK_STATUS_UPDATED, REASON_TASK_INVALID, REASON_TASK_KILLED_DURING_LAUNCH, REASON_TASK_UNAUTHORIZED, + REASON_TASK_UNKNOWN +} diff --git a/SingularityBase/src/main/java/com/hubspot/singularity/ExtendedTaskState.java b/SingularityBase/src/main/java/com/hubspot/singularity/ExtendedTaskState.java index 48c6ecd626..4b4fe437b7 100644 --- a/SingularityBase/src/main/java/com/hubspot/singularity/ExtendedTaskState.java +++ b/SingularityBase/src/main/java/com/hubspot/singularity/ExtendedTaskState.java @@ -1,44 +1,23 @@ package com.hubspot.singularity; -import java.util.Map; - -import org.apache.mesos.v1.Protos.TaskState; - import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.collect.Maps; +import com.hubspot.mesos.protos.MesosTaskState; public enum ExtendedTaskState { - TASK_LAUNCHED("launched", false, Optional.absent()), TASK_STAGING("staging", false, Optional.of(TaskState.TASK_STAGING)), - TASK_STARTING("starting", false, Optional.of(TaskState.TASK_STARTING)), TASK_RUNNING("running", false, Optional.of(TaskState.TASK_RUNNING)), - TASK_CLEANING("cleaning", false, Optional.absent()), TASK_KILLING("killing", false, Optional.of(TaskState.TASK_KILLING)), TASK_FINISHED("finished", true, Optional.of(TaskState.TASK_FINISHED)), - TASK_FAILED("failed", true, Optional.of(TaskState.TASK_FAILED)), TASK_KILLED("killed", true, Optional.of(TaskState.TASK_KILLED)), - TASK_LOST("lost", true, Optional.of(TaskState.TASK_LOST)), TASK_LOST_WHILE_DOWN("lost", true, Optional.absent()), TASK_ERROR("error", true, Optional.of(TaskState.TASK_ERROR)), - TASK_DROPPED("dropped", true, Optional.of(TaskState.TASK_DROPPED)), TASK_GONE("gone", true, Optional.of(TaskState.TASK_GONE)), TASK_UNREACHABLE("unreachable", true, Optional.of(TaskState.TASK_UNREACHABLE)), - TASK_GONE_BY_OPERATOR("goneByOperator", true, Optional.of(TaskState.TASK_GONE_BY_OPERATOR)), TASK_UNKNOWN("dropped", true, Optional.of(TaskState.TASK_UNKNOWN)); - - private static final Map map; - static { - map = Maps.newHashMapWithExpectedSize(ExtendedTaskState.values().length); - for (ExtendedTaskState extendedTaskState : ExtendedTaskState.values()) { - if (extendedTaskState.toTaskState().isPresent()) { - map.put(extendedTaskState.toTaskState().get(), extendedTaskState); - } - } - - for (TaskState t : TaskState.values()) { - if (map.get(t) == null) { - throw new IllegalStateException("No ExtendedTaskState provided for TaskState " + t + ", you probably have incompatible versions of Mesos and Singularity."); - } - } - } + TASK_LAUNCHED("launched", false, Optional.absent()), TASK_STAGING("staging", false, Optional.of(MesosTaskState.TASK_STAGING)), + TASK_STARTING("starting", false, Optional.of(MesosTaskState.TASK_STARTING)), TASK_RUNNING("running", false, Optional.of(MesosTaskState.TASK_RUNNING)), + TASK_CLEANING("cleaning", false, Optional.absent()), TASK_KILLING("killing", false, Optional.of(MesosTaskState.TASK_KILLING)), TASK_FINISHED("finished", true, Optional.of(MesosTaskState.TASK_FINISHED)), + TASK_FAILED("failed", true, Optional.of(MesosTaskState.TASK_FAILED)), TASK_KILLED("killed", true, Optional.of(MesosTaskState.TASK_KILLED)), + TASK_LOST("lost", true, Optional.of(MesosTaskState.TASK_LOST)), TASK_LOST_WHILE_DOWN("lost", true, Optional.absent()), TASK_ERROR("error", true, Optional.of(MesosTaskState.TASK_ERROR)), + TASK_DROPPED("dropped", true, Optional.of(MesosTaskState.TASK_DROPPED)), TASK_GONE("gone", true, Optional.of(MesosTaskState.TASK_GONE)), TASK_UNREACHABLE("unreachable", true, Optional.of(MesosTaskState.TASK_UNREACHABLE)), + TASK_GONE_BY_OPERATOR("goneByOperator", true, Optional.of(MesosTaskState.TASK_GONE_BY_OPERATOR)), TASK_UNKNOWN("dropped", true, Optional.of(MesosTaskState.TASK_UNKNOWN)); private final String displayName; private final boolean isDone; - private final Optional taskState; + private final Optional taskState; - ExtendedTaskState(String displayName, boolean isDone, Optional taskState) { + ExtendedTaskState(String displayName, boolean isDone, Optional taskState) { this.displayName = displayName; this.isDone = isDone; this.taskState = taskState; @@ -60,14 +39,7 @@ public boolean isSuccess() { return this == TASK_FINISHED; } - public Optional toTaskState() { + public Optional toTaskState() { return taskState; } - - public static ExtendedTaskState fromTaskState(TaskState taskState) { - ExtendedTaskState extendedTaskState = map.get(taskState); - Preconditions.checkArgument(extendedTaskState != null, "No ExtendedTaskState for TaskState %s", taskState); - return extendedTaskState; - } - } diff --git a/SingularityBase/src/main/java/com/hubspot/singularity/SingularityRequest.java b/SingularityBase/src/main/java/com/hubspot/singularity/SingularityRequest.java index 4c22e545d5..e1b3f24090 100644 --- a/SingularityBase/src/main/java/com/hubspot/singularity/SingularityRequest.java +++ b/SingularityBase/src/main/java/com/hubspot/singularity/SingularityRequest.java @@ -61,6 +61,7 @@ public class SingularityRequest { private final Optional maxTasksPerOffer; private final Optional allowBounceToSameHost; + @Deprecated private final Optional dataCenter; @JsonCreator @@ -361,7 +362,8 @@ public Optional getTaskPriorityLevel() { return taskPriorityLevel; } - @ApiModelProperty(required=false, value="When using the cluster coordinator, the data center associated with this request") + @Deprecated + @ApiModelProperty(required=false, value="the data center associated with this request") public Optional getDataCenter() { return dataCenter; } diff --git a/SingularityBase/src/main/java/com/hubspot/singularity/SingularityRequestBuilder.java b/SingularityBase/src/main/java/com/hubspot/singularity/SingularityRequestBuilder.java index 93194c7853..f2a08522cd 100644 --- a/SingularityBase/src/main/java/com/hubspot/singularity/SingularityRequestBuilder.java +++ b/SingularityBase/src/main/java/com/hubspot/singularity/SingularityRequestBuilder.java @@ -51,6 +51,7 @@ public class SingularityRequestBuilder { private Optional taskPriorityLevel; private Optional maxTasksPerOffer; private Optional allowBounceToSameHost; + @Deprecated private Optional dataCenter; public SingularityRequestBuilder(String id, RequestType requestType) { @@ -357,10 +358,12 @@ public SingularityRequestBuilder setAllowBounceToSameHost(Optional allo return this; } + @Deprecated public Optional getDataCenter() { return dataCenter; } + @Deprecated public SingularityRequestBuilder setDataCenter(Optional dataCenter) { this.dataCenter = dataCenter; return this; diff --git a/SingularityBase/src/main/java/com/hubspot/singularity/SingularitySlaveUsage.java b/SingularityBase/src/main/java/com/hubspot/singularity/SingularitySlaveUsage.java index f6e328f38c..60407be5b7 100644 --- a/SingularityBase/src/main/java/com/hubspot/singularity/SingularitySlaveUsage.java +++ b/SingularityBase/src/main/java/com/hubspot/singularity/SingularitySlaveUsage.java @@ -9,7 +9,7 @@ public class SingularitySlaveUsage { public enum ResourceUsageType { - CPU_USED, MEMORY_BYTES_USED, CPU_FREE, MEMORY_BYTES_FREE + CPU_USED, MEMORY_BYTES_USED, DISK_BYTES_USED, CPU_FREE, MEMORY_BYTES_FREE, DISK_BYTES_FREE } public static final long BYTES_PER_MEGABYTE = 1000L * 1000L; diff --git a/SingularityBase/src/main/java/com/hubspot/singularity/SingularityTask.java b/SingularityBase/src/main/java/com/hubspot/singularity/SingularityTask.java index 4aa7e89930..795eb0da2e 100644 --- a/SingularityBase/src/main/java/com/hubspot/singularity/SingularityTask.java +++ b/SingularityBase/src/main/java/com/hubspot/singularity/SingularityTask.java @@ -3,48 +3,38 @@ import java.util.Collections; import java.util.List; -import org.apache.mesos.v1.Protos.AgentID; -import org.apache.mesos.v1.Protos.TaskInfo; - import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.base.Optional; import com.google.common.base.Preconditions; -import com.hubspot.mesos.MesosUtils; -import com.hubspot.mesos.json.SingularityMesosOfferObject; -import com.hubspot.mesos.json.SingularityMesosTaskObject; +import com.hubspot.mesos.protos.MesosStringValue; +import com.hubspot.mesos.protos.MesosOfferObject; +import com.hubspot.mesos.protos.MesosTaskObject; import com.wordnik.swagger.annotations.ApiModelProperty; public class SingularityTask extends SingularityTaskIdHolder { private final SingularityTaskRequest taskRequest; - private final List offers; - /* - * Only used when we first send this task to mesos. This should not be saved to JSON - * A subset of this information which we have greater control over is saved in the - * SingularityMesosTaskObject - */ - private final TaskInfo mesosTaskProtos; - private final SingularityMesosTaskObject mesosTask; + private final List offers; + private final MesosTaskObject mesosTask; private final Optional rackId; - public SingularityTask(SingularityTaskRequest taskRequest, SingularityTaskId taskId, List offers, TaskInfo mesosTaskProtos, SingularityMesosTaskObject task, Optional rackId) { - this(taskRequest, taskId, null, offers, mesosTaskProtos, task, rackId); - } - - public SingularityTask(SingularityTaskRequest taskRequest, SingularityTaskId taskId, List offers, SingularityMesosTaskObject task, Optional rackId) { - this(taskRequest, taskId, null, offers, null, task, rackId); + public SingularityTask(SingularityTaskRequest taskRequest, SingularityTaskId taskId, List offers, MesosTaskObject task, Optional rackId) { + this(taskRequest, taskId, null, offers, task, rackId); } @JsonCreator - public SingularityTask(@JsonProperty("taskRequest") SingularityTaskRequest taskRequest, @JsonProperty("taskId") SingularityTaskId taskId, @JsonProperty("offer") SingularityMesosOfferObject offer, @JsonProperty("offers") List offers, - @JsonProperty("mesosTaskProtos") TaskInfo mesosTaskProtos, @JsonProperty("mesosTask") SingularityMesosTaskObject task, @JsonProperty("rackId") Optional rackId) { + public SingularityTask(@JsonProperty("taskRequest") SingularityTaskRequest taskRequest, + @JsonProperty("taskId") SingularityTaskId taskId, + @JsonProperty("offer") MesosOfferObject offer, + @JsonProperty("offers") List offers, + @JsonProperty("mesosTask") MesosTaskObject task, + @JsonProperty("rackId") Optional rackId) { super(taskId); Preconditions.checkArgument(offer != null || offers != null, "Must specify at least one of offer / offers"); this.taskRequest = taskRequest; this.mesosTask = task; - this.mesosTaskProtos = mesosTaskProtos; this.rackId = rackId; if (offers != null) { this.offers = offers; @@ -62,41 +52,26 @@ public SingularityTaskRequest getTaskRequest() { */ @Deprecated @ApiModelProperty(hidden=true) - public SingularityMesosOfferObject getOffer() { + public MesosOfferObject getOffer() { return offers.get(0); } @ApiModelProperty(hidden=true) - public List getOffers() { + public List getOffers() { return offers; } @ApiModelProperty(hidden=true) - public SingularityMesosTaskObject getMesosTask() { + public MesosTaskObject getMesosTask() { return mesosTask; } - @JsonIgnore - public TaskInfo getMesosTaskProtos() { - return mesosTaskProtos; - } - public Optional getRackId() { return rackId; } @JsonIgnore - public Optional getPortByIndex(int index) { - List ports = MesosUtils.getAllPorts(mesosTask.getResources()); - if (index >= ports.size() || index < 0) { - return Optional.absent(); - } else { - return Optional.of(ports.get(index)); - } - } - - @JsonIgnore - public AgentID getAgentId() { + public MesosStringValue getAgentId() { return offers.get(0).getAgentId(); } @@ -109,8 +84,8 @@ public String getHostname() { public String toString() { return "SingularityTask{" + "taskRequest=" + taskRequest + - ", offer=" + MesosUtils.formatForLogging(offers) + - ", mesosTask=" + MesosUtils.formatForLogging(mesosTask) + + ", offer=" + offers + + ", mesosTask=" + mesosTask + ", rackId=" + rackId + '}'; } diff --git a/SingularityBase/src/main/java/com/hubspot/singularity/SingularityTaskStatusHolder.java b/SingularityBase/src/main/java/com/hubspot/singularity/SingularityTaskStatusHolder.java index 26b31451ab..16e6613d57 100644 --- a/SingularityBase/src/main/java/com/hubspot/singularity/SingularityTaskStatusHolder.java +++ b/SingularityBase/src/main/java/com/hubspot/singularity/SingularityTaskStatusHolder.java @@ -3,19 +3,18 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.base.Optional; -import com.hubspot.mesos.MesosUtils; -import com.hubspot.mesos.json.SingularityMesosTaskStatusObject; +import com.hubspot.mesos.protos.MesosTaskStatusObject; public class SingularityTaskStatusHolder { - private final Optional taskStatus; + private final Optional taskStatus; private final SingularityTaskId taskId; private final long serverTimestamp; private final String serverId; private final Optional slaveId; @JsonCreator - public SingularityTaskStatusHolder(@JsonProperty("taskId") SingularityTaskId taskId, @JsonProperty("taskStatus") Optional taskStatus, @JsonProperty("serverTimestamp") long serverTimestamp, @JsonProperty("serverId") String serverId, @JsonProperty("slaveId") Optional slaveId) { + public SingularityTaskStatusHolder(@JsonProperty("taskId") SingularityTaskId taskId, @JsonProperty("taskStatus") Optional taskStatus, @JsonProperty("serverTimestamp") long serverTimestamp, @JsonProperty("serverId") String serverId, @JsonProperty("slaveId") Optional slaveId) { this.taskId = taskId; this.taskStatus = taskStatus; this.serverTimestamp = serverTimestamp; @@ -23,7 +22,7 @@ public SingularityTaskStatusHolder(@JsonProperty("taskId") SingularityTaskId tas this.slaveId = slaveId; } - public Optional getTaskStatus() { + public Optional getTaskStatus() { return taskStatus; } @@ -46,7 +45,7 @@ public Optional getSlaveId() { @Override public String toString() { return "SingularityTaskStatusHolder{" + - "taskStatus=" + MesosUtils.formatForLogging(taskStatus) + + "taskStatus=" + taskStatus + ", taskId=" + taskId + ", serverTimestamp=" + serverTimestamp + ", serverId='" + serverId + '\'' + diff --git a/SingularityBase/src/main/java/com/hubspot/singularity/SingularityUser.java b/SingularityBase/src/main/java/com/hubspot/singularity/SingularityUser.java index 4089e7c282..62c4416267 100644 --- a/SingularityBase/src/main/java/com/hubspot/singularity/SingularityUser.java +++ b/SingularityBase/src/main/java/com/hubspot/singularity/SingularityUser.java @@ -2,6 +2,8 @@ import static com.google.common.collect.ImmutableSet.copyOf; +import java.security.Principal; +import java.util.Collections; import java.util.Objects; import java.util.Set; @@ -9,26 +11,38 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.base.Optional; -public class SingularityUser { +public class SingularityUser implements Principal { private final String id; private final Optional name; private final Optional email; private final Set groups; + private final boolean authenticated; + + public static SingularityUser DEFAULT_USER = new SingularityUser("singularity", Optional.absent(), Optional.absent(), Collections.emptySet(), false); + + public SingularityUser(String id, Optional name, Optional email, Set groups) { + this(id, name, email, groups, true); + } @JsonCreator - public SingularityUser(@JsonProperty("id") String id, @JsonProperty("name") Optional name, @JsonProperty("email") Optional email, @JsonProperty("groups") Set groups) { + public SingularityUser(@JsonProperty("id") String id, + @JsonProperty("name") Optional name, + @JsonProperty("email") Optional email, + @JsonProperty("groups") Set groups, + @JsonProperty("authenticated") boolean authenticated) { this.id = id; this.name = name; this.email = email; this.groups = copyOf(groups); + this.authenticated = authenticated; } public String getId() { return id; } - public Optional getName() { - return name; + public String getName() { + return name.or(id); } public Optional getEmail() { @@ -39,33 +53,39 @@ public Set getGroups() { return groups; } - @Override - public String toString() { - return "SingularityUser{" + - "id='" + id + '\'' + - ", name=" + name + - ", email=" + email + - ", groups=" + groups + - '}'; + public boolean isAuthenticated() { + return authenticated; } @Override - public boolean equals(Object o) { - if (this == o) { + public boolean equals(Object obj) { + if (this == obj) { return true; } - if (o == null || getClass() != o.getClass()) { - return false; + if (obj instanceof SingularityUser) { + final SingularityUser that = (SingularityUser) obj; + return Objects.equals(this.authenticated, that.authenticated) && + Objects.equals(this.id, that.id) && + Objects.equals(this.name, that.name) && + Objects.equals(this.email, that.email) && + Objects.equals(this.groups, that.groups); } - SingularityUser that = (SingularityUser) o; - return Objects.equals(id, that.id) && - Objects.equals(name, that.name) && - Objects.equals(email, that.email) && - Objects.equals(groups, that.groups); + return false; } @Override public int hashCode() { - return Objects.hash(id, name, email, groups); + return Objects.hash(id, name, email, groups, authenticated); + } + + @Override + public String toString() { + return "SingularityUser{" + + "id='" + id + '\'' + + ", name=" + name + + ", email=" + email + + ", groups=" + groups + + ", authenticated=" + authenticated + + '}'; } } diff --git a/SingularityClient/src/main/java/com/hubspot/singularity/client/SingularityClient.java b/SingularityClient/src/main/java/com/hubspot/singularity/client/SingularityClient.java index 90d9eedde4..3c8a6b62c3 100644 --- a/SingularityClient/src/main/java/com/hubspot/singularity/client/SingularityClient.java +++ b/SingularityClient/src/main/java/com/hubspot/singularity/client/SingularityClient.java @@ -104,7 +104,8 @@ public class SingularityClient { private static final String BASE_API_FORMAT = "%s://%s/%s"; - private static final String AUTH_CHECK_FORMAT = "%s/auth/%s/auth-check/%s"; + private static final String AUTH_CHECK_FORMAT = "%s/auth/%s/auth-check"; + private static final String AUTH_CHECK_USER_FORMAT = AUTH_CHECK_FORMAT + "/%s"; private static final String STATE_FORMAT = "%s/state"; private static final String TASK_RECONCILIATION_FORMAT = STATE_FORMAT + "/task-reconciliation"; @@ -1446,7 +1447,27 @@ public void deletePriorityFreeze() { * true if the user is authorized for scope, false otherwise */ public boolean isUserAuthorized(String requestId, String userId, SingularityAuthorizationScope scope) { - final Function requestUri = (host) -> String.format(AUTH_CHECK_FORMAT, getApiBase(host), requestId, userId); + final Function requestUri = (host) -> String.format(AUTH_CHECK_USER_FORMAT, getApiBase(host), requestId, userId); + Map params = Collections.singletonMap("scope", scope.name()); + HttpResponse response = executeGetSingleWithParams(requestUri, "auth check", "", Optional.of(params)); + return response.isSuccess(); + } + + /** + * Check if the current client's user is authorized for the specified scope on the specified request + * + * @param requestId + * The request to check authorization on + * @param userId + * The user whose authorization will be checked + * @param scope + * The scope to check that `user` has + * + * @return + * true if the user is authorized for scope, false otherwise + */ + public boolean isUserAuthorized(String requestId, SingularityAuthorizationScope scope) { + final Function requestUri = (host) -> String.format(AUTH_CHECK_FORMAT, getApiBase(host), requestId); Map params = Collections.singletonMap("scope", scope.name()); HttpResponse response = executeGetSingleWithParams(requestUri, "auth check", "", Optional.of(params)); return response.isSuccess(); diff --git a/SingularityClusterCoordinator/.blazar.yaml b/SingularityClusterCoordinator/.blazar.yaml deleted file mode 100644 index 0748966170..0000000000 --- a/SingularityClusterCoordinator/.blazar.yaml +++ /dev/null @@ -1,3 +0,0 @@ -env: - MAVEN_ARGS: "-T 8 -Dsingularity.jar.name.format=\\${project.artifactId} -Dgpg.skip=true -DforkCount=2C" - SET_VERSION_OVERRIDE: "0.18.0-$GIT_BRANCH-SNAPSHOT" diff --git a/SingularityClusterCoordinator/.build-executable b/SingularityClusterCoordinator/.build-executable deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/SingularityClusterCoordinator/pom.xml b/SingularityClusterCoordinator/pom.xml deleted file mode 100644 index 4ae4d34375..0000000000 --- a/SingularityClusterCoordinator/pom.xml +++ /dev/null @@ -1,133 +0,0 @@ - - - 4.0.0 - - - com.hubspot - Singularity - 0.18.0-SNAPSHOT - - - SingularityClusterCoordinator - - - com.hubspot.singularity.SingularityClusterCoordinator - - - - - com.hubspot - SingularityBase - - - - com.hubspot - SingularityServiceBase - - - - com.hubspot - SingularityClient - - - - com.hubspot.dropwizard - dropwizard-guicier - - - - io.dropwizard - dropwizard-core - - - - io.dropwizard - dropwizard-lifecycle - - - - io.dropwizard - dropwizard-assets - - - - com.hubspot - HorizonCore - - - - com.hubspot - HorizonNing - - - - io.dropwizard - dropwizard-views - - - - io.dropwizard.metrics - metrics-healthchecks - - - - com.fasterxml.jackson.datatype - jackson-datatype-guava - - - - com.fasterxml.jackson.core - jackson-core - - - - javax.validation - validation-api - - - - com.fasterxml.jackson.core - jackson-databind - - - - com.google.inject - guice - - - - javax.ws.rs - javax.ws.rs-api - - - - com.fasterxml.jackson.core - jackson-annotations - - - - org.hibernate - hibernate-validator - - - - com.hubspot.jackson - jackson-datatype-protobuf - - - - com.google.guava - guava - - - - javax.servlet - javax.servlet-api - - - - org.slf4j - slf4j-api - - - diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/CoordinatorDropwizardHealthcheck.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/CoordinatorDropwizardHealthcheck.java deleted file mode 100644 index f905ef7e61..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/CoordinatorDropwizardHealthcheck.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.hubspot.singularity; - -import com.codahale.metrics.health.HealthCheck; -import com.google.inject.Inject; -import com.hubspot.singularity.config.ClusterCoordinatorConfiguration; - -public class CoordinatorDropwizardHealthcheck extends HealthCheck { - private final ClusterCoordinatorConfiguration configuration; - - @Inject - public CoordinatorDropwizardHealthcheck(ClusterCoordinatorConfiguration configuration) { - this.configuration = configuration; - } - - @Override - protected Result check() throws Exception { - if (configuration.getDataCenters().isEmpty()) { - return Result.unhealthy("No configured data centers"); - } else { - return Result.healthy("Valid data centers", configuration.getDataCenters()); - } - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/SingularityClusterCoordinator.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/SingularityClusterCoordinator.java deleted file mode 100644 index 044c88a441..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/SingularityClusterCoordinator.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.hubspot.singularity; - -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.datatype.guava.GuavaModule; -import com.hubspot.dropwizard.guicier.GuiceBundle; -import com.hubspot.jackson.datatype.protobuf.ProtobufModule; -import com.hubspot.singularity.config.ClusterCoordinatorConfiguration; - -import io.dropwizard.Application; -import io.dropwizard.assets.AssetsBundle; -import io.dropwizard.setup.Bootstrap; -import io.dropwizard.setup.Environment; -import io.dropwizard.views.ViewBundle; - -public class SingularityClusterCoordinator extends Application { - @Override - public void initialize(final Bootstrap bootstrap) { - - final GuiceBundle guiceBundle = GuiceBundle.defaultBuilder(ClusterCoordinatorConfiguration.class) - .modules(new SingularityClusterCoordinatorModule()) - .build(); - bootstrap.addBundle(guiceBundle); - - bootstrap.addBundle(new ViewBundle<>()); - bootstrap.addBundle(new AssetsBundle("/assets/static/", "/static/")); - bootstrap.addBundle(new AssetsBundle("/assets/api-docs/", "/api-docs/", "index.html", "api-docs")); - - bootstrap.getObjectMapper().registerModule(new ProtobufModule()); - bootstrap.getObjectMapper().registerModule(new GuavaModule()); - bootstrap.getObjectMapper().setSerializationInclusion(Include.NON_NULL); - bootstrap.getObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - } - - @Override - public void run(final ClusterCoordinatorConfiguration configuration, final Environment environment) throws Exception {} - - public static void main(final String[] args) throws Exception { - try { - new SingularityClusterCoordinator().run(args); - } catch (final Throwable t) { - t.printStackTrace(); - System.exit(1); - } - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/SingularityClusterCoordinatorModule.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/SingularityClusterCoordinatorModule.java deleted file mode 100644 index 563e6eb55e..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/SingularityClusterCoordinatorModule.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.hubspot.singularity; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.inject.Binder; -import com.google.inject.Scopes; -import com.hubspot.dropwizard.guicier.DropwizardAwareModule; -import com.hubspot.mesos.JavaUtils; -import com.hubspot.singularity.config.ClusterCoordinatorConfiguration; -import com.hubspot.singularity.proxy.SingularityClusterCoodinatorResourcesModule; - -public class SingularityClusterCoordinatorModule extends DropwizardAwareModule { - - @Override - public void configure(Binder binder) { - binder.bind(ObjectMapper.class).toInstance(JavaUtils.newObjectMapper()); - binder.bind(CoordinatorDropwizardHealthcheck.class).in(Scopes.SINGLETON); - binder.install(new SingularityClusterCoodinatorResourcesModule(getConfiguration())); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/config/ClusterCoordinatorConfiguration.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/config/ClusterCoordinatorConfiguration.java deleted file mode 100644 index 4bd54e44e7..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/config/ClusterCoordinatorConfiguration.java +++ /dev/null @@ -1,174 +0,0 @@ -package com.hubspot.singularity.config; - -import java.util.List; - -import javax.validation.Valid; - -import org.hibernate.validator.constraints.NotEmpty; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.base.Optional; -import com.hubspot.singularity.SingularityClientCredentials; - -import io.dropwizard.Configuration; - -public class ClusterCoordinatorConfiguration extends Configuration { - /* - * List of possible Singularity clusters. The first in the list will be considered the default - */ - @NotEmpty - private List dataCenters; - - @JsonProperty("ui") - @Valid - private UIConfiguration uiConfiguration = new UIConfiguration(); - - private Optional defaultClientCredentials; - - private boolean errorOnDataCenterNotSpecified = false; - - // Settings to inform the ui - private Integer defaultMemory = 64; - private Integer defaultCpus = 1; - private Integer slaveHttpPort = 5051; - private Optional slaveHttpsPort = Optional.absent(); - private int bounceExpirationMinutes = 60; - private long healthcheckIntervalSeconds = 5; - private long healthcheckTimeoutSeconds = 5; - private Optional healthcheckMaxRetries = Optional.absent(); - private int startupTimeoutSeconds = 45; - private boolean loadBalancingEnabled = false; - private Optional commonHostnameSuffixToOmit = Optional.absent(); - private Integer warnIfScheduledJobIsRunningPastNextRunPct = 200; - - - - public List getDataCenters() { - return dataCenters; - } - - public void setDataCenters(List dataCenters) { - this.dataCenters = dataCenters; - } - - public UIConfiguration getUiConfiguration() { - return uiConfiguration; - } - - public void setUiConfiguration(UIConfiguration uiConfiguration) { - this.uiConfiguration = uiConfiguration; - } - - public Integer getDefaultMemory() { - return defaultMemory; - } - - public void setDefaultMemory(Integer defaultMemory) { - this.defaultMemory = defaultMemory; - } - - public Integer getDefaultCpus() { - return defaultCpus; - } - - public void setDefaultCpus(Integer defaultCpus) { - this.defaultCpus = defaultCpus; - } - - public Integer getSlaveHttpPort() { - return slaveHttpPort; - } - - public void setSlaveHttpPort(Integer slaveHttpPort) { - this.slaveHttpPort = slaveHttpPort; - } - - public Optional getSlaveHttpsPort() { - return slaveHttpsPort; - } - - public void setSlaveHttpsPort(Optional slaveHttpsPort) { - this.slaveHttpsPort = slaveHttpsPort; - } - - public int getBounceExpirationMinutes() { - return bounceExpirationMinutes; - } - - public void setBounceExpirationMinutes(int bounceExpirationMinutes) { - this.bounceExpirationMinutes = bounceExpirationMinutes; - } - - public long getHealthcheckIntervalSeconds() { - return healthcheckIntervalSeconds; - } - - public void setHealthcheckIntervalSeconds(long healthcheckIntervalSeconds) { - this.healthcheckIntervalSeconds = healthcheckIntervalSeconds; - } - - public long getHealthcheckTimeoutSeconds() { - return healthcheckTimeoutSeconds; - } - - public void setHealthcheckTimeoutSeconds(long healthcheckTimeoutSeconds) { - this.healthcheckTimeoutSeconds = healthcheckTimeoutSeconds; - } - - public Optional getHealthcheckMaxRetries() { - return healthcheckMaxRetries; - } - - public void setHealthcheckMaxRetries(Optional healthcheckMaxRetries) { - this.healthcheckMaxRetries = healthcheckMaxRetries; - } - - public int getStartupTimeoutSeconds() { - return startupTimeoutSeconds; - } - - public void setStartupTimeoutSeconds(int startupTimeoutSeconds) { - this.startupTimeoutSeconds = startupTimeoutSeconds; - } - - public boolean isLoadBalancingEnabled() { - return loadBalancingEnabled; - } - - public void setLoadBalancingEnabled(boolean loadBalancingEnabled) { - this.loadBalancingEnabled = loadBalancingEnabled; - } - - public Optional getCommonHostnameSuffixToOmit() { - return commonHostnameSuffixToOmit; - } - - public void setCommonHostnameSuffixToOmit(Optional commonHostnameSuffixToOmit) { - this.commonHostnameSuffixToOmit = commonHostnameSuffixToOmit; - } - - public Integer getWarnIfScheduledJobIsRunningPastNextRunPct() { - return warnIfScheduledJobIsRunningPastNextRunPct; - } - - public void setWarnIfScheduledJobIsRunningPastNextRunPct(Integer warnIfScheduledJobIsRunningPastNextRunPct) { - this.warnIfScheduledJobIsRunningPastNextRunPct = warnIfScheduledJobIsRunningPastNextRunPct; - } - - public Optional getDefaultClientCredentials() { - return defaultClientCredentials; - } - - public void setDefaultClientCredentials(Optional defaultClientCredentials) { - this.defaultClientCredentials = defaultClientCredentials; - } - - public boolean isErrorOnDataCenterNotSpecified() { - return errorOnDataCenterNotSpecified; - } - - public ClusterCoordinatorConfiguration setErrorOnDataCenterNotSpecified(boolean errorOnDataCenterNotSpecified) { - this.errorOnDataCenterNotSpecified = errorOnDataCenterNotSpecified; - return this; - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/config/DataCenter.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/config/DataCenter.java deleted file mode 100644 index 75bd160474..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/config/DataCenter.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.hubspot.singularity.config; - -import java.util.List; - -import javax.validation.constraints.NotNull; - -import org.hibernate.validator.constraints.NotEmpty; - -import com.google.common.base.Optional; -import com.hubspot.singularity.SingularityClientCredentials; - -public class DataCenter { - @NotNull - private String name; - @NotEmpty - private List hosts; - @NotNull - private String contextPath; - // http or https - private String scheme = "http"; - - private Optional clientCredentials = Optional.absent(); - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public List getHosts() { - return hosts; - } - - public void setHosts(List hosts) { - this.hosts = hosts; - } - - public String getContextPath() { - return contextPath; - } - - public void setContextPath(String contextPath) { - this.contextPath = contextPath; - } - - public String getScheme() { - return scheme; - } - - public void setScheme(String scheme) { - this.scheme = scheme; - } - - public Optional getClientCredentials() { - return clientCredentials; - } - - public void setClientCredentials(Optional clientCredentials) { - this.clientCredentials = clientCredentials; - } - - @Override - public String toString() { - return "DataCenter{" + - "name='" + name + '\'' + - ", hosts=" + hosts + - ", contextPath='" + contextPath + '\'' + - ", scheme='" + scheme + '\'' + - ", clientCredentials=" + clientCredentials + - '}'; - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/DataCenterConflictException.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/DataCenterConflictException.java deleted file mode 100644 index 9c08fd4f75..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/DataCenterConflictException.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.hubspot.singularity.exceptions; - -import javax.ws.rs.WebApplicationException; - -public class DataCenterConflictException extends WebApplicationException { - public DataCenterConflictException(String message) { - super(message, 409); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/DataCenterNotFoundException.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/DataCenterNotFoundException.java deleted file mode 100644 index e003980199..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/DataCenterNotFoundException.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.hubspot.singularity.exceptions; - -import javax.ws.rs.WebApplicationException; - -public class DataCenterNotFoundException extends WebApplicationException { - public DataCenterNotFoundException(String message, int status) { - super(message, status); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/NotImplemenedException.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/NotImplemenedException.java deleted file mode 100644 index c8a71b6260..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/NotImplemenedException.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.hubspot.singularity.exceptions; - -import javax.ws.rs.WebApplicationException; - -public class NotImplemenedException extends WebApplicationException { - public NotImplemenedException() { - super("Not Implemented", 500); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/AuthResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/AuthResource.java deleted file mode 100644 index 075fe7a15b..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/AuthResource.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.common.base.Optional; -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityAuthorizationScope; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.AUTH_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class AuthResource extends ProxyResource { - - @Inject - public AuthResource() {} - - @GET - @Path("/user") - public Response getUser(@Context HttpServletRequest request) { - return routeToDefaultDataCenter(request); - } - - @GET - @Path("/{requestId}/auth-check/{userId}") - public Response checkReadOnlyAuth(@Context HttpServletRequest request, @PathParam("requestId") String requestId, @PathParam("userId") String userId, @QueryParam("scope") Optional scope) { - return routeToDefaultDataCenter(request); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DataCenterLocator.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DataCenterLocator.java deleted file mode 100644 index d9700ef5ef..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DataCenterLocator.java +++ /dev/null @@ -1,206 +0,0 @@ -package com.hubspot.singularity.proxy; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; -import java.util.Random; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Optional; -import com.google.common.collect.ImmutableMap; -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityRequestGroup; -import com.hubspot.singularity.SingularityRequestParent; -import com.hubspot.singularity.SingularitySlave; -import com.hubspot.singularity.client.SingularityClient; -import com.hubspot.singularity.config.ClusterCoordinatorConfiguration; -import com.hubspot.singularity.config.DataCenter; -import com.hubspot.singularity.exceptions.DataCenterNotFoundException; - -import io.dropwizard.lifecycle.Managed; - -public class DataCenterLocator implements Managed { - private static final Logger LOG = LoggerFactory.getLogger(DataCenterLocator.class); - - private final ClusterCoordinatorConfiguration configuration; - private final Map dataCenters; - private final Map clients; - - private final Random random = new Random(); - - private final Map> requestIdsByDataCenter = new ConcurrentHashMap<>(); - private final Map> requestGroupsByDataCenter = new ConcurrentHashMap<>(); - private final Map> slaveIdsByDataCenter = new ConcurrentHashMap<>(); - private final Map> hostnamesByDataCenter = new ConcurrentHashMap<>(); - private final Map> rackIdsByDataCenter = new ConcurrentHashMap<>(); - - - @Inject - public DataCenterLocator(ClusterCoordinatorConfiguration configuration, Map clients) { - this.configuration = configuration; - this.clients = clients; - - ImmutableMap.Builder builder = ImmutableMap.builder(); - configuration.getDataCenters().forEach((dc) -> builder.put(dc.getName(), dc)); - this.dataCenters = builder.build(); - } - - String getHost(DataCenter dataCenter) { - return dataCenter.getHosts().get(random.nextInt(dataCenter.getHosts().size())); - } - - DataCenter getDataCenterForRequest(String requestId, boolean isGetRequest) { - for (Map.Entry entry : dataCenters.entrySet()) { - if (requestIdsByDataCenter.get(entry.getKey()).contains(requestId)) { - return entry.getValue(); - } - } - - // Not found, try each one - for (Map.Entry entry : dataCenters.entrySet()) { - SingularityClient client = clients.get(entry.getKey()); - Optional maybeRequest = client.getSingularityRequest(requestId); - if (maybeRequest.isPresent()) { - requestIdsByDataCenter.get(entry.getKey()).add(maybeRequest.get().getRequest().getId()); - return entry.getValue(); - } - } - throw new DataCenterNotFoundException(String.format("Could not find requestId '%s' in any data center", requestId), isGetRequest ? 404 :500); - } - - Optional getMaybeDataCenterForRequest(String requestId, boolean isGetRequest) { - try { - return Optional.of(getDataCenterForRequest(requestId, isGetRequest)); - } catch (DataCenterNotFoundException nfe) { - return Optional.absent(); - } - } - - DataCenter getDataCenterForRequestGroup(String requestGroupId, boolean isGetRequest) { - for (Map.Entry entry : dataCenters.entrySet()) { - if (requestGroupsByDataCenter.get(entry.getKey()).contains(requestGroupId)) { - return entry.getValue(); - } - } - - // Not found, try each one - for (Map.Entry entry : dataCenters.entrySet()) { - SingularityClient client = clients.get(entry.getKey()); - Optional maybeRequestGroup = client.getRequestGroup(requestGroupId); - if (maybeRequestGroup.isPresent()) { - requestGroupsByDataCenter.get(entry.getKey()).add(maybeRequestGroup.get().getId()); - return entry.getValue(); - } - } - throw new DataCenterNotFoundException(String.format("Could not find requestGroupId '%s' in any data center", requestGroupId), isGetRequest ? 404 :500); - } - - DataCenter getDataCenterForSlaveId(String slaveId, boolean isGetRequest) { - for (Map.Entry entry : dataCenters.entrySet()) { - if (slaveIdsByDataCenter.get(entry.getKey()).contains(slaveId)) { - return entry.getValue(); - } - } - - // Not found, try each one - for (Map.Entry entry : dataCenters.entrySet()) { - SingularityClient client = clients.get(entry.getKey()); - Optional maybeSlave = client.getSlave(slaveId); - if (maybeSlave.isPresent()) { - slaveIdsByDataCenter.get(entry.getKey()).add(maybeSlave.get().getId()); - hostnamesByDataCenter.get(entry.getKey()).add(maybeSlave.get().getHost()); - rackIdsByDataCenter.get(entry.getKey()).add(maybeSlave.get().getRackId()); - return entry.getValue(); - } - } - throw new DataCenterNotFoundException(String.format("Could not find slaveId '%s' in any data center", slaveId), isGetRequest ? 404 :500); - } - - DataCenter getDataCenterForSlaveHostname(String hostname, boolean isGetRequest) { - for (Map.Entry entry : dataCenters.entrySet()) { - if (hostnamesByDataCenter.get(entry.getKey()).contains(hostname)) { - return entry.getValue(); - } - } - - // Not found, try each one - for (Map.Entry entry : dataCenters.entrySet()) { - SingularityClient client = clients.get(entry.getKey()); - Collection slaves = client.getSlaves(Optional.absent()); - for (SingularitySlave slave : slaves) { - if (slave.getHost().equals(hostname)) { - slaveIdsByDataCenter.get(entry.getKey()).add(slave.getId()); - hostnamesByDataCenter.get(entry.getKey()).add(slave.getHost()); - rackIdsByDataCenter.get(entry.getKey()).add(slave.getRackId()); - return entry.getValue(); - } - } - } - throw new DataCenterNotFoundException(String.format("Could not find slave with hostname '%s' in any data center", hostname), isGetRequest ? 404 :500); - } - - DataCenter getDataCenterForRackId(String rackId, boolean isGetRequest) { - for (Map.Entry entry : dataCenters.entrySet()) { - if (rackIdsByDataCenter.get(entry.getKey()).contains(rackId)) { - return entry.getValue(); - } - } - - // Not found, try each one - for (Map.Entry entry : dataCenters.entrySet()) { - SingularityClient client = clients.get(entry.getKey()); - Collection slaves = client.getSlaves(Optional.absent()); - for (SingularitySlave slave : slaves) { - if (slave.getRackId().equals(rackId)) { - slaveIdsByDataCenter.get(entry.getKey()).add(slave.getId()); - hostnamesByDataCenter.get(entry.getKey()).add(slave.getHost()); - rackIdsByDataCenter.get(entry.getKey()).add(slave.getRackId()); - return entry.getValue(); - } - } - } - throw new DataCenterNotFoundException(String.format("Could not find rack with id '%s' in any data center", rackId), isGetRequest ? 404 :500); - } - - DataCenter getDataCenter(String name) { - if (dataCenters.containsKey(name)) { - return dataCenters.get(name); - } else { - throw new DataCenterNotFoundException(String.format("No known data center with name: %s", name), 404); - } - } - - @Override - public void start() { - loadData(); - } - - private void loadData() { - configuration.getDataCenters().forEach((dc) -> { - SingularityClient singularityClient = clients.get(dc.getName()); - - Collection requestParents = singularityClient.getSingularityRequests(); - requestIdsByDataCenter.put(dc.getName(), requestParents.stream().map((r) -> r.getRequest().getId()).collect(Collectors.toSet())); - LOG.info("Loaded {} requests for data center {}", requestParents.size(), dc.getName()); - - Collection slaves = singularityClient.getSlaves(Optional.absent()); - Set rackIds = slaves.stream().map(SingularitySlave::getRackId).collect(Collectors.toSet()); - rackIdsByDataCenter.put(dc.getName(), new HashSet<>(rackIds)); - slaveIdsByDataCenter.put(dc.getName(), slaves.stream().map(SingularitySlave::getId).collect(Collectors.toSet())); - hostnamesByDataCenter.put(dc.getName(), slaves.stream().map(SingularitySlave::getHost).collect(Collectors.toSet())); - LOG.info("Loaded {} slaves for data center {}", slaves.size(), dc.getName()); - - Collection requestGroups = singularityClient.getRequestGroups(); - requestGroupsByDataCenter.put(dc.getName(), requestGroups.stream().map(SingularityRequestGroup::getId).collect(Collectors.toSet())); - LOG.info("Loaded {} request groups for data center {}", requestGroups.size(), dc.getName()); - }); - } - - @Override - public void stop() {} -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DeployResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DeployResource.java deleted file mode 100644 index 848ee9e376..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DeployResource.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityUpdatePendingDeployRequest; -import com.hubspot.singularity.api.SingularityDeployRequest; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.DEPLOY_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class DeployResource extends ProxyResource { - - @Inject - public DeployResource() {} - - @GET - @Path("/pending") - public Response getPendingDeploys(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @POST - @Consumes({ MediaType.APPLICATION_JSON }) - public Response deploy(@Context HttpServletRequest request, SingularityDeployRequest deployRequest) { - return routeByRequestId(request, deployRequest.getDeploy().getRequestId(), deployRequest); - } - - @DELETE - @Path("/deploy/{deployId}/request/{requestId}") - public Response cancelDeploy(@Context HttpServletRequest request, @PathParam("requestId") String requestId, @PathParam("deployId") String deployId) { - return routeByRequestId(request, requestId); - } - - @POST - @Path("/update") - public Response updatePendingDeploy(@Context HttpServletRequest request, SingularityUpdatePendingDeployRequest updateRequest) { - return routeByRequestId(request, updateRequest.getRequestId(), updateRequest); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DisastersResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DisastersResource.java deleted file mode 100644 index 49e8e3f836..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DisastersResource.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.hubspot.singularity.proxy; - -import java.util.List; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; - -import com.google.common.base.Optional; -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityAction; -import com.hubspot.singularity.SingularityDisabledAction; -import com.hubspot.singularity.SingularityDisasterType; -import com.hubspot.singularity.SingularityDisastersData; -import com.hubspot.singularity.SingularityTaskCredits; -import com.hubspot.singularity.api.SingularityDisabledActionRequest; -import com.hubspot.singularity.config.ApiPaths; -import com.hubspot.singularity.exceptions.NotImplemenedException; - -@Path(ApiPaths.DISASTERS_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class DisastersResource extends ProxyResource { - - @Inject - public DisastersResource() {} - - @GET - @Path("/stats") - public SingularityDisastersData disasterStats(@Context HttpServletRequest request) { - throw new NotImplemenedException(); - } - - @GET - @Path("/active") - public List activeDisasters(@Context HttpServletRequest request) { - throw new NotImplemenedException(); - } - - @POST - @Path("/disable") - public void disableAutomatedDisasterCreation(@Context HttpServletRequest request) { - throw new NotImplemenedException(); - } - - @POST - @Path("/enable") - public void enableAutomatedDisasterCreation(@Context HttpServletRequest request) { - throw new NotImplemenedException(); - } - - @DELETE - @Path("/active/{type}") - public void removeDisaster(@Context HttpServletRequest request, @PathParam("type") SingularityDisasterType type) { - throw new NotImplemenedException(); - } - - @POST - @Path("/active/{type}") - public void newDisaster(@Context HttpServletRequest request, @PathParam("type") SingularityDisasterType type) { - throw new NotImplemenedException(); - } - - @GET - @Path("/disabled-actions") - public List disabledActions(@Context HttpServletRequest request) { - throw new NotImplemenedException(); - } - - @POST - @Path("/disabled-actions/{action}") - public void disableAction(@Context HttpServletRequest request, @PathParam("action") SingularityAction action, SingularityDisabledActionRequest disabledActionRequest) { - throw new NotImplemenedException(); - } - - @DELETE - @Path("/disabled-actions/{action}") - public void enableAction(@Context HttpServletRequest request, @PathParam("action") SingularityAction action) { - throw new NotImplemenedException(); - } - - @POST - @Path("/task-credits") - public void addTaskCredits(@Context HttpServletRequest request, @QueryParam("credits") Optional credits) throws Exception { - throw new NotImplemenedException(); - } - - @DELETE - @Path("/task-credits") - public void disableTaskCredits(@Context HttpServletRequest request) throws Exception { - throw new NotImplemenedException(); - } - - @GET - @Path("/task-credits") - public SingularityTaskCredits getTaskCreditData(@Context HttpServletRequest request) throws Exception { - throw new NotImplemenedException(); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/HistoryResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/HistoryResource.java deleted file mode 100644 index 6e4897020a..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/HistoryResource.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.common.base.Optional; -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityTaskId; -import com.hubspot.singularity.config.ApiPaths; - -// Omits some or all @QueryParam annotated args, will be copied from request context -@Path(ApiPaths.HISTORY_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class HistoryResource extends ProxyResource { - - @Inject - public HistoryResource() {} - - @GET - @Path("/task/{taskId}") - public Response getHistoryForTask(@Context HttpServletRequest request, @PathParam("taskId") String taskId) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId()); - } - - - @GET - @Path("/request/{requestId}/tasks/active") - public Response getTaskHistoryForRequest(@Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/deploy/{deployId}") - public Response getDeploy(@Context HttpServletRequest request, @PathParam("requestId") String requestId, @PathParam("deployId") String deployId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/deploy/{deployId}/tasks/active") - public Response getActiveDeployTasks(@Context HttpServletRequest request, @PathParam("requestId") String requestId, @PathParam("deployId") String deployId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/deploy/{deployId}/tasks/inactive") - public Response getInactiveDeployTasks(@Context HttpServletRequest request, @PathParam("requestId") String requestId, @PathParam("deployId") String deployId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/deploy/{deployId}/tasks/inactive/withmetadata") - public Response getInactiveDeployTasksWithMetadata( - @Context HttpServletRequest request, @PathParam("requestId") String requestId, @PathParam("deployId") String deployId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/tasks") - public Response getTaskHistory( - @Context HttpServletRequest request, @QueryParam("requestId") Optional requestId, @QueryParam("deployId") Optional deployId, - @QueryParam("runId") Optional runId, @QueryParam("host") Optional host) { - // TODO - what if requestId not present? - return routeByRequestId(request, requestId.or("")); - } - - @GET - @Path("/tasks/withmetadata") - public Response getTaskHistoryWithMetadata( - @Context HttpServletRequest request, @QueryParam("requestId") Optional requestId, @QueryParam("deployId") Optional deployId, - @QueryParam("runId") Optional runId, @QueryParam("host") Optional host) { - // TODO - what if requestId not present? - return routeByRequestId(request, requestId.or("")); - } - - @GET - @Path("/request/{requestId}/tasks") - public Response getTaskHistoryForRequest( - @Context HttpServletRequest request, @PathParam("requestId") String requestId, @QueryParam("deployId") Optional deployId, - @QueryParam("runId") Optional runId, @QueryParam("host") Optional host) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/tasks/withmetadata") - public Response getTaskHistoryForRequestWithMetadata( - @Context HttpServletRequest request, @PathParam("requestId") String requestId, @QueryParam("deployId") Optional deployId, - @QueryParam("runId") Optional runId, @QueryParam("host") Optional host) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/run/{runId}") - public Response getTaskHistoryForRequestAndRunId( - @Context HttpServletRequest request, @PathParam("requestId") String requestId, @PathParam("runId") String runId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/deploys") - public Response getDeploys( - @Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/deploys/withmetadata") - public Response getDeploysWithMetadata( - @Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/requests") - public Response getRequestHistoryForRequest( - @Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/requests/withmetadata") - public Response getRequestHistoryForRequestWithMetadata( - @Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/requests/search") - public Response getRequestHistoryForRequestLike(@Context HttpServletRequest request, @QueryParam("requestIdLike") String requestIdLike) { - return getMergedListResult(request); - } - - @GET - @Path("/request/{requestId}/command-line-args") - public Response getRecentCommandLineArgs(@Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return getMergedListResult(request); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/InactiveSlaveResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/InactiveSlaveResource.java deleted file mode 100644 index f98a121f8d..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/InactiveSlaveResource.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.INACTIVE_SLAVES_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class InactiveSlaveResource extends ProxyResource { - - @Inject - public InactiveSlaveResource() {} - - @GET - public Response getInactiveSlaves(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @POST - public Response deactivateSlave(@Context HttpServletRequest request, @QueryParam("host") String host) { - return routeByHostname(request, host); - } - - @DELETE - public Response reactivateSlave(@Context HttpServletRequest request, @QueryParam("host") String host) { - return routeByHostname(request, host); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/PriorityResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/PriorityResource.java deleted file mode 100644 index 74d5b97917..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/PriorityResource.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; - -import com.google.common.base.Optional; -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityPriorityFreezeParent; -import com.hubspot.singularity.api.SingularityPriorityFreeze; -import com.hubspot.singularity.config.ApiPaths; -import com.hubspot.singularity.exceptions.NotImplemenedException; - -@Path(ApiPaths.PRIORITY_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class PriorityResource extends ProxyResource { - - @Inject - public PriorityResource() {} - - @GET - @Path("/freeze") - public Optional getActivePriorityFreeze(@Context HttpServletRequest request) { - throw new NotImplemenedException(); - } - - @DELETE - @Path("/freeze") - public void deleteActivePriorityFreeze(@Context HttpServletRequest request) { - throw new NotImplemenedException(); - } - - @POST - @Path("/freeze") - public SingularityPriorityFreezeParent createPriorityFreeze(@Context HttpServletRequest request, SingularityPriorityFreeze priorityFreezeRequest) { - throw new NotImplemenedException(); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/ProxyResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/ProxyResource.java deleted file mode 100644 index 7b6db2c99f..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/ProxyResource.java +++ /dev/null @@ -1,323 +0,0 @@ -package com.hubspot.singularity.proxy; - -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.ResponseBuilder; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.base.Throwables; -import com.google.common.collect.Lists; -import com.google.inject.Inject; -import com.google.inject.name.Named; -import com.hubspot.horizon.AsyncHttpClient; -import com.hubspot.horizon.Header; -import com.hubspot.horizon.HttpRequest; -import com.hubspot.horizon.HttpRequest.Method; -import com.hubspot.horizon.HttpResponse; -import com.hubspot.singularity.config.ApiPaths; -import com.hubspot.singularity.config.ClusterCoordinatorConfiguration; -import com.hubspot.singularity.config.DataCenter; - -import io.dropwizard.server.SimpleServerFactory; - -public class ProxyResource { - private static final Logger LOG = LoggerFactory.getLogger(ProxyResource.class); - - private AsyncHttpClient httpClient; - protected ClusterCoordinatorConfiguration configuration; - private ObjectMapper objectMapper; - protected DataCenterLocator dataCenterLocator; - private String contextPath; - - public ProxyResource() {} - - @Inject - void injectProxyDeps(ClusterCoordinatorConfiguration configuration, - @Named(SingularityClusterCoodinatorResourcesModule.ASYNC_HTTP_CLIENT) AsyncHttpClient httpClient, - ObjectMapper objectMapper, - DataCenterLocator dataCenterLocator) { - this.configuration = configuration; - this.httpClient = httpClient; - this.objectMapper = objectMapper; - this.dataCenterLocator = dataCenterLocator; - String baseContextPath = ((SimpleServerFactory) configuration.getServerFactory()).getApplicationContextPath(); - if (baseContextPath.startsWith("/")) { - baseContextPath = baseContextPath.substring(1, baseContextPath.length()); - } - if (baseContextPath.endsWith("/")) { - baseContextPath = baseContextPath.substring(0, baseContextPath.length() -1); - } - this.contextPath = baseContextPath + ApiPaths.API_BASE_PATH; - } - - /* - * For items where the dataCenter is part of the object, or where a full list is desired. - * Collect and merge results from each configured dataCenter - */ - public Response getMergedListResult(HttpServletRequest request) { - return getMergedListResult(request, null); - } - - public Response getMergedListResult(HttpServletRequest request, T body) { - List headers = getHeaders(request); - List params = getParams(request); - - // TODO - parallelize - List combined = Lists.newArrayList(); - ResponseBuilder builder = null; - - for (DataCenter dataCenter : configuration.getDataCenters()) { - try { - HttpResponse response = proxyAndGetResponse(dataCenter, request, body, headers, params); - if (builder == null) { - builder = Response.status(response.getStatusCode()); - for (Header header : response.getHeaders()) { - builder.header(header.getName(), header.getValue()); - } - } - - List content = response.getAs(new TypeReference>() {}); - LOG.trace("Data center {} had response {}", dataCenter.getName(), response.getStatusCode()); - - combined.addAll(content); - } catch (RuntimeException re) { - LOG.error("Could not get data from dataCenter {}, omitting", dataCenter.getName(), re); - } - } - - if (builder != null) { - return builder.entity(combined).build(); - } else { - throw new WebApplicationException("Got no results", 500); - } - } - - /* - * Route a request to a particular dataCenter using the requestId to locate the correct Singularity cluster - */ - public Response routeByRequestId(HttpServletRequest request, String requestId) { - return routeByRequestId(request, requestId, null); - } - - public Response routeByRequestId(HttpServletRequest request, String requestId, T body) { - List headers = getHeaders(request); - List params = getParams(request); - - DataCenter dataCenter = getDataCenterForRequest(requestId, request.getMethod().toUpperCase().equals("GET")); - - return toResponse(proxyAndGetResponse(dataCenter, request, body, headers, params)); - } - - /* - * Route a request to a particular dataCenter using the request group Id to locate the correct Singularity cluster - */ - Response routeByRequestGroupId(HttpServletRequest request, String requestGroupId) { - List headers = getHeaders(request); - List params = getParams(request); - - DataCenter dataCenter = getDataCenterForRequestGroup(requestGroupId, request.getMethod().toUpperCase().equals("GET")); - - return toResponse(proxyAndGetResponse(dataCenter, request, null, headers, params)); - } - - /* - * Route a request to a particular dataCenter using the slaveId/hostname to locate the correct Singularity cluster - */ - Response routeBySlaveId(HttpServletRequest request, String slaveId) { - return routeBySlaveId(request, slaveId, null); - } - - Response routeBySlaveId(HttpServletRequest request, String slaveId, T body) { - List headers = getHeaders(request); - List params = getParams(request); - - DataCenter dataCenter = getDataCenterForSlaveId(slaveId, request.getMethod().toUpperCase().equals("GET")); - - return toResponse(proxyAndGetResponse(dataCenter, request, body, headers, params)); - } - - Response routeByHostname(HttpServletRequest request, String hostname) { - List headers = getHeaders(request); - List params = getParams(request); - - DataCenter dataCenter = getDataCenterForSlaveHostname(hostname, request.getMethod().toUpperCase().equals("GET")); - - return toResponse(proxyAndGetResponse(dataCenter, request, null, headers, params)); - } - - /* - * Route a request to a particular dataCenter using the rack ID to locate the correct Singularity cluster - */ - Response routeByRackId(HttpServletRequest request, String rackId) { - return routeByRackId(request, rackId, null); - } - - Response routeByRackId(HttpServletRequest request, String rackId, T body) { - List headers = getHeaders(request); - List params = getParams(request); - - DataCenter dataCenter = getDataCenterForRackId(rackId, request.getMethod().toUpperCase().equals("GET")); - - return toResponse(proxyAndGetResponse(dataCenter, request, body, headers, params)); - } - - /* - * Route a request to a particular dataCenter by name, failing if it is not present - */ - Response routeByDataCenter(HttpServletRequest request, String dataCenterName, T body) { - List headers = getHeaders(request); - List params = getParams(request); - - DataCenter dataCenter = getDataCenter(dataCenterName); - - return toResponse(proxyAndGetResponse(dataCenter, request, body, headers, params)); - } - - /* - * Route to the default Singularity cluster - */ - Response routeToDefaultDataCenter(HttpServletRequest request) { - return routeToDefaultDataCenter(request, null); - } - - Response routeToDefaultDataCenter(HttpServletRequest request, T body) { - List headers = getHeaders(request); - List params = getParams(request); - - DataCenter dataCenter = configuration.getDataCenters().get(0); - - return toResponse(proxyAndGetResponse(dataCenter, request, body, headers, params)); - } - - /* - * Working with data centers - */ - private String getHost(DataCenter dataCenter) { - return dataCenterLocator.getHost(dataCenter); - } - - private DataCenter getDataCenterForRequest(String requestId, boolean isGetRequest) { - return dataCenterLocator.getDataCenterForRequest(requestId, isGetRequest); - } - - private DataCenter getDataCenterForRequestGroup(String requestGroupId, boolean isGetRequest) { - return dataCenterLocator.getDataCenterForRequestGroup(requestGroupId, isGetRequest); - } - - private DataCenter getDataCenterForSlaveId(String slaveId, boolean isGetRequest) { - return dataCenterLocator.getDataCenterForSlaveId(slaveId, isGetRequest); - } - - private DataCenter getDataCenterForSlaveHostname(String hostname, boolean isGetRequest) { - return dataCenterLocator.getDataCenterForSlaveHostname(hostname, isGetRequest); - } - - private DataCenter getDataCenterForRackId(String rackId, boolean isGetRequest) { - return dataCenterLocator.getDataCenterForRackId(rackId, isGetRequest); - } - - private DataCenter getDataCenter(String name) { - return dataCenterLocator.getDataCenter(name); - } - - /* - * Generic methods for proxying requests - */ - private HttpResponse proxyAndGetResponse(DataCenter dc, HttpServletRequest request, T body, List headers, List params) { - String fullPath = request.getContextPath() + request.getPathInfo(); - String url = String.format("%s://%s%s", dc.getScheme(), getHost(dc), fullPath.replace(contextPath, dc.getContextPath())); - - LOG.debug("Proxying {} {} to: ({}) {}", request.getMethod(), fullPath, dc.getName(), url); - HttpRequest.Builder requestBuilder = HttpRequest.newBuilder() - .setMethod(Method.valueOf(request.getMethod())) - .setUrl(url); - if (body != null) { - requestBuilder.setBody(body); - LOG.trace("Added body {} to request", body); - } else if (!request.getMethod().equals("GET")){ - requestBuilder.setBody(new byte[]{}); - } - headers.forEach((h) -> requestBuilder.addHeader(h.getKey(), h.getValue())); - params.forEach((h) -> requestBuilder.setQueryParam(h.getKey()).to(h.getValue())); - - try { - return httpClient.execute(requestBuilder.build()).get(); - } catch (InterruptedException|ExecutionException ioe) { - if (Throwables.getRootCause(ioe) instanceof TimeoutException) { - throw new WebApplicationException(ioe, 503); - } else { - throw new WebApplicationException(ioe); - } - } - } - - private List getHeaders(HttpServletRequest request) { - List headers = new ArrayList<>(); - Enumeration headerNames = request.getHeaderNames(); - if (headerNames != null) { - while (headerNames.hasMoreElements()) { - String headerName = headerNames.nextElement(); - headers.add(new Param(headerName, request.getHeader(headerName))); - } - } - LOG.trace("Found headers: {}", headers); - return headers; - } - - private List getParams(HttpServletRequest request) { - List params = new ArrayList<>(); - for (Map.Entry entry : request.getParameterMap().entrySet()) { - for (String value : entry.getValue()) { - params.add(new Param(entry.getKey(), value)); - } - } - LOG.trace("Found params {}", params); - return params; - } - - private Response toResponse(HttpResponse original) { - LOG.trace("Got response {}", original.getStatusCode()); - ResponseBuilder builder = Response.status(original.getStatusCode()) - .entity(original.getAsString()); - original.getHeaders().forEach((h) -> builder.header(h.getName(), h.getValue())); - return builder.build(); - } - - private static class Param { - private final String key; - private final String value; - - Param(String key, String value) { - this.key = key; - this.value = value; - } - - String getKey() { - return key; - } - - String getValue() { - return value; - } - - @Override - public String toString() { - return "Param{" + - "key='" + key + '\'' + - ", value='" + value + '\'' + - '}'; - } - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RackResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RackResource.java deleted file mode 100644 index 69a291317f..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RackResource.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.api.SingularityMachineChangeRequest; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.RACK_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class RackResource extends ProxyResource { - - @Inject - public RackResource() {} - - @GET - @Path("/") - public Response getRacks(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/rack/{rackId}") - public Response getRackHistory(@Context HttpServletRequest request, @PathParam("rackId") String rackId) { - return routeByRackId(request, rackId); - } - - @DELETE - @Path("/rack/{rackId}") - public Response removeRack(@Context HttpServletRequest request, @PathParam("rackId") String rackId) { - // TODO - remove from internal list as well? - return routeByRackId(request, rackId); - } - - @POST - @Path("/rack/{rackId}/decommission") - public Response decommissionRack(@Context HttpServletRequest request, @PathParam("rackId") String rackId, SingularityMachineChangeRequest changeRequest) { - return routeByRackId(request, rackId); - } - - @POST - @Path("/rack/{rackId}/freeze") - public Response freezeRack(@Context HttpServletRequest request, @PathParam("rackId") String rackId, SingularityMachineChangeRequest changeRequest) { - return routeByRackId(request, rackId, changeRequest); - } - - @POST - @Path("/rack/{rackId}/activate") - public Response activateRack(@Context HttpServletRequest request, @PathParam("rackId") String rackId, SingularityMachineChangeRequest changeRequest) { - return routeByRackId(request, rackId, changeRequest); - } - - @DELETE - @Path("/rack/{rackId}/expiring") - public Response deleteExpiringStateChange(@Context HttpServletRequest request, @PathParam("rackId") String rackId) { - return routeByRackId(request, rackId); - } - - @GET - @Path("/expiring") - public Response getExpiringStateChanges(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RequestGroupResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RequestGroupResource.java deleted file mode 100644 index 23194a58da..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RequestGroupResource.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityRequestGroup; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.REQUEST_GROUP_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class RequestGroupResource extends ProxyResource { - - @Inject - public RequestGroupResource() {} - - @GET - public Response getRequestGroupIds(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/group/{requestGroupId}") - public Response getRequestGroup(@Context HttpServletRequest request, @PathParam("requestGroupId") String requestGroupId) { - return routeByRequestGroupId(request, requestGroupId); - } - - @DELETE - @Path("/group/{requestGroupId}") - public Response deleteRequestGroup(@Context HttpServletRequest request, @PathParam("requestGroupId") String requestGroupId) { - return routeByRequestGroupId(request, requestGroupId); - } - - @POST - public Response saveRequestGroup(@Context HttpServletRequest request, SingularityRequestGroup requestGroup) { - // TODO - route by more than first request id? - // TODO - add to internal list of groups? - // TODO - error if list is empty? - return routeByRequestId(request, requestGroup.getRequestIds().get(0), requestGroup); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RequestResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RequestResource.java deleted file mode 100644 index 79a663791d..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RequestResource.java +++ /dev/null @@ -1,272 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Optional; -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityRequest; -import com.hubspot.singularity.api.SingularityBounceRequest; -import com.hubspot.singularity.api.SingularityDeleteRequestRequest; -import com.hubspot.singularity.api.SingularityExitCooldownRequest; -import com.hubspot.singularity.api.SingularityPauseRequest; -import com.hubspot.singularity.api.SingularityRunNowRequest; -import com.hubspot.singularity.api.SingularityScaleRequest; -import com.hubspot.singularity.api.SingularitySkipHealthchecksRequest; -import com.hubspot.singularity.api.SingularityUnpauseRequest; -import com.hubspot.singularity.config.ApiPaths; -import com.hubspot.singularity.config.DataCenter; -import com.hubspot.singularity.exceptions.DataCenterConflictException; -import com.hubspot.singularity.exceptions.DataCenterNotFoundException; - -@Path(ApiPaths.REQUEST_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class RequestResource extends ProxyResource { - private static final Logger LOG = LoggerFactory.getLogger(RequestResource.class); - - @Inject - public RequestResource() {} - - @POST - @Consumes({ MediaType.APPLICATION_JSON }) - public Response postRequest(@Context HttpServletRequest requestContext, SingularityRequest request) { - Optional maybeDataCenter = dataCenterLocator.getMaybeDataCenterForRequest(request.getId(), false); - if (maybeDataCenter.isPresent()) { - LOG.trace("Found data center {} for request {}", maybeDataCenter.get().getName(), request.getId()); - if (request.getDataCenter().isPresent() && !request.getDataCenter().get().equals(maybeDataCenter.get().getName())) { - throw new DataCenterConflictException(String.format("Cannot create request with id %s in multiple datacenters (requested: %s), already found in %s", request.getId(), request.getDataCenter().get(), maybeDataCenter.get().getName())); - } - return routeByDataCenter(requestContext, maybeDataCenter.get().getName(), request); - } - - LOG.trace("Check request {}", request); - if (request.getDataCenter().isPresent()) { - return routeByDataCenter(requestContext, request.getDataCenter().get(), request); - } - if (configuration.isErrorOnDataCenterNotSpecified()) { - throw new DataCenterNotFoundException(String.format("No data center specified in request %s, and no existing request found in any data center", request.getId()), 500); - } else { - return routeToDefaultDataCenter(requestContext, request); - } - } - - @POST - @Path("/request/{requestId}/bounce") - public Response bounce(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext) { - return bounce(requestId, requestContext, null); - } - - @POST - @Path("/request/{requestId}/bounce") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response bounce(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext, - SingularityBounceRequest bounceRequest) { - return routeByRequestId(requestContext, requestId, bounceRequest); - } - - @POST - @Path("/request/{requestId}/run") - public Response scheduleImmediately(@Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return scheduleImmediately(request, requestId, null); - } - - @POST - @Path("/request/{requestId}/run") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response scheduleImmediately(@Context HttpServletRequest requestContext, @PathParam("requestId") String requestId, - SingularityRunNowRequest runNowRequest) { - return routeByRequestId(requestContext, requestId, runNowRequest); - } - - @GET - @Path("/request/{requestId}/run/{runId}") - public Response getTaskByRunId(@Context HttpServletRequest request, @PathParam("requestId") String requestId, @PathParam("runId") String runId) { - return routeByRequestId(request, requestId); - } - - @POST - @Path("/request/{requestId}/pause") - public Response pause(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext) { - return pause(requestId, requestContext, null); - } - - @POST - @Path("/request/{requestId}/pause") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response pause(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext, - SingularityPauseRequest pauseRequest) { - return routeByRequestId(requestContext, requestId, pauseRequest); - } - - @POST - @Path("/request/{requestId}/unpause") - public Response unpauseNoBody(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext) { - return unpause(requestId, requestContext, null); - } - - @POST - @Path("/request/{requestId}/unpause") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response unpause(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext, - SingularityUnpauseRequest unpauseRequest) { - return routeByRequestId(requestContext, requestId, unpauseRequest); - } - - @POST - @Path("/request/{requestId}/exit-cooldown") - public Response exitCooldown(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext) { - return exitCooldown(requestId, requestContext, null); - } - - @POST - @Path("/request/{requestId}/exit-cooldown") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response exitCooldown(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext, - SingularityExitCooldownRequest exitCooldownRequest) { - return routeByRequestId(requestContext, requestId, exitCooldownRequest); - } - - @GET - @Path("/active") - public Response getActiveRequests(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/paused") - public Response getPausedRequests(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/cooldown") - public Response getCooldownRequests(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/finished") - public Response getFinishedRequests(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - public Response getRequests(@Context HttpServletRequest request) { - // TODO - update internal cache list? - return getMergedListResult(request); - } - - @GET - @Path("/queued/pending") - public Response getPendingRequests(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/queued/cleanup") - public Response getCleanupRequests(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/request/{requestId}") - public Response getRequest(@Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @DELETE - @Path("/request/{requestId}") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response deleteRequest(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext, - SingularityDeleteRequestRequest deleteRequest) { - // TODO - update internal list? - return routeByRequestId(requestContext, requestId, deleteRequest); - } - - @PUT - @Path("/request/{requestId}/scale") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response scale(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext, - SingularityScaleRequest scaleRequest) { - return routeByRequestId(requestContext, requestId, scaleRequest); - } - - @DELETE - @Path("/request/{requestId}/scale") - public Response deleteExpiringScale(@Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @Deprecated - @DELETE - @Path("/request/{requestId}/skipHealthchecks") - public Response deleteExpiringSkipHealthchecksDeprecated(@Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @DELETE - @Path("/request/{requestId}/skip-healthchecks") - public Response deleteExpiringSkipHealthchecks(@Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @DELETE - @Path("/request/{requestId}/pause") - public Response deleteExpiringPause(@Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @DELETE - @Path("/request/{requestId}/bounce") - public Response deleteExpiringBounce(@Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @Deprecated - @PUT - @Path("/request/{requestId}/skipHealthchecks") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response skipHealthchecksDeprecated(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext, - SingularitySkipHealthchecksRequest skipHealthchecksRequest) { - return skipHealthchecks(requestId, requestContext, skipHealthchecksRequest); - } - - @PUT - @Path("/request/{requestId}/skip-healthchecks") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response skipHealthchecks(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext, - SingularitySkipHealthchecksRequest skipHealthchecksRequest) { - return routeByRequestId(requestContext, requestId, skipHealthchecksRequest); - } - - @GET - @Path("/lbcleanup") - public Response getLbCleanupRequests(@Context HttpServletRequest request) { - return getMergedListResult(request); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/S3LogResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/S3LogResource.java deleted file mode 100644 index 82ff94c3d4..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/S3LogResource.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityTaskId; -import com.hubspot.singularity.api.SingularityS3SearchRequest; -import com.hubspot.singularity.config.ApiPaths; -import com.hubspot.singularity.exceptions.NotImplemenedException; - -@Path(ApiPaths.S3_LOG_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class S3LogResource extends ProxyResource { - - @Inject - public S3LogResource() {} - - @GET - @Path("/task/{taskId}") - public Response getS3LogsForTask(@Context HttpServletRequest request, @PathParam("taskId") String taskId) throws Exception { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId()); - } - - @GET - @Path("/request/{requestId}") - public Response getS3LogsForRequest(@Context HttpServletRequest request, @PathParam("requestId") String requestId) throws Exception { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/deploy/{deployId}") - public Response getS3LogsForDeploy(@Context HttpServletRequest request, @PathParam("requestId") String requestId, @PathParam("deployId") String deployId) throws Exception { - return routeByRequestId(request, requestId); - } - - @POST - @Path("/search") - @Consumes(MediaType.APPLICATION_JSON) - public Response getPaginatedS3Logs(@Context HttpServletRequest request, SingularityS3SearchRequest search) throws Exception { - // TODO - merge search results from multiple data centers, route if request id set - throw new NotImplemenedException(); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SandboxResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SandboxResource.java deleted file mode 100644 index 1b5349b03e..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SandboxResource.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityTaskId; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.SANDBOX_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class SandboxResource extends ProxyResource { - - @Inject - public SandboxResource() {} - - @GET - @Path("/{taskId}/browse") - public Response browse(@Context HttpServletRequest request, @PathParam("taskId") String taskId) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId()); - } - - @GET - @Path("/{taskId}/read") - public Response read(@Context HttpServletRequest request, @PathParam("taskId") String taskId) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId()); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SingularityClusterCoodinatorResourcesModule.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SingularityClusterCoodinatorResourcesModule.java deleted file mode 100644 index 94e6b59010..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SingularityClusterCoodinatorResourcesModule.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.hubspot.singularity.proxy; - -import java.util.HashMap; -import java.util.Map; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.base.Optional; -import com.google.inject.AbstractModule; -import com.google.inject.Provides; -import com.google.inject.Scopes; -import com.google.inject.Singleton; -import com.google.inject.name.Named; -import com.hubspot.horizon.AsyncHttpClient; -import com.hubspot.horizon.HttpClient; -import com.hubspot.horizon.HttpConfig; -import com.hubspot.horizon.ning.NingAsyncHttpClient; -import com.hubspot.horizon.ning.NingHttpClient; -import com.hubspot.singularity.SingularityClientCredentials; -import com.hubspot.singularity.SingularityServiceBaseModule; -import com.hubspot.singularity.client.SingularityClient; -import com.hubspot.singularity.client.SingularityClientProvider; -import com.hubspot.singularity.config.ClusterCoordinatorConfiguration; -import com.hubspot.singularity.config.IndexViewConfiguration; - -import io.dropwizard.server.SimpleServerFactory; - -public class SingularityClusterCoodinatorResourcesModule extends AbstractModule { - public static final String ASYNC_HTTP_CLIENT = "singularity.async.http.client"; - - private final ClusterCoordinatorConfiguration configuration; - - public SingularityClusterCoodinatorResourcesModule(ClusterCoordinatorConfiguration configuration) { - this.configuration = configuration; - } - - @Override - public void configure() { - bind(DataCenterLocator.class).in(Scopes.SINGLETON); - - bind(DeployResource.class); - bind(HistoryResource.class); - bind(RackResource.class); - bind(RequestResource.class); - bind(S3LogResource.class); - bind(SandboxResource.class); - bind(SlaveResource.class); - bind(StateResource.class); - bind(TaskResource.class); - bind(WebhookResource.class); - bind(AuthResource.class); - bind(UserResource.class); - bind(DisastersResource.class); - bind(PriorityResource.class); - bind(UsageResource.class); - bind(RequestGroupResource.class); - bind(InactiveSlaveResource.class); - bind(TaskTrackerResource.class); - - install(new SingularityServiceBaseModule(configuration.getUiConfiguration())); - } - - @Provides - @Singleton - public IndexViewConfiguration provideIndexViewConfiguration() { - return new IndexViewConfiguration( - configuration.getUiConfiguration(), - configuration.getDefaultMemory(), - configuration.getDefaultCpus(), - configuration.getSlaveHttpPort(), - configuration.getSlaveHttpsPort(), - configuration.getBounceExpirationMinutes(), - configuration.getHealthcheckIntervalSeconds(), - configuration.getHealthcheckTimeoutSeconds(), - configuration.getHealthcheckMaxRetries(), - configuration.getStartupTimeoutSeconds(), - configuration.isLoadBalancingEnabled(), - configuration.getCommonHostnameSuffixToOmit(), - configuration.getWarnIfScheduledJobIsRunningPastNextRunPct() - ); - } - - @Provides - @Named(SingularityServiceBaseModule.SINGULARITY_URI_BASE) - public String getSingularityUriBase() { - final String singularityUiPrefix = configuration.getUiConfiguration().getBaseUrl().or(((SimpleServerFactory) configuration.getServerFactory()).getApplicationContextPath()); - return (singularityUiPrefix.endsWith("/")) ? singularityUiPrefix.substring(0, singularityUiPrefix.length() - 1) : singularityUiPrefix; - } - - @Provides - @Singleton - @Named(ASYNC_HTTP_CLIENT) - public AsyncHttpClient provideAsyncHttpClient(ObjectMapper objectMapper) { - return new NingAsyncHttpClient(HttpConfig.newBuilder().setObjectMapper(objectMapper).build()); - } - - @Provides - @Singleton - public Map provideClients(ObjectMapper objectMapper) { - HttpClient httpClient = new NingHttpClient(HttpConfig.newBuilder().setObjectMapper(objectMapper).build()); - SingularityClientProvider clientProvider = new SingularityClientProvider(httpClient); - Map clients = new HashMap<>(); - configuration.getDataCenters().forEach((dc) -> { - clientProvider.setHosts(dc.getHosts()); - clientProvider.setContextPath(dc.getContextPath()); - clientProvider.setSsl(dc.getScheme().equals("https")); - Optional maybeCredentials = dc.getClientCredentials().or(configuration.getDefaultClientCredentials()); - clients.put(dc.getName(), clientProvider.get(maybeCredentials)); - }); - return clients; - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SlaveResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SlaveResource.java deleted file mode 100644 index c485af5dc7..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SlaveResource.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.api.SingularityMachineChangeRequest; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.SLAVE_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class SlaveResource extends ProxyResource { - - @Inject - public SlaveResource() {} - - @GET - public Response getSlaves(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/slave/{slaveId}") - public Response getSlaveHistory(@Context HttpServletRequest request, @PathParam("slaveId") String slaveId) { - return routeBySlaveId(request, slaveId); - } - - @GET - @Path("/slave/{slaveId}/details") - public Response getSlave(@Context HttpServletRequest request, @PathParam("slaveId") String slaveId) { - return routeBySlaveId(request, slaveId); - } - - @DELETE - @Path("/slave/{slaveId}") - public Response removeSlave(@Context HttpServletRequest request, @PathParam("slaveId") String slaveId) { - return routeBySlaveId(request, slaveId); - } - - @POST - @Path("/slave/{slaveId}/decommission") - public Response decommissionSlave(@Context HttpServletRequest request, @PathParam("slaveId") String slaveId, SingularityMachineChangeRequest changeRequest) { - return routeBySlaveId(request, slaveId, changeRequest); - } - - @POST - @Path("/slave/{slaveId}/freeze") - public Response freezeSlave(@Context HttpServletRequest request, @PathParam("slaveId") String slaveId, SingularityMachineChangeRequest changeRequest) { - return routeBySlaveId(request, slaveId, changeRequest); - } - - @POST - @Path("/slave/{slaveId}/activate") - public Response activateSlave(@Context HttpServletRequest request, @PathParam("slaveId") String slaveId, SingularityMachineChangeRequest changeRequest) { - return routeBySlaveId(request, slaveId, changeRequest); - } - - @DELETE - @Path("/slave/{slaveId}/expiring") - public Response deleteExpiringStateChange(@Context HttpServletRequest request, @PathParam("slaveId") String slaveId) { - return routeBySlaveId(request, slaveId); - } - - @GET - @Path("/expiring") - public Response getExpiringStateChanges(@Context HttpServletRequest request) { - return getMergedListResult(request); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/StateResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/StateResource.java deleted file mode 100644 index d0fd72ccbb..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/StateResource.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityState; -import com.hubspot.singularity.config.ApiPaths; -import com.hubspot.singularity.exceptions.NotImplemenedException; - -@Path(ApiPaths.STATE_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class StateResource extends ProxyResource { - - @Inject - public StateResource() {} - - @GET - public SingularityState getState(@Context HttpServletRequest request) { - // TODO - merge this result? - throw new NotImplemenedException(); - } - - @GET - @Path("/requests/under-provisioned") - public Response getUnderProvisionedRequestIds(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/requests/over-provisioned") - public Response getOverProvisionedRequestIds(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/task-reconciliation") - public Response getTaskReconciliationStatistics(@Context HttpServletRequest request) { - // TODO - merge this result? - throw new NotImplemenedException(); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/TaskResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/TaskResource.java deleted file mode 100644 index 60a807da6e..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/TaskResource.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityPendingTaskId; -import com.hubspot.singularity.SingularityShellCommand; -import com.hubspot.singularity.SingularityTaskId; -import com.hubspot.singularity.api.SingularityKillTaskRequest; -import com.hubspot.singularity.api.SingularityTaskMetadataRequest; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.TASK_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class TaskResource extends ProxyResource { - - @Inject - public TaskResource() {} - - @GET - @Path("/scheduled") - public Response getScheduledTasks(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/scheduled/ids") - public Response getScheduledTaskIds(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/scheduled/task/{pendingTaskId}") - public Response getPendingTask(@Context HttpServletRequest request, @PathParam("pendingTaskId") String pendingTaskIdStr) { - SingularityPendingTaskId parsedId = SingularityPendingTaskId.valueOf(pendingTaskIdStr); - return routeByRequestId(request, parsedId.getRequestId()); - } - - @GET - @Path("/scheduled/request/{requestId}") - public Response getScheduledTasksForRequest(@Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/active/slave/{slaveId}") - public Response getTasksForSlave(@Context HttpServletRequest request, @PathParam("slaveId") String slaveId) { - return routeBySlaveId(request, slaveId); - } - - @GET - @Path("/active") - public Response getActiveTasks(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/cleaning") - public Response getCleaningTasks(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/killed") - public Response getKilledTasks(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/lbcleanup") - public Response getLbCleanupTasks(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/task/{taskId}") - public Response getActiveTask(@Context HttpServletRequest request, @PathParam("taskId") String taskId) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId()); - } - - @GET - @Path("/task/{taskId}/statistics") - public Response getTaskStatistics(@Context HttpServletRequest request, @PathParam("taskId") String taskId) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId()); - } - - @GET - @Path("/task/{taskId}/cleanup") - public Response getTaskCleanup(@Context HttpServletRequest request, @PathParam("taskId") String taskId) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId()); - } - - @DELETE - @Path("/task/{taskId}") - public Response killTask(@Context HttpServletRequest request, @PathParam("taskId") String taskId, @Context HttpServletRequest requestContext) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId()); - } - - @DELETE - @Path("/task/{taskId}") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response killTask(@PathParam("taskId") String taskId, - @Context HttpServletRequest requestContext, - SingularityKillTaskRequest killTaskRequest) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(requestContext, parsedId.getRequestId(), killTaskRequest); - } - - @GET - @Path("/commands/queued") - public Response getQueuedShellCommands(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @POST - @Path("/task/{taskId}/metadata") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response postTaskMetadata(@Context HttpServletRequest request, @PathParam("taskId") String taskId, final SingularityTaskMetadataRequest taskMetadataRequest) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId(), taskMetadataRequest); - } - - @POST - @Path("/task/{taskId}/command") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response runShellCommand(@Context HttpServletRequest request, @PathParam("taskId") String taskId, final SingularityShellCommand shellCommand) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId(), shellCommand); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/TaskTrackerResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/TaskTrackerResource.java deleted file mode 100644 index 1b64d16372..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/TaskTrackerResource.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityTaskId; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.TASK_TRACKER_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class TaskTrackerResource extends ProxyResource { - - @Inject - public TaskTrackerResource() {} - - @GET - @Path("/task/{taskId}") - public Response getTaskState(@Context HttpServletRequest request, @PathParam("taskId") String taskId) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId()); - } - - @GET - @Path("/run/{requestId}/{runId}") - public Response getTaskStateByRunId(@Context HttpServletRequest request, @PathParam("requestId") String requestId, @PathParam("runId") String runId) { - return routeByRequestId(request, requestId); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/UsageResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/UsageResource.java deleted file mode 100644 index eb6516b474..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/UsageResource.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityTaskId; - -@Path("/api/usage") -public class UsageResource extends ProxyResource { - - @Inject - public UsageResource() {} - - @GET - @Path("/slaves") - public Response getSlavesWithUsage(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/slaves/{slaveId}/tasks/current") - public Response getSlaveCurrentTaskUsage(@Context HttpServletRequest request, @PathParam("slaveId") String slaveId) { - return routeBySlaveId(request, slaveId); - } - - @GET - @Path("/slaves/{slaveId}/history") - public Response getSlaveUsageHistory(@Context HttpServletRequest request, @PathParam("slaveId") String slaveId) { - return routeBySlaveId(request, slaveId); - } - - @GET - @Path("/tasks/{taskId}/history") - public Response getTaskUsageHistory(@Context HttpServletRequest request, @PathParam("taskId") String taskId) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId()); - } - -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/UserResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/UserResource.java deleted file mode 100644 index 9f21e27bb2..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/UserResource.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityUserSettings; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.USER_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class UserResource extends ProxyResource { - // TODO - replicate to all data centers? - - @Inject - public UserResource() {} - - @GET - @Path("/settings") - public Response getUserSettings(@Context HttpServletRequest request) { - return routeToDefaultDataCenter(request); - } - - @POST - @Path("/settings") - public Response setUserSettings(@Context HttpServletRequest request, SingularityUserSettings settings) { - return routeToDefaultDataCenter(request, settings); - } - - @POST - @Path("/settings/starred-requests") - public Response addStarredRequests(@Context HttpServletRequest request, SingularityUserSettings settings) { - return routeToDefaultDataCenter(request, settings); - } - - @DELETE - @Path("/settings/starred-requests") - public Response deleteStarredRequests(@Context HttpServletRequest request, SingularityUserSettings settings) { - return routeToDefaultDataCenter(request, settings); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/WebhookResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/WebhookResource.java deleted file mode 100644 index 1d35c01f2d..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/WebhookResource.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityWebhook; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.WEBHOOK_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class WebhookResource extends ProxyResource { - // TODO - better routing here, route to all? - - @Inject - public WebhookResource() {} - - @GET - public Response getActiveWebhooks(@Context HttpServletRequest request) { - return routeToDefaultDataCenter(request); - } - - @GET - @Path("/summary") - public Response getWebhooksWithQueueSize(@Context HttpServletRequest request) { - return routeToDefaultDataCenter(request); - } - - @POST - public Response addWebhook(@Context HttpServletRequest request, SingularityWebhook webhook) { - return routeToDefaultDataCenter(request, webhook); - } - - @DELETE - @Deprecated - @Path("/{webhookId}") - public Response deleteWebhookDeprecated(@Context HttpServletRequest request, @PathParam("webhookId") String webhookId) { - return routeToDefaultDataCenter(request); - } - - @GET - @Deprecated - @Path("/deploy/{webhookId}") - public Response getQueuedDeployUpdatesDeprecated(@Context HttpServletRequest request, @PathParam("webhookId") String webhookId) { - return routeToDefaultDataCenter(request); - } - - @GET - @Deprecated - @Path("/request/{webhookId}") - public Response getQueuedRequestUpdatesDeprecated(@Context HttpServletRequest request, @PathParam("webhookId") String webhookId) { - return routeToDefaultDataCenter(request); - } - - @GET - @Deprecated - @Path("/task/{webhookId}") - public Response getQueuedTaskUpdatesDeprecated(@Context HttpServletRequest request, @PathParam("webhookId") String webhookId) { - return routeToDefaultDataCenter(request); - } - - @DELETE - public Response deleteWebhook(@Context HttpServletRequest request, @QueryParam("webhookId") String webhookId) { - return routeToDefaultDataCenter(request); - } - - @GET - @Path("/deploy") - public Response getQueuedDeployUpdates(@Context HttpServletRequest request, @QueryParam("webhookId") String webhookId) { - return routeToDefaultDataCenter(request); - } - - @GET - @Path("/request") - public Response getQueuedRequestUpdates(@Context HttpServletRequest request, @QueryParam("webhookId") String webhookId) { - return routeToDefaultDataCenter(request); - } - - @GET - @Path("/task") - public Response getQueuedTaskUpdates(@Context HttpServletRequest request, @QueryParam("webhookId") String webhookId) { - return routeToDefaultDataCenter(request); - } - -} diff --git a/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/SingularityExecutor.java b/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/SingularityExecutor.java index 2373902919..254973efa2 100644 --- a/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/SingularityExecutor.java +++ b/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/SingularityExecutor.java @@ -8,12 +8,12 @@ import org.slf4j.LoggerFactory; import com.google.inject.Inject; -import com.hubspot.mesos.MesosUtils; import com.hubspot.singularity.executor.SingularityExecutorMonitor.KillState; import com.hubspot.singularity.executor.SingularityExecutorMonitor.SubmitState; import com.hubspot.singularity.executor.config.SingularityExecutorTaskBuilder; import com.hubspot.singularity.executor.task.SingularityExecutorTask; import com.hubspot.singularity.executor.utils.ExecutorUtils; +import com.hubspot.singularity.executor.utils.MesosUtils; public class SingularityExecutor implements Executor { diff --git a/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/config/SingularityExecutorConfiguration.java b/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/config/SingularityExecutorConfiguration.java index 216f44b1e7..a7d90be596 100644 --- a/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/config/SingularityExecutorConfiguration.java +++ b/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/config/SingularityExecutorConfiguration.java @@ -15,10 +15,10 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.base.Optional; -import com.hubspot.mesos.MesosUtils; import com.hubspot.singularity.executor.SingularityExecutorLogrotateFrequency; import com.hubspot.singularity.executor.models.ThreadCheckerType; import com.hubspot.singularity.executor.shells.SingularityExecutorShellCommandDescriptor; +import com.hubspot.singularity.executor.utils.MesosUtils; import com.hubspot.singularity.runner.base.configuration.BaseRunnerConfiguration; import com.hubspot.singularity.runner.base.configuration.Configuration; import com.hubspot.singularity.runner.base.constraints.DirectoryExists; diff --git a/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/config/SingularityExecutorTaskBuilder.java b/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/config/SingularityExecutorTaskBuilder.java index f42acb1f2e..c5c4d8fc84 100644 --- a/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/config/SingularityExecutorTaskBuilder.java +++ b/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/config/SingularityExecutorTaskBuilder.java @@ -13,7 +13,6 @@ import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.inject.name.Named; -import com.hubspot.mesos.MesosUtils; import com.hubspot.singularity.SingularityTaskExecutorData; import com.hubspot.singularity.executor.TemplateManager; import com.hubspot.singularity.executor.task.SingularityExecutorArtifactFetcher; @@ -21,6 +20,7 @@ import com.hubspot.singularity.executor.task.SingularityExecutorTaskDefinition; import com.hubspot.singularity.executor.utils.DockerUtils; import com.hubspot.singularity.executor.utils.ExecutorUtils; +import com.hubspot.singularity.executor.utils.MesosUtils; import com.hubspot.singularity.runner.base.config.SingularityRunnerBaseModule; import com.hubspot.singularity.runner.base.configuration.SingularityRunnerBaseConfiguration; import com.hubspot.singularity.runner.base.shared.JsonObjectFileHelper; diff --git a/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/shells/SingularityExecutorShellCommandRunner.java b/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/shells/SingularityExecutorShellCommandRunner.java index 7c6ebcfe84..ae4c0934b5 100644 --- a/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/shells/SingularityExecutorShellCommandRunner.java +++ b/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/shells/SingularityExecutorShellCommandRunner.java @@ -17,12 +17,12 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; -import com.hubspot.mesos.MesosUtils; import com.hubspot.singularity.SingularityTaskShellCommandRequest; import com.hubspot.singularity.SingularityTaskShellCommandUpdate.UpdateType; import com.hubspot.singularity.executor.config.SingularityExecutorConfiguration; import com.hubspot.singularity.executor.task.SingularityExecutorTask; import com.hubspot.singularity.executor.task.SingularityExecutorTaskProcessCallable; +import com.hubspot.singularity.executor.utils.MesosUtils; public class SingularityExecutorShellCommandRunner { diff --git a/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/task/SingularityExecutorTask.java b/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/task/SingularityExecutorTask.java index f89434673d..327d0955f3 100644 --- a/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/task/SingularityExecutorTask.java +++ b/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/task/SingularityExecutorTask.java @@ -20,6 +20,7 @@ import com.hubspot.singularity.executor.config.SingularityExecutorConfiguration; import com.hubspot.singularity.executor.utils.DockerUtils; import com.hubspot.singularity.executor.utils.ExecutorUtils; +import com.hubspot.singularity.executor.utils.MesosUtils; import com.hubspot.singularity.runner.base.configuration.SingularityRunnerBaseConfiguration; import com.hubspot.singularity.runner.base.shared.JsonObjectFileHelper; import com.hubspot.singularity.s3.base.config.SingularityS3Configuration; @@ -67,7 +68,7 @@ public SingularityExecutorTask(ExecutorDriver driver, ExecutorUtils executorUtil } public void cleanup(TaskState state) { - ExtendedTaskState extendedTaskState = ExtendedTaskState.fromTaskState(org.apache.mesos.v1.Protos.TaskState.valueOf(state.toString())); // #gross + ExtendedTaskState extendedTaskState = MesosUtils.fromTaskState(org.apache.mesos.v1.Protos.TaskState.valueOf(state.toString())); boolean cleanupAppTaskDirectory = !extendedTaskState.isFailed() && !taskDefinition.getExecutorData().getPreserveTaskSandboxAfterFinish().or(Boolean.FALSE); diff --git a/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/utils/MesosUtils.java b/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/utils/MesosUtils.java new file mode 100644 index 0000000000..fffb3cf6aa --- /dev/null +++ b/SingularityExecutor/src/main/java/com/hubspot/singularity/executor/utils/MesosUtils.java @@ -0,0 +1,48 @@ +package com.hubspot.singularity.executor.utils; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; + +import org.apache.mesos.v1.Protos.TaskState; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import com.hubspot.singularity.ExtendedTaskState; + +public class MesosUtils { + public static Path getTaskDirectoryPath(String taskId) { + return Paths.get(getSafeTaskIdForDirectory(taskId)).toAbsolutePath(); + } + + public static String getSafeTaskIdForDirectory(String taskId) { + return taskId.replace(":", "_"); + } + + public static String formatForLogging(Object object) { + return object.toString().replace("\n", "").replaceAll("( )+", " "); + } + + private static final Map map; + static { + map = Maps.newHashMapWithExpectedSize(ExtendedTaskState.values().length); + for (ExtendedTaskState extendedTaskState : ExtendedTaskState.values()) { + if (extendedTaskState.toTaskState().isPresent()) { + + map.put(TaskState.valueOf(extendedTaskState.toTaskState().get().name()), extendedTaskState); + } + } + + for (TaskState t : TaskState.values()) { + if (map.get(t) == null) { + throw new IllegalStateException("No ExtendedTaskState provided for TaskState " + t + ", you probably have incompatible versions of Mesos and Singularity."); + } + } + } + + public static ExtendedTaskState fromTaskState(TaskState taskState) { + ExtendedTaskState extendedTaskState = map.get(taskState); + Preconditions.checkArgument(extendedTaskState != null, "No ExtendedTaskState for TaskState %s", taskState); + return extendedTaskState; + } +} diff --git a/SingularityS3Base/pom.xml b/SingularityS3Base/pom.xml index fe5e37eda6..baa32383e7 100644 --- a/SingularityS3Base/pom.xml +++ b/SingularityS3Base/pom.xml @@ -68,6 +68,25 @@ org.hibernate hibernate-validator + + + + junit + junit + test + + + + org.assertj + assertj-core + test + + + + org.mockito + mockito-core + test + diff --git a/SingularityS3Base/src/main/java/com/hubspot/singularity/s3/base/ArtifactManager.java b/SingularityS3Base/src/main/java/com/hubspot/singularity/s3/base/ArtifactManager.java index 1dd88cd763..eb680b6909 100644 --- a/SingularityS3Base/src/main/java/com/hubspot/singularity/s3/base/ArtifactManager.java +++ b/SingularityS3Base/src/main/java/com/hubspot/singularity/s3/base/ArtifactManager.java @@ -3,6 +3,7 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SeekableByteChannel; +import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -200,11 +201,17 @@ private void downloadUri(String uri, Path path) { public void copy(Path source, Path destination, String destinationFilename) { log.info("Copying {} to {}", source, destination); + Path destinationPath = destination.resolve(destinationFilename); try { Files.createDirectories(destination); - Files.copy(source, destination.resolve(destinationFilename)); - } catch (IOException e) { - throw Throwables.propagate(e); + Files.copy(source, destinationPath); + } catch (FileAlreadyExistsException e) { + if (!calculateMd5sum(source).equals(calculateMd5sum(destinationPath))) { + throw new RuntimeException(e); + } + } + catch (IOException e) { + throw new RuntimeException(e); } } diff --git a/SingularityS3Base/src/test/java/com/hubspot/singularity/s3/base/ArtifactManagerTest.java b/SingularityS3Base/src/test/java/com/hubspot/singularity/s3/base/ArtifactManagerTest.java new file mode 100644 index 0000000000..c65fda30eb --- /dev/null +++ b/SingularityS3Base/src/test/java/com/hubspot/singularity/s3/base/ArtifactManagerTest.java @@ -0,0 +1,93 @@ +package com.hubspot.singularity.executor.config; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Throwables; +import com.hubspot.singularity.runner.base.configuration.SingularityRunnerBaseConfiguration; +import com.hubspot.singularity.runner.base.sentry.SingularityRunnerExceptionNotifier; +import com.hubspot.singularity.s3.base.ArtifactManager; +import com.hubspot.singularity.s3.base.config.SingularityS3Configuration; + +public class ArtifactManagerTest { + + private ArtifactManager artifactManager; + + @Rule + public TemporaryFolder cacheDir = new TemporaryFolder(); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Before + public void setup() { + SingularityRunnerBaseConfiguration baseConfig = new SingularityRunnerBaseConfiguration(); + SingularityS3Configuration s3Config = new SingularityS3Configuration(); + + s3Config.setArtifactCacheDirectory(cacheDir.toString()); + artifactManager = new ArtifactManager( + baseConfig, + s3Config, + LoggerFactory.getLogger(ArtifactManagerTest.class), + new SingularityRunnerExceptionNotifier(baseConfig) + ); + } + + @Test + public void itAbsorbsFileAlreadyExistsExceptionsWhenCopyingDuplicateFiles() { + List lines = Arrays.asList("Testing", "1", "2", "3"); + Path originalPath = write("original.txt", lines); + + assertThat(originalPath.toFile()).hasContent(String.join(System.lineSeparator(), lines)); + + Path copyPath = Paths.get(cacheDir.getRoot().toString() + "/copy.txt"); + assertThat(copyPath).doesNotExist(); + artifactManager.copy(originalPath, cacheDir.getRoot().toPath(), "copy.txt"); + assertThat(copyPath).exists(); + + // A redundant copy operation should not throw. + artifactManager.copy(originalPath, cacheDir.getRoot().toPath(), "copy.txt"); + } + + @Test + public void itPropagatesFileAlreadyExistsExceptionsWhenCopyingNonDuplicateFiles() { + Path firstPath = write("a.txt", Arrays.asList("Testing", "1", "2", "3")); + Path secondPath = write("b.txt", Arrays.asList("Testing", "a", "b", "c")); + + try { + artifactManager.copy(firstPath, secondPath.getParent(), secondPath.getFileName().toString()); + fail("Expected copy operation to throw when trying to overwrite non-duplicate file."); + } catch (Exception e) { + assertThat(Throwables.getRootCause(e)).isInstanceOf(FileAlreadyExistsException.class); + } + + } + + public Path write(String fileName, List lines) { + try { + File file = cacheDir.newFile(fileName); + Path path = file.toPath(); + Files.write(path, lines, Charset.forName("UTF-8")); + return path; + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/SingularityService/pom.xml b/SingularityService/pom.xml index 7ecdc44ff0..f60b941619 100644 --- a/SingularityService/pom.xml +++ b/SingularityService/pom.xml @@ -280,6 +280,21 @@ metrics-guice + + io.dropwizard + dropwizard-auth + + + + org.glassfish.jersey.core + jersey-server + + + + org.glassfish.hk2 + hk2-api + + com.fasterxml.jackson.core jackson-annotations diff --git a/SingularityService/src/main/docker/singularity.yaml b/SingularityService/src/main/docker/singularity.yaml index 8844db4c0f..744d3f2c7a 100644 --- a/SingularityService/src/main/docker/singularity.yaml +++ b/SingularityService/src/main/docker/singularity.yaml @@ -14,6 +14,7 @@ mesos: master: localhost:5050 defaultCpus: 1 defaultMemory: 128 + defaultDisk: 1024 frameworkName: Singularity frameworkId: Singularity frameworkFailoverTimeout: 1000000 diff --git a/SingularityService/src/main/docker/start.sh b/SingularityService/src/main/docker/start.sh index 1e3e50dd88..7beae8bee6 100755 --- a/SingularityService/src/main/docker/start.sh +++ b/SingularityService/src/main/docker/start.sh @@ -20,6 +20,7 @@ args+=( -Ddw.mesos.frameworkId="${SINGULARITY_MESOS_FRAMEWORK_ID:=Singularity}" [[ ! ${SINGULARITY_MESOS_FRAMEWORK_ROLE:-} ]] || args+=( -Ddw.mesos.frameworkRole="${SINGULARITY_MESOS_FRAMEWORK_ROLE}" ) args+=( -Ddw.mesos.defaultCpus="${SINGULARITY_MESOS_DEFAULT_CPUS:=1}" ) args+=( -Ddw.mesos.defaultMemory="${SINGULARITY_MESOS_DEFAULT_MEMORY:=128}" ) +args+=( -Ddw.mesos.defaultDisk="${SINGULARITY_MESOS_DEFAULT_DISK:=1024}" ) args+=( -Ddw.zookeeper.quorum="${SINGULARITY_ZK:=localhost:2181}" ) args+=( -Ddw.zookeeper.zkNamespace="${SINGULARITY_ZK_NAMESPACE:=singularity}" ) args+=( -Ddw.ui.baseUrl="${SINGULARITY_UI_BASE:=/singularity}" ) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java b/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java index fc626197a3..c406989cdd 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java @@ -1,27 +1,31 @@ package com.hubspot.singularity; -import com.google.common.base.Optional; import com.google.inject.Binder; -import com.google.inject.Module; import com.google.inject.Scopes; -import com.google.inject.TypeLiteral; +import com.google.inject.multibindings.Multibinder; +import com.hubspot.dropwizard.guicier.DropwizardAwareModule; +import com.hubspot.singularity.auth.SingularityAuthFeature; +import com.hubspot.singularity.auth.SingularityAuthenticatorClass; import com.hubspot.singularity.auth.SingularityAuthorizationHelper; import com.hubspot.singularity.auth.authenticator.SingularityAuthenticator; +import com.hubspot.singularity.auth.authenticator.SingularityMultiMethodAuthenticator; import com.hubspot.singularity.auth.datastore.SingularityAuthDatastore; import com.hubspot.singularity.config.SingularityConfiguration; -public class SingularityAuthModule implements Module { - private final SingularityConfiguration configuration; +public class SingularityAuthModule extends DropwizardAwareModule { - public SingularityAuthModule(SingularityConfiguration configuration) { - this.configuration = configuration; - } + public SingularityAuthModule() {} @Override public void configure(Binder binder) { - binder.bind(SingularityAuthenticator.class).to(configuration.getAuthConfiguration().getAuthenticator().getAuthenticatorClass()); - binder.bind(SingularityAuthDatastore.class).to(configuration.getAuthConfiguration().getDatastore().getAuthDatastoreClass()); - binder.bind(new TypeLiteral>() {}).toProvider(SingularityAuthenticator.class); + Multibinder multibinder = Multibinder.newSetBinder(binder, SingularityAuthenticator.class); + for (SingularityAuthenticatorClass clazz : getConfiguration().getAuthConfiguration().getAuthenticators()) { + multibinder.addBinding().to(clazz.getAuthenticatorClass()); + } + + binder.bind(SingularityAuthFeature.class); + binder.bind(SingularityMultiMethodAuthenticator.class); + binder.bind(SingularityAuthDatastore.class).to(getConfiguration().getAuthConfiguration().getDatastore().getAuthDatastoreClass()); binder.bind(SingularityAuthorizationHelper.class).in(Scopes.SINGLETON); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/SingularityLeaderController.java b/SingularityService/src/main/java/com/hubspot/singularity/SingularityLeaderController.java index 948e2fae21..8781fe682d 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/SingularityLeaderController.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/SingularityLeaderController.java @@ -19,7 +19,7 @@ import com.google.common.net.HostAndPort; import com.google.inject.Inject; import com.google.inject.name.Named; -import com.hubspot.mesos.MesosUtils; +import com.hubspot.singularity.helpers.MesosUtils; import com.hubspot.singularity.SingularityAbort.AbortReason; import com.hubspot.singularity.config.SingularityConfiguration; import com.hubspot.singularity.data.StateManager; diff --git a/SingularityService/src/main/java/com/hubspot/singularity/SingularityService.java b/SingularityService/src/main/java/com/hubspot/singularity/SingularityService.java index 009c1915a6..2e21b1fb75 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/SingularityService.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/SingularityService.java @@ -45,6 +45,7 @@ public void initialize(final Bootstrap bootstrap) { guiceBundle = GuiceBundle.defaultBuilder(SingularityConfiguration.class) .modules(new SingularityServiceModule()) + .modules(new SingularityAuthModule()) .modules(additionalModules) .build(); bootstrap.addBundle(guiceBundle); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/SingularityServiceModule.java b/SingularityService/src/main/java/com/hubspot/singularity/SingularityServiceModule.java index f21163957a..eda8fc4e6e 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/SingularityServiceModule.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/SingularityServiceModule.java @@ -6,6 +6,7 @@ import com.google.inject.Singleton; import com.hubspot.dropwizard.guicier.DropwizardAwareModule; import com.hubspot.mesos.client.SingularityMesosClientModule; +import com.hubspot.singularity.auth.SingularityAuthenticatorClass; import com.hubspot.singularity.config.IndexViewConfiguration; import com.hubspot.singularity.config.SingularityConfiguration; import com.hubspot.singularity.data.SingularityDataModule; @@ -37,8 +38,6 @@ public void configure(Binder binder) { binder.install(new SingularityJerseyModule()); binder.install(new SingularityEventModule(getConfiguration())); - - binder.install(new SingularityAuthModule(getConfiguration())); } @Provides @@ -49,6 +48,7 @@ public IndexViewConfiguration provideIndexViewConfiguration() { configuration.getUiConfiguration(), configuration.getMesosConfiguration().getDefaultMemory(), configuration.getMesosConfiguration().getDefaultCpus(), + configuration.getMesosConfiguration().getDefaultDisk(), configuration.getMesosConfiguration().getSlaveHttpPort(), configuration.getMesosConfiguration().getSlaveHttpsPort(), configuration.getDefaultBounceExpirationMinutes(), @@ -58,7 +58,8 @@ public IndexViewConfiguration provideIndexViewConfiguration() { configuration.getStartupTimeoutSeconds(), !Strings.isNullOrEmpty(configuration.getLoadBalancerUri()), configuration.getCommonHostnameSuffixToOmit(), - configuration.getWarnIfScheduledJobIsRunningPastNextRunPct() + configuration.getWarnIfScheduledJobIsRunningPastNextRunPct(), + configuration.getAuthConfiguration().isEnabled() && configuration.getAuthConfiguration().getAuthenticators().contains(SingularityAuthenticatorClass.WEBHOOK) ); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFactoryProvider.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFactoryProvider.java new file mode 100644 index 0000000000..d55503797d --- /dev/null +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFactoryProvider.java @@ -0,0 +1,34 @@ +package com.hubspot.singularity.auth; + +import org.glassfish.hk2.api.Factory; +import org.glassfish.hk2.api.ServiceLocator; +import org.glassfish.jersey.server.internal.inject.AbstractValueFactoryProvider; +import org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider; +import org.glassfish.jersey.server.model.Parameter; +import org.glassfish.jersey.server.model.Parameter.Source; + +import com.hubspot.singularity.SingularityUser; + +import io.dropwizard.auth.Auth; + +public class SingularityAuthFactoryProvider extends AbstractValueFactoryProvider { + private SingularityAuthedUserFactory authFactory; + + @javax.inject.Inject + public SingularityAuthFactoryProvider(final MultivaluedParameterExtractorProvider extractorProvider, + ServiceLocator locator, + SingularityAuthedUserFactory authFactory) { + super(extractorProvider, locator, Source.UNKNOWN); + this.authFactory = authFactory; + } + + @Override + protected Factory createValueFactory(Parameter parameter) { + Class paramType = parameter.getRawType(); + Auth annotation = parameter.getAnnotation(Auth.class); + if (annotation != null && paramType.isAssignableFrom(SingularityUser.class)) { + return authFactory; + } + return null; + } +} diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFeature.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFeature.java new file mode 100644 index 0000000000..833b009bed --- /dev/null +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFeature.java @@ -0,0 +1,37 @@ +package com.hubspot.singularity.auth; + +import javax.inject.Singleton; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.core.Feature; +import javax.ws.rs.core.FeatureContext; + +import org.glassfish.hk2.api.InjectionResolver; +import org.glassfish.hk2.api.TypeLiteral; +import org.glassfish.hk2.utilities.binding.AbstractBinder; +import org.glassfish.jersey.server.spi.internal.ValueFactoryProvider; + +import com.hubspot.singularity.SingularityUser; +import com.hubspot.singularity.auth.authenticator.SingularityMultiMethodAuthenticator; + +import io.dropwizard.auth.Auth; +import io.dropwizard.auth.Authenticator; + +@javax.ws.rs.ext.Provider +public class SingularityAuthFeature implements Feature { + @javax.inject.Inject + SingularityAuthFeature() {} + + @Override + public boolean configure(FeatureContext context) { + context.register(new AbstractBinder() { + @Override + public void configure() { + bind(SingularityMultiMethodAuthenticator.class).to(new TypeLiteral>() {}).in(Singleton.class); + bind(SingularityAuthedUserFactory.class).to(SingularityAuthedUserFactory.class).in(Singleton.class); + bind(SingularityAuthFactoryProvider.class).to(ValueFactoryProvider.class).in(Singleton.class); + bind(SingularityAuthParamInjectionResolver.class).to(new TypeLiteral>(){}).in(Singleton.class); + } + }); + return true; + } +} diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthParamInjectionResolver.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthParamInjectionResolver.java new file mode 100644 index 0000000000..d33d4f1ef9 --- /dev/null +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthParamInjectionResolver.java @@ -0,0 +1,14 @@ +package com.hubspot.singularity.auth; + +import org.glassfish.jersey.server.internal.inject.ParamInjectionResolver; + +import com.google.inject.Singleton; + +import io.dropwizard.auth.Auth; + +@Singleton +public class SingularityAuthParamInjectionResolver extends ParamInjectionResolver { + public SingularityAuthParamInjectionResolver() { + super(SingularityAuthFactoryProvider.class); + } +} diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthedUserFactory.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthedUserFactory.java new file mode 100644 index 0000000000..2d7244872d --- /dev/null +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthedUserFactory.java @@ -0,0 +1,55 @@ +package com.hubspot.singularity.auth; + +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.ws.rs.WebApplicationException; + +import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory; + +import com.google.inject.Singleton; +import com.hubspot.singularity.SingularityUser; +import com.hubspot.singularity.WebExceptions; +import com.hubspot.singularity.auth.authenticator.SingularityAuthenticator; +import com.hubspot.singularity.config.SingularityConfiguration; + +@Singleton +public class SingularityAuthedUserFactory extends AbstractContainerRequestValueFactory { + private final Set authenticators; + private final SingularityConfiguration configuration; + + @javax.inject.Inject + public SingularityAuthedUserFactory(Set authenticators, SingularityConfiguration configuration) { + this.authenticators = authenticators; + this.configuration = configuration; + } + + @Override + public SingularityUser provide() { + WebApplicationException unauthenticatedException = null; + for (SingularityAuthenticator authenticator : authenticators) { + try { + Optional maybeUser = authenticator.getUser(getContainerRequest()); + if (maybeUser.isPresent()) { + return maybeUser.get(); + } + } catch (WebApplicationException e) { + unauthenticatedException = e; + } + } + + // No user found if we got here + if (configuration.getAuthConfiguration().isEnabled()) { + if (unauthenticatedException != null) { + throw unauthenticatedException; + } else { + throw WebExceptions.unauthorized(String.format("Unable to authenticate user using methods: %s", + authenticators.stream().map(SingularityAuthenticator::getClass).collect(Collectors.toList()))); + } + } + + // Auth is disabled, return a dummy/default user + return SingularityUser.DEFAULT_USER; + } +} diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthenticatorClass.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthenticatorClass.java index 69a84e631a..7137fb7735 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthenticatorClass.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthenticatorClass.java @@ -2,13 +2,15 @@ import com.hubspot.singularity.auth.authenticator.SingularityAuthenticator; import com.hubspot.singularity.auth.authenticator.SingularityDisabledAuthenticator; +import com.hubspot.singularity.auth.authenticator.SingularityWebhookAuthenticator; import com.hubspot.singularity.auth.authenticator.SingularityHeaderPassthroughAuthenticator; import com.hubspot.singularity.auth.authenticator.SingularityQueryParamAuthenticator; public enum SingularityAuthenticatorClass { DISABLED(SingularityDisabledAuthenticator.class), HEADER_PASSTHROUGH(SingularityHeaderPassthroughAuthenticator.class), - QUERYPARAM_PASSTHROUGH(SingularityQueryParamAuthenticator.class); + QUERYPARAM_PASSTHROUGH(SingularityQueryParamAuthenticator.class), + WEBHOOK(SingularityWebhookAuthenticator.class); private final Class authenticatorClass; diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthorizationHelper.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthorizationHelper.java index 772f74a148..d27b8d5ee3 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthorizationHelper.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthorizationHelper.java @@ -3,7 +3,6 @@ import static com.google.common.collect.ImmutableSet.copyOf; import static com.hubspot.singularity.WebExceptions.badRequest; import static com.hubspot.singularity.WebExceptions.checkForbidden; -import static com.hubspot.singularity.WebExceptions.checkNotFound; import static com.hubspot.singularity.WebExceptions.checkUnauthorized; import java.util.Collections; @@ -57,31 +56,36 @@ public static boolean groupsIntersect(Set a, Set b) { return !Sets.intersection(a, b).isEmpty(); } - public boolean hasAdminAuthorization(Optional user) { + public boolean hasAdminAuthorization(SingularityUser user) { // disabled auth == no rules! if (!authEnabled) { return true; } + if (!user.isAuthenticated()) { + return false; + } + // not authenticated, or no groups, or no admin groups == can't possibly be admin - if (!user.isPresent() || user.get().getGroups().isEmpty() || adminGroups.isEmpty()) { + if (user.getGroups().isEmpty() || adminGroups.isEmpty()) { return false; } - return groupsIntersect(user.get().getGroups(), adminGroups); + return groupsIntersect(user.getGroups(), adminGroups); } - public void checkAdminAuthorization(Optional user) { + public void checkAdminAuthorization(SingularityUser user) { if (authEnabled) { - checkUnauthorized(user.isPresent(), "Please log in to perform this action."); + checkForbidden(user.isAuthenticated(), "Not Authenticated!"); if (!adminGroups.isEmpty()) { - checkForbidden(groupsIntersect(user.get().getGroups(), adminGroups), "%s must be part of one or more admin groups: %s", user.get().getId(), JavaUtils.COMMA_JOINER.join(adminGroups)); + checkForbidden(groupsIntersect(user.getGroups(), adminGroups), "%s must be part of one or more admin groups: %s", user.getId(), JavaUtils.COMMA_JOINER.join(adminGroups)); } } } - public void checkForAuthorizationByTaskId(String taskId, Optional user, SingularityAuthorizationScope scope) { + public void checkForAuthorizationByTaskId(String taskId, SingularityUser user, SingularityAuthorizationScope scope) { if (authEnabled) { + checkForbidden(user.isAuthenticated(), "Not Authenticated!"); try { final SingularityTaskId taskIdObj = SingularityTaskId.valueOf(taskId); @@ -96,8 +100,9 @@ public void checkForAuthorizationByTaskId(String taskId, Optional user, SingularityAuthorizationScope scope) { + public void checkForAuthorizationByRequestId(String requestId, SingularityUser user, SingularityAuthorizationScope scope) { if (authEnabled) { + final Optional maybeRequest = requestManager.getRequest(requestId); if (maybeRequest.isPresent()) { @@ -106,16 +111,16 @@ public void checkForAuthorizationByRequestId(String requestId, Optional user, SingularityAuthorizationScope scope) { + public boolean isAuthorizedForRequest(SingularityRequest request, SingularityUser user, SingularityAuthorizationScope scope) { if (!authEnabled) { return true; // no auth == no rules! } - if (!user.isPresent()) { + if (!user.isAuthenticated()) { return false; } - final Set userGroups = user.get().getGroups(); + final Set userGroups = user.getGroups(); final Set readWriteGroups = Sets.union(request.getGroup().asSet(), request.getReadWriteGroups().or(Collections.emptySet())); final Set readOnlyGroups = request.getReadOnlyGroups().or(defaultReadOnlyGroups); @@ -136,14 +141,14 @@ public boolean isAuthorizedForRequest(SingularityRequest request, Optional user, SingularityAuthorizationScope scope) { + public void checkForAuthorization(SingularityRequest request, SingularityUser user, SingularityAuthorizationScope scope) { if (!authEnabled) { return; } - checkUnauthorized(user.isPresent(), "user must be present"); + checkForbidden(user.isAuthenticated(), "Not authenticated!"); - final Set userGroups = user.get().getGroups(); + final Set userGroups = user.getGroups(); final Set readWriteGroups = Sets.union(request.getGroup().asSet(), request.getReadWriteGroups().or(Collections.emptySet())); final Set readOnlyGroups = request.getReadOnlyGroups().or(defaultReadOnlyGroups); @@ -157,26 +162,26 @@ public void checkForAuthorization(SingularityRequest request, Optional user) { + public void checkForAuthorizedChanges(SingularityRequest request, SingularityRequest oldRequest, SingularityUser user) { if (!authEnabled) { return; } - checkUnauthorized(user.isPresent(), "user must be present"); + checkForbidden(user.isAuthenticated(), "Not Authenticated!"); if (oldRequest.getGroup().isPresent() && !oldRequest.getReadWriteGroups().equals(request.getReadWriteGroups())) { - final Set userGroups = user.get().getGroups(); + final Set userGroups = user.getGroups(); final boolean userIsAdmin = !adminGroups.isEmpty() && groupsIntersect(userGroups, adminGroups); final boolean userIsJITA = !jitaGroups.isEmpty() && groupsIntersect(userGroups, jitaGroups); final boolean userIsRequestOwner = userGroups.contains(oldRequest.getGroup().get()); @@ -186,7 +191,7 @@ public void checkForAuthorizedChanges(SingularityRequest request, SingularityReq } } - public Iterable filterByAuthorizedRequests(final Optional user, List objects, final Function requestIdFunction, final SingularityAuthorizationScope scope) { + public Iterable filterByAuthorizedRequests(final SingularityUser user, List objects, final Function requestIdFunction, final SingularityAuthorizationScope scope) { if (hasAdminAuthorization(user)) { return objects; } @@ -214,7 +219,7 @@ public boolean apply(@Nonnull T input) { }); } - public Iterable filterAuthorizedRequestIds(final Optional user, List requestIds, final SingularityAuthorizationScope scope, boolean useWebCache) { + public Iterable filterAuthorizedRequestIds(final SingularityUser user, List requestIds, final SingularityAuthorizationScope scope, boolean useWebCache) { if (hasAdminAuthorization(user)) { return requestIds; } @@ -233,13 +238,4 @@ public boolean apply(@Nonnull String input) { } }); } - - public String getAuthUserId(Optional user) { - if (authEnabled) { - checkUnauthorized(user.isPresent(), "Please log in to perform this action."); - } else { - checkNotFound(user.isPresent(), "Please set a user id to perform this action."); - } - return user.get().getId(); - } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityAuthenticator.java index 5f4d0905d8..6866ece6eb 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityAuthenticator.java @@ -1,9 +1,11 @@ package com.hubspot.singularity.auth.authenticator; -import com.google.common.base.Optional; -import com.google.inject.Provider; -import com.hubspot.singularity.SingularityUser; +import java.util.Optional; + +import javax.ws.rs.container.ContainerRequestContext; -public interface SingularityAuthenticator extends Provider> { +import com.hubspot.singularity.SingularityUser; +public interface SingularityAuthenticator { + Optional getUser(ContainerRequestContext context); } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityDisabledAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityDisabledAuthenticator.java index c4e102677c..f89ed1887a 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityDisabledAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityDisabledAuthenticator.java @@ -1,6 +1,9 @@ package com.hubspot.singularity.auth.authenticator; -import com.google.common.base.Optional; +import java.util.Optional; + +import javax.ws.rs.container.ContainerRequestContext; + import com.google.inject.Inject; import com.google.inject.Singleton; import com.hubspot.singularity.SingularityUser; @@ -11,7 +14,7 @@ public class SingularityDisabledAuthenticator implements SingularityAuthenticato public SingularityDisabledAuthenticator() {} @Override - public Optional get() { - return Optional.absent(); + public Optional getUser(ContainerRequestContext context) { + return Optional.empty(); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityHeaderPassthroughAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityHeaderPassthroughAuthenticator.java index 6eebb9fe48..1ff41369b9 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityHeaderPassthroughAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityHeaderPassthroughAuthenticator.java @@ -1,46 +1,50 @@ package com.hubspot.singularity.auth.authenticator; -import javax.servlet.http.HttpServletRequest; +import java.util.Optional; + +import javax.ws.rs.container.ContainerRequestContext; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import com.google.common.base.Optional; import com.google.common.base.Strings; import com.google.inject.Inject; -import com.google.inject.Provider; import com.google.inject.ProvisionException; import com.google.inject.Singleton; import com.hubspot.singularity.SingularityUser; +import com.hubspot.singularity.WebExceptions; import com.hubspot.singularity.auth.datastore.SingularityAuthDatastore; import com.hubspot.singularity.config.SingularityConfiguration; @Singleton public class SingularityHeaderPassthroughAuthenticator implements SingularityAuthenticator { - private final SingularityAuthDatastore datastore; + private static final Logger LOG = LoggerFactory.getLogger(SingularityHeaderPassthroughAuthenticator.class); + + private final SingularityAuthDatastore authDatastore; private final String requestUserHeaderName; - private final Provider requestProvider; @Inject - public SingularityHeaderPassthroughAuthenticator(SingularityAuthDatastore datastore, SingularityConfiguration configuration, Provider requestProvider) { - this.datastore = datastore; + public SingularityHeaderPassthroughAuthenticator(SingularityAuthDatastore authDatastore, SingularityConfiguration configuration) { + this.authDatastore = authDatastore; this.requestUserHeaderName = configuration.getAuthConfiguration().getRequestUserHeaderName(); - this.requestProvider = requestProvider; } - private Optional getUserId() { + private Optional getUserId(ContainerRequestContext context) { try { - return Optional.fromNullable(Strings.emptyToNull(requestProvider.get().getHeader(requestUserHeaderName))); + return Optional.ofNullable(Strings.emptyToNull(context.getHeaderString(requestUserHeaderName))); } catch (ProvisionException pe) { - return Optional.absent(); + return Optional.empty(); } } @Override - public Optional get() { - final Optional maybeUsername = getUserId(); + public Optional getUser(ContainerRequestContext context) { + final Optional maybeUsername = getUserId(context); if (!maybeUsername.isPresent()) { - return Optional.absent(); + throw WebExceptions.unauthorized("(HeaderPassthrough) Could not determine username from header"); } - return datastore.getUser(maybeUsername.get()); + return authDatastore.getUser(maybeUsername.get()); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiMethodAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiMethodAuthenticator.java new file mode 100644 index 0000000000..d35d2e7c49 --- /dev/null +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiMethodAuthenticator.java @@ -0,0 +1,57 @@ +package com.hubspot.singularity.auth.authenticator; + +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.container.ContainerRequestContext; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.hubspot.singularity.SingularityUser; +import com.hubspot.singularity.WebExceptions; +import com.hubspot.singularity.config.SingularityConfiguration; + +import io.dropwizard.auth.Authenticator; + +public class SingularityMultiMethodAuthenticator implements Authenticator { + private static final Logger LOG = LoggerFactory.getLogger(SingularityMultiMethodAuthenticator.class); + + private final Set authenticators; + private final SingularityConfiguration configuration; + + @javax.inject.Inject + public SingularityMultiMethodAuthenticator(Set authenticators, SingularityConfiguration configuration) { + this.authenticators = authenticators; + this.configuration = configuration; + } + + public Optional authenticate(ContainerRequestContext context) { + WebApplicationException unauthorizedException = null; + for (SingularityAuthenticator authenticator : authenticators) { + try { + Optional maybeUser = authenticator.getUser(context); + if (maybeUser.isPresent()) { + return maybeUser; + } + } catch (WebApplicationException e) { + LOG.trace("Unauthenticated: {}", e.getMessage()); + unauthorizedException = e; + } + } + + // No user found if we got here + if (configuration.getAuthConfiguration().isEnabled()) { + if (unauthorizedException != null) { + throw unauthorizedException; + } else { + throw WebExceptions.unauthorized(String.format("Unable to authenticate user using methods: %s", authenticators.stream().map(SingularityAuthenticator::getClass).collect(Collectors.toList()))); + } + } + + // Auth is disabled, return a dummy/default user + return Optional.of(SingularityUser.DEFAULT_USER); + } +} diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityQueryParamAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityQueryParamAuthenticator.java index 49d575822d..db98bfc100 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityQueryParamAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityQueryParamAuthenticator.java @@ -1,43 +1,32 @@ package com.hubspot.singularity.auth.authenticator; -import javax.servlet.http.HttpServletRequest; +import java.util.Optional; + +import javax.ws.rs.container.ContainerRequestContext; -import com.google.common.base.Optional; import com.google.common.base.Strings; import com.google.inject.Inject; -import com.google.inject.Provider; -import com.google.inject.ProvisionException; import com.google.inject.Singleton; import com.hubspot.singularity.SingularityUser; +import com.hubspot.singularity.WebExceptions; import com.hubspot.singularity.auth.datastore.SingularityAuthDatastore; @Singleton public class SingularityQueryParamAuthenticator implements SingularityAuthenticator { - private final Provider requestProvider; private final SingularityAuthDatastore authDatastore; @Inject - public SingularityQueryParamAuthenticator(Provider requestProvider, SingularityAuthDatastore authDatastore) { - this.requestProvider = requestProvider; + public SingularityQueryParamAuthenticator(SingularityAuthDatastore authDatastore) { this.authDatastore = authDatastore; } - private Optional getUserId() { - try { - return Optional.fromNullable(Strings.emptyToNull(requestProvider.get().getParameter("user"))); - } catch (ProvisionException pe) { - return Optional.absent(); - } - } - @Override - public Optional get() { - final Optional maybeUser = getUserId(); + public Optional getUser(ContainerRequestContext context) { + final Optional maybeUserId = Optional.ofNullable(Strings.emptyToNull(context.getUriInfo().getQueryParameters().getFirst("user"))); - if (maybeUser.isPresent()) { - return authDatastore.getUser(maybeUser.get()); - } else { - return Optional.absent(); + if (!maybeUserId.isPresent()) { + throw WebExceptions.unauthorized("(QueryParam) No user specified"); } + return authDatastore.getUser(maybeUserId.get()); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserPermissionsResponse.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserPermissionsResponse.java new file mode 100644 index 0000000000..02344fad72 --- /dev/null +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserPermissionsResponse.java @@ -0,0 +1,53 @@ +package com.hubspot.singularity.auth.authenticator; + +import java.util.Objects; +import java.util.Optional; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.hubspot.singularity.SingularityUser; + +public class SingularityUserPermissionsResponse { + private final Optional user; + private final Optional error; + + @JsonCreator + public SingularityUserPermissionsResponse(@JsonProperty("user") Optional user, @JsonProperty("error") Optional error) { + this.user = user; + this.error = error; + } + + public Optional getUser() { + return user; + } + + public Optional getError() { + return error; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof SingularityUserPermissionsResponse) { + final SingularityUserPermissionsResponse that = (SingularityUserPermissionsResponse) obj; + return Objects.equals(this.user, that.user) && + Objects.equals(this.error, that.error); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(user, error); + } + + @Override + public String toString() { + return "SingularityUserPermissionsResponse{" + + "user=" + user + + ", error=" + error + + '}'; + } +} diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java new file mode 100644 index 0000000000..15f70a7462 --- /dev/null +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java @@ -0,0 +1,91 @@ +package com.hubspot.singularity.auth.authenticator; + +import java.io.IOException; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import javax.ws.rs.container.ContainerRequestContext; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Strings; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.net.HttpHeaders; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.hubspot.singularity.SingularityUser; +import com.hubspot.singularity.WebExceptions; +import com.hubspot.singularity.config.SingularityConfiguration; +import com.hubspot.singularity.config.WebhookAuthConfiguration; +import com.ning.http.client.AsyncHttpClient; +import com.ning.http.client.Response; + +@Singleton +public class SingularityWebhookAuthenticator implements SingularityAuthenticator { + private final AsyncHttpClient asyncHttpClient; + private final WebhookAuthConfiguration webhookAuthConfiguration; + private final ObjectMapper objectMapper; + private final Cache permissionsCache; + + @Inject + public SingularityWebhookAuthenticator(AsyncHttpClient asyncHttpClient, + SingularityConfiguration configuration, + ObjectMapper objectMapper) { + this.asyncHttpClient = asyncHttpClient; + this.webhookAuthConfiguration = configuration.getWebhookAuthConfiguration(); + this.objectMapper = objectMapper; + this.permissionsCache = CacheBuilder.newBuilder() + .expireAfterWrite(webhookAuthConfiguration.getCacheValidationMs(), TimeUnit.MILLISECONDS) + .build(); + } + + @Override + public Optional getUser(ContainerRequestContext context) { + String authHeaderValue = extractAuthHeader(context); + + SingularityUserPermissionsResponse permissionsResponse = verify(authHeaderValue); + + return permissionsResponse.getUser(); + } + + private String extractAuthHeader(ContainerRequestContext context) { + final String authHeaderValue = context.getHeaderString(HttpHeaders.AUTHORIZATION); + + if (Strings.isNullOrEmpty(authHeaderValue)) { + throw WebExceptions.unauthorized("(Webhook) No Authorization header present, please log in first"); + } else { + return authHeaderValue; + } + } + + private SingularityUserPermissionsResponse verify(String authHeaderValue) { + SingularityUserPermissionsResponse maybeCachedPermissions = permissionsCache.getIfPresent(authHeaderValue); + if (maybeCachedPermissions != null) { + return maybeCachedPermissions; + } else { + try { + Response response = asyncHttpClient.prepareGet(webhookAuthConfiguration.getAuthVerificationUrl()) + .addHeader("Authorization", authHeaderValue) + .execute() + .get(); + if (response.getStatusCode() > 299) { + throw WebExceptions.unauthorized(String.format("(Webhook) Got status code %d when verifying jwt", response.getStatusCode())); + } else { + String responseBody = response.getResponseBody(); + SingularityUserPermissionsResponse permissionsResponse = objectMapper.readValue(responseBody, SingularityUserPermissionsResponse.class); + if (!permissionsResponse.getUser().isPresent()) { + throw WebExceptions.unauthorized(String.format("(Webhook) No user present in response %s", permissionsResponse)); + } + if (!permissionsResponse.getUser().get().isAuthenticated()) { + throw WebExceptions.unauthorized(String.format("(Webhook) User not authenticated (response: %s)", permissionsResponse)); + } + permissionsCache.put(authHeaderValue, permissionsResponse); + return permissionsResponse; + } + } catch (IOException|ExecutionException|InterruptedException e) { + throw WebExceptions.unauthorized(String.format("(Webhook) Exception while verifying token: %s", e.getMessage())); + } + } + } +} diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityAuthDatastore.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityAuthDatastore.java index d40d462805..b7f164a22d 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityAuthDatastore.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityAuthDatastore.java @@ -1,9 +1,10 @@ package com.hubspot.singularity.auth.datastore; -import com.google.common.base.Optional; +import java.util.Optional; + import com.hubspot.singularity.SingularityUser; public interface SingularityAuthDatastore { Optional getUser(String username); - Optional isHealthy(); + com.google.common.base.Optional isHealthy(); } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityDisabledAuthDatastore.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityDisabledAuthDatastore.java index 23b6750990..9b69c7ae5c 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityDisabledAuthDatastore.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityDisabledAuthDatastore.java @@ -1,6 +1,7 @@ package com.hubspot.singularity.auth.datastore; -import com.google.common.base.Optional; +import java.util.Optional; + import com.google.inject.Inject; import com.google.inject.Singleton; import com.hubspot.singularity.SingularityUser; @@ -12,11 +13,11 @@ public SingularityDisabledAuthDatastore() {} @Override public Optional getUser(String username) { - return Optional.absent(); + return Optional.empty(); } @Override - public Optional isHealthy() { - return Optional.absent(); + public com.google.common.base.Optional isHealthy() { + return com.google.common.base.Optional.absent(); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityDummyDatastore.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityDummyDatastore.java index 1bb4094fd4..f3dc6abd06 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityDummyDatastore.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityDummyDatastore.java @@ -14,8 +14,8 @@ public SingularityDummyDatastore() { } @Override - public Optional getUser(String username) { - return Optional.of(new SingularityUser(username, Optional.of(username), Optional.of(username), Collections.emptySet())); + public java.util.Optional getUser(String username) { + return java.util.Optional.of(new SingularityUser(username, Optional.of(username), Optional.of(username), Collections.emptySet())); } @Override diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityLDAPDatastore.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityLDAPDatastore.java index ba828a0dfe..1876a749fd 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityLDAPDatastore.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityLDAPDatastore.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.util.HashSet; +import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -19,7 +20,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Optional; import com.google.common.base.Strings; import com.google.common.base.Throwables; import com.google.common.cache.Cache; @@ -94,12 +94,12 @@ public SingularityLDAPDatastore(SingularityConfiguration configuration, .build(); ldapCache = Optional.of(cache); } else { - ldapCache = Optional.absent(); + ldapCache = Optional.empty(); } } @Override - public Optional isHealthy() { + public com.google.common.base.Optional isHealthy() { try { final LdapConnection connection = connectionPool.getConnection(); @@ -107,7 +107,7 @@ public Optional isHealthy() { if (connection.isConnected() && connection.isAuthenticated()) { connection.bind(); try { - return Optional.of(true); + return com.google.common.base.Optional.of(true); } finally { connection.unBind(); } @@ -119,7 +119,7 @@ public Optional isHealthy() { LOG.warn("LdapException caught when checking health", e); exceptionNotifier.notify(String.format("LdapException caught when checking health (%s)", e.getMessage()), e); } - return Optional.of(false); + return com.google.common.base.Optional.of(false); } @Override @@ -153,10 +153,10 @@ public Optional getUser(String user) { if (!userCursor.next()) { if (ldapCache.isPresent()) { - ldapCache.get().put(user, Optional. absent()); + ldapCache.get().put(user, Optional.empty()); } - return Optional.absent(); + return Optional.empty(); } final Entry userEntry = userCursor.get(); @@ -170,8 +170,8 @@ public Optional getUser(String user) { groups.add(cursor.get().get(configuration.getGroupNameAttribute()).getString()); } - Optional result = Optional.of(new SingularityUser(user, Optional.fromNullable(Strings.emptyToNull(userEntry.get(configuration.getUserNameAttribute()).getString())), - Optional.fromNullable(Strings.emptyToNull(userEntry.get(configuration.getUserEmailAttribute()).getString())), groups)); + Optional result = Optional.of(new SingularityUser(user, com.google.common.base.Optional.fromNullable(Strings.emptyToNull(userEntry.get(configuration.getUserNameAttribute()).getString())), + com.google.common.base.Optional.fromNullable(Strings.emptyToNull(userEntry.get(configuration.getUserEmailAttribute()).getString())), groups)); if (ldapCache.isPresent()) { ldapCache.get().put(user, result); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/config/AuthConfiguration.java b/SingularityService/src/main/java/com/hubspot/singularity/config/AuthConfiguration.java index ed33fd3e00..577abf5fa8 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/config/AuthConfiguration.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/config/AuthConfiguration.java @@ -1,11 +1,13 @@ package com.hubspot.singularity.config; import java.util.HashSet; +import java.util.List; import java.util.Set; import javax.validation.constraints.NotNull; import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.Lists; import com.hubspot.singularity.auth.SingularityAuthDatastoreClass; import com.hubspot.singularity.auth.SingularityAuthenticatorClass; @@ -15,8 +17,13 @@ public class AuthConfiguration { @JsonProperty @NotNull + @Deprecated private SingularityAuthenticatorClass authenticator = SingularityAuthenticatorClass.QUERYPARAM_PASSTHROUGH; + @JsonProperty + @NotNull + private List authenticators = Lists.newArrayList(SingularityAuthenticatorClass.QUERYPARAM_PASSTHROUGH); + @JsonProperty @NotNull private SingularityAuthDatastoreClass datastore = SingularityAuthDatastoreClass.DUMMY; @@ -53,12 +60,22 @@ public void setEnabled(boolean enabled) { this.enabled = enabled; } + @Deprecated public SingularityAuthenticatorClass getAuthenticator() { return authenticator; } public void setAuthenticator(SingularityAuthenticatorClass authenticator) { this.authenticator = authenticator; + this.authenticators.add(authenticator); + } + + public List getAuthenticators() { + return authenticators; + } + + public void setAuthenticators(List authenticators) { + this.authenticators = authenticators; } public SingularityAuthDatastoreClass getDatastore() { diff --git a/SingularityService/src/main/java/com/hubspot/singularity/config/MesosConfiguration.java b/SingularityService/src/main/java/com/hubspot/singularity/config/MesosConfiguration.java index d42a0abd2a..2d13291144 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/config/MesosConfiguration.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/config/MesosConfiguration.java @@ -45,6 +45,8 @@ public class MesosConfiguration { private int maxNumCpusPerRequest = 900; private int maxMemoryMbPerInstance = 24000; private int maxMemoryMbPerRequest = 450000; + private int maxDiskMbPerInstance = 60000; + private int maxDiskMbPerRequest = 3000000; private Optional credentialPrincipal = Optional.absent(); private Optional credentialSecret = Optional.absent(); @@ -87,6 +89,22 @@ public int getMaxMemoryMbPerRequest() { return maxMemoryMbPerRequest; } + public void setMaxDiskMbPerInstance(int maxDiskMbPerInstance) { + this.maxDiskMbPerInstance = maxDiskMbPerInstance; + } + + public int getMaxDiskMbPerInstance() { + return maxDiskMbPerInstance; + } + + public void setMaxDiskMbPerRequest(int maxDiskMbPerRequest) { + this.maxDiskMbPerRequest = maxDiskMbPerRequest; + } + + public int getMaxDiskMbPerRequest() { + return maxDiskMbPerRequest; + } + public void setMaxMemoryMbPerRequest(int maxMemoryMbPerRequest) { this.maxMemoryMbPerRequest = maxMemoryMbPerRequest; } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java b/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java index 1bd201a686..8bc02fc491 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java @@ -114,6 +114,9 @@ public class SingularityConfiguration extends Configuration { @NotNull private SlavePlacement defaultSlavePlacement = SlavePlacement.GREEDY; + @Min(value = 0, message = "Must be non-negative") + private double placementLeniency = 0.09d; + private boolean defaultValueForKillTasksOfPausedRequests = true; private int defaultDeployStepWaitTimeMs = 0; @@ -202,13 +205,17 @@ public class SingularityConfiguration extends Configuration { private int maxTasksPerOfferPerRequest = 0; - private double longRunningUsedCpuWeightForOffer = 0.30; + private double longRunningUsedCpuWeightForOffer = 0.25; + + private double longRunningUsedMemWeightForOffer = 0.65; + + private double longRunningUsedDiskWeightForOffer = 0.10; - private double longRunningUsedMemWeightForOffer = 0.70; + private double freeCpuWeightForOffer = 0.25; - private double freeCpuWeightForOffer = 0.30; + private double freeMemWeightForOffer = 0.65; - private double freeMemWeightForOffer = 0.70; + private double freeDiskWeightForOffer = 0.10; private double defaultOfferScoreForMissingUsage = 0.30; @@ -306,6 +313,10 @@ public class SingularityConfiguration extends Configuration { @Valid private LDAPConfiguration ldapConfiguration; + @JsonProperty("webhookAuth") + @Valid + private WebhookAuthConfiguration webhookAuthConfiguration = new WebhookAuthConfiguration(); + @JsonProperty("auth") @NotNull @Valid @@ -528,6 +539,10 @@ public SlavePlacement getDefaultSlavePlacement() { return defaultSlavePlacement; } + public double getPlacementLeniency() { + return placementLeniency; + } + public int getDefaultDeployStepWaitTimeMs() { return defaultDeployStepWaitTimeMs; } @@ -704,6 +719,10 @@ public double getLongRunningUsedMemWeightForOffer() { return longRunningUsedMemWeightForOffer; } + public double getLongRunningUsedDiskWeightForOffer() { + return longRunningUsedDiskWeightForOffer; + } + public double getFreeCpuWeightForOffer() { return freeCpuWeightForOffer; } @@ -712,6 +731,10 @@ public double getFreeMemWeightForOffer() { return freeMemWeightForOffer; } + public double getFreeDiskWeightForOffer() { + return freeDiskWeightForOffer; + } + public double getDefaultOfferScoreForMissingUsage() { return defaultOfferScoreForMissingUsage; } @@ -958,6 +981,10 @@ public void setDefaultSlavePlacement(SlavePlacement defaultSlavePlacement) { this.defaultSlavePlacement = defaultSlavePlacement; } + public void setPlacementLeniency(double placementLeniency) { + this.placementLeniency = placementLeniency; + } + public void setDefaultValueForKillTasksOfPausedRequests(boolean defaultValueForKillTasksOfPausedRequests) { this.defaultValueForKillTasksOfPausedRequests = defaultValueForKillTasksOfPausedRequests; } @@ -1098,6 +1125,11 @@ public SingularityConfiguration setLongRunningUsedMemWeightForOffer(double longR return this; } + public SingularityConfiguration setLongRunningUsedDiskWeightForOffer(double longRunningUsedDiskWeightForOffer) { + this.longRunningUsedDiskWeightForOffer = longRunningUsedDiskWeightForOffer; + return this; + } + public SingularityConfiguration setFreeCpuWeightForOffer(double freeCpuWeightForOffer) { this.freeCpuWeightForOffer = freeCpuWeightForOffer; return this; @@ -1108,6 +1140,11 @@ public SingularityConfiguration setFreeMemWeightForOffer(double freeMemWeightFor return this; } + public SingularityConfiguration setFreeDiskWeightForOffer(double freeDiskWeightForOffer) { + this.freeDiskWeightForOffer = freeDiskWeightForOffer; + return this; + } + public SingularityConfiguration setDefaultOfferScoreForMissingUsage(double defaultOfferScoreForMissingUsage) { this.defaultOfferScoreForMissingUsage = defaultOfferScoreForMissingUsage; return this; @@ -1256,6 +1293,14 @@ public Optional getLdapConfigurationOptional() { return Optional.fromNullable(ldapConfiguration); } + public WebhookAuthConfiguration getWebhookAuthConfiguration() { + return webhookAuthConfiguration; + } + + public void setWebhookAuthConfiguration(WebhookAuthConfiguration webhookAuthConfiguration) { + this.webhookAuthConfiguration = webhookAuthConfiguration; + } + public void setLdapConfiguration(LDAPConfiguration ldapConfiguration) { this.ldapConfiguration = ldapConfiguration; } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/config/WebhookAuthConfiguration.java b/SingularityService/src/main/java/com/hubspot/singularity/config/WebhookAuthConfiguration.java new file mode 100644 index 0000000000..b511e00ea5 --- /dev/null +++ b/SingularityService/src/main/java/com/hubspot/singularity/config/WebhookAuthConfiguration.java @@ -0,0 +1,32 @@ +package com.hubspot.singularity.config; + +import javax.annotation.Nonnegative; +import javax.validation.constraints.NotNull; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class WebhookAuthConfiguration { + @JsonProperty + @NotNull + private String authVerificationUrl = ""; + + @JsonProperty + @Nonnegative + private long cacheValidationMs = 60000; + + public String getAuthVerificationUrl() { + return authVerificationUrl; + } + + public void setAuthVerificationUrl(String authVerificationUrl) { + this.authVerificationUrl = authVerificationUrl; + } + + public long getCacheValidationMs() { + return cacheValidationMs; + } + + public void setCacheValidationMs(long cacheValidationMs) { + this.cacheValidationMs = cacheValidationMs; + } +} diff --git a/SingularityService/src/main/java/com/hubspot/singularity/data/SingularityValidator.java b/SingularityService/src/main/java/com/hubspot/singularity/data/SingularityValidator.java index 8ec36c0a21..d4a945c506 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/data/SingularityValidator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/data/SingularityValidator.java @@ -89,6 +89,8 @@ public class SingularityValidator { private final int defaultBounceExpirationMinutes; private final int maxMemoryMbPerRequest; private final int maxMemoryMbPerInstance; + private final int maxDiskMbPerRequest; + private final int maxDiskMbPerInstance; private final Optional maxTotalHealthcheckTimeoutSeconds; private final long defaultKillHealthcheckAfterSeconds; private final int defaultHealthcheckIntervalSeconds; @@ -133,6 +135,8 @@ public SingularityValidator(SingularityConfiguration configuration, DeployHistor this.maxCpusPerRequest = configuration.getMesosConfiguration().getMaxNumCpusPerRequest(); this.maxMemoryMbPerInstance = configuration.getMesosConfiguration().getMaxMemoryMbPerInstance(); this.maxMemoryMbPerRequest = configuration.getMesosConfiguration().getMaxMemoryMbPerRequest(); + this.maxDiskMbPerInstance = configuration.getMesosConfiguration().getMaxDiskMbPerInstance(); + this.maxDiskMbPerRequest = configuration.getMesosConfiguration().getMaxDiskMbPerRequest(); this.maxInstancesPerRequest = configuration.getMesosConfiguration().getMaxNumInstancesPerRequest(); this.allowBounceToSameHost = configuration.isAllowBounceToSameHost(); @@ -561,9 +565,11 @@ private void checkForIllegalResources(SingularityRequest request, SingularityDep int instances = request.getInstancesSafe(); double cpusPerInstance = deploy.getResources().or(defaultResources).getCpus(); double memoryMbPerInstance = deploy.getResources().or(defaultResources).getMemoryMb(); + double diskMbPerInstance = deploy.getResources().or(defaultResources).getDiskMb(); checkBadRequest(cpusPerInstance > 0, "Request must have more than 0 cpus"); checkBadRequest(memoryMbPerInstance > 0, "Request must have more than 0 memoryMb"); + checkBadRequest(diskMbPerInstance >= 0, "Request must have non-negative diskMb"); checkBadRequest(cpusPerInstance <= maxCpusPerInstance, "Deploy %s uses too many cpus %s (maxCpusPerInstance %s in mesos configuration)", deploy.getId(), cpusPerInstance, maxCpusPerInstance); checkBadRequest(cpusPerInstance * instances <= maxCpusPerRequest, @@ -573,6 +579,11 @@ private void checkForIllegalResources(SingularityRequest request, SingularityDep "Deploy %s uses too much memoryMb %s (maxMemoryMbPerInstance %s in mesos configuration)", deploy.getId(), memoryMbPerInstance, maxMemoryMbPerInstance); checkBadRequest(memoryMbPerInstance * instances <= maxMemoryMbPerRequest, "Deploy %s uses too much memoryMb %s (%s*%s) (maxMemoryMbPerRequest %s in mesos configuration)", deploy.getId(), memoryMbPerInstance * instances, memoryMbPerInstance, instances, maxMemoryMbPerRequest); + + checkBadRequest(diskMbPerInstance <= maxDiskMbPerInstance, + "Deploy %s uses too much diskMb %s (maxDiskMbPerInstance %s in mesos configuration)", deploy.getId(), diskMbPerInstance, maxDiskMbPerInstance); + checkBadRequest(diskMbPerInstance * instances <= maxDiskMbPerRequest, "Deploy %s uses too much diskMb %s (%s*%s) (maxDiskMbPerRequest %s in mesos configuration)", deploy.getId(), + diskMbPerInstance * instances, diskMbPerInstance, instances, maxDiskMbPerRequest); } private void checkForValidRFC5545Schedule(String schedule) { diff --git a/SingularityService/src/main/java/com/hubspot/singularity/data/zkmigrations/LastTaskStatusMigration.java b/SingularityService/src/main/java/com/hubspot/singularity/data/zkmigrations/LastTaskStatusMigration.java index cfcc3bfa79..d42c2c7750 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/data/zkmigrations/LastTaskStatusMigration.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/data/zkmigrations/LastTaskStatusMigration.java @@ -11,25 +11,28 @@ import com.google.common.collect.Lists; import com.google.inject.Inject; import com.google.inject.name.Named; -import com.hubspot.mesos.json.SingularityMesosTaskStatusObject; +import com.hubspot.mesos.protos.MesosTaskStatusObject; import com.hubspot.singularity.SingularityMainModule; import com.hubspot.singularity.SingularityTask; import com.hubspot.singularity.SingularityTaskHistoryUpdate; import com.hubspot.singularity.SingularityTaskId; import com.hubspot.singularity.SingularityTaskStatusHolder; import com.hubspot.singularity.data.TaskManager; +import com.hubspot.singularity.helpers.MesosProtosUtils; @Singleton public class LastTaskStatusMigration extends ZkDataMigration { private final TaskManager taskManager; private final String serverId; + private final MesosProtosUtils mesosProtosUtils; @Inject - public LastTaskStatusMigration(TaskManager taskManager, @Named(SingularityMainModule.SERVER_ID_PROPERTY) String serverId) { + public LastTaskStatusMigration(TaskManager taskManager, @Named(SingularityMainModule.SERVER_ID_PROPERTY) String serverId, MesosProtosUtils mesosProtosUtils) { super(1); this.taskManager = taskManager; this.serverId = serverId; + this.mesosProtosUtils = mesosProtosUtils; } @Override @@ -39,16 +42,16 @@ public void applyMigration() { for (SingularityTaskId taskId : taskIds) { List updates = Lists.reverse(taskManager.getTaskHistoryUpdates(taskId)); - Optional taskStatus = Optional.absent(); + Optional taskStatus = Optional.absent(); for (SingularityTaskHistoryUpdate update : updates) { if (update.getTaskState().toTaskState().isPresent()) { Optional task = taskManager.getTask(taskId); - taskStatus = Optional.of(SingularityMesosTaskStatusObject.fromProtos(TaskStatus.newBuilder() + taskStatus = Optional.of(mesosProtosUtils.taskStatusFromProtos(TaskStatus.newBuilder() .setTaskId(TaskID.newBuilder().setValue(taskId.getId())) - .setAgentId(task.get().getAgentId()) - .setState(update.getTaskState().toTaskState().get()) + .setAgentId(MesosProtosUtils.toAgentId(task.get().getAgentId())) + .setState(MesosProtosUtils.toTaskState(update.getTaskState())) .build())); break; diff --git a/SingularityService/src/main/java/com/hubspot/singularity/helpers/MesosProtosUtils.java b/SingularityService/src/main/java/com/hubspot/singularity/helpers/MesosProtosUtils.java new file mode 100644 index 0000000000..7790526d88 --- /dev/null +++ b/SingularityService/src/main/java/com/hubspot/singularity/helpers/MesosProtosUtils.java @@ -0,0 +1,70 @@ +package com.hubspot.singularity.helpers; + +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.mesos.v1.Protos.AgentID; +import org.apache.mesos.v1.Protos.ExecutorID; +import org.apache.mesos.v1.Protos.FrameworkID; +import org.apache.mesos.v1.Protos.Offer; +import org.apache.mesos.v1.Protos.Resource; +import org.apache.mesos.v1.Protos.TaskID; +import org.apache.mesos.v1.Protos.TaskInfo; +import org.apache.mesos.v1.Protos.TaskState; +import org.apache.mesos.v1.Protos.TaskStatus; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.inject.Inject; +import com.hubspot.mesos.protos.MesosOfferObject; +import com.hubspot.mesos.protos.MesosResourceObject; +import com.hubspot.mesos.protos.MesosStringValue; +import com.hubspot.mesos.protos.MesosTaskObject; +import com.hubspot.mesos.protos.MesosTaskStatusObject; +import com.hubspot.singularity.ExtendedTaskState; + +public class MesosProtosUtils { + private final ObjectMapper objectMapper; + + @Inject + public MesosProtosUtils(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + public MesosTaskObject taskFromProtos(TaskInfo taskInfo) { + return objectMapper.convertValue(taskInfo, MesosTaskObject.class); + } + + public MesosOfferObject offerFromProtos(Offer offer) { + return objectMapper.convertValue(offer, MesosOfferObject.class); + } + + public static AgentID toAgentId(MesosStringValue stringValue) { + return AgentID.newBuilder().setValue(stringValue.getValue()).build(); + } + + public static TaskID toTaskId(MesosStringValue stringValue) { + return TaskID.newBuilder().setValue(stringValue.getValue()).build(); + } + + public static ExecutorID toExecutorId(MesosStringValue stringValue) { + return ExecutorID.newBuilder().setValue(stringValue.getValue()).build(); + } + + public static FrameworkID toFrameworkId(MesosStringValue stringValue) { + return FrameworkID.newBuilder().setValue(stringValue.getValue()).build(); + } + + public List toResourceList(List resourceObjects) { + return resourceObjects.stream() + .map((r) -> objectMapper.convertValue(r, Resource.class)) + .collect(Collectors.toList()); + } + + public MesosTaskStatusObject taskStatusFromProtos(TaskStatus status) { + return objectMapper.convertValue(status, MesosTaskStatusObject.class); + } + + public static TaskState toTaskState(ExtendedTaskState extendedTaskState) { + return TaskState.valueOf(extendedTaskState.toTaskState().get().name()); + } +} diff --git a/SingularityBase/src/main/java/com/hubspot/mesos/MesosUtils.java b/SingularityService/src/main/java/com/hubspot/singularity/helpers/MesosUtils.java similarity index 91% rename from SingularityBase/src/main/java/com/hubspot/mesos/MesosUtils.java rename to SingularityService/src/main/java/com/hubspot/singularity/helpers/MesosUtils.java index c3929ef83f..2473efb46d 100644 --- a/SingularityBase/src/main/java/com/hubspot/mesos/MesosUtils.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/helpers/MesosUtils.java @@ -1,13 +1,12 @@ -package com.hubspot.mesos; +package com.hubspot.singularity.helpers; import java.net.UnknownHostException; import java.nio.ByteBuffer; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.Random; import java.util.Set; @@ -24,9 +23,12 @@ import com.google.common.base.Preconditions; import com.google.common.base.Throwables; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.net.InetAddresses; import com.google.common.primitives.Longs; +import com.hubspot.mesos.Resources; +import com.hubspot.singularity.ExtendedTaskState; public final class MesosUtils { @@ -148,6 +150,16 @@ public static List getAllPorts(List resources) { return ports; } + + public static Optional getPortByIndex(List resources, int index) { + List ports = MesosUtils.getAllPorts(resources); + if (index >= ports.size() || index < 0) { + return Optional.absent(); + } else { + return Optional.of(ports.get(index)); + } + } + public static Resource getPortsResource(int numPorts, Offer offer) { return getPortsResource(numPorts, offer.getResourcesList(), Collections.emptyList()); } @@ -427,8 +439,27 @@ public static Resources buildResourcesFromMesosResourceList(List resou return new Resources(getNumCpus(resources, requiredRole), getMemory(resources, requiredRole), getNumPorts(resources), getDisk(resources, requiredRole)); } - public static Path getTaskDirectoryPath(String taskId) { - return Paths.get(getSafeTaskIdForDirectory(taskId)).toAbsolutePath(); + private static final Map map; + static { + map = Maps.newHashMapWithExpectedSize(ExtendedTaskState.values().length); + for (ExtendedTaskState extendedTaskState : ExtendedTaskState.values()) { + if (extendedTaskState.toTaskState().isPresent()) { + + map.put(TaskState.valueOf(extendedTaskState.toTaskState().get().name()), extendedTaskState); + } + } + + for (TaskState t : TaskState.values()) { + if (map.get(t) == null) { + throw new IllegalStateException("No ExtendedTaskState provided for TaskState " + t + ", you probably have incompatible versions of Mesos and Singularity."); + } + } + } + + public static ExtendedTaskState fromTaskState(TaskState taskState) { + ExtendedTaskState extendedTaskState = map.get(taskState); + Preconditions.checkArgument(extendedTaskState != null, "No ExtendedTaskState for TaskState %s", taskState); + return extendedTaskState; } public static String getSafeTaskIdForDirectory(String taskId) { diff --git a/SingularityService/src/main/java/com/hubspot/singularity/helpers/SingularityMesosTaskHolder.java b/SingularityService/src/main/java/com/hubspot/singularity/helpers/SingularityMesosTaskHolder.java new file mode 100644 index 0000000000..bf5cdeffb8 --- /dev/null +++ b/SingularityService/src/main/java/com/hubspot/singularity/helpers/SingularityMesosTaskHolder.java @@ -0,0 +1,23 @@ +package com.hubspot.singularity.helpers; + +import org.apache.mesos.v1.Protos.TaskInfo; + +import com.hubspot.singularity.SingularityTask; + +public class SingularityMesosTaskHolder { + private final SingularityTask task; + private final TaskInfo mesosTask; + + public SingularityMesosTaskHolder(SingularityTask task, TaskInfo mesosTask) { + this.task = task; + this.mesosTask = mesosTask; + } + + public SingularityTask getTask() { + return task; + } + + public TaskInfo getMesosTask() { + return mesosTask; + } +} diff --git a/SingularityService/src/main/java/com/hubspot/singularity/hooks/LoadBalancerClientImpl.java b/SingularityService/src/main/java/com/hubspot/singularity/hooks/LoadBalancerClientImpl.java index 5b620cbf24..6c2cba1afd 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/hooks/LoadBalancerClientImpl.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/hooks/LoadBalancerClientImpl.java @@ -8,7 +8,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.apache.mesos.v1.Protos; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,6 +23,7 @@ import com.hubspot.baragon.models.RequestAction; import com.hubspot.baragon.models.UpstreamInfo; import com.hubspot.mesos.JavaUtils; +import com.hubspot.mesos.protos.MesosParameter; import com.hubspot.singularity.LoadBalancerRequestType.LoadBalancerRequestId; import com.hubspot.singularity.SingularityDeploy; import com.hubspot.singularity.SingularityLoadBalancerUpdate; @@ -31,6 +31,8 @@ import com.hubspot.singularity.SingularityRequest; import com.hubspot.singularity.SingularityTask; import com.hubspot.singularity.config.SingularityConfiguration; +import com.hubspot.singularity.helpers.MesosProtosUtils; +import com.hubspot.singularity.helpers.MesosUtils; import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClient.BoundRequestBuilder; import com.ning.http.client.ListenableFuture; @@ -51,17 +53,19 @@ public class LoadBalancerClientImpl implements LoadBalancerClient { private final AsyncHttpClient httpClient; private final ObjectMapper objectMapper; private final Optional taskLabelForLoadBalancerUpstreamGroup; + private final MesosProtosUtils mesosProtosUtils; private static final String OPERATION_URI = "%s/%s"; @Inject - public LoadBalancerClientImpl(SingularityConfiguration configuration, ObjectMapper objectMapper, AsyncHttpClient httpClient) { + public LoadBalancerClientImpl(SingularityConfiguration configuration, ObjectMapper objectMapper, AsyncHttpClient httpClient, MesosProtosUtils mesosProtosUtils) { this.httpClient = httpClient; this.objectMapper = objectMapper; this.loadBalancerUri = configuration.getLoadBalancerUri(); this.loadBalancerTimeoutMillis = configuration.getLoadBalancerRequestTimeoutMillis(); this.loadBalancerQueryParams = configuration.getLoadBalancerQueryParams(); this.taskLabelForLoadBalancerUpstreamGroup = configuration.getTaskLabelForLoadBalancerUpstreamGroup(); + this.mesosProtosUtils = mesosProtosUtils; } private String getLoadBalancerUri(LoadBalancerRequestId loadBalancerRequestId) { @@ -186,14 +190,14 @@ private List tasksToUpstreams(List tasks, String final List upstreams = Lists.newArrayListWithCapacity(tasks.size()); for (SingularityTask task : tasks) { - final Optional maybeLoadBalancerPort = task.getPortByIndex(task.getTaskRequest().getDeploy().getLoadBalancerPortIndex().or(0)); + final Optional maybeLoadBalancerPort = MesosUtils.getPortByIndex(mesosProtosUtils.toResourceList(task.getMesosTask().getResources()), task.getTaskRequest().getDeploy().getLoadBalancerPortIndex().or(0)); if (maybeLoadBalancerPort.isPresent()) { String upstream = String.format("%s:%d", task.getHostname(), maybeLoadBalancerPort.get()); Optional group = loadBalancerUpstreamGroup; if (taskLabelForLoadBalancerUpstreamGroup.isPresent()) { - for (Protos.Label label : task.getMesosTask().getLabels().getLabelsList()) { + for (MesosParameter label : task.getMesosTask().getLabels().getLabels()) { if (label.hasKey() && label.getKey().equals(taskLabelForLoadBalancerUpstreamGroup.get()) && label.hasValue()) { group = Optional.of(label.getValue()); break; diff --git a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityMesosModule.java b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityMesosModule.java index b0678b9898..cb374981ef 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityMesosModule.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityMesosModule.java @@ -11,6 +11,7 @@ import com.google.inject.Scopes; import com.google.inject.Singleton; import com.google.inject.name.Named; +import com.hubspot.singularity.helpers.MesosProtosUtils; public class SingularityMesosModule extends AbstractModule { @@ -19,6 +20,7 @@ public class SingularityMesosModule extends AbstractModule { @Override public void configure() { + bind(MesosProtosUtils.class).in(Scopes.SINGLETON); bind(SingularityMesosExecutorInfoSupport.class).in(Scopes.SINGLETON); bind(SingularityMesosScheduler.class).to(SingularityMesosSchedulerImpl.class).in(Scopes.SINGLETON); bind(SingularityMesosFrameworkMessageHandler.class).in(Scopes.SINGLETON); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityMesosOfferScheduler.java b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityMesosOfferScheduler.java index 07b08c0c39..d37d8dc2e8 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityMesosOfferScheduler.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityMesosOfferScheduler.java @@ -18,8 +18,9 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; import com.google.inject.Inject; -import com.hubspot.mesos.MesosUtils; +import com.hubspot.singularity.helpers.MesosUtils; import com.hubspot.mesos.Resources; +import com.hubspot.singularity.helpers.SingularityMesosTaskHolder; import com.hubspot.singularity.RequestType; import com.hubspot.singularity.SingularityDeployStatistics; import com.hubspot.singularity.SingularityPendingTaskId; @@ -163,14 +164,14 @@ public List checkOffers(final Collection offers) SingularityOfferHolder bestOffer = Collections.max(scorePerOffer.entrySet(), Map.Entry.comparingByValue()).getKey(); LOG.info("Best offer {}/1 is on {}", scorePerOffer.get(bestOffer), bestOffer.getSanitizedHost()); - SingularityTask task = acceptTask(bestOffer, tasksPerOfferPerRequest, taskRequestHolder); + SingularityMesosTaskHolder taskHolder = acceptTask(bestOffer, tasksPerOfferPerRequest, taskRequestHolder); tasksScheduled++; if (useTaskCredits) { taskCredits--; LOG.debug("Remaining task credits: {}", taskCredits); } - bestOffer.addMatchedTask(task); + bestOffer.addMatchedTask(taskHolder); addedTaskInLastLoop = true; iterator.remove(); if (useTaskCredits && taskCredits == 0) { @@ -193,18 +194,24 @@ public List checkOffers(final Collection offers) private double getNormalizedWeight(ResourceUsageType type) { double freeCpuWeight = configuration.getFreeCpuWeightForOffer(); double freeMemWeight = configuration.getFreeMemWeightForOffer(); + double freeDiskWeight = configuration.getFreeDiskWeightForOffer(); double usedCpuWeight = configuration.getLongRunningUsedCpuWeightForOffer(); double usedMemWeight = configuration.getLongRunningUsedMemWeightForOffer(); + double usedDiskWeight = configuration.getLongRunningUsedDiskWeightForOffer(); switch (type) { case CPU_FREE: - return freeCpuWeight + freeMemWeight != 1 ? freeCpuWeight / (freeCpuWeight + freeMemWeight) : freeCpuWeight; + return freeCpuWeight + freeMemWeight + freeDiskWeight != 1 ? freeCpuWeight / (freeCpuWeight + freeMemWeight + freeDiskWeight) : freeCpuWeight; case MEMORY_BYTES_FREE: - return freeCpuWeight + freeMemWeight != 1 ? freeMemWeight / (freeCpuWeight + freeMemWeight) : freeMemWeight; + return freeCpuWeight + freeMemWeight + freeDiskWeight != 1 ? freeMemWeight / (freeCpuWeight + freeMemWeight + freeDiskWeight) : freeMemWeight; + case DISK_BYTES_FREE: + return freeCpuWeight + freeMemWeight + freeDiskWeight != 1 ? freeDiskWeight / (freeCpuWeight + freeMemWeight + freeDiskWeight) : freeDiskWeight; case CPU_USED: - return usedCpuWeight + usedMemWeight != 1 ? usedCpuWeight / (usedCpuWeight + usedMemWeight) : usedCpuWeight; + return usedCpuWeight + usedMemWeight + usedDiskWeight != 1 ? usedCpuWeight / (usedCpuWeight + usedMemWeight + usedDiskWeight) : usedCpuWeight; case MEMORY_BYTES_USED: - return usedCpuWeight + usedMemWeight != 1 ? usedMemWeight / (usedCpuWeight + usedMemWeight) : usedMemWeight; + return usedCpuWeight + usedMemWeight + usedDiskWeight != 1 ? usedMemWeight / (usedCpuWeight + usedMemWeight + usedDiskWeight) : usedMemWeight; + case DISK_BYTES_USED: + return usedCpuWeight + usedMemWeight + usedDiskWeight != 1 ? usedDiskWeight / (usedCpuWeight + usedMemWeight + usedDiskWeight) : usedDiskWeight; default: LOG.error("Invalid ResourceUsageType {}", type); return 0; @@ -295,32 +302,35 @@ private double score(SingularityOfferHolder offerHolder, Map maybeSlaveUsage) { return !maybeSlaveUsage.isPresent() || - !maybeSlaveUsage.get().getCpusTotal().isPresent() || !maybeSlaveUsage.get().getMemoryMbTotal().isPresent() || + !maybeSlaveUsage.get().getCpusTotal().isPresent() || !maybeSlaveUsage.get().getMemoryMbTotal().isPresent() || !maybeSlaveUsage.get().getDiskMbTotal().isPresent() || maybeSlaveUsage.get().getLongRunningTasksUsage() == null || !maybeSlaveUsage.get().getLongRunningTasksUsage().containsKey(ResourceUsageType.CPU_USED) || - !maybeSlaveUsage.get().getLongRunningTasksUsage().containsKey(ResourceUsageType.MEMORY_BYTES_USED); + !maybeSlaveUsage.get().getLongRunningTasksUsage().containsKey(ResourceUsageType.MEMORY_BYTES_USED) || + !maybeSlaveUsage.get().getLongRunningTasksUsage().containsKey(ResourceUsageType.DISK_BYTES_USED); } private boolean isLongRunning(SingularityTaskRequest taskRequest) { return taskRequest.getRequest().getRequestType().isLongRunning(); } - private double scoreLongRunningTask(double longRunningMemUsedScore, double memFreeScore, double longRunningCpusUsedScore, double cpusFreeScore) { + private double scoreLongRunningTask(double longRunningMemUsedScore, double memFreeScore, double longRunningCpusUsedScore, double cpusFreeScore, double longRunningDiskUsedScore, double diskFreeScore) { // unused, reserved resources improve score - return calculateScore(1 - longRunningMemUsedScore, memFreeScore, 1 - longRunningCpusUsedScore, cpusFreeScore, 0.50, 0.50); + return calculateScore(1 - longRunningMemUsedScore, memFreeScore, 1 - longRunningCpusUsedScore, cpusFreeScore, 1 - longRunningDiskUsedScore, diskFreeScore, 0.50, 0.50); } - private double scoreNonLongRunningTask(SingularityTaskRequest taskRequest, double longRunningMemUsedScore, double memFreeScore, double longRunningCpusUsedScore, double cpusFreeScore) { + private double scoreNonLongRunningTask(SingularityTaskRequest taskRequest, double longRunningMemUsedScore, double memFreeScore, double longRunningCpusUsedScore, double cpusFreeScore, double longRunningDiskUsedScore, double diskFreeScore) { Optional statistics = deployManager.getDeployStatistics(taskRequest.getRequest().getId(), taskRequest.getDeploy().getId()); final double epsilon = 0.0001; @@ -332,44 +342,46 @@ private double scoreNonLongRunningTask(SingularityTaskRequest taskRequest, doubl usedResourceWeight = Math.min((double) TimeUnit.MILLISECONDS.toSeconds(statistics.get().getAverageRuntimeMillis().get()) / configuration.getConsiderNonLongRunningTaskLongRunningAfterRunningForSeconds(), 1) * maxNonLongRunningUsedResourceWeight; if (Math.abs(usedResourceWeight - maxNonLongRunningUsedResourceWeight) < epsilon) { - return scoreLongRunningTask(longRunningMemUsedScore, memFreeScore, longRunningCpusUsedScore, cpusFreeScore); + return scoreLongRunningTask(longRunningMemUsedScore, memFreeScore, longRunningCpusUsedScore, cpusFreeScore, longRunningDiskUsedScore, diskFreeScore); } freeResourceWeight = 1 - usedResourceWeight; } // usage reduces score - return calculateScore(longRunningMemUsedScore, memFreeScore, longRunningCpusUsedScore, cpusFreeScore, freeResourceWeight, usedResourceWeight * -1); + return calculateScore(longRunningMemUsedScore, memFreeScore, longRunningCpusUsedScore, cpusFreeScore, longRunningDiskUsedScore, diskFreeScore, freeResourceWeight, usedResourceWeight * -1); } - private double calculateScore(double longRunningMemUsedScore, double memFreeScore, double longRunningCpusUsedScore, double cpusFreeScore, double freeResourceWeight, double usedResourceWeight) { + private double calculateScore(double longRunningMemUsedScore, double memFreeScore, double longRunningCpusUsedScore, double cpusFreeScore, double longRunningDiskUsedScore, double diskFreeScore, double freeResourceWeight, double usedResourceWeight) { double score = 0; score += (getNormalizedWeight(ResourceUsageType.CPU_USED) * usedResourceWeight) * longRunningCpusUsedScore; score += (getNormalizedWeight(ResourceUsageType.MEMORY_BYTES_USED) * usedResourceWeight) * longRunningMemUsedScore; + score += (getNormalizedWeight(ResourceUsageType.DISK_BYTES_USED) * usedResourceWeight) * longRunningDiskUsedScore; score += (getNormalizedWeight(ResourceUsageType.CPU_FREE) * freeResourceWeight) * cpusFreeScore; score += (getNormalizedWeight(ResourceUsageType.MEMORY_BYTES_FREE) * freeResourceWeight) * memFreeScore; + score += (getNormalizedWeight(ResourceUsageType.DISK_BYTES_FREE) * freeResourceWeight) * diskFreeScore; return score; } - private SingularityTask acceptTask(SingularityOfferHolder offerHolder, Map> tasksPerOfferPerRequest, SingularityTaskRequestHolder taskRequestHolder) { + private SingularityMesosTaskHolder acceptTask(SingularityOfferHolder offerHolder, Map> tasksPerOfferPerRequest, SingularityTaskRequestHolder taskRequestHolder) { final SingularityTaskRequest taskRequest = taskRequestHolder.getTaskRequest(); - final SingularityTask task = mesosTaskBuilder.buildTask(offerHolder, offerHolder.getCurrentResources(), taskRequest, taskRequestHolder.getTaskResources(), taskRequestHolder.getExecutorResources()); + final SingularityMesosTaskHolder taskHolder = mesosTaskBuilder.buildTask(offerHolder, offerHolder.getCurrentResources(), taskRequest, taskRequestHolder.getTaskResources(), taskRequestHolder.getExecutorResources()); - final SingularityTask zkTask = taskSizeOptimizer.getSizeOptimizedTask(task); + final SingularityTask zkTask = taskSizeOptimizer.getSizeOptimizedTask(taskHolder); if (LOG.isTraceEnabled()) { LOG.trace("Accepted and built task {}", zkTask); } - LOG.info("Launching task {} slot on slave {} ({})", task.getTaskId(), offerHolder.getSlaveId(), offerHolder.getHostname()); + LOG.info("Launching task {} slot on slave {} ({})", taskHolder.getTask().getTaskId(), offerHolder.getSlaveId(), offerHolder.getHostname()); taskManager.createTaskAndDeletePendingTask(zkTask); addRequestToMapByOfferHost(tasksPerOfferPerRequest, offerHolder.getHostname(), taskRequest.getRequest().getId()); - return task; + return taskHolder; } private void addRequestToMapByOfferHost(Map> tasksPerOfferHostPerRequest, String hostname, String requestId) { diff --git a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityMesosSchedulerImpl.java b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityMesosSchedulerImpl.java index 50027af75f..c65836138b 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityMesosSchedulerImpl.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityMesosSchedulerImpl.java @@ -39,7 +39,8 @@ import com.google.inject.name.Named; import com.google.protobuf.ByteString; import com.hubspot.mesos.JavaUtils; -import com.hubspot.mesos.MesosUtils; +import com.hubspot.singularity.helpers.MesosProtosUtils; +import com.hubspot.singularity.helpers.MesosUtils; import com.hubspot.singularity.RequestCleanupType; import com.hubspot.singularity.SingularityAbort; import com.hubspot.singularity.SingularityAbort.AbortReason; @@ -424,8 +425,8 @@ public void killAndRecord(SingularityTaskId taskId, Optional if (task.get().getTaskRequest().getDeploy().getCustomExecutorCmd().isPresent()) { byte[] messageBytes = transcoder.toBytes(new SingularityTaskDestroyFrameworkMessage(taskId, user)); message(Message.newBuilder() - .setAgentId(task.get().getMesosTask().getAgentId()) - .setExecutorId(task.get().getMesosTask().getExecutor().getExecutorId()) + .setAgentId(MesosProtosUtils.toAgentId(task.get().getMesosTask().getAgentId())) + .setExecutorId(MesosProtosUtils.toExecutorId(task.get().getMesosTask().getExecutor().getExecutorId())) .setData(ByteString.copyFrom(messageBytes)) .build()); } else { diff --git a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityMesosStatusUpdateHandler.java b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityMesosStatusUpdateHandler.java index 6837db6c3a..956f422cc9 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityMesosStatusUpdateHandler.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityMesosStatusUpdateHandler.java @@ -17,7 +17,8 @@ import com.google.inject.Singleton; import com.google.inject.name.Named; import com.hubspot.mesos.JavaUtils; -import com.hubspot.mesos.json.SingularityMesosTaskStatusObject; +import com.hubspot.singularity.helpers.MesosProtosUtils; +import com.hubspot.singularity.helpers.MesosUtils; import com.hubspot.singularity.ExtendedTaskState; import com.hubspot.singularity.InvalidSingularityTaskIdException; import com.hubspot.singularity.SingularityCreateResult; @@ -55,6 +56,7 @@ public class SingularityMesosStatusUpdateHandler { private final SingularityMesosExecutorInfoSupport logSupport; private final SingularityScheduler scheduler; private final SingularityLeaderCache leaderCache; + private final MesosProtosUtils mesosProtosUtils; private final String serverId; private final SingularityMesosSchedulerClient schedulerClient; private final SingularitySchedulerLock schedulerLock; @@ -79,6 +81,7 @@ public SingularityMesosStatusUpdateHandler(TaskManager taskManager, SingularitySchedulerLock schedulerLock, SingularityConfiguration configuration, SingularityLeaderCache leaderCache, + MesosProtosUtils mesosProtosUtils, @Named(SingularityMesosModule.TASK_LOST_REASONS_COUNTER) Multiset taskLostReasons, @Named(SingularityMainModule.LOST_TASKS_METER) Meter lostTasksMeter, @Named(SingularityMainModule.STATUS_UPDATE_DELTAS) ConcurrentHashMap statusUpdateDeltas) { @@ -93,6 +96,7 @@ public SingularityMesosStatusUpdateHandler(TaskManager taskManager, this.logSupport = logSupport; this.scheduler = scheduler; this.leaderCache = leaderCache; + this.mesosProtosUtils = mesosProtosUtils; this.serverId = serverId; this.schedulerClient = schedulerClient; this.schedulerLock = schedulerLock; @@ -151,7 +155,13 @@ private Optional getStatusMessage(Protos.TaskStatus status, Optionalabsent()); + final SingularityTaskStatusHolder newTaskStatusHolder = new SingularityTaskStatusHolder(taskIdObj, Optional.of(mesosProtosUtils.taskStatusFromProtos(status)), System.currentTimeMillis(), serverId, Optional.absent()); final Optional previousTaskStatusHolder = taskManager.getLastActiveTaskStatus(taskIdObj); - final ExtendedTaskState taskState = ExtendedTaskState.fromTaskState(status.getState()); + final ExtendedTaskState taskState = MesosUtils.fromTaskState(status.getState()); if (isDuplicateOrIgnorableStatusUpdate(previousTaskStatusHolder, newTaskStatusHolder)) { LOG.trace("Ignoring status update {} to {}", taskState, taskIdObj); @@ -244,6 +254,8 @@ private void unsafeProcessStatusUpdate(Protos.TaskStatus status) { scheduler.handleCompletedTask(task, taskIdObj, isActiveTask, timestamp, taskState, taskHistoryUpdateCreateResult, status); } + System.out.println(newTaskStatusHolder); + saveNewTaskStatusHolder(taskIdObj, newTaskStatusHolder, taskState); } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityMesosTaskBuilder.java b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityMesosTaskBuilder.java index 29a2f35a03..ac604227ed 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityMesosTaskBuilder.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityMesosTaskBuilder.java @@ -36,7 +36,8 @@ import com.google.inject.Inject; import com.google.protobuf.ByteString; import com.hubspot.deploy.ExecutorDataBuilder; -import com.hubspot.mesos.MesosUtils; +import com.hubspot.singularity.helpers.MesosProtosUtils; +import com.hubspot.singularity.helpers.MesosUtils; import com.hubspot.mesos.Resources; import com.hubspot.mesos.SingularityContainerInfo; import com.hubspot.mesos.SingularityDockerInfo; @@ -44,10 +45,9 @@ import com.hubspot.mesos.SingularityDockerParameter; import com.hubspot.mesos.SingularityDockerPortMapping; import com.hubspot.mesos.SingularityMesosArtifact; +import com.hubspot.singularity.helpers.SingularityMesosTaskHolder; import com.hubspot.mesos.SingularityMesosTaskLabel; import com.hubspot.mesos.SingularityVolume; -import com.hubspot.mesos.json.SingularityMesosOfferObject; -import com.hubspot.mesos.json.SingularityMesosTaskObject; import com.hubspot.singularity.SingularityS3UploaderFile; import com.hubspot.singularity.SingularityTask; import com.hubspot.singularity.SingularityTaskExecutorData; @@ -64,15 +64,17 @@ class SingularityMesosTaskBuilder { private final ObjectMapper objectMapper; private final ExecutorIdGenerator idGenerator; private final SingularityConfiguration configuration; + private final MesosProtosUtils mesosProtosUtils; @Inject - SingularityMesosTaskBuilder(ObjectMapper objectMapper, ExecutorIdGenerator idGenerator, SingularityConfiguration configuration) { + SingularityMesosTaskBuilder(ObjectMapper objectMapper, ExecutorIdGenerator idGenerator, SingularityConfiguration configuration, MesosProtosUtils mesosProtosUtils) { this.objectMapper = objectMapper; this.idGenerator = idGenerator; this.configuration = configuration; + this.mesosProtosUtils = mesosProtosUtils; } - public SingularityTask buildTask(SingularityOfferHolder offerHolder, List availableResources, SingularityTaskRequest taskRequest, Resources desiredTaskResources, Resources desiredExecutorResources) { + public SingularityMesosTaskHolder buildTask(SingularityOfferHolder offerHolder, List availableResources, SingularityTaskRequest taskRequest, Resources desiredTaskResources, Resources desiredExecutorResources) { final String sanitizedRackId = offerHolder.getSanitizedRackId(); final String sanitizedHost = offerHolder.getSanitizedHost(); @@ -113,15 +115,7 @@ public SingularityTask buildTask(SingularityOfferHolder offerHolder, List requiredRole = taskRequest.getRequest().getRequiredRole(); bldr.addResources(MesosUtils.getCpuResource(desiredTaskResources.getCpus(), requiredRole)); bldr.addResources(MesosUtils.getMemoryResource(desiredTaskResources.getMemoryMb(), requiredRole)); - - if (desiredTaskResources.getDiskMb() > 0) { - bldr.addResources(MesosUtils.getDiskResource(desiredTaskResources.getDiskMb(), requiredRole)); - } else if (MesosUtils.getDisk(offerHolder.getCurrentResources(), Optional.absent()) >= 1.0) { - // If this offer contains 1MB of disk resources, claim it to enable disk usage reporting. - // This is just a temporary hack to enable disk usage reporting where we can, not an actual way to match task disk requirements to offers. - offerHolder.subtractResources(Collections.singletonList(MesosUtils.getDiskResource(1.0, Optional.absent()))); - bldr.addResources(MesosUtils.getDiskResource(1.0, requiredRole)); - } + bldr.addResources(MesosUtils.getDiskResource(desiredTaskResources.getDiskMb(), requiredRole)); bldr.setAgentId(offerHolder.getOffers().get(0).getAgentId()); @@ -156,12 +150,13 @@ public SingularityTask buildTask(SingularityOfferHolder offerHolder, List mesosProtosUtils.offerFromProtos(o)).collect(Collectors.toList()), + mesosProtosUtils.taskFromProtos(task), + Optional.of(offerHolder.getRackId())), + task); } private boolean hasLiteralPortMapping(Optional maybeContainerInfo) { diff --git a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityOfferHolder.java b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityOfferHolder.java index 96ee197696..fac7ebbab1 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityOfferHolder.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityOfferHolder.java @@ -20,9 +20,9 @@ import com.google.common.base.Optional; import com.google.common.collect.Lists; import com.hubspot.mesos.JavaUtils; -import com.hubspot.mesos.MesosUtils; +import com.hubspot.singularity.helpers.MesosUtils; +import com.hubspot.singularity.helpers.SingularityMesosTaskHolder; import com.hubspot.singularity.SingularityPendingTaskId; -import com.hubspot.singularity.SingularityTask; import com.hubspot.singularity.SingularityTaskId; public class SingularityOfferHolder { @@ -30,7 +30,7 @@ public class SingularityOfferHolder { private static final Logger LOG = LoggerFactory.getLogger(SingularityMesosScheduler.class); private final List offers; - private final List acceptedTasks; + private final List acceptedTasks; private final Set rejectedPendingTaskIds; private List currentResources; private Set roles; @@ -103,16 +103,16 @@ boolean hasRejectedPendingTaskAlready(SingularityPendingTaskId pendingTaskId) { return rejectedPendingTaskIds.contains(pendingTaskId); } - public void addMatchedTask(SingularityTask task) { - LOG.trace("Accepting task {} for offers {}", task.getTaskId(), offers.stream().map(Offer::getId).collect(Collectors.toList())); - acceptedTasks.add(task); + public void addMatchedTask(SingularityMesosTaskHolder taskHolder) { + LOG.trace("Accepting task {} for offers {}", taskHolder.getTask().getTaskId(), offers.stream().map(Offer::getId).collect(Collectors.toList())); + acceptedTasks.add(taskHolder); // subtract task resources from offer - subtractResources(task.getMesosTask().getResources()); + subtractResources(taskHolder.getMesosTask().getResourcesList()); // subtract executor resources from offer, if any are defined - if (task.getMesosTask().hasExecutor() && task.getMesosTask().getExecutor().getResourcesCount() > 0) { - subtractResources(task.getMesosTask().getExecutor().getResourcesList()); + if (taskHolder.getMesosTask().hasExecutor() && taskHolder.getMesosTask().getExecutor().getResourcesCount() > 0) { + subtractResources(taskHolder.getMesosTask().getExecutor().getResourcesList()); } } @@ -124,11 +124,11 @@ public List launchTasksAndGetUnusedOffers(SingularityMesosSchedulerClient final List toLaunch = Lists.newArrayListWithCapacity(acceptedTasks.size()); final List taskIds = Lists.newArrayListWithCapacity(acceptedTasks.size()); - for (SingularityTask task : acceptedTasks) { - taskIds.add(task.getTaskId()); - toLaunch.add(task.getMesosTaskProtos()); - LOG.debug("Launching {} with offer {}", task.getTaskId(), offers.get(0).getId()); - LOG.trace("Launching {} mesos task: {}", task.getTaskId(), MesosUtils.formatForLogging(task.getMesosTask())); + for (SingularityMesosTaskHolder taskHolder : acceptedTasks) { + taskIds.add(taskHolder.getTask().getTaskId()); + toLaunch.add(taskHolder.getMesosTask()); + LOG.debug("Launching {} with offer {}", taskHolder.getTask().getTaskId(), offers.get(0).getId()); + LOG.trace("Launching {} mesos task: {}", taskHolder.getTask().getTaskId(), MesosUtils.formatForLogging(taskHolder.getMesosTask())); } // At this point, `currentResources` contains a list of unused resources, because we subtracted out the required resources of every task we accepted. @@ -182,7 +182,7 @@ public List launchTasksAndGetUnusedOffers(SingularityMesosSchedulerClient return leftoverOffers; } - public List getAcceptedTasks() { + public List getAcceptedTasks() { return acceptedTasks; } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularitySlaveAndRackManager.java b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularitySlaveAndRackManager.java index 797002b89f..9179b66ba2 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularitySlaveAndRackManager.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularitySlaveAndRackManager.java @@ -11,8 +11,8 @@ import javax.inject.Singleton; -import org.apache.mesos.v1.Protos.Offer; import org.apache.mesos.v1.Protos.AgentID; +import org.apache.mesos.v1.Protos.Offer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,6 +30,7 @@ import com.hubspot.singularity.SingularityMachineAbstraction; import com.hubspot.singularity.SingularityPendingRequest.PendingType; import com.hubspot.singularity.SingularityPendingTask; +import com.hubspot.singularity.SingularityPendingTaskId; import com.hubspot.singularity.SingularityRack; import com.hubspot.singularity.SingularityRequest; import com.hubspot.singularity.SingularitySlave; @@ -217,19 +218,21 @@ public SlaveMatchState doesOfferMatch(SingularityOfferHolder offerHolder, Singul // If no tasks are active for this request yet, we can fall back to greedy. if (currentlyActiveTasksForRequestClusterwide.size() > 0) { + Collection pendingTasksForRequestClusterwide = leaderCache.getPendingTaskIdsForRequest(taskRequest.getRequest().getId()); Set currentHostsForRequest = currentlyActiveTasksForRequestClusterwide.stream() .map(SingularityTaskId::getSanitizedHost) .collect(Collectors.toSet()); final double numPerSlave = currentlyActiveTasksForRequestClusterwide.size() / (double) currentHostsForRequest.size(); - - final boolean isSlaveOk = numOnSlave <= numPerSlave; + final double leniencyCoefficient = configuration.getPlacementLeniency(); + final double threshold = numPerSlave * (1 + (pendingTasksForRequestClusterwide.size() * leniencyCoefficient)); + final boolean isSlaveOk = numOnSlave <= threshold; if (!isSlaveOk) { LOG.trace( - "Rejecting OPTIMISTIC task {} from slave {} ({}) due to numOnSlave {} and numPerSlave {} (based on currentlyActiveTasksForRequest {} and currentHostsForRequest {})", - taskRequest.getRequest().getId(), slaveId, host, numOnSlave, numPerSlave, currentlyActiveTasksForRequestClusterwide.size(), currentHostsForRequest.size() + "Rejecting OPTIMISTIC task {} from slave {} ({}) because numOnSlave {} violates threshold {} (based on active tasks for request {}, current hosts for request {}, pending tasks for request {})", + taskRequest.getRequest().getId(), slaveId, host, numOnSlave, threshold, currentlyActiveTasksForRequestClusterwide.size(), currentHostsForRequest.size(), pendingTasksForRequestClusterwide.size() ); return SlaveMatchState.SLAVE_SATURATED; } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityStartup.java b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityStartup.java index ad0e1623b2..5034d0b3ae 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityStartup.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityStartup.java @@ -14,7 +14,7 @@ import com.google.common.collect.Maps; import com.google.inject.Inject; import com.hubspot.mesos.JavaUtils; -import com.hubspot.mesos.MesosUtils; +import com.hubspot.singularity.helpers.MesosUtils; import com.hubspot.mesos.client.MesosClient; import com.hubspot.mesos.json.MesosMasterStateObject; import com.hubspot.singularity.RequestType; diff --git a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityTaskSizeOptimizer.java b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityTaskSizeOptimizer.java index 6809cf9f55..5223ff1233 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityTaskSizeOptimizer.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularityTaskSizeOptimizer.java @@ -8,8 +8,9 @@ import com.google.common.base.Optional; import com.google.inject.Inject; import com.google.inject.Singleton; -import com.hubspot.mesos.json.SingularityMesosOfferObject; -import com.hubspot.mesos.json.SingularityMesosTaskObject; +import com.hubspot.singularity.helpers.MesosProtosUtils; +import com.hubspot.singularity.helpers.SingularityMesosTaskHolder; +import com.hubspot.mesos.protos.MesosOfferObject; import com.hubspot.singularity.SingularityDeployBuilder; import com.hubspot.singularity.SingularityTask; import com.hubspot.singularity.SingularityTaskRequest; @@ -19,24 +20,28 @@ public class SingularityTaskSizeOptimizer { private final SingularityConfiguration configuration; + private final MesosProtosUtils mesosProtosUtils; @Inject - public SingularityTaskSizeOptimizer(SingularityConfiguration configuration) { + public SingularityTaskSizeOptimizer(SingularityConfiguration configuration, MesosProtosUtils mesosProtosUtils) { this.configuration = configuration; + this.mesosProtosUtils = mesosProtosUtils; } - SingularityTask getSizeOptimizedTask(SingularityTask task) { + SingularityTask getSizeOptimizedTask(SingularityMesosTaskHolder taskHolder) { if (configuration.isStoreAllMesosTaskInfoForDebugging()) { - return task; + return taskHolder.getTask(); } - TaskInfo.Builder mesosTask = task.getMesosTaskProtos().toBuilder(); + SingularityTask task = taskHolder.getTask(); + + TaskInfo.Builder mesosTask = taskHolder.getMesosTask().toBuilder(); mesosTask.clearData(); - List offers = task.getOffers() + List offers = task.getOffers() .stream() - .map(SingularityMesosOfferObject::sizeOptimized) + .map(MesosOfferObject::sizeOptimized) .collect(Collectors.toList()); SingularityTaskRequest taskRequest = task.getTaskRequest(); @@ -51,7 +56,7 @@ SingularityTask getSizeOptimizedTask(SingularityTask task) { deploy.build(), task.getTaskRequest().getPendingTask()); } - return new SingularityTask(taskRequest, task.getTaskId(), offers, SingularityMesosTaskObject.fromProtos(mesosTask.build()), task.getRackId()); + return new SingularityTask(taskRequest, task.getTaskId(), offers, mesosProtosUtils.taskFromProtos(mesosTask.build()), task.getRackId()); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractHistoryResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractHistoryResource.java index 5ef3c36138..3d26842987 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractHistoryResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractHistoryResource.java @@ -21,14 +21,12 @@ public abstract class AbstractHistoryResource { protected final TaskManager taskManager; protected final DeployManager deployManager; protected final SingularityAuthorizationHelper authorizationHelper; - protected final Optional user; - public AbstractHistoryResource(HistoryManager historyManager, TaskManager taskManager, DeployManager deployManager, SingularityAuthorizationHelper authorizationHelper, Optional user) { + public AbstractHistoryResource(HistoryManager historyManager, TaskManager taskManager, DeployManager deployManager, SingularityAuthorizationHelper authorizationHelper) { this.historyManager = historyManager; this.taskManager = taskManager; this.deployManager = deployManager; this.authorizationHelper = authorizationHelper; - this.user = user; } protected SingularityTaskId getTaskIdObject(String taskId) { @@ -39,7 +37,7 @@ protected SingularityTaskId getTaskIdObject(String taskId) { } } - protected Optional getTaskHistory(SingularityTaskId taskId) { + protected Optional getTaskHistory(SingularityTaskId taskId, SingularityUser user) { authorizationHelper.checkForAuthorizationByRequestId(taskId.getRequestId(), user, SingularityAuthorizationScope.READ); Optional history = taskManager.getTaskHistory(taskId); @@ -51,15 +49,15 @@ protected Optional getTaskHistory(SingularityTaskId task return history; } - protected SingularityTaskHistory getTaskHistoryRequired(SingularityTaskId taskId) { - Optional history = getTaskHistory(taskId); + protected SingularityTaskHistory getTaskHistoryRequired(SingularityTaskId taskId, SingularityUser user) { + Optional history = getTaskHistory(taskId, user); checkNotFound(history.isPresent(), "No history for task %s", taskId); return history.get(); } - protected SingularityDeployHistory getDeployHistory(String requestId, String deployId) { + protected SingularityDeployHistory getDeployHistory(String requestId, String deployId, SingularityUser user) { authorizationHelper.checkForAuthorizationByRequestId(requestId, user, SingularityAuthorizationScope.READ); Optional deployHistory = deployManager.getDeployHistory(requestId, deployId, true); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractMachineResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractMachineResource.java index f6b562ef29..079d3f61a5 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractMachineResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractMachineResource.java @@ -24,29 +24,27 @@ public abstract class AbstractMachineResource> { protected final AbstractMachineManager manager; - protected final Optional user; protected final SingularityAuthorizationHelper authorizationHelper; private final SingularityValidator validator; - public AbstractMachineResource(AbstractMachineManager manager, SingularityAuthorizationHelper authorizationHelper, Optional user, SingularityValidator validator) { + public AbstractMachineResource(AbstractMachineManager manager, SingularityAuthorizationHelper authorizationHelper, SingularityValidator validator) { this.manager = manager; this.authorizationHelper = authorizationHelper; - this.user = user; this.validator = validator; } - protected void remove(String objectId) { + protected void remove(String objectId, SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); checkNotFound(manager.deleteObject(objectId) == SingularityDeleteResult.DELETED, "Couldn't find dead %s with id %s", getObjectTypeString(), objectId); } - protected void cancelExpiring(String objectId) { + protected void cancelExpiring(String objectId, SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); manager.deleteExpiringObject(objectId); } - protected List getExpiringStateChanges() { + protected List getExpiringStateChanges(SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); return manager.getExpiringObjects(); } @@ -73,36 +71,36 @@ private void changeState(String objectId, MachineState newState, Optional decommissionRequest, Optional queryUser, SingularityAction action) { + protected void decommission(String objectId, Optional decommissionRequest, SingularityUser user, SingularityAction action) { authorizationHelper.checkAdminAuthorization(user); validator.checkActionEnabled(action); validator.validateExpiringMachineStateChange(decommissionRequest, MachineState.STARTING_DECOMMISSION, manager.getExpiringObject(objectId)); validator.validateDecommissioningCount(); - changeState(objectId, MachineState.STARTING_DECOMMISSION, decommissionRequest, queryUser); - saveExpiring(decommissionRequest, queryUser, objectId); + changeState(objectId, MachineState.STARTING_DECOMMISSION, decommissionRequest, user.getEmail()); + saveExpiring(decommissionRequest, user, objectId); } - protected void freeze(String objectId, Optional freezeRequest, Optional queryUser, SingularityAction action) { + protected void freeze(String objectId, Optional freezeRequest, SingularityUser user, SingularityAction action) { authorizationHelper.checkAdminAuthorization(user); validator.checkActionEnabled(action); validator.validateExpiringMachineStateChange(freezeRequest, MachineState.FROZEN, manager.getExpiringObject(objectId)); - changeState(objectId, MachineState.FROZEN, freezeRequest, queryUser); - saveExpiring(freezeRequest, queryUser, objectId); + changeState(objectId, MachineState.FROZEN, freezeRequest, user.getEmail()); + saveExpiring(freezeRequest, user, objectId); } - protected void activate(String objectId, Optional activateRequest, Optional queryUser, SingularityAction action) { + protected void activate(String objectId, Optional activateRequest, SingularityUser user, SingularityAction action) { authorizationHelper.checkAdminAuthorization(user); validator.checkActionEnabled(action); validator.validateExpiringMachineStateChange(activateRequest, MachineState.ACTIVE, manager.getExpiringObject(objectId)); - changeState(objectId, MachineState.ACTIVE, activateRequest, queryUser); - saveExpiring(activateRequest, queryUser, objectId); + changeState(objectId, MachineState.ACTIVE, activateRequest, user.getEmail()); + saveExpiring(activateRequest, user, objectId); } - private void saveExpiring(Optional changeRequest, Optional queryUser, String objectId) { + private void saveExpiring(Optional changeRequest, SingularityUser user, String objectId) { if (changeRequest.isPresent() && changeRequest.get().getDurationMillis().isPresent()) { manager.saveExpiringObject( new SingularityExpiringMachineState( - queryUser, + user.getEmail(), System.currentTimeMillis(), changeRequest.get().getActionId().or(UUID.randomUUID().toString()), changeRequest.get(), diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractRequestResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractRequestResource.java index 6fab114f00..12569c0465 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractRequestResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractRequestResource.java @@ -25,25 +25,23 @@ public class AbstractRequestResource extends AbstractLeaderAwareResource { protected final RequestManager requestManager; protected final DeployManager deployManager; - protected final Optional user; protected final SingularityValidator validator; protected final SingularityAuthorizationHelper authorizationHelper; - public AbstractRequestResource(RequestManager requestManager, DeployManager deployManager, Optional user, SingularityValidator validator, SingularityAuthorizationHelper authorizationHelper, + public AbstractRequestResource(RequestManager requestManager, DeployManager deployManager, SingularityValidator validator, SingularityAuthorizationHelper authorizationHelper, AsyncHttpClient httpClient, LeaderLatch leaderLatch, ObjectMapper objectMapper) { super(httpClient, leaderLatch, objectMapper); this.requestManager = requestManager; this.deployManager = deployManager; - this.user = user; this.validator = validator; this.authorizationHelper = authorizationHelper; } - protected SingularityRequestWithState fetchRequestWithState(String requestId) { - return fetchRequestWithState(requestId, false); + protected SingularityRequestWithState fetchRequestWithState(String requestId, SingularityUser user) { + return fetchRequestWithState(requestId, false, user); } - protected SingularityRequestWithState fetchRequestWithState(String requestId, boolean useWebCache) { + protected SingularityRequestWithState fetchRequestWithState(String requestId, boolean useWebCache, SingularityUser user) { Optional request = requestManager.getRequest(requestId, useWebCache); checkNotFound(request.isPresent(), "Couldn't find request with id %s", requestId); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/AuthResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/AuthResource.java index 5bcfa778af..0c52f7d603 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/AuthResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/AuthResource.java @@ -13,7 +13,6 @@ import com.hubspot.singularity.SingularityAuthorizationScope; import com.hubspot.singularity.SingularityUser; import com.hubspot.singularity.SingularityUserHolder; -import com.hubspot.singularity.SingularityUserSettings; import com.hubspot.singularity.auth.SingularityAuthorizationHelper; import com.hubspot.singularity.auth.datastore.SingularityAuthDatastore; import com.hubspot.singularity.config.ApiPaths; @@ -21,22 +20,21 @@ import com.hubspot.singularity.data.UserManager; import com.wordnik.swagger.annotations.ApiOperation; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.AUTH_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) public class AuthResource { - private final Optional user; private final UserManager userManager; private final SingularityConfiguration configuration; private final SingularityAuthorizationHelper authorizationHelper; private final SingularityAuthDatastore authDatastore; @Inject - public AuthResource(Optional user, - UserManager userManager, + public AuthResource(UserManager userManager, SingularityConfiguration configuration, SingularityAuthorizationHelper authorizationHelper, SingularityAuthDatastore authDatastore) { - this.user = user; this.userManager = userManager; this.configuration = configuration; this.authorizationHelper = authorizationHelper; @@ -45,19 +43,30 @@ public AuthResource(Optional user, @GET @Path("/user") - public SingularityUserHolder getUser() { + public SingularityUserHolder getUser(@Auth SingularityUser user) { return new SingularityUserHolder( - user, - user.isPresent() ? userManager.getUserSettings(user.get().getId()) : Optional.absent(), - user.isPresent(), + Optional.of(user), + userManager.getUserSettings(user.getId()), + true, configuration.getAuthConfiguration().isEnabled()); } @GET @Path("/{requestId}/auth-check/{userId}") @ApiOperation("Check if the specified user is authorized for a request") - public Response checkReadOnlyAuth(@PathParam("requestId") String requestId, @PathParam("userId") String userId, @QueryParam("scope") Optional scope) { - authorizationHelper.checkForAuthorizationByRequestId(requestId, authDatastore.getUser(userId), scope.or(SingularityAuthorizationScope.READ)); + public Response checkReadOnlyAuth(@PathParam("requestId") String requestId, @PathParam("userId") String userId, + @QueryParam("scope") Optional scope) { + authorizationHelper.checkForAuthorizationByRequestId(requestId, authDatastore.getUser(userId).orElse(SingularityUser.DEFAULT_USER), scope.or(SingularityAuthorizationScope.READ)); + return Response.ok().build(); + } + + @GET + @Path("/{requestId}/auth-check}") + @ApiOperation("Check if the specified user is authorized for a request") + public Response checkReadOnlyAuth(@Auth SingularityUser user, + @PathParam("requestId") String requestId, @PathParam("userId") String userId, + @QueryParam("scope") Optional scope) { + authorizationHelper.checkForAuthorizationByRequestId(requestId, user, scope.or(SingularityAuthorizationScope.READ)); return Response.ok().build(); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/DeployResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/DeployResource.java index 6a2dcf2639..cacecc93f3 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/DeployResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/DeployResource.java @@ -23,7 +23,6 @@ import com.google.common.base.Optional; import com.google.inject.Inject; import com.hubspot.jackson.jaxrs.PropertyFiltering; -import com.hubspot.mesos.JavaUtils; import com.hubspot.singularity.DeployState; import com.hubspot.singularity.RequestState; import com.hubspot.singularity.SingularityAction; @@ -40,7 +39,6 @@ import com.hubspot.singularity.SingularityRequestDeployState; import com.hubspot.singularity.SingularityRequestParent; import com.hubspot.singularity.SingularityRequestWithState; -import com.hubspot.singularity.SingularityTaskId; import com.hubspot.singularity.SingularityTransformHelpers; import com.hubspot.singularity.SingularityUpdatePendingDeployRequest; import com.hubspot.singularity.SingularityUser; @@ -59,6 +57,8 @@ import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.DEPLOY_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api(description="Manages Singularity Deploys for existing requests", value=ApiPaths.DEPLOY_RESOURCE_PATH, position=2) @@ -67,10 +67,10 @@ public class DeployResource extends AbstractRequestResource { private final TaskManager taskManager; @Inject - public DeployResource(RequestManager requestManager, DeployManager deployManager, SingularityValidator validator, SingularityAuthorizationHelper authorizationHelper, Optional user, + public DeployResource(RequestManager requestManager, DeployManager deployManager, SingularityValidator validator, SingularityAuthorizationHelper authorizationHelper, SingularityConfiguration configuration, TaskManager taskManager, LeaderLatch leaderLatch, AsyncHttpClient httpClient, ObjectMapper objectMapper) { - super(requestManager, deployManager, user, validator, authorizationHelper, httpClient, leaderLatch, objectMapper); + super(requestManager, deployManager, validator, authorizationHelper, httpClient, leaderLatch, objectMapper); this.configuration = configuration; this.taskManager = taskManager; } @@ -79,7 +79,7 @@ public DeployResource(RequestManager requestManager, DeployManager deployManager @PropertyFiltering @Path("/pending") @ApiOperation(response=SingularityPendingDeploy.class, responseContainer="List", value="Retrieve the list of current pending deploys") - public Iterable getPendingDeploys() { + public Iterable getPendingDeploys(@Auth SingularityUser user) { return authorizationHelper.filterByAuthorizedRequests(user, deployManager.getPendingDeploys(), SingularityTransformHelpers.PENDING_DEPLOY_TO_REQUEST_ID, SingularityAuthorizationScope.READ); } @@ -90,19 +90,19 @@ public Iterable getPendingDeploys() { @ApiResponse(code=400, message="Deploy object is invalid"), @ApiResponse(code=409, message="A current deploy is in progress. It may be canceled by calling DELETE"), }) - public SingularityRequestParent deploy(@Context HttpServletRequest requestContext, @ApiParam(required=true) SingularityDeployRequest deployRequest) { - return maybeProxyToLeader(requestContext, SingularityRequestParent.class, deployRequest, () -> deploy(deployRequest)); + public SingularityRequestParent deploy(@Auth SingularityUser user, @Context HttpServletRequest requestContext, @ApiParam(required=true) SingularityDeployRequest deployRequest) { + return maybeProxyToLeader(requestContext, SingularityRequestParent.class, deployRequest, () -> deploy(deployRequest, user)); } - public SingularityRequestParent deploy(SingularityDeployRequest deployRequest) { + public SingularityRequestParent deploy(SingularityDeployRequest deployRequest, SingularityUser user) { validator.checkActionEnabled(SingularityAction.DEPLOY); SingularityDeploy deploy = deployRequest.getDeploy(); checkNotNullBadRequest(deploy, "DeployRequest must have a deploy object"); - final Optional deployUser = JavaUtils.getUserEmail(user); + final Optional deployUser = user.getEmail(); final String requestId = checkNotNullBadRequest(deploy.getRequestId(), "DeployRequest must have a non-null requestId"); - SingularityRequestWithState requestWithState = fetchRequestWithState(requestId); + SingularityRequestWithState requestWithState = fetchRequestWithState(requestId, user); authorizationHelper.checkForAuthorization(requestWithState.getRequest(), user, SingularityAuthorizationScope.WRITE); @@ -140,7 +140,7 @@ public SingularityRequestParent deploy(SingularityDeployRequest deployRequest) { deploy.getDeployStepWaitTimeMs().or(configuration.getDefaultDeployStepWaitTimeMs()), false, deploy.getAutoAdvanceDeploySteps().or(true), - Collections.emptySet(), + Collections.emptySet(), System.currentTimeMillis())); } @@ -154,7 +154,7 @@ public SingularityRequestParent deploy(SingularityDeployRequest deployRequest) { boolean deployAlreadyInProgress = deployManager.createPendingDeploy(pendingDeployObj) == SingularityCreateResult.EXISTED; if (deployAlreadyInProgress && deployToUnpause) { - requestManager.pause(request, now, deployUser, Optional.absent()); + requestManager.pause(request, now, deployUser, Optional.absent()); } checkConflict(!deployAlreadyInProgress, @@ -177,9 +177,10 @@ public SingularityRequestParent deploy(SingularityDeployRequest deployRequest) { @ApiResponse(code=400, message="Deploy is not in the pending state pending or is not not present"), }) public SingularityRequestParent cancelDeploy( + @Auth SingularityUser user, @ApiParam(required=true, value="The Singularity Request Id from which the deployment is removed.") @PathParam("requestId") String requestId, @ApiParam(required=true, value="The Singularity Deploy Id that should be removed.") @PathParam("deployId") String deployId) { - SingularityRequestWithState requestWithState = fetchRequestWithState(requestId); + SingularityRequestWithState requestWithState = fetchRequestWithState(requestId, user); authorizationHelper.checkForAuthorization(requestWithState.getRequest(), user, SingularityAuthorizationScope.WRITE); validator.checkActionEnabled(SingularityAction.CANCEL_DEPLOY); @@ -189,7 +190,7 @@ public SingularityRequestParent cancelDeploy( checkBadRequest(deployState.isPresent() && deployState.get().getPendingDeploy().isPresent() && deployState.get().getPendingDeploy().get().getDeployId().equals(deployId), "Request %s does not have a pending deploy %s", requestId, deployId); - deployManager.createCancelDeployRequest(new SingularityDeployMarker(requestId, deployId, System.currentTimeMillis(), JavaUtils.getUserEmail(user), Optional. absent())); + deployManager.createCancelDeployRequest(new SingularityDeployMarker(requestId, deployId, System.currentTimeMillis(), user.getEmail(), Optional. absent())); return fillEntireRequest(requestWithState); } @@ -200,8 +201,9 @@ public SingularityRequestParent cancelDeploy( @ApiResponses({ @ApiResponse(code=400, message="Deploy is not in the pending state pending or is not not present") }) - public SingularityRequestParent updatePendingDeploy(@ApiParam(required=true) SingularityUpdatePendingDeployRequest updateRequest) { - SingularityRequestWithState requestWithState = fetchRequestWithState(updateRequest.getRequestId()); + public SingularityRequestParent updatePendingDeploy(@Auth SingularityUser user, + @ApiParam(required=true) SingularityUpdatePendingDeployRequest updateRequest) { + SingularityRequestWithState requestWithState = fetchRequestWithState(updateRequest.getRequestId(), user); authorizationHelper.checkForAuthorization(requestWithState.getRequest(), user, SingularityAuthorizationScope.WRITE); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/DisastersResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/DisastersResource.java index ba04e0d41a..e93cb5245c 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/DisastersResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/DisastersResource.java @@ -3,7 +3,6 @@ import java.util.Collections; import java.util.List; -import javax.servlet.http.HttpServletRequest; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; @@ -11,7 +10,6 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import com.google.common.base.Optional; @@ -29,26 +27,26 @@ import com.wordnik.swagger.annotations.Api; import com.wordnik.swagger.annotations.ApiOperation; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.DISASTERS_RESOURCE_PATH) @Produces(MediaType.APPLICATION_JSON) @Api(description="Manages Singularity Deploys for existing requests", value=ApiPaths.DISASTERS_RESOURCE_PATH) public class DisastersResource { private final DisasterManager disasterManager; private final SingularityAuthorizationHelper authorizationHelper; - private final Optional user; + @Inject public DisastersResource(DisasterManager disasterManager, - SingularityAuthorizationHelper authorizationHelper, - Optional user) { + SingularityAuthorizationHelper authorizationHelper) { this.disasterManager = disasterManager; this.authorizationHelper = authorizationHelper; - this.user = user; } @GET @Path("/stats") @ApiOperation(value="Get current data related to disaster detection", response=SingularityDisastersData.class) - public SingularityDisastersData disasterStats() { + public SingularityDisastersData disasterStats(@Auth SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); return disasterManager.getDisastersData(); } @@ -56,7 +54,7 @@ public SingularityDisastersData disasterStats() { @GET @Path("/active") @ApiOperation(value="Get a list of current active disasters") - public List activeDisasters() { + public List activeDisasters(@Auth SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); return disasterManager.getActiveDisasters(); } @@ -64,7 +62,7 @@ public List activeDisasters() { @POST @Path("/disable") @ApiOperation(value="Do not allow the automated poller to disable actions when a disaster is detected") - public void disableAutomatedDisasterCreation() { + public void disableAutomatedDisasterCreation(@Auth SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); disasterManager.disableAutomatedDisabledActions(); } @@ -72,7 +70,7 @@ public void disableAutomatedDisasterCreation() { @POST @Path("/enable") @ApiOperation(value="Allow the automated poller to disable actions when a disaster is detected") - public void enableAutomatedDisasterCreation() { + public void enableAutomatedDisasterCreation(@Auth SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); disasterManager.enableAutomatedDisabledActions(); } @@ -80,7 +78,7 @@ public void enableAutomatedDisasterCreation() { @DELETE @Path("/active/{type}") @ApiOperation(value="Remove an active disaster (make it inactive)") - public void removeDisaster(@PathParam("type") SingularityDisasterType type) { + public void removeDisaster(@Auth SingularityUser user, @PathParam("type") SingularityDisasterType type) { authorizationHelper.checkAdminAuthorization(user); disasterManager.removeDisaster(type); } @@ -88,7 +86,7 @@ public void removeDisaster(@PathParam("type") SingularityDisasterType type) { @POST @Path("/active/{type}") @ApiOperation(value="Create a new active disaster") - public void newDisaster(@PathParam("type") SingularityDisasterType type) { + public void newDisaster(@Auth SingularityUser user, @PathParam("type") SingularityDisasterType type) { authorizationHelper.checkAdminAuthorization(user); disasterManager.addDisaster(type); disasterManager.addDisabledActionsForDisasters(Collections.singletonList(type)); @@ -97,7 +95,7 @@ public void newDisaster(@PathParam("type") SingularityDisasterType type) { @GET @Path("/disabled-actions") @ApiOperation(value="Get a list of actions that are currently disable") - public List disabledActions() { + public List disabledActions(@Auth SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); return disasterManager.getDisabledActions(); } @@ -105,17 +103,17 @@ public List disabledActions() { @POST @Path("/disabled-actions/{action}") @ApiOperation(value="Disable a specific action") - public void disableAction(@PathParam("action") SingularityAction action, SingularityDisabledActionRequest disabledActionRequest) { + public void disableAction(@Auth SingularityUser user, @PathParam("action") SingularityAction action, SingularityDisabledActionRequest disabledActionRequest) { final Optional maybeRequest = Optional.fromNullable(disabledActionRequest); authorizationHelper.checkAdminAuthorization(user); Optional message = maybeRequest.isPresent() ? maybeRequest.get().getMessage() : Optional.absent(); - disasterManager.disable(action, message, user, false, Optional.absent()); + disasterManager.disable(action, message, Optional.of(user), false, Optional.absent()); } @DELETE @Path("/disabled-actions/{action}") @ApiOperation(value="Re-enable a specific action if it has been disabled") - public void enableAction(@PathParam("action") SingularityAction action) { + public void enableAction(@Auth SingularityUser user, @PathParam("action") SingularityAction action) { authorizationHelper.checkAdminAuthorization(user); disasterManager.enable(action); } @@ -123,7 +121,7 @@ public void enableAction(@PathParam("action") SingularityAction action) { @POST @Path("/task-credits") @ApiOperation(value="Add task credits, enables task credit system if not already enabled") - public void addTaskCredits(@QueryParam("credits") Optional credits) throws Exception { + public void addTaskCredits(@Auth SingularityUser user, @QueryParam("credits") Optional credits) throws Exception { authorizationHelper.checkAdminAuthorization(user); disasterManager.enableTaskCredits(); disasterManager.enqueueCreditsChange(credits.or(0)); @@ -132,7 +130,7 @@ public void addTaskCredits(@QueryParam("credits") Optional credits) thr @DELETE @Path("/task-credits") @ApiOperation(value="Disable task credit system") - public void disableTaskCredits(@Context HttpServletRequest request) throws Exception { + public void disableTaskCredits(@Auth SingularityUser user) throws Exception { authorizationHelper.checkAdminAuthorization(user); disasterManager.disableTaskCredits(); } @@ -140,7 +138,7 @@ public void disableTaskCredits(@Context HttpServletRequest request) throws Excep @GET @Path("/task-credits") @ApiOperation(value="Get task credit data") - public SingularityTaskCredits getTaskCreditData() throws Exception { + public SingularityTaskCredits getTaskCreditData(@Auth SingularityUser user) throws Exception { authorizationHelper.checkAdminAuthorization(user); return new SingularityTaskCredits(disasterManager.isTaskCreditEnabled(), disasterManager.getTaskCredits()); } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/HistoryResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/HistoryResource.java index 4c31fb6778..34b88ab5e9 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/HistoryResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/HistoryResource.java @@ -42,6 +42,8 @@ import com.wordnik.swagger.annotations.ApiOperation; import com.wordnik.swagger.annotations.ApiParam; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.HISTORY_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api(description = "Manages historical data for tasks, requests, and deploys.", value = ApiPaths.HISTORY_RESOURCE_PATH) @@ -55,8 +57,8 @@ public class HistoryResource extends AbstractHistoryResource { @Inject public HistoryResource(HistoryManager historyManager, TaskManager taskManager, DeployManager deployManager, DeployHistoryHelper deployHistoryHelper, TaskHistoryHelper taskHistoryHelper, - RequestHistoryHelper requestHistoryHelper, SingularityAuthorizationHelper authorizationHelper, Optional user, DeployTaskHistoryHelper deployTaskHistoryHelper) { - super(historyManager, taskManager, deployManager, authorizationHelper, user); + RequestHistoryHelper requestHistoryHelper, SingularityAuthorizationHelper authorizationHelper, DeployTaskHistoryHelper deployTaskHistoryHelper) { + super(historyManager, taskManager, deployManager, authorizationHelper); this.requestHistoryHelper = requestHistoryHelper; this.deployHistoryHelper = deployHistoryHelper; @@ -67,10 +69,12 @@ public HistoryResource(HistoryManager historyManager, TaskManager taskManager, D @GET @Path("/task/{taskId}") @ApiOperation("Retrieve the history for a specific task.") - public SingularityTaskHistory getHistoryForTask(@ApiParam("Task ID to look up") @PathParam("taskId") String taskId) { + public SingularityTaskHistory getHistoryForTask( + @Auth SingularityUser user, + @ApiParam("Task ID to look up") @PathParam("taskId") String taskId) { SingularityTaskId taskIdObj = getTaskIdObject(taskId); - return getTaskHistoryRequired(taskIdObj); + return getTaskHistoryRequired(taskIdObj, user); } private Integer getLimitCount(Integer countParam) { @@ -107,8 +111,8 @@ private Optional getPageCount(Optional dataCount, Integer coun @GET @Path("/request/{requestId}/tasks/active") @ApiOperation("Retrieve the history for all active tasks of a specific request.") - public List getTaskHistoryForRequest( - @ApiParam("Request ID to look up") @PathParam("requestId") String requestId) { + public List getTaskHistoryForRequest(@Auth SingularityUser user, + @ApiParam("Request ID to look up") @PathParam("requestId") String requestId) { authorizationHelper.checkForAuthorizationByRequestId(requestId, user, SingularityAuthorizationScope.READ); List activeTaskIds = taskManager.getActiveTaskIdsForRequest(requestId); @@ -118,15 +122,18 @@ public List getTaskHistoryForRequest( @GET @Path("/request/{requestId}/deploy/{deployId}") @ApiOperation("Retrieve the history for a specific deploy.") - public SingularityDeployHistory getDeploy(@ApiParam("Request ID for deploy") @PathParam("requestId") String requestId, + public SingularityDeployHistory getDeploy( + @Auth SingularityUser user, + @ApiParam("Request ID for deploy") @PathParam("requestId") String requestId, @ApiParam("Deploy ID") @PathParam("deployId") String deployId) { - return getDeployHistory(requestId, deployId); + return getDeployHistory(requestId, deployId, user); } @GET @Path("/request/{requestId}/deploy/{deployId}/tasks/active") @ApiOperation("Retrieve the task history for a specific deploy.") public List getActiveDeployTasks( + @Auth SingularityUser user, @ApiParam("Request ID for deploy") @PathParam("requestId") String requestId, @ApiParam("Deploy ID") @PathParam("deployId") String deployId) { authorizationHelper.checkForAuthorizationByRequestId(requestId, user, SingularityAuthorizationScope.READ); @@ -138,6 +145,7 @@ public List getActiveDeployTasks( @Path("/request/{requestId}/deploy/{deployId}/tasks/inactive") @ApiOperation("Retrieve the task history for a specific deploy.") public List getInactiveDeployTasks( + @Auth SingularityUser user, @ApiParam("Request ID for deploy") @PathParam("requestId") String requestId, @ApiParam("Deploy ID") @PathParam("deployId") String deployId, @ApiParam("Maximum number of items to return") @QueryParam("count") Integer count, @@ -155,6 +163,7 @@ public List getInactiveDeployTasks( @Path("/request/{requestId}/deploy/{deployId}/tasks/inactive/withmetadata") @ApiOperation("Retrieve the task history for a specific deploy.") public SingularityPaginatedResponse getInactiveDeployTasksWithMetadata( + @Auth SingularityUser user, @ApiParam("Request ID for deploy") @PathParam("requestId") String requestId, @ApiParam("Deploy ID") @PathParam("deployId") String deployId, @ApiParam("Maximum number of items to return") @QueryParam("count") Integer count, @@ -176,6 +185,7 @@ public SingularityPaginatedResponse getInactiveDeployT @Path("/tasks") @ApiOperation("Retrieve the history sorted by startedAt for all inactive tasks.") public List getTaskHistory( + @Auth SingularityUser user, @ApiParam("Optional Request ID to match") @QueryParam("requestId") Optional requestId, @ApiParam("Optional deploy ID to match") @QueryParam("deployId") Optional deployId, @ApiParam("Optional runId to match") @QueryParam("runId") Optional runId, @@ -205,6 +215,7 @@ public List getTaskHistory( @Path("/tasks/withmetadata") @ApiOperation("Retrieve the history sorted by startedAt for all inactive tasks.") public SingularityPaginatedResponse getTaskHistoryWithMetadata( + @Auth SingularityUser user, @ApiParam("Optional Request ID to match") @QueryParam("requestId") Optional requestId, @ApiParam("Optional deploy ID to match") @QueryParam("deployId") Optional deployId, @ApiParam("Optional runId to match") @QueryParam("runId") Optional runId, @@ -225,7 +236,7 @@ public SingularityPaginatedResponse getTaskHistoryWith final Optional dataCount = taskHistoryHelper.getBlendedHistoryCount(new SingularityTaskHistoryQuery(requestId, deployId, runId, host, lastTaskStatus, startedBefore, startedAfter, updatedBefore, updatedAfter, orderDirection)); final int limitCount = getLimitCount(count); - final List data = this.getTaskHistory(requestId, deployId, runId, host, lastTaskStatus, startedBefore, startedAfter, updatedBefore, updatedAfter, orderDirection, count, page); + final List data = this.getTaskHistory(user, requestId, deployId, runId, host, lastTaskStatus, startedBefore, startedAfter, updatedBefore, updatedAfter, orderDirection, count, page); final Optional pageCount = getPageCount(dataCount, limitCount); return new SingularityPaginatedResponse<>(dataCount, pageCount, Optional.fromNullable(page), data); @@ -235,6 +246,7 @@ public SingularityPaginatedResponse getTaskHistoryWith @Path("/request/{requestId}/tasks") @ApiOperation("Retrieve the history sorted by startedAt for all inactive tasks of a specific request.") public List getTaskHistoryForRequest( + @Auth SingularityUser user, @ApiParam("Request ID to match") @PathParam("requestId") String requestId, @ApiParam("Optional deploy ID to match") @QueryParam("deployId") Optional deployId, @ApiParam("Optional runId to match") @QueryParam("runId") Optional runId, @@ -260,6 +272,7 @@ public List getTaskHistoryForRequest( @Path("/request/{requestId}/tasks/withmetadata") @ApiOperation("Retrieve the history count for all inactive tasks of a specific request.") public SingularityPaginatedResponse getTaskHistoryForRequestWithMetadata( + @Auth SingularityUser user, @ApiParam("Request ID to match") @PathParam("requestId") String requestId, @ApiParam("Optional deploy ID to match") @QueryParam("deployId") Optional deployId, @ApiParam("Optional runId to match") @QueryParam("runId") Optional runId, @@ -276,7 +289,7 @@ public SingularityPaginatedResponse getTaskHistoryForR final Optional dataCount = taskHistoryHelper.getBlendedHistoryCount(new SingularityTaskHistoryQuery(Optional.of(requestId), deployId, runId, host, lastTaskStatus, startedBefore, startedAfter, updatedBefore, updatedAfter, orderDirection)); final int limitCount = getLimitCount(count); - final List data = this.getTaskHistoryForRequest(requestId, deployId, runId, host, lastTaskStatus, startedBefore, startedAfter, updatedBefore, updatedAfter, orderDirection, count, page); + final List data = this.getTaskHistoryForRequest(user, requestId, deployId, runId, host, lastTaskStatus, startedBefore, startedAfter, updatedBefore, updatedAfter, orderDirection, count, page); final Optional pageCount = getPageCount(dataCount, limitCount); return new SingularityPaginatedResponse<>(dataCount, pageCount, Optional.fromNullable(page), data); @@ -286,6 +299,7 @@ public SingularityPaginatedResponse getTaskHistoryForR @Path("/request/{requestId}/run/{runId}") @ApiOperation("Retrieve the history for a task by runId") public Optional getTaskHistoryForRequestAndRunId( + @Auth SingularityUser user, @ApiParam("Request ID to look up") @PathParam("requestId") String requestId, @ApiParam("runId to look up") @PathParam("runId") String runId) { authorizationHelper.checkForAuthorizationByRequestId(requestId, user, SingularityAuthorizationScope.READ); @@ -297,6 +311,7 @@ public Optional getTaskHistoryForRequestAndRunId( @Path("/request/{requestId}/deploys") @ApiOperation("Get deploy history for a single request") public List getDeploys( + @Auth SingularityUser user, @ApiParam("Request ID to look up") @PathParam("requestId") String requestId, @ApiParam("Maximum number of items to return") @QueryParam("count") Integer count, @ApiParam("Which page of items to view") @QueryParam("page") Integer page) { @@ -312,6 +327,7 @@ public List getDeploys( @Path("/request/{requestId}/deploys/withmetadata") @ApiOperation("Get deploy history with metadata for a single request") public SingularityPaginatedResponse getDeploysWithMetadata( + @Auth SingularityUser user, @ApiParam("Request ID to look up") @PathParam("requestId") String requestId, @ApiParam("Maximum number of items to return") @QueryParam("count") Integer count, @ApiParam("Which page of items to view") @QueryParam("page") Integer page) { @@ -319,7 +335,7 @@ public SingularityPaginatedResponse getDeploysWithMeta final Optional dataCount = deployHistoryHelper.getBlendedHistoryCount(requestId); final int limitCount = getLimitCount(count); - final List data = this.getDeploys(requestId, count, page); + final List data = this.getDeploys(user, requestId, count, page); final Optional pageCount = getPageCount(dataCount, limitCount); return new SingularityPaginatedResponse<>(dataCount, pageCount, Optional.fromNullable(page), data); @@ -329,6 +345,7 @@ public SingularityPaginatedResponse getDeploysWithMeta @Path("/request/{requestId}/requests") @ApiOperation("Get request history for a single request") public List getRequestHistoryForRequest( + @Auth SingularityUser user, @ApiParam("Request ID to look up") @PathParam("requestId") String requestId, @ApiParam("Maximum number of items to return") @QueryParam("count") Integer count, @ApiParam("Which page of items to view") @QueryParam("page") Integer page) { @@ -344,6 +361,7 @@ public List getRequestHistoryForRequest( @Path("/request/{requestId}/requests/withmetadata") @ApiOperation("Get request history for a single request") public SingularityPaginatedResponse getRequestHistoryForRequestWithMetadata( + @Auth SingularityUser user, @ApiParam("Request ID to look up") @PathParam("requestId") String requestId, @ApiParam("Maximum number of items to return") @QueryParam("count") Integer count, @ApiParam("Which page of items to view") @QueryParam("page") Integer page) { @@ -362,6 +380,7 @@ public SingularityPaginatedResponse getRequestHistory @Path("/requests/search") @ApiOperation("Search for requests.") public Iterable getRequestHistoryForRequestLike( + @Auth SingularityUser user, @ApiParam("Request ID prefix to search for") @QueryParam("requestIdLike") String requestIdLike, @ApiParam("Maximum number of items to return") @QueryParam("count") Integer count, @ApiParam("Which page of items to view") @QueryParam("page") Integer page, @@ -378,6 +397,7 @@ public Iterable getRequestHistoryForRequestLike( @Path("/request/{requestId}/command-line-args") @ApiOperation("Get a list of recently used command line args for an on-demand or scheduled request") public Set> getRecentCommandLineArgs( + @Auth SingularityUser user, @ApiParam("Request ID to look up") @PathParam("requestId") String requestId, @ApiParam("Max number of recent args to return") @QueryParam("count") Optional count) { authorizationHelper.checkForAuthorizationByRequestId(requestId, user, SingularityAuthorizationScope.READ); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/InactiveSlaveResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/InactiveSlaveResource.java index 422e36f6d3..878a41ff69 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/InactiveSlaveResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/InactiveSlaveResource.java @@ -10,7 +10,6 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; -import com.google.common.base.Optional; import com.google.inject.Inject; import com.hubspot.singularity.SingularityUser; import com.hubspot.singularity.auth.SingularityAuthorizationHelper; @@ -18,22 +17,21 @@ import com.hubspot.singularity.data.InactiveSlaveManager; import com.wordnik.swagger.annotations.Api; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.INACTIVE_SLAVES_RESOURCE_PATH) @Produces(MediaType.APPLICATION_JSON) @Api(description="Manages Singularity Deploys for existing requests", value=ApiPaths.INACTIVE_SLAVES_RESOURCE_PATH) public class InactiveSlaveResource { private final InactiveSlaveManager inactiveSlaveManager; private final SingularityAuthorizationHelper authorizationHelper; - private final Optional user; @Inject public InactiveSlaveResource(InactiveSlaveManager inactiveSlaveManager, - SingularityAuthorizationHelper authorizationHelper, - Optional user) { + SingularityAuthorizationHelper authorizationHelper) { this.inactiveSlaveManager = inactiveSlaveManager; this.authorizationHelper = authorizationHelper; - this.user = user; } @GET @@ -42,13 +40,15 @@ public Set getInactiveSlaves() { } @POST - public void deactivateSlave(@QueryParam("host") String host) { + public void deactivateSlave(@Auth SingularityUser user, + @QueryParam("host") String host) { authorizationHelper.checkAdminAuthorization(user); inactiveSlaveManager.deactivateSlave(host); } @DELETE - public void reactivateSlave(@QueryParam("host") String host) { + public void reactivateSlave(@Auth SingularityUser user, + @QueryParam("host") String host) { authorizationHelper.checkAdminAuthorization(user); inactiveSlaveManager.activateSlave(host); } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/PriorityResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/PriorityResource.java index 1280324cf6..d5241e108f 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/PriorityResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/PriorityResource.java @@ -11,7 +11,6 @@ import com.google.common.base.Optional; import com.google.inject.Inject; -import com.hubspot.mesos.JavaUtils; import com.hubspot.singularity.SingularityDeleteResult; import com.hubspot.singularity.SingularityPriorityFreezeParent; import com.hubspot.singularity.SingularityUser; @@ -25,18 +24,18 @@ import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.PRIORITY_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api(description="Manages whether or not to schedule tasks based on their priority levels.", value=ApiPaths.PRIORITY_RESOURCE_PATH ) public class PriorityResource { - private final Optional user; private final SingularityAuthorizationHelper authorizationHelper; private final SingularityValidator singularityValidator; private final PriorityManager priorityManager; @Inject - public PriorityResource(Optional user, SingularityAuthorizationHelper authorizationHelper, SingularityValidator singularityValidator, PriorityManager priorityManager) { - this.user = user; + public PriorityResource(SingularityAuthorizationHelper authorizationHelper, SingularityValidator singularityValidator, PriorityManager priorityManager) { this.authorizationHelper = authorizationHelper; this.singularityValidator = singularityValidator; this.priorityManager = priorityManager; @@ -49,7 +48,8 @@ public PriorityResource(Optional user, SingularityAuthorization @ApiResponse(code=200, message="The active priority freeze."), @ApiResponse(code=404, message="There was no active priority freeze.") }) - public Optional getActivePriorityFreeze() { + public Optional getActivePriorityFreeze(@Auth SingularityUser user) { + authorizationHelper.checkAdminAuthorization(user); return priorityManager.getActivePriorityFreeze(); } @@ -60,7 +60,7 @@ public Optional getActivePriorityFreeze() { @ApiResponse(code=202, message="The active priority freeze was deleted."), @ApiResponse(code=400, message="There was no active priority freeze to delete.") }) - public void deleteActivePriorityFreeze() { + public void deleteActivePriorityFreeze(@Auth SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); final SingularityDeleteResult deleteResult = priorityManager.deleteActivePriorityFreeze(); @@ -77,11 +77,11 @@ public void deleteActivePriorityFreeze() { @ApiResponse(code=200, message="The priority freeze request was accepted."), @ApiResponse(code=400, message="There was a validation error with the priority freeze request.") }) - public SingularityPriorityFreezeParent createPriorityFreeze(SingularityPriorityFreeze priorityFreezeRequest) { + public SingularityPriorityFreezeParent createPriorityFreeze(@Auth SingularityUser user, SingularityPriorityFreeze priorityFreezeRequest) { authorizationHelper.checkAdminAuthorization(user); priorityFreezeRequest = singularityValidator.checkSingularityPriorityFreeze(priorityFreezeRequest); - final SingularityPriorityFreezeParent priorityFreezeRequestParent = new SingularityPriorityFreezeParent(priorityFreezeRequest, System.currentTimeMillis(), JavaUtils.getUserEmail(user)); + final SingularityPriorityFreezeParent priorityFreezeRequestParent = new SingularityPriorityFreezeParent(priorityFreezeRequest, System.currentTimeMillis(), user.getEmail()); priorityManager.createPriorityFreeze(priorityFreezeRequestParent); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/RackResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/RackResource.java index 81d6eb37f5..6914eeed94 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/RackResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/RackResource.java @@ -13,7 +13,6 @@ import com.google.common.base.Optional; import com.google.inject.Inject; -import com.hubspot.mesos.JavaUtils; import com.hubspot.singularity.MachineState; import com.hubspot.singularity.SingularityAction; import com.hubspot.singularity.SingularityMachineStateHistoryUpdate; @@ -29,14 +28,16 @@ import com.wordnik.swagger.annotations.ApiOperation; import com.wordnik.swagger.annotations.ApiParam; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.RACK_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api( description="Manages Singularity racks.", value=ApiPaths.RACK_RESOURCE_PATH ) public class RackResource extends AbstractMachineResource { @Inject - public RackResource(RackManager rackManager, SingularityAuthorizationHelper authorizationHelper, Optional user, SingularityValidator validator) { - super(rackManager, authorizationHelper, user, validator); + public RackResource(RackManager rackManager, SingularityAuthorizationHelper authorizationHelper, SingularityValidator validator) { + super(rackManager, authorizationHelper, validator); } @Override @@ -47,60 +48,62 @@ protected String getObjectTypeString() { @GET @Path("/") @ApiOperation("Retrieve the list of all known racks, optionally filtering by a particular state") - public List getRacks(@ApiParam("Optionally specify a particular state to filter racks by") @QueryParam("state") Optional filterState) { + public List getRacks(@Auth SingularityUser user, @ApiParam("Optionally specify a particular state to filter racks by") @QueryParam("state") Optional filterState) { + authorizationHelper.checkAdminAuthorization(user); return manager.getObjectsFiltered(filterState); } @GET @Path("/rack/{rackId}") @ApiOperation("Retrieve the history of a given rack") - public List getRackHistory(@ApiParam("Rack ID") @PathParam("rackId") String rackId) { + public List getRackHistory(@Auth SingularityUser user, @ApiParam("Rack ID") @PathParam("rackId") String rackId) { + authorizationHelper.checkAdminAuthorization(user); return manager.getHistory(rackId); } @DELETE @Path("/rack/{rackId}") @ApiOperation("Remove a known rack, erasing history. This operation will cancel decommissioning of racks") - public void removeRack(@ApiParam("Rack ID") @PathParam("rackId") String rackId) { - super.remove(rackId); + public void removeRack(@Auth SingularityUser user, @ApiParam("Rack ID") @PathParam("rackId") String rackId) { + super.remove(rackId, user); } @POST @Path("/rack/{rackId}/decommission") @ApiOperation("Begin decommissioning a specific active rack") - public void decommissionRack(@ApiParam("Active rack ID") @PathParam("rackId") String rackId, SingularityMachineChangeRequest changeRequest) { + public void decommissionRack(@Auth SingularityUser user, @ApiParam("Active rack ID") @PathParam("rackId") String rackId, SingularityMachineChangeRequest changeRequest) { final Optional maybeChangeRequest = Optional.fromNullable(changeRequest); - super.decommission(rackId, maybeChangeRequest, JavaUtils.getUserEmail(user), SingularityAction.DECOMMISSION_RACK); + super.decommission(rackId, maybeChangeRequest, user, SingularityAction.DECOMMISSION_RACK); } @POST @Path("/rack/{rackId}/freeze") @ApiOperation("Freeze a specific rack") - public void freezeRack(@ApiParam("Rack ID") @PathParam("rackId") String rackId, SingularityMachineChangeRequest changeRequest) { + public void freezeRack(@Auth SingularityUser user, @ApiParam("Rack ID") @PathParam("rackId") String rackId, SingularityMachineChangeRequest changeRequest) { final Optional maybeChangeRequest = Optional.fromNullable(changeRequest); - super.freeze(rackId, maybeChangeRequest, JavaUtils.getUserEmail(user), SingularityAction.FREEZE_RACK); + super.freeze(rackId, maybeChangeRequest, user, SingularityAction.FREEZE_RACK); } @POST @Path("/rack/{rackId}/activate") @ApiOperation("Activate a decomissioning rack, canceling decomission without erasing history") - public void activateRack(@ApiParam("Active rackId") @PathParam("rackId") String rackId, SingularityMachineChangeRequest changeRequest) { + public void activateRack(@Auth SingularityUser user, @ApiParam("Active rackId") @PathParam("rackId") String rackId, SingularityMachineChangeRequest changeRequest) { final Optional maybeChangeRequest = Optional.fromNullable(changeRequest); - super.activate(rackId, maybeChangeRequest, JavaUtils.getUserEmail(user), SingularityAction.ACTIVATE_RACK); + super.activate(rackId, maybeChangeRequest, user, SingularityAction.ACTIVATE_RACK); } @DELETE @Path("/rack/{rackId}/expiring") @ApiOperation("Delete any expiring machine state changes for this rack") - public void deleteExpiringStateChange(@ApiParam("Active slaveId") @PathParam("rackId") String rackId) { - super.cancelExpiring(rackId); + public void deleteExpiringStateChange(@Auth SingularityUser user, @ApiParam("Active slaveId") @PathParam("rackId") String rackId) { + super.cancelExpiring(rackId, user); } @GET @Path("/expiring") @ApiOperation("Get all expiring state changes for all racks") - public List getExpiringStateChanges() { - return super.getExpiringStateChanges(); + public List getExpiringStateChanges(@Auth SingularityUser user) { + return super.getExpiringStateChanges(user); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/RequestGroupResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/RequestGroupResource.java index 5777556099..523aa88394 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/RequestGroupResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/RequestGroupResource.java @@ -21,45 +21,45 @@ import com.wordnik.swagger.annotations.ApiOperation; @Path(ApiPaths.REQUEST_GROUP_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -@Api(description="Manages Singularity Request Groups, which are collections of one or more Singularity Requests", value=ApiPaths.REQUEST_GROUP_RESOURCE_PATH, position=1) +@Produces({MediaType.APPLICATION_JSON}) +@Api(description = "Manages Singularity Request Groups, which are collections of one or more Singularity Requests", value = ApiPaths.REQUEST_GROUP_RESOURCE_PATH, position = 1) public class RequestGroupResource { - private final RequestGroupManager requestGroupManager; - private final SingularityValidator validator; + private final RequestGroupManager requestGroupManager; + private final SingularityValidator validator; - @Inject - public RequestGroupResource(RequestGroupManager requestGroupManager, SingularityValidator validator) { - this.requestGroupManager = requestGroupManager; - this.validator = validator; - } + @Inject + public RequestGroupResource(RequestGroupManager requestGroupManager, SingularityValidator validator) { + this.requestGroupManager = requestGroupManager; + this.validator = validator; + } - @GET - @ApiOperation(value="Get a list of Singularity request groups") - public List getRequestGroupIds(@QueryParam("useWebCache") Boolean useWebCache) { - return requestGroupManager.getRequestGroups(useWebCache != null && useWebCache); - } + @GET + @ApiOperation(value = "Get a list of Singularity request groups") + public List getRequestGroupIds(@QueryParam("useWebCache") Boolean useWebCache) { + return requestGroupManager.getRequestGroups(useWebCache != null && useWebCache); + } - @GET - @Path("/group/{requestGroupId}") - @ApiOperation(value="Get a specific Singularity request group by ID") - public Optional getRequestGroup(@PathParam("requestGroupId") String requestGroupId) { - return requestGroupManager.getRequestGroup(requestGroupId); - } + @GET + @Path("/group/{requestGroupId}") + @ApiOperation(value = "Get a specific Singularity request group by ID") + public Optional getRequestGroup(@PathParam("requestGroupId") String requestGroupId) { + return requestGroupManager.getRequestGroup(requestGroupId); + } - @DELETE - @Path("/group/{requestGroupId}") - @ApiOperation(value="Delete a specific Singularity request group by ID") - public void deleteRequestGroup(@PathParam("requestGroupId") String requestGroupId) { - requestGroupManager.deleteRequestGroup(requestGroupId); - } + @DELETE + @Path("/group/{requestGroupId}") + @ApiOperation(value = "Delete a specific Singularity request group by ID") + public void deleteRequestGroup(@PathParam("requestGroupId") String requestGroupId) { + requestGroupManager.deleteRequestGroup(requestGroupId); + } - @POST - @ApiOperation(value="Create a Singularity request group") - public SingularityRequestGroup saveRequestGroup(SingularityRequestGroup requestGroup) { - validator.checkRequestGroup(requestGroup); + @POST + @ApiOperation(value = "Create a Singularity request group") + public SingularityRequestGroup saveRequestGroup(SingularityRequestGroup requestGroup) { + validator.checkRequestGroup(requestGroup); - requestGroupManager.saveRequestGroup(requestGroup); + requestGroupManager.saveRequestGroup(requestGroup); - return requestGroup; - } + return requestGroup; + } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/RequestResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/RequestResource.java index 3e37638c5d..51d63ec8c5 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/RequestResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/RequestResource.java @@ -33,7 +33,6 @@ import com.google.common.collect.Lists; import com.google.inject.Inject; import com.hubspot.jackson.jaxrs.PropertyFiltering; -import com.hubspot.mesos.JavaUtils; import com.hubspot.singularity.MachineState; import com.hubspot.singularity.RequestCleanupType; import com.hubspot.singularity.RequestState; @@ -87,6 +86,8 @@ import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.REQUEST_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api(description="Manages Singularity Requests, the parent object for any deployed task", value=ApiPaths.REQUEST_RESOURCE_PATH, position=1) @@ -101,9 +102,9 @@ public class RequestResource extends AbstractRequestResource { @Inject public RequestResource(SingularityValidator validator, DeployManager deployManager, TaskManager taskManager, RequestManager requestManager, SingularityMailer mailer, - SingularityAuthorizationHelper authorizationHelper, Optional user, RequestHelper requestHelper, LeaderLatch leaderLatch, + SingularityAuthorizationHelper authorizationHelper, RequestHelper requestHelper, LeaderLatch leaderLatch, SlaveManager slaveManager, DisasterManager disasterManager, AsyncHttpClient httpClient, ObjectMapper objectMapper) { - super(requestManager, deployManager, user, validator, authorizationHelper, httpClient, leaderLatch, objectMapper); + super(requestManager, deployManager, validator, authorizationHelper, httpClient, leaderLatch, objectMapper); this.mailer = mailer; this.taskManager = taskManager; this.requestHelper = requestHelper; @@ -112,7 +113,7 @@ public RequestResource(SingularityValidator validator, DeployManager deployManag } private void submitRequest(SingularityRequest request, Optional oldRequestWithState, Optional historyType, - Optional skipHealthchecks, Optional message, Optional maybeBounceRequest) { + Optional skipHealthchecks, Optional message, Optional maybeBounceRequest, SingularityUser user) { checkNotNullBadRequest(request.getId(), "Request must have an id"); checkConflict(!requestManager.cleanupRequestExists(request.getId()), "Request %s is currently cleaning. Try again after a few moments", request.getId()); @@ -147,7 +148,7 @@ private void submitRequest(SingularityRequest request, Optional postRequest(request)); + return maybeProxyToLeader(requestContext, SingularityRequestParent.class, request, () -> postRequest(request, user)); } - public SingularityRequestParent postRequest(SingularityRequest request) { - submitRequest(request, requestManager.getRequest(request.getId()), Optional. absent(), Optional. absent(), Optional. absent(), Optional.absent()); - return fillEntireRequest(fetchRequestWithState(request.getId())); + public SingularityRequestParent postRequest(SingularityRequest request, SingularityUser user) { + submitRequest(request, requestManager.getRequest(request.getId()), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), user); + return fillEntireRequest(fetchRequestWithState(request.getId(), user)); } private String getAndCheckDeployId(String requestId) { @@ -177,9 +179,10 @@ private String getAndCheckDeployId(String requestId) { @POST @Path("/request/{requestId}/bounce") - public SingularityRequestParent bounce(@PathParam("requestId") String requestId, + public SingularityRequestParent bounce(@Auth SingularityUser user, + @PathParam("requestId") String requestId, @Context HttpServletRequest requestContext) { - return bounce(requestId, requestContext, null); + return bounce(user, requestId, requestContext, null); } @POST @@ -187,15 +190,16 @@ public SingularityRequestParent bounce(@PathParam("requestId") String requestId, @Consumes({ MediaType.APPLICATION_JSON }) @ApiOperation(value="Bounce a specific Singularity request. A bounce launches replacement task(s), and then kills the original task(s) if the replacement(s) are healthy.", response=SingularityRequestParent.class) - public SingularityRequestParent bounce(@ApiParam("The request ID to bounce") @PathParam("requestId") String requestId, + public SingularityRequestParent bounce(@Auth SingularityUser user, + @ApiParam("The request ID to bounce") @PathParam("requestId") String requestId, @Context HttpServletRequest requestContext, @ApiParam("Bounce request options") SingularityBounceRequest bounceRequest) { final Optional maybeBounceRequest = Optional.fromNullable(bounceRequest); - return maybeProxyToLeader(requestContext, SingularityRequestParent.class, maybeBounceRequest.orNull(), () -> bounce(requestId, maybeBounceRequest)); + return maybeProxyToLeader(requestContext, SingularityRequestParent.class, maybeBounceRequest.orNull(), () -> bounce(requestId, maybeBounceRequest, user)); } - public SingularityRequestParent bounce(String requestId, Optional bounceRequest) { - SingularityRequestWithState requestWithState = fetchRequestWithState(requestId); + public SingularityRequestParent bounce(String requestId, Optional bounceRequest, SingularityUser user) { + SingularityRequestWithState requestWithState = fetchRequestWithState(requestId, user); authorizationHelper.checkForAuthorization(requestWithState.getRequest(), user, SingularityAuthorizationScope.WRITE); validator.checkActionEnabled(SingularityAction.BOUNCE_REQUEST); @@ -233,14 +237,14 @@ public SingularityRequestParent bounce(String requestId, Optional absent(), Optional.absent(), requestId, Optional.of(deployId), skipHealthchecks, message, actionId, runBeforeKill)); + new SingularityRequestCleanup(user.getEmail(), isIncrementalBounce ? RequestCleanupType.INCREMENTAL_BOUNCE : RequestCleanupType.BOUNCE, + System.currentTimeMillis(), Optional.absent(), Optional.absent(), requestId, Optional.of(deployId), skipHealthchecks, message, actionId, runBeforeKill)); - requestManager.bounce(requestWithState.getRequest(), System.currentTimeMillis(), JavaUtils.getUserEmail(user), message); + requestManager.bounce(requestWithState.getRequest(), System.currentTimeMillis(), Optional.of(user.getId()), message); final SingularityBounceRequest validatedBounceRequest = validator.checkBounceRequest(bounceRequest.or(SingularityBounceRequest.defaultRequest())); - requestManager.saveExpiringObject(new SingularityExpiringBounce(requestId, deployId, JavaUtils.getUserEmail(user), + requestManager.saveExpiringObject(new SingularityExpiringBounce(requestId, deployId, Optional.of(user.getId()), System.currentTimeMillis(), validatedBounceRequest, actionId.get())); return fillEntireRequest(requestWithState); @@ -248,8 +252,8 @@ public SingularityRequestParent bounce(String requestId, Optional maybeRunNowRequest = Optional.fromNullable(runNowRequest); - SingularityRequestWithState requestWithState = fetchRequestWithState(requestId); + SingularityRequestWithState requestWithState = fetchRequestWithState(requestId, user); authorizationHelper.checkForAuthorization(requestWithState.getRequest(), user, SingularityAuthorizationScope.WRITE); @@ -271,7 +276,7 @@ public SingularityPendingRequestParent scheduleImmediately(@ApiParam("The reques final SingularityPendingRequest pendingRequest = validator.checkRunNowRequest( getAndCheckDeployId(requestId), - JavaUtils.getUserEmail(user), + user.getEmail(), requestWithState.getRequest(), maybeRunNowRequest, taskManager.getActiveTaskIdsForRequest(requestId), @@ -287,17 +292,18 @@ public SingularityPendingRequestParent scheduleImmediately(@ApiParam("The reques @GET @Path("/request/{requestId}/run/{runId}") @ApiOperation("Retrieve an active task by runId") - public Optional getTaskByRunId(@PathParam("requestId") String requestId, @PathParam("runId") String runId) { - SingularityRequestWithState requestWithState = fetchRequestWithState(requestId); + public Optional getTaskByRunId(@Auth SingularityUser user, @PathParam("requestId") String requestId, @PathParam("runId") String runId) { + SingularityRequestWithState requestWithState = fetchRequestWithState(requestId, user); authorizationHelper.checkForAuthorization(requestWithState.getRequest(), user, SingularityAuthorizationScope.READ); return taskManager.getTaskByRunId(requestId, runId); } @POST @Path("/request/{requestId}/pause") - public SingularityRequestParent pause(@PathParam("requestId") String requestId, + public SingularityRequestParent pause(@Auth SingularityUser user, + @PathParam("requestId") String requestId, @Context HttpServletRequest requestContext) { - return pause(requestId, requestContext, null); + return pause(user, requestId, requestContext, null); } @POST @@ -307,16 +313,17 @@ public SingularityRequestParent pause(@PathParam("requestId") String requestId, @ApiResponses({ @ApiResponse(code=409, message="Request is already paused or being cleaned"), }) - public SingularityRequestParent pause(@ApiParam("The request ID to pause") @PathParam("requestId") String requestId, + public SingularityRequestParent pause(@Auth SingularityUser user, + @ApiParam("The request ID to pause") @PathParam("requestId") String requestId, @Context HttpServletRequest requestContext, @ApiParam("Pause Request Options") SingularityPauseRequest pauseRequest) { final Optional maybePauseRequest = Optional.fromNullable(pauseRequest); - return maybeProxyToLeader(requestContext, SingularityRequestParent.class, maybePauseRequest.orNull(), () -> pause(requestId, maybePauseRequest)); + return maybeProxyToLeader(requestContext, SingularityRequestParent.class, maybePauseRequest.orNull(), () -> pause(requestId, maybePauseRequest, user)); } - public SingularityRequestParent pause(String requestId, Optional pauseRequest) { + public SingularityRequestParent pause(String requestId, Optional pauseRequest, SingularityUser user) { - SingularityRequestWithState requestWithState = fetchRequestWithState(requestId); + SingularityRequestWithState requestWithState = fetchRequestWithState(requestId, user); authorizationHelper.checkForAuthorization(requestWithState.getRequest(), user, SingularityAuthorizationScope.WRITE); @@ -343,17 +350,17 @@ public SingularityRequestParent pause(String requestId, Optional removeFromLoadBalancer = Optional.absent(); - SingularityCreateResult result = requestManager.createCleanupRequest(new SingularityRequestCleanup(JavaUtils.getUserEmail(user), + SingularityCreateResult result = requestManager.createCleanupRequest(new SingularityRequestCleanup(user.getEmail(), RequestCleanupType.PAUSING, now, killTasks, removeFromLoadBalancer, requestId, Optional. absent(), Optional. absent(), message, actionId, runBeforeKill)); checkConflict(result == SingularityCreateResult.CREATED, "%s is already pausing - try again soon", requestId, result); - mailer.sendRequestPausedMail(requestWithState.getRequest(), pauseRequest, JavaUtils.getUserEmail(user)); + mailer.sendRequestPausedMail(requestWithState.getRequest(), pauseRequest, user.getEmail()); - requestManager.pause(requestWithState.getRequest(), now, JavaUtils.getUserEmail(user), message); + requestManager.pause(requestWithState.getRequest(), now, user.getEmail(), message); if (pauseRequest.isPresent() && pauseRequest.get().getDurationMillis().isPresent()) { - requestManager.saveExpiringObject(new SingularityExpiringPause(requestId, JavaUtils.getUserEmail(user), + requestManager.saveExpiringObject(new SingularityExpiringPause(requestId, user.getEmail(), System.currentTimeMillis(), pauseRequest.get(), actionId.get())); } @@ -362,9 +369,10 @@ public SingularityRequestParent pause(String requestId, Optional maybeUnpauseRequest = Optional.fromNullable(unpauseRequest); - return maybeProxyToLeader(requestContext, SingularityRequestParent.class, maybeUnpauseRequest.orNull(), () -> unpause(requestId, maybeUnpauseRequest)); + return maybeProxyToLeader(requestContext, SingularityRequestParent.class, maybeUnpauseRequest.orNull(), () -> unpause(requestId, maybeUnpauseRequest, user)); } - public SingularityRequestParent unpause(String requestId, Optional unpauseRequest) { + public SingularityRequestParent unpause(String requestId, Optional unpauseRequest, SingularityUser user) { - SingularityRequestWithState requestWithState = fetchRequestWithState(requestId); + SingularityRequestWithState requestWithState = fetchRequestWithState(requestId, user); authorizationHelper.checkForAuthorization(requestWithState.getRequest(), user, SingularityAuthorizationScope.WRITE); @@ -399,16 +408,17 @@ public SingularityRequestParent unpause(String requestId, Optional maybeExitCooldownRequest = Optional.fromNullable(exitCooldownRequest); - return maybeProxyToLeader(requestContext, SingularityRequestParent.class, maybeExitCooldownRequest.orNull(), () -> exitCooldown(requestId, maybeExitCooldownRequest)); + return maybeProxyToLeader(requestContext, SingularityRequestParent.class, maybeExitCooldownRequest.orNull(), () -> exitCooldown(requestId, maybeExitCooldownRequest, user)); } - public SingularityRequestParent exitCooldown(String requestId, Optional exitCooldownRequest) { - final SingularityRequestWithState requestWithState = fetchRequestWithState(requestId); + public SingularityRequestParent exitCooldown(String requestId, Optional exitCooldownRequest, SingularityUser user) { + final SingularityRequestWithState requestWithState = fetchRequestWithState(requestId, user); authorizationHelper.checkForAuthorization(requestWithState.getRequest(), user, SingularityAuthorizationScope.WRITE); @@ -444,10 +455,10 @@ public SingularityRequestParent exitCooldown(String requestId, Optional getActiveRequests(@QueryParam("useWebCache") Boolean useWebCache) { - return getRequestsWithDeployState(requestManager.getActiveRequests(useWebCache(useWebCache)), SingularityAuthorizationScope.READ); + public List getActiveRequests(@Auth SingularityUser user, + @QueryParam("useWebCache") Boolean useWebCache) { + return getRequestsWithDeployState(requestManager.getActiveRequests(useWebCache(useWebCache)), SingularityAuthorizationScope.READ, user); } - private List getRequestsWithDeployState(Iterable requests, final SingularityAuthorizationScope scope) { + private List getRequestsWithDeployState(Iterable requests, final SingularityAuthorizationScope scope, SingularityUser user) { if (!authorizationHelper.hasAdminAuthorization(user) && disasterManager.isDisabled(SingularityAction.EXPENSIVE_API_CALLS)) { LOG.trace("Short circuting getRequestsWithDeployState() to [] due to EXPENSIVE_API_CALLS disabled"); return Collections.emptyList(); @@ -498,38 +510,42 @@ public boolean apply(SingularityRequestWithState input) { @PropertyFiltering @Path("/paused") @ApiOperation(value="Retrieve the list of paused requests", response=SingularityRequestParent.class, responseContainer="List") - public List getPausedRequests(@QueryParam("useWebCache") Boolean useWebCache) { - return getRequestsWithDeployState(requestManager.getPausedRequests(useWebCache(useWebCache)), SingularityAuthorizationScope.READ); + public List getPausedRequests(@Auth SingularityUser user, + @QueryParam("useWebCache") Boolean useWebCache) { + return getRequestsWithDeployState(requestManager.getPausedRequests(useWebCache(useWebCache)), SingularityAuthorizationScope.READ, user); } @GET @PropertyFiltering @Path("/cooldown") @ApiOperation(value="Retrieve the list of requests in system cooldown", response=SingularityRequestParent.class, responseContainer="List") - public List getCooldownRequests(@QueryParam("useWebCache") Boolean useWebCache) { - return getRequestsWithDeployState(requestManager.getCooldownRequests(useWebCache(useWebCache)), SingularityAuthorizationScope.READ); + public List getCooldownRequests(@Auth SingularityUser user, + @QueryParam("useWebCache") Boolean useWebCache) { + return getRequestsWithDeployState(requestManager.getCooldownRequests(useWebCache(useWebCache)), SingularityAuthorizationScope.READ, user); } @GET @PropertyFiltering @Path("/finished") @ApiOperation(value="Retreive the list of finished requests (Scheduled requests which have exhausted their schedules)", response=SingularityRequestParent.class, responseContainer="List") - public List getFinishedRequests(@QueryParam("useWebCache") Boolean useWebCache) { - return getRequestsWithDeployState(requestManager.getFinishedRequests(useWebCache(useWebCache)), SingularityAuthorizationScope.READ); + public List getFinishedRequests(@Auth SingularityUser user, + @QueryParam("useWebCache") Boolean useWebCache) { + return getRequestsWithDeployState(requestManager.getFinishedRequests(useWebCache(useWebCache)), SingularityAuthorizationScope.READ, user); } @GET @PropertyFiltering @ApiOperation(value="Retrieve the list of all requests", response=SingularityRequestParent.class, responseContainer="List") - public List getRequests(@QueryParam("useWebCache") Boolean useWebCache) { - return getRequestsWithDeployState(requestManager.getRequests(useWebCache(useWebCache)), SingularityAuthorizationScope.READ); + public List getRequests(@Auth SingularityUser user, + @QueryParam("useWebCache") Boolean useWebCache) { + return getRequestsWithDeployState(requestManager.getRequests(useWebCache(useWebCache)), SingularityAuthorizationScope.READ, user); } @GET @PropertyFiltering @Path("/queued/pending") @ApiOperation(value="Retrieve the list of pending requests", response=SingularityPendingRequest.class, responseContainer="List") - public Iterable getPendingRequests() { + public Iterable getPendingRequests(@Auth SingularityUser user) { return authorizationHelper.filterByAuthorizedRequests(user, requestManager.getPendingRequests(), SingularityTransformHelpers.PENDING_REQUEST_TO_REQUEST_ID, SingularityAuthorizationScope.READ); } @@ -537,7 +553,7 @@ public Iterable getPendingRequests() { @PropertyFiltering @Path("/queued/cleanup") @ApiOperation(value="Retrieve the list of requests being cleaned up", response=SingularityRequestCleanup.class, responseContainer="List") - public Iterable getCleanupRequests() { + public Iterable getCleanupRequests(@Auth SingularityUser user) { return authorizationHelper.filterByAuthorizedRequests(user, requestManager.getCleanupRequests(), SingularityTransformHelpers.REQUEST_CLEANUP_TO_REQUEST_ID, SingularityAuthorizationScope.READ); } @@ -547,12 +563,14 @@ public Iterable getCleanupRequests() { @ApiResponses({ @ApiResponse(code=404, message="No Request with that ID"), }) - public SingularityRequestParent getRequest(@ApiParam("Request ID") @PathParam("requestId") String requestId, @QueryParam("useWebCache") Boolean useWebCache) { - return fillEntireRequest(fetchRequestWithState(requestId, useWebCache(useWebCache))); + public SingularityRequestParent getRequest(@Auth SingularityUser user, + @ApiParam("Request ID") @PathParam("requestId") String requestId, + @QueryParam("useWebCache") Boolean useWebCache) { + return fillEntireRequest(fetchRequestWithState(requestId, useWebCache(useWebCache), user)); } - public SingularityRequestParent getRequest(String requestId) { - return fillEntireRequest(fetchRequestWithState(requestId, false)); + public SingularityRequestParent getRequest(String requestId, SingularityUser user) { + return fillEntireRequest(fetchRequestWithState(requestId, false, user)); } @DELETE @@ -562,15 +580,16 @@ public SingularityRequestParent getRequest(String requestId) { @ApiResponses({ @ApiResponse(code=404, message="No Request with that ID"), }) - public SingularityRequest deleteRequest(@ApiParam("The request ID to delete.") @PathParam("requestId") String requestId, + public SingularityRequest deleteRequest(@Auth SingularityUser user, + @ApiParam("The request ID to delete.") @PathParam("requestId") String requestId, @Context HttpServletRequest requestContext, @ApiParam("Delete options") SingularityDeleteRequestRequest deleteRequest) { final Optional maybeDeleteRequest = Optional.fromNullable(deleteRequest); - return maybeProxyToLeader(requestContext, SingularityRequest.class, maybeDeleteRequest.orNull(), () -> deleteRequest(requestId, maybeDeleteRequest)); + return maybeProxyToLeader(requestContext, SingularityRequest.class, maybeDeleteRequest.orNull(), () -> deleteRequest(requestId, maybeDeleteRequest, user)); } - public SingularityRequest deleteRequest(String requestId, Optional deleteRequest) { - SingularityRequest request = fetchRequestWithState(requestId).getRequest(); + public SingularityRequest deleteRequest(String requestId, Optional deleteRequest, SingularityUser user) { + SingularityRequest request = fetchRequestWithState(requestId, user).getRequest(); authorizationHelper.checkForAuthorization(request, user, SingularityAuthorizationScope.WRITE); validator.checkActionEnabled(SingularityAction.REMOVE_REQUEST); @@ -585,9 +604,9 @@ public SingularityRequest deleteRequest(String requestId, Optional scale(requestId, scaleRequest)); + return maybeProxyToLeader(requestContext, SingularityRequestParent.class, scaleRequest, () -> scale(requestId, scaleRequest, user)); } - public SingularityRequestParent scale(String requestId, SingularityScaleRequest scaleRequest) { - SingularityRequestWithState oldRequestWithState = fetchRequestWithState(requestId); + public SingularityRequestParent scale(String requestId, SingularityScaleRequest scaleRequest, SingularityUser user) { + SingularityRequestWithState oldRequestWithState = fetchRequestWithState(requestId, user); SingularityRequest oldRequest = oldRequestWithState.getRequest(); authorizationHelper.checkForAuthorization(oldRequest, user, SingularityAuthorizationScope.WRITE); @@ -637,23 +657,23 @@ public SingularityRequestParent scale(String requestId, SingularityScaleRequest SingularityBounceRequest bounceRequest = new SingularityBounceRequest(Optional.of(isIncrementalBounce), scaleRequest.getSkipHealthchecks(), Optional.absent(), Optional.of(UUID.randomUUID().toString()), Optional.absent(), Optional.absent()); - submitRequest(newRequest, Optional.of(oldRequestWithState), Optional.of(RequestHistoryType.SCALED), scaleRequest.getSkipHealthchecks(), Optional.of(scaleMessage), Optional.of(bounceRequest)); + submitRequest(newRequest, Optional.of(oldRequestWithState), Optional.of(RequestHistoryType.SCALED), scaleRequest.getSkipHealthchecks(), Optional.of(scaleMessage), Optional.of(bounceRequest), user); } else { - submitRequest(newRequest, Optional.of(oldRequestWithState), Optional.of(RequestHistoryType.SCALED), scaleRequest.getSkipHealthchecks(), Optional.of(scaleMessage), Optional.absent()); + submitRequest(newRequest, Optional.of(oldRequestWithState), Optional.of(RequestHistoryType.SCALED), scaleRequest.getSkipHealthchecks(), Optional.of(scaleMessage), Optional.absent(), user); } if (scaleRequest.getDurationMillis().isPresent()) { - requestManager.saveExpiringObject(new SingularityExpiringScale(requestId, JavaUtils.getUserEmail(user), + requestManager.saveExpiringObject(new SingularityExpiringScale(requestId, user.getEmail(), System.currentTimeMillis(), scaleRequest, oldRequest.getInstances(), scaleRequest.getActionId().or(UUID.randomUUID().toString()), scaleRequest.getBounce())); } - mailer.sendRequestScaledMail(newRequest, Optional.of(scaleRequest), oldRequest.getInstances(), JavaUtils.getUserEmail(user)); + mailer.sendRequestScaledMail(newRequest, Optional.of(scaleRequest), oldRequest.getInstances(), user.getEmail()); - return fillEntireRequest(fetchRequestWithState(requestId)); + return fillEntireRequest(fetchRequestWithState(requestId, user)); } - private > SingularityRequestParent deleteExpiringObject(Class clazz, String requestId) { - SingularityRequestWithState requestWithState = fetchRequestWithState(requestId); + private > SingularityRequestParent deleteExpiringObject(Class clazz, String requestId, SingularityUser user) { + SingularityRequestWithState requestWithState = fetchRequestWithState(requestId, user); SingularityDeleteResult deleteResult = requestManager.deleteExpiringObject(clazz, requestId); @@ -668,8 +688,9 @@ private > SingularityRequest @ApiResponses({ @ApiResponse(code=404, message="No Request or expiring scale request for that ID"), }) - public SingularityRequestParent deleteExpiringScale(@ApiParam("The Request ID") @PathParam("requestId") String requestId) { - return deleteExpiringObject(SingularityExpiringScale.class, requestId); + public SingularityRequestParent deleteExpiringScale(@Auth SingularityUser user, + @ApiParam("The Request ID") @PathParam("requestId") String requestId) { + return deleteExpiringObject(SingularityExpiringScale.class, requestId, user); } @Deprecated @@ -679,8 +700,9 @@ public SingularityRequestParent deleteExpiringScale(@ApiParam("The Request ID") @ApiResponses({ @ApiResponse(code=404, message="No Request or expiring skipHealthchecks request for that ID"), }) - public SingularityRequestParent deleteExpiringSkipHealthchecksDeprecated(@ApiParam("The Request ID") @PathParam("requestId") String requestId) { - return deleteExpiringSkipHealthchecks(requestId); + public SingularityRequestParent deleteExpiringSkipHealthchecksDeprecated(@Auth SingularityUser user, + @ApiParam("The Request ID") @PathParam("requestId") String requestId) { + return deleteExpiringSkipHealthchecks(user, requestId); } @DELETE @@ -689,8 +711,9 @@ public SingularityRequestParent deleteExpiringSkipHealthchecksDeprecated(@ApiPar @ApiResponses({ @ApiResponse(code=404, message="No Request or expiring skipHealthchecks request for that ID"), }) - public SingularityRequestParent deleteExpiringSkipHealthchecks(@ApiParam("The Request ID") @PathParam("requestId") String requestId) { - return deleteExpiringObject(SingularityExpiringSkipHealthchecks.class, requestId); + public SingularityRequestParent deleteExpiringSkipHealthchecks(@Auth SingularityUser user, + @ApiParam("The Request ID") @PathParam("requestId") String requestId) { + return deleteExpiringObject(SingularityExpiringSkipHealthchecks.class, requestId, user); } @DELETE @@ -699,8 +722,9 @@ public SingularityRequestParent deleteExpiringSkipHealthchecks(@ApiParam("The Re @ApiResponses({ @ApiResponse(code=404, message="No Request or expiring pause request for that ID"), }) - public SingularityRequestParent deleteExpiringPause(@ApiParam("The Request ID") @PathParam("requestId") String requestId) { - return deleteExpiringObject(SingularityExpiringPause.class, requestId); + public SingularityRequestParent deleteExpiringPause(@Auth SingularityUser user, + @ApiParam("The Request ID") @PathParam("requestId") String requestId) { + return deleteExpiringObject(SingularityExpiringPause.class, requestId, user); } @DELETE @@ -709,8 +733,9 @@ public SingularityRequestParent deleteExpiringPause(@ApiParam("The Request ID") @ApiResponses({ @ApiResponse(code=404, message="No Request or expiring bounce request for that ID"), }) - public SingularityRequestParent deleteExpiringBounce(@ApiParam("The Request ID") @PathParam("requestId") String requestId) { - return deleteExpiringObject(SingularityExpiringBounce.class, requestId); + public SingularityRequestParent deleteExpiringBounce(@Auth SingularityUser user, + @ApiParam("The Request ID") @PathParam("requestId") String requestId) { + return deleteExpiringObject(SingularityExpiringBounce.class, requestId, user); } @Deprecated @@ -721,10 +746,11 @@ public SingularityRequestParent deleteExpiringBounce(@ApiParam("The Request ID") @ApiResponses({ @ApiResponse(code=404, message="No Request with that ID"), }) - public SingularityRequestParent skipHealthchecksDeprecated(@ApiParam("The Request ID to scale") @PathParam("requestId") String requestId, + public SingularityRequestParent skipHealthchecksDeprecated(@Auth SingularityUser user, + @ApiParam("The Request ID to scale") @PathParam("requestId") String requestId, @Context HttpServletRequest requestContext, @ApiParam("SkipHealtchecks options") SingularitySkipHealthchecksRequest skipHealthchecksRequest) { - return skipHealthchecks(requestId, requestContext, skipHealthchecksRequest); + return skipHealthchecks(user, requestId, requestContext, skipHealthchecksRequest); } @PUT @@ -734,33 +760,35 @@ public SingularityRequestParent skipHealthchecksDeprecated(@ApiParam("The Reques @ApiResponses({ @ApiResponse(code=404, message="No Request with that ID"), }) - public SingularityRequestParent skipHealthchecks(@ApiParam("The Request ID to scale") @PathParam("requestId") String requestId, + public SingularityRequestParent skipHealthchecks(@Auth SingularityUser user, + @ApiParam("The Request ID to scale") @PathParam("requestId") String requestId, @Context HttpServletRequest requestContext, @ApiParam("SkipHealtchecks options") SingularitySkipHealthchecksRequest skipHealthchecksRequest) { - return maybeProxyToLeader(requestContext, SingularityRequestParent.class, skipHealthchecksRequest, () -> skipHealthchecks(requestId, skipHealthchecksRequest)); + return maybeProxyToLeader(requestContext, SingularityRequestParent.class, skipHealthchecksRequest, () -> skipHealthchecks(requestId, skipHealthchecksRequest, user)); } - public SingularityRequestParent skipHealthchecks(String requestId, SingularitySkipHealthchecksRequest skipHealthchecksRequest) { - SingularityRequestWithState oldRequestWithState = fetchRequestWithState(requestId); + public SingularityRequestParent skipHealthchecks(String requestId, SingularitySkipHealthchecksRequest skipHealthchecksRequest, SingularityUser user) { + SingularityRequestWithState oldRequestWithState = fetchRequestWithState(requestId, user); SingularityRequest oldRequest = oldRequestWithState.getRequest(); SingularityRequest newRequest = oldRequest.toBuilder().setSkipHealthchecks(skipHealthchecksRequest.getSkipHealthchecks()).build(); - submitRequest(newRequest, Optional.of(oldRequestWithState), Optional. absent(), Optional. absent(), skipHealthchecksRequest.getMessage(), Optional.absent()); + submitRequest(newRequest, Optional.of(oldRequestWithState), Optional.absent(), Optional.absent(), skipHealthchecksRequest.getMessage(), Optional.absent(), user); if (skipHealthchecksRequest.getDurationMillis().isPresent()) { - requestManager.saveExpiringObject(new SingularityExpiringSkipHealthchecks(requestId, JavaUtils.getUserEmail(user), + requestManager.saveExpiringObject(new SingularityExpiringSkipHealthchecks(requestId, user.getEmail(), System.currentTimeMillis(), skipHealthchecksRequest, oldRequest.getSkipHealthchecks(), skipHealthchecksRequest.getActionId().or(UUID.randomUUID().toString()))); } - return fillEntireRequest(fetchRequestWithState(requestId)); + return fillEntireRequest(fetchRequestWithState(requestId, user)); } @GET @PropertyFiltering @Path("/lbcleanup") @ApiOperation("Retrieve the list of tasks being cleaned from load balancers.") - public Iterable getLbCleanupRequests(@QueryParam("useWebCache") Boolean useWebCache) { + public Iterable getLbCleanupRequests(@Auth SingularityUser user, + @QueryParam("useWebCache") Boolean useWebCache) { return authorizationHelper.filterAuthorizedRequestIds(user, requestManager.getLbCleanupRequestIds(), SingularityAuthorizationScope.READ, useWebCache(useWebCache)); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/S3LogResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/S3LogResource.java index ccddd7d225..1c0fba741c 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/S3LogResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/S3LogResource.java @@ -66,7 +66,6 @@ import com.hubspot.singularity.SingularityS3Log; import com.hubspot.singularity.SingularityS3LogMetadata; import com.hubspot.singularity.SingularityS3UploaderFile; -import com.hubspot.singularity.SingularityService; import com.hubspot.singularity.SingularityTaskHistory; import com.hubspot.singularity.SingularityTaskHistoryUpdate; import com.hubspot.singularity.SingularityTaskHistoryUpdate.SimplifiedTaskState; @@ -90,6 +89,8 @@ import com.wordnik.swagger.annotations.ApiOperation; import com.wordnik.swagger.annotations.ApiParam; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.S3_LOG_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api(description="Manages Singularity task logs stored in S3.", value=ApiPaths.S3_LOG_RESOURCE_PATH) @@ -116,8 +117,8 @@ public int compare(SingularityS3LogMetadata o1, SingularityS3LogMetadata o2) { @Inject public S3LogResource(RequestManager requestManager, HistoryManager historyManager, RequestHistoryHelper requestHistoryHelper, TaskManager taskManager, DeployManager deployManager, - Optional configuration, SingularityAuthorizationHelper authorizationHelper, Optional user,SingularityS3Services s3Services) { - super(historyManager, taskManager, deployManager, authorizationHelper, user); + Optional configuration, SingularityAuthorizationHelper authorizationHelper, SingularityS3Services s3Services) { + super(historyManager, taskManager, deployManager, authorizationHelper); this.requestManager = requestManager; this.configuration = configuration; this.requestHistoryHelper = requestHistoryHelper; @@ -125,8 +126,8 @@ public S3LogResource(RequestManager requestManager, HistoryManager historyManage } // Generation of prefixes - private Collection getS3PrefixesForTask(S3Configuration s3Configuration, SingularityTaskId taskId, Optional startArg, Optional endArg, String group) { - Optional history = getTaskHistory(taskId); + private Collection getS3PrefixesForTask(S3Configuration s3Configuration, SingularityTaskId taskId, Optional startArg, Optional endArg, String group, SingularityUser user) { + Optional history = getTaskHistory(taskId, user); long start = taskId.getStartedAt(); if (startArg.isPresent()) { @@ -204,8 +205,8 @@ private Collection getS3PrefixesForRequest(S3Configuration s3Configurati return prefixes; } - private Collection getS3PrefixesForDeploy(S3Configuration s3Configuration, String requestId, String deployId, Optional startArg, Optional endArg, String group) { - SingularityDeployHistory deployHistory = getDeployHistory(requestId, deployId); + private Collection getS3PrefixesForDeploy(S3Configuration s3Configuration, String requestId, String deployId, Optional startArg, Optional endArg, String group, SingularityUser user) { + SingularityDeployHistory deployHistory = getDeployHistory(requestId, deployId, user); long start = deployHistory.getDeployMarker().getTimestamp(); if (startArg.isPresent()) { @@ -240,15 +241,15 @@ private Collection getS3PrefixesForDeploy(S3Configuration s3Configuratio return prefixes; } - private Map> getServiceToPrefixes(SingularityS3SearchRequest search) { + private Map> getServiceToPrefixes(SingularityS3SearchRequest search, SingularityUser user) { Map> servicesToPrefixes = new HashMap<>(); if (!search.getTaskIds().isEmpty()) { for (String taskId : search.getTaskIds()) { SingularityTaskId taskIdObject = getTaskIdObject(taskId); - String group = getRequestGroupForTask(taskIdObject).or(SingularityS3FormatHelper.DEFAULT_GROUP_NAME); + String group = getRequestGroupForTask(taskIdObject, user).or(SingularityS3FormatHelper.DEFAULT_GROUP_NAME); Set s3Buckets = getBuckets(group); - Collection prefixes = getS3PrefixesForTask(configuration.get(), taskIdObject, search.getStart(), search.getEnd(), group); + Collection prefixes = getS3PrefixesForTask(configuration.get(), taskIdObject, search.getStart(), search.getEnd(), group, user); for (String s3Bucket : s3Buckets) { SingularityS3Service s3Service = s3Services.getServiceByGroupAndBucketOrDefault(group, s3Bucket); if (!servicesToPrefixes.containsKey(s3Service)) { @@ -260,12 +261,12 @@ private Map> getServiceToPrefixes(SingularityS } if (!search.getRequestsAndDeploys().isEmpty()) { for (Map.Entry> entry : search.getRequestsAndDeploys().entrySet()) { - String group = getRequestGroup(entry.getKey()).or(SingularityS3FormatHelper.DEFAULT_GROUP_NAME); + String group = getRequestGroup(entry.getKey(), user).or(SingularityS3FormatHelper.DEFAULT_GROUP_NAME); Set s3Buckets = getBuckets(group); List prefixes = new ArrayList<>(); if (!entry.getValue().isEmpty()) { for (String deployId : entry.getValue()) { - prefixes.addAll(getS3PrefixesForDeploy(configuration.get(), entry.getKey(), deployId, search.getStart(), search.getEnd(), group)); + prefixes.addAll(getS3PrefixesForDeploy(configuration.get(), entry.getKey(), deployId, search.getStart(), search.getEnd(), group, user)); } } else { prefixes.addAll(getS3PrefixesForRequest(configuration.get(), entry.getKey(), search.getStart(), search.getEnd(), group)); @@ -502,18 +503,18 @@ private boolean isFinalPageForAllPrefixes(Collection continua } // Finding request group - private Optional getRequestGroupForTask(final SingularityTaskId taskId) { - Optional maybeTaskHistory = getTaskHistory(taskId); + private Optional getRequestGroupForTask(final SingularityTaskId taskId, SingularityUser user) { + Optional maybeTaskHistory = getTaskHistory(taskId, user); if (maybeTaskHistory.isPresent()) { SingularityRequest request = maybeTaskHistory.get().getTask().getTaskRequest().getRequest(); authorizationHelper.checkForAuthorization(request, user, SingularityAuthorizationScope.READ); return request.getGroup(); } else { - return getRequestGroup(taskId.getRequestId()); + return getRequestGroup(taskId.getRequestId(), user); } } - private Optional getRequestGroup(final String requestId) { + private Optional getRequestGroup(final String requestId, SingularityUser user) { final Optional maybeRequest = requestManager.getRequest(requestId); if (maybeRequest.isPresent()) { authorizationHelper.checkForAuthorization(maybeRequest.get().getRequest(), user, SingularityAuthorizationScope.READ); @@ -540,6 +541,7 @@ private void checkS3() { @Path("/task/{taskId}") @ApiOperation("Retrieve the list of logs stored in S3 for a specific task.") public List getS3LogsForTask( + @Auth SingularityUser user, @ApiParam("The task ID to search for") @PathParam("taskId") String taskId, @ApiParam("Start timestamp (millis, 13 digit)") @QueryParam("start") Optional start, @ApiParam("End timestamp (mills, 13 digit)") @QueryParam("end") Optional end, @@ -558,7 +560,7 @@ public List getS3LogsForTask( Collections.emptyMap()); try { - return getS3Logs(configuration.get(), getServiceToPrefixes(search), search, false).getResults(); + return getS3Logs(configuration.get(), getServiceToPrefixes(search, user), search, false).getResults(); } catch (TimeoutException te) { throw timeout("Timed out waiting for response from S3 for %s", taskId); } catch (Throwable t) { @@ -570,6 +572,7 @@ public List getS3LogsForTask( @Path("/request/{requestId}") @ApiOperation("Retrieve the list of logs stored in S3 for a specific request.") public List getS3LogsForRequest( + @Auth SingularityUser user, @ApiParam("The request ID to search for") @PathParam("requestId") String requestId, @ApiParam("Start timestamp (millis, 13 digit)") @QueryParam("start") Optional start, @ApiParam("End timestamp (mills, 13 digit)") @QueryParam("end") Optional end, @@ -589,7 +592,7 @@ public List getS3LogsForRequest( Optional.absent(), Collections.emptyMap()); - return getS3Logs(configuration.get(), getServiceToPrefixes(search), search, false).getResults(); + return getS3Logs(configuration.get(), getServiceToPrefixes(search, user), search, false).getResults(); } catch (TimeoutException te) { throw timeout("Timed out waiting for response from S3 for %s", requestId); } catch (Throwable t) { @@ -601,6 +604,7 @@ public List getS3LogsForRequest( @Path("/request/{requestId}/deploy/{deployId}") @ApiOperation("Retrieve the list of logs stored in S3 for a specific deploy.") public List getS3LogsForDeploy( + @Auth SingularityUser user, @ApiParam("The request ID to search for") @PathParam("requestId") String requestId, @ApiParam("The deploy ID to search for") @PathParam("deployId") String deployId, @ApiParam("Start timestamp (millis, 13 digit)") @QueryParam("start") Optional start, @@ -621,7 +625,7 @@ public List getS3LogsForDeploy( Optional.absent(), Collections.emptyMap()); - return getS3Logs(configuration.get(), getServiceToPrefixes(search), search, false).getResults(); + return getS3Logs(configuration.get(), getServiceToPrefixes(search, user), search, false).getResults(); } catch (TimeoutException te) { throw timeout("Timed out waiting for response from S3 for %s-%s", requestId, deployId); } catch (Throwable t) { @@ -633,13 +637,13 @@ public List getS3LogsForDeploy( @Path("/search") @Consumes(MediaType.APPLICATION_JSON) @ApiOperation("Retrieve a paginated list of logs stored in S3") - public SingularityS3SearchResult getPaginatedS3Logs(@ApiParam(required = true) SingularityS3SearchRequest search) throws Exception { + public SingularityS3SearchResult getPaginatedS3Logs(@Auth SingularityUser user, @ApiParam(required = true) SingularityS3SearchRequest search) throws Exception { checkS3(); checkBadRequest(!search.getRequestsAndDeploys().isEmpty() || !search.getTaskIds().isEmpty(), "Must specify at least one request or task to search"); try { - return getS3Logs(configuration.get(), getServiceToPrefixes(search), search, true); + return getS3Logs(configuration.get(), getServiceToPrefixes(search, user), search, true); } catch (TimeoutException te) { throw timeout("Timed out waiting for response from S3 for %s", search); } catch (Throwable t) { diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/SandboxResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/SandboxResource.java index 2a4466e436..f721166d1f 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/SandboxResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/SandboxResource.java @@ -45,6 +45,8 @@ import com.wordnik.swagger.annotations.ApiOperation; import com.wordnik.swagger.annotations.ApiParam; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.SANDBOX_RESOURCE_PATH) @Produces({MediaType.APPLICATION_JSON}) @Api(description="Provides a proxy to Mesos sandboxes.", value=ApiPaths.SANDBOX_RESOURCE_PATH) @@ -55,17 +57,17 @@ public class SandboxResource extends AbstractHistoryResource { @Inject public SandboxResource(HistoryManager historyManager, TaskManager taskManager, SandboxManager sandboxManager, DeployManager deployManager, SingularityMesosExecutorInfoSupport logSupport, - SingularityConfiguration configuration, SingularityAuthorizationHelper authorizationHelper, Optional user) { - super(historyManager, taskManager, deployManager, authorizationHelper, user); + SingularityConfiguration configuration, SingularityAuthorizationHelper authorizationHelper) { + super(historyManager, taskManager, deployManager, authorizationHelper); this.configuration = configuration; this.sandboxManager = sandboxManager; this.logSupport = logSupport; } - private SingularityTaskHistory checkHistory(String taskId) { + private SingularityTaskHistory checkHistory(String taskId, SingularityUser user) { final SingularityTaskId taskIdObj = getTaskIdObject(taskId); - final SingularityTaskHistory taskHistory = getTaskHistoryRequired(taskIdObj); + final SingularityTaskHistory taskHistory = getTaskHistoryRequired(taskIdObj, user); if (!taskHistory.getDirectory().isPresent()) { logSupport.checkDirectoryAndContainerId(taskIdObj); @@ -89,8 +91,8 @@ private String getCurrentDirectory(String taskId, String currentDirectory) { @GET @Path("/{taskId}/browse") @ApiOperation("Retrieve information about a specific task's sandbox.") - public SingularitySandbox browse(@ApiParam("The task ID to browse") @PathParam("taskId") String taskId, - @ApiParam("The path to browse from") @QueryParam("path") String path) { + public SingularitySandbox browse(@Auth SingularityUser user, @ApiParam("The task ID to browse") @PathParam("taskId") String taskId, + @ApiParam("The path to browse from") @QueryParam("path") String path) { authorizationHelper.checkForAuthorizationByTaskId(taskId, user, SingularityAuthorizationScope.READ); // Remove all trailing slashes from the path @@ -99,7 +101,7 @@ public SingularitySandbox browse(@ApiParam("The task ID to browse") @PathParam(" } final String currentDirectory = getCurrentDirectory(taskId, path); - final SingularityTaskHistory history = checkHistory(taskId); + final SingularityTaskHistory history = checkHistory(taskId, user); final String slaveHostname = history.getTask().getHostname(); final String pathToRoot = history.getDirectory().get(); @@ -127,14 +129,15 @@ public SingularitySandboxFile apply(MesosFileObject input) { @GET @Path("/{taskId}/read") @ApiOperation("Retrieve part of the contents of a file in a specific task's sandbox.") - public MesosFileChunkObject read(@ApiParam("The task ID of the sandbox to read from") @PathParam("taskId") String taskId, - @ApiParam("The path to the file to be read") @QueryParam("path") String path, - @ApiParam("Optional string to grep for") @QueryParam("grep") Optional grep, - @ApiParam("Byte offset to start reading from") @QueryParam("offset") Optional offset, - @ApiParam("Maximum number of bytes to read") @QueryParam("length") Optional length) { + public MesosFileChunkObject read(@Auth SingularityUser user, + @ApiParam("The task ID of the sandbox to read from") @PathParam("taskId") String taskId, + @ApiParam("The path to the file to be read") @QueryParam("path") String path, + @ApiParam("Optional string to grep for") @QueryParam("grep") Optional grep, + @ApiParam("Byte offset to start reading from") @QueryParam("offset") Optional offset, + @ApiParam("Maximum number of bytes to read") @QueryParam("length") Optional length) { authorizationHelper.checkForAuthorizationByTaskId(taskId, user, SingularityAuthorizationScope.READ); - final SingularityTaskHistory history = checkHistory(taskId); + final SingularityTaskHistory history = checkHistory(taskId, user); checkBadRequest(!Strings.isNullOrEmpty(path), "Must specify 'path'"); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/SlaveResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/SlaveResource.java index ec233efeb7..aba56eec3e 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/SlaveResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/SlaveResource.java @@ -13,7 +13,6 @@ import com.google.common.base.Optional; import com.google.inject.Inject; -import com.hubspot.mesos.JavaUtils; import com.hubspot.singularity.MachineState; import com.hubspot.singularity.SingularityAction; import com.hubspot.singularity.SingularityMachineStateHistoryUpdate; @@ -29,13 +28,15 @@ import com.wordnik.swagger.annotations.ApiOperation; import com.wordnik.swagger.annotations.ApiParam; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.SLAVE_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api(description="Manages Singularity slaves.", value=ApiPaths.SLAVE_RESOURCE_PATH) public class SlaveResource extends AbstractMachineResource { @Inject - public SlaveResource(SlaveManager slaveManager, SingularityAuthorizationHelper authorizationHelper, Optional user, SingularityValidator validator) { - super(slaveManager, authorizationHelper, user, validator); + public SlaveResource(SlaveManager slaveManager, SingularityAuthorizationHelper authorizationHelper, SingularityValidator validator) { + super(slaveManager, authorizationHelper, validator); } @Override @@ -46,67 +47,71 @@ protected String getObjectTypeString() { @GET @Path("/") @ApiOperation("Retrieve the list of all known slaves, optionally filtering by a particular state") - public List getSlaves(@ApiParam("Optionally specify a particular state to filter slaves by") @QueryParam("state") Optional filterState) { + public List getSlaves(@Auth SingularityUser user, + @ApiParam("Optionally specify a particular state to filter slaves by") @QueryParam("state") Optional filterState) { + authorizationHelper.checkAdminAuthorization(user); return manager.getObjectsFiltered(filterState); } @GET @Path("/slave/{slaveId}") @ApiOperation("Retrieve the history of a given slave") - public List getSlaveHistory(@ApiParam("Slave ID") @PathParam("slaveId") String slaveId) { + public List getSlaveHistory(@Auth SingularityUser user, @ApiParam("Slave ID") @PathParam("slaveId") String slaveId) { + authorizationHelper.checkAdminAuthorization(user); return manager.getHistory(slaveId); } @GET @Path("/slave/{slaveId}/details") @ApiOperation("Get information about a particular slave") - public Optional getSlave(@ApiParam("Slave ID") @PathParam("slaveId") String slaveId) { + public Optional getSlave(@Auth SingularityUser user, @ApiParam("Slave ID") @PathParam("slaveId") String slaveId) { + authorizationHelper.checkAdminAuthorization(user); return manager.getObject(slaveId); } @DELETE @Path("/slave/{slaveId}") @ApiOperation("Remove a known slave, erasing history. This operation will cancel decomissioning of the slave") - public void removeSlave(@ApiParam("Active SlaveId") @PathParam("slaveId") String slaveId) { - super.remove(slaveId); + public void removeSlave(@Auth SingularityUser user, @ApiParam("Active SlaveId") @PathParam("slaveId") String slaveId) { + super.remove(slaveId, user); } @POST @Path("/slave/{slaveId}/decommission") @ApiOperation("Begin decommissioning a specific active slave") - public void decommissionSlave(@ApiParam("Active slaveId") @PathParam("slaveId") String slaveId, SingularityMachineChangeRequest changeRequest) { + public void decommissionSlave(@Auth SingularityUser user, @ApiParam("Active slaveId") @PathParam("slaveId") String slaveId, SingularityMachineChangeRequest changeRequest) { final Optional maybeChangeRequest = Optional.fromNullable(changeRequest); - super.decommission(slaveId, maybeChangeRequest, JavaUtils.getUserEmail(user), SingularityAction.DECOMMISSION_SLAVE); + super.decommission(slaveId, maybeChangeRequest, user, SingularityAction.DECOMMISSION_SLAVE); } @POST @Path("/slave/{slaveId}/freeze") @ApiOperation("Freeze tasks on a specific slave") - public void freezeSlave(@ApiParam("Slave ID") @PathParam("slaveId") String slaveId, SingularityMachineChangeRequest changeRequest) { + public void freezeSlave(@Auth SingularityUser user, @ApiParam("Slave ID") @PathParam("slaveId") String slaveId, SingularityMachineChangeRequest changeRequest) { final Optional maybeChangeRequest = Optional.fromNullable(changeRequest); - super.freeze(slaveId, maybeChangeRequest, JavaUtils.getUserEmail(user), SingularityAction.FREEZE_SLAVE); + super.freeze(slaveId, maybeChangeRequest, user, SingularityAction.FREEZE_SLAVE); } @POST @Path("/slave/{slaveId}/activate") @ApiOperation("Activate a decomissioning slave, canceling decomission without erasing history") - public void activateSlave(@ApiParam("Active slaveId") @PathParam("slaveId") String slaveId, SingularityMachineChangeRequest changeRequest) { + public void activateSlave(@Auth SingularityUser user, @ApiParam("Active slaveId") @PathParam("slaveId") String slaveId, SingularityMachineChangeRequest changeRequest) { final Optional maybeChangeRequest = Optional.fromNullable(changeRequest); - super.activate(slaveId, maybeChangeRequest, JavaUtils.getUserEmail(user), SingularityAction.ACTIVATE_SLAVE); + super.activate(slaveId, maybeChangeRequest, user, SingularityAction.ACTIVATE_SLAVE); } @DELETE @Path("/slave/{slaveId}/expiring") @ApiOperation("Delete any expiring machine state changes for this slave") - public void deleteExpiringStateChange(@ApiParam("Active slaveId") @PathParam("slaveId") String slaveId) { - super.cancelExpiring(slaveId); + public void deleteExpiringStateChange(@Auth SingularityUser user, @ApiParam("Active slaveId") @PathParam("slaveId") String slaveId) { + super.cancelExpiring(slaveId, user); } @GET @Path("/expiring") @ApiOperation("Get all expiring state changes for all slaves") - public List getExpiringStateChanges() { - return super.getExpiringStateChanges(); + public List getExpiringStateChanges(@Auth SingularityUser user) { + return super.getExpiringStateChanges(user); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/TaskResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/TaskResource.java index 4382ee7d14..c9552b991c 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/TaskResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/TaskResource.java @@ -36,7 +36,6 @@ import com.google.common.collect.Lists; import com.google.inject.Inject; import com.hubspot.jackson.jaxrs.PropertyFiltering; -import com.hubspot.mesos.JavaUtils; import com.hubspot.mesos.client.MesosClient; import com.hubspot.mesos.json.MesosTaskMonitorObject; import com.hubspot.mesos.json.MesosTaskStatisticsObject; @@ -87,6 +86,8 @@ import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.TASK_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api(description="Manages Singularity tasks.", value=ApiPaths.TASK_RESOURCE_PATH) @@ -99,7 +100,6 @@ public class TaskResource extends AbstractLeaderAwareResource { private final TaskRequestManager taskRequestManager; private final MesosClient mesosClient; private final SingularityAuthorizationHelper authorizationHelper; - private final Optional user; private final SingularityTaskMetadataConfiguration taskMetadataConfiguration; private final SingularityValidator validator; private final DisasterManager disasterManager; @@ -108,7 +108,7 @@ public class TaskResource extends AbstractLeaderAwareResource { @Inject public TaskResource(TaskRequestManager taskRequestManager, TaskManager taskManager, SlaveManager slaveManager, MesosClient mesosClient, SingularityTaskMetadataConfiguration taskMetadataConfiguration, - SingularityAuthorizationHelper authorizationHelper, Optional user, RequestManager requestManager, SingularityValidator validator, DisasterManager disasterManager, + SingularityAuthorizationHelper authorizationHelper, RequestManager requestManager, SingularityValidator validator, DisasterManager disasterManager, AsyncHttpClient httpClient, LeaderLatch leaderLatch, ObjectMapper objectMapper, SingularityDeployHealthHelper deployHealthHelper, DeployManager deployManager) { super(httpClient, leaderLatch, objectMapper); this.taskManager = taskManager; @@ -118,7 +118,6 @@ public TaskResource(TaskRequestManager taskRequestManager, TaskManager taskManag this.mesosClient = mesosClient; this.requestManager = requestManager; this.authorizationHelper = authorizationHelper; - this.user = user; this.validator = validator; this.disasterManager = disasterManager; this.deployHealthHelper = deployHealthHelper; @@ -129,7 +128,7 @@ public TaskResource(TaskRequestManager taskRequestManager, TaskManager taskManag @PropertyFiltering @Path("/scheduled") @ApiOperation("Retrieve list of scheduled tasks.") - public List getScheduledTasks(@QueryParam("useWebCache") Boolean useWebCache) { + public List getScheduledTasks(@Auth SingularityUser user, @QueryParam("useWebCache") Boolean useWebCache) { if (!authorizationHelper.hasAdminAuthorization(user) && disasterManager.isDisabled(SingularityAction.EXPENSIVE_API_CALLS)) { LOG.trace("Short circuting getScheduledTasks() to [] due to EXPENSIVE_API_CALLS disabled"); return Collections.emptyList(); @@ -143,7 +142,7 @@ public List getScheduledTasks(@QueryParam("useWebCache") @PropertyFiltering @Path("/scheduled/ids") @ApiOperation("Retrieve list of scheduled task IDs.") - public Iterable getScheduledTaskIds(@QueryParam("useWebCache") Boolean useWebCache) { + public Iterable getScheduledTaskIds(@Auth SingularityUser user, @QueryParam("useWebCache") Boolean useWebCache) { return authorizationHelper.filterByAuthorizedRequests(user, taskManager.getPendingTaskIds(useWebCache(useWebCache)), SingularityTransformHelpers.PENDING_TASK_ID_TO_REQUEST_ID, SingularityAuthorizationScope.READ); } @@ -167,7 +166,7 @@ private SingularityTaskId getTaskIdFromStr(String activeTaskIdStr) { @PropertyFiltering @Path("/scheduled/task/{pendingTaskId}") @ApiOperation("Retrieve information about a pending task.") - public SingularityTaskRequest getPendingTask(@PathParam("pendingTaskId") String pendingTaskIdStr) { + public SingularityTaskRequest getPendingTask(@Auth SingularityUser user, @PathParam("pendingTaskId") String pendingTaskIdStr) { Optional pendingTask = taskManager.getPendingTask(getPendingTaskIdFromStr(pendingTaskIdStr)); checkNotFound(pendingTask.isPresent(), "Couldn't find %s", pendingTaskIdStr); @@ -185,7 +184,7 @@ public SingularityTaskRequest getPendingTask(@PathParam("pendingTaskId") String @PropertyFiltering @Path("/scheduled/request/{requestId}") @ApiOperation("Retrieve list of scheduled tasks for a specific request.") - public List getScheduledTasksForRequest(@PathParam("requestId") String requestId, @QueryParam("useWebCache") Boolean useWebCache) { + public List getScheduledTasksForRequest(@Auth SingularityUser user, @PathParam("requestId") String requestId, @QueryParam("useWebCache") Boolean useWebCache) { authorizationHelper.checkForAuthorizationByRequestId(requestId, user, SingularityAuthorizationScope.READ); final List tasks = Lists.newArrayList(Iterables.filter(taskManager.getPendingTasks(useWebCache(useWebCache)), SingularityPendingTask.matchingRequest(requestId))); @@ -196,7 +195,7 @@ public List getScheduledTasksForRequest(@PathParam("requ @GET @Path("/ids/request/{requestId}") @ApiOperation("Retrieve a list of task ids separated by status") - public Optional getTaskIdsByStatusForRequest(@PathParam("requestId") String requestId) { + public Optional getTaskIdsByStatusForRequest(@Auth SingularityUser user, @PathParam("requestId") String requestId) { authorizationHelper.checkForAuthorizationByRequestId(requestId, user, SingularityAuthorizationScope.READ); Optional requestWithState = requestManager.getRequest(requestId); @@ -235,7 +234,7 @@ public Optional getTaskIdsByStatusForRequest(@PathPa @GET @Path("/active/slave/{slaveId}") @ApiOperation("Retrieve list of active tasks on a specific slave.") - public Iterable getTasksForSlave(@PathParam("slaveId") String slaveId, @QueryParam("useWebCache") Boolean useWebCache) { + public Iterable getTasksForSlave(@Auth SingularityUser user, @PathParam("slaveId") String slaveId, @QueryParam("useWebCache") Boolean useWebCache) { Optional maybeSlave = slaveManager.getObject(slaveId); checkNotFound(maybeSlave.isPresent(), "Couldn't find a slave in any state with id %s", slaveId); @@ -247,7 +246,7 @@ public Iterable getTasksForSlave(@PathParam("slaveId") String s @PropertyFiltering @Path("/active") @ApiOperation("Retrieve the list of active tasks.") - public Iterable getActiveTasks(@QueryParam("useWebCache") Boolean useWebCache) { + public Iterable getActiveTasks(@Auth SingularityUser user, @QueryParam("useWebCache") Boolean useWebCache) { return authorizationHelper.filterByAuthorizedRequests(user, taskManager.getActiveTasks(useWebCache(useWebCache)), SingularityTransformHelpers.TASK_TO_REQUEST_ID, SingularityAuthorizationScope.READ); } @@ -255,7 +254,7 @@ public Iterable getActiveTasks(@QueryParam("useWebCache") Boole @PropertyFiltering @Path("/cleaning") @ApiOperation("Retrieve the list of cleaning tasks.") - public Iterable getCleaningTasks(@QueryParam("useWebCache") Boolean useWebCache) { + public Iterable getCleaningTasks(@Auth SingularityUser user, @QueryParam("useWebCache") Boolean useWebCache) { if (!authorizationHelper.hasAdminAuthorization(user) && disasterManager.isDisabled(SingularityAction.EXPENSIVE_API_CALLS)) { LOG.trace("Short circuting getCleaningTasks() to [] due to EXPENSIVE_API_CALLS disabled"); return Collections.emptyList(); @@ -267,7 +266,7 @@ public Iterable getCleaningTasks(@QueryParam("useWebCach @GET @Path("/killed") @ApiOperation("Retrieve the list of killed tasks.") - public Iterable getKilledTasks() { + public Iterable getKilledTasks(@Auth SingularityUser user) { return authorizationHelper.filterByAuthorizedRequests(user, taskManager.getKilledTaskIdRecords(), SingularityTransformHelpers.KILLED_TASK_ID_RECORD_TO_REQUEST_ID, SingularityAuthorizationScope.READ); } @@ -275,11 +274,11 @@ public Iterable getKilledTasks() { @PropertyFiltering @Path("/lbcleanup") @ApiOperation("Retrieve the list of tasks being cleaned from load balancers.") - public Iterable getLbCleanupTasks() { + public Iterable getLbCleanupTasks(@Auth SingularityUser user) { return authorizationHelper.filterByAuthorizedRequests(user, taskManager.getLBCleanupTasks(), SingularityTransformHelpers.TASK_ID_TO_REQUEST_ID, SingularityAuthorizationScope.READ); } - private SingularityTask checkActiveTask(String taskId, SingularityAuthorizationScope scope) { + private SingularityTask checkActiveTask(String taskId, SingularityAuthorizationScope scope, SingularityUser user) { SingularityTaskId taskIdObj = getTaskIdFromStr(taskId); Optional task = taskManager.getTask(taskIdObj); @@ -296,15 +295,15 @@ private SingularityTask checkActiveTask(String taskId, SingularityAuthorizationS @GET @Path("/task/{taskId}") @ApiOperation("Retrieve information about a specific active task.") - public SingularityTask getActiveTask(@PathParam("taskId") String taskId) { - return checkActiveTask(taskId, SingularityAuthorizationScope.READ); + public SingularityTask getActiveTask(@Auth SingularityUser user, @PathParam("taskId") String taskId) { + return checkActiveTask(taskId, SingularityAuthorizationScope.READ, user); } @GET @Path("/task/{taskId}/statistics") @ApiOperation("Retrieve statistics about a specific active task.") - public MesosTaskStatisticsObject getTaskStatistics(@PathParam("taskId") String taskId) { - SingularityTask task = checkActiveTask(taskId, SingularityAuthorizationScope.READ); + public MesosTaskStatisticsObject getTaskStatistics(@Auth SingularityUser user, @PathParam("taskId") String taskId) { + SingularityTask task = checkActiveTask(taskId, SingularityAuthorizationScope.READ, user); String executorIdToMatch = null; @@ -326,7 +325,7 @@ public MesosTaskStatisticsObject getTaskStatistics(@PathParam("taskId") String t @GET @Path("/task/{taskId}/cleanup") @ApiOperation("Get the cleanup object for the task, if it exists") - public Optional getTaskCleanup(@PathParam("taskId") String taskId) { + public Optional getTaskCleanup(@Auth SingularityUser user, @PathParam("taskId") String taskId) { authorizationHelper.checkForAuthorizationByTaskId(taskId, user, SingularityAuthorizationScope.READ); return taskManager.getTaskCleanup(taskId); @@ -334,8 +333,8 @@ public Optional getTaskCleanup(@PathParam("taskId") Stri @DELETE @Path("/task/{taskId}") - public SingularityTaskCleanup killTask(@PathParam("taskId") String taskId, @Context HttpServletRequest requestContext) { - return killTask(taskId, requestContext, null); + public SingularityTaskCleanup killTask(@Auth SingularityUser user, @PathParam("taskId") String taskId, @Context HttpServletRequest requestContext) { + return killTask(taskId, requestContext, null, user); } @DELETE @@ -347,13 +346,14 @@ public SingularityTaskCleanup killTask(@PathParam("taskId") String taskId, @Cont }) public SingularityTaskCleanup killTask(@PathParam("taskId") String taskId, @Context HttpServletRequest requestContext, - SingularityKillTaskRequest killTaskRequest) { + SingularityKillTaskRequest killTaskRequest, + @Auth SingularityUser user) { final Optional maybeKillTaskRequest = Optional.fromNullable(killTaskRequest); - return maybeProxyToLeader(requestContext, SingularityTaskCleanup.class, maybeKillTaskRequest.orNull(), () -> killTask(taskId, maybeKillTaskRequest)); + return maybeProxyToLeader(requestContext, SingularityTaskCleanup.class, maybeKillTaskRequest.orNull(), () -> killTask(taskId, maybeKillTaskRequest, user)); } - public SingularityTaskCleanup killTask(String taskId, Optional killTaskRequest) { - final SingularityTask task = checkActiveTask(taskId, SingularityAuthorizationScope.WRITE); + public SingularityTaskCleanup killTask(String taskId, Optional killTaskRequest, SingularityUser user) { + final SingularityTask task = checkActiveTask(taskId, SingularityAuthorizationScope.WRITE, user); Optional message = Optional.absent(); Optional override = Optional.absent(); @@ -367,7 +367,7 @@ public SingularityTaskCleanup killTask(String taskId, Optional> absent(), Optional. absent(), Optional. absent(), message, actionId)); } @@ -429,7 +429,7 @@ boolean userRequestedKillTakesPriority(String taskId) { @Path("/commands/queued") @ApiOperation(value="Retrieve a list of all the shell commands queued for execution") - public List getQueuedShellCommands() { + public List getQueuedShellCommands(@Auth SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); return taskManager.getAllQueuedTaskShellCommandRequests(); } @@ -443,7 +443,7 @@ public List getQueuedShellCommands() { @ApiResponse(code=409, message="Metadata with this type/timestamp already existed") }) @Consumes({ MediaType.APPLICATION_JSON }) - public void postTaskMetadata(@PathParam("taskId") String taskId, final SingularityTaskMetadataRequest taskMetadataRequest) { + public void postTaskMetadata(@Auth SingularityUser user, @PathParam("taskId") String taskId, final SingularityTaskMetadataRequest taskMetadataRequest) { SingularityTaskId taskIdObj = getTaskIdFromStr(taskId); authorizationHelper.checkForAuthorizationByTaskId(taskId, user, SingularityAuthorizationScope.WRITE); @@ -464,7 +464,7 @@ public void postTaskMetadata(@PathParam("taskId") String taskId, final Singulari WebExceptions.checkNotFound(taskManager.taskExistsInZk(taskIdObj), "Task %s not found in ZooKeeper (can not save metadata to tasks which have been persisted", taskIdObj); final SingularityTaskMetadata taskMetadata = new SingularityTaskMetadata(taskIdObj, System.currentTimeMillis(), taskMetadataRequest.getType(), taskMetadataRequest.getTitle(), - taskMetadataRequest.getMessage(), JavaUtils.getUserEmail(user), taskMetadataRequest.getLevel()); + taskMetadataRequest.getMessage(), user.getEmail(), taskMetadataRequest.getLevel()); SingularityCreateResult result = taskManager.saveTaskMetadata(taskMetadata); @@ -479,7 +479,7 @@ public void postTaskMetadata(@PathParam("taskId") String taskId, final Singulari @ApiResponse(code=403, message="Given shell command doesn't exist") }) @Consumes({ MediaType.APPLICATION_JSON }) - public SingularityTaskShellCommandRequest runShellCommand(@PathParam("taskId") String taskId, final SingularityShellCommand shellCommand) { + public SingularityTaskShellCommandRequest runShellCommand(@Auth SingularityUser user, @PathParam("taskId") String taskId, final SingularityShellCommand shellCommand) { SingularityTaskId taskIdObj = getTaskIdFromStr(taskId); authorizationHelper.checkForAuthorizationByTaskId(taskId, user, SingularityAuthorizationScope.WRITE); @@ -489,13 +489,13 @@ public SingularityTaskShellCommandRequest runShellCommand(@PathParam("taskId") S throw WebExceptions.badRequest("%s is not an active task, can't run %s on it", taskId, shellCommand.getName()); } - return startShellCommand(taskIdObj, shellCommand); + return startShellCommand(taskIdObj, shellCommand, user); } - private SingularityTaskShellCommandRequest startShellCommand(SingularityTaskId taskId, final SingularityShellCommand shellCommand) { + private SingularityTaskShellCommandRequest startShellCommand(SingularityTaskId taskId, final SingularityShellCommand shellCommand, SingularityUser user) { validator.checkValidShellCommand(shellCommand); - SingularityTaskShellCommandRequest shellRequest = new SingularityTaskShellCommandRequest(taskId, JavaUtils.getUserEmail(user), System.currentTimeMillis(), shellCommand); + SingularityTaskShellCommandRequest shellRequest = new SingularityTaskShellCommandRequest(taskId, user.getEmail(), System.currentTimeMillis(), shellCommand); taskManager.saveTaskShellCommandRequestToQueue(shellRequest); return shellRequest; } @@ -503,7 +503,7 @@ private SingularityTaskShellCommandRequest startShellCommand(SingularityTaskId t @GET @Path("/task/{taskId}/command") @ApiOperation(value="Retrieve a list of shell commands that have run for a task") - public List getShellCommandHisotry(@PathParam("taskId") String taskId) { + public List getShellCommandHisotry(@Auth SingularityUser user, @PathParam("taskId") String taskId) { authorizationHelper.checkForAuthorizationByTaskId(taskId, user, SingularityAuthorizationScope.READ); SingularityTaskId taskIdObj = getTaskIdFromStr(taskId); @@ -513,7 +513,7 @@ public List getShellCommandHisotry(@PathPara @GET @Path("/task/{taskId}/command/{commandName}/{commandTimestamp}") @ApiOperation(value="Retrieve a list of shell commands updates for a particular shell command on a task") - public List getShellCommandHisotryUpdates(@PathParam("taskId") String taskId, @PathParam("commandName") String commandName, @PathParam("commandTimestamp") Long commandTimestamp) { + public List getShellCommandHisotryUpdates(@Auth SingularityUser user, @PathParam("taskId") String taskId, @PathParam("commandName") String commandName, @PathParam("commandTimestamp") Long commandTimestamp) { authorizationHelper.checkForAuthorizationByTaskId(taskId, user, SingularityAuthorizationScope.READ); SingularityTaskId taskIdObj = getTaskIdFromStr(taskId); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/TaskTrackerResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/TaskTrackerResource.java index 147affa3b3..790dbbdf40 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/TaskTrackerResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/TaskTrackerResource.java @@ -10,10 +10,13 @@ import com.google.common.base.Optional; import com.google.inject.Inject; +import com.hubspot.singularity.SingularityAuthorizationScope; import com.hubspot.singularity.SingularityPendingTask; import com.hubspot.singularity.SingularityTaskHistory; import com.hubspot.singularity.SingularityTaskId; import com.hubspot.singularity.SingularityTaskState; +import com.hubspot.singularity.SingularityUser; +import com.hubspot.singularity.auth.SingularityAuthorizationHelper; import com.hubspot.singularity.config.ApiPaths; import com.hubspot.singularity.data.TaskManager; import com.hubspot.singularity.data.history.HistoryManager; @@ -22,17 +25,21 @@ import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.TASK_TRACKER_RESOURCE_PATH) @Produces({MediaType.APPLICATION_JSON}) @Api(description="Find a task by taskId or runId", value=ApiPaths.TASK_TRACKER_RESOURCE_PATH) public class TaskTrackerResource { private final TaskManager taskManager; private final HistoryManager historyManager; + private final SingularityAuthorizationHelper authorizationHelper; @Inject - public TaskTrackerResource(TaskManager taskManager, HistoryManager historyManager) { + public TaskTrackerResource(TaskManager taskManager, HistoryManager historyManager, SingularityAuthorizationHelper authorizationHelper) { this.taskManager = taskManager; this.historyManager = historyManager; + this.authorizationHelper = authorizationHelper; } @GET @@ -41,7 +48,8 @@ public TaskTrackerResource(TaskManager taskManager, HistoryManager historyManage @ApiResponses({ @ApiResponse(code=404, message="Task with this id does not exist") }) - public Optional getTaskState(@PathParam("taskId") String taskId) { + public Optional getTaskState(@Auth SingularityUser user, @PathParam("taskId") String taskId) { + authorizationHelper.checkForAuthorizationByTaskId(taskId, user, SingularityAuthorizationScope.READ); return getTaskStateFromId(SingularityTaskId.valueOf(taskId)); } @@ -51,7 +59,9 @@ public Optional getTaskState(@PathParam("taskId") String t @ApiResponses({ @ApiResponse(code=404, message="Task with this runId does not exist") }) - public Optional getTaskStateByRunId(@PathParam("requestId") String requestId, @PathParam("runId") String runId) { + public Optional getTaskStateByRunId(@Auth SingularityUser user, @PathParam("requestId") String requestId, @PathParam("runId") String runId) { + authorizationHelper.checkForAuthorizationByRequestId(requestId, user, SingularityAuthorizationScope.READ); + // Check if it's active or inactive Optional maybeTaskId = taskManager.getTaskByRunId(requestId, runId); if (maybeTaskId.isPresent()) { diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/UsageResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/UsageResource.java index 1b2222da90..a3a5c0753d 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/UsageResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/UsageResource.java @@ -11,6 +11,7 @@ import com.google.common.base.Optional; import com.google.inject.Inject; +import com.hubspot.singularity.SingularityAuthorizationScope; import com.hubspot.singularity.SingularityClusterUtilization; import com.hubspot.singularity.SingularityService; import com.hubspot.singularity.SingularitySlave; @@ -20,13 +21,17 @@ import com.hubspot.singularity.SingularityTaskCurrentUsageWithId; import com.hubspot.singularity.SingularityTaskId; import com.hubspot.singularity.SingularityTaskUsage; +import com.hubspot.singularity.SingularityUser; import com.hubspot.singularity.WebExceptions; +import com.hubspot.singularity.auth.SingularityAuthorizationHelper; import com.hubspot.singularity.config.ApiPaths; import com.hubspot.singularity.data.SlaveManager; import com.hubspot.singularity.data.TaskManager; import com.hubspot.singularity.data.UsageManager; import com.wordnik.swagger.annotations.Api; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.USAGE_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api(description="Provides usage data about slaves and tasks", value=ApiPaths.USAGE_RESOURCE_PATH) @@ -37,23 +42,27 @@ public class UsageResource { private final UsageManager usageManager; private final TaskManager taskManager; private final SlaveManager slaveManager; + private final SingularityAuthorizationHelper authorizationHelper; @Inject - public UsageResource(UsageManager usageManager, TaskManager taskManager, SlaveManager slaveManager) { + public UsageResource(UsageManager usageManager, TaskManager taskManager, SlaveManager slaveManager, SingularityAuthorizationHelper authorizationHelper) { this.usageManager = usageManager; this.taskManager = taskManager; this.slaveManager = slaveManager; + this.authorizationHelper = authorizationHelper; } @GET @Path("/slaves") - public List getSlavesWithUsage() { + public List getSlavesWithUsage(@Auth SingularityUser user) { + authorizationHelper.checkAdminAuthorization(user); return usageManager.getAllCurrentSlaveUsage(); } @GET @Path("/slaves/{slaveId}/tasks/current") - public List getSlaveCurrentTaskUsage(@PathParam("slaveId") String slaveId) { + public List getSlaveCurrentTaskUsage(@Auth SingularityUser user, @PathParam("slaveId") String slaveId) { + authorizationHelper.checkAdminAuthorization(user); Optional slave = slaveManager.getObject(slaveId); WebExceptions.checkNotFound(slave.isPresent(), "No slave found with id %s", slaveId); @@ -70,19 +79,22 @@ public List getSlaveCurrentTaskUsage(@PathPar @GET @Path("/slaves/{slaveId}/history") - public List getSlaveUsageHistory(@PathParam("slaveId") String slaveId) { + public List getSlaveUsageHistory(@Auth SingularityUser user, @PathParam("slaveId") String slaveId) { + authorizationHelper.checkAdminAuthorization(user); return usageManager.getSlaveUsage(slaveId); } @GET @Path("/tasks/{taskId}/history") - public List getTaskUsageHistory(@PathParam("taskId") String taskId) { + public List getTaskUsageHistory(@Auth SingularityUser user, @PathParam("taskId") String taskId) { + authorizationHelper.checkForAuthorizationByTaskId(taskId, user, SingularityAuthorizationScope.READ); return usageManager.getTaskUsage(taskId); } @GET @Path("/cluster/utilization") - public SingularityClusterUtilization getClusterUtilization() { + public SingularityClusterUtilization getClusterUtilization(@Auth SingularityUser user) { + //authorizationHelper.checkAdminAuthorization(user); Needed for ui pages outside single request WebExceptions.checkNotFound(usageManager.getClusterUtilization().isPresent(), "No cluster utilization has been saved yet"); return usageManager.getClusterUtilization().get(); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/UserResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/UserResource.java index 13f5a3b41a..f304ba9274 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/UserResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/UserResource.java @@ -7,52 +7,48 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; -import com.google.common.base.Optional; import com.google.inject.Inject; import com.hubspot.singularity.SingularityUser; import com.hubspot.singularity.SingularityUserSettings; -import com.hubspot.singularity.auth.SingularityAuthorizationHelper; import com.hubspot.singularity.config.ApiPaths; import com.hubspot.singularity.data.UserManager; import com.wordnik.swagger.annotations.Api; import com.wordnik.swagger.annotations.ApiParam; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.USER_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api(description = "Retrieve settings for a particular user", value = ApiPaths.USER_RESOURCE_PATH) public class UserResource { private final UserManager userManager; - private final Optional user; - private final SingularityAuthorizationHelper authorizationHelper; @Inject - public UserResource(UserManager userManager, Optional user, SingularityAuthorizationHelper authorizationHelper) { + public UserResource(UserManager userManager) { this.userManager = userManager; - this.user = user; - this.authorizationHelper = authorizationHelper; } @GET @Path("/settings") - public SingularityUserSettings getUserSettings() { - return userManager.getUserSettings(authorizationHelper.getAuthUserId(user)).or(SingularityUserSettings.empty()); + public SingularityUserSettings getUserSettings(@Auth SingularityUser user) { + return userManager.getUserSettings(user.getId()).or(SingularityUserSettings.empty()); } @POST @Path("/settings") - public void setUserSettings(@ApiParam("Update all settings for a user") SingularityUserSettings settings) { - userManager.updateUserSettings(authorizationHelper.getAuthUserId(user), settings); + public void setUserSettings(@Auth SingularityUser user, @ApiParam("Update all settings for a user") SingularityUserSettings settings) { + userManager.updateUserSettings(user.getId(), settings); } @POST @Path("/settings/starred-requests") - public void addStarredRequests(@ApiParam("Add starred requests for a user") SingularityUserSettings settings) { - userManager.addStarredRequestIds(authorizationHelper.getAuthUserId(user), settings.getStarredRequestIds()); + public void addStarredRequests(@Auth SingularityUser user, @ApiParam("Add starred requests for a user") SingularityUserSettings settings) { + userManager.addStarredRequestIds(user.getId(), settings.getStarredRequestIds()); } @DELETE @Path("/settings/starred-requests") - public void deleteStarredRequests(@ApiParam("Remove starred requests for a user") SingularityUserSettings settings) { - userManager.deleteStarredRequestIds(authorizationHelper.getAuthUserId(user), settings.getStarredRequestIds()); + public void deleteStarredRequests(@Auth SingularityUser user, @ApiParam("Remove starred requests for a user") SingularityUserSettings settings) { + userManager.deleteStarredRequestIds(user.getId(), settings.getStarredRequestIds()); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/WebhookResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/WebhookResource.java index 3a2b16bd00..1c4fe2cbf5 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/WebhookResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/WebhookResource.java @@ -11,7 +11,6 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; -import com.google.common.base.Optional; import com.google.inject.Inject; import com.hubspot.mesos.JavaUtils; import com.hubspot.singularity.SingularityAction; @@ -30,26 +29,26 @@ import com.wordnik.swagger.annotations.Api; import com.wordnik.swagger.annotations.ApiOperation; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.WEBHOOK_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api(description="Manages Singularity webhooks.", value=ApiPaths.WEBHOOK_RESOURCE_PATH) public class WebhookResource { private final WebhookManager webhookManager; - private final Optional user; private final SingularityAuthorizationHelper authorizationHelper; private final SingularityValidator validator; @Inject - public WebhookResource(WebhookManager webhookManager, SingularityAuthorizationHelper authorizationHelper, Optional user, SingularityValidator validator) { + public WebhookResource(WebhookManager webhookManager, SingularityAuthorizationHelper authorizationHelper, SingularityValidator validator) { this.webhookManager = webhookManager; this.authorizationHelper = authorizationHelper; - this.user = user; this.validator = validator; } @GET @ApiOperation("Retrieve a list of active webhooks.") - public List getActiveWebhooks() { + public List getActiveWebhooks(@Auth SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); return webhookManager.getActiveWebhooks(); } @@ -57,14 +56,14 @@ public List getActiveWebhooks() { @GET @Path("/summary") @ApiOperation("Retrieve a summary of each active webhook") - public List getWebhooksWithQueueSize() { + public List getWebhooksWithQueueSize(@Auth SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); return webhookManager.getWebhooksWithQueueSize(); } @POST @ApiOperation("Add a new webhook.") - public SingularityCreateResult addWebhook(SingularityWebhook webhook) { + public SingularityCreateResult addWebhook(@Auth SingularityUser user, SingularityWebhook webhook) { authorizationHelper.checkAdminAuthorization(user); validator.checkActionEnabled(SingularityAction.ADD_WEBHOOK); validator.checkSingularityWebhook(webhook); @@ -75,7 +74,7 @@ public SingularityCreateResult addWebhook(SingularityWebhook webhook) { @Deprecated @Path("/{webhookId}") @ApiOperation("Delete a specific webhook.") - public SingularityDeleteResult deleteWebhookDeprecated(@PathParam("webhookId") String webhookId) { + public SingularityDeleteResult deleteWebhookDeprecated(@Auth SingularityUser user, @PathParam("webhookId") String webhookId) { authorizationHelper.checkAdminAuthorization(user); validator.checkActionEnabled(SingularityAction.REMOVE_WEBHOOK); return webhookManager.deleteWebhook(JavaUtils.urlEncode(webhookId)); @@ -85,7 +84,7 @@ public SingularityDeleteResult deleteWebhookDeprecated(@PathParam("webhookId") S @Deprecated @Path("/deploy/{webhookId}") @ApiOperation("Retrieve a list of queued deploy updates for a specific webhook.") - public List getQueuedDeployUpdatesDeprecated(@PathParam("webhookId") String webhookId) { + public List getQueuedDeployUpdatesDeprecated(@Auth SingularityUser user, @PathParam("webhookId") String webhookId) { authorizationHelper.checkAdminAuthorization(user); return webhookManager.getQueuedDeployUpdatesForHook(JavaUtils.urlEncode(webhookId)); } @@ -94,7 +93,7 @@ public List getQueuedDeployUpdatesDeprecated(@PathParam @Deprecated @Path("/request/{webhookId}") @ApiOperation("Retrieve a list of queued request updates for a specific webhook.") - public List getQueuedRequestUpdatesDeprecated(@PathParam("webhookId") String webhookId) { + public List getQueuedRequestUpdatesDeprecated(@Auth SingularityUser user, @PathParam("webhookId") String webhookId) { authorizationHelper.checkAdminAuthorization(user); return webhookManager.getQueuedRequestHistoryForHook(JavaUtils.urlEncode(webhookId)); } @@ -103,14 +102,14 @@ public List getQueuedRequestUpdatesDeprecated(@PathPa @Deprecated @Path("/task/{webhookId}") @ApiOperation("Retrieve a list of queued task updates for a specific webhook.") - public List getQueuedTaskUpdatesDeprecated(@PathParam("webhookId") String webhookId) { + public List getQueuedTaskUpdatesDeprecated(@Auth SingularityUser user, @PathParam("webhookId") String webhookId) { authorizationHelper.checkAdminAuthorization(user); return webhookManager.getQueuedTaskUpdatesForHook(JavaUtils.urlEncode(webhookId)); } @DELETE @ApiOperation("Delete a specific webhook.") - public SingularityDeleteResult deleteWebhook(@QueryParam("webhookId") String webhookId) { + public SingularityDeleteResult deleteWebhook(@Auth SingularityUser user, @QueryParam("webhookId") String webhookId) { authorizationHelper.checkAdminAuthorization(user); validator.checkActionEnabled(SingularityAction.REMOVE_WEBHOOK); return webhookManager.deleteWebhook(JavaUtils.urlEncode(webhookId)); @@ -119,7 +118,7 @@ public SingularityDeleteResult deleteWebhook(@QueryParam("webhookId") String web @GET @Path("/deploy") @ApiOperation("Retrieve a list of queued deploy updates for a specific webhook.") - public List getQueuedDeployUpdates(@QueryParam("webhookId") String webhookId) { + public List getQueuedDeployUpdates(@Auth SingularityUser user, @QueryParam("webhookId") String webhookId) { authorizationHelper.checkAdminAuthorization(user); return webhookManager.getQueuedDeployUpdatesForHook(JavaUtils.urlEncode(webhookId)); } @@ -127,7 +126,7 @@ public List getQueuedDeployUpdates(@QueryParam("webhook @GET @Path("/request") @ApiOperation("Retrieve a list of queued request updates for a specific webhook.") - public List getQueuedRequestUpdates(@QueryParam("webhookId") String webhookId) { + public List getQueuedRequestUpdates(@Auth SingularityUser user, @QueryParam("webhookId") String webhookId) { authorizationHelper.checkAdminAuthorization(user); return webhookManager.getQueuedRequestHistoryForHook(JavaUtils.urlEncode(webhookId)); } @@ -135,7 +134,7 @@ public List getQueuedRequestUpdates(@QueryParam("webh @GET @Path("/task") @ApiOperation("Retrieve a list of queued task updates for a specific webhook.") - public List getQueuedTaskUpdates(@QueryParam("webhookId") String webhookId) { + public List getQueuedTaskUpdates(@Auth SingularityUser user, @QueryParam("webhookId") String webhookId) { authorizationHelper.checkAdminAuthorization(user); return webhookManager.getQueuedTaskUpdatesForHook(JavaUtils.urlEncode(webhookId)); } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityHealthchecker.java b/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityHealthchecker.java index c088dde4b3..2f2853fa7c 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityHealthchecker.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityHealthchecker.java @@ -20,6 +20,8 @@ import com.google.inject.Inject; import com.google.inject.name.Named; import com.hubspot.deploy.HealthcheckOptions; +import com.hubspot.singularity.helpers.MesosProtosUtils; +import com.hubspot.singularity.helpers.MesosUtils; import com.hubspot.singularity.ExtendedTaskState; import com.hubspot.singularity.HealthcheckProtocol; import com.hubspot.singularity.SingularityAbort; @@ -57,11 +59,13 @@ public class SingularityHealthchecker { private final SingularityExceptionNotifier exceptionNotifier; private final DisasterManager disasterManager; + private final MesosProtosUtils mesosProtosUtils; @Inject public SingularityHealthchecker(@Named(SingularityMainModule.HEALTHCHECK_THREADPOOL_NAME) ScheduledExecutorService executorService, AsyncHttpClient http, SingularityConfiguration configuration, SingularityNewTaskChecker newTaskChecker, - TaskManager taskManager, SingularityAbort abort, SingularityExceptionNotifier exceptionNotifier, DisasterManager disasterManager) { + TaskManager taskManager, SingularityAbort abort, SingularityExceptionNotifier exceptionNotifier, DisasterManager disasterManager, + MesosProtosUtils mesosProtosUtils) { this.http = http; this.configuration = configuration; this.newTaskChecker = newTaskChecker; @@ -73,6 +77,7 @@ public SingularityHealthchecker(@Named(SingularityMainModule.HEALTHCHECK_THREADP this.executorService = executorService; this.disasterManager = disasterManager; + this.mesosProtosUtils = mesosProtosUtils; } public void enqueueHealthcheck(SingularityTask task, boolean ignoreExisting, boolean inStartup, boolean isFirstCheck) { @@ -213,7 +218,7 @@ private Optional getHealthcheckUri(SingularityTask task) { final String hostname = task.getHostname(); - Optional healthcheckPort = options.getPortNumber().or(task.getPortByIndex(options.getPortIndex().or(0))); + Optional healthcheckPort = options.getPortNumber().or(MesosUtils.getPortByIndex(mesosProtosUtils.toResourceList(task.getMesosTask().getResources()), options.getPortIndex().or(0))); if (!healthcheckPort.isPresent() || healthcheckPort.get() < 1L) { LOG.warn("Couldn't find a port for health check for task {}", task); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityLeaderCache.java b/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityLeaderCache.java index 7cd17ef1aa..c186596529 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityLeaderCache.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityLeaderCache.java @@ -120,6 +120,12 @@ public List getPendingTaskIds() { return new ArrayList<>(pendingTaskIdToPendingTask.keySet()); } + public List getPendingTaskIdsForRequest(String requestId) { + return pendingTaskIdToPendingTask.keySet().stream() + .filter(t -> t.getRequestId().equals(requestId)) + .collect(Collectors.toList()); + } + public void deletePendingTask(SingularityPendingTaskId pendingTaskId) { if (!active) { LOG.warn("deletePendingTask {}, but not active", pendingTaskId); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularitySlaveReconciliationPoller.java b/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularitySlaveReconciliationPoller.java index 0172af3809..e51ec37347 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularitySlaveReconciliationPoller.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularitySlaveReconciliationPoller.java @@ -12,7 +12,7 @@ import com.google.common.base.Optional; import com.google.inject.Inject; import com.hubspot.mesos.JavaUtils; -import com.hubspot.mesos.MesosUtils; +import com.hubspot.singularity.helpers.MesosUtils; import com.hubspot.mesos.client.MesosClient; import com.hubspot.mesos.json.MesosMasterStateObject; import com.hubspot.singularity.MachineState; diff --git a/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityTaskReconciliation.java b/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityTaskReconciliation.java index ed8257b696..7edff2346d 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityTaskReconciliation.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityTaskReconciliation.java @@ -27,7 +27,8 @@ import com.google.inject.Inject; import com.google.inject.name.Named; import com.hubspot.mesos.JavaUtils; -import com.hubspot.mesos.json.SingularityMesosTaskStatusObject; +import com.hubspot.singularity.helpers.MesosProtosUtils; +import com.hubspot.mesos.protos.MesosTaskStatusObject; import com.hubspot.singularity.SingularityAbort; import com.hubspot.singularity.SingularityAbort.AbortReason; import com.hubspot.singularity.SingularityMainModule; @@ -52,6 +53,7 @@ public class SingularityTaskReconciliation { private final AtomicBoolean isRunningReconciliation; private final SingularityConfiguration configuration; private final SingularityAbort abort; + private final MesosProtosUtils mesosProtosUtils; private final SingularityExceptionNotifier exceptionNotifier; private final SingularityMesosSchedulerClient schedulerClient; private final StateManager stateManager; @@ -64,6 +66,7 @@ public SingularityTaskReconciliation(SingularityManagedScheduledExecutorServiceF SingularityConfiguration configuration, @Named(SingularityMainModule.SERVER_ID_PROPERTY) String serverId, SingularityAbort abort, + MesosProtosUtils mesosProtosUtils, SingularityMesosSchedulerClient schedulerClient) { this.taskManager = taskManager; this.stateManager = stateManager; @@ -72,6 +75,7 @@ public SingularityTaskReconciliation(SingularityManagedScheduledExecutorServiceF this.exceptionNotifier = exceptionNotifier; this.configuration = configuration; this.abort = abort; + this.mesosProtosUtils = mesosProtosUtils; this.schedulerClient = schedulerClient; this.isRunningReconciliation = new AtomicBoolean(false); @@ -132,7 +136,7 @@ public void run() { private void checkReconciliation(final long reconciliationStart, final Collection remainingTaskIds, final int numTimes, final Histogram histogram) { final List taskStatusHolders = taskManager.getLastActiveTaskStatusesFor(remainingTaskIds); - final List taskStatuses = Lists.newArrayListWithCapacity(taskStatusHolders.size()); + final List taskStatuses = Lists.newArrayListWithCapacity(taskStatusHolders.size()); for (SingularityTaskStatusHolder taskStatusHolder : taskStatusHolders) { if (taskStatusHolder.getServerId().equals(serverId) && taskStatusHolder.getServerTimestamp() > reconciliationStart) { @@ -153,7 +157,7 @@ private void checkReconciliation(final long reconciliationStart, final Collectio } LOG.info("Task {} didn't have a TaskStatus yet, submitting fake status", taskStatusHolder.getTaskId()); - taskStatuses.add(SingularityMesosTaskStatusObject.fromProtos(fakeTaskStatusBuilder.build())); + taskStatuses.add(mesosProtosUtils.taskStatusFromProtos(fakeTaskStatusBuilder.build())); } } @@ -170,7 +174,7 @@ private void checkReconciliation(final long reconciliationStart, final Collectio LOG.info("Requesting reconciliation of {} taskStatuses, task reconciliation has been running for {}", taskStatuses.size(), JavaUtils.duration(reconciliationStart)); - schedulerClient.reconcile(taskStatuses.stream().map((t) -> Task.newBuilder().setTaskId(t.getTaskId()).setAgentId(t.getAgentId()).build()).collect(Collectors.toList())); + schedulerClient.reconcile(taskStatuses.stream().map((t) -> Task.newBuilder().setTaskId(MesosProtosUtils.toTaskId(t.getTaskId())).setAgentId(MesosProtosUtils.toAgentId(t.getAgentId())).build()).collect(Collectors.toList())); scheduleReconciliationCheck(reconciliationStart, remainingTaskIds, numTimes, histogram); } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityTaskShellCommandDispatchPoller.java b/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityTaskShellCommandDispatchPoller.java index 810eef37dc..21a1b42a1f 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityTaskShellCommandDispatchPoller.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityTaskShellCommandDispatchPoller.java @@ -18,6 +18,7 @@ import com.hubspot.singularity.config.SingularityConfiguration; import com.hubspot.singularity.data.TaskManager; import com.hubspot.singularity.data.transcoders.Transcoder; +import com.hubspot.singularity.helpers.MesosProtosUtils; import com.hubspot.singularity.mesos.SingularityMesosSchedulerClient; @Singleton @@ -62,8 +63,8 @@ public void runActionOnPoll() { continue; } - final ExecutorID executorId = task.get().getMesosTask().getExecutor().getExecutorId(); - final AgentID slaveId = task.get().getMesosTask().getAgentId(); + final ExecutorID executorId = MesosProtosUtils.toExecutorId(task.get().getMesosTask().getExecutor().getExecutorId()); + final AgentID slaveId = MesosProtosUtils.toAgentId(task.get().getMesosTask().getAgentId()); final byte[] bytes = transcoder.toBytes(shellRequest); schedulerClient.frameworkMessage(executorId, slaveId, bytes); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityUsagePoller.java b/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityUsagePoller.java index f62423dba8..d60723f36f 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityUsagePoller.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityUsagePoller.java @@ -86,6 +86,7 @@ public void runActionOnPoll() { Map longRunningTasksUsage = new HashMap<>(); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 0); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, 0); Optional memoryMbTotal = Optional.absent(); Optional cpusTotal = Optional.absent(); @@ -140,7 +141,7 @@ public void runActionOnPoll() { double taskCpusUsed = ((latestUsage.getCpuSeconds() - lastUsage.getCpuSeconds()) / (latestUsage.getTimestamp() - lastUsage.getTimestamp())); if (isLongRunning(task) || isConsideredLongRunning(task)) { - updateLongRunningTasksUsage(longRunningTasksUsage, latestUsage.getMemoryTotalBytes(), taskCpusUsed); + updateLongRunningTasksUsage(longRunningTasksUsage, latestUsage.getMemoryTotalBytes(), taskCpusUsed, latestUsage.getDiskTotalBytes()); } SingularityTaskCurrentUsage currentUsage = new SingularityTaskCurrentUsage(latestUsage.getMemoryTotalBytes(), now, taskCpusUsed); @@ -210,9 +211,10 @@ private boolean isConsideredLongRunning(SingularityTaskId task) { deployStatistics.get().getAverageRuntimeMillis().get() >= configuration.getConsiderNonLongRunningTaskLongRunningAfterRunningForSeconds(); } - private void updateLongRunningTasksUsage(Map longRunningTasksUsage, long memBytesUsed, double cpuUsed) { + private void updateLongRunningTasksUsage(Map longRunningTasksUsage, long memBytesUsed, double cpuUsed, long diskBytesUsed) { longRunningTasksUsage.compute(ResourceUsageType.MEMORY_BYTES_USED, (k, v) -> (v == null) ? memBytesUsed : v.longValue() + memBytesUsed); longRunningTasksUsage.compute(ResourceUsageType.CPU_USED, (k, v) -> (v == null) ? cpuUsed : v.doubleValue() + cpuUsed); + longRunningTasksUsage.compute(ResourceUsageType.DISK_BYTES_USED, (k, v) -> (v == null) ? diskBytesUsed : v.doubleValue() + diskBytesUsed); } private void updateRequestUtilization(Map utilizationPerRequestId, diff --git a/SingularityService/src/main/java/com/hubspot/singularity/sentry/SingularityExceptionNotifier.java b/SingularityService/src/main/java/com/hubspot/singularity/sentry/SingularityExceptionNotifier.java index 45ab11ffa1..36a996c837 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/sentry/SingularityExceptionNotifier.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/sentry/SingularityExceptionNotifier.java @@ -12,7 +12,6 @@ import com.google.inject.Provider; import com.google.inject.name.Named; import com.hubspot.singularity.SingularityMainModule; -import com.hubspot.singularity.SingularityUser; import com.hubspot.singularity.config.SentryConfiguration; import net.kencochrane.raven.Raven; @@ -21,20 +20,17 @@ import net.kencochrane.raven.event.EventBuilder; import net.kencochrane.raven.event.interfaces.ExceptionInterface; import net.kencochrane.raven.event.interfaces.HttpInterface; -import net.kencochrane.raven.event.interfaces.UserInterface; @Singleton public class SingularityExceptionNotifier { private final Optional raven; private final Optional sentryConfiguration; private final Provider> requestProvider; - private final Provider> userProvider; @Inject - public SingularityExceptionNotifier(Optional sentryConfiguration, @Named(SingularityMainModule.CURRENT_HTTP_REQUEST) Provider> requestProvider, Provider> userProvider) { + public SingularityExceptionNotifier(Optional sentryConfiguration, @Named(SingularityMainModule.CURRENT_HTTP_REQUEST) Provider> requestProvider) { this.sentryConfiguration = sentryConfiguration; this.requestProvider = requestProvider; - this.userProvider = userProvider; if (sentryConfiguration.isPresent()) { this.raven = Optional.of(RavenFactory.ravenInstance(sentryConfiguration.get().getDsn())); } else { @@ -88,12 +84,6 @@ public void notify(String message, Throwable t, Map extraData) { eventBuilder.withSentryInterface(new HttpInterface(maybeRequest.get())); } - final Optional maybeUser = userProvider.get(); - - if (maybeUser.isPresent()) { - eventBuilder.withSentryInterface(new UserInterface(maybeUser.get().getId(), maybeUser.get().getId(), "", maybeUser.get().getEmail().or(""))); - } - if (extraData != null && !extraData.isEmpty()) { for (Map.Entry entry : extraData.entrySet()) { eventBuilder.withExtra(entry.getKey(), entry.getValue()); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/smtp/MailTemplateHelpers.java b/SingularityService/src/main/java/com/hubspot/singularity/smtp/MailTemplateHelpers.java index 795a6d0eae..57e2456399 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/smtp/MailTemplateHelpers.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/smtp/MailTemplateHelpers.java @@ -18,7 +18,7 @@ import com.google.common.collect.Lists; import com.google.inject.Inject; import com.google.inject.Singleton; -import com.hubspot.mesos.MesosUtils; +import com.hubspot.singularity.helpers.MesosUtils; import com.hubspot.mesos.json.MesosFileChunkObject; import com.hubspot.singularity.ExtendedTaskState; import com.hubspot.singularity.SingularityDisasterDataPoint; diff --git a/SingularityService/src/test/java/com/hubspot/singularity/SingularityAuthorizationHelperTest.java b/SingularityService/src/test/java/com/hubspot/singularity/SingularityAuthorizationHelperTest.java index 6a502991b2..369e08f38c 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/SingularityAuthorizationHelperTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/SingularityAuthorizationHelperTest.java @@ -58,11 +58,11 @@ public static SingularityConfiguration buildAuthEnabledConfig(Set requir public static final SingularityRequest REQUEST_WITH_GROUP_B = new SingularityRequestBuilder("test_b", RequestType.SERVICE).setGroup(Optional.of("b")).build(); - public static final Optional NOT_LOGGED_IN = Optional.absent(); - public static final Optional USER_GROUP_A = Optional.of(new SingularityUser("test1", Optional.of("test user1"), Optional.of("test1@test.com"), ImmutableSet.of("a"))); - public static final Optional USER_GROUP_AB = Optional.of(new SingularityUser("test2", Optional.of("test user2"), Optional.of("test2@test.com"), ImmutableSet.of("a", "b"))); - public static final Optional USER_GROUP_B = Optional.of(new SingularityUser("test3", Optional.of("test user3"), Optional.of("test3@test.com"), ImmutableSet.of("b"))); - public static final Optional USER_GROUP_ADMIN = Optional.of(new SingularityUser("admin", Optional.of("admin user"), Optional.of("admin@test.com"), ImmutableSet.of("admin"))); + public static final SingularityUser NOT_LOGGED_IN = SingularityUser.DEFAULT_USER; + public static final SingularityUser USER_GROUP_A = new SingularityUser("test1", Optional.of("test user1"), Optional.of("test1@test.com"), ImmutableSet.of("a")); + public static final SingularityUser USER_GROUP_AB = new SingularityUser("test2", Optional.of("test user2"), Optional.of("test2@test.com"), ImmutableSet.of("a", "b")); + public static final SingularityUser USER_GROUP_B = new SingularityUser("test3", Optional.of("test user3"), Optional.of("test3@test.com"), ImmutableSet.of("b")); + public static final SingularityUser USER_GROUP_ADMIN = new SingularityUser("admin", Optional.of("admin user"), Optional.of("admin@test.com"), ImmutableSet.of("admin")); private SingularityAuthorizationHelper buildAuthorizationHelper(SingularityConfiguration configuration) { return new SingularityAuthorizationHelper(requestManager, configuration); diff --git a/SingularityService/src/test/java/com/hubspot/singularity/SingularityCuratorTestBase.java b/SingularityService/src/test/java/com/hubspot/singularity/SingularityCuratorTestBase.java index 7203f9acf4..ecb2fb958e 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/SingularityCuratorTestBase.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/SingularityCuratorTestBase.java @@ -8,6 +8,7 @@ import org.junit.rules.Timeout; import com.google.inject.Inject; +import com.hubspot.singularity.helpers.MesosProtosUtils; import com.hubspot.singularity.scheduler.SingularityTestModule; import com.squarespace.jersey2.guice.JerseyGuiceUtils; @@ -20,6 +21,8 @@ public class SingularityCuratorTestBase { protected CuratorFramework cf; @Inject protected TestingServer ts; + @Inject + protected MesosProtosUtils mesosProtosUtils; private SingularityTestModule singularityTestModule; diff --git a/SingularityService/src/test/java/com/hubspot/singularity/SingularityHistoryTest.java b/SingularityService/src/test/java/com/hubspot/singularity/SingularityHistoryTest.java index 60cf0956ed..95dd9cc683 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/SingularityHistoryTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/SingularityHistoryTest.java @@ -1,8 +1,7 @@ package com.hubspot.singularity; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.List; @@ -20,7 +19,6 @@ import com.google.common.base.Optional; import com.google.inject.Inject; import com.google.inject.Provider; -import com.hubspot.mesos.Resources; import com.hubspot.singularity.SingularityRequestHistory.RequestHistoryType; import com.hubspot.singularity.api.SingularityDeleteRequestRequest; import com.hubspot.singularity.api.SingularityRunNowRequest; @@ -199,8 +197,8 @@ public void testRunId() { String runId = "my-run-id"; - SingularityPendingRequestParent parent = requestResource.scheduleImmediately(requestId, - new SingularityRunNowRequest(Optional. absent(), Optional. absent(), Optional.of(runId), Optional.> absent(), Optional.absent(), Optional.absent())); + SingularityPendingRequestParent parent = requestResource.scheduleImmediately(singularityUser, requestId, + new SingularityRunNowRequest(Optional.absent(), Optional.absent(), Optional.of(runId), Optional.absent(), Optional.absent(), Optional.absent())); Assert.assertEquals(runId, parent.getPendingRequest().getRunId().get()); @@ -220,7 +218,7 @@ public void testRunId() { Assert.assertEquals(runId, historyManager.getTaskHistory(taskId.getId()).get().getTask().getTaskRequest().getPendingTask().getRunId().get()); Assert.assertEquals(runId, getTaskHistoryForRequest(requestId, 0, 10).get(0).getRunId().get()); - parent = requestResource.scheduleImmediately(requestId); + parent = requestResource.scheduleImmediately(singularityUser, requestId); Assert.assertTrue(parent.getPendingRequest().getRunId().isPresent()); } @@ -261,7 +259,7 @@ public void testPersisterRaceCondition() { initScheduledRequest(); initFirstDeploy(); - requestResource.scheduleImmediately(requestId); + requestResource.scheduleImmediately(singularityUser, requestId); resourceOffers(); @@ -490,8 +488,8 @@ public void testMessage() { msg = msg + i; } - requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(2), Optional. absent(), Optional. absent(), Optional. absent(), Optional.of(msg), Optional.absent(), Optional.absent())); - requestResource.deleteRequest(requestId, Optional.of(new SingularityDeleteRequestRequest(Optional.of("a msg"), Optional. absent(), Optional.absent()))); + requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(2), Optional.absent(), Optional.absent(), Optional.absent(), Optional.of(msg), Optional.absent(), Optional.absent()), singularityUser); + requestResource.deleteRequest(requestId, Optional.of(new SingularityDeleteRequestRequest(Optional.of("a msg"), Optional.absent(), Optional.absent())), singularityUser); cleaner.drainCleanupQueue(); diff --git a/SingularityService/src/test/java/com/hubspot/singularity/SingularityTestAuthenticator.java b/SingularityService/src/test/java/com/hubspot/singularity/SingularityTestAuthenticator.java index 07b1da5e9e..1816ddd5de 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/SingularityTestAuthenticator.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/SingularityTestAuthenticator.java @@ -1,11 +1,14 @@ package com.hubspot.singularity; -import com.google.common.base.Optional; +import java.util.Optional; + +import javax.ws.rs.container.ContainerRequestContext; + import com.google.inject.Inject; import com.hubspot.singularity.auth.authenticator.SingularityAuthenticator; public class SingularityTestAuthenticator implements SingularityAuthenticator { - private Optional user = Optional.absent(); + private Optional user = Optional.empty(); @Inject public SingularityTestAuthenticator() { @@ -13,7 +16,7 @@ public SingularityTestAuthenticator() { } @Override - public Optional get() { + public Optional getUser(ContainerRequestContext context) { return user; } diff --git a/SingularityService/src/test/java/com/hubspot/singularity/data/StateManagerTest.java b/SingularityService/src/test/java/com/hubspot/singularity/data/StateManagerTest.java index 770cdd6106..25f2aa9b9e 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/data/StateManagerTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/data/StateManagerTest.java @@ -28,7 +28,7 @@ public void itDoesntCountCleaningTasks() { initRequest(); initFirstDeploy(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); saveAndSchedule(request.toBuilder().setInstances(Optional.of(3))); resourceOffers(); diff --git a/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityMesosOfferSchedulerTest.java b/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityMesosOfferSchedulerTest.java index 02d94c72dd..cd1088a9b5 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityMesosOfferSchedulerTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityMesosOfferSchedulerTest.java @@ -52,10 +52,12 @@ public SingularityMesosOfferSchedulerTest() { @Before public void setup() { - configuration.setLongRunningUsedCpuWeightForOffer(0.40); - configuration.setLongRunningUsedMemWeightForOffer(0.60); - configuration.setFreeCpuWeightForOffer(0.40); - configuration.setFreeMemWeightForOffer(0.60); + configuration.setLongRunningUsedCpuWeightForOffer(0.30); + configuration.setLongRunningUsedMemWeightForOffer(0.50); + configuration.setLongRunningUsedDiskWeightForOffer(0.20); + configuration.setFreeCpuWeightForOffer(0.30); + configuration.setFreeMemWeightForOffer(0.50); + configuration.setFreeDiskWeightForOffer(0.20); configuration.setDefaultOfferScoreForMissingUsage(0.10); configuration.setConsiderNonLongRunningTaskLongRunningAfterRunningForSeconds(TimeUnit.HOURS.toSeconds(6)); configuration.setMaxNonLongRunningUsedResourceWeight(0.50); @@ -82,7 +84,8 @@ public void itCorrectlyUsesDefaults() { setRequestType(RequestType.ON_DEMAND); longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 5); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, mbToBytes(5)); - assertValueIs(0.25, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(5, 10, 5, 10, longRunningTasksUsage)))); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, mbToBytes(5)); + assertValueIs(0.25, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(5, 10, 5, 10, 5, 10, longRunningTasksUsage)))); } @Test @@ -93,92 +96,85 @@ public void itCorrectlyScoresLongRunningTasks() { // new slave (no resources used) -> perfect score longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 0); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); - assertValueIs(1, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0,10, 0,10, longRunningTasksUsage)))); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, 0); + assertValueIs(1, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0,10, 0,10, 0, 10, longRunningTasksUsage)))); - // cpu used, no mem used + // cpu used, no mem used, no disk used longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 5); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); - assertValueIs(0.80, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0, 10, 5, 10, longRunningTasksUsage)))); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, 0); + assertValueIs(0.85, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0, 10, 5, 10, 0, 10, longRunningTasksUsage)))); longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 8); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); - assertValueIs(0.68, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0, 10, 8, 10, longRunningTasksUsage)))); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, 0); + assertValueIs(0.76, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0, 10, 8, 10, 0, 10, longRunningTasksUsage)))); - // no cpu used, mem used + // no cpu used, mem used, no disk used longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 0); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, mbToBytes(5)); - assertValueIs(0.70, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(5, 10, 0, 10, longRunningTasksUsage)))); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, 0); + assertValueIs(0.75, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(5, 10, 0, 10, 0, 10, longRunningTasksUsage)))); longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 0); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, mbToBytes(8)); - assertValueIs(0.52, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(8, 10, 0, 10, longRunningTasksUsage)))); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, 0); + assertValueIs(0.60, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(8, 10, 0, 10, 0, 10, longRunningTasksUsage)))); - // cpu used, mem used + // no cpu used, no mem used, disk used + longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 0); + longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, mbToBytes(5)); + assertValueIs(0.90, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0, 10, 0, 10, 5, 10, longRunningTasksUsage)))); + + longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 0); + longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, mbToBytes(8)); + assertValueIs(0.84, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0, 10, 0, 10, 8, 10, longRunningTasksUsage)))); + + // cpu used, mem used, no disk used longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 5); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, mbToBytes(5)); - assertValueIs(0.50, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(5, 10, 5, 10, longRunningTasksUsage)))); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, 0); + assertValueIs(0.60, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(5, 10, 5, 10, 0, 10, longRunningTasksUsage)))); longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 8); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, mbToBytes(8)); - assertValueIs(0.20, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(8, 10, 8, 10, longRunningTasksUsage)))); - } - - @Test - public void itCorrectlyScoresEmptySlaveNonLongRunningTasks() { - Map longRunningTasksUsage = new HashMap<>(); - setRequestType(RequestType.ON_DEMAND); - - // new slave (no resources used) -> near perfect score - setDeployStatistics(TimeUnit.MINUTES, 5); - longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 0); - longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); - assertValueIs(.993, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0, 10, 0, 10, longRunningTasksUsage)))); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, 0); + assertValueIs(0.36, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(8, 10, 8, 10, 0, 10, longRunningTasksUsage)))); - setDeployStatistics(TimeUnit.HOURS, 6); + // no cpu used, mem used, disk used longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 0); - longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); - assertValueIs(1, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0, 10, 0, 10, longRunningTasksUsage)))); + longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, mbToBytes(5)); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, mbToBytes(5)); + assertValueIs(0.65, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(5,10, 0,10, 5, 10, longRunningTasksUsage)))); - setDeployStatistics(TimeUnit.HOURS, 12); longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 0); - longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); - assertValueIs(1, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0, 10, 0, 10, longRunningTasksUsage)))); - } - - @Test - public void itCorrectlyScoresShortNonLongRunningTasks() { - Map longRunningTasksUsage = new HashMap<>(); - setRequestType(RequestType.ON_DEMAND); - - // short duration - setDeployStatistics(TimeUnit.HOURS, 1); + longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, mbToBytes(8)); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, mbToBytes(8)); + assertValueIs(0.44, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(8,10, 0,10, 8, 10, longRunningTasksUsage)))); - // 50% LR cpu -- 0% LR mem + // cpu used, no mem used, disk used longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 5); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); - assertValueIs(0.717, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0, 10, 5, 10, longRunningTasksUsage)))); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, mbToBytes(5)); + assertValueIs(0.75, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0,10, 5,10, 5, 10, longRunningTasksUsage)))); - // 20% NLR cpu -- 20% NLR mem - longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 0); + longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 8); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); - double nlrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(2, 10, 2, 10, longRunningTasksUsage))); - assertValueIs(0.733, nlrScore); - - // 20% LR cpu -- 20% LR mem - longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 2); - longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, mbToBytes(2)); - double lrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(2, 10, 2, 10, longRunningTasksUsage))); - assertValueIs(0.717, lrScore); - - Assert.assertTrue(nlrScore > lrScore); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, mbToBytes(8)); + assertValueIs(0.60, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0,10, 8,10, 8, 10, longRunningTasksUsage)))); - // 30% NLR cpu -- 30% NLR mem - longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 0); - longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); - nlrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(3, 10, 3, 10, longRunningTasksUsage))); - assertValueIs(0.642, nlrScore); + // cpu used, mem used, disk used + longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 5); + longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, mbToBytes(5)); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, mbToBytes(5)); + assertValueIs(0.5, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(5,10, 5,10, 5, 10, longRunningTasksUsage)))); - Assert.assertTrue(nlrScore < lrScore); + longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 8); + longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, mbToBytes(8)); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, mbToBytes(8)); + assertValueIs(0.2, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(8,10, 8,10, 8, 10, longRunningTasksUsage)))); } @Test @@ -189,30 +185,40 @@ public void itCorrectlyScoresMediumLongNonLongRunningTasks() { // medium duration setDeployStatistics(TimeUnit.HOURS, 3); - // 50% LR cpu -- 0% LR mem + // 50% LR cpu -- 0% LR mem -- 0% LR disk longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 5); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); - assertValueIs(0.55, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0, 10, 5, 10, longRunningTasksUsage)))); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, 0); + assertValueIs(0.6, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0, 10, 5, 10, 0, 10, longRunningTasksUsage)))); + + // 0% LR cpu -- 0% LR mem -- 50% LR disk + longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 0); + longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, mbToBytes(5)); + assertValueIs(0.65, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0, 10, 0, 10, 5, 10, longRunningTasksUsage)))); - // 20% NLR cpu -- 20% NLR mem + // 20% NLR cpu -- 20% NLR mem -- 0% LR disk longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 0); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); - double nlrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(2, 10, 2, 10, longRunningTasksUsage))); - assertValueIs(0.6, nlrScore); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, 0); + double nlrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(2, 10, 2, 10, 0, 10, longRunningTasksUsage))); + assertValueIs(0.63, nlrScore); - // 20% LR cpu -- 20% LR mem + // 20% LR cpu -- 20% LR mem -- 0% LR disk longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 2); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, mbToBytes(2)); - double lrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(2, 10, 2, 10, longRunningTasksUsage))); - assertValueIs(0.55, lrScore); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, 0); + double lrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(2, 10,2, 10, 0, 10, longRunningTasksUsage))); + assertValueIs(0.59, lrScore); Assert.assertTrue(nlrScore > lrScore); - // 30% NLR cpu -- 30% NLR mem + // 30% NLR cpu -- 30% NLR mem -- 0% LR disk longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 0); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); - nlrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(3, 10, 3, 10, longRunningTasksUsage))); - assertValueIs(0.525, nlrScore); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, 0); + nlrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(3, 10, 3, 10, 0, 10, longRunningTasksUsage))); + assertValueIs(0.57, nlrScore); Assert.assertTrue(lrScore > nlrScore); } @@ -225,60 +231,80 @@ public void itCorrectlyScoresLongNonLongRunningTasks() { // long duration setDeployStatistics(TimeUnit.HOURS, 6); - // 50% LR cpu -- 0% LR mem + // 50% LR cpu -- 0% LR mem -- 0% LR disk longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 5); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); - assertValueIs(0.8, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0, 10, 5, 10, longRunningTasksUsage)))); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, 0); + assertValueIs(0.85, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0, 10, 5, 10, 0, 10, longRunningTasksUsage)))); - // 20% NLR cpu -- 20% NLR mem + // 0% LR cpu -- 0% LR mem -- 50% LR disk longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 0); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); - double nlrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(2, 10, 2, 10, longRunningTasksUsage))); - assertValueIs(0.9, nlrScore); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, mbToBytes(5)); + assertValueIs(0.9, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0, 10, 0, 10, 5, 10, longRunningTasksUsage)))); - // 20% LR cpu -- 20% LR mem + // 20% NLR cpu -- 20% NLR mem -- 0% LR disk + longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 0); + longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, 0); + double nlrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(2, 10, 2, 10, 0, 10, longRunningTasksUsage))); + assertValueIs(0.92, nlrScore); + + // 20% LR cpu -- 20% LR mem -- 0% LR disk longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 2); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, mbToBytes(2)); - double lrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(2, 10, 2, 10, longRunningTasksUsage))); - assertValueIs(0.8, lrScore); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, 0); + double lrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(2, 10, 2, 10, 0, 10, longRunningTasksUsage))); + assertValueIs(0.84, lrScore); Assert.assertTrue(nlrScore > lrScore); - // 30% NLR cpu -- 30% NLR mem + // 30% NLR cpu -- 30% NLR mem -- 0% LR disk longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 0); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); - nlrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(3, 10, 3, 10, longRunningTasksUsage))); - assertValueIs(0.85, nlrScore); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, 0); + nlrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(3, 10, 3, 10, 0, 10, longRunningTasksUsage))); + assertValueIs(0.88, nlrScore); Assert.assertTrue(nlrScore > lrScore); // over max duration setDeployStatistics(TimeUnit.HOURS, 12); - // 50% LR cpu -- 0% LR mem + // 50% LR cpu -- 0% LR mem -- 0% LR disk longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 5); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); - assertValueIs(0.8, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0, 10, 5, 10, longRunningTasksUsage)))); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, 0); + assertValueIs(0.85, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0, 10, 5, 10, 0, 10, longRunningTasksUsage)))); + + // 0% LR cpu -- 0% LR mem -- 50% LR disk + longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 0); + longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, mbToBytes(5)); + assertValueIs(0.9, scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(0, 10, 0, 10, 5, 10, longRunningTasksUsage)))); - // 20% NLR cpu -- 20% NLR mem + // 20% NLR cpu -- 20% NLR mem -- 0% LR disk longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 0); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); - nlrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(2, 10, 2, 10, longRunningTasksUsage))); - assertValueIs(0.9, nlrScore); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, 0); + nlrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(2, 10, 2, 10, 0, 10, longRunningTasksUsage))); + assertValueIs(0.92, nlrScore); - // 20% LR cpu -- 20% LR mem + // 20% LR cpu -- 20% LR mem -- 0% LR disk longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 2); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, mbToBytes(2)); - lrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(2, 10, 2, 10, longRunningTasksUsage))); - assertValueIs(0.8, lrScore); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, 0); + lrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(2, 10, 2, 10, 0, 10, longRunningTasksUsage))); + assertValueIs(0.84, lrScore); Assert.assertTrue(nlrScore > lrScore); - // 30% NLR cpu -- 30% NLR mem + // 30% NLR cpu -- 30% NLR mem -- 0% LR disk longRunningTasksUsage.put(ResourceUsageType.CPU_USED, 0); longRunningTasksUsage.put(ResourceUsageType.MEMORY_BYTES_USED, 0); - nlrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(3, 10, 3, 10, longRunningTasksUsage))); - assertValueIs(0.85, nlrScore); + longRunningTasksUsage.put(ResourceUsageType.DISK_BYTES_USED, 0); + nlrScore = scheduler.score(SLAVE_ID, taskRequest, Optional.of(getUsage(3, 10, 3, 10, 0, 10, longRunningTasksUsage))); + assertValueIs(0.88, nlrScore); Assert.assertTrue(nlrScore > lrScore); } @@ -292,10 +318,10 @@ private long mbToBytes(long memMb) { return memMb * 1000L * 1000L; } - private SingularitySlaveUsageWithId getUsage(long memMbReserved, long memMbTotal, double cpusReserved, double cpusTotal, Map longRunningTasksUsage) { + private SingularitySlaveUsageWithId getUsage(long memMbReserved, long memMbTotal, double cpusReserved, double cpusTotal, long diskMbReserved, long diskMbTotal, Map longRunningTasksUsage) { return new SingularitySlaveUsageWithId( new SingularitySlaveUsage( - 0, cpusReserved, Optional.of(cpusTotal), 0, memMbReserved, Optional.of(memMbTotal), 0, 0, Optional.of(0L), longRunningTasksUsage, 1, 0L + 0, cpusReserved, Optional.of(cpusTotal), 0, memMbReserved, Optional.of(memMbTotal), 0, diskMbReserved, Optional.of(diskMbTotal), longRunningTasksUsage, 1, 0L ), SLAVE_ID ); diff --git a/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityMesosTaskBuilderTest.java b/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityMesosTaskBuilderTest.java index d3004b5b55..bf7c55a2a7 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityMesosTaskBuilderTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityMesosTaskBuilderTest.java @@ -3,8 +3,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.Collections; @@ -13,6 +12,7 @@ import java.util.concurrent.atomic.AtomicLong; import org.apache.mesos.v1.Protos; +import org.apache.mesos.v1.Protos.AgentID; import org.apache.mesos.v1.Protos.ContainerInfo.DockerInfo.PortMapping; import org.apache.mesos.v1.Protos.ContainerInfo.Type; import org.apache.mesos.v1.Protos.Environment.Variable; @@ -20,7 +20,6 @@ import org.apache.mesos.v1.Protos.Offer; import org.apache.mesos.v1.Protos.OfferID; import org.apache.mesos.v1.Protos.Parameter; -import org.apache.mesos.v1.Protos.AgentID; import org.apache.mesos.v1.Protos.Volume.Mode; import org.junit.Before; import org.junit.Test; @@ -28,9 +27,12 @@ import org.mockito.stubbing.Answer; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.guava.GuavaModule; import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; -import com.hubspot.mesos.MesosUtils; +import com.hubspot.jackson.datatype.protobuf.ProtobufModule; +import com.hubspot.singularity.helpers.MesosProtosUtils; +import com.hubspot.singularity.helpers.MesosUtils; import com.hubspot.mesos.Resources; import com.hubspot.mesos.SingularityContainerInfo; import com.hubspot.mesos.SingularityContainerType; @@ -38,6 +40,7 @@ import com.hubspot.mesos.SingularityDockerNetworkType; import com.hubspot.mesos.SingularityDockerPortMapping; import com.hubspot.mesos.SingularityDockerVolumeMode; +import com.hubspot.singularity.helpers.SingularityMesosTaskHolder; import com.hubspot.mesos.SingularityPortMappingType; import com.hubspot.mesos.SingularityVolume; import com.hubspot.singularity.RequestType; @@ -48,7 +51,6 @@ import com.hubspot.singularity.SingularityPendingTaskId; import com.hubspot.singularity.SingularityRequest; import com.hubspot.singularity.SingularityRequestBuilder; -import com.hubspot.singularity.SingularityTask; import com.hubspot.singularity.SingularityTaskRequest; import com.hubspot.singularity.config.NetworkConfiguration; import com.hubspot.singularity.config.SingularityConfiguration; @@ -62,6 +64,7 @@ public class SingularityMesosTaskBuilderTest { private Offer offer; private SingularityOfferHolder offerHolder; private SingularityPendingTask pendingTask; + private ObjectMapper objectMapper; private final String user = "testUser"; @@ -75,7 +78,11 @@ public void createMocks() { when(idGenerator.getNextExecutorId()).then(new CreateFakeId()); - builder = new SingularityMesosTaskBuilder(new ObjectMapper(), idGenerator, configuration); + objectMapper = new ObjectMapper(); + objectMapper.registerModule(new ProtobufModule()); + objectMapper.registerModule(new GuavaModule()); + + builder = new SingularityMesosTaskBuilder(objectMapper, idGenerator, configuration, new MesosProtosUtils(objectMapper)); taskResources = new Resources(1, 1, 0, 0); executorResources = new Resources(0.1, 1, 0, 0); @@ -107,7 +114,7 @@ public void testShellCommand() { .setCommand(Optional.of("/bin/echo hi")) .build(); final SingularityTaskRequest taskRequest = new SingularityTaskRequest(request, deploy, pendingTask); - final SingularityTask task = builder.buildTask(offerHolder, null, taskRequest, taskResources, executorResources); + final SingularityMesosTaskHolder task = builder.buildTask(offerHolder, null, taskRequest, taskResources, executorResources); assertEquals("/bin/echo hi", task.getMesosTask().getCommand().getValue()); assertEquals(0, task.getMesosTask().getCommand().getArgumentsCount()); @@ -122,7 +129,7 @@ public void testJobUserPassedAsEnvironmentVariable() { .setCommand(Optional.of("/bin/echo hi")) .build(); final SingularityTaskRequest taskRequest = new SingularityTaskRequest(request, deploy, pendingTask); - final SingularityTask task = builder.buildTask(offerHolder, null, taskRequest, taskResources, executorResources); + final SingularityMesosTaskHolder task = builder.buildTask(offerHolder, null, taskRequest, taskResources, executorResources); List environmentVariables = task.getMesosTask() .getCommand() @@ -145,7 +152,7 @@ public void testArgumentCommand() { .setArguments(Optional.of(Collections.singletonList("wat"))) .build(); final SingularityTaskRequest taskRequest = new SingularityTaskRequest(request, deploy, pendingTask); - final SingularityTask task = builder.buildTask(offerHolder, null, taskRequest, taskResources, executorResources); + final SingularityMesosTaskHolder task = builder.buildTask(offerHolder, null, taskRequest, taskResources, executorResources); assertEquals("/bin/echo", task.getMesosTask().getCommand().getValue()); assertEquals(1, task.getMesosTask().getCommand().getArgumentsCount()); @@ -182,7 +189,7 @@ public void testDockerTask() { .setArguments(Optional.of(Collections.singletonList("wat"))) .build(); final SingularityTaskRequest taskRequest = new SingularityTaskRequest(request, deploy, pendingTask); - final SingularityTask task = builder.buildTask(offerHolder, Collections.singletonList(portsResource), taskRequest, taskResources, executorResources); + final SingularityMesosTaskHolder task = builder.buildTask(offerHolder, Collections.singletonList(portsResource), taskRequest, taskResources, executorResources); assertEquals("/bin/echo", task.getMesosTask().getCommand().getValue()); assertEquals(1, task.getMesosTask().getCommand().getArgumentsCount()); @@ -200,14 +207,14 @@ public void testDockerTask() { Parameter envParameter = Parameter.newBuilder().setKey("env").setValue("var=value").build(); assertTrue(task.getMesosTask().getContainer().getDocker().getParametersList().contains(envParameter)); - assertEquals(String.format("/container/%s/%s", task.getTaskRequest().getDeploy().getRequestId(), task.getTaskRequest().getDeploy().getId()), task.getMesosTask().getContainer().getVolumes(1).getContainerPath()); + assertEquals(String.format("/container/%s/%s", task.getTask().getTaskRequest().getDeploy().getRequestId(), task.getTask().getTaskRequest().getDeploy().getId()), task.getMesosTask().getContainer().getVolumes(1).getContainerPath()); assertEquals(String.format("/host/%s", task.getMesosTask().getTaskId().getValue()), task.getMesosTask().getContainer().getVolumes(1).getHostPath()); assertEquals(Mode.RO, task.getMesosTask().getContainer().getVolumes(1).getMode()); assertEquals(80, task.getMesosTask().getContainer().getDocker().getPortMappings(0).getContainerPort()); assertEquals(8080, task.getMesosTask().getContainer().getDocker().getPortMappings(0).getHostPort()); assertEquals("tcp", task.getMesosTask().getContainer().getDocker().getPortMappings(0).getProtocol()); - assertTrue(MesosUtils.getAllPorts(task.getMesosTask().getResources()).contains(8080L)); + assertTrue(MesosUtils.getAllPorts(task.getMesosTask().getResourcesList()).contains(8080L)); assertEquals(81, task.getMesosTask().getContainer().getDocker().getPortMappings(1).getContainerPort()); assertEquals(31000, task.getMesosTask().getContainer().getDocker().getPortMappings(1).getHostPort()); @@ -233,7 +240,7 @@ public void testDockerMinimalNetworking() { .setContainerInfo(Optional.of(containerInfo)) .build(); final SingularityTaskRequest taskRequest = new SingularityTaskRequest(request, deploy, pendingTask); - final SingularityTask task = builder.buildTask(offerHolder, Collections.emptyList(), taskRequest, taskResources, executorResources); + final SingularityMesosTaskHolder task = builder.buildTask(offerHolder, Collections.emptyList(), taskRequest, taskResources, executorResources); assertEquals(Type.DOCKER, task.getMesosTask().getContainer().getType()); assertEquals(Protos.ContainerInfo.DockerInfo.Network.NONE, task.getMesosTask().getContainer().getDocker().getNetwork()); @@ -260,7 +267,7 @@ public void testAutomaticPortMapping() { .setContainerInfo(Optional.of(containerInfo)) .build(); final SingularityTaskRequest taskRequest = new SingularityTaskRequest(request, deploy, pendingTask); - final SingularityTask task = builder.buildTask(offerHolder, Collections.singletonList(MesosUtils.getPortRangeResource(31010, 31011)), taskRequest, taskResources, executorResources); + final SingularityMesosTaskHolder task = builder.buildTask(offerHolder, Collections.singletonList(MesosUtils.getPortRangeResource(31010, 31011)), taskRequest, taskResources, executorResources); assertEquals(Type.DOCKER, task.getMesosTask().getContainer().getType()); assertEquals(Protos.ContainerInfo.DockerInfo.Network.BRIDGE, task.getMesosTask().getContainer().getDocker().getNetwork()); diff --git a/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityStartupTest.java b/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityStartupTest.java index 470674e03d..b86e00d0c6 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityStartupTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityStartupTest.java @@ -112,7 +112,7 @@ public void testScheduledTasksDontGetRescheduledDuringRun() { boolean caughtException = false; try { - requestResource.scheduleImmediately(requestId); + requestResource.scheduleImmediately(singularityUser, requestId); } catch (Exception e) { caughtException = true; } diff --git a/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityTaskShellCommandTest.java b/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityTaskShellCommandTest.java index f924130e94..8b91e8094b 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityTaskShellCommandTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityTaskShellCommandTest.java @@ -30,6 +30,7 @@ import com.hubspot.singularity.config.shell.ShellCommandDescriptor; import com.hubspot.singularity.config.shell.ShellCommandOptionDescriptor; import com.hubspot.singularity.data.transcoders.Transcoder; +import com.hubspot.singularity.helpers.MesosProtosUtils; import com.hubspot.singularity.resources.TaskResource; import com.hubspot.singularity.scheduler.SingularitySchedulerTestBase; import com.hubspot.singularity.scheduler.SingularityTaskShellCommandDispatchPoller; @@ -46,6 +47,8 @@ public class SingularityTaskShellCommandTest extends SingularitySchedulerTestBas private Transcoder updateTranscoder; @Inject private UIConfiguration uiConfiguration; + @Inject + private MesosProtosUtils mesosProtosUtils; public SingularityTaskShellCommandTest() { super(false); @@ -63,19 +66,19 @@ public void testTaskShellCommandPersistence() { // bad shell cmd try { - taskResource.runShellCommand(task.getTaskId().getId(), new SingularityShellCommand("test-cmd", Optional.of(Arrays.asList("one", "two")), user, Optional.absent())); + taskResource.runShellCommand(singularityUser, task.getTaskId().getId(), new SingularityShellCommand("test-cmd", Optional.of(Arrays.asList("one", "two")), user, Optional.absent())); } catch (WebApplicationException exception) { assertEquals(400, exception.getResponse().getStatus()); } // bad option try { - taskResource.runShellCommand(task.getTaskId().getId(), new SingularityShellCommand("d1", Optional.of(Arrays.asList("one", "two")), user, Optional.absent())); + taskResource.runShellCommand(singularityUser, task.getTaskId().getId(), new SingularityShellCommand("d1", Optional.of(Arrays.asList("one", "two")), user, Optional.absent())); } catch (WebApplicationException exception) { assertEquals(400, exception.getResponse().getStatus()); } - SingularityTaskShellCommandRequest firstShellRequest = taskResource.runShellCommand(task.getTaskId().getId(), new SingularityShellCommand("d1", Optional.of(Arrays.asList("o1", "o2")), user, Optional.absent())); + SingularityTaskShellCommandRequest firstShellRequest = taskResource.runShellCommand(singularityUser, task.getTaskId().getId(), new SingularityShellCommand("d1", Optional.of(Arrays.asList("o1", "o2")), user, Optional.absent())); try { Thread.sleep(3); @@ -83,7 +86,7 @@ public void testTaskShellCommandPersistence() { } - SingularityTaskShellCommandRequest secondShellRequest = taskResource.runShellCommand(task.getTaskId().getId(), new SingularityShellCommand("d2", Optional.> absent(), user, Optional.absent())); + SingularityTaskShellCommandRequest secondShellRequest = taskResource.runShellCommand(singularityUser, task.getTaskId().getId(), new SingularityShellCommand("d2", Optional.> absent(), user, Optional.absent())); assertEquals(2, taskManager.getAllQueuedTaskShellCommandRequests().size()); @@ -94,20 +97,20 @@ public void testTaskShellCommandPersistence() { assertEquals(2, taskManager.getTaskShellCommandRequestsForTask(task.getTaskId()).size()); mesosScheduler.message(Event.Message.newBuilder() - .setExecutorId(task.getMesosTask().getExecutor().getExecutorId()) - .setAgentId(task.getMesosTask().getAgentId()) + .setExecutorId(MesosProtosUtils.toExecutorId(task.getMesosTask().getExecutor().getExecutorId())) + .setAgentId(MesosProtosUtils.toAgentId(task.getMesosTask().getAgentId())) .setData(ByteString.copyFrom(updateTranscoder.toBytes(new SingularityTaskShellCommandUpdate(firstShellRequest.getId(), System.currentTimeMillis(), Optional. of("hi"), Optional.absent(), UpdateType.STARTED)))) .build()); mesosScheduler.message(Event.Message.newBuilder() - .setExecutorId(task.getMesosTask().getExecutor().getExecutorId()) - .setAgentId(task.getMesosTask().getAgentId()) + .setExecutorId(MesosProtosUtils.toExecutorId(task.getMesosTask().getExecutor().getExecutorId())) + .setAgentId(MesosProtosUtils.toAgentId(task.getMesosTask().getAgentId())) .setData(ByteString.copyFrom(updateTranscoder.toBytes(new SingularityTaskShellCommandUpdate(new SingularityTaskShellCommandRequestId(task.getTaskId(), "wat", System.currentTimeMillis()), System.currentTimeMillis(), Optional. of("hi"), Optional.absent(), UpdateType.STARTED)))) .build()); mesosScheduler.message(Event.Message.newBuilder() - .setExecutorId(task.getMesosTask().getExecutor().getExecutorId()) - .setAgentId(task.getMesosTask().getAgentId()) + .setExecutorId(MesosProtosUtils.toExecutorId(task.getMesosTask().getExecutor().getExecutorId())) + .setAgentId(MesosProtosUtils.toAgentId(task.getMesosTask().getAgentId())) .setData(ByteString.copyFrom(updateTranscoder.toBytes(new SingularityTaskShellCommandUpdate(new SingularityTaskShellCommandRequestId(new SingularityTaskId("makingitup", "did", System.currentTimeMillis(), 1, "host", "rack"), "wat", System.currentTimeMillis()), System.currentTimeMillis(), Optional. of("hi"), Optional.absent(), UpdateType.STARTED)))) .build()); @@ -127,8 +130,8 @@ public void testRunCommandBeforeBounceKill() { launchTask(request, firstDeploy, 1, TaskState.TASK_RUNNING); - requestResource.bounce(requestId, Optional.of(new SingularityBounceRequest(Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), - Optional.of(new SingularityShellCommand("d1", Optional.of(Arrays.asList("o1", "o2")), user, Optional.absent()))))); + requestResource.bounce(requestId, Optional.of(new SingularityBounceRequest(Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), + Optional.of(new SingularityShellCommand("d1", Optional.of(Arrays.asList("o1", "o2")), user, Optional.absent())))), singularityUser); cleaner.drainCleanupQueue(); diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/MesosUtilsTest.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/MesosUtilsTest.java index 7f07de1db6..3a7bae4222 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/MesosUtilsTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/MesosUtilsTest.java @@ -19,7 +19,7 @@ import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; -import com.hubspot.mesos.MesosUtils; +import com.hubspot.singularity.helpers.MesosUtils; import com.hubspot.singularity.ExtendedTaskState; import com.hubspot.singularity.SingularityTaskHistoryUpdate; import com.hubspot.singularity.SingularityTaskId; diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityDeploysTest.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityDeploysTest.java index 0ddd3c75a2..8478f53b43 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityDeploysTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityDeploysTest.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; import javax.ws.rs.WebApplicationException; @@ -110,9 +111,9 @@ public void testDeployClearsObsoleteScheduledTasks() { public void testDeployAllInstancesAtOnce() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); initFirstDeploy(); @@ -144,9 +145,9 @@ public void testDeployAllInstancesAtOnce() { public void testDeployOneInstanceAtATime() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); initFirstDeploy(); @@ -200,8 +201,8 @@ public void testIncrementalDeployCancel() { initRequest(); // Set up incremental deploy that is partly finished - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); initFirstDeploy(); SingularityTask firstTask = launchTask(request, firstDeploy, 1, TaskState.TASK_RUNNING); @@ -221,7 +222,7 @@ public void testIncrementalDeployCancel() { resourceOffers(); // End in-progress incremental deploy setup - deployResource.cancelDeploy(requestId, secondDeployId); + deployResource.cancelDeploy(singularityUser, requestId, secondDeployId); deployChecker.checkDeploys(); Assert.assertEquals(taskManager.getCleanupTasks().get(0).getCleanupType(), TaskCleanupType.INCREMENTAL_DEPLOY_CANCELLED); @@ -239,9 +240,9 @@ public void testIncrementalDeployCancel() { public void testScaleDownDuringDeploy() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); initFirstDeploy(); @@ -259,7 +260,7 @@ public void testScaleDownDuringDeploy() { statusUpdate(taskManager.getTask(taskId).get(), TaskState.TASK_RUNNING); } - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(1)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(1)).build(), singularityUser); scheduler.drainPendingQueue(); Assert.assertEquals(1, taskManager.getCleanupTaskIds().size()); @@ -276,9 +277,9 @@ public void testScaleDownDuringDeploy() { public void testDeployWithManualStep() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); initFirstDeploy(); @@ -314,7 +315,7 @@ public void testDeployWithManualStep() { Assert.assertEquals(1, deployProgressStepOne.getTargetActiveInstances()); // Add the 'ok' to move to the next step - deployResource.updatePendingDeploy(new SingularityUpdatePendingDeployRequest(requestId, secondDeployId, 2)); + deployResource.updatePendingDeploy(singularityUser, new SingularityUpdatePendingDeployRequest(requestId, secondDeployId, 2)); deployChecker.checkDeploys(); @@ -341,9 +342,9 @@ public void testDeployWithManualStep() { public void testDeployMultipleInstancesAtOnce() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(4)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(4)).build(), singularityUser); initFirstDeploy(); @@ -401,7 +402,7 @@ public void testDeployMultipleInstancesAtOnce() { public void testCancelDeploy() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); initFirstDeploy(); @@ -417,7 +418,7 @@ public void testCancelDeploy() { SingularityTaskId firstNewTaskId = taskManager.getActiveTaskIdsForDeploy(requestId, secondDeployId).get(0); statusUpdate(taskManager.getTask(firstNewTaskId).get(), TaskState.TASK_RUNNING); - deployResource.cancelDeploy(requestId, secondDeployId); + deployResource.cancelDeploy(singularityUser, requestId, secondDeployId); deployChecker.checkDeploys(); @@ -431,7 +432,7 @@ public void testCancelDeploy() { public void testDeployFails() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); initFirstDeploy(); @@ -458,7 +459,7 @@ public void testDeployFails() { public void testDeployFailsForInvalidRequestState() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); initFirstDeploy(); @@ -517,8 +518,8 @@ public void testDeploySucceedsWithTaskRetries() { public void testLbUpdatesAfterEachDeployStep() { initLoadBalancedRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); initFirstDeploy(); SingularityTask firstTask = launchTask(request, firstDeploy, 1, TaskState.TASK_RUNNING); SingularityTask secondTask = launchTask(request, firstDeploy, 2, TaskState.TASK_RUNNING); @@ -594,9 +595,9 @@ public void testLbUpdatesAfterEachDeployStep() { public void testCanceledDeployTasksStayActiveUntilReplaced() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); initFirstDeploy(); @@ -625,7 +626,7 @@ public void testCanceledDeployTasksStayActiveUntilReplaced() { statusUpdate(firstTask, TaskState.TASK_KILLED); deployChecker.checkDeploys(); - deployResource.cancelDeploy(requestId, secondDeployId); + deployResource.cancelDeploy(singularityUser, requestId, secondDeployId); deployChecker.checkDeploys(); scheduler.drainPendingQueue(); @@ -727,7 +728,7 @@ public void testUnpauseOnDeploy() { Assert.assertTrue(requestManager.getRequest(requestId).get().getState() == RequestState.DEPLOYING_TO_UNPAUSE); scheduler.drainPendingQueue(); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1"))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1"))); statusUpdate(taskManager.getActiveTasks().get(0), TaskState.TASK_FAILED); @@ -744,7 +745,7 @@ public void testUnpauseOnDeploy() { Assert.assertTrue(requestManager.getRequest(requestId).get().getState() == RequestState.DEPLOYING_TO_UNPAUSE); scheduler.drainPendingQueue(); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1"))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1"))); statusUpdate(taskManager.getActiveTasks().get(0), TaskState.TASK_RUNNING); deployChecker.checkDeploys(); @@ -789,13 +790,13 @@ public void testUsesNewRequestDataFromPendingDeploy() { Assert.assertEquals(2, taskManager.getPendingTaskIds().size()); Assert.assertEquals(2, requestManager.getRequest(requestId).get().getRequest().getInstancesSafe()); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); SingularityRequest newRequest = request.toBuilder().setInstances(Optional.of(1)).build(); String deployId = "test_new_request_data"; SingularityDeploy deploy = new SingularityDeployBuilder(request.getId(), deployId).setCommand(Optional.of("sleep 100")).build(); - deployResource.deploy(new SingularityDeployRequest(deploy, Optional.absent(), Optional.absent(), Optional.of(newRequest))); + deployResource.deploy(new SingularityDeployRequest(deploy, Optional.absent(), Optional.absent(), Optional.of(newRequest)), singularityUser); deployChecker.checkDeploys(); scheduler.drainPendingQueue(); @@ -819,19 +820,18 @@ public void testUsesNewRequestDataFromPendingDeploy() { Assert.assertEquals(1, requestManager.getRequest(requestId).get().getRequest().getInstancesSafe()); } - @Test(expected = WebApplicationException.class) public void testCannotUpdateRequestDuringPendingDeployWithNewData() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); SingularityRequest newRequest = request.toBuilder().setInstances(Optional.of(1)).build(); String deployId = "test_new_request_data"; SingularityDeploy deploy = new SingularityDeployBuilder(request.getId(), deployId).setCommand(Optional.of("sleep 100")).build(); - deployResource.deploy(new SingularityDeployRequest(deploy, Optional.absent(), Optional.absent(), Optional.of(newRequest))); + deployResource.deploy(new SingularityDeployRequest(deploy, Optional.absent(), Optional.absent(), Optional.of(newRequest)), singularityUser); - requestResource.postRequest(newRequest); + requestResource.postRequest(newRequest, singularityUser); } @Test @@ -855,8 +855,8 @@ public void testDeployTimesOut() { @Test public void testIncrementalDeployInstanceCounter() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(4)).build()); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(4)).build(), singularityUser); initFirstDeploy(); SingularityTask firstTask = launchTask(request, firstDeploy, 1, TaskState.TASK_RUNNING); @@ -917,7 +917,7 @@ public void testDeployWithImmediateRunIsLaunchedImmediately() { .setCommand(Optional.of("printenv > tmp.txt")) .build(); SingularityDeployRequest deployRequest = new SingularityDeployRequest(deploy, Optional.absent(), Optional.absent()); - deployResource.deploy(deployRequest); + deployResource.deploy(deployRequest, singularityUser); deployChecker.checkDeploys(); scheduler.drainPendingQueue(); @@ -928,7 +928,9 @@ public void testDeployWithImmediateRunIsLaunchedImmediately() { SingularityTaskId taskId = taskManager.getActiveTaskIdsForDeploy(requestId, deployId).get(0); SingularityTask task = taskManager.getTask(taskId).get(); - Assert.assertEquals("printenv > tmp.txt", task.getMesosTask().getCommand().getValue()); + Map command = (Map) task.getMesosTask().getAllOtherFields().get("command"); + + Assert.assertEquals("printenv > tmp.txt", (String) command.get("value")); } @Test @@ -942,7 +944,7 @@ public void testDeployWithImmediateRunSchedulesAfterRunningImmediately() { .setCommand(Optional.of("printenv > tmp.txt")) .build(); SingularityDeployRequest deployRequest = new SingularityDeployRequest(deploy, Optional.absent(), Optional.absent()); - deployResource.deploy(deployRequest); + deployResource.deploy(deployRequest, singularityUser); deployChecker.checkDeploys(); scheduler.drainPendingQueue(); diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityExpiringActionsTest.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityExpiringActionsTest.java index d8ff317951..83f6cacdc1 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityExpiringActionsTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityExpiringActionsTest.java @@ -6,7 +6,6 @@ import com.google.common.base.Optional; import com.hubspot.singularity.RequestState; -import com.hubspot.singularity.SingularityShellCommand; import com.hubspot.singularity.SingularityTask; import com.hubspot.singularity.api.SingularityBounceRequest; import com.hubspot.singularity.api.SingularityPauseRequest; @@ -26,7 +25,7 @@ public void testExpiringPause() { SingularityTask taskOne = startTask(firstDeploy); - requestResource.pause(requestId, Optional.of(new SingularityPauseRequest(Optional. absent(), Optional.of(1L), Optional. absent(), Optional.absent(), Optional.absent()))); + requestResource.pause(requestId, Optional.of(new SingularityPauseRequest(Optional.absent(), Optional.of(1L), Optional.absent(), Optional.absent(), Optional.absent())), singularityUser); cleaner.drainCleanupQueue(); @@ -65,7 +64,7 @@ public void testExpiringBounceGoesAway() { startTask(firstDeploy, 1); requestResource.bounce(requestId, - Optional.of(new SingularityBounceRequest(Optional.of(false), Optional. absent(), Optional.of(1L), Optional. absent(), Optional.of("msg"), Optional.absent()))); + Optional.of(new SingularityBounceRequest(Optional.of(false), Optional.absent(), Optional.of(1L), Optional.absent(), Optional.of("msg"), Optional.absent())), singularityUser); cleaner.drainCleanupQueue(); resourceOffers(); @@ -84,7 +83,7 @@ public void testExpiringNonIncrementalBounce() { initWithTasks(3); requestResource.bounce(requestId, - Optional.of(new SingularityBounceRequest(Optional. absent(), Optional. absent(), Optional.of(1L), Optional.of("aid"), Optional. absent(), Optional.absent()))); + Optional.of(new SingularityBounceRequest(Optional.absent(), Optional.absent(), Optional.of(1L), Optional.of("aid"), Optional.absent(), Optional.absent())), singularityUser); Assert.assertTrue(!requestManager.getCleanupRequests().get(0).getMessage().isPresent()); Assert.assertEquals("aid", requestManager.getCleanupRequests().get(0).getActionId().get()); @@ -129,7 +128,7 @@ public void testExpiringNonIncrementalBounce() { public void testExpiringIncrementalBounce() { initRequest(); - requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(3), Optional. absent(), Optional. absent(), Optional. absent(), Optional.absent(), Optional.absent(), Optional.absent())); + requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(3), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); initFirstDeploy(); @@ -138,7 +137,7 @@ public void testExpiringIncrementalBounce() { startTask(firstDeploy, 3); requestResource.bounce(requestId, - Optional.of(new SingularityBounceRequest(Optional.of(true), Optional. absent(), Optional.of(1L), Optional. absent(), Optional.of("msg"), Optional.absent()))); + Optional.of(new SingularityBounceRequest(Optional.of(true), Optional.absent(), Optional.of(1L), Optional.absent(), Optional.of("msg"), Optional.absent())), singularityUser); Assert.assertTrue(requestManager.cleanupRequestExists(requestId)); Assert.assertEquals("msg", requestManager.getCleanupRequests().get(0).getMessage().get()); @@ -187,7 +186,7 @@ public void testExpiringScale() { initRequest(); initFirstDeploy(); - requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(5), Optional.of(1L), Optional. absent(), Optional. absent(), Optional.absent(), Optional.absent(), Optional.absent())); + requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(5), Optional.of(1L), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); try { Thread.sleep(2); @@ -214,7 +213,7 @@ public void testExpiringSkipHealthchecks() { Assert.assertTrue(healthchecker.cancelHealthcheck(firstTask.getTaskId().getId())); - requestResource.skipHealthchecks(requestId, new SingularitySkipHealthchecksRequest(Optional.of(true), Optional.of(1L), Optional. absent(), Optional.absent())); + requestResource.skipHealthchecks(requestId, new SingularitySkipHealthchecksRequest(Optional.of(true), Optional.of(1L), Optional.absent(), Optional.absent()), singularityUser); statusUpdate(firstTask, TaskState.TASK_FAILED); @@ -236,9 +235,9 @@ public void testExpiringScaleWithBounce() { initRequest(); initFirstDeploy(); - requestResource.postRequest(request.toBuilder().setBounceAfterScale(Optional.of(true)).build()); + requestResource.postRequest(request.toBuilder().setBounceAfterScale(Optional.of(true)).build(), singularityUser); - requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(5), Optional.of(1L), Optional. absent(), Optional. absent(), Optional.absent(), Optional.absent(), Optional.absent())); + requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(5), Optional.of(1L), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); Assert.assertEquals(1, requestManager.getCleanupRequests().size()); cleaner.drainCleanupQueue(); diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityHealthchecksTest.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityHealthchecksTest.java index e9a09f9e2d..e67ba38541 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityHealthchecksTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityHealthchecksTest.java @@ -17,7 +17,6 @@ import com.hubspot.singularity.DeployState; import com.hubspot.singularity.SingularityDeploy; import com.hubspot.singularity.SingularityDeployBuilder; -import com.hubspot.singularity.SingularityShellCommand; import com.hubspot.singularity.SingularityTask; import com.hubspot.singularity.SingularityTaskHealthcheckResult; import com.hubspot.singularity.SingularityTaskId; @@ -42,7 +41,7 @@ public void testSkipHealthchecksEdgeCases() { initRequest(); initHCDeploy(); - requestResource.skipHealthchecks(requestId, new SingularitySkipHealthchecksRequest(Optional.of(Boolean.TRUE), Optional. absent(), Optional. absent(), Optional. absent())); + requestResource.skipHealthchecks(requestId, new SingularitySkipHealthchecksRequest(Optional.of(Boolean.TRUE), Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); SingularityTask firstTask = startTask(firstDeploy, 1); @@ -53,7 +52,7 @@ public void testSkipHealthchecksEdgeCases() { Assert.assertEquals(1, taskManager.getNumActiveTasks()); - requestResource.skipHealthchecks(requestId, new SingularitySkipHealthchecksRequest(Optional.of(Boolean.FALSE), Optional. absent(), Optional. absent(), Optional. absent())); + requestResource.skipHealthchecks(requestId, new SingularitySkipHealthchecksRequest(Optional.of(Boolean.FALSE), Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); // run new task check ONLY. newTaskChecker.enqueueNewTaskCheck(firstTask, requestManager.getRequest(requestId), healthchecker); @@ -79,7 +78,7 @@ public void testSkipHealthchecksDuringBounce() { SingularityTask firstTask = startTask(firstDeploy, 1); - requestResource.bounce(requestId, Optional.of(new SingularityBounceRequest(Optional. absent(), Optional.of(true), Optional. absent(), Optional. absent(), Optional.absent(), Optional.absent()))); + requestResource.bounce(requestId, Optional.of(new SingularityBounceRequest(Optional.absent(), Optional.of(true), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent())), singularityUser); setConfigurationForNoDelay(); @@ -121,7 +120,7 @@ public void testHealthchecksDuringBounce() { startTask(firstDeploy); - requestResource.bounce(requestId, Optional.absent()); + requestResource.bounce(requestId, Optional.absent(), singularityUser); cleaner.drainCleanupQueue(); @@ -373,11 +372,11 @@ public void testPortIndices() { firstDeploy = initAndFinishDeploy(request, new SingularityDeployBuilder(request.getId(), firstDeployId).setCommand(Optional.of("sleep 100")) .setHealthcheck(Optional.of(options)), Optional.of(new Resources(1, 64, 3, 0))); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); scheduler.drainPendingQueue(); String[] portRange = {"80:82"}; - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional. absent(), Collections.emptyMap(), portRange))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1", Optional. absent(), Collections.emptyMap(), portRange))); SingularityTaskId firstTaskId = taskManager.getActiveTaskIdsForRequest(requestId).get(0); @@ -404,11 +403,11 @@ public void testPortNumber() { .setCommand(Optional.of("sleep 100")).setResources(Optional.of(new Resources(1, 64, 3, 0))) .setHealthcheck(Optional.of(options)), Optional.absent()); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); scheduler.drainPendingQueue(); String[] portRange = {"80:82"}; - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional. absent(), Collections. emptyMap(), portRange))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1", Optional. absent(), Collections. emptyMap(), portRange))); SingularityTaskId firstTaskId = taskManager.getActiveTaskIdsForRequest(requestId).get(0); diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityMachineStatesTest.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityMachineStatesTest.java index 9e33a5fa66..bed4f7e14f 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityMachineStatesTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityMachineStatesTest.java @@ -76,9 +76,9 @@ public void testDeadSlavesArePurged() { @Test public void testBasicSlaveAndRackState() { - sms.resourceOffers(Arrays.asList(createOffer(1, 1, "slave1", "host1", Optional.of("rack1")))); - sms.resourceOffers(Arrays.asList(createOffer(1, 1, "slave2", "host2", Optional.of("rack2")))); - sms.resourceOffers(Arrays.asList(createOffer(1, 1, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 1, 1, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 1, 1, "slave2", "host2", Optional.of("rack2")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 1, 1, "slave1", "host1", Optional.of("rack1")))); Assert.assertEquals(1, slaveManager.getHistory("slave1").size()); Assert.assertTrue(slaveManager.getNumObjectsAtState(MachineState.ACTIVE) == 2); @@ -97,7 +97,7 @@ public void testBasicSlaveAndRackState() { Assert.assertTrue(slaveManager.getObject("slave1").get().getCurrentState().getState() == MachineState.DEAD); Assert.assertTrue(rackManager.getObject("rack1").get().getCurrentState().getState() == MachineState.DEAD); - sms.resourceOffers(Arrays.asList(createOffer(1, 1, "slave3", "host3", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 1, 1, "slave3", "host3", Optional.of("rack1")))); Assert.assertTrue(slaveManager.getNumObjectsAtState(MachineState.ACTIVE) == 2); Assert.assertEquals(2, rackManager.getNumObjectsAtState(MachineState.ACTIVE)); @@ -105,7 +105,7 @@ public void testBasicSlaveAndRackState() { Assert.assertTrue(rackManager.getHistory("rack1").size() == 3); - sms.resourceOffers(Arrays.asList(createOffer(1, 1, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 1, 1, "slave1", "host1", Optional.of("rack1")))); Assert.assertTrue(slaveManager.getNumObjectsAtState(MachineState.ACTIVE) == 3); Assert.assertTrue(rackManager.getNumObjectsAtState(MachineState.ACTIVE) == 2); @@ -140,10 +140,10 @@ public void testDecommissioning() { scheduler.drainPendingQueue(); - sms.resourceOffers(Arrays.asList(createOffer(1, 129, "slave1", "host1", Optional.of("rack1")))); - sms.resourceOffers(Arrays.asList(createOffer(1, 129, "slave2", "host2", Optional.of("rack1")))); - sms.resourceOffers(Arrays.asList(createOffer(1, 129, "slave3", "host3", Optional.of("rack2")))); - sms.resourceOffers(Arrays.asList(createOffer(1, 129, "slave4", "host4", Optional.of("rack2")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 129, 1025, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 129, 1025, "slave2", "host2", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 129, 1025, "slave3", "host3", Optional.of("rack2")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 129, 1025, "slave4", "host4", Optional.of("rack2")))); for (SingularityTask task : taskManager.getTasksOnSlave(taskManager.getActiveTaskIds(), slaveManager.getObject("slave1").get())) { @@ -170,7 +170,7 @@ public void testDecommissioning() { saveAndSchedule(request.toBuilder().setInstances(Optional.of(3))); - sms.resourceOffers(Arrays.asList(createOffer(1, 129, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 129, 1025, "slave1", "host1", Optional.of("rack1")))); Assert.assertTrue(taskManager.getTasksOnSlave(taskManager.getActiveTaskIds(), slaveManager.getObject("slave1").get()).size() == 1); @@ -182,8 +182,8 @@ public void testDecommissioning() { Assert.assertTrue(slaveManager.getObject("slave1").get().getCurrentState().getState() == MachineState.DECOMMISSIONING); Assert.assertTrue(slaveManager.getObject("slave1").get().getCurrentState().getUser().get().equals("user1")); - sms.resourceOffers(Arrays.asList(createOffer(1, 129, "slave4", "host4", Optional.of("rack2")))); - sms.resourceOffers(Arrays.asList(createOffer(1, 129, "slave3", "host3", Optional.of("rack2")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 129, 1025, "slave4", "host4", Optional.of("rack2")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 129, 1025, "slave3", "host3", Optional.of("rack2")))); for (SingularityTask task : taskManager.getTasksOnSlave(taskManager.getActiveTaskIds(), slaveManager.getObject("slave4").get())) { statusUpdate(task, TaskState.TASK_RUNNING); @@ -210,23 +210,23 @@ public void testDecommissioning() { Assert.assertEquals(StateChangeResult.SUCCESS, rackManager.changeState("rack2", MachineState.STARTING_DECOMMISSION, Optional.absent(), Optional.of("user2"))); // it shouldn't place any on here, since it's DECOMMissioned - sms.resourceOffers(Arrays.asList(createOffer(1, 129, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 129, 1025, "slave1", "host1", Optional.of("rack1")))); Assert.assertEquals(0, taskManager.getTasksOnSlave(taskManager.getActiveTaskIds(), slaveManager.getObject("slave1").get()).size()); - sms.resourceOffers(Arrays.asList(createOffer(1, 129, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 129, 1025, "slave1", "host1", Optional.of("rack1")))); Assert.assertEquals(0, taskManager.getTasksOnSlave(taskManager.getActiveTaskIds(), slaveManager.getObject("slave1").get()).size()); - slaveResource.activateSlave("slave1", null); + slaveResource.activateSlave(singularityUser, "slave1", null); - sms.resourceOffers(Arrays.asList(createOffer(1, 129, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 129, 1025, "slave1", "host1", Optional.of("rack1")))); Assert.assertEquals(1, taskManager.getTasksOnSlave(taskManager.getActiveTaskIds(), slaveManager.getObject("slave1").get()).size()); Assert.assertTrue(rackManager.getObject("rack2").get().getCurrentState().getState() == MachineState.DECOMMISSIONING); - sms.resourceOffers(Arrays.asList(createOffer(1, 129, "slave2", "host2", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 129, 1025, "slave2", "host2", Optional.of("rack1")))); for (SingularityTask task : taskManager.getTasksOnSlave(taskManager.getActiveTaskIds(), slaveManager.getObject("slave1").get())) { statusUpdate(task, TaskState.TASK_RUNNING); @@ -250,12 +250,12 @@ public void testDecommissioning() { @Test public void testEmptyDecommissioning() { - sms.resourceOffers(Arrays.asList(createOffer(1, 129, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 129, 1025, "slave1", "host1", Optional.of("rack1")))); Assert.assertEquals(StateChangeResult.SUCCESS, slaveManager.changeState("slave1", MachineState.STARTING_DECOMMISSION, Optional.absent(), Optional.of("user1"))); scheduler.drainPendingQueue(); - sms.resourceOffers(Arrays.asList(createOffer(1, 129, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 129, 1025, "slave1", "host1", Optional.of("rack1")))); Assert.assertEquals(MachineState.DECOMMISSIONED, slaveManager.getObject("slave1").get().getCurrentState().getState()); } @@ -333,8 +333,8 @@ public void testFrozenSlaveCanBeDecommissioned() { saveAndSchedule(request.toBuilder().setSlavePlacement(Optional.of(SlavePlacement.GREEDY)).setInstances(Optional.of(2))); - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave1", "host1"))); - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave2", "host2"))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave1", "host1"))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave2", "host2"))); // freeze slave1 Assert.assertEquals(StateChangeResult.SUCCESS, slaveManager.changeState("slave1", MachineState.FROZEN, Optional.absent(), Optional.of("user1"))); @@ -437,7 +437,7 @@ public void testExpiringMachineState() { SingularitySlave slave = slaveManager.getObjects().get(0); - slaveResource.freezeSlave(slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.ACTIVE), Optional.absent())); + slaveResource.freezeSlave(singularityUser, slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.ACTIVE), Optional.absent())); Assert.assertEquals(MachineState.FROZEN, slaveManager.getObjects().get(0).getCurrentState().getState()); @@ -454,31 +454,31 @@ private SingularitySlave getSingleSlave() { @Test(expected = WebApplicationException.class) public void testCannotUseStateReservedForSystem() { SingularitySlave slave = getSingleSlave(); - slaveResource.freezeSlave(slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.DEAD), Optional.absent())); + slaveResource.freezeSlave(singularityUser, slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.DEAD), Optional.absent())); } @Test(expected = WebApplicationException.class) public void testBadExpiringStateTransition() { SingularitySlave slave = getSingleSlave(); - slaveResource.decommissionSlave(slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.FROZEN), Optional.absent())); + slaveResource.decommissionSlave(singularityUser, slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.FROZEN), Optional.absent())); } @Test(expected = WebApplicationException.class) public void testInvalidTransitionToDecommissioned() { SingularitySlave slave = getSingleSlave(); - slaveResource.decommissionSlave(slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.DECOMMISSIONED), Optional.absent())); + slaveResource.decommissionSlave(singularityUser, slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.DECOMMISSIONED), Optional.absent())); } @Test public void testValidTransitionToDecommissioned() { initRequest(); initFirstDeploy(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); scheduler.drainPendingQueue(); resourceOffers(1); SingularitySlave slave = slaveManager.getObjects().get(0); - slaveResource.decommissionSlave(slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.DECOMMISSIONED), Optional.of(true))); + slaveResource.decommissionSlave(singularityUser, slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.DECOMMISSIONED), Optional.of(true))); Assert.assertEquals(MachineState.STARTING_DECOMMISSION, slaveManager.getObjects().get(0).getCurrentState().getState()); scheduler.checkForDecomissions(); scheduler.drainPendingQueue(); @@ -492,10 +492,10 @@ public void testValidTransitionToDecommissioned() { @Test public void testSystemChangeClearsExpiringChangeIfInvalid() { SingularitySlave slave = getSingleSlave(); - slaveResource.freezeSlave(slave.getId(), null); - slaveResource.activateSlave(slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.FROZEN), Optional.absent())); + slaveResource.freezeSlave(singularityUser, slave.getId(), null); + slaveResource.activateSlave(singularityUser, slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.FROZEN), Optional.absent())); Assert.assertTrue(slaveManager.getExpiringObject(slave.getId()).isPresent()); - slaveResource.decommissionSlave(slave.getId(), null); + slaveResource.decommissionSlave(singularityUser, slave.getId(), null); Assert.assertFalse(slaveManager.getExpiringObject(slave.getId()).isPresent()); } diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityOfferPerformanceTestRunner.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityOfferPerformanceTestRunner.java index f920cf7254..b23248066e 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityOfferPerformanceTestRunner.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityOfferPerformanceTestRunner.java @@ -9,7 +9,12 @@ import org.junit.Ignore; import org.junit.Test; +import com.google.common.base.Optional; import com.hubspot.mesos.JavaUtils; +import com.hubspot.singularity.RequestType; +import com.hubspot.singularity.SingularityRequest; +import com.hubspot.singularity.SingularityRequestBuilder; +import com.hubspot.singularity.SlavePlacement; public class SingularityOfferPerformanceTestRunner extends SingularitySchedulerTestBase { @@ -17,7 +22,7 @@ public SingularityOfferPerformanceTestRunner() { super(false); } - @Test + @Test(timeout = 300000L) @Ignore public void testOfferCache() { long start = System.currentTimeMillis(); @@ -28,15 +33,22 @@ public void testOfferCache() { Random r = new Random(); Iterator cpuIterator = r.doubles(1, 5).iterator(); Iterator memoryIterator = r.doubles(15, 20000).iterator(); + Iterator diskIterator = r.doubles(30, 50000).iterator(); for (int i = 0; i < numRequests; i++) { - createAndDeployRequest("request-" + i, cpuIterator.next(), memoryIterator.next()); + SingularityRequestBuilder bldr = new SingularityRequestBuilder("request-" + i, RequestType.SERVICE); + + bldr.setInstances(Optional.of(5)); + bldr.setSlavePlacement(Optional.of(SlavePlacement.GREEDY)); + SingularityRequest request = bldr.build(); + saveRequest(request); + deployRequest(request, cpuIterator.next(), memoryIterator.next()); } List offers = new ArrayList<>(numOffers); for (int i = 0; i < numOffers; i++) { - offers.add(createOffer(cpuIterator.next(), memoryIterator.next(), "slave-" + i, "host-" + i)); + offers.add(createOffer(cpuIterator.next(), memoryIterator.next(), diskIterator.next(), "slave-" + i, "host-" + i)); } System.out.println("Starting after " + JavaUtils.duration(start)); diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySchedulerTest.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySchedulerTest.java index e219fdd86a..bc049b523b 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySchedulerTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySchedulerTest.java @@ -29,7 +29,9 @@ import com.google.common.collect.Sets; import com.google.inject.Inject; import com.hubspot.baragon.models.BaragonRequestState; -import com.hubspot.mesos.MesosUtils; +import com.hubspot.mesos.protos.MesosTaskState; +import com.hubspot.singularity.helpers.MesosProtosUtils; +import com.hubspot.singularity.helpers.MesosUtils; import com.hubspot.mesos.Resources; import com.hubspot.mesos.SingularityContainerInfo; import com.hubspot.mesos.SingularityContainerType; @@ -82,7 +84,6 @@ import com.hubspot.singularity.api.SingularityPriorityFreeze; import com.hubspot.singularity.api.SingularityRunNowRequest; import com.hubspot.singularity.api.SingularityScaleRequest; -import com.hubspot.singularity.api.SingularityUnpauseRequest; import com.hubspot.singularity.data.AbstractMachineManager.StateChangeResult; import com.hubspot.singularity.data.SingularityValidator; import com.hubspot.singularity.mesos.OfferCache; @@ -107,6 +108,9 @@ public class SingularitySchedulerTest extends SingularitySchedulerTestBase { @Inject private OfferCache offerCache; + @Inject + private MesosProtosUtils mesosProtosUtils; + public SingularitySchedulerTest() { super(false); } @@ -129,7 +133,7 @@ public void testOfferCacheRescindOffers() { initRequest(); initFirstDeploy(); - requestResource.postRequest(request.toBuilder().setSlavePlacement(Optional.of(SlavePlacement.SEPARATE)).setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setSlavePlacement(Optional.of(SlavePlacement.SEPARATE)).setInstances(Optional.of(2)).build(), singularityUser); schedulerPoller.runActionOnPoll(); @@ -163,7 +167,7 @@ public void testOfferCache() { initRequest(); initFirstDeploy(); - requestResource.postRequest(request.toBuilder().setSlavePlacement(Optional.of(SlavePlacement.SEPARATE)).setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setSlavePlacement(Optional.of(SlavePlacement.SEPARATE)).setInstances(Optional.of(2)).build(), singularityUser); schedulerPoller.runActionOnPoll(); @@ -180,8 +184,8 @@ public void testOfferCombination() { configuration.setOfferCacheSize(2); // Each are half of needed memory - Offer offer1 = createOffer(1, 64, "slave1", "host1"); - Offer offer2 = createOffer(1, 64, "slave1", "host1"); + Offer offer1 = createOffer(1, 64, 1024, "slave1", "host1"); + Offer offer2 = createOffer(1, 64, 1024, "slave1", "host1"); sms.resourceOffers(ImmutableList.of(offer1, offer2)); initRequest(); @@ -200,8 +204,8 @@ public void testOfferCombination() { public void testLeftoverCachedOffersAreReturnedToCache() throws Exception { configuration.setCacheOffers(true); - Offer neededOffer = createOffer(1, 128, "slave1", "host1", Optional.absent(), Collections.emptyMap(), new String[]{"80:81"}); - Offer extraOffer = createOffer(4, 256, "slave1", "host1", Optional.absent(), Collections.emptyMap(), new String[]{"83:84"}); + Offer neededOffer = createOffer(1, 128, 1024, "slave1", "host1", Optional.absent(), Collections.emptyMap(), new String[]{"80:81"}); + Offer extraOffer = createOffer(4, 256, 1024, "slave1", "host1", Optional.absent(), Collections.emptyMap(), new String[]{"83:84"}); sms.resourceOffers(ImmutableList.of(neededOffer, extraOffer)); @@ -233,8 +237,8 @@ public void testLeftoverCachedOffersAreReturnedToCache() throws Exception { public void testLeftoverNewOffersAreCached() { configuration.setCacheOffers(true); - Offer neededOffer = createOffer(1, 128, "slave1", "host1"); - Offer extraOffer = createOffer(4, 256, "slave1", "host1"); + Offer neededOffer = createOffer(1, 128, 1024, "slave1", "host1"); + Offer extraOffer = createOffer(4, 256, 0, "slave1", "host1"); initRequest(); initFirstDeploy(); @@ -305,7 +309,7 @@ public void testCleanerLeavesPausedRequestTasksByDemand() { SingularityTask firstTask = launchTask(request, firstDeploy, 1, TaskState.TASK_RUNNING); createAndSchedulePendingTask(firstDeployId); - requestResource.pause(requestId, Optional.of(new SingularityPauseRequest(Optional.of(false), Optional. absent(), Optional. absent(), Optional.absent(), Optional.absent()))); + requestResource.pause(requestId, Optional.of(new SingularityPauseRequest(Optional.of(false), Optional. absent(), Optional. absent(), Optional.absent(), Optional.absent())), singularityUser); cleaner.drainCleanupQueue(); @@ -326,7 +330,7 @@ public void testTaskKill() { SingularityTask firstTask = startTask(firstDeploy); - taskResource.killTask(firstTask.getTaskId().getId(), Optional.absent()); + taskResource.killTask(firstTask.getTaskId().getId(), Optional.absent(), singularityUser); cleaner.drainCleanupQueue(); killKilledTasks(); @@ -345,7 +349,7 @@ public void testTaskDestroy() { SingularityTask thirdTask = startTask(firstDeploy, 3); taskResource.killTask(secondTask.getTaskId().getId(), Optional.of( - new SingularityKillTaskRequest(Optional.of(true), Optional.of("kill -9 bb"), Optional. absent(), Optional. absent(), Optional. absent()))); + new SingularityKillTaskRequest(Optional.of(true), Optional.of("kill -9 bb"), Optional.absent(), Optional.absent(), Optional.absent())), singularityUser); cleaner.drainCleanupQueue(); killKilledTasks(); @@ -363,7 +367,7 @@ public void testTaskBounce() { SingularityTask firstTask = startTask(firstDeploy); taskResource.killTask(firstTask.getTaskId().getId(), Optional.of( - new SingularityKillTaskRequest(Optional. absent(), Optional.of("msg"), Optional. absent(), Optional.of(true), Optional.absent()))); + new SingularityKillTaskRequest(Optional.absent(), Optional.of("msg"), Optional.absent(), Optional.of(true), Optional.absent())), singularityUser); cleaner.drainCleanupQueue(); @@ -396,7 +400,7 @@ public void testBounceWithLoadBalancer() { saveLoadBalancerState(BaragonRequestState.SUCCESS, taskOne.getTaskId(), LoadBalancerRequestType.ADD); - requestResource.bounce(requestId, Optional. absent()); + requestResource.bounce(requestId, Optional.absent(), singularityUser); cleaner.drainCleanupQueue(); resourceOffers(); @@ -443,7 +447,7 @@ public void testKilledTaskIdRecords() { launchTask(request, firstDeploy, 1, TaskState.TASK_RUNNING); - requestResource.deleteRequest(requestId, Optional. absent()); + requestResource.deleteRequest(requestId, Optional.absent(), singularityUser); Assert.assertTrue(requestManager.getCleanupRequests().size() == 1); @@ -489,10 +493,10 @@ public void testSchedulerCanBatchOnOffers() { initRequest(); initFirstDeploy(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(3)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(3)).build(), singularityUser); scheduler.drainPendingQueue(); - List oneOffer = Arrays.asList(createOffer(12, 1024)); + List oneOffer = Arrays.asList(createOffer(12, 1024, 5000)); sms.resourceOffers(oneOffer); Assert.assertTrue(taskManager.getActiveTasks().size() == 3); @@ -505,10 +509,10 @@ public void testSchedulerExhaustsOffers() { initRequest(); initFirstDeploy(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(10)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(10)).build(), singularityUser); scheduler.drainPendingQueue(); - sms.resourceOffers(Arrays.asList(createOffer(2, 1024), createOffer(1, 1024))); + sms.resourceOffers(Arrays.asList(createOffer(2, 1024, 2048), createOffer(1, 1024, 2048))); Assert.assertEquals(3, taskManager.getActiveTaskIds().size()); Assert.assertEquals(7, taskManager.getPendingTaskIds().size()); @@ -519,10 +523,10 @@ public void testSchedulerRandomizesOffers() { initRequest(); initFirstDeploy(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(15)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(15)).build(), singularityUser); scheduler.drainPendingQueue(); - sms.resourceOffers(Arrays.asList(createOffer(20, 1024), createOffer(20, 1024))); + sms.resourceOffers(Arrays.asList(createOffer(20, 1024, 20000), createOffer(20, 1024, 20000))); Assert.assertEquals(15, taskManager.getActiveTaskIds().size()); @@ -543,18 +547,18 @@ public void testSchedulerHandlesFinishedTasks() { schedule = "*/1 * * * * ? 1995"; // cause it to be pending - requestResource.postRequest(request.toBuilder().setQuartzSchedule(Optional.of(schedule)).build()); + requestResource.postRequest(request.toBuilder().setQuartzSchedule(Optional.of(schedule)).build(), singularityUser); scheduler.drainPendingQueue(); - Assert.assertTrue(requestResource.getActiveRequests(false).isEmpty()); + Assert.assertTrue(requestResource.getActiveRequests(singularityUser, false).isEmpty()); Assert.assertTrue(requestManager.getRequest(requestId).get().getState() == RequestState.FINISHED); Assert.assertTrue(taskManager.getPendingTaskIds().isEmpty()); schedule = "*/1 * * * * ?"; - requestResource.postRequest(request.toBuilder().setQuartzSchedule(Optional.of(schedule)).build()); + requestResource.postRequest(request.toBuilder().setQuartzSchedule(Optional.of(schedule)).build(), singularityUser); scheduler.drainPendingQueue(); - Assert.assertTrue(!requestResource.getActiveRequests(false).isEmpty()); + Assert.assertTrue(!requestResource.getActiveRequests(singularityUser, false).isEmpty()); Assert.assertTrue(requestManager.getRequest(requestId).get().getState() == RequestState.ACTIVE); Assert.assertTrue(!taskManager.getPendingTaskIds().isEmpty()); @@ -563,7 +567,7 @@ public void testSchedulerHandlesFinishedTasks() { @Test public void testOneOffsDontRunByThemselves() { SingularityRequestBuilder bldr = new SingularityRequestBuilder(requestId, RequestType.ON_DEMAND); - requestResource.postRequest(bldr.build()); + requestResource.postRequest(bldr.build(), singularityUser); Assert.assertTrue(requestManager.getPendingRequests().isEmpty()); deploy("d2"); @@ -573,7 +577,7 @@ public void testOneOffsDontRunByThemselves() { Assert.assertTrue(requestManager.getPendingRequests().isEmpty()); - requestResource.scheduleImmediately(requestId); + requestResource.scheduleImmediately(singularityUser, requestId); resourceOffers(); @@ -585,7 +589,7 @@ public void testOneOffsDontRunByThemselves() { Assert.assertEquals(0, taskManager.getActiveTaskIds().size()); Assert.assertEquals(0, taskManager.getPendingTaskIds().size()); - requestResource.scheduleImmediately(requestId); + requestResource.scheduleImmediately(singularityUser, requestId); resourceOffers(); @@ -601,17 +605,17 @@ public void testOneOffsDontRunByThemselves() { @Test public void testOneOffsDontMoveDuringDecomission() { SingularityRequestBuilder bldr = new SingularityRequestBuilder(requestId, RequestType.ON_DEMAND); - requestResource.postRequest(bldr.build()); + requestResource.postRequest(bldr.build(), singularityUser); deploy("d2"); - requestResource.scheduleImmediately(requestId); + requestResource.scheduleImmediately(singularityUser, requestId); validateTaskDoesntMoveDuringDecommission(); } private void validateTaskDoesntMoveDuringDecommission() { - sms.resourceOffers(Arrays.asList(createOffer(1, 129, "slave1", "host1", Optional.of("rack1")))); - sms.resourceOffers(Arrays.asList(createOffer(1, 129, "slave2", "host2", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 129, 1025, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 129, 1025, "slave2", "host2", Optional.of("rack1")))); Assert.assertEquals(1, taskManager.getActiveTaskIds().size()); @@ -619,11 +623,11 @@ private void validateTaskDoesntMoveDuringDecommission() { Assert.assertEquals(StateChangeResult.SUCCESS, slaveManager.changeState("slave1", MachineState.STARTING_DECOMMISSION, Optional. absent(), Optional.of("user1"))); - sms.resourceOffers(Arrays.asList(createOffer(1, 129, "slave2", "host2", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 129, 1025, "slave2", "host2", Optional.of("rack1")))); cleaner.drainCleanupQueue(); - sms.resourceOffers(Arrays.asList(createOffer(1, 129, "slave2", "host2", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 129, 1025, "slave2", "host2", Optional.of("rack1")))); cleaner.drainCleanupQueue(); @@ -637,11 +641,11 @@ private void validateTaskDoesntMoveDuringDecommission() { @Test public void testCustomResourcesWithRunNowRequest() { SingularityRequestBuilder bldr = new SingularityRequestBuilder(requestId, RequestType.ON_DEMAND); - requestResource.postRequest(bldr.build()); + requestResource.postRequest(bldr.build(), singularityUser); deploy("d2"); SingularityRunNowRequest runNowRequest = new SingularityRunNowRequest(Optional.absent(), Optional.absent(), Optional.absent(), Optional.>absent(), Optional.of(new Resources(2, 2, 0)), Optional.absent()); - requestResource.scheduleImmediately(requestId, runNowRequest); + requestResource.scheduleImmediately(singularityUser, requestId, runNowRequest); scheduler.drainPendingQueue(); @@ -649,10 +653,10 @@ public void testCustomResourcesWithRunNowRequest() { Assert.assertTrue(pendingTaskWithResourcs.getResources().isPresent()); Assert.assertEquals(pendingTaskWithResourcs.getResources().get().getCpus(), 2, 0.0); - sms.resourceOffers(Arrays.asList(createOffer(5, 5, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(5, 5, 5, "slave1", "host1", Optional.of("rack1")))); SingularityTask task = taskManager.getActiveTasks().get(0); - Assert.assertEquals(MesosUtils.getNumCpus(task.getMesosTask().getResources(), Optional.absent()), 2.0, 0.0); + Assert.assertEquals(MesosUtils.getNumCpus(mesosProtosUtils.toResourceList(task.getMesosTask().getResources()), Optional.absent()), 2.0, 0.0); } @Test @@ -661,7 +665,7 @@ public void testRunOnceRunOnlyOnce() { request = bldr.build(); saveRequest(request); - deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d1").setCommand(Optional.of("cmd")).build(), Optional. absent(), Optional. absent())); + deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d1").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent()), singularityUser); scheduler.drainPendingQueue(); @@ -680,7 +684,7 @@ public void testRunOnceRunOnlyOnce() { Assert.assertTrue(taskManager.getActiveTaskIds().isEmpty()); - deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d2").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional. absent())); + deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d2").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent()), singularityUser); scheduler.drainPendingQueue(); @@ -707,11 +711,11 @@ public void testMultipleRunOnceTasks() { request = bldr.build(); saveRequest(request); - deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d1").setCommand(Optional.of("cmd")).build(), Optional. absent(), Optional. absent())); + deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d1").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent()), singularityUser); deployChecker.checkDeploys(); Assert.assertEquals(1, requestManager.getSizeOfPendingQueue()); - deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d2").setCommand(Optional.of("cmd")).build(), Optional. absent(), Optional. absent())); + deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d2").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent()), singularityUser); deployChecker.checkDeploys(); Assert.assertEquals(2, requestManager.getSizeOfPendingQueue()); @@ -727,7 +731,7 @@ public void testRunOnceDontMoveDuringDecomission() { request = bldr.build(); saveRequest(request); - deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d1").setCommand(Optional.of("cmd")).build(), Optional. absent(), Optional. absent())); + deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d1").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent()), singularityUser); scheduler.drainPendingQueue(); @@ -740,7 +744,7 @@ public void testRunOnceDontMoveDuringDecomission() { public void testDecommissionDoesntKillPendingDeploy() { initRequest(); - deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d1").setCommand(Optional.of("cmd")).build(), Optional. absent(), Optional. absent())); + deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d1").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent()), singularityUser); scheduler.drainPendingQueue(); deployChecker.checkDeploys(); @@ -748,7 +752,7 @@ public void testDecommissionDoesntKillPendingDeploy() { Assert.assertEquals(1, taskManager.getNumActiveTasks()); - slaveResource.decommissionSlave(taskManager.getActiveTasks().get(0).getAgentId().getValue(), null); + slaveResource.decommissionSlave(singularityUser, taskManager.getActiveTasks().get(0).getAgentId().getValue(), null); scheduler.checkForDecomissions(); @@ -778,7 +782,7 @@ public void testRetries() { request = bldr.setNumRetriesOnFailure(Optional.of(2)).build(); saveRequest(request); - deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d1").setCommand(Optional.of("cmd")).build(), Optional. absent(), Optional. absent())); + deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d1").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent()), singularityUser); scheduler.drainPendingQueue(); deployChecker.checkDeploys(); @@ -982,7 +986,7 @@ public void testLbCleanupSkippedOnSkipRemoveFlag() { boolean removeFromLoadBalancer = false; SingularityDeleteRequestRequest deleteRequest = new SingularityDeleteRequestRequest(Optional.absent(), Optional.absent(), Optional.of(removeFromLoadBalancer)); - requestResource.deleteRequest(requestId, Optional.of(deleteRequest)); + requestResource.deleteRequest(requestId, Optional.of(deleteRequest), singularityUser); testingLbClient.setNextBaragonRequestState(BaragonRequestState.WAITING); @@ -1003,7 +1007,7 @@ public void testLbCleanupOccursOnRequestDelete() { initLoadBalancedDeploy(); startTask(firstDeploy); - requestResource.deleteRequest(requestId, Optional.absent()); + requestResource.deleteRequest(requestId, Optional.absent(), singularityUser); testingLbClient.setNextBaragonRequestState(BaragonRequestState.WAITING); @@ -1149,7 +1153,7 @@ public void testPause() { SingularityTask taskOne = startTask(firstDeploy); - requestResource.pause(requestId, Optional. absent()); + requestResource.pause(requestId, Optional.absent(), singularityUser); cleaner.drainCleanupQueue(); @@ -1164,7 +1168,7 @@ public void testPause() { Assert.assertEquals(RequestState.PAUSED, requestManager.getRequest(requestId).get().getState()); Assert.assertEquals(requestId, requestManager.getPausedRequests(false).iterator().next().getRequest().getId()); - requestResource.unpause(requestId, Optional. absent()); + requestResource.unpause(requestId, Optional.absent(), singularityUser); resourceOffers(); @@ -1179,7 +1183,7 @@ public void testPause() { public void testBounce() { initRequest(); - requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(3), Optional. absent(), Optional. absent(), Optional. absent(), Optional.absent(), Optional.absent(), Optional.absent())); + requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(3), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); initFirstDeploy(); @@ -1187,7 +1191,7 @@ public void testBounce() { SingularityTask taskTwo = startTask(firstDeploy, 2); SingularityTask taskThree = startTask(firstDeploy, 3); - requestResource.bounce(requestId, Optional. absent()); + requestResource.bounce(requestId, Optional.absent(), singularityUser); Assert.assertTrue(requestManager.cleanupRequestExists(requestId)); @@ -1225,7 +1229,7 @@ public void testBounce() { public void testIncrementalBounceShutsDownOldTasksPerNewHealthyTask() { initRequest(); - requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(3), Optional. absent(), Optional. absent(), Optional. absent(), Optional.absent(), Optional.absent(), Optional.absent())); + requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(3), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); initFirstDeploy(); @@ -1234,7 +1238,7 @@ public void testIncrementalBounceShutsDownOldTasksPerNewHealthyTask() { startTask(firstDeploy, 3); requestResource.bounce(requestId, - Optional.of(new SingularityBounceRequest(Optional.of(true), Optional.absent(), Optional.of(1L), Optional.absent(), Optional.of("msg"), Optional.absent()))); + Optional.of(new SingularityBounceRequest(Optional.of(true), Optional.absent(), Optional.of(1L), Optional.absent(), Optional.of("msg"), Optional.absent())), singularityUser); Assert.assertTrue(requestManager.cleanupRequestExists(requestId)); @@ -1269,7 +1273,7 @@ public void testBounceOnPendingInstancesReleasesLock() { Assert.assertEquals("Bounce starts when tasks have not yet been launched", 0, taskManager.getActiveTaskIds().size()); - requestResource.bounce(requestId, Optional.of(new SingularityBounceRequest(Optional.absent(), Optional.of(true), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()))); + requestResource.bounce(requestId, Optional.of(new SingularityBounceRequest(Optional.absent(), Optional.of(true), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent())), singularityUser); // It acquires a lock on the bounce Assert.assertTrue("Lock on bounce should be acquired during bounce", requestManager.getExpiringBounce(requestId).isPresent()); @@ -1308,7 +1312,7 @@ public void testBounceOnRunningInstancesReleasesLock() { startTask(firstDeploy, 1); Assert.assertEquals(1, taskManager.getActiveTaskIds().size()); - requestResource.bounce(requestId, Optional.of(new SingularityBounceRequest(Optional.absent(), Optional.of(true), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()))); + requestResource.bounce(requestId, Optional.of(new SingularityBounceRequest(Optional.absent(), Optional.of(true), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent())), singularityUser); cleaner.drainCleanupQueue(); // It acquires a lock on the bounce @@ -1343,11 +1347,11 @@ public void testIncrementalBounce() { initRequest(); resourceOffers(2); // set up slaves so scale validate will pass - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); requestResource.postRequest(request.toBuilder() .setSlavePlacement(Optional.of(SlavePlacement.SEPARATE_BY_REQUEST)) - .setInstances(Optional.of(2)).build() + .setInstances(Optional.of(2)).build(), singularityUser ); initHCDeploy(); @@ -1505,25 +1509,25 @@ public void testTaskOddities() { @Test public void testOnDemandTasksPersist() { SingularityRequestBuilder bldr = new SingularityRequestBuilder(requestId, RequestType.ON_DEMAND); - requestResource.postRequest(bldr.build()); + requestResource.postRequest(bldr.build(), singularityUser); deploy("d2"); deployChecker.checkDeploys(); - requestResource.scheduleImmediately(requestId); + requestResource.scheduleImmediately(singularityUser, requestId); resourceOffers(); - requestResource.scheduleImmediately(requestId); + requestResource.scheduleImmediately(singularityUser, requestId); resourceOffers(); Assert.assertEquals(2, taskManager.getActiveTaskIds().size()); - requestResource.scheduleImmediately(requestId); + requestResource.scheduleImmediately(singularityUser, requestId); scheduler.drainPendingQueue(); - requestResource.scheduleImmediately(requestId); + requestResource.scheduleImmediately(singularityUser, requestId); scheduler.drainPendingQueue(); @@ -1537,12 +1541,12 @@ public void testOnDemandTasksPersist() { @Test public void testRunNowScheduledJobDoesNotRetry() { initScheduledRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); SingularityRequest newRequest = request.toBuilder().setNumRetriesOnFailure(Optional.of(2)).build(); - requestResource.postRequest(newRequest); + requestResource.postRequest(newRequest, singularityUser); initFirstDeploy(); - requestResource.scheduleImmediately(requestId, new SingularityRunNowRequest(Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent())); + requestResource.scheduleImmediately(singularityUser, requestId, new SingularityRunNowRequest(Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent())); resourceOffers(); SingularityTask task = taskManager.getActiveTasks().get(0); @@ -1550,7 +1554,7 @@ public void testRunNowScheduledJobDoesNotRetry() { SingularityDeployStatistics deployStatistics = deployManager.getDeployStatistics(task.getTaskId().getRequestId(), task.getTaskId().getDeployId()).get(); - Assert.assertEquals(TaskState.TASK_FAILED, deployStatistics.getLastTaskState().get().toTaskState().get()); + Assert.assertEquals(MesosTaskState.TASK_FAILED, deployStatistics.getLastTaskState().get().toTaskState().get()); Assert.assertEquals(PendingType.TASK_DONE, taskManager.getPendingTaskIds().get(0).getPendingType()); Assert.assertEquals(1, deployStatistics.getNumFailures()); Assert.assertEquals(0, deployStatistics.getNumSequentialRetries()); @@ -1565,6 +1569,7 @@ public void testOnDemandRunNowJobRespectsSpecifiedRunAtTime() { long requestedLaunchTime = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(10); requestResource.scheduleImmediately( + singularityUser, requestId, new SingularityRunNowRequest( Optional.absent(), @@ -1592,6 +1597,7 @@ public void testScheduledRunNowJobRespectsSpecifiedRunAtTime() { long requestedLaunchTime = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(10); requestResource.scheduleImmediately( + singularityUser, requestId, new SingularityRunNowRequest( Optional.absent(), @@ -1642,8 +1648,8 @@ public void testScaleDownTakesHighestInstances() { Assert.assertEquals(5, taskManager.getActiveTaskIds().size()); - requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(2), Optional. absent(), Optional. absent(), - Optional. absent(), Optional.absent(), Optional.absent(), Optional.absent())); + requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(2), Optional.absent(), Optional.absent(), + Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); resourceOffers(); cleaner.drainCleanupQueue(); @@ -1683,8 +1689,8 @@ public void testScaleDownTakesHighestInstancesWithPendingTask() { System.out.println(taskManager.getPendingTaskIds()); - requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(3), Optional. absent(), Optional. absent(), - Optional. absent(), Optional.absent(), Optional.absent(), Optional.absent())); + requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(3), Optional.absent(), Optional.absent(), + Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); scheduler.drainPendingQueue(); cleaner.drainCleanupQueue(); @@ -1823,8 +1829,8 @@ public void itCorrectlyUpdatesRequestDeletingStateHistory() { public void itSetsRequestStateToDeletedAfterAllTasksAreCleanedUp() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); initFirstDeploy(); launchTask(request, firstDeploy, 1, TaskState.TASK_RUNNING); @@ -1849,8 +1855,8 @@ public void itSetsRequestStateToDeletedAfterAllTasksAreCleanedUp() { public void itSetsRequestStateToDeletedIfTaskCleanupFails() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); initFirstDeploy(); SingularityTask firstTask = launchTask(request, firstDeploy, 1, TaskState.TASK_RUNNING); @@ -1881,14 +1887,14 @@ public void testMaxTasksPerOffer() { initRequest(); initFirstDeploy(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(20)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(20)).build(), singularityUser); scheduler.drainPendingQueue(); - sms.resourceOffers(Arrays.asList(createOffer(36, 12024))); + sms.resourceOffers(Arrays.asList(createOffer(36, 12024, 50000))); Assert.assertTrue(taskManager.getActiveTasks().size() == 3); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1"), createOffer(20, 20000, "slave2", "host2"))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1"), createOffer(20, 20000, 50000, "slave2", "host2"))); Assert.assertTrue(taskManager.getActiveTasks().size() == 9); @@ -1905,23 +1911,23 @@ public void testRequestedPorts() { initRequest(); initAndFinishDeploy(request, deployBuilder, Optional.of(new Resources(1, 64, 3, 0))); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); scheduler.drainPendingQueue(); String[] portRangeWithNoRequestedPorts = {"65:70"}; - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional. absent(), Collections.emptyMap(), portRangeWithNoRequestedPorts))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1", Optional. absent(), Collections.emptyMap(), portRangeWithNoRequestedPorts))); Assert.assertEquals(0, taskManager.getActiveTasks().size()); String[] portRangeWithSomeRequestedPorts = {"80:82"}; - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional. absent(), Collections.emptyMap(), portRangeWithSomeRequestedPorts))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1", Optional. absent(), Collections.emptyMap(), portRangeWithSomeRequestedPorts))); Assert.assertEquals(0, taskManager.getActiveTasks().size()); String[] portRangeWithRequestedButNotEnoughPorts = {"80:80", "8080:8080"}; - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional. absent(), Collections.emptyMap(), portRangeWithRequestedButNotEnoughPorts))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1", Optional. absent(), Collections.emptyMap(), portRangeWithRequestedButNotEnoughPorts))); Assert.assertEquals(0, taskManager.getActiveTasks().size()); String[] portRangeWithNeededPorts = {"80:83", "8080:8080"}; - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional. absent(), Collections.emptyMap(), portRangeWithNeededPorts))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1", Optional. absent(), Collections.emptyMap(), portRangeWithNeededPorts))); Assert.assertEquals(1, taskManager.getActiveTaskIds().size()); } @@ -1948,7 +1954,7 @@ private SingularityDeployBuilder dockerDeployWithPorts() { @Test public void testQueueMultipleOneOffs() { SingularityRequestBuilder bldr = new SingularityRequestBuilder(requestId, RequestType.ON_DEMAND); - requestResource.postRequest(bldr.build()); + requestResource.postRequest(bldr.build(), singularityUser); deploy("on_demand_deploy"); deployChecker.checkDeploys(); @@ -1981,7 +1987,7 @@ public void testPriorityFreezeKillsActiveTasks() { final SingularityTask highPriorityTask = launchTask(highPriorityRequest, highPriorityDeploy, 10, 1, TaskState.TASK_RUNNING); // priority freeze of .5 means that lowPriorityRequest's task should have a cleanup - priorityResource.createPriorityFreeze(new SingularityPriorityFreeze(.5, true, Optional.of("test"), Optional.absent())); + priorityResource.createPriorityFreeze(singularityUser, new SingularityPriorityFreeze(.5, true, Optional.of("test"), Optional.absent())); // perform the killing priorityKillPoller.runActionOnPoll(); @@ -2007,7 +2013,7 @@ public void testPriorityFreezeKillsActiveTasks() { Assert.assertEquals(lowPriorityRequest.getId(), pendingTaskId.getRequestId()); // end the priority freeze - priorityResource.deleteActivePriorityFreeze(); + priorityResource.deleteActivePriorityFreeze(singularityUser); // launch task(s) scheduler.drainPendingQueue(); @@ -2023,21 +2029,21 @@ public void testPriorityFreezeDoesntLaunchTasks() { final SingularityRequest lowPriorityRequest = new SingularityRequestBuilder("lowPriorityRequest", RequestType.ON_DEMAND).setTaskPriorityLevel(Optional.of(.25)).build(); saveRequest(lowPriorityRequest); deployResource.deploy( - new SingularityDeployRequest(new SingularityDeployBuilder(lowPriorityRequest.getId(), "d1").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent())); + new SingularityDeployRequest(new SingularityDeployBuilder(lowPriorityRequest.getId(), "d1").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent()), singularityUser); // deploy medium priority request (NOT affected by priority freeze) final SingularityRequest mediumPriorityRequest = new SingularityRequestBuilder("mediumPriorityRequest", RequestType.ON_DEMAND).setTaskPriorityLevel(Optional.of(.5)).build(); saveRequest(mediumPriorityRequest); deployResource.deploy( - new SingularityDeployRequest(new SingularityDeployBuilder(mediumPriorityRequest.getId(), "d2").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent())); + new SingularityDeployRequest(new SingularityDeployBuilder(mediumPriorityRequest.getId(), "d2").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent()), singularityUser); // create priority freeze priorityManager.createPriorityFreeze( new SingularityPriorityFreezeParent(new SingularityPriorityFreeze(0.3, true, Optional.absent(), Optional.absent()), System.currentTimeMillis(), Optional.absent())); // launch both tasks - requestResource.scheduleImmediately(lowPriorityRequest.getId()); - requestResource.scheduleImmediately(mediumPriorityRequest.getId()); + requestResource.scheduleImmediately(singularityUser, lowPriorityRequest.getId()); + requestResource.scheduleImmediately(singularityUser, mediumPriorityRequest.getId()); // drain pending queue scheduler.drainPendingQueue(); @@ -2068,7 +2074,7 @@ public void testObsoletePendingRequestsRemoved() { initRequest(); initFirstDeploy(); SingularityTask taskOne = startTask(firstDeploy); - requestResource.pause(requestId, Optional. absent()); + requestResource.pause(requestId, Optional.absent(), singularityUser); requestManager.addToPendingQueue(new SingularityPendingRequest(requestId, firstDeployId, System.currentTimeMillis(), Optional.absent(), PendingType.NEW_DEPLOY, Optional.absent(), Optional.absent())); Assert.assertEquals(requestManager.getPendingRequests().size(), 1); @@ -2106,7 +2112,7 @@ public void testCronScheduleChanges() throws Exception { final SingularityDeploy newDeploy = new SingularityDeployBuilder(request.getId(), "2").setCommand(Optional.of("sleep 100")).build(); - deployResource.deploy(new SingularityDeployRequest(newDeploy, Optional.absent(), Optional.absent(), Optional.of(newRequest))); + deployResource.deploy(new SingularityDeployRequest(newDeploy, Optional.absent(), Optional.absent(), Optional.of(newRequest)), singularityUser); deployChecker.checkDeploys(); @@ -2123,7 +2129,7 @@ public void testImmediateRunReplacesScheduledTask() { .setCommand(Optional.of("sleep 100")) .build(); SingularityDeployRequest singularityDeployRequest = new SingularityDeployRequest(deploy, Optional.absent(), Optional.absent(), Optional.absent()); - deployResource.deploy(singularityDeployRequest); + deployResource.deploy(singularityDeployRequest, singularityUser); scheduler.drainPendingQueue(); @@ -2148,14 +2154,14 @@ public void testSchedulerDropsMultipleScheduledTaskInstances() { .setCommand(Optional.of("sleep 100")) .build(); SingularityDeployRequest singularityDeployRequest = new SingularityDeployRequest(deploy, Optional.absent(), Optional.absent(), Optional.absent()); - deployResource.deploy(singularityDeployRequest); + deployResource.deploy(singularityDeployRequest, singularityUser); scheduler.drainPendingQueue(); requestManager.addToPendingQueue(new SingularityPendingRequest(requestId, firstDeployId, Instant.now().plus(3, ChronoUnit.DAYS).toEpochMilli(), Optional.absent(), PendingType.NEW_DEPLOY, Optional.absent(), Optional.absent())); SingularityRunNowRequest runNowRequest = new SingularityRunNowRequest(Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()); - requestResource.scheduleImmediately(requestId, runNowRequest); + requestResource.scheduleImmediately(singularityUser, requestId, runNowRequest); @@ -2164,7 +2170,6 @@ public void testSchedulerDropsMultipleScheduledTaskInstances() { Assert.assertEquals(PendingType.IMMEDIATE, requestManager.getPendingRequests().get(0).getPendingType()); Assert.assertEquals(PendingType.NEW_DEPLOY, requestManager.getPendingRequests().get(1).getPendingType()); - scheduler.drainPendingQueue(); Assertions.assertThat(taskManager.getPendingTaskIds()) .describedAs("Only the immediate request gets run") @@ -2186,7 +2191,7 @@ public void testInvalidQuartzTimeZoneErrors() { .setScheduleTimeZone(Optional.of("invalid_timezone")) .build(); - requestResource.postRequest(req); + requestResource.postRequest(req, singularityUser); } @Test @@ -2205,8 +2210,8 @@ public void testDifferentQuartzTimeZones() { .setScheduleTimeZone(Optional.of("GMT")) .build(); - requestResource.postRequest(requestEST); - requestResource.postRequest(requestGMT); + requestResource.postRequest(requestEST, singularityUser); + requestResource.postRequest(requestGMT, singularityUser); SingularityDeploy deployEST = new SingularityDeployBuilder(requestEST.getId(), "est_deploy_id") .setCommand(Optional.of("sleep 1")) @@ -2216,8 +2221,8 @@ public void testDifferentQuartzTimeZones() { .setCommand(Optional.of("sleep 1")) .build(); - deployResource.deploy(new SingularityDeployRequest(deployEST, Optional.absent(), Optional.absent(), Optional.absent())); - deployResource.deploy(new SingularityDeployRequest(deployGMT, Optional.absent(), Optional.absent(), Optional.absent())); + deployResource.deploy(new SingularityDeployRequest(deployEST, Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); + deployResource.deploy(new SingularityDeployRequest(deployGMT, Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); deployChecker.checkDeploys(); scheduler.drainPendingQueue(); @@ -2245,7 +2250,7 @@ public void testDeployCleanupOverwritesTaskBounceCleanup() { final SingularityTask oldTask = startTask(firstDeploy); taskResource - .killTask(oldTask.getTaskId().getId(), Optional.of(new SingularityKillTaskRequest(Optional. absent(), Optional. absent(), Optional. absent(), Optional.of(true), Optional.absent()))); + .killTask(oldTask.getTaskId().getId(), Optional.of(new SingularityKillTaskRequest(Optional.absent(), Optional.absent(), Optional.absent(), Optional.of(true), Optional.absent())), singularityUser); final Optional taskCleanup = taskManager.getTaskCleanup(oldTask.getTaskId().getId()); Assert.assertTrue(taskCleanup.isPresent()); @@ -2268,7 +2273,7 @@ public void testCleanerFindsTasksWithSkippedHealthchecks() { initRequest(); resourceOffers(2); // set up slaves so scale validate will pass - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); long now = System.currentTimeMillis(); @@ -2318,7 +2323,7 @@ public void testScaleWithBounceDoesNotLaunchExtraInstances() { initFirstDeploy(); launchTask(request, firstDeploy, 1, TaskState.TASK_RUNNING); - requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(5), Optional.of(1L), Optional. absent(), Optional. absent(), Optional.absent(), Optional.of(true), Optional.absent())); + requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(5), Optional.of(1L), Optional.absent(), Optional.absent(), Optional.absent(), Optional.of(true), Optional.absent()), singularityUser); Assert.assertEquals(1, requestManager.getCleanupRequests().size()); cleaner.drainCleanupQueue(); @@ -2332,11 +2337,11 @@ public void testScaleWithBounceDoesNotLaunchExtraInstances() { public void testAcceptOffersWithRoleForRequestWithRole() { SingularityRequestBuilder bldr = new SingularityRequestBuilder(requestId, RequestType.ON_DEMAND); bldr.setRequiredRole(Optional.of("test-role")); - requestResource.postRequest(bldr.build()); + requestResource.postRequest(bldr.build(), singularityUser); deploy("d2"); SingularityRunNowRequest runNowRequest = new SingularityRunNowRequest(Optional.absent(), Optional.absent(), Optional.absent(), Optional.>absent(), Optional.of(new Resources(2, 2, 0)), Optional.absent()); - requestResource.scheduleImmediately(requestId, runNowRequest); + requestResource.scheduleImmediately(singularityUser, requestId, runNowRequest); scheduler.drainPendingQueue(); @@ -2344,25 +2349,25 @@ public void testAcceptOffersWithRoleForRequestWithRole() { Assert.assertTrue(pendingTaskWithResources.getResources().isPresent()); Assert.assertEquals(pendingTaskWithResources.getResources().get().getCpus(), 2, 0.0); - sms.resourceOffers(Arrays.asList(createOffer(5, 5))); + sms.resourceOffers(Arrays.asList(createOffer(5, 5, 5))); pendingTaskWithResources = taskManager.getPendingTasks().get(0); Assert.assertTrue(pendingTaskWithResources.getResources().isPresent()); Assert.assertEquals(pendingTaskWithResources.getResources().get().getCpus(), 2, 0.0); - sms.resourceOffers(Arrays.asList(createOffer(5, 5, Optional.of("test-role")))); + sms.resourceOffers(Arrays.asList(createOffer(5, 5, 5, Optional.of("test-role")))); SingularityTask task = taskManager.getActiveTasks().get(0); - Assert.assertEquals(MesosUtils.getNumCpus(task.getMesosTask().getResources(), Optional.of("test-role")), 2.0, 0.0); + Assert.assertEquals(MesosUtils.getNumCpus(mesosProtosUtils.toResourceList(task.getMesosTask().getResources()), Optional.of("test-role")), 2.0, 0.0); } @Test public void testNotAcceptOfferWithRoleForRequestWithoutRole() { SingularityRequestBuilder bldr = new SingularityRequestBuilder(requestId, RequestType.ON_DEMAND); - requestResource.postRequest(bldr.build()); + requestResource.postRequest(bldr.build(), singularityUser); deploy("d2"); SingularityRunNowRequest runNowRequest = new SingularityRunNowRequest(Optional.absent(), Optional.absent(), Optional.absent(), Optional.>absent(), Optional.of(new Resources(2, 2, 0)), Optional.absent()); - requestResource.scheduleImmediately(requestId, runNowRequest); + requestResource.scheduleImmediately(singularityUser, requestId, runNowRequest); scheduler.drainPendingQueue(); @@ -2370,7 +2375,7 @@ public void testNotAcceptOfferWithRoleForRequestWithoutRole() { Assert.assertTrue(pendingTaskWithResources.getResources().isPresent()); Assert.assertEquals(pendingTaskWithResources.getResources().get().getCpus(), 2, 0.0); - sms.resourceOffers(Arrays.asList(createOffer(5, 5, Optional.of("test-role")))); + sms.resourceOffers(Arrays.asList(createOffer(5, 5, 5, Optional.of("test-role")))); pendingTaskWithResources = taskManager.getPendingTasks().get(0); Assert.assertTrue(pendingTaskWithResources.getResources().isPresent()); @@ -2381,7 +2386,7 @@ public void testNotAcceptOfferWithRoleForRequestWithoutRole() { public void testMaxOnDemandTasks() { SingularityRequestBuilder bldr = new SingularityRequestBuilder(requestId, RequestType.ON_DEMAND); bldr.setInstances(Optional.of(1)); - requestResource.postRequest(bldr.build()); + requestResource.postRequest(bldr.build(), singularityUser); deploy("on_demand_deploy"); deployChecker.checkDeploys(); diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySchedulerTestBase.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySchedulerTestBase.java index c0d75a0f6b..0c0ef92fae 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySchedulerTestBase.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySchedulerTestBase.java @@ -41,11 +41,10 @@ import com.google.inject.name.Named; import com.hubspot.baragon.models.BaragonRequestState; import com.hubspot.deploy.HealthcheckOptionsBuilder; -import com.hubspot.mesos.MesosUtils; +import com.hubspot.singularity.helpers.MesosProtosUtils; +import com.hubspot.singularity.helpers.MesosUtils; import com.hubspot.mesos.Resources; -import com.hubspot.mesos.json.SingularityMesosOfferObject; -import com.hubspot.mesos.json.SingularityMesosTaskObject; -import com.hubspot.mesos.json.SingularityMesosTaskStatusObject; +import com.hubspot.mesos.protos.MesosTaskStatusObject; import com.hubspot.singularity.DeployState; import com.hubspot.singularity.LoadBalancerRequestType; import com.hubspot.singularity.LoadBalancerRequestType.LoadBalancerRequestId; @@ -75,6 +74,7 @@ import com.hubspot.singularity.SingularityTaskId; import com.hubspot.singularity.SingularityTaskRequest; import com.hubspot.singularity.SingularityTaskStatusHolder; +import com.hubspot.singularity.SingularityUser; import com.hubspot.singularity.SlavePlacement; import com.hubspot.singularity.api.SingularityDeployRequest; import com.hubspot.singularity.api.SingularityScaleRequest; @@ -188,6 +188,8 @@ public class SingularitySchedulerTestBase extends SingularityCuratorTestBase { protected Optional user = Optional.absent(); + protected SingularityUser singularityUser = SingularityUser.DEFAULT_USER; + public SingularitySchedulerTestBase(boolean useDBTests) { super(useDBTests); } @@ -206,31 +208,31 @@ public final void setupDriver() throws Exception { migrationRunner.checkMigrations(); } - protected Offer createOffer(double cpus, double memory) { - return createOffer(cpus, memory, "slave1", "host1", Optional. absent()); + protected Offer createOffer(double cpus, double memory, double disk) { + return createOffer(cpus, memory, disk,"slave1", "host1", Optional. absent()); } - protected Offer createOffer(double cpus, double memory, Optional role) { - return createOffer(cpus, memory, "slave1", "host1", Optional. absent(), Collections. emptyMap(), new String[0], role); + protected Offer createOffer(double cpus, double memory, double disk, Optional role) { + return createOffer(cpus, memory, disk, "slave1", "host1", Optional. absent(), Collections. emptyMap(), new String[0], role); } - protected Offer createOffer(double cpus, double memory, String slave, String host) { - return createOffer(cpus, memory, slave, host, Optional.absent()); + protected Offer createOffer(double cpus, double memory, double disk, String slave, String host) { + return createOffer(cpus, memory, disk, slave, host, Optional.absent()); } - protected Offer createOffer(double cpus, double memory, String slave, String host, Optional rack) { - return createOffer(cpus, memory, slave, host, rack, Collections. emptyMap(), new String[0], Optional.absent()); + protected Offer createOffer(double cpus, double memory, double disk, String slave, String host, Optional rack) { + return createOffer(cpus, memory, disk, slave, host, rack, Collections. emptyMap(), new String[0], Optional.absent()); } - protected Offer createOffer(double cpus, double memory, String slave, String host, Optional rack, Map attributes) { - return createOffer(cpus, memory, slave, host, rack, attributes, new String[0], Optional.absent()); + protected Offer createOffer(double cpus, double memory, double disk, String slave, String host, Optional rack, Map attributes) { + return createOffer(cpus, memory, disk, slave, host, rack, attributes, new String[0], Optional.absent()); } - protected Offer createOffer(double cpus, double memory, String slave, String host, Optional rack, Map attributes, String[] portRanges) { - return createOffer(cpus, memory, slave, host, rack, attributes, portRanges, Optional.absent()); + protected Offer createOffer(double cpus, double memory, double disk, String slave, String host, Optional rack, Map attributes, String[] portRanges) { + return createOffer(cpus, memory, disk, slave, host, rack, attributes, portRanges, Optional.absent()); } - protected Offer createOffer(double cpus, double memory, String slave, String host, Optional rack, Map attributes, String[] portRanges, Optional role) { + protected Offer createOffer(double cpus, double memory, double disk, String slave, String host, Optional rack, Map attributes, String[] portRanges, Optional role) { AgentID slaveId = AgentID.newBuilder().setValue(slave).build(); FrameworkID frameworkId = FrameworkID.newBuilder().setValue("framework1").build(); @@ -247,9 +249,11 @@ protected Offer createOffer(double cpus, double memory, String slave, String hos Resource.Builder cpusResource = Resource.newBuilder().setType(Type.SCALAR).setName(MesosUtils.CPUS).setScalar(Scalar.newBuilder().setValue(cpus)); Resource.Builder memoryResources = Resource.newBuilder().setType(Type.SCALAR).setName(MesosUtils.MEMORY).setScalar(Scalar.newBuilder().setValue(memory)); + Resource.Builder diskResources = Resource.newBuilder().setType(Type.SCALAR).setName(MesosUtils.DISK).setScalar(Scalar.newBuilder().setValue(disk)); if(role.isPresent()) { cpusResource = cpusResource.setRole(role.get()); memoryResources = memoryResources.setRole(role.get()); + diskResources = diskResources.setRole(role.get()); } return Offer.newBuilder() @@ -261,6 +265,7 @@ protected Offer createOffer(double cpus, double memory, String slave, String hos .addAttributes(Attribute.newBuilder().setType(Type.TEXT).setText(Text.newBuilder().setValue(rack.or(configuration.getMesosConfiguration().getDefaultRackId()))).setName(configuration.getMesosConfiguration().getRackIdAttributeKey())) .addResources(cpusResource) .addResources(memoryResources) + .addResources(diskResources) .addResources(MesosUtilsTest.buildPortRanges(portRanges)) .addAllAttributes(attributesList) .build(); @@ -304,9 +309,9 @@ protected SingularityTask prepTask(SingularityRequest request, SingularityDeploy Offer offer; if (separateHosts) { - offer = createOffer(125, 1024, String.format("slave%s", instanceNo), String.format("host%s", instanceNo)); + offer = createOffer(125, 1024, 2048, String.format("slave%s", instanceNo), String.format("host%s", instanceNo)); } else { - offer = createOffer(125, 1024); + offer = createOffer(125, 1024, 2048); } SingularityTaskId taskId = new SingularityTaskId(request.getId(), deploy.getId(), launchTime, instanceNo, offer.getHostname(), "rack1"); @@ -319,7 +324,7 @@ protected SingularityTask prepTask(SingularityRequest request, SingularityDeploy .setName("name") .build(); - SingularityTask task = new SingularityTask(taskRequest, taskId, Collections.singletonList(SingularityMesosOfferObject.fromProtos(offer)), taskInfo, SingularityMesosTaskObject.fromProtos(taskInfo), Optional.of("rack1")); + SingularityTask task = new SingularityTask(taskRequest, taskId, Collections.singletonList(mesosProtosUtils.offerFromProtos(offer)), mesosProtosUtils.taskFromProtos(taskInfo), Optional.of("rack1")); taskManager.savePendingTask(pendingTask); @@ -342,8 +347,8 @@ protected SingularityTask launchTask(SingularityRequest request, SingularityDepl protected void statusUpdate(SingularityTask task, TaskState state, Optional timestamp) { TaskStatus.Builder bldr = TaskStatus.newBuilder() - .setTaskId(task.getMesosTask().getTaskId()) - .setAgentId(task.getAgentId()) + .setTaskId(MesosProtosUtils.toTaskId(task.getMesosTask().getTaskId())) + .setAgentId(MesosProtosUtils.toAgentId(task.getAgentId())) .setState(state); if (timestamp.isPresent()) { @@ -459,7 +464,7 @@ protected SingularityDeploy deployRequest(SingularityRequest request, double cpu .setResources(Optional.of(r)) .build(); - deployResource.deploy(new SingularityDeployRequest(deploy, Optional. absent(), Optional. absent())); + deployResource.deploy(new SingularityDeployRequest(deploy, Optional.absent(), Optional.absent()), singularityUser); return deploy; } @@ -488,7 +493,7 @@ protected SingularityRequest startAndDeploySecondRequest() { SingularityDeploy deploy = new SingularityDeployBuilder(request.getId(), "d1").setCommand(Optional.of("sleep 1")).build(); - deployResource.deploy(new SingularityDeployRequest(deploy, Optional. absent(), Optional. absent())); + deployResource.deploy(new SingularityDeployRequest(deploy, Optional.absent(), Optional.absent()), singularityUser); return request; } @@ -510,7 +515,7 @@ protected void initRequest() { protected void initWithTasks(int num) { initRequest(); - requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(num), Optional. absent(), Optional. absent(), Optional. absent(), Optional.absent(), Optional.absent(), Optional.absent())); + requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(num), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); initFirstDeploy(); @@ -620,8 +625,8 @@ protected SingularityTask startSeparatePlacementTask(SingularityDeploy deploy, i } protected List resourceOffers() { - Offer offer1 = createOffer(20, 20000, "slave1", "host1"); - Offer offer2 = createOffer(20, 20000, "slave2", "host2"); + Offer offer1 = createOffer(20, 20000, 50000, "slave1", "host1"); + Offer offer2 = createOffer(20, 20000, 50000, "slave2", "host2"); List offers = Arrays.asList(offer1, offer2); @@ -633,7 +638,7 @@ protected List resourceOffers() { protected void resourceOffersByNumTasks(int numTasks) { List offers = new ArrayList<>(); for (int i = 1; i <= numTasks; i++) { - offers.add(createOffer(1, 128, String.format("slave%s", i), String.format("host%s", i))); + offers.add(createOffer(1, 128, 1024, String.format("slave%s", i), String.format("host%s", i))); } sms.resourceOffers(offers); } @@ -641,7 +646,7 @@ protected void resourceOffersByNumTasks(int numTasks) { protected void resourceOffers(int numSlaves) { List offers = new ArrayList<>(); for (int i = 1; i <= numSlaves; i++) { - offers.add(createOffer(20, 20000, String.format("slave%s", i), String.format("host%s", i))); + offers.add(createOffer(20, 20000, 50000, String.format("slave%s", i), String.format("host%s", i))); } sms.resourceOffers(offers); } @@ -667,7 +672,7 @@ protected void deploy(String deployId, Optional unpauseOnDeploy, Option .setServiceBasePath(Optional.of("/basepath")) .setLoadBalancerGroups(Optional.of(groups)); } - deployResource.deploy(new SingularityDeployRequest(builder.build(), unpauseOnDeploy, Optional. absent())); + deployResource.deploy(new SingularityDeployRequest(builder.build(), unpauseOnDeploy, Optional.absent()), singularityUser); } protected SingularityPendingTask createAndSchedulePendingTask(String deployId) { @@ -704,12 +709,12 @@ protected void sleep(long millis) { } } - protected void saveLastActiveTaskStatus(SingularityTask task, Optional taskStatus, long millisAdjustment) { + protected void saveLastActiveTaskStatus(SingularityTask task, Optional taskStatus, long millisAdjustment) { taskManager.saveLastActiveTaskStatus(new SingularityTaskStatusHolder(task.getTaskId(), taskStatus, System.currentTimeMillis() + millisAdjustment, serverId, Optional.of("slaveId"))); } - protected SingularityMesosTaskStatusObject buildTaskStatus(SingularityTask task) { - return SingularityMesosTaskStatusObject.fromProtos(TaskStatus.newBuilder().setTaskId(TaskID.newBuilder().setValue(task.getTaskId().getId())).setState(TaskState.TASK_RUNNING).build()); + protected MesosTaskStatusObject buildTaskStatus(SingularityTask task) { + return mesosProtosUtils.taskStatusFromProtos(TaskStatus.newBuilder().setTaskId(TaskID.newBuilder().setValue(task.getTaskId().getId())).setState(TaskState.TASK_RUNNING).build()); } protected SingularityRequest buildRequest(String requestId) { diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySlavePlacementTest.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySlavePlacementTest.java index e409afdd41..a3867f9f03 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySlavePlacementTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySlavePlacementTest.java @@ -34,24 +34,24 @@ public void testSlavePlacementSeparate() { saveAndSchedule(request.toBuilder().setInstances(Optional.of(2)).setSlavePlacement(Optional.of(SlavePlacement.SEPARATE))); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1"), createOffer(20, 20000, "slave1", "host1"))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1"), createOffer(20, 20000, 50000, "slave1", "host1"))); Assert.assertTrue(taskManager.getPendingTaskIds().size() == 1); Assert.assertTrue(taskManager.getActiveTaskIds().size() == 1); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1"))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1"))); Assert.assertTrue(taskManager.getPendingTaskIds().size() == 1); Assert.assertTrue(taskManager.getActiveTaskIds().size() == 1); eventListener.taskHistoryUpdateEvent(new SingularityTaskHistoryUpdate(taskManager.getActiveTaskIds().get(0), System.currentTimeMillis(), ExtendedTaskState.TASK_CLEANING, Optional.absent(), Optional.absent())); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1"))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1"))); Assert.assertTrue(taskManager.getPendingTaskIds().size() == 1); Assert.assertTrue(taskManager.getActiveTaskIds().size() == 1); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave2", "host2"))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave2", "host2"))); Assert.assertTrue(taskManager.getPendingTaskIds().isEmpty()); Assert.assertTrue(taskManager.getActiveTaskIds().size() == 2); @@ -64,20 +64,20 @@ public void testSlavePlacementSpread() { saveAndSchedule(request.toBuilder().setInstances(Optional.of(1)).setSlavePlacement(Optional.of(SlavePlacement.SPREAD_ALL_SLAVES))); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1", Optional.of("rack1")))); // assert one Request on one slave. Assert.assertTrue(slaveManager.getNumObjectsAtState(MachineState.ACTIVE) == 1); Assert.assertTrue(taskManager.getPendingTaskIds().size() == 0); Assert.assertTrue(taskManager.getActiveTaskIds().size() == 1); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave2", "host2"))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave2", "host2"))); Assert.assertTrue(slaveManager.getNumObjectsAtState(MachineState.ACTIVE) == 2); spreadAllPoller.runActionOnPoll(); scheduler.drainPendingQueue(); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave2", "host2"))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave2", "host2"))); // assert Request is spread over the two slaves Assert.assertTrue(taskManager.getPendingTaskIds().size() == 0); @@ -104,32 +104,33 @@ public void testSlavePlacementOptimistic() { initRequest(); initFirstDeploy(); - saveAndSchedule(request.toBuilder().setInstances(Optional.of(7)).setSlavePlacement(Optional.of(SlavePlacement.OPTIMISTIC))); + saveAndSchedule(request.toBuilder().setInstances(Optional.of(20)).setSlavePlacement(Optional.of(SlavePlacement.OPTIMISTIC))); // Default behavior if we don't have info about other hosts that can run this task: be greedy. - sms.resourceOffers(Arrays.asList(createOffer(2, 128 * 2, "slave1", "host1"))); + sms.resourceOffers(Arrays.asList(createOffer(2, 128 * 2, 1024 * 2, "slave1", "host1"))); Assert.assertEquals(2, taskManager.getActiveTaskIds().size()); - // Now that at least one other host is running tasks for this request, we expect an even spread. - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave2", "host2"))); - Assert.assertEquals(5, taskManager.getActiveTaskIds().size()); + // Now that at least one other host is running tasks for this request, we expect an even-ish spread, + // but because we have many tasks pending, we allow quite a bit of unevenness. + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave2", "host2"))); + Assert.assertEquals(13, taskManager.getActiveTaskIds().size()); - // ...and we don't allow a violation of this even spread by refusing to schedule more tasks on host2 (because it's hosting 3/5 tasks). - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave2", "host2"))); - Assert.assertEquals(5, taskManager.getActiveTaskIds().size()); + // ...but now we won't schedule more tasks on host2, because it's hosting a disproportionate number of tasks. + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave2", "host2"))); + Assert.assertEquals(13, taskManager.getActiveTaskIds().size()); - // ...but since host1 is only hosting 2/5 tasks, we will schedule more tasks on it when an offer is received. - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1"))); - Assert.assertEquals(7, taskManager.getActiveTaskIds().size()); + // ...but since host1 is only hosting 2 tasks, we will schedule more tasks on it when an offer is received. + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1"))); + Assert.assertEquals(20, taskManager.getActiveTaskIds().size()); Map> tasksByHost = taskManager.getActiveTaskIdsForRequest(request.getId()).stream() .collect(Collectors.groupingBy(SingularityTaskId::getSanitizedHost)); Assert.assertNotNull(tasksByHost.get("host1")); - Assert.assertEquals(4, tasksByHost.get("host1").size()); + Assert.assertEquals(9, tasksByHost.get("host1").size()); Assert.assertNotNull(tasksByHost.get("host2")); - Assert.assertEquals(3, tasksByHost.get("host2").size()); + Assert.assertEquals(11, tasksByHost.get("host2").size()); } @@ -140,7 +141,7 @@ public void testSlavePlacementGreedy() { saveAndSchedule(request.toBuilder().setInstances(Optional.of(3)).setSlavePlacement(Optional.of(SlavePlacement.GREEDY))); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1"))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1"))); Assert.assertTrue(taskManager.getActiveTaskIds().size() == 3); } @@ -155,11 +156,11 @@ public void testReservedSlaveAttribute() { initFirstDeploy(); saveAndSchedule(request.toBuilder().setInstances(Optional.of(1))); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional.absent(), ImmutableMap.of("reservedKey", "reservedValue1")))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1", Optional.absent(), ImmutableMap.of("reservedKey", "reservedValue1")))); Assert.assertTrue(taskManager.getActiveTaskIds().size() == 0); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave2", "host2", Optional.absent(), ImmutableMap.of("reservedKey", "notAReservedValue")))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave2", "host2", Optional.absent(), ImmutableMap.of("reservedKey", "notAReservedValue")))); Assert.assertTrue(taskManager.getActiveTaskIds().size() == 1); } @@ -175,13 +176,13 @@ public void testReservedSlaveWithMatchinRequestAttribute() { initFirstDeploy(); saveAndSchedule(request.toBuilder().setInstances(Optional.of(1))); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional.absent(), reservedAttributesMap))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1", Optional.absent(), reservedAttributesMap))); Assert.assertTrue(taskManager.getActiveTaskIds().size() == 0); saveAndSchedule(request.toBuilder().setInstances(Optional.of(1)).setRequiredSlaveAttributes(Optional.of(reservedAttributesMap))); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional.absent(), ImmutableMap.of("reservedKey", "reservedValue1")))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1", Optional.absent(), ImmutableMap.of("reservedKey", "reservedValue1")))); Assert.assertTrue(taskManager.getActiveTaskIds().size() == 1); } @@ -199,13 +200,13 @@ public void testAllowedSlaveAttributes() { initFirstDeploy(); saveAndSchedule(request.toBuilder().setInstances(Optional.of(1))); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional.absent(), ImmutableMap.of("reservedKey", "reservedValue1")))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1", Optional.absent(), ImmutableMap.of("reservedKey", "reservedValue1")))); Assert.assertTrue(taskManager.getActiveTaskIds().size() == 0); saveAndSchedule(request.toBuilder().setInstances(Optional.of(1)).setAllowedSlaveAttributes(Optional.of(allowedAttributes))); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional.absent(), ImmutableMap.of("reservedKey", "reservedValue1")))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1", Optional.absent(), ImmutableMap.of("reservedKey", "reservedValue1")))); Assert.assertTrue(taskManager.getActiveTaskIds().size() == 1); } @@ -219,12 +220,12 @@ public void testRequiredSlaveAttributesForRequest() { initFirstDeploy(); saveAndSchedule(request.toBuilder().setInstances(Optional.of(1)).setRequiredSlaveAttributes(Optional.of(requiredAttributes))); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional.absent(), ImmutableMap.of("requiredKey", "notTheRightValue")))); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave2", "host2", Optional.absent(), ImmutableMap.of("notTheRightKey", "requiredValue1")))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1", Optional.absent(), ImmutableMap.of("requiredKey", "notTheRightValue")))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave2", "host2", Optional.absent(), ImmutableMap.of("notTheRightKey", "requiredValue1")))); Assert.assertTrue(taskManager.getActiveTaskIds().size() == 0); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave2", "host2", Optional.absent(), requiredAttributes))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave2", "host2", Optional.absent(), requiredAttributes))); Assert.assertTrue(taskManager.getActiveTaskIds().size() == 1); } @@ -239,12 +240,12 @@ public void testMultipleRequiredAttributes() { initFirstDeploy(); saveAndSchedule(request.toBuilder().setInstances(Optional.of(1)).setRequiredSlaveAttributes(Optional.of(requiredAttributes))); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional.absent(), ImmutableMap.of("requiredKey1", "requiredValue1")))); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave2", "host2", Optional.absent(), ImmutableMap.of("requiredKey1", "requiredValue1", "someotherkey", "someothervalue")))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave1", "host1", Optional.absent(), ImmutableMap.of("requiredKey1", "requiredValue1")))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave2", "host2", Optional.absent(), ImmutableMap.of("requiredKey1", "requiredValue1", "someotherkey", "someothervalue")))); Assert.assertTrue(taskManager.getActiveTaskIds().size() == 0); - sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave2", "host2", Optional.absent(), requiredAttributes))); + sms.resourceOffers(Arrays.asList(createOffer(20, 20000, 50000, "slave2", "host2", Optional.absent(), requiredAttributes))); Assert.assertTrue(taskManager.getActiveTaskIds().size() == 1); } @@ -252,38 +253,38 @@ public void testMultipleRequiredAttributes() { @Test public void testEvenRackPlacement() { // Set up 3 active racks - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1")))); - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave2", "host2", Optional.of("rack2")))); - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave3", "host3", Optional.of("rack3")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave2", "host2", Optional.of("rack2")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave3", "host3", Optional.of("rack3")))); initRequest(); initFirstDeploy(); saveAndSchedule(request.toBuilder().setInstances(Optional.of(7)).setRackSensitive(Optional.of(true))); // rack1 -> 1, rack2 -> 2, rack3 -> 3 - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1")))); - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave2", "host2", Optional.of("rack2")))); - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave3", "host3", Optional.of("rack3")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave2", "host2", Optional.of("rack2")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave3", "host3", Optional.of("rack3")))); Assert.assertEquals(3, taskManager.getActiveTaskIds().size()); - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave1", "host1", Optional.of("rack1")))); Assert.assertEquals(4, taskManager.getActiveTaskIds().size()); - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave1", "host1", Optional.of("rack1")))); Assert.assertEquals(4, taskManager.getActiveTaskIds().size()); - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave2", "host2", Optional.of("rack2")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave2", "host2", Optional.of("rack2")))); Assert.assertEquals(5, taskManager.getActiveTaskIds().size()); // rack1 should not get a third instance until rack3 has a second - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave1", "host1", Optional.of("rack1")))); Assert.assertEquals(5, taskManager.getActiveTaskIds().size()); - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave3", "host3", Optional.of("rack3")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave3", "host3", Optional.of("rack3")))); Assert.assertEquals(6, taskManager.getActiveTaskIds().size()); - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave1", "host1", Optional.of("rack1")))); Assert.assertEquals(7, taskManager.getActiveTaskIds().size()); } @@ -292,21 +293,21 @@ public void testRackPlacementOnScaleDown() { try { configuration.setRebalanceRacksOnScaleDown(true); // Set up 3 active racks - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1")))); - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave2", "host2", Optional.of("rack2")))); - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave3", "host3", Optional.of("rack3")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave2", "host2", Optional.of("rack2")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave3", "host3", Optional.of("rack3")))); initRequest(); initFirstDeploy(); saveAndSchedule(request.toBuilder().setInstances(Optional.of(7)).setRackSensitive(Optional.of(true))); - sms.resourceOffers(Arrays.asList(createOffer(2, 256, "slave1", "host1", Optional.of("rack1")))); - sms.resourceOffers(Arrays.asList(createOffer(2, 256, "slave2", "host2", Optional.of("rack2")))); - sms.resourceOffers(Arrays.asList(createOffer(3, 384, "slave3", "host3", Optional.of("rack3")))); + sms.resourceOffers(Arrays.asList(createOffer(2, 256, 2048, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(2, 256, 2048, "slave2", "host2", Optional.of("rack2")))); + sms.resourceOffers(Arrays.asList(createOffer(3, 384, 3072, "slave3", "host3", Optional.of("rack3")))); Assert.assertEquals(7, taskManager.getActiveTaskIds().size()); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(4)).setRackSensitive(Optional.of(true)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(4)).setRackSensitive(Optional.of(true)).build(), singularityUser); scheduler.drainPendingQueue(); @@ -328,7 +329,7 @@ public void testRackPlacementOnScaleDown() { @Test public void testPlacementOfBounceTasks() { // Set up 1 active rack - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave1", "host1", Optional.of("rack1")))); initRequest(); initFirstDeploy(); @@ -341,11 +342,11 @@ public void testPlacementOfBounceTasks() { saveAndSchedule(newRequest.toBuilder()); scheduler.drainPendingQueue(); - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1")))); - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave2", "host2", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave2", "host2", Optional.of("rack1")))); Assert.assertEquals(2, taskManager.getActiveTaskIds().size()); - requestResource.bounce(requestId, Optional.absent()); + requestResource.bounce(requestId, Optional.absent(), singularityUser); cleaner.drainCleanupQueue(); scheduler.drainPendingQueue(); @@ -354,16 +355,16 @@ public void testPlacementOfBounceTasks() { Assert.assertEquals(taskManager.getCleanupTasks().get(0).getActionId().get(), taskManager.getPendingTasks().get(0).getActionId().get()); // BOUNCE should allow a task to launch on the same host - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave1", "host1", Optional.of("rack1")))); Assert.assertEquals(3, taskManager.getActiveTaskIds().size()); // But not a second one from the same bounce - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave1", "host1", Optional.of("rack1")))); Assert.assertEquals(3, taskManager.getActiveTaskIds().size()); // Other pending type should not allow tasks on same host saveAndSchedule(newRequest.toBuilder().setInstances(Optional.of(2))); - sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1")))); + sms.resourceOffers(Arrays.asList(createOffer(1, 128, 1024, "slave1", "host1", Optional.of("rack1")))); Assert.assertEquals(3, taskManager.getActiveTaskIds().size()); } } diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityTestModule.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityTestModule.java index 1a8164db80..d39694c802 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityTestModule.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityTestModule.java @@ -35,10 +35,12 @@ import com.hubspot.jackson.datatype.protobuf.ProtobufModule; import com.hubspot.mesos.client.MesosClient; import com.hubspot.singularity.SingularityAbort; -import com.hubspot.singularity.SingularityAuthModule; import com.hubspot.singularity.SingularityMainModule; import com.hubspot.singularity.SingularityTestAuthenticator; +import com.hubspot.singularity.auth.SingularityAuthorizationHelper; import com.hubspot.singularity.auth.authenticator.SingularityAuthenticator; +import com.hubspot.singularity.auth.datastore.SingularityAuthDatastore; +import com.hubspot.singularity.auth.datastore.SingularityDisabledAuthDatastore; import com.hubspot.singularity.config.MesosConfiguration; import com.hubspot.singularity.config.SMTPConfiguration; import com.hubspot.singularity.config.SentryConfiguration; @@ -192,14 +194,12 @@ public void configure(Binder binder) { mainBinder.install(new SingularityZkMigrationsModule()); mainBinder.install(new SingularityEventModule(configuration)); - mainBinder.install(Modules.override(new SingularityAuthModule(configuration)) - .with(new Module() { - @Override - public void configure(Binder binder) { - binder.bind(SingularityAuthenticator.class).to(SingularityTestAuthenticator.class); - binder.bind(SingularityTestAuthenticator.class).in(Scopes.SINGLETON); - } - })); + + // Auth module bits + mainBinder.bind(SingularityAuthenticator.class).to(SingularityTestAuthenticator.class); + mainBinder.bind(SingularityAuthDatastore.class).to(SingularityDisabledAuthDatastore.class); + mainBinder.bind(SingularityAuthorizationHelper.class).in(Scopes.SINGLETON); + mainBinder.bind(SingularityTestAuthenticator.class).in(Scopes.SINGLETON); mainBinder.bind(DeployResource.class); mainBinder.bind(RequestResource.class); @@ -226,6 +226,7 @@ private static SingularityConfiguration getSingularityConfigurationForTestingSer MesosConfiguration mc = new MesosConfiguration(); mc.setDefaultCpus(1); mc.setDefaultMemory(128); + mc.setDefaultDisk(1024); config.setMesosConfiguration(mc); config.setSmtpConfiguration(new SMTPConfiguration()); diff --git a/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/IndexViewConfiguration.java b/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/IndexViewConfiguration.java index 5c8e6f3a5f..511b96f0bc 100644 --- a/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/IndexViewConfiguration.java +++ b/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/IndexViewConfiguration.java @@ -6,6 +6,7 @@ public class IndexViewConfiguration { private final UIConfiguration uiConfiguration; private final Integer defaultMemory; private final Integer defaultCpus; + private final Integer defaultDisk; private final Integer slaveHttpPort; private final Optional slaveHttpsPort; private final int bounceExpirationMinutes; @@ -16,10 +17,12 @@ public class IndexViewConfiguration { private final boolean loadBalancingEnabled; private final Optional commonHostnameSuffixToOmit; private final Integer warnIfScheduledJobIsRunningPastNextRunPct; + private final boolean generateAuthHeader; public IndexViewConfiguration(UIConfiguration uiConfiguration, Integer defaultMemory, Integer defaultCpus, + Integer defaultDisk, Integer slaveHttpPort, Optional slaveHttpsPort, int bounceExpirationMinutes, @@ -28,10 +31,12 @@ public IndexViewConfiguration(UIConfiguration uiConfiguration, Optional healthcheckMaxRetries, int startupTimeoutSeconds, boolean loadBalancingEnabled, - Optional commonHostnameSuffixToOmit, Integer warnIfScheduledJobIsRunningPastNextRunPct) { + Optional commonHostnameSuffixToOmit, Integer warnIfScheduledJobIsRunningPastNextRunPct, + boolean generateAuthHeader) { this.uiConfiguration = uiConfiguration; this.defaultMemory = defaultMemory; this.defaultCpus = defaultCpus; + this.defaultDisk = defaultDisk; this.slaveHttpPort = slaveHttpPort; this.slaveHttpsPort = slaveHttpsPort; this.bounceExpirationMinutes = bounceExpirationMinutes; @@ -42,6 +47,7 @@ public IndexViewConfiguration(UIConfiguration uiConfiguration, this.loadBalancingEnabled = loadBalancingEnabled; this.commonHostnameSuffixToOmit = commonHostnameSuffixToOmit; this.warnIfScheduledJobIsRunningPastNextRunPct = warnIfScheduledJobIsRunningPastNextRunPct; + this.generateAuthHeader = generateAuthHeader; } public UIConfiguration getUiConfiguration() { @@ -52,6 +58,10 @@ public Integer getDefaultMemory() { return defaultMemory; } + public Integer getDefaultDisk() { + return defaultDisk; + } + public Integer getDefaultCpus() { return defaultCpus; } @@ -95,4 +105,8 @@ public Optional getCommonHostnameSuffixToOmit() { public Integer getWarnIfScheduledJobIsRunningPastNextRunPct() { return warnIfScheduledJobIsRunningPastNextRunPct; } + + public boolean isGenerateAuthHeader() { + return generateAuthHeader; + } } diff --git a/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/UIConfiguration.java b/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/UIConfiguration.java index c136e2bc41..0045f38c2b 100644 --- a/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/UIConfiguration.java +++ b/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/UIConfiguration.java @@ -56,7 +56,7 @@ public static RootUrlMode parse(String value) { @JsonProperty @NotNull - private boolean showTaskDiskResource = false; + private boolean showTaskDiskResource = true; @JsonProperty @NotNull @@ -90,6 +90,21 @@ public static RootUrlMode parse(String value) { @JsonProperty private Optional extraScript = Optional.absent(); + @JsonProperty + private String authTokenKey = "token"; + + @JsonProperty + @NotNull + private String authCookieName = ""; + + @JsonProperty + private Optional apiRootOverride = Optional.absent(); + + @JsonProperty + private Optional appRootOverride = Optional.absent(); + + @JsonProperty + private Optional staticRootOverride = Optional.absent(); public boolean isHideNewDeployButton() { return hideNewDeployButton; @@ -226,4 +241,44 @@ public Optional getExtraScript() { public void setExtraScript(Optional extraScript) { this.extraScript = extraScript; } + + public String getAuthTokenKey() { + return authTokenKey; + } + + public void setAuthTokenKey(String authTokenKey) { + this.authTokenKey = authTokenKey; + } + + public String getAuthCookieName() { + return authCookieName; + } + + public void setAuthCookieName(String authCookieName) { + this.authCookieName = authCookieName; + } + + public Optional getApiRootOverride() { + return apiRootOverride; + } + + public void setApiRootOverride(Optional apiRootOverride) { + this.apiRootOverride = apiRootOverride; + } + + public Optional getAppRootOverride() { + return appRootOverride; + } + + public void setAppRootOverride(Optional appRootOverride) { + this.appRootOverride = appRootOverride; + } + + public Optional getStaticRootOverride() { + return staticRootOverride; + } + + public void setStaticRootOverride(Optional staticRootOverride) { + this.staticRootOverride = staticRootOverride; + } } diff --git a/SingularityServiceBase/src/main/java/com/hubspot/singularity/views/IndexView.java b/SingularityServiceBase/src/main/java/com/hubspot/singularity/views/IndexView.java index 9a8e562db3..32f2c03938 100644 --- a/SingularityServiceBase/src/main/java/com/hubspot/singularity/views/IndexView.java +++ b/SingularityServiceBase/src/main/java/com/hubspot/singularity/views/IndexView.java @@ -22,6 +22,7 @@ public class IndexView extends View { private final Integer defaultMemory; private final Integer defaultCpus; + private final Integer defaultDisk; private final Boolean hideNewDeployButton; private final Boolean hideNewRequestButton; @@ -61,6 +62,10 @@ public class IndexView extends View { private final String extraScript; + private final boolean generateAuthHeader; + private final String authCookieName; + private final String authTokenKey; + public IndexView(String singularityUriBase, String appRoot, IndexViewConfiguration configuration, ObjectMapper mapper) { super("index.mustache"); @@ -70,10 +75,10 @@ public IndexView(String singularityUriBase, String appRoot, IndexViewConfigurati String rawAppRoot = String.format("%s%s", singularityUriBase, appRoot); - this.appRoot = (rawAppRoot.endsWith("/")) ? rawAppRoot.substring(0, rawAppRoot.length() - 1) : rawAppRoot; - this.staticRoot = String.format("%s/static", singularityUriBase); + this.appRoot = uiConfiguration.getAppRootOverride().or((rawAppRoot.endsWith("/")) ? rawAppRoot.substring(0, rawAppRoot.length() - 1) : rawAppRoot); + this.staticRoot = uiConfiguration.getStaticRootOverride().or(String.format("%s/static", singularityUriBase)); this.apiDocs = String.format("%s/api-docs/", singularityUriBase); - this.apiRoot = String.format("%s%s", singularityUriBase, ApiPaths.API_BASE_PATH); + this.apiRoot = uiConfiguration.getApiRootOverride().or(String.format("%s%s", singularityUriBase, ApiPaths.API_BASE_PATH)); this.title = uiConfiguration.getTitle(); @@ -82,6 +87,7 @@ public IndexView(String singularityUriBase, String appRoot, IndexViewConfigurati this.defaultCpus = configuration.getDefaultCpus(); this.defaultMemory = configuration.getDefaultMemory(); + this.defaultDisk = configuration.getDefaultDisk(); this.hideNewDeployButton = uiConfiguration.isHideNewDeployButton(); this.hideNewRequestButton = uiConfiguration.isHideNewRequestButton(); @@ -122,6 +128,10 @@ public IndexView(String singularityUriBase, String appRoot, IndexViewConfigurati this.timestampWithSecondsFormat = uiConfiguration.getTimestampWithSecondsFormat(); this.extraScript = uiConfiguration.getExtraScript().orNull(); + + this.generateAuthHeader = configuration.isGenerateAuthHeader(); + this.authCookieName = uiConfiguration.getAuthCookieName(); + this.authTokenKey = uiConfiguration.getAuthTokenKey(); } public String getAppRoot() { @@ -164,6 +174,10 @@ public Integer getDefaultCpus() { return defaultCpus; } + public Integer getDefaultDisk() { + return defaultDisk; + } + public Boolean getHideNewDeployButton() { return hideNewDeployButton; } @@ -244,6 +258,18 @@ public boolean isShortenSlaveUsageHostname() { return shortenSlaveUsageHostname; } + public boolean isGenerateAuthHeader() { + return generateAuthHeader; + } + + public String getAuthCookieName() { + return authCookieName; + } + + public String getAuthTokenKey() { + return authTokenKey; + } + @Override public String toString() { return "IndexView{" + @@ -254,6 +280,7 @@ public String toString() { ", navColor='" + navColor + '\'' + ", defaultMemory=" + defaultMemory + ", defaultCpus=" + defaultCpus + + ", defaultDisk=" + defaultDisk + ", hideNewDeployButton=" + hideNewDeployButton + ", hideNewRequestButton=" + hideNewRequestButton + ", loadBalancingEnabled=" + loadBalancingEnabled + @@ -277,6 +304,9 @@ public String toString() { ", timestampWithSecondsFormat='" + timestampWithSecondsFormat + '\'' + ", redirectOnUnauthorizedUrl='" + redirectOnUnauthorizedUrl + '\'' + ", extraScript='" + extraScript + '\'' + + ", generateAuthHeader=" + generateAuthHeader + + ", authCookieName='" + authCookieName + '\'' + + ", authTokenKey='" + authTokenKey + '\'' + "} " + super.toString(); } } diff --git a/SingularityUI/SingularityTailer/package.json b/SingularityUI/SingularityTailer/package.json index 7f21b09593..4bda86036b 100644 --- a/SingularityUI/SingularityTailer/package.json +++ b/SingularityUI/SingularityTailer/package.json @@ -1,6 +1,6 @@ { "name": "singularityui-tailer", - "version": "0.3.11", + "version": "0.3.13", "description": "A robust log tailer", "author": "Danny Wolf ", "contributors": [ diff --git a/SingularityUI/SingularityTailer/src/actions/index.js b/SingularityUI/SingularityTailer/src/actions/index.js index f07192e03d..0aac3d836a 100644 --- a/SingularityUI/SingularityTailer/src/actions/index.js +++ b/SingularityUI/SingularityTailer/src/actions/index.js @@ -136,6 +136,12 @@ export const tailIntervalMs = (tailIntervalMs) => ({ tailIntervalMs }); +export const SET_AUTHORIZATION_HEADER = `${frameworkName}_SET_AUTHORIZATION_HEADER`; +export const setAuthorizationHeader = (authorizationHeader) => ({ + type: SET_AUTHORIZATION_HEADER, + authorizationHeader +}); + export const sandboxFetchChunk = (id, taskId, path, start, end, config) => { return (dispatch) => { dispatch( @@ -146,7 +152,12 @@ export const sandboxFetchChunk = (id, taskId, path, start, end, config) => { const query = `?path=${path}&offset=${start}&length=${end - start}`; const apiPath = `${apiRoot}/sandbox/${taskId}/read${query}`; - return fetch(apiPath, {credentials: 'include'}) + const options = {credentials: 'include'} + if (config.authorizationHeader) { + options['headers'] = {'Authorization': config.authorizationHeader} + } + + return fetch(apiPath, options) .then((r) => {return checkStatus(r, taskId)}) .then(parseJSON) .then(({data, offset}) => { @@ -185,7 +196,12 @@ export const sandboxFetchLength = (id, taskId, path, config) => { const query = `?path=${path}&length=${0}`; const apiPath = `${apiRoot}/sandbox/${taskId}/read${query}`; - return fetch(apiPath, {credentials: 'include'}) + const options = {credentials: 'include'} + if (config.authorizationHeader) { + options['headers'] = {'Authorization': config.authorizationHeader} + } + + return fetch(apiPath, options) .then((r) => {return checkStatus(r, taskId)}) .then(parseJSON) .then(({offset}) => { @@ -247,7 +263,12 @@ export const blazarLogFetchChunk = (id, buildId, start, end, config) => { const query = `?offset=${start}&length=${end - start}`; const apiPath = `${apiRoot}/modules/builds/${buildId}/log${query}`; - return fetch(apiPath, {credentials: 'include'}) + const options = {credentials: 'include'} + if (config.authorizationHeader) { + options['headers'] = {'Authorization': config.authorizationHeader} + } + + return fetch(apiPath, options) .then(checkStatus) .then(parseJSON) .then(({data, offset}) => { @@ -285,7 +306,12 @@ export const blazarLogFetchLength = (id, buildId, config) => { const apiRoot = config.blazarApiRoot; const apiPath = `${apiRoot}/modules/builds/${buildId}/log/size`; - return fetch(apiPath, {credentials: 'include'}) + const options = {credentials: 'include'} + if (config.authorizationHeader) { + options['headers'] = {'Authorization': config.authorizationHeader} + } + + return fetch(apiPath, options) .then(checkStatus) .then(parseJSON) .then(({size}) => { diff --git a/SingularityUI/SingularityTailer/src/reducers/config.js b/SingularityUI/SingularityTailer/src/reducers/config.js index edb2c5a4a2..52101ac6b1 100644 --- a/SingularityUI/SingularityTailer/src/reducers/config.js +++ b/SingularityUI/SingularityTailer/src/reducers/config.js @@ -3,7 +3,8 @@ import { BLAZAR_SET_API_ROOT, TOGGLE_ANSI_COLORING, TOGGLE_FETCH_OVERSCAN, - SET_TAIL_INTERVAL_MS + SET_TAIL_INTERVAL_MS, + SET_AUTHORIZATION_HEADER } from '../actions'; const initialState = { @@ -39,6 +40,11 @@ const configReducer = (state = initialState, action) => { ...state, tailIntervalMs: action.tailIntervalMs } + case SET_AUTHORIZATION_HEADER: + return { + ...state, + authorizationHeader: action.authorizationHeader + } default: return state; } diff --git a/SingularityUI/app/actions/api/base.es6 b/SingularityUI/app/actions/api/base.es6 index b80338a75f..2e4e55a858 100644 --- a/SingularityUI/app/actions/api/base.es6 +++ b/SingularityUI/app/actions/api/base.es6 @@ -76,6 +76,12 @@ export function buildApiAction(actionName, opts = {}, keyFunc = undefined) { userParam = `?user=${localStorage.getItem('singularityUserId')}` } } + + if (config.generateAuthHeader) { + options.headers = options.headers || {}; + options.headers.Authorization = Utils.getAuthTokenHeader(); + } + return fetch(config.apiRoot + options.url + userParam, _.extend({credentials: 'include'}, _.omit(options, 'url'))) .then(response => { apiResponse = response; diff --git a/SingularityUI/app/actions/api/history.es6 b/SingularityUI/app/actions/api/history.es6 index 75bfb5283f..1a3c8e65fd 100644 --- a/SingularityUI/app/actions/api/history.es6 +++ b/SingularityUI/app/actions/api/history.es6 @@ -101,7 +101,7 @@ export const FetchRequestArgHistory = buildApiAction( 'FETCH_REQUEST_ARG_HISTORY', (requestId) => ({ url: `/history/request/${requestId}/command-line-args`, - catchStatusCodes: [400, 404] + catchStatusCodes: [400, 404, 500] }), (requestId) => requestId ); diff --git a/SingularityUI/app/assets/index.mustache b/SingularityUI/app/assets/index.mustache index 364b080e4f..1ac25dffc1 100644 --- a/SingularityUI/app/assets/index.mustache +++ b/SingularityUI/app/assets/index.mustache @@ -25,6 +25,7 @@ loadBalancingEnabled: {{{loadBalancingEnabled}}}, defaultCpus: {{{defaultCpus}}}, defaultMemory: {{{defaultMemory}}}, + defaultDisk: {{{defaultDisk}}}, defaultBounceExpirationMinutes: {{{defaultBounceExpirationMinutes}}}, defaultHealthcheckIntervalSeconds: {{{defaultHealthcheckIntervalSeconds}}}, defaultHealthcheckTimeoutSeconds: {{{defaultHealthcheckTimeoutSeconds}}}, @@ -47,7 +48,10 @@ shortenSlaveUsageHostname : {{{shortenSlaveUsageHostname}}}, redirectOnUnauthorizedUrl: "{{{redirectOnUnauthorizedUrl}}}", globalRefreshInterval: 60000, - sentryDsn: "{{{sentryDsn}}}" + sentryDsn: "{{{sentryDsn}}}", + generateAuthHeader: {{{generateAuthHeader}}}, + authCookieName: "{{{ authCookieName }}}", + authTokenKey: "{{{ authTokenKey }}}" }; diff --git a/SingularityUI/app/components/newDeployForm/NewDeployForm.jsx b/SingularityUI/app/components/newDeployForm/NewDeployForm.jsx index c22b290183..151023810c 100644 --- a/SingularityUI/app/components/newDeployForm/NewDeployForm.jsx +++ b/SingularityUI/app/components/newDeployForm/NewDeployForm.jsx @@ -1279,6 +1279,7 @@ class NewDeployForm extends Component { onChange={event => this.updateField('diskMb', event.target.value)} value={this.props.form.diskMb} label="Disk (MB)" + placeholder={`default: ${config.defaultDisk}`} feedback={this.formFieldFeedback(INDEXED_FIELDS.diskMb, this.props.form.diskMb)} /> ); diff --git a/SingularityUI/app/components/newDeployForm/fields.es6 b/SingularityUI/app/components/newDeployForm/fields.es6 index 60b1c95e5d..f44f067e8f 100644 --- a/SingularityUI/app/components/newDeployForm/fields.es6 +++ b/SingularityUI/app/components/newDeployForm/fields.es6 @@ -15,7 +15,7 @@ export const FIELDS = { {id: 'cpus', type: 'number', default: 1}, {id: 'memoryMb', type: 'number', default: 128}, {id: 'numPorts', type: 'number', default: 0}, - {id: 'diskMb', type: 'number'} + {id: 'diskMb', type: 'number', default: 1024} ] } ], diff --git a/SingularityUI/app/components/taskDetail/TaskS3Logs.jsx b/SingularityUI/app/components/taskDetail/TaskS3Logs.jsx index 0076812e21..2d3263814e 100644 --- a/SingularityUI/app/components/taskDetail/TaskS3Logs.jsx +++ b/SingularityUI/app/components/taskDetail/TaskS3Logs.jsx @@ -1,67 +1,118 @@ -import React, { PropTypes } from 'react'; +import React, { PropTypes, Component } from 'react'; import Utils from '../../utils'; import Section from '../common/Section'; import UITable from '../common/table/UITable'; import Column from '../common/table/Column'; import { Glyphicon } from 'react-bootstrap'; +import { groupBy } from 'underscore'; -function TaskS3Logs (props) { - return ( -
- s3File.key} - rowChunkSize={5} - paginated={true} - > - ( - - {Utils.trimS3File(s3File.key.substring(s3File.key.lastIndexOf('/') + 1), props.taskId)} - - )} - /> - Utils.humanizeFileSize(s3File.size)} - /> - Utils.absoluteTimestampWithSeconds(s3File.lastModified)} - /> - (s3File.startTime) ? Utils.absoluteTimestampWithSeconds(s3File.startTime) : Utils.absoluteTimestampWithSeconds(props.taskStartedAt)} - /> - (s3File.endTime) ? Utils.absoluteTimestampWithSeconds(s3File.endTime) : Utils.absoluteTimestampWithSeconds(s3File.lastModified)} - /> - ( - - +class TaskS3Logs extends Component { + constructor(props) { + super(props); + this.state = { + viewingGroup: null + }; + } + + getFileType(s3File) { + return s3File.key.split('/')[0]; + } + + renderTable(s3Files) { + const { taskStartedAt, taskId } = this.props; + return ( +
+
+ this.setState({ viewingGroup: null })}> + + Back + + {this.state.viewingGroup} +
+ s3File.key} + rowChunkSize={5} + paginated={true} + > + ( + + {Utils.trimS3File(s3File.key.substring(s3File.key.lastIndexOf('/') + 1), taskId)} + + )} + /> + Utils.humanizeFileSize(s3File.size)} + /> + Utils.absoluteTimestampWithSeconds(s3File.lastModified)} + /> + (s3File.startTime) ? Utils.absoluteTimestampWithSeconds(s3File.startTime) : Utils.absoluteTimestampWithSeconds(taskStartedAt)} + /> + (s3File.endTime) ? Utils.absoluteTimestampWithSeconds(s3File.endTime) : Utils.absoluteTimestampWithSeconds(s3File.lastModified)} + /> + ( + + + + )} + /> + +
+ ); + } + + renderFolders(groups) { + return ( +
- ); + + ))} + + ); + } + + render() { + const { s3Files } = this.props; + const groupedFiles = groupBy(s3Files, this.getFileType); + + return ( +
+ {this.state.viewingGroup + ? this.renderTable(groupedFiles[this.state.viewingGroup]) + : this.renderFolders(Object.keys(groupedFiles)) + } +
+ ); + } } TaskS3Logs.propTypes = { diff --git a/SingularityUI/app/containers/CustomLogTailerContainer.jsx b/SingularityUI/app/containers/CustomLogTailerContainer.jsx index f24aa555b5..538940aadb 100644 --- a/SingularityUI/app/containers/CustomLogTailerContainer.jsx +++ b/SingularityUI/app/containers/CustomLogTailerContainer.jsx @@ -28,5 +28,5 @@ class CustomLogTailerContainer extends React.Component { }; export default connect(null, { - setTailerGroups + setTailerGroups, })(CustomLogTailerContainer); diff --git a/SingularityUI/app/initialize.jsx b/SingularityUI/app/initialize.jsx index 34b7803d6d..6e557c6a0b 100644 --- a/SingularityUI/app/initialize.jsx +++ b/SingularityUI/app/initialize.jsx @@ -61,6 +61,9 @@ document.addEventListener('DOMContentLoaded', () => { const store = configureStore({}, history); store.dispatch(tailerActions.sandboxSetApiRoot(config.apiRoot)); + if (config.generateAuthHeader) { + store.dispatch(tailerActions.setAuthorizationHeader(Utils.getAuthTokenHeader())); + } // set up user let userId; diff --git a/SingularityUI/app/utils.es6 b/SingularityUI/app/utils.es6 index 8a8a57e0e7..37bfdd778a 100644 --- a/SingularityUI/app/utils.es6 +++ b/SingularityUI/app/utils.es6 @@ -473,6 +473,19 @@ const Utils = { } } return array.join('&'); + }, + + getAuthTokenHeader() { + if (!config.authCookieName) { + return null; + } + const encodedKey = encodeURIComponent(config.authCookieName).replace(/[\-\.\+\*]/g, '\\$&'); + const authCookie = decodeURIComponent(document.cookie.replace(new RegExp(`(?:(?:^|.*;)\\s*${encodedKey}\\s*\\=\\s*([^;]*).*$)|^.*$`), '$1')) || null; + if (!authCookie) { + return ''; + } + const authToken = JSON.parse(authCookie)[config.authTokenKey]; + return `Bearer ${ authToken }`; } }; diff --git a/SingularityUI/gulpfile.js b/SingularityUI/gulpfile.js index 2d320475cd..a5ea7f8453 100644 --- a/SingularityUI/gulpfile.js +++ b/SingularityUI/gulpfile.js @@ -24,12 +24,13 @@ var templateData = { navColor: process.env.SINGULARITY_NAV_COLOR, defaultCpus: process.env.SINGUALRITY_DEFAULT_CPUS || 1, defaultMemory: process.env.SINGULARITY_DEFAULT_MEMORY || 128, + defaultDisk: process.env.SINGULARITY_DEFAULT_DISK || 1024, defaultBounceExpirationMinutes: process.env.SINGULARITY_DEFAULT_BOUNCE_EXPIRATION_MINUTES || 60, defaultHealthcheckIntervalSeconds: process.env.SINGULARITY_DEFAULT_HEALTHCHECK_INTERVAL_SECONDS || 5, defaultHealthcheckTimeoutSeconds: process.env.SINGULARITY_HEALTHCHECK_TIMEOUT_SECONDS || 5, defaultStartupTimeoutSeconds: process.env.SINGULARITY_DEFAULT_STARTUP_TIMEOUT_SECONDS || 60, defaultHealthcheckMaxRetries: process.env.SINGULARITY_HEALTHCHECK_MAX_RETRIES || 0, - showTaskDiskResource: process.env.SINGULARITY_SHOW_TASK_DISK_RESOURCE || 'false', + showTaskDiskResource: process.env.SINGULARITY_SHOW_TASK_DISK_RESOURCE || 'true', hideNewDeployButton: process.env.SINGULARITY_HIDE_NEW_DEPLOY_BUTTON || 'false', hideNewRequestButton: process.env.SINGULARITY_HIDE_NEW_REQUEST_BUTTON || 'false', loadBalancingEnabled: process.env.SINGULARITY_LOAD_BALANCING_ENABLED || 'false', @@ -44,7 +45,10 @@ var templateData = { timestampWithSecondsFormat: process.env.SINGULARITY_TIMESTAMP_WITH_SECONDS_FORMAT || 'lll:ss', redirectOnUnauthorizedUrl: process.env.SINGULARITY_REDIRECT_ON_UNAUTHORIZED_URL || '', extraScript: process.env.SINGULARITY_EXTRA_SCRIPT || '', - sentryDsn: process.env.SINGULARITY_SENTRY_DSN || '' + sentryDsn: process.env.SINGULARITY_SENTRY_DSN || '', + generateAuthHeader: process.env.SINGULARITY_GENERATE_AUTH_HEADER || 'false', + authTokenKey: process.env.SINGULARITY_AUTH_TOKEN_KEY || 'token', + authCookieName: process.env.SINGULARITY_AUTH_COOKIE_NAME || '' }; var dest = path.resolve(__dirname, 'dist'); diff --git a/SingularityUI/package.json b/SingularityUI/package.json index 76db4cc4bd..2369cf5764 100644 --- a/SingularityUI/package.json +++ b/SingularityUI/package.json @@ -62,7 +62,7 @@ "redux-thunk": "^2.0.1", "reselect": "^3.0.1", "select2": "^4.0.3", - "singularityui-tailer": "0.3.11", + "singularityui-tailer": "0.3.13", "typeahead.js": "^0.11.1", "underscore": "~1.8.0", "uuid": "^3.0.0" diff --git a/pom.xml b/pom.xml index 380e741bf8..39589ce238 100644 --- a/pom.xml +++ b/pom.xml @@ -70,7 +70,6 @@ SingularityServiceIntegrationTests SingularityUI SingularityServiceBase - SingularityClusterCoordinator @@ -160,6 +159,12 @@ ${dropwizard.guicier.version}
+ + io.dropwizard + dropwizard-auth + ${dropwizard.version} + + com.hubspot.jackson jackson-jaxrs-propertyfiltering