Skip to content

Commit

Permalink
feat: add aws region to config, add 'config' command
Browse files Browse the repository at this point in the history
  • Loading branch information
dwmkerr committed Jan 21, 2024
1 parent 3ec3e0b commit 5cfd11b
Show file tree
Hide file tree
Showing 12 changed files with 94 additions and 29 deletions.
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,37 @@ Additional parameters for `costs` are available:
|-------------------------|---------------------------------------------------|
| `-m`, `--month <month>` | Get costs for a specific month. 1=Jan, 2=Feb etc. |
### `boxes config`
Shows the current configuration that has been loaded for `boxes`. Can be helpful for troubleshooting whether things like the region are set properly:
```bash
% boxes config
{
"boxes": ...
"aws": {
"region": "us-west-2"
}
}
```
## Configuration
A local `boxes.json` file can be used for configuration. The following values are supported:
```
{
"boxes": {
/* box configuration */
},
"aws": {
"region": "us-west-2"
}
}
```
Box configuration is evolving rapidly and the documentation will be updated. The AWS configuration is more stable.
## Enabling Cost Reporting
If you want to be able to show the costs that are associated with each box, you will need to:
Expand Down Expand Up @@ -250,6 +281,14 @@ Note that you will need to rebuild the code if you change it, so run `npm run bu
npm run relink
```
If you are developing and would like to run the `boxes` command without relinking, just build, link, then run:
```bash
npm run build:watch
```
This will keep the `./build` folder up-to-date and the `boxes` command will use the latest compiled code.
### Error Handling
To show a warning and terminate the application, throw a `TerminatingWarning` error:
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
},
"scripts": {
"build": "tsc",
"build:watch": "tsc -w",
"start": "ts-node ./src/cli.ts",
"test": "NODE_OPTIONS='--experimental-vm-modules' jest",
"lint": "eslint .",
Expand Down
9 changes: 9 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { list, info } from "./commands";
import { start } from "./commands/start";
import { stop } from "./commands/stop";
import { ssh } from "./commands/ssh";
import { config } from "./commands/config";
import { getCosts } from "./commands/getCosts";
import { connect } from "./commands/connect";
import theme from "./theme";
Expand Down Expand Up @@ -137,6 +138,14 @@ program
});
});

program
.command("config")
.description("Show current configuration")
.action(async () => {
const configuration = await config();
console.log(JSON.stringify(configuration, null, 2));
});

