Skip to content

Commit

Permalink
feat: add deploy command to cli
Browse files Browse the repository at this point in the history
  • Loading branch information
iocanel committed Jan 11, 2023
1 parent d259593 commit f6cd97d
Show file tree
Hide file tree
Showing 10 changed files with 366 additions and 2 deletions.
37 changes: 37 additions & 0 deletions devtools/cli/src/main/java/io/quarkus/cli/Deploy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.quarkus.cli;

import java.util.List;
import java.util.Map;
import java.util.Optional;

import io.quarkus.cli.deploy.Knative;
import io.quarkus.cli.deploy.Kubernetes;
import io.quarkus.cli.deploy.Openshift;
import io.quarkus.devtools.project.BuildTool;
import picocli.CommandLine;
import picocli.CommandLine.Unmatched;

@CommandLine.Command(name = "deploy", sortOptions = false, mixinStandardHelpOptions = false, header = "Deploy application.", subcommands = {
Kubernetes.class, Openshift.class, Knative.class
}, headerHeading = "%n", commandListHeading = "%nCommands:%n", synopsisHeading = "%nUsage: ", optionListHeading = "%nOptions:%n")
public class Deploy extends BuildToolDelegatingCommand {

private static final Map<BuildTool, String> ACTION_MAPPING = Map.of(BuildTool.MAVEN, "quarkus:deploy",
BuildTool.GRADLE, "deploy");

@CommandLine.Spec
protected CommandLine.Model.CommandSpec spec;

@Unmatched // avoids throwing errors for unmatched arguments
List<String> unmatchedArgs;

@Override
public Optional<String> getAction() {
return Optional.ofNullable(ACTION_MAPPING.get(getRunner().getBuildTool()));
}

@Override
public String toString() {
return "Deploy {}";
}
}
3 changes: 2 additions & 1 deletion devtools/cli/src/main/java/io/quarkus/cli/QuarkusCli.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
import picocli.CommandLine.UnmatchedArgumentException;

@CommandLine.Command(name = "quarkus", subcommands = {
Create.class, Build.class, Dev.class, Test.class, ProjectExtensions.class, Image.class, Registry.class, Info.class,
Create.class, Build.class, Dev.class, Test.class, ProjectExtensions.class, Image.class, Deploy.class, Registry.class,
Info.class,
Update.class,
Version.class,
Completion.class }, scope = ScopeType.INHERIT, sortOptions = false, showDefaultValues = true, versionProvider = Version.class, subcommandsRepeatable = false, mixinStandardHelpOptions = false, commandListHeading = "%nCommands:%n", synopsisHeading = "%nUsage: ", optionListHeading = "Options:%n", headerHeading = "%n", parameterListHeading = "%n")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.quarkus.cli.deploy;

import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;

import io.quarkus.cli.BuildToolDelegatingCommand;
import picocli.CommandLine;

public class BaseKubernetes extends BuildToolDelegatingCommand {

@CommandLine.ArgGroup(order = 2, exclusive = false, validate = false, heading = "%nKubernetes options:%n")
KubernetesOptions kubernetesOptions = new KubernetesOptions();

@Override
public void populateConfiguration(Map<String, String> properties) {
kubernetesOptions.masterUrl.ifPresent(u -> properties.put("quarkus.kubernetes-client.master-url", u));
kubernetesOptions.username.ifPresent(u -> properties.put("quarkus.kubernetes-client.username", u));
kubernetesOptions.password.ifPresent(p -> properties.put("quarkus.kubernetes-client.password", p));
kubernetesOptions.token.ifPresent(t -> properties.put("quarkus.kubernetes-client.token", t));
kubernetesOptions.namespace.ifPresent(n -> properties.put("quarkus.kubernetes-client.namespace", n));

kubernetesOptions.caCertFile.ifPresent(c -> properties.put("quarkus.kubernetes-client.ca-cert-file", c));
kubernetesOptions.caCertData.ifPresent(c -> properties.put("quarkus.kubernetes-client.ca-cert-data", c));

kubernetesOptions.clientCertFile.ifPresent(c -> properties.put("quarkus.kubernetes-client.client-cert-file", c));
kubernetesOptions.clientCertData.ifPresent(c -> properties.put("quarkus.kubernetes-client.client-cert-data", c));

kubernetesOptions.clientKeyFile.ifPresent(c -> properties.put("quarkus.kubernetes-client.client-key-file", c));
kubernetesOptions.clientKeyData.ifPresent(c -> properties.put("quarkus.kubernetes-client.client-key-data", c));
kubernetesOptions.clientKeyAlgo.ifPresent(c -> properties.put("quarkus.kubernetes-client.client-key-algo", c));
kubernetesOptions.clientKeyPassphrase
.ifPresent(c -> properties.put("quarkus.kubernetes-client.client-key-passphrase", c));

kubernetesOptions.httpProxy.ifPresent(p -> properties.put("quarkus.kubernetes-client.http-proxy", p));
kubernetesOptions.httpsProxy.ifPresent(p -> properties.put("quarkus.kubernetes-client.https-proxy", p));
kubernetesOptions.proxyUsername.ifPresent(p -> properties.put("quarkus.kubernetes-client.proxy-username", p));
kubernetesOptions.proxyPassword.ifPresent(p -> properties.put("quarkus.kubernetes-client.proxy-password", p));
if (kubernetesOptions.noProxy != null && kubernetesOptions.noProxy.length > 0) {
properties.put("quarkus.kubernetes-client.no-proxy",
Arrays.stream(kubernetesOptions.noProxy).collect(Collectors.joining(", ")));
}
}
}
31 changes: 31 additions & 0 deletions devtools/cli/src/main/java/io/quarkus/cli/deploy/Knative.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.quarkus.cli.deploy;

