Skip to content

Commit

Permalink
Merge 561f743 into b9581ee
Browse files Browse the repository at this point in the history
  • Loading branch information
tgianos committed Feb 21, 2020
2 parents b9581ee + 561f743 commit 0da307a
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 1 deletion.
1 change: 1 addition & 0 deletions genie-web/src/docs/asciidoc/api/commands/_api.adoc
Expand Up @@ -32,3 +32,4 @@ include::_addClusterCriterionForCommand.adoc[]
include::_setClusterCriteriaForCommand.adoc[]
include::_addClusterCriterionWithPriorityForCommand.adoc[]
include::_removeClusterCriterionFromCommand.adoc[]
include::_resolveClustersForCommandClusterCriterion.adoc[]
Expand Up @@ -6,7 +6,7 @@ Search the clusters that the given command is linked to.

==== Endpoint

`GET /api/v3/applications/{id}/commands`
`GET /api/v3/commands/{id}/clusters`

:snippet-base: {snippets}/command-rest-controller-integration-test/can-get-clusters-for-command/8
:id-base: get-clusters-for-command
Expand Down
@@ -0,0 +1,26 @@
=== Resolve Clusters for Command Criterion

==== Description

For a given `Command` retrieve the `Criterion` identified by `priority` and attempt to
resolve all the `Cluster's` this `Criterion` would currently match within the database.

==== Endpoint

`GET /api/v3/commands/{id}/clusterCriteria/{priority}`

:snippet-base: {snippets}/command-rest-controller-integration-test/test-resolve-clusters-for-command-cluster-criterion/5
:id-base: resolve-clusters-for-command-cluster-criterion
:!request-headers:
:request-path-params: {snippet-base}/path-parameters.adoc
:request-query-params: {snippet-base}/request-parameters.adoc
:!request-fields:
:curl-request: {snippet-base}/curl-request.adoc
:httpie-request: {snippet-base}/httpie-request.adoc
:response-headers: {snippet-base}/response-headers.adoc
:response-fields: {snippet-base}/response-fields.adoc
:!response-links:
:http-request: {snippet-base}/http-request.adoc
:http-response: {snippet-base}/http-response.adoc

include::../_apiTemplate.adoc[]
Expand Up @@ -1989,6 +1989,138 @@ public void testRemoveClusterCriterionFromCommand() throws Exception {
.statusCode(HttpStatus.NOT_FOUND.value());
}