async function run() {
try {
await program.parseAsync();
Expand Down
6 changes: 6 additions & 0 deletions src/commands/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { getConfiguration } from "../configuration";

export async function config() {
const configuration = await getConfiguration();
return configuration;
}
6 changes: 2 additions & 4 deletions src/commands/connect.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
// Note that 'clipboardy' and 'open' require dynamic imports so that this
// can be packaged as a commonjs module. The dyanmic imports are directly
// used in the 'connect' function below.
// import clipboard from "clipboardy";
import { getBoxes } from "../lib/get-boxes";
import { getBoxConfig } from "../config";
import { getConfiguration } from "../configuration";
import { TerminatingWarning } from "../lib/errors";

export async function connect(
Expand All @@ -13,7 +11,7 @@ export async function connect(
) {
// First, we need to load box configuration. If it is missing, or we don't
// have configuration for the given box, we'll bail.
const boxesConfig = await getBoxConfig();
const boxesConfig = await getConfiguration();
const boxConfig = boxesConfig.boxes[boxId];
if (!boxConfig) {
throw new TerminatingWarning(
Expand Down
4 changes: 2 additions & 2 deletions src/commands/ssh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// used in the 'connect' function below.
// import clipboard from "clipboardy";
import { getBoxes } from "../lib/get-boxes";
import { getBoxConfig } from "../config";
import { getConfiguration } from "../configuration";
import { TerminatingWarning } from "../lib/errors";

export async function ssh(
Expand All @@ -13,7 +13,7 @@ export async function ssh(
) {
// First, we need to load box configuration. If it is missing, or we don't
// have configuration for the given box, we'll bail.
const boxesConfig = await getBoxConfig();
const boxesConfig = await getConfiguration();
const boxConfig = boxesConfig.boxes[boxId];
if (!boxConfig) {
throw new TerminatingWarning(
Expand Down
4 changes: 3 additions & 1 deletion src/commands/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { EC2Client, StartInstancesCommand } from "@aws-sdk/client-ec2";
import { TerminatingWarning } from "../lib/errors";
import { getBoxes } from "../lib/get-boxes";
import { BoxState, awsStateToBoxState } from "../box";
import { getConfiguration } from "../configuration";

export interface BoxTransition {
boxId: string;
Expand All @@ -26,7 +27,8 @@ export async function start(boxId: string): Promise<BoxTransition> {
}

// Create an EC2 client.
const client = new EC2Client({});
const { aws: awsConfig } = await getConfiguration();
const client = new EC2Client(awsConfig);

// Send the 'stop instances' command. Find the status of the stopping
// instance in the respose.
Expand Down
4 changes: 3 additions & 1 deletion src/commands/stop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { EC2Client, StopInstancesCommand } from "@aws-sdk/client-ec2";
import { TerminatingWarning } from "../lib/errors";
import { getBoxes } from "../lib/get-boxes";
import { awsStateToBoxState } from "../box";
import { getConfiguration } from "../configuration";

export async function stop(boxId: string, detach: boolean) {
// Get the box, fail with a warning if it is not found.
Expand All @@ -19,7 +20,8 @@ export async function stop(boxId: string, detach: boolean) {
}

// Create an EC2 client.
const client = new EC2Client({});
const { aws: awsConfig } = await getConfiguration();
const client = new EC2Client(awsConfig);

// Send the 'stop instances' command. Find the status of the stopping
// instance in the respose.
Expand Down
9 changes: 0 additions & 9 deletions src/config.ts

This file was deleted.

33 changes: 23 additions & 10 deletions src/configuration.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
// TODO wip
// {
// aws: {
// region: configuration("string")
// .optional()
// .default(null)
// .fromConfigFile("aws/region")
// .fromParam("awsRegion");
// }
// }
import fs from "fs/promises";

interface AwsConfig {
region: string | undefined;
}

export async function getConfiguration() {
// For now, box config is hard coded to the current location.
const boxConfigPath = "./boxes.json";
const data = await fs.readFile(boxConfigPath, "utf8");
const json = JSON.parse(data);

// If we have an aws key and a region key, set it.
const awsRegion = json["aws"]?.["region"];
const awsConfig: AwsConfig = {
...(awsRegion && { region: awsRegion }),
};

return {
...json,
aws: awsConfig,
};
}
4 changes: 3 additions & 1 deletion src/lib/get-boxes-costs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
GetCostAndUsageCommand,
} from "@aws-sdk/client-cost-explorer"; // ES Modules import
import { TerminatingWarning } from "./errors";
import { getConfiguration } from "../configuration";

function dateToLocalDateString(date: Date): string {
const year = `${date.getFullYear()}`.padStart(4, "0");
Expand Down Expand Up @@ -30,7 +31,8 @@ export interface BoxCostDescription {
export async function getBoxesCosts(
options?: GetBoxesCostsOptions,
): Promise<BoxCostDescription[]> {
const client = new CostExplorerClient({});
const { aws: awsConfig } = await getConfiguration();
const client = new CostExplorerClient(awsConfig);

// If the caller has specified a year, we must also have a month.
// It is fine to just have a month on it's own - it'll default to
Expand Down
4 changes: 3 additions & 1 deletion src/lib/get-boxes.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { EC2Client, DescribeInstancesCommand, Tag } from "@aws-sdk/client-ec2";
import { Box, awsStateToBoxState } from "../box";
import { TerminatingWarning } from "./errors";
import { getConfiguration } from "../configuration";

export async function getBoxes(): Promise<Box[]> {
const client = new EC2Client({});
const { aws: awsConfig } = await getConfiguration();
const client = new EC2Client(awsConfig);

const instancesResponse = await client.send(
new DescribeInstancesCommand({
Expand Down

0 comments on commit 5cfd11b

Please sign in to comment.