import java.util.Map;

import io.quarkus.cli.Deploy;
import picocli.CommandLine;
import picocli.CommandLine.ParentCommand;

@CommandLine.Command(name = "knative", sortOptions = false, showDefaultValues = true, mixinStandardHelpOptions = false, header = "Perform the deploy action on knative.", description = "%n"
+ "The command will deploy the application on knative.", footer = "%n"
+ "For example (using default values), it will create a Deployment named '<project.artifactId>' using the image with REPOSITORY='${user.name}/<project.artifactId>' and TAG='<project.version>' and will deploy it to the target cluster.", headerHeading = "%n", commandListHeading = "%nCommands:%n", synopsisHeading = "%nUsage: ", parameterListHeading = "%n", optionListHeading = "Options:%n")
public class Knative extends BaseKubernetes {

private static final String KNATIVE = "knative";
private static final String KNATIVE_EXTENSION = "quarkus-kubernetes";
private static final String DEPLOYER = "quarkus.deployment.deployer";

@ParentCommand
Deploy parent;

@Override
public Integer call() throws Exception {
Map<String, String> properties = parent.getPropertiesOptions().properties;
properties.put(DEPLOYER, KNATIVE);
parent.getForcedExtensions().add(KNATIVE_EXTENSION);
parent.setRunMode(getRunMode());
parent.setBuildOptions(getBuildOptions());
parent.setOutput(output);
return parent.call();
}
}
42 changes: 42 additions & 0 deletions devtools/cli/src/main/java/io/quarkus/cli/deploy/Kubernetes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.quarkus.cli.deploy;

import java.util.Map;

import io.quarkus.cli.Deploy;
import picocli.CommandLine;
import picocli.CommandLine.ParentCommand;

