replace lock env with source path
This commit is contained in:
parent
18578b037b
commit
335660f4db
66
README.md
66
README.md
|
@ -151,20 +151,14 @@ interface Lock {
|
|||
path: string;
|
||||
author: string;
|
||||
links: Map<string, string>;
|
||||
// often duplicates of path, but useful for cross-project locks
|
||||
source: string;
|
||||
|
||||
// Timestamps, calculated from --duration and --until
|
||||
created_at: number;
|
||||
updated_at: number;
|
||||
expires_at: number;
|
||||
|
||||
// Env fields
|
||||
// often duplicates of path, but useful for cross-project locks
|
||||
env: {
|
||||
cluster: string;
|
||||
account: string;
|
||||
target?: string; // optional
|
||||
}
|
||||
|
||||
// CI fields, optional
|
||||
ci?: {
|
||||
project: string;
|
||||
|
@ -183,7 +177,7 @@ If `$CI` is not set, the `ci` sub-struct will not be present.
|
|||
- create a new lock: `locked ${path} for ${type:friendly} until ${expires_at:datetime}`
|
||||
- > Locked `apps/acceptance/a` for a deploy until Sat 31 Dec, 12:00
|
||||
- > Locked `gitlab/production` for an incident until Sat 31 Dec, 12:00
|
||||
- error, existing lock: `error: ${path} is locked until ${expires_at:datetime} by ${type:friendly} in ${cluster}/${env}`
|
||||
- error, existing lock: `error: ${path} is locked until ${expires_at:datetime} by ${type:friendly} in ${source}`
|
||||
- > Error: `apps/acceptance` is locked until Sat 31 Dec, 12:00 by an automation run in `testing/staging`.
|
||||
|
||||
#### Friendly Types
|
||||
|
@ -254,24 +248,31 @@ Friendly strings for `type`:
|
|||
- mutually exclusive with `--until`
|
||||
- `--link`
|
||||
- array, strings
|
||||
- `--source`
|
||||
- string
|
||||
- each component:
|
||||
- first
|
||||
- defaults to `$CLUSTER_NAME` if set
|
||||
- defaults to `path.split.0` otherwise
|
||||
- second
|
||||
- defaults to `$DEPLOY_ENV` if set
|
||||
- defaults to `path.split.1` otherwise
|
||||
- third
|
||||
- defaults to `$DEPLOY_TARGET` if set
|
||||
- defaults to `path.split.2` otherwise
|
||||
- fourth
|
||||
- defaults to `--ci-project` if set
|
||||
- defaults to `$CI_PROJECT_PATH` if set
|
||||
- defaults to `path.split.3` otherwise
|
||||
- fifth
|
||||
- defaults to `--ci-ref` if set
|
||||
- defaults to `$CI_COMMIT_REF_SLUG` if set
|
||||
- defaults to `path.split.4` otherwise
|
||||
- `--until`
|
||||
- string, timestamp
|
||||
- duration of lock, absolute
|
||||
- may be given in epoch seconds (`\d+`) or as an ISO-8601 date (intervals are not allowed)
|
||||
- mutually exclusive with `--duration`
|
||||
- `--env-cluster`
|
||||
- string, enum
|
||||
- defaults to `$CLUSTER_NAME` if set
|
||||
- defaults to `path.split.0` otherwise
|
||||
- `--env-account`
|
||||
- string, enum
|
||||
- defaults to `$DEPLOY_ENV` if set
|
||||
- defaults to `path.split.1` otherwise
|
||||
- `--env-target`
|
||||
- optional string
|
||||
- `/^[a-z]$/`
|
||||
- defaults to `$DEPLOY_TARGET` if set
|
||||
- defaults to `path.split.2` otherwise
|
||||
- `--ci-project`
|
||||
- optional string
|
||||
- project path
|
||||
|
@ -334,6 +335,11 @@ Friendly strings for `type`:
|
|||
|
||||
### Features
|
||||
|
||||
- in-memory data store, mostly for testing
|
||||
- DynamoDB data store
|
||||
- lock paths and recursive checking
|
||||
- infer lock data from CI variables
|
||||
|
||||
### Building
|
||||
|
||||
1. Clone with `git clone git@github.com:ssube/deploy-lock.git`
|
||||
|
@ -353,17 +359,18 @@ Friendly strings for `type`:
|
|||
|
||||
### TODOs
|
||||
|
||||
1. SQL data store, with history (don't need to remove old records)
|
||||
2. S3 data store
|
||||
3. REST API with lock endpoints
|
||||
4. Kubernetes admission controller with webhook endpoint
|
||||
1. Infer lock source from other arguments/CI variables
|
||||
2. SQL data store, with history (don't need to remove old records)
|
||||
3. S3 data store
|
||||
4. REST API with lock endpoints
|
||||
5. Kubernetes admission controller with webhook endpoint
|
||||
|
||||
### Questions
|
||||
|
||||
1. In the [deploy path](#deploy-path), should account come before region or region before account?
|
||||
1. `aws/us-east-1/staging` vs `aws/staging/us-east-1`
|
||||
2. This is purely a recommendation in the docs, `lock.path` and `lock.source` (replacing `lock.env`) will both be
|
||||
slash-delimited or array paths.
|
||||
2. This is purely a recommendation in the docs, `lock.path` and `lock.source` will both be slash-delimited or array
|
||||
paths.
|
||||
2. Should there be an `update` or `replace` command?
|
||||
1. Probably not, at least not without lock history or multi-party locks.
|
||||
2. When the data store can keep old locks, `replace` could expire an existing lock and create a new one
|
||||
|
@ -382,7 +389,8 @@ Friendly strings for `type`:
|
|||
2. for an incident: `[first-responder, incident-commander]`
|
||||
4. Each author has to `unlock` before the lock is removed/released
|
||||
5. Should `LockData.env` be a string/array, like `LockData.path`?
|
||||
1. Very probably yes, because otherwise it will need `env.cloud`, `env.network`, etc, and those
|
||||
1. Done
|
||||
2. Very probably yes, because otherwise it will need `env.cloud`, `env.network`, etc, and those
|
||||
are not always predictable/present.
|
||||
6. Should there be an `--allow`/`LockData.allow` field?
|
||||
1. Probably yes
|
||||
|
|
18
src/args.ts
18
src/args.ts
|
@ -31,10 +31,7 @@ export interface ParsedArgs {
|
|||
recursive: boolean;
|
||||
links: Array<string>;
|
||||
now: number;
|
||||
|
||||
'env-cluster'?: string;
|
||||
'env-account'?: string;
|
||||
'env-target'?: string;
|
||||
source: string;
|
||||
|
||||
'ci-project'?: string;
|
||||
'ci-ref'?: string;
|
||||
|
@ -116,6 +113,10 @@ export async function parseArgs(argv: Array<string>): Promise<ParsedArgs> {
|
|||
type: 'string',
|
||||
default: 'us-east-1',
|
||||
},
|
||||
'source': {
|
||||
type: 'string',
|
||||
require: true,
|
||||
},
|
||||
'storage': {
|
||||
type: 'string',
|
||||
choices: Object.keys(STORAGE_TYPES) as ReadonlyArray<StorageType>,
|
||||
|
@ -134,15 +135,6 @@ export async function parseArgs(argv: Array<string>): Promise<ParsedArgs> {
|
|||
'until': {
|
||||
type: 'string',
|
||||
},
|
||||
'env-cluster': {
|
||||
type: 'string',
|
||||
},
|
||||
'env-account': {
|
||||
type: 'string',
|
||||
},
|
||||
'env-target': {
|
||||
type: 'string',
|
||||
},
|
||||
'ci-project': {
|
||||
type: 'string',
|
||||
},
|
||||
|
|
|
@ -16,12 +16,6 @@ export interface LockCI {
|
|||
job: string;
|
||||
}
|
||||
|
||||
export interface LockEnv {
|
||||
cluster: string;
|
||||
account: string;
|
||||
target?: string; // optional
|
||||
}
|
||||
|
||||
export interface LockData {
|
||||
type: LockType;
|
||||
|
||||
|
@ -44,7 +38,7 @@ export interface LockData {
|
|||
/**
|
||||
* Environment where the lock was created. Often duplicates the path, but useful for cross-project locks.
|
||||
*/
|
||||
env: LockEnv;
|
||||
source: string;
|
||||
|
||||
/**
|
||||
* Attribution info when CI was the source of the lock.
|
||||
|
|
14
src/utils.ts
14
src/utils.ts
|
@ -2,7 +2,7 @@
|
|||
import { doesExist, InvalidArgumentError, mustDefault } from '@apextoaster/js-utils';
|
||||
|
||||
import { ParsedArgs } from './args.js';
|
||||
import { LOCK_TYPES, LockCI, LockData, LockEnv } from './lock.js';
|
||||
import { LOCK_TYPES, LockCI, LockData } from './lock.js';
|
||||
|
||||
export function matchPath(baseStr: string, otherStr: string): boolean {
|
||||
const base = splitPath(baseStr);
|
||||
|
@ -53,14 +53,6 @@ export function buildCI(args: ParsedArgs, env: typeof process.env): LockCI | und
|
|||
return undefined;
|
||||
}
|
||||
|
||||
export function buildEnv(args: ParsedArgs, env: typeof process.env): LockEnv {
|
||||
return {
|
||||
cluster: mustDefault(args['env-cluster'], env.CLUSTER_NAME, ENV_UNSET),
|
||||
account: mustDefault(args['env-account'], env.DEPLOY_ENV, ENV_UNSET),
|
||||
target: mustDefault(args['env-target'], env.DEPLOY_TARGET, ENV_UNSET),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildAuthor(args: ParsedArgs, env: typeof process.env): string {
|
||||
if (doesExist(env.GITLAB_CI)) {
|
||||
return mustDefault(args.author, env.GITLAB_USER_EMAIL, env.USER);
|
||||
|
@ -92,14 +84,14 @@ export function buildLock(args: ParsedArgs, env = process.env): LockData {
|
|||
created_at: args.now,
|
||||
updated_at: args.now,
|
||||
expires_at: calculateExpires(args),
|
||||
env: buildEnv(args, env),
|
||||
source: args.source,
|
||||
ci: buildCI(args, env),
|
||||
};
|
||||
}
|
||||
|
||||
export function printLock(path: string, data: LockData): string {
|
||||
const friendlyType = LOCK_TYPES[data.type];
|
||||
return `${path} is locked until ${data.expires_at} by ${friendlyType} in ${data.env.cluster}`;
|
||||
return `${path} is locked until ${data.expires_at} by ${friendlyType} in ${data.source}`;
|
||||
}
|
||||
|
||||
export function calculateExpires(args: ParsedArgs): number {
|
||||
|
|
Loading…
Reference in New Issue