Skip to content

Commit

Permalink
Support shared pipelines (#279)
Browse files Browse the repository at this point in the history
* Allow to choose pipeline visibility when listing pipelines

* Allow to launch shared pipelines

* Add visibility column at list

* Add missing license header

* Fix tests

* Search for multiple pipelines at launch command
  • Loading branch information
jordeu authored Dec 5, 2022
1 parent 4ee5c5a commit 503eaad
Show file tree
Hide file tree
Showing 14 changed files with 95 additions and 42 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ dependencies {
implementation 'org.slf4j:slf4j-api:1.7.36'
implementation 'ch.qos.logback:logback-core:1.2.11'
implementation 'ch.qos.logback:logback-classic:1.2.11'
implementation 'io.seqera.tower:tower-java-sdk:1.4.2'
implementation 'io.seqera.tower:tower-java-sdk:1.4.3'
implementation 'info.picocli:picocli:4.6.3'
annotationProcessor 'info.picocli:picocli-codegen:4.6.3'

Expand Down
10 changes: 8 additions & 2 deletions conf/resource-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,17 @@
"bundles":[
{
"name":"org.glassfish.jersey.client.internal.localization",
"locales":["und"]
"locales":[
"",
"und"
]
},
{
"name":"org.glassfish.jersey.internal.localization",
"locales":["und"]
"locales":[
"",
"und"
]
},
{
"name":"org.glassfish.jersey.media.multipart.internal.localization"
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/io/seqera/tower/cli/commands/AbstractApiCmd.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import io.seqera.tower.model.ListComputeEnvsResponseEntry;
import io.seqera.tower.model.ListWorkspacesAndOrgResponse;
import io.seqera.tower.model.OrgAndWorkspaceDbDto;
import io.seqera.tower.model.PipelineDbDto;
import io.seqera.tower.model.User;
import io.seqera.tower.model.WorkflowQueryAttribute;
import org.glassfish.jersey.client.ClientConfig;
Expand Down Expand Up @@ -381,6 +382,19 @@ protected String workspaceRef(Long workspaceId) throws ApiException {
return buildWorkspaceRef(orgName(workspaceId), workspaceName(workspaceId));
}

protected Long sourceWorkspaceId(Long currentWorkspace, PipelineDbDto pipeline) {
if (pipeline == null)
return null;

if (pipeline.getWorkspaceId() == null)
return null;

if (pipeline.getWorkspaceId().equals(currentWorkspace))
return null;

return pipeline.getWorkspaceId();
}

@Override
public Integer call() {
try {
Expand Down
37 changes: 28 additions & 9 deletions src/main/java/io/seqera/tower/cli/commands/LaunchCmd.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.seqera.tower.model.ComputeEnvResponseDto;
import io.seqera.tower.model.Launch;
import io.seqera.tower.model.ListPipelinesResponse;
import io.seqera.tower.model.PipelineDbDto;
import io.seqera.tower.model.SubmitWorkflowLaunchRequest;
import io.seqera.tower.model.SubmitWorkflowLaunchResponse;
import io.seqera.tower.model.WorkflowLaunchRequest;
Expand Down Expand Up @@ -104,7 +105,7 @@ protected Response runNextflowPipeline(Long wspId) throws ApiException, IOExcept
.workDir(ce.getConfig().getWorkDir())
.preRunScript(ce.getConfig().getPreRunScript())
.postRunScript(ce.getConfig().getPostRunScript())
), wspId);
), wspId, null);
}

private WorkflowLaunchRequest updateLaunchRequest(WorkflowLaunchRequest base) throws IOException {
Expand All @@ -130,28 +131,46 @@ private WorkflowLaunchRequest updateLaunchRequest(WorkflowLaunchRequest base) th
}

protected Response runTowerPipeline(Long wspId) throws ApiException, IOException {
ListPipelinesResponse pipelines = api().listPipelines(Collections.emptyList(), wspId, 2, 0, pipeline, null);
ListPipelinesResponse pipelines = api().listPipelines(Collections.emptyList(), wspId, 50, 0, pipeline, "all");
if (pipelines.getTotalSize() == 0) {
throw new InvalidResponseException(String.format("Pipeline '%s' not found on this workspace.", pipeline));
}

if (pipelines.getTotalSize() > 1) {
throw new InvalidResponseException(String.format("Multiple pipelines match '%s'", pipeline));
PipelineDbDto pipe = null;
for (PipelineDbDto p : pipelines.getPipelines()) {
if (pipeline.equals(p.getName())) {
pipe = p;
break;
}
}

Long pipelineId = pipelines.getPipelines().get(0).getPipelineId();
Launch launch = api().describePipelineLaunch(pipelineId, wspId).getLaunch();
if (pipe == null) {
throw new InvalidResponseException(String.format("Pipeline '%s' not found", pipe));
}

Long sourceWorkspaceId = sourceWorkspaceId(wspId, pipe);

Launch launch = api().describePipelineLaunch(pipe.getPipelineId(), wspId, sourceWorkspaceId).getLaunch();

WorkflowLaunchRequest launchRequest = createLaunchRequest(launch);
if (computeEnv != null) {
launchRequest.computeEnvId(computeEnvByRef(wspId, computeEnv).getId());
}

return submitWorkflow(updateLaunchRequest(launchRequest), wspId);
if (launchRequest.getComputeEnvId() == null) {
launchRequest.computeEnvId(primaryComputeEnv(wspId).getId());
}

if (launchRequest.getWorkDir() == null) {
ComputeEnvResponseDto ce = api().describeComputeEnv(launchRequest.getComputeEnvId(), wspId, NO_CE_ATTRIBUTES).getComputeEnv();
launchRequest.workDir(ce.getConfig().getWorkDir());
}

return submitWorkflow(updateLaunchRequest(launchRequest), wspId, sourceWorkspaceId);
}

protected Response submitWorkflow(WorkflowLaunchRequest launch, Long wspId) throws ApiException {
SubmitWorkflowLaunchResponse response = api().createWorkflowLaunch(new SubmitWorkflowLaunchRequest().launch(launch), wspId, null, null);
protected Response submitWorkflow(WorkflowLaunchRequest launch, Long wspId, Long sourceWorkspaceId) throws ApiException {
SubmitWorkflowLaunchResponse response = api().createWorkflowLaunch(new SubmitWorkflowLaunchRequest().launch(launch), wspId, null, sourceWorkspaceId);
String workflowId = response.getWorkflowId();
return new RunSubmited(workflowId, wspId, baseWorkspaceUrl(wspId), workspaceRef(wspId));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public AbstractPipelinesCmd() {

protected PipelineDbDto pipelineByName(Long workspaceId, String name) throws ApiException {

ListPipelinesResponse list = api().listPipelines(Collections.emptyList(), workspaceId, null, null, name, null);
ListPipelinesResponse list = api().listPipelines(Collections.emptyList(), workspaceId, null, null, name, "all");

if (list.getPipelines().isEmpty()) {
throw new PipelineNotFoundException(name, workspaceRef(workspaceId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ public class ExportCmd extends AbstractPipelinesCmd {
protected Response exec() throws ApiException {
Long wspId = workspaceId(workspace.workspace);
PipelineDbDto pipeline = fetchPipeline(pipelineRefOptions, wspId);

Launch launch = api().describePipelineLaunch(pipeline.getPipelineId(), wspId).getLaunch();
Long sourceWorkspaceId = sourceWorkspaceId(wspId, pipeline);
Launch launch = api().describePipelineLaunch(pipeline.getPipelineId(), wspId, sourceWorkspaceId).getLaunch();

WorkflowLaunchRequest workflowLaunchRequest = ModelHelper.createLaunchRequest(launch);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ public class ListCmd extends AbstractPipelinesCmd {
@CommandLine.Option(names = {"-f", "--filter"}, description = "Show only pipelines that contain the given word.")
public String filter;

@CommandLine.Option(names = {"--visibility"}, description = "Show pipelines: ${COMPLETION-CANDIDATES} [default: private].", defaultValue = "private")
public PipelineVisibility visibility;

@CommandLine.Mixin
PaginationOptions paginationOptions;

Expand All @@ -48,7 +51,7 @@ protected Response exec() throws ApiException, IOException {
ListPipelinesResponse response = new ListPipelinesResponse();

try {
response = api().listPipelines(Collections.emptyList(), wspId, max, offset, filter, null);
response = api().listPipelines(Collections.emptyList(), wspId, max, offset, filter, visibility.toString());

} catch (ApiException apiException) {
if (apiException.getCode() == 404){
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2021, Seqera Labs.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This Source Code Form is "Incompatible With Secondary Licenses", as
* defined by the Mozilla Public License, v. 2.0.
*/

package io.seqera.tower.cli.commands.pipelines;

public enum PipelineVisibility {
ALL("all"),
PRIVATE("private"),
SHARED("shared");

private final String value;

PipelineVisibility(String value) {
this.value = value;
}

@Override
public String toString() {
return value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ protected Response exec() throws ApiException, IOException {
id = pipe.getPipelineId();
}

Launch launch = api().describePipelineLaunch(id, wspId).getLaunch();
Long sourceWorkspaceId = sourceWorkspaceId(wspId, pipe);
Launch launch = api().describePipelineLaunch(id, wspId, sourceWorkspaceId).getLaunch();

// Retrieve the provided computeEnv or use the primary if not provided
String ceId = opts.computeEnv != null ? computeEnvByRef(wspId, opts.computeEnv).getId() : launch.getComputeEnv().getId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ public class ViewCmd extends AbstractPipelinesCmd {
protected Response exec() throws ApiException {
Long wspId = workspaceId(workspace.workspace);
PipelineDbDto pipeline = fetchPipeline(pipelineRefOptions, wspId);
Long sourceWorkspaceId = sourceWorkspaceId(wspId, pipeline);

Launch launch = api().describePipelineLaunch(pipeline.getPipelineId(), wspId).getLaunch();
Launch launch = api().describePipelineLaunch(pipeline.getPipelineId(), wspId, sourceWorkspaceId).getLaunch();

return new PipelinesView(workspaceRef(wspId), pipeline, launch, baseWorkspaceUrl(wspId));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ public void toString(PrintWriter out) {
return;
}

TableList table = new TableList(out, 3, "ID", "Name", "Repository", "Description").sortBy(0);
TableList table = new TableList(out, 4, "ID", "Name", "Repository", "Visibility").sortBy(0);
table.setPrefix(" ");
pipelines.forEach(pipe -> table.addRow(
formatPipelineId(pipe.getPipelineId(), baseWorkspaceUrl),
pipe.getName(),
pipe.getRepository(),
pipe.getDescription()
pipe.getVisibility()
));
table.print();
out.println("");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public void toString(PrintWriter out) {
table.addRow("Name", info.getName());
table.addRow("Description", info.getDescription());
table.addRow("Repository", info.getRepository());
table.addRow("Compute env.", launch.getComputeEnv().getName());
table.addRow("Compute env.", launch.getComputeEnv() == null ? "(not defined)" : launch.getComputeEnv().getName());
table.print();

out.println(String.format("%n Configuration:%n%n%s%n", configJson.replaceAll("(?m)^", " ")));
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/io/seqera/tower/cli/utils/ModelHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ private ModelHelper() {
public static WorkflowLaunchRequest createLaunchRequest(Launch launch) {
return new WorkflowLaunchRequest()
.id(launch.getId())
.computeEnvId(launch.getComputeEnv().getId())
.computeEnvId(launch.getComputeEnv() != null ? launch.getComputeEnv().getId() : null)
.pipeline(launch.getPipeline())
.workDir(launch.getWorkDir())
.revision(launch.getRevision())
Expand Down
20 changes: 0 additions & 20 deletions src/test/java/io/seqera/tower/cli/LaunchCmdTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,26 +72,6 @@ void testPipelineNotfound(MockServerClient mock) {
assertEquals(1, out.exitCode);
}

@Test
void testMultiplePipelinesFound(MockServerClient mock) {

mock.reset();

// Create server expectation
mock.when(
request().withMethod("GET").withPath("/pipelines"), exactly(1)
).respond(
response().withStatusCode(200).withBody(loadResource("pipelines_multiple")).withContentType(MediaType.APPLICATION_JSON)
);

// Run the command
ExecOut out = exec(mock, "launch", "hello");

// Assert results
assertEquals(errorMessage(out.app, new InvalidResponseException("Multiple pipelines match 'hello'")), out.stdErr);
assertEquals(1, out.exitCode);
}

@ParameterizedTest
@EnumSource(OutputType.class)
void testSubmitLaunchpadPipeline(OutputType format, MockServerClient mock) {
Expand Down

0 comments on commit 503eaad

Please sign in to comment.