@CommandLine.Command(name = "kubernetes", sortOptions = false, showDefaultValues = true, mixinStandardHelpOptions = false, header = "Perform the deploy action on kubernetes.", description = "%n"
+ "The command will deploy the application on kubernetes.", footer = "%n"
+ "For example (using default values), it will create a Deployment named '<project.artifactId>' using the image with REPOSITORY='${user.name}/<project.artifactId>' and TAG='<project.version>' and will deploy it to the target cluster.", headerHeading = "%n", commandListHeading = "%nCommands:%n", synopsisHeading = "%nUsage: ", parameterListHeading = "%n", optionListHeading = "Options:%n")
public class Kubernetes extends BaseKubernetes {

private static final String KUBERNETES = "kubernetes";
private static final String KUBERNETES_EXTENSION = "quarkus-kubernetes";
private static final String DEPLOYER = "quarkus.deployment.deployer";
private static final String DEPLOYMENT_KIND = "quarkus.kubernetes.deployment-kind";

public enum DeploymentKind {
Deployment,
StatefulSet,
Job
}

@CommandLine.Option(names = { "--deployment-kind" }, description = "The kind of resource to generate and deploy")
public DeploymentKind kind = DeploymentKind.Deployment;

@ParentCommand
Deploy parent;

@Override
public Integer call() throws Exception {
Map<String, String> properties = parent.getPropertiesOptions().properties;
properties.put(DEPLOYER, KUBERNETES);
properties.put(DEPLOYMENT_KIND, kind.name());
parent.getForcedExtensions().add(KUBERNETES_EXTENSION);
parent.setRunMode(getRunMode());
parent.setBuildOptions(getBuildOptions());
parent.setOutput(output);
return parent.call();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package io.quarkus.cli.deploy;

import java.util.Optional;

import picocli.CommandLine;

public class KubernetesOptions {

@CommandLine.Option(order = 3, names = { "--master-url" }, description = "The URL of the kubernetes API server")
public Optional<String> masterUrl;

@CommandLine.Option(order = 4, names = { "--username" }, description = "Kubernetes username")
public Optional<String> username = Optional.empty();

@CommandLine.Option(order = 5, names = {
"--password" }, description = "Kubernetes password")
public Optional<String> password = Optional.empty();

@CommandLine.Option(order = 6, names = {
"--token" }, description = "Kubernetes oAuth token")
public Optional<String> token = Optional.empty();

@CommandLine.Option(order = 7, names = {
"--trust-certs" }, description = "Flag to trust self-signed certificates")
public Optional<Boolean> trustCerts = Optional.empty();

@CommandLine.Option(order = 8, names = { "--namespace" }, description = "The Kubernetes namespace")
public Optional<String> namespace = Optional.empty();

@CommandLine.Option(order = 9, names = { "--ca-cert-file" }, description = "The CA certificate file")
public Optional<String> caCertFile = Optional.empty();

@CommandLine.Option(order = 10, names = { "--ca-cert-data" }, description = "The CA certificate data")
public Optional<String> caCertData = Optional.empty();

@CommandLine.Option(order = 11, names = { "--client-cert-file" }, description = "The client certificate file")
public Optional<String> clientCertFile = Optional.empty();

@CommandLine.Option(order = 12, names = { "--client-cert-data" }, description = "The client certificate data")
public Optional<String> clientCertData = Optional.empty();

@CommandLine.Option(order = 13, names = { "--client-key-file" }, description = "The client key file")
public Optional<String> clientKeyFile = Optional.empty();

@CommandLine.Option(order = 14, names = { "--client-key-data" }, description = "The client key data")
public Optional<String> clientKeyData = Optional.empty();

@CommandLine.Option(order = 15, names = { "--client-key-algo" }, description = "The client key algorithm")
public Optional<String> clientKeyAlgo = Optional.empty();

@CommandLine.Option(order = 16, names = { "--client-key-passphrase" }, description = "The client key passphrase")
public Optional<String> clientKeyPassphrase = Optional.empty();

@CommandLine.Option(order = 17, names = {
"--http-proxy" }, description = "HTTP proxy used to access the Kubernetes API server")
public Optional<String> httpProxy = Optional.empty();

@CommandLine.Option(order = 18, names = {
"--https-proxy" }, description = "HTTPS proxy used to access the Kubernetes API server")
public Optional<String> httpsProxy = Optional.empty();

@CommandLine.Option(order = 18, names = { "--proxy-username" }, description = "Proxy username")
public Optional<String> proxyUsername = Optional.empty();

@CommandLine.Option(order = 19, names = { "--proxy-password" }, description = "Proxy password")
public Optional<String> proxyPassword = Optional.empty();

@CommandLine.Option(order = 19, names = {
"--no-proxy" }, arity = "0..*", description = "IP addresses or hosts to exclude from proxying")
public String[] noProxy = new String[0];
}
43 changes: 43 additions & 0 deletions devtools/cli/src/main/java/io/quarkus/cli/deploy/Openshift.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.quarkus.cli.deploy;

import java.util.Map;

import io.quarkus.cli.Deploy;
import picocli.CommandLine;
import picocli.CommandLine.ParentCommand;

@CommandLine.Command(name = "openshift", sortOptions = false, showDefaultValues = true, mixinStandardHelpOptions = false, header = "Perform the deploy action on openshift.", description = "%n"
+ "The command will deploy the application on openshift.", footer = "%n"
+ "For example (using default values), it will create a Deployment named '<project.artifactId>' using the image with REPOSITORY='${user.name}/<project.artifactId>' and TAG='<project.version>' and will deploy it to the target cluster.", headerHeading = "%n", commandListHeading = "%nCommands:%n", synopsisHeading = "%nUsage: ", parameterListHeading = "%n", optionListHeading = "Options:%n")
public class Openshift extends BaseKubernetes {

private static final String OPENSHIFT = "openshift";
private static final String OPENSHIFT_EXTENSION = "quarkus-kubernetes";
private static final String DEPLOYER = "quarkus.deployment.deployer";
private static final String DEPLOYMENT_KIND = "quarkus.kubernetes.deployment-kind";

public enum DeploymentKind {
Deployment,
DeploymentConfig,
StatefulSet,
Job
}

@CommandLine.Option(names = { "--deployment-kind" }, description = "The kind of resource to generate and deploy")
public DeploymentKind kind = DeploymentKind.DeploymentConfig;

@ParentCommand
Deploy parent;

@Override
public Integer call() throws Exception {
Map<String, String> properties = parent.getPropertiesOptions().properties;
properties.put(DEPLOYER, OPENSHIFT);
properties.put(DEPLOYMENT_KIND, kind.name());
parent.getForcedExtensions().add(OPENSHIFT_EXTENSION);
parent.setRunMode(getRunMode());
parent.setBuildOptions(getBuildOptions());
parent.setOutput(output);
return parent.call();
}
}
46 changes: 45 additions & 1 deletion devtools/cli/src/test/java/io/quarkus/cli/CliHelpTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -285,10 +285,54 @@ public void testImagePushOpenshiftHelp() throws Exception {
}

@Test
@Order(99)
@Order(100)
public void testImagePushBuildpackHelp() throws Exception {
CliDriver.Result result = CliDriver.execute(workspaceRoot, "image", "push", "buildpack", "--help");
result.echoSystemOut();
assertThat(result.stdout).contains("Usage");
}

@Test
@Order(101)
public void testDeployHelp() throws Exception {
CliDriver.Result result = CliDriver.execute(workspaceRoot, "deploy", "--help");
result.echoSystemOut();
assertThat(result.stdout).contains("Usage");
}

@Test
@Order(102)
public void testDeployKubernetesHelp() throws Exception {
CliDriver.Result result = CliDriver.execute(workspaceRoot, "deploy", "kubernetes", "--help");
result.echoSystemOut();
assertThat(result.stdout).contains("Usage");
assertThat(result.stdout).contains("--master-url");
assertThat(result.stdout).contains("--token");
assertThat(result.stdout).contains("--namespace");
assertThat(result.stdout).contains("--deployment-kind");
}

@Test
@Order(103)
public void testDeployOpenshiftHelp() throws Exception {
CliDriver.Result result = CliDriver.execute(workspaceRoot, "deploy", "openshift", "--help");
result.echoSystemOut();
assertThat(result.stdout).contains("Usage");
assertThat(result.stdout).contains("--master-url");
assertThat(result.stdout).contains("--token");
assertThat(result.stdout).contains("--namespace");
assertThat(result.stdout).contains("--deployment-kind");
}

@Test
@Order(104)
public void testDeployKnativeHelp() throws Exception {
CliDriver.Result result = CliDriver.execute(workspaceRoot, "deploy", "knative", "--help");
result.echoSystemOut();
assertThat(result.stdout).contains("Usage");
assertThat(result.stdout).contains("--master-url");
assertThat(result.stdout).contains("--token");
assertThat(result.stdout).contains("--namespace");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package io.quarkus.cli.image;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.nio.file.Path;
import java.nio.file.Paths;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import io.quarkus.cli.CliDriver;
import io.quarkus.devtools.testing.RegistryClientTestHelper;
import picocli.CommandLine;

/**
* Similar to CliProjecMavenTest ..
*/
public class CliDeployMavenTest {
static Path workspaceRoot = Paths.get(System.getProperty("user.dir")).toAbsolutePath()
.resolve("target/test-project/CliDeployMavenTest");
Path project;

@BeforeAll
public static void setupTestRegistry() {
RegistryClientTestHelper.enableRegistryClientTestConfig();
}

@AfterAll
public static void cleanupTestRegistry() {
RegistryClientTestHelper.disableRegistryClientTestConfig();
}

@BeforeEach
public void setupTestDirectories() throws Exception {
CliDriver.deleteDir(workspaceRoot);
project = workspaceRoot.resolve("code-with-quarkus");
}

@Test
public void testUsage() throws Exception {
CliDriver.Result result = CliDriver.execute(workspaceRoot, "create", "app", "-e", "-B", "--verbose");
assertEquals(CommandLine.ExitCode.OK, result.getExitCode(), "Expected OK return code." + result);

// 1 deploy --dry-run
result = CliDriver.execute(project, "deploy", "--dry-run");
assertEquals(CommandLine.ExitCode.OK, result.getExitCode(), "Expected OK return code." + result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public void shouldCreateTasks() {
assertNotNull(tasks.getByName(QuarkusPlugin.ADD_EXTENSION_TASK_NAME));
assertNotNull(tasks.getByName(QuarkusPlugin.IMAGE_BUILD_TASK_NAME));
assertNotNull(tasks.getByName(QuarkusPlugin.IMAGE_PUSH_TASK_NAME));
assertNotNull(tasks.getByName(QuarkusPlugin.DEPLOY_TASK_NAME));
}

@Test
Expand Down

0 comments on commit f6cd97d

Please sign in to comment.