Skip to content

Commit

Permalink
Improve Config CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
radcortez committed Jun 14, 2024
1 parent 3c6e8f9 commit 5fe2434
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 77 deletions.
8 changes: 7 additions & 1 deletion devtools/cli/src/main/java/io/quarkus/cli/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,23 @@
import java.util.List;
import java.util.concurrent.Callable;

import io.quarkus.cli.common.HelpOption;
import io.quarkus.cli.common.OutputOptionMixin;
import io.quarkus.cli.config.Encrypt;
import io.quarkus.cli.config.RemoveConfig;
import io.quarkus.cli.config.SetConfig;
import picocli.CommandLine;
import picocli.CommandLine.Command;

@Command(name = "config", header = "Manage Quarkus configuration", subcommands = { SetConfig.class, Encrypt.class })
@Command(name = "config", header = "Manage Quarkus configuration", subcommands = { SetConfig.class, RemoveConfig.class,
Encrypt.class })
public class Config implements Callable<Integer> {
@CommandLine.Mixin(name = "output")
protected OutputOptionMixin output;

@CommandLine.Mixin
protected HelpOption helpOption;

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.List;

import io.quarkus.cli.common.HelpOption;
import io.quarkus.cli.common.OutputOptionMixin;
import io.smallrye.config.ConfigValue;
import picocli.CommandLine;

public class BaseConfigCommand {
@CommandLine.Mixin(name = "output")
protected OutputOptionMixin output;

@CommandLine.Mixin
protected HelpOption helpOption;

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

Expand All @@ -29,4 +35,15 @@ protected Path projectRoot() {
protected String encodeToString(byte[] data) {
return Base64.getUrlEncoder().withoutPadding().encodeToString(data);
}

protected ConfigValue findKey(List<String> lines, String name) {
ConfigValue configValue = ConfigValue.builder().withName(name).build();
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
if (line.startsWith(configValue.getName() + "=")) {
return configValue.withValue(line.substring(name.length() + 1)).withLineNumber(i);
}
}
return configValue;
}
}
11 changes: 6 additions & 5 deletions devtools/cli/src/main/java/io/quarkus/cli/config/Encrypt.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@

import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;

@Command(name = "encrypt", aliases = "enc", header = "Encrypt Secrets using AES/GCM/NoPadding algorithm by default")
public class Encrypt extends BaseConfigCommand implements Callable<Integer> {
@Option(required = true, names = { "-s", "--secret" }, description = "Secret")
@Parameters(index = "0", paramLabel = "SECRET", description = "The Secret value to encrypt")
String secret;

@Option(names = { "-k", "--key" }, description = "Encryption Key")
@Option(names = { "-k", "--key" }, description = "The Encryption Key")
String encryptionKey;

@Option(names = { "-f", "--format" }, description = "Encryption Key Format (base64 / plain)", defaultValue = "base64")
@Option(names = { "-f", "--format" }, description = "The Encryption Key Format (base64 / plain)", defaultValue = "base64")
KeyFormat encryptionKeyFormat;

@Option(hidden = true, names = { "-a", "--algorithm" }, description = "Algorithm", defaultValue = "AES")
Expand All @@ -33,7 +34,7 @@ public class Encrypt extends BaseConfigCommand implements Callable<Integer> {
@Option(hidden = true, names = { "-m", "--mode" }, description = "Mode", defaultValue = "GCM")
String mode;

@Option(hidden = true, names = { "-p", "--padding" }, description = "Algorithm", defaultValue = "NoPadding")
@Option(hidden = true, names = { "-p", "--padding" }, description = "Padding", defaultValue = "NoPadding")
String padding;

@Option(hidden = true, names = { "-q", "--quiet" }, defaultValue = "false")
Expand Down Expand Up @@ -68,7 +69,7 @@ public Integer call() throws Exception {
this.encryptedSecret = Base64.getUrlEncoder().withoutPadding().encodeToString((message.array()));
if (!quiet) {
System.out.println("Encrypted Secret: " + encryptedSecret);
System.out.println("Encryption Key: " + encryptionKey);
System.out.println("Encryption Key (" + encryptionKeyFormat + "): " + encryptionKey);
}

return 0;
Expand Down
44 changes: 44 additions & 0 deletions devtools/cli/src/main/java/io/quarkus/cli/config/RemoveConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.quarkus.cli.config;

import java.io.BufferedWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.concurrent.Callable;

import io.smallrye.config.ConfigValue;
import picocli.CommandLine;

@CommandLine.Command(name = "remove", header = "Removes a configuration in application.properties")
public class RemoveConfig extends BaseConfigCommand implements Callable<Integer> {
@CommandLine.Parameters(index = "0", arity = "1", paramLabel = "NAME", description = "Configuration name")
String name;

@Override
public Integer call() throws Exception {
Path properties = projectRoot().resolve("src/main/resources/application.properties");
if (!properties.toFile().exists()) {
System.out.println("Could not find an application.properties file");
return 0;
}

List<String> lines = Files.readAllLines(properties);

ConfigValue configValue = findKey(lines, name);
if (configValue.getLineNumber() != -1) {
System.out.println("Removing configuration " + name);
lines.remove(configValue.getLineNumber());
} else {
System.out.println("Could not find configuration " + name);
}

try (BufferedWriter writer = Files.newBufferedWriter(properties)) {
for (String i : lines) {
writer.write(i);
writer.newLine();
}
}

return 0;
}
}
54 changes: 18 additions & 36 deletions devtools/cli/src/main/java/io/quarkus/cli/config/SetConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,27 @@
import io.smallrye.config.ConfigValue;
import io.smallrye.config.Converters;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;

@CommandLine.Command(name = "set")
@Command(name = "set", header = "Sets a configuration in application.properties")
public class SetConfig extends BaseConfigCommand implements Callable<Integer> {
@CommandLine.Option(required = true, names = { "-n", "--name" }, description = "Configuration name")
@Parameters(index = "0", arity = "1", paramLabel = "NAME", description = "Configuration name")
String name;
@CommandLine.Option(names = { "-a", "--value" }, description = "Configuration value")
@Parameters(index = "1", arity = "0..1", paramLabel = "VALUE", description = "Configuration value")
String value;
@CommandLine.Option(names = { "-k", "--encrypt" }, description = "Encrypt value")
@Option(names = { "-k", "--encrypt" }, description = "Encrypt the configuration value")
boolean encrypt;

@Override
public Integer call() throws Exception {
Path properties = projectRoot().resolve("src/main/resources/application.properties");
if (!properties.toFile().exists()) {
System.out.println("Could not find an application.properties file");
return 0;
System.out.println("Could not find an application.properties file. Creating one...");
Path resources = projectRoot().resolve("src/main/resources");
Files.createDirectories(resources);
Files.createFile(resources.resolve("application.properties"));
}

List<String> lines = Files.readAllLines(properties);
Expand All @@ -37,7 +42,7 @@ public Integer call() throws Exception {
if (value == null) {
value = findKey(lines, name).getValue();
}
args.add("--secret=" + value);
args.add(value);
if (value == null || value.length() == 0) {
System.out.println("Cannot encrypt an empty value");
return -1;
Expand All @@ -64,25 +69,13 @@ public Integer call() throws Exception {
}
}

int nameLineNumber = -1;
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
if (line.startsWith(name + "=")) {
nameLineNumber = i;
break;
}
}

if (nameLineNumber != -1) {
if (value != null) {
System.out.println("Setting " + name + " to " + value);
lines.set(nameLineNumber, name + "=" + value);
} else {
System.out.println("Removing " + name);
lines.remove(nameLineNumber);
}
ConfigValue configValue = findKey(lines, name);
String actualValue = value != null ? value : "empty value";
if (configValue.getLineNumber() != -1) {
System.out.println("Setting configuration " + name + " to " + actualValue);
lines.set(configValue.getLineNumber(), name + "=" + (value != null ? value : ""));
} else {
System.out.println("Adding " + name + " with " + value);
System.out.println("Adding configuration " + name + " with " + actualValue);
lines.add(name + "=" + (value != null ? value : ""));
}

Expand All @@ -95,15 +88,4 @@ public Integer call() throws Exception {

return 0;
}

public static ConfigValue findKey(List<String> lines, String name) {
ConfigValue configValue = ConfigValue.builder().withName(name).build();
for (int i = 0; i < lines.size(); i++) {
final String line = lines.get(i);
if (line.startsWith(configValue.getName() + "=")) {
return configValue.withValue(line.substring(name.length() + 1)).withLineNumber(i);
}
}
return configValue;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class EncryptTest {

@Test
void encrypt() throws Exception {
CliDriver.Result result = CliDriver.execute(tempDir, "config", "encrypt", "--secret=12345678");
CliDriver.Result result = CliDriver.execute(tempDir, "config", "encrypt", "12345678");
Scanner scanner = new Scanner(result.getStdout());
String secret = scanner.nextLine().split(": ")[1];
String encryptionKey = scanner.nextLine().split(": ")[1];
Expand All @@ -35,7 +35,7 @@ void encrypt() throws Exception {

@Test
void keyPlain() throws Exception {
CliDriver.Result result = CliDriver.execute(tempDir, "config", "encrypt", "--secret=12345678", "-f=plain",
CliDriver.Result result = CliDriver.execute(tempDir, "config", "encrypt", "12345678", "-f=plain",
"--key=12345678");
Scanner scanner = new Scanner(result.getStdout());
String secret = scanner.nextLine().split(": ")[1];
Expand All @@ -62,7 +62,7 @@ void keyPlain() throws Exception {

@Test
void keyBase64() throws Exception {
CliDriver.Result result = CliDriver.execute(tempDir, "config", "encrypt", "--secret=12345678", "--key=12345678");
CliDriver.Result result = CliDriver.execute(tempDir, "config", "encrypt", "12345678", "--key=12345678");
Scanner scanner = new Scanner(result.getStdout());
String secret = scanner.nextLine().split(": ")[1];

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package io.quarkus.cli.config;

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

import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Properties;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import io.quarkus.cli.CliDriver;
import io.smallrye.config.PropertiesConfigSource;
import io.smallrye.config.SmallRyeConfig;
import io.smallrye.config.SmallRyeConfigBuilder;

public class RemoveConfigTest {
@TempDir
Path tempDir;

@BeforeEach
void setUp() throws Exception {
Path resources = tempDir.resolve("src/main/resources");
Files.createDirectories(resources);
Files.createFile(resources.resolve("application.properties"));
}

@Test
void removeConfiguration() throws Exception {
Path propertiesFile = tempDir.resolve("src/main/resources/application.properties");
Properties properties = new Properties();
try (InputStream inputStream = propertiesFile.toUri().toURL().openStream()) {
properties.load(inputStream);
}
properties.put("foo.bar", "1234");
try (FileOutputStream outputStream = new FileOutputStream(propertiesFile.toFile())) {
properties.store(outputStream, "");
}
CliDriver.Result result = CliDriver.execute(tempDir, "config", "remove", "foo.bar");
assertEquals(0, result.getExitCode());
assertTrue(result.getStdout().contains("Removing configuration foo.bar"));
assertTrue(config().getOptionalValue("foo.bar", String.class).isEmpty());
}

private SmallRyeConfig config() throws Exception {
PropertiesConfigSource propertiesConfigSource = new PropertiesConfigSource(
tempDir.resolve("src/main/resources/application.properties").toUri().toURL());
return new SmallRyeConfigBuilder()
.withSources(propertiesConfigSource)
.build();
}
}
Loading

0 comments on commit 5fe2434

Please sign in to comment.