Skip to content

Commit

Permalink
Merge pull request #154 from DataBiosphere/ps/fix-stairctl
Browse files Browse the repository at this point in the history
PF-3004: Fix stairctl so it runs again, other updates
  • Loading branch information
pshapiro4broad authored Aug 5, 2024
2 parents fccfb7e + 939558d commit baceb1d
Show file tree
Hide file tree
Showing 17 changed files with 268 additions and 332 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@ pom.xml

### Jenv directory-local version setting
.java-version

### Spring Shell
spring-shell.log
27 changes: 16 additions & 11 deletions STAIRCTL.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ In the short-term, you can:
- run StairCtl: `java -jar stairctl/build/libs/stairctl-VERSION.jar`

## Command Summary
StairCtl will be getting new capabilities frequently, so this document may not be an up-to-date
reference of the available commands. You can use the `help` command to get a list of the
You can use the `help` command to get a list of the
commands and use `help "<command>"` to get more detailed help.

```
Expand Down Expand Up @@ -49,6 +48,11 @@ Stairway Commands
list stairways: List stairway instances
```

### Using Spring Shell
StairCtl is built using [Spring Shell](https://docs.spring.io/spring-shell/reference/index.html),
so it can be used as an interactive shell or to run a single command.
As an interactive shell, it supports history operations and tab completion.

## Connecting to a Stairway
The most complex part of using StairCtl is connecting to a Stairway; that is, providing the
information for connecting to a Stairway database.
Expand All @@ -60,15 +64,16 @@ connect [--username string] [--password string] [--dbname string] [--host str
```

This table shows the precedence and defaults for each part of the input. Switches override
environment variables, which override the default.
environment variables, which override the default. If you've run the stairway tests locally,
the default settings will connect to the local stairway database used by these tests.

| **Option** | **Environment Variable** | **Default** |
|:---|:---|:---|
| `-u, -U, --username`| STAIRCTL_USERNAME | stairwayuser |
| `-w, --password` | STAIRCTL_PASSWORD | starwaypw |
| `-h, --host` | STAIRCTL_HOST | 127.0.0.1 |
| `-p, --port` | STAIRCTL_PORT | 5432 |
| `-d, --dbname` | STAIRCTL_DBNAME | stairwaylib |
| **Option** | **Environment Variable** | **Default** |
|:---------------------|:---|:---|
| `-u, -U, --username` | STAIRCTL_USERNAME | stairwayuser |
| `-w, --password` | STAIRCTL_PASSWORD | starwaypw |
| `-H, --host` | STAIRCTL_HOST | 127.0.0.1 |
| `-p, --port` | STAIRCTL_PORT | 5432 |
| `-d, --dbname` | STAIRCTL_DBNAME | stairwaylib |

### `disconnect` command
You can disconnect from a Stairway with the `disconnect` command. It takes no options.
Expand Down Expand Up @@ -276,7 +281,7 @@ This section provides a few capabilities of StairCtl.
### Unrecoverable Flight
We have had cases where a service is unable to start, because a Stairway
flight is not recoverable. The Stairway `recoverAndStart` call fails and
the service is unable start.
the service is unable to start.

This problem can be addressed by locating the stuck flight, perhaps using the `list flights` and
`get flight` commands. Then forcing the flight into the "dismal failure" state, so that it will
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ repositories {
dependencies {
implementation group: 'com.diffplug.spotless', name: 'spotless-plugin-gradle', version: '6.25.0'
implementation group: 'com.srcclr.gradle', name: 'com.srcclr.gradle.gradle.plugin', version: '3.1.12'
implementation group: 'org.apache.commons', name: 'commons-compress', version: '1.25.0'
implementation group: 'org.apache.commons', name: 'commons-compress', version: '1.26.0'
}
3 changes: 1 addition & 2 deletions buildSrc/src/main/groovy/stairway.java-conventions.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ plugins {
id 'idea'
id 'jacoco'
id 'java'
id 'java-library'
id 'com.diffplug.spotless'
}

Expand All @@ -14,7 +13,7 @@ repositories {

dependencies {
// For logging
implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.36'
implementation group: 'org.slf4j', name: 'slf4j-api', version: '2.0.13'

// Database
runtimeOnly group: 'org.postgresql', name: 'postgresql', version: '42.7.2'
Expand Down
2 changes: 1 addition & 1 deletion stairctl/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ dependencies {
implementation group: 'org.apache.commons', name: 'commons-lang3'

// Spring
implementation group: 'org.springframework.boot', name: 'spring-boot-starter'
implementation group: 'org.springframework.boot', name: 'spring-boot'
// Spring Boot and Spring Shell versions should stay in sync
implementation group: 'org.springframework.shell', name: 'spring-shell-starter', version: '3.3.1'
}
2 changes: 0 additions & 2 deletions stairctl/src/main/java/bio/terra/stairctl/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.shell.jline.PromptProvider;

@SpringBootApplication
@ComponentScan(basePackages = {"bio.terra.stairctl"})
public class Application {

public static void main(String[] args) {
Expand Down
68 changes: 10 additions & 58 deletions stairctl/src/main/java/bio/terra/stairctl/ConnectParams.java
Original file line number Diff line number Diff line change
@@ -1,71 +1,23 @@
package bio.terra.stairctl;

import java.util.Optional;
import java.util.Objects;

// Container for DB connection parameters. It provides defaulting from another instance
// of ConnectParams so that we can easily apply the incoming configuration.
public class ConnectParams {
private String username;
private String password;
private String host;
private String port;
private String dbname;

public String getUsername() {
return username;
}

public ConnectParams username(String username) {
this.username = username;
return this;
}

public String getPassword() {
return password;
}

public ConnectParams password(String password) {
this.password = password;
return this;
}

public String getHost() {
return host;
}

public ConnectParams host(String host) {
this.host = host;
return this;
}

public String getPort() {
return port;
}

public ConnectParams port(String port) {
this.port = port;
return this;
}

public String getDbname() {
return dbname;
}

public ConnectParams dbname(String dbname) {
this.dbname = dbname;
return this;
}
public record ConnectParams(
String username, String password, String host, String port, String dbname) {

public String makeUri() {
return String.format("jdbc:postgresql://%s:%s/%s", host, port, dbname);
}

// Fill in any null parameters with those from the provided defaults
public void applyDefaults(ConnectParams defaults) {
username = Optional.ofNullable(username).orElse(defaults.getUsername());
password = Optional.ofNullable(password).orElse(defaults.getPassword());
host = Optional.ofNullable(host).orElse(defaults.getHost());
port = Optional.ofNullable(port).orElse(defaults.getPort());
dbname = Optional.ofNullable(dbname).orElse(defaults.getDbname());
public ConnectParams withDefaults(ConnectParams defaults) {
return new ConnectParams(
Objects.requireNonNullElseGet(username, defaults::username),
Objects.requireNonNullElseGet(password, defaults::password),
Objects.requireNonNullElseGet(host, defaults::host),
Objects.requireNonNullElseGet(port, defaults::port),
Objects.requireNonNullElseGet(dbname, defaults::dbname));
}
}
21 changes: 10 additions & 11 deletions stairctl/src/main/java/bio/terra/stairctl/StairwayService.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package bio.terra.stairctl;

import bio.terra.stairctl.commands.Output;
import bio.terra.stairctl.configuration.StairwayConfiguration;
import bio.terra.stairway.Control;
import bio.terra.stairway.Stairway;
Expand All @@ -13,28 +14,26 @@
import org.apache.commons.dbcp2.PoolingDataSource;
import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class StairwayService {
private static final Logger logger = LoggerFactory.getLogger(StairwayService.class);

private final StairwayConfiguration stairwayConfiguration;
private final Output output;
private Stairway stairway;
private Control control;
private ConnectParams currentConnectParams;

@Autowired
public StairwayService(StairwayConfiguration stairwayConfiguration) {
public StairwayService(StairwayConfiguration stairwayConfiguration, Output output) {
this.stairwayConfiguration = stairwayConfiguration;
this.output = output;
}

public void connectStairway(ConnectParams connectParams) {
// Fill in the defaults from the configuration
connectParams.applyDefaults(stairwayConfiguration.makeConnectParams());
connectParams = connectParams.withDefaults(stairwayConfiguration.makeConnectParams());
DataSource dataSource = configureDataSource(connectParams);

try {
Expand All @@ -53,10 +52,10 @@ public void connectStairway(ConnectParams connectParams) {

control = stairway.getControl();
currentConnectParams = connectParams;
System.out.println("Connected to Stairway");
output.println("Connected to Stairway");
} catch (Exception ex) {
System.err.println("Failed to connect to Stairway or database: " + ex.getMessage());
logger.error("Failed to connect to Stairway", ex);
output.error(
"Failed to connect to Stairway or database using connection: " + connectParams, ex);
}
}

Expand Down Expand Up @@ -84,8 +83,8 @@ public boolean isConnected() {

private DataSource configureDataSource(ConnectParams connectParams) {
Properties props = new Properties();
props.setProperty("user", connectParams.getUsername());
props.setProperty("password", connectParams.getPassword());
props.setProperty("user", connectParams.username());
props.setProperty("password", connectParams.password());
ConnectionFactory connectionFactory =
new DriverManagerConnectionFactory(connectParams.makeUri(), props);
PoolableConnectionFactory poolableConnectionFactory =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
@ShellComponent
public class ConnectCommands {
private final StairwayService stairwayService;
private final Output output;

@Autowired
public ConnectCommands(StairwayService stairwayService) {
public ConnectCommands(StairwayService stairwayService, Output output) {
this.stairwayService = stairwayService;
this.output = output;
}

@ShellMethod(value = "Connect to a Stairway database", key = "connect")
Expand All @@ -31,47 +33,41 @@ public void connect(
defaultValue = ShellOption.NULL)
String dbname,
@ShellOption(
value = {"-h", "--host"},
value = {"-H", "--host"},
defaultValue = ShellOption.NULL)
String host,
@ShellOption(
value = {"-p", "--port"},
defaultValue = ShellOption.NULL)
String port)
throws Exception {
String port) {

ConnectParams connectParams =
new ConnectParams()
.username(username)
.password(password)
.dbname(dbname)
.host(host)
.port(port);
ConnectParams connectParams = new ConnectParams(username, password, host, port, dbname);

disconnectIfConnected();
stairwayService.connectStairway(connectParams);
System.out.println("Connected to Stairway on database " + connectParams.getDbname());
output.println(
"Connected to Stairway on database " + stairwayService.getCurrentConnectParams().dbname());
}

@ShellMethod(value = "Disconnect from a Stairway database", key = "disconnect")
public void disconnect() throws Exception {
public void disconnect() {
disconnectIfConnected();
}

@ShellMethod(value = "Show the current connection", key = "show connection")
public void showConnection() throws Exception {
public void showConnection() {
if (!stairwayService.isConnected()) {
System.out.println("Not connected to a Stairway");
output.println("Not connected to a Stairway");
} else {
Output.showConnection(stairwayService.getCurrentConnectParams());
output.showConnection(stairwayService.getCurrentConnectParams());
}
}

private void disconnectIfConnected() {
if (stairwayService.isConnected()) {
System.out.println(
output.println(
"Disconnecting from Stairway on database "
+ stairwayService.getCurrentConnectParams().getDbname());
+ stairwayService.getCurrentConnectParams().dbname());
stairwayService.disconnectStairway();
}
}
Expand Down
Loading

0 comments on commit baceb1d

Please sign in to comment.