/**
* Test to make sure we can resolve clusters for a command given a criterion.
*
* @throws Exception on unexpected error
*/
@Test
public void testResolveClustersForCommandClusterCriterion() throws Exception {
final Cluster cluster0 = new Cluster.Builder(
UUID.randomUUID().toString(),
UUID.randomUUID().toString(),
UUID.randomUUID().toString(),
ClusterStatus.UP
)
.withTags(Sets.newHashSet(UUID.randomUUID().toString(), UUID.randomUUID().toString()))
.build();
final Cluster cluster1 = new Cluster.Builder(
cluster0.getName(),
cluster0.getUser(),
cluster0.getVersion(),
ClusterStatus.OUT_OF_SERVICE
)
.withTags(cluster0.getTags())
.build();
final Cluster cluster2 = new Cluster.Builder(
UUID.randomUUID().toString(),
UUID.randomUUID().toString(),
UUID.randomUUID().toString(),
ClusterStatus.UP
)
.withTags(Sets.newHashSet(UUID.randomUUID().toString(), UUID.randomUUID().toString()))
.build();
final String cluster0Id = this.createConfigResource(cluster0, null);
final String cluster1Id = this.createConfigResource(cluster1, null);
final String cluster2Id = this.createConfigResource(cluster2, null);

final Command command = new Command
.Builder(NAME, USER, VERSION, CommandStatus.ACTIVE, EXECUTABLE_AND_ARGS, CHECK_DELAY)
.withClusterCriteria(
Lists.newArrayList(
new Criterion.Builder().withId(cluster0Id).build(),
new Criterion.Builder().withName(cluster1.getName()).build(),
new Criterion.Builder().withVersion(cluster2.getVersion()).build(),
new Criterion.Builder().withStatus(ClusterStatus.TERMINATED.name()).build(),
new Criterion.Builder().withTags(cluster2.getTags()).build()
)
)
.build();
final String commandId = this.createConfigResource(command, null);

final RestDocumentationFilter resolveFilter = RestAssuredRestDocumentation.document(
"{class-name}/{method-name}/{step}/",
// Path parameters
Snippets
.ID_PATH_PARAM
.and(
RequestDocumentation
.parameterWithName("priority")
.description("Priority of the criterion to insert")
),
// Request parameters
RequestDocumentation.requestParameters(
RequestDocumentation
.parameterWithName("addDefaultStatus")
.description(
"Whether the system should add the default cluster status to the criterion. Default: true"
)
.optional()
),
// Response Content Type
Snippets.HAL_CONTENT_TYPE_HEADER,
// Response Fields
Snippets.resolveClustersForCommandClusterCriterionResponsePayload()
);

List<String> clusterIds = RestAssured
.given(this.getRequestSpecification())
.filter(resolveFilter)
.param("addDefaultStatus", "false")
.when()
.port(this.port)
.get(COMMANDS_API + "/{id}/clusterCriteria/{priority}/clusters", commandId, 1)
.then()
.statusCode(HttpStatus.OK.value())
.contentType(Matchers.containsString(MediaTypes.HAL_JSON_VALUE))
.extract()
.jsonPath()
.getList("id", String.class);

Assertions.assertThat(clusterIds).hasSize(2).containsExactlyInAnyOrder(cluster0Id, cluster1Id);

clusterIds = RestAssured
.given(this.getRequestSpecification())
.filter(resolveFilter)
.when()
.port(this.port)
.get(COMMANDS_API + "/{id}/clusterCriteria/{priority}/clusters", commandId, 1)
.then()
.statusCode(HttpStatus.OK.value())
.contentType(Matchers.containsString(MediaTypes.HAL_JSON_VALUE))
.extract()
.jsonPath()
.getList("id", String.class);

Assertions.assertThat(clusterIds).hasSize(1).containsExactlyInAnyOrder(cluster0Id);

clusterIds = RestAssured
.given(this.getRequestSpecification())
.when()
.port(this.port)
.get(COMMANDS_API + "/{id}/clusterCriteria/{priority}/clusters", commandId, 3)
.then()
.statusCode(HttpStatus.OK.value())
.contentType(Matchers.containsString(MediaTypes.HAL_JSON_VALUE))
.extract()
.jsonPath()
.getList("id", String.class);
Assertions.assertThat(clusterIds).isEmpty();

clusterIds = RestAssured
.given(this.getRequestSpecification())
.when()
.port(this.port)
.get(COMMANDS_API + "/{id}/clusterCriteria/{priority}/clusters", commandId, 4)
.then()
.statusCode(HttpStatus.OK.value())
.contentType(Matchers.containsString(MediaTypes.HAL_JSON_VALUE))
.extract()
.jsonPath()
.getList("id", String.class);
Assertions.assertThat(clusterIds).containsExactlyInAnyOrder(cluster2Id);
}

private String createCommandWithDefaultClusterCriteria() throws Exception {
return this.createConfigResource(
new Command.Builder(NAME, USER, VERSION, CommandStatus.ACTIVE, EXECUTABLE_AND_ARGS, CHECK_DELAY)
Expand Down
Expand Up @@ -717,6 +717,33 @@ static ResponseFieldsSnippet getClusterCriteriaForCommandResponsePayload() {
.andWithPrefix("[].", getCriterionFieldDescriptors());
}

static ResponseFieldsSnippet resolveClustersForCommandClusterCriterionResponsePayload() {
return PayloadDocumentation
.responseFields(
PayloadDocumentation
.fieldWithPath("[]")
.attributes(EMPTY_CONSTRAINTS)
.description("The set of resolved clusters")
.type(JsonFieldType.ARRAY)
.optional()
)
.andWithPrefix(
"[].",
ArrayUtils.add(
getClusterFieldDescriptors(),
PayloadDocumentation
.subsectionWithPath("links")
.attributes(
Attributes
.key(CONSTRAINTS)
.value("")
)
.description("<<_hateoas,Links>> to other resources.")
.ignored()
)
);
}

private static FieldDescriptor[] getApplicationFieldDescriptors() {
return ArrayUtils.addAll(
getConfigFieldDescriptors(APPLICATION_CONSTRAINTS),
Expand Down
Expand Up @@ -36,6 +36,7 @@
import com.netflix.genie.web.apis.rest.v3.hateoas.assemblers.ClusterModelAssembler;
import com.netflix.genie.web.apis.rest.v3.hateoas.assemblers.CommandModelAssembler;
import com.netflix.genie.web.apis.rest.v3.hateoas.assemblers.EntityModelAssemblers;
import com.netflix.genie.web.data.services.ClusterPersistenceService;
import com.netflix.genie.web.data.services.CommandPersistenceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -90,6 +91,7 @@
public class CommandRestController {

private final CommandPersistenceService commandPersistenceService;
private final ClusterPersistenceService clusterPersistenceService;
private final CommandModelAssembler commandModelAssembler;
private final ApplicationModelAssembler applicationModelAssembler;
private final ClusterModelAssembler clusterModelAssembler;
Expand All @@ -98,14 +100,17 @@ public class CommandRestController {
* Constructor.
*
* @param commandPersistenceService The command configuration service to use.
* @param clusterPersistenceService The {@link ClusterPersistenceService} implementation to use
* @param entityModelAssemblers The encapsulation of all available V3 resource assemblers
*/
@Autowired
public CommandRestController(
final CommandPersistenceService commandPersistenceService,
final ClusterPersistenceService clusterPersistenceService,
final EntityModelAssemblers entityModelAssemblers
) {
this.commandPersistenceService = commandPersistenceService;
this.clusterPersistenceService = clusterPersistenceService;
this.commandModelAssembler = entityModelAssemblers.getCommandModelAssembler();
this.applicationModelAssembler = entityModelAssemblers.getApplicationModelAssembler();
this.clusterModelAssembler = entityModelAssemblers.getClusterModelAssembler();
Expand Down Expand Up @@ -788,4 +793,42 @@ public void removeClusterCriterionFromCommand(
log.info("Called to remove the criterion from command {} with priority {}", id, priority);
this.commandPersistenceService.removeClusterCriterionForCommand(id, priority);
}

/**
* For a given {@link Command} retrieve the {@link Criterion} identified by {@literal priority} and attempt to
* resolve all the {@link Cluster}'s this {@link Criterion} would currently match within the database.
*
* @param id The id of the command to get the criterion from
* @param priority The priority ({@literal 0} indexed). Must be
* {@literal 0 <= priority < clusterCriteria.length}
* @param addDefaultStatus Whether the system should add the default {@link ClusterStatus} to the {@link Criterion}
* if no status currently is within the {@link Criterion}. The default value is
* {@literal true} which will currently add the status {@link ClusterStatus#UP}
* @return The set of {@link Cluster}'s which matched the {@link Criterion} if any. Each {@link Cluster} is
* represented as an {@link EntityModel} with HAL links
* @throws GenieNotFoundException If no command with {@literal id} is found or if no {Criterion} with
* {@literal priority} exists within the command
*/
@GetMapping(value = "/{id}/clusterCriteria/{priority}/clusters", produces = MediaTypes.HAL_JSON_VALUE)
@ResponseStatus(HttpStatus.OK)
public Set<EntityModel<Cluster>> resolveClustersForCommandClusterCriterion(
@PathVariable("id") final String id,
@PathVariable("priority") @Min(0) final int priority,
@RequestParam(value = "addDefaultStatus", defaultValue = "true") final Boolean addDefaultStatus
) throws GenieNotFoundException {
log.info("Called to get clusters for command {} cluster criterion {}", id, priority);

// TODO: Improvement here to add API which only retrieves the single criterion but in the end the DB
// query likely will fetch entire collection without a lot of work and may not be worth the hassle
final List<Criterion> criteria = this.commandPersistenceService.getClusterCriteriaForCommand(id);
if (priority >= criteria.size()) {
throw new GenieNotFoundException("No criterion with priority " + priority + " exists for command " + id);
}

return this.clusterPersistenceService.findClustersMatchingCriterion(criteria.get(priority), addDefaultStatus)
.stream()
.map(DtoConverters::toV3Cluster)
.map(this.clusterModelAssembler::toModel)
.collect(Collectors.toSet());
}
}

0 comments on commit 0da307a

Please sign in to comment.