Compare commits

..

52 Commits

Author SHA1 Message Date
703dbe0a0f Merge branch 'main' into command_help_2
All checks were successful
continuous-integration/drone/pr Build is passing
2021-01-01 13:09:57 +01:00
7abb0191e1 Use upstream CI image
All checks were successful
continuous-integration/drone/push Build is passing
See https://git.autonomic.zone/coop-cloud/docker-dind-bats-kcov.
2021-01-01 12:26:50 +01:00
3wc
886ae5b7f2 Fix help function names
All checks were successful
continuous-integration/drone/pr Build is passing
2021-01-01 01:46:09 +02:00
3wc
5411c85793 Don't require weird app_ls format for abra help
All checks were successful
continuous-integration/drone/pr Build is passing
2021-01-01 01:42:17 +02:00
3wc
caa315e361 Add per-subcommand help 2021-01-01 01:13:17 +02:00
3wc
e6b24fe65c Change -v option to version subcommand 2021-01-01 01:10:03 +02:00
3wc
81782bb5f0 make test → test_local, test_docker, test_dind
All checks were successful
continuous-integration/drone/push Build is passing
test_local: run `bats tests/`. Requires `bats`.

test_docker: run docker and install bats in it.

Both of these options require that your local Docker daemon is running
in swarm mode.

test_dind: starts a Docker daemon and runs tests in that. Requires sudo
2020-12-31 22:10:52 +02:00
406b9e374e Only output length if using it
All checks were successful
continuous-integration/drone/push Build is passing
Closes https://git.autonomic.zone/coop-cloud/abra/issues/67.
2020-12-31 18:16:01 +01:00
ce0e0e893c Use plain usage only when erroring out
All checks were successful
continuous-integration/drone/push Build is passing
Closes https://git.autonomic.zone/coop-cloud/abra/issues/65.
2020-12-31 18:12:01 +01:00
fac45f276e Add entry
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-31 18:11:22 +01:00
44d3ac3a1c Support pwqgen/pwgen checking
Closes https://git.autonomic.zone/coop-cloud/abra/issues/66.
2020-12-31 18:10:13 +01:00
5da9f26076 Remove old function
Follow on from 3936d6afc0.
2020-12-31 18:06:20 +01:00
4e99cf1ded Add log entry
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-31 16:57:10 +01:00
55324524ca Don't cut since export ... isn't in the env file
Closes https://git.autonomic.zone/coop-cloud/abra/issues/69.
2020-12-31 16:55:33 +01:00
b6928959cb Fix test target path
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-31 13:50:10 +01:00
3wc
8ddb290683 Further update to tests for #47
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-31 14:34:52 +02:00
2cb1134a54 Use _ now
Some checks failed
continuous-integration/drone/push Build is failing
2020-12-31 13:26:25 +01:00
c4b1ac482e Update change log
Some checks failed
continuous-integration/drone/push Build is failing
2020-12-31 13:22:26 +01:00
3wc
29cc392dff Prompt on app .. config if $EDITOR is un-set
Some checks failed
continuous-integration/drone/push Build is failing
Closes #41
2020-12-31 12:52:44 +02:00
3wc
8839bd4595 Fix server bash completion
Some checks failed
continuous-integration/drone/push Build is failing
Ref #45
2020-12-31 11:47:14 +02:00
3wc
0179f600f5 Change -v option to version subcommand 2020-12-31 11:47:12 +02:00
15f0233351 Merge pull request '<domain> → <app>, APP → TYPE' (#60) from domain_to_app into main
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: https://git.autonomic.zone/coop-cloud/abra/pulls/60
2020-12-30 22:22:11 +01:00
3wc
bbaacb7b9f Update tests for app-name
Some checks failed
continuous-integration/drone/pr Build is failing
2020-12-30 21:56:52 +02:00
3wc
6761574d74 Add "app name" concept, and rename APP → TYPE
Some checks failed
continuous-integration/drone/pr Build is failing
Ref #47
2020-12-30 21:26:17 +02:00
3wc
8384af8b95 First foray into <domain> → <app> 2020-12-30 13:47:41 +02:00
3wc
b9e97688d6 ... and update docopt
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-30 13:43:17 +02:00
3wc
1055805c8d Merge duplicate run commands
All checks were successful
continuous-integration/drone/push Build is passing
Closes #57
2020-12-30 13:33:57 +02:00
678906cb39 Merge pull request 'Use set -a/+a and docker env file formats' (#55) from use-set-a into main
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: https://git.autonomic.zone/coop-cloud/abra/pulls/55
2020-12-30 11:23:30 +01:00
11c50ae98d Use set -a/+a and docker env file formats
Some checks failed
continuous-integration/drone/pr Build is failing
Closes https://git.autonomic.zone/coop-cloud/abra/issues/40.
2020-12-30 11:22:58 +01:00
e911ab246b Merge pull request 'Merge logs/multilogs and avoid multitail external' (#56) from merge-logging into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: https://git.autonomic.zone/coop-cloud/abra/pulls/56
2020-12-30 11:21:46 +01:00
34775f306b Merge branch 'main' into merge-logging
All checks were successful
continuous-integration/drone/pr Build is passing
2020-12-30 11:21:03 +01:00
375a4dd29d Re-add require_app_latest for custom commands
All checks were successful
continuous-integration/drone/push Build is passing
Revision of 67cce192df.

See https://git.autonomic.zone/coop-cloud/abra/pulls/54#issuecomment-2300.
2020-12-30 11:19:55 +01:00
2b951e9f54 Mark minor
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-30 11:18:32 +01:00
3wc
01184c313a Add missing CHANGELOG entries
All checks were successful
continuous-integration/drone/push Build is passing
Closes #46
2020-12-30 00:43:52 +02:00
3936d6afc0 Merge logs/multilogs and avoid multitail external
All checks were successful
continuous-integration/drone/pr Build is passing
2020-12-29 23:22:46 +01:00
407744827f Add change log entry for #42
All checks were successful
continuous-integration/drone/push Build is passing
See https://git.autonomic.zone/coop-cloud/abra/issues/42.
2020-12-29 17:11:30 +01:00
b634b4c668 Merge pull request 'Make sure to git pull latest changes on ~/.abra/apps side' (#54) from latest-checks into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: https://git.autonomic.zone/coop-cloud/abra/pulls/54
2020-12-29 17:10:16 +01:00
67cce192df Don't do more cloning that necessary
All checks were successful
continuous-integration/drone/pr Build is passing
2020-12-29 17:06:49 +01:00
3a9e141b24 Pull latest changes
Closes https://git.autonomic.zone/coop-cloud/abra/issues/42.
2020-12-29 17:06:32 +01:00
ebfe7ca4e8 Suppress output of clone and do better logging 2020-12-29 17:05:30 +01:00
fff2fbe819 Prepare function name for new functionality 2020-12-29 17:05:00 +01:00
f213c3df5f Follow convention and show type of message 2020-12-29 17:04:17 +01:00
9b1be33018 Mark as quote and not as entry
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-29 15:11:15 +01:00
6ecf4f287a Add missing )
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-29 15:10:57 +01:00
e1d6ff8b73 Add docs link
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-29 15:10:05 +01:00
07d4815a74 Start 0.5.0 change log
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-29 15:08:42 +01:00
33315f6b43 Merge pull request '<app> -> <type>' (#53) from app-goes-to-type into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: https://git.autonomic.zone/coop-cloud/abra/pulls/53
2020-12-29 15:03:43 +01:00
f017324431 <app> -> <type>
All checks were successful
continuous-integration/drone/pr Build is passing
Closes https://git.autonomic.zone/coop-cloud/abra/issues/48.
2020-12-29 14:56:50 +01:00
4339c91cf3 Merge pull request 'Remove abra server use' (#52) from server-use-remove into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: https://git.autonomic.zone/coop-cloud/abra/pulls/52
2020-12-29 14:26:39 +01:00
592f515ec4 Remove abra server use
All checks were successful
continuous-integration/drone/pr Build is passing
2020-12-29 14:24:50 +01:00
3wc
77ba5652b2 Run "check" during "deploy", + "--skip-check"
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-29 11:15:14 +02:00
3wc
fdf6334ed3 Use temporary ABRA_DIr in tests
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-27 21:53:44 +02:00
6 changed files with 378 additions and 203 deletions

View File

@ -1,6 +1,29 @@
# abra x.x.x (UNRELEASED) # abra x.x.x (UNRELEASED)
- ??? - Avoid outputting length during secret generation when not in use ([#67](https://git.autonomic.zone/coop-cloud/abra/issues/67))
- Support graceful failure when missing secret generation commands ([44d3ac3a1cb86edc9b9e91eea1a00e70eae14965](https://git.autonomic.zone/coop-cloud/abra/commit/44d3ac3a1cb86edc9b9e91eea1a00e70eae14965))
- Fix secret detection when using new `.env` file format in apps ([55324524ca77141666ffe6cc41b62cc71cf89ace](https://git.autonomic.zone/coop-cloud/abra/commit/55324524ca77141666ffe6cc41b62cc71cf89ace))
- Support choosing an `$EDITOR` when editing configs ([29cc392dff3e93e48e0e2edd3ce11b405c66a95a](https://git.autonomic.zone/coop-cloud/abra/commit/29cc392dff3e93e48e0e2edd3ce11b405c66a95a))
- "server" shell completion fixed ([8839bd45951d00dccf4ef81ece445bcc49e13ee6](https://git.autonomic.zone/coop-cloud/abra/commit/8839bd45951d00dccf4ef81ece445bcc49e13ee6))
- Make `-v` spit out the version ([0179f600f5ce40fc1520fc365f92b7cb6eeec1f5](https://git.autonomic.zone/coop-cloud/abra/commit/0179f600f5ce40fc1520fc365f92b7cb6eeec1f5))
- Drop `multilogs` command ([#56](https://git.autonomic.zone/coop-cloud/abra/pulls/56))
- Remove `server use` command ([#51](https://git.autonomic.zone/coop-cloud/abra/issues/51))
- `new <app>` becomes `new <type>` ([#48](https://git.autonomic.zone/coop-cloud/abra/issues/48))
- `check` is run on `deploy` now and configurable ([77ba5652b2fe15820f5edfa0f642636f7b8eae7e](https://git.autonomic.zone/coop-cloud/abra/commit/77ba5652b2fe15820f5edfa0f642636f7b8eae7e))
- App configurations are always updated now ([#42](https://git.autonomic.zone/coop-cloud/abra/issues/42))
- We use docker format `.env` files (no "export" syntax) from now now ([#55](https://git.autonomic.zone/coop-cloud/abra/pulls/55))
- Rename `<domain>` option to `<app>` and `APP` variable to `TYPE`, see [#47](https://git.autonomic.zone/coop-cloud/abra/pulls/55)
# abra 0.4.1 (2020-12-24)
- Bug-fixes on `app ls --status` & custom commands
- Add `app ls --server=...` and alias
# abra 0.4.0 (2020-12-24)
- New command-line interface based on docopt
- `~/.abra` directory instead of expecting local `.env` files
- Integration tests & code coverage
# abra 0.3.1 (2020-09-27) # abra 0.3.1 (2020-09-27)
@ -8,7 +31,7 @@
# abra 0.3.0 (2020-09-27) # abra 0.3.0 (2020-09-27)
- Add multilogs stack logs implementation ([#8](https://git.autonomic.zone/compose-stacks/abra/issues/8) - Add multilogs stack logs implementation ([#8](https://git.autonomic.zone/compose-stacks/abra/issues/8))
- Add beginnings of "monorepo" functionality - Add beginnings of "monorepo" functionality
# abra 0.2.0 (2020-09-24) # abra 0.2.0 (2020-09-24)

View File

@ -1,12 +1,30 @@
.PHONY: test shellcheck docopt kcov codecov .PHONY: test shellcheck docopt kcov codecov
test: test_dind:
@sudo DOCKER_CONTEXT=default docker run \
-v $$(pwd):/workdir \
--privileged \
-d \
--name=abra-test-dind \
-e DOCKER_TLS_CERTDIR="" \
decentral1se/docker-dind-bats-kcov
@DOCKER_CONTEXT=default docker exec \
-it \
abra-test-dind \
sh -c "cd /workdir && bats /workdir/tests"
@DOCKER_CONTEXT=default docker stop abra-test-dind
@DOCKER_CONTEXT=default docker rm abra-test-dind
test_docker:
@DOCKER_CONTEXT=default docker run \ @DOCKER_CONTEXT=default docker run \
-it \ -it \
--rm \ --rm \
-v $$(pwd):/workdir \ -v $$(pwd):/workdir \
docker:dind \ decentral1se/docker-dind-bats-kcov \
sh -c "apk add bats git bash && cd /workdir && bats /workdir/test.bats" sh -c "cd /workdir && bats /workdir/tests"
test_local:
bats tests
shellcheck: shellcheck:
@docker run \ @docker run \

View File

@ -7,6 +7,10 @@
The cooperative cloud utility belt 🎩🐇 The cooperative cloud utility belt 🎩🐇
## Documentation
> [docs.cloud.autonomic.zone](https://docs.cloud.autonomic.zone/)
## Install ## Install
```sh ```sh

479
abra
View File

@ -2,6 +2,7 @@
GIT_URL="https://git.autonomic.zone/coop-cloud/" GIT_URL="https://git.autonomic.zone/coop-cloud/"
ABRA_DIR="${ABRA_DIR:-$HOME/.abra}" ABRA_DIR="${ABRA_DIR:-$HOME/.abra}"
ABRA_VERSION="0.4.1"
####################################### #######################################
# Global help # Global help
@ -11,37 +12,35 @@ DOC="
The cooperative cloud utility belt 🎩🐇 The cooperative cloud utility belt 🎩🐇
Usage: Usage:
abra [options] app new [--server=<server>] [--domain=<domain>] [--pass] [--auto] <app> abra [options] app new [--server=<server>] [--domain=<domain>] [--app-name=<app_name>] [--pass] [--auto] <type>
abra [options] app (list|ls) [--status] [--server=<server>] abra [options] app (list|ls) [--status] [--server=<server>]
abra [options] app <domain> deploy abra [options] app <app> deploy [--skip-check]
abra [options] app <domain> undeploy abra [options] app <app> undeploy
abra [options] app <domain> config abra [options] app <app> config
abra [options] app <domain> (delete|rm) [--force] abra [options] app <app> (delete|rm) [--force]
abra [options] app <domain> logs [<service>] abra [options] app <app> logs [<service>]
abra [options] app <domain> multilogs abra [options] app <app> cp <src> <dst>
abra [options] app <domain> cp <src> <dst> abra [options] app <app> check
abra [options] app <domain> check abra [options] app <app> ps
abra [options] app <domain> ps abra [options] app <app> run [--no-tty] [--user=<user>] <service> <args>...
abra [options] app <domain> run [--no-tty] [--user=<user>] <service> <args>... abra [options] app <app> secret auto [--pass]
abra [options] app <domain> run <service> <args>... abra [options] app <app> secret generate <secret> <version> [<cmd>] [--pass]
abra [options] app <domain> secret auto [--pass] abra [options] app <app> secret insert <secret> <version> <data> [--pass]
abra [options] app <domain> secret generate <secret> <version> [<cmd>] [--pass] abra [options] app <app> secret (delete|rm) (<secret>|--all) [--pass] [--force]
abra [options] app <domain> secret insert <secret> <version> <data> [--pass] abra [options] app <app> <command> [<args>...]
abra [options] app <domain> secret (delete|rm) (<secret>|--all) [--pass] [--force]
abra [options] app <domain> <command> [<args>...]
abra [options] server add <host> [<user>] [<port>] abra [options] server add <host> [<user>] [<port>]
abra [options] server (list|ls) abra [options] server (list|ls)
abra [options] server <host> rm abra [options] server <host> rm
abra [options] server <host> use
abra [options] server <host> init abra [options] server <host> init
abra [options] server <host> apps [--status] abra [options] server <host> apps [--status]
abra [options] upgrade abra [options] upgrade
abra [options] version
abra [options] help [<subcommands>...]
Options: Options:
-e, --env=<path> Environment variables to load -e, --env=<path> Environment variables to load
-h, --help Show this message and exit -h, --help Show this message and exit
-s, --stack=<stack> Name of the target stack -s, --stack=<stack> Name of the target stack
-v, --version Show program version
See 'abra help <command>' to read about a specific subcommand. See 'abra help <command>' to read about a specific subcommand.
" "
@ -145,92 +144,95 @@ eval "var_$1+=($value)"; else eval "var_$1=$value"; fi; return 0; fi; done
return 1; }; stdout() { printf -- "cat <<'EOM'\n%s\nEOM\n" "$1"; }; stderr() { return 1; }; stdout() { printf -- "cat <<'EOM'\n%s\nEOM\n" "$1"; }; stderr() {
printf -- "cat <<'EOM' >&2\n%s\nEOM\n" "$1"; }; error() { printf -- "cat <<'EOM' >&2\n%s\nEOM\n" "$1"; }; error() {
[[ -n $1 ]] && stderr "$1"; stderr "$usage"; _return 1; }; _return() { [[ -n $1 ]] && stderr "$1"; stderr "$usage"; _return 1; }; _return() {
printf -- "exit %d\n" "$1"; exit "$1"; }; set -e; trimmed_doc=${DOC:1:1579} printf -- "exit %d\n" "$1"; exit "$1"; }; set -e; trimmed_doc=${DOC:1:1470}
usage=${DOC:40:1271}; digest=974cf usage=${DOC:40:1206}; digest=b4523
shorts=(-h -e -s -v '' '' '' '' '' '' '' '' '') shorts=(-h -e -s '' '' '' '' '' '' '' '' '' '' '')
longs=(--help --env --stack --version --server --domain --pass --auto --status --force --no-tty --user --all) longs=(--help --env --stack --server --domain --app-name --pass --auto --status --skip-check --force --no-tty --user --all)
argcounts=(0 1 1 0 1 1 0 0 0 0 0 1 0); node_0(){ switch __help 0; }; node_1(){ argcounts=(0 1 1 1 1 1 0 0 0 0 0 0 1 0); node_0(){ switch __help 0; }; node_1(){
value __env 1; }; node_2(){ value __stack 2; }; node_3(){ switch __version 3; } value __env 1; }; node_2(){ value __stack 2; }; node_3(){ value __server 3; }
node_4(){ value __server 4; }; node_5(){ value __domain 5; }; node_6(){ node_4(){ value __domain 4; }; node_5(){ value __app_name 5; }; node_6(){
switch __pass 6; }; node_7(){ switch __auto 7; }; node_8(){ switch __status 8; } switch __pass 6; }; node_7(){ switch __auto 7; }; node_8(){ switch __status 8; }
node_9(){ switch __force 9; }; node_10(){ switch __no_tty 10; }; node_11(){ node_9(){ switch __skip_check 9; }; node_10(){ switch __force 10; }; node_11(){
value __user 11; }; node_12(){ switch __all 12; }; node_13(){ value _app_ a; } switch __no_tty 11; }; node_12(){ value __user 12; }; node_13(){ switch __all 13
node_14(){ value _domain_ a; }; node_15(){ value _service_ a; }; node_16(){ }; node_14(){ value _type_ a; }; node_15(){ value _app_ a; }; node_16(){
value _src_ a; }; node_17(){ value _dst_ a; }; node_18(){ value _args_ a true; } value _service_ a; }; node_17(){ value _src_ a; }; node_18(){ value _dst_ a; }
node_19(){ value _secret_ a; }; node_20(){ value _version_ a; }; node_21(){ node_19(){ value _args_ a true; }; node_20(){ value _secret_ a; }; node_21(){
value _cmd_ a; }; node_22(){ value _data_ a; }; node_23(){ value _command_ a; } value _version_ a; }; node_22(){ value _cmd_ a; }; node_23(){ value _data_ a; }
node_24(){ value _host_ a; }; node_25(){ value _user_ a; }; node_26(){ node_24(){ value _command_ a; }; node_25(){ value _host_ a; }; node_26(){
value _port_ a; }; node_27(){ _command app; }; node_28(){ _command new; } value _user_ a; }; node_27(){ value _port_ a; }; node_28(){
node_29(){ _command list; }; node_30(){ _command ls; }; node_31(){ value _subcommands_ a true; }; node_29(){ _command app; }; node_30(){
_command deploy; }; node_32(){ _command undeploy; }; node_33(){ _command config _command new; }; node_31(){ _command list; }; node_32(){ _command ls; }
}; node_34(){ _command delete; }; node_35(){ _command rm; }; node_36(){ node_33(){ _command deploy; }; node_34(){ _command undeploy; }; node_35(){
_command logs; }; node_37(){ _command multilogs; }; node_38(){ _command cp; } _command config; }; node_36(){ _command delete; }; node_37(){ _command rm; }
node_39(){ _command check; }; node_40(){ _command ps; }; node_41(){ _command run node_38(){ _command logs; }; node_39(){ _command cp; }; node_40(){
}; node_42(){ _command secret; }; node_43(){ _command auto; }; node_44(){ _command check; }; node_41(){ _command ps; }; node_42(){ _command run; }
_command generate; }; node_45(){ _command insert; }; node_46(){ _command server node_43(){ _command secret; }; node_44(){ _command auto; }; node_45(){
}; node_47(){ _command add; }; node_48(){ _command use; }; node_49(){ _command generate; }; node_46(){ _command insert; }; node_47(){ _command server
_command init; }; node_50(){ _command apps; }; node_51(){ _command upgrade; } }; node_48(){ _command add; }; node_49(){ _command init; }; node_50(){
node_52(){ optional 0 1 2 3; }; node_53(){ optional 52; }; node_54(){ optional 4 _command apps; }; node_51(){ _command upgrade; }; node_52(){ _command version; }
}; node_55(){ optional 5; }; node_56(){ optional 6; }; node_57(){ optional 7; } node_53(){ _command help; }; node_54(){ optional 0 1 2; }; node_55(){
node_58(){ required 53 27 28 54 55 56 57 13; }; node_59(){ either 29 30; } optional 54; }; node_56(){ optional 3; }; node_57(){ optional 4; }; node_58(){
node_60(){ required 59; }; node_61(){ optional 8; }; node_62(){ optional 5; }; node_59(){ optional 6; }; node_60(){ optional 7; }; node_61(){
required 53 27 60 61 54; }; node_63(){ required 53 27 14 31; }; node_64(){ required 55 29 30 56 57 58 59 60 14; }; node_62(){ either 31 32; }; node_63(){
required 53 27 14 32; }; node_65(){ required 53 27 14 33; }; node_66(){ required 62; }; node_64(){ optional 8; }; node_65(){ required 55 29 63 64 56; }
either 34 35; }; node_67(){ required 66; }; node_68(){ optional 9; }; node_69(){ node_66(){ optional 9; }; node_67(){ required 55 29 15 33 66; }; node_68(){
required 53 27 14 67 68; }; node_70(){ optional 15; }; node_71(){ required 55 29 15 34; }; node_69(){ required 55 29 15 35; }; node_70(){
required 53 27 14 36 70; }; node_72(){ required 53 27 14 37; }; node_73(){ either 36 37; }; node_71(){ required 70; }; node_72(){ optional 10; }
required 53 27 14 38 16 17; }; node_74(){ required 53 27 14 39; }; node_75(){ node_73(){ required 55 29 15 71 72; }; node_74(){ optional 16; }; node_75(){
required 53 27 14 40; }; node_76(){ optional 10; }; node_77(){ optional 11; } required 55 29 15 38 74; }; node_76(){ required 55 29 15 39 17 18; }; node_77(){
node_78(){ oneormore 18; }; node_79(){ required 53 27 14 41 76 77 15 78; } required 55 29 15 40; }; node_78(){ required 55 29 15 41; }; node_79(){
node_80(){ required 53 27 14 41 15 78; }; node_81(){ required 53 27 14 42 43 56 optional 11; }; node_80(){ optional 12; }; node_81(){ oneormore 19; }
}; node_82(){ optional 21; }; node_83(){ required 53 27 14 42 44 19 20 82 56; } node_82(){ required 55 29 15 42 79 80 16 81; }; node_83(){
node_84(){ required 53 27 14 42 45 19 20 22 56; }; node_85(){ either 19 12; } required 55 29 15 43 44 59; }; node_84(){ optional 22; }; node_85(){
node_86(){ required 85; }; node_87(){ required 53 27 14 42 67 86 56 68; } required 55 29 15 43 45 20 21 84 59; }; node_86(){
node_88(){ optional 78; }; node_89(){ required 53 27 14 23 88; }; node_90(){ required 55 29 15 43 46 20 21 23 59; }; node_87(){ either 20 13; }; node_88(){
optional 25; }; node_91(){ optional 26; }; node_92(){ required 53 46 47 24 90 91 required 87; }; node_89(){ required 55 29 15 43 71 88 59 72; }; node_90(){
}; node_93(){ required 53 46 60; }; node_94(){ required 53 46 24 35; } optional 81; }; node_91(){ required 55 29 15 24 90; }; node_92(){ optional 26; }
node_95(){ required 53 46 24 48; }; node_96(){ required 53 46 24 49; } node_93(){ optional 27; }; node_94(){ required 55 47 48 25 92 93; }; node_95(){
node_97(){ required 53 46 24 50 61; }; node_98(){ required 53 51; }; node_99(){ required 55 47 63; }; node_96(){ required 55 47 25 37; }; node_97(){
either 58 62 63 64 65 69 71 72 73 74 75 79 80 81 83 84 87 89 92 93 94 95 96 97 98 required 55 47 25 49; }; node_98(){ required 55 47 25 50 64; }; node_99(){
}; node_100(){ required 99; }; cat <<<' docopt_exit() { required 55 51; }; node_100(){ required 55 52; }; node_101(){ oneormore 28; }
[[ -n $1 ]] && printf "%s\n" "$1" >&2; printf "%s\n" "${DOC:40:1271}" >&2 node_102(){ optional 101; }; node_103(){ required 55 53 102; }; node_104(){
exit 1; }'; unset var___help var___env var___stack var___version var___server \ either 61 65 67 68 69 73 75 76 77 78 82 83 85 86 89 91 94 95 96 97 98 99 100 103
var___domain var___pass var___auto var___status var___force var___no_tty \ }; node_105(){ required 104; }; cat <<<' docopt_exit() {
var___user var___all var__app_ var__domain_ var__service_ var__src_ var__dst_ \ [[ -n $1 ]] && printf "%s\n" "$1" >&2; printf "%s\n" "${DOC:40:1206}" >&2
var__args_ var__secret_ var__version_ var__cmd_ var__data_ var__command_ \ exit 1; }'; unset var___help var___env var___stack var___server var___domain \
var__host_ var__user_ var__port_ var_app var_new var_list var_ls var_deploy \ var___app_name var___pass var___auto var___status var___skip_check var___force \
var_undeploy var_config var_delete var_rm var_logs var_multilogs var_cp \ var___no_tty var___user var___all var__type_ var__app_ var__service_ var__src_ \
var_check var_ps var_run var_secret var_auto var_generate var_insert \ var__dst_ var__args_ var__secret_ var__version_ var__cmd_ var__data_ \
var_server var_add var_use var_init var_apps var_upgrade; parse 100 "$@" var__command_ var__host_ var__user_ var__port_ var__subcommands_ var_app \
local prefix=${DOCOPT_PREFIX:-''}; unset "${prefix}__help" "${prefix}__env" \ var_new var_list var_ls var_deploy var_undeploy var_config var_delete var_rm \
"${prefix}__stack" "${prefix}__version" "${prefix}__server" \ var_logs var_cp var_check var_ps var_run var_secret var_auto var_generate \
"${prefix}__domain" "${prefix}__pass" "${prefix}__auto" "${prefix}__status" \ var_insert var_server var_add var_init var_apps var_upgrade var_version var_help
"${prefix}__force" "${prefix}__no_tty" "${prefix}__user" "${prefix}__all" \ parse 105 "$@"; local prefix=${DOCOPT_PREFIX:-''}; unset "${prefix}__help" \
"${prefix}_app_" "${prefix}_domain_" "${prefix}_service_" "${prefix}_src_" \ "${prefix}__env" "${prefix}__stack" "${prefix}__server" "${prefix}__domain" \
"${prefix}_dst_" "${prefix}_args_" "${prefix}_secret_" "${prefix}_version_" \ "${prefix}__app_name" "${prefix}__pass" "${prefix}__auto" "${prefix}__status" \
"${prefix}_cmd_" "${prefix}_data_" "${prefix}_command_" "${prefix}_host_" \ "${prefix}__skip_check" "${prefix}__force" "${prefix}__no_tty" \
"${prefix}_user_" "${prefix}_port_" "${prefix}app" "${prefix}new" \ "${prefix}__user" "${prefix}__all" "${prefix}_type_" "${prefix}_app_" \
"${prefix}list" "${prefix}ls" "${prefix}deploy" "${prefix}undeploy" \ "${prefix}_service_" "${prefix}_src_" "${prefix}_dst_" "${prefix}_args_" \
"${prefix}config" "${prefix}delete" "${prefix}rm" "${prefix}logs" \ "${prefix}_secret_" "${prefix}_version_" "${prefix}_cmd_" "${prefix}_data_" \
"${prefix}multilogs" "${prefix}cp" "${prefix}check" "${prefix}ps" \ "${prefix}_command_" "${prefix}_host_" "${prefix}_user_" "${prefix}_port_" \
"${prefix}run" "${prefix}secret" "${prefix}auto" "${prefix}generate" \ "${prefix}_subcommands_" "${prefix}app" "${prefix}new" "${prefix}list" \
"${prefix}insert" "${prefix}server" "${prefix}add" "${prefix}use" \ "${prefix}ls" "${prefix}deploy" "${prefix}undeploy" "${prefix}config" \
"${prefix}init" "${prefix}apps" "${prefix}upgrade" "${prefix}delete" "${prefix}rm" "${prefix}logs" "${prefix}cp" "${prefix}check" \
eval "${prefix}"'__help=${var___help:-false}' "${prefix}ps" "${prefix}run" "${prefix}secret" "${prefix}auto" \
"${prefix}generate" "${prefix}insert" "${prefix}server" "${prefix}add" \
"${prefix}init" "${prefix}apps" "${prefix}upgrade" "${prefix}version" \
"${prefix}help"; eval "${prefix}"'__help=${var___help:-false}'
eval "${prefix}"'__env=${var___env:-}' eval "${prefix}"'__env=${var___env:-}'
eval "${prefix}"'__stack=${var___stack:-}' eval "${prefix}"'__stack=${var___stack:-}'
eval "${prefix}"'__version=${var___version:-false}'
eval "${prefix}"'__server=${var___server:-}' eval "${prefix}"'__server=${var___server:-}'
eval "${prefix}"'__domain=${var___domain:-}' eval "${prefix}"'__domain=${var___domain:-}'
eval "${prefix}"'__app_name=${var___app_name:-}'
eval "${prefix}"'__pass=${var___pass:-false}' eval "${prefix}"'__pass=${var___pass:-false}'
eval "${prefix}"'__auto=${var___auto:-false}' eval "${prefix}"'__auto=${var___auto:-false}'
eval "${prefix}"'__status=${var___status:-false}' eval "${prefix}"'__status=${var___status:-false}'
eval "${prefix}"'__skip_check=${var___skip_check:-false}'
eval "${prefix}"'__force=${var___force:-false}' eval "${prefix}"'__force=${var___force:-false}'
eval "${prefix}"'__no_tty=${var___no_tty:-false}' eval "${prefix}"'__no_tty=${var___no_tty:-false}'
eval "${prefix}"'__user=${var___user:-}' eval "${prefix}"'__user=${var___user:-}'
eval "${prefix}"'__all=${var___all:-false}' eval "${prefix}"'__all=${var___all:-false}'
eval "${prefix}"'_app_=${var__app_:-}' eval "${prefix}"'_type_=${var__type_:-}'; eval "${prefix}"'_app_=${var__app_:-}'
eval "${prefix}"'_domain_=${var__domain_:-}'
eval "${prefix}"'_service_=${var__service_:-}' eval "${prefix}"'_service_=${var__service_:-}'
eval "${prefix}"'_src_=${var__src_:-}'; eval "${prefix}"'_dst_=${var__dst_:-}' eval "${prefix}"'_src_=${var__src_:-}'; eval "${prefix}"'_dst_=${var__dst_:-}'
if declare -p var__args_ >/dev/null 2>&1; then if declare -p var__args_ >/dev/null 2>&1; then
@ -242,14 +244,16 @@ eval "${prefix}"'_command_=${var__command_:-}'
eval "${prefix}"'_host_=${var__host_:-}' eval "${prefix}"'_host_=${var__host_:-}'
eval "${prefix}"'_user_=${var__user_:-}' eval "${prefix}"'_user_=${var__user_:-}'
eval "${prefix}"'_port_=${var__port_:-}' eval "${prefix}"'_port_=${var__port_:-}'
eval "${prefix}"'app=${var_app:-false}'; eval "${prefix}"'new=${var_new:-false}' if declare -p var__subcommands_ >/dev/null 2>&1; then
eval "${prefix}"'_subcommands_=("${var__subcommands_[@]}")'; else
eval "${prefix}"'_subcommands_=()'; fi; eval "${prefix}"'app=${var_app:-false}'
eval "${prefix}"'new=${var_new:-false}'
eval "${prefix}"'list=${var_list:-false}'; eval "${prefix}"'ls=${var_ls:-false}' eval "${prefix}"'list=${var_list:-false}'; eval "${prefix}"'ls=${var_ls:-false}'
eval "${prefix}"'deploy=${var_deploy:-false}' eval "${prefix}"'deploy=${var_deploy:-false}'
eval "${prefix}"'undeploy=${var_undeploy:-false}' eval "${prefix}"'undeploy=${var_undeploy:-false}'
eval "${prefix}"'config=${var_config:-false}' eval "${prefix}"'config=${var_config:-false}'
eval "${prefix}"'delete=${var_delete:-false}' eval "${prefix}"'delete=${var_delete:-false}'
eval "${prefix}"'rm=${var_rm:-false}'; eval "${prefix}"'logs=${var_logs:-false}' eval "${prefix}"'rm=${var_rm:-false}'; eval "${prefix}"'logs=${var_logs:-false}'
eval "${prefix}"'multilogs=${var_multilogs:-false}'
eval "${prefix}"'cp=${var_cp:-false}' eval "${prefix}"'cp=${var_cp:-false}'
eval "${prefix}"'check=${var_check:-false}' eval "${prefix}"'check=${var_check:-false}'
eval "${prefix}"'ps=${var_ps:-false}'; eval "${prefix}"'run=${var_run:-false}' eval "${prefix}"'ps=${var_ps:-false}'; eval "${prefix}"'run=${var_run:-false}'
@ -258,24 +262,28 @@ eval "${prefix}"'auto=${var_auto:-false}'
eval "${prefix}"'generate=${var_generate:-false}' eval "${prefix}"'generate=${var_generate:-false}'
eval "${prefix}"'insert=${var_insert:-false}' eval "${prefix}"'insert=${var_insert:-false}'
eval "${prefix}"'server=${var_server:-false}' eval "${prefix}"'server=${var_server:-false}'
eval "${prefix}"'add=${var_add:-false}'; eval "${prefix}"'use=${var_use:-false}' eval "${prefix}"'add=${var_add:-false}'
eval "${prefix}"'init=${var_init:-false}' eval "${prefix}"'init=${var_init:-false}'
eval "${prefix}"'apps=${var_apps:-false}' eval "${prefix}"'apps=${var_apps:-false}'
eval "${prefix}"'upgrade=${var_upgrade:-false}'; local docopt_i=1 eval "${prefix}"'upgrade=${var_upgrade:-false}'
eval "${prefix}"'version=${var_version:-false}'
eval "${prefix}"'help=${var_help:-false}'; local docopt_i=1
[[ $BASH_VERSION =~ ^4.3 ]] && docopt_i=2; for ((;docopt_i>0;docopt_i--)); do [[ $BASH_VERSION =~ ^4.3 ]] && docopt_i=2; for ((;docopt_i>0;docopt_i--)); do
declare -p "${prefix}__help" "${prefix}__env" "${prefix}__stack" \ declare -p "${prefix}__help" "${prefix}__env" "${prefix}__stack" \
"${prefix}__version" "${prefix}__server" "${prefix}__domain" "${prefix}__pass" \ "${prefix}__server" "${prefix}__domain" "${prefix}__app_name" \
"${prefix}__auto" "${prefix}__status" "${prefix}__force" "${prefix}__no_tty" \ "${prefix}__pass" "${prefix}__auto" "${prefix}__status" \
"${prefix}__user" "${prefix}__all" "${prefix}_app_" "${prefix}_domain_" \ "${prefix}__skip_check" "${prefix}__force" "${prefix}__no_tty" \
"${prefix}__user" "${prefix}__all" "${prefix}_type_" "${prefix}_app_" \
"${prefix}_service_" "${prefix}_src_" "${prefix}_dst_" "${prefix}_args_" \ "${prefix}_service_" "${prefix}_src_" "${prefix}_dst_" "${prefix}_args_" \
"${prefix}_secret_" "${prefix}_version_" "${prefix}_cmd_" "${prefix}_data_" \ "${prefix}_secret_" "${prefix}_version_" "${prefix}_cmd_" "${prefix}_data_" \
"${prefix}_command_" "${prefix}_host_" "${prefix}_user_" "${prefix}_port_" \ "${prefix}_command_" "${prefix}_host_" "${prefix}_user_" "${prefix}_port_" \
"${prefix}app" "${prefix}new" "${prefix}list" "${prefix}ls" "${prefix}deploy" \ "${prefix}_subcommands_" "${prefix}app" "${prefix}new" "${prefix}list" \
"${prefix}undeploy" "${prefix}config" "${prefix}delete" "${prefix}rm" \ "${prefix}ls" "${prefix}deploy" "${prefix}undeploy" "${prefix}config" \
"${prefix}logs" "${prefix}multilogs" "${prefix}cp" "${prefix}check" \ "${prefix}delete" "${prefix}rm" "${prefix}logs" "${prefix}cp" "${prefix}check" \
"${prefix}ps" "${prefix}run" "${prefix}secret" "${prefix}auto" \ "${prefix}ps" "${prefix}run" "${prefix}secret" "${prefix}auto" \
"${prefix}generate" "${prefix}insert" "${prefix}server" "${prefix}add" \ "${prefix}generate" "${prefix}insert" "${prefix}server" "${prefix}add" \
"${prefix}use" "${prefix}init" "${prefix}apps" "${prefix}upgrade"; done; } "${prefix}init" "${prefix}apps" "${prefix}upgrade" "${prefix}version" \
"${prefix}help"; done; }
# docopt parser above, complete command for generating this parser is `docopt.sh abra` # docopt parser above, complete command for generating this parser is `docopt.sh abra`
PROGRAM_NAME=$(basename "$0") PROGRAM_NAME=$(basename "$0")
@ -296,7 +304,7 @@ warning() {
} }
success() { success() {
echo "$(tput setaf 2)$*$(tput sgr0)" echo "$(tput setaf 2)SUCCESS: $*$(tput sgr0)"
} }
###### Default settings ###### Default settings
@ -307,9 +315,15 @@ fi
###### Safety checks ###### Safety checks
require_multitail() { require_pwqgen() {
if ! type multitail > /dev/null 2>&1; then if ! type pwqgen > /dev/null 2>&1; then
error "multitail program is not installed" error "pwqgen program is not installed"
fi
}
require_pwgen() {
if ! type pwgen > /dev/null 2>&1; then
error "pwgen program is not installed"
fi fi
} }
@ -323,20 +337,23 @@ require_stack() {
fi fi
} }
require_app_clone() { require_app_latest() {
APP="$1" APP="$1"
APP_DIR="$ABRA_DIR/apps/$APP" APP_DIR="$ABRA_DIR/apps/$APP"
if [ ! -d "$APP_DIR" ]; then if [ ! -d "$APP_DIR" ]; then
warning "'$APP' not found, fetching via git.." warning "The app type '$APP' was not found, fetching via Git"
if ! git clone "$GIT_URL/$APP.git" "$ABRA_DIR/apps/$APP"; then if ! git clone "$GIT_URL/$APP.git" "$ABRA_DIR/apps/$APP" > /dev/null 2>&1 ; then
error "Could not retrieve '$APP', this app doesn't exist?" error "Could not retrieve app type '$APP', this app type doesn't exist?"
fi fi
success "Fetched app configuration via Git"
fi fi
cd "$APP_DIR" && git pull > /dev/null 2>&1
} }
# FIXME 3wc: update or remove # FIXME 3wc: update or remove
if [ -z "$ABRA_ENV" ] && [ -f .envrc ] && type direnv > /dev/null 2>&1 && ! direnv status | grep -q 'Found RC allowed true'; then if [ -z "$ABRA_ENV" ] && [ -f .env ] && type direnv > /dev/null 2>&1 && ! direnv status | grep -q 'Found RC allowed true'; then
error "direnv is blocked, run direnv allow" error "direnv is blocked, run direnv allow"
fi fi
@ -348,11 +365,10 @@ load_custom_commands() {
source abra-commands.sh source abra-commands.sh
fi fi
if [ -n "$abra__domain_" ]; then if [ -n "$abra__app_" ]; then
load_instance load_instance
load_instance_env load_instance_env
require_app_latest "$TYPE"
require_app_clone "$APP"
fi fi
if [ -f "$APP_DIR/abra-commands.sh" ]; then if [ -f "$APP_DIR/abra-commands.sh" ]; then
@ -377,22 +393,22 @@ get_app_secrets() {
get_app_passwords() { get_app_passwords() {
# FIXME 3wc: requires bash 4, use for loop instead # FIXME 3wc: requires bash 4, use for loop instead
mapfile -t PASSWORDS < <(grep "SECRET.*PASSWORD.*VERSION.*" "$ENV_FILE" | cut -d ' ' -f2-) mapfile -t PASSWORDS < <(grep "SECRET.*PASSWORD.*VERSION.*" "$ENV_FILE")
} }
get_app_keys() { get_app_keys() {
# FIXME 3wc: requires bash 4, use for loop instead # FIXME 3wc: requires bash 4, use for loop instead
mapfile -t KEYS < <(grep "SECRET.*KEY.*VERSION.*" "$ENV_FILE" | cut -d' ' -f2-) mapfile -t KEYS < <(grep "SECRET.*KEY.*VERSION.*" "$ENV_FILE")
} }
load_instance() { load_instance() {
DOMAIN="$abra__domain_" APP="$abra__app_"
# FIXME 3wc: requires bash 4, use for loop instead # FIXME 3wc: requires bash 4, use for loop instead
mapfile -t ENV_FILES < <(find -L "$ABRA_DIR" -name "$DOMAIN.env") mapfile -t ENV_FILES < <(find -L "$ABRA_DIR" -name "$APP.env")
case "${#ENV_FILES[@]}" in case "${#ENV_FILES[@]}" in
1 ) ;; 1 ) ;;
0 ) error "Can't find app '$DOMAIN'"; return;; 0 ) error "Can't find app '$APP'"; return;;
* ) error "Found $DOMAIN in multiple servers: ${ENV_FILES[*]}"; return;; * ) error "Found $APP in multiple servers: ${ENV_FILES[*]}"; return;;
esac esac
ENV_FILE="${ENV_FILES[0]}" ENV_FILE="${ENV_FILES[0]}"
if [ ! -f "$ENV_FILE" ]; then if [ ! -f "$ENV_FILE" ]; then
@ -400,16 +416,19 @@ load_instance() {
fi fi
IFS='/' read -r -a PARTS <<< "$ENV_FILE" IFS='/' read -r -a PARTS <<< "$ENV_FILE"
SERVER="${PARTS[-2]}" SERVER="${PARTS[-2]}"
export STACK_NAME="${DOMAIN//./_}" export STACK_NAME="$APP"
} }
load_instance_env() { load_instance_env() {
set -a
# shellcheck disable=SC1090 # shellcheck disable=SC1090
source "$ENV_FILE" source "$ENV_FILE"
if [ -z "$APP" ]; then set +a
error "APP not set, maybe $ENV_FILE is using an old format?"
if [ -z "$TYPE" ]; then
error "TYPE not set, maybe $ENV_FILE is using an old format?"
fi fi
APP_DIR="$ABRA_DIR/apps/$APP" APP_DIR="$ABRA_DIR/apps/$TYPE"
export DOCKER_CONTEXT="$SERVER" export DOCKER_CONTEXT="$SERVER"
export DOMAIN export DOMAIN
} }
@ -449,19 +468,55 @@ parse_secret() {
abra__version_="$(echo "$SECRET" | sed -n 's/.*\(v[0-9]\).*/\1/p')" abra__version_="$(echo "$SECRET" | sed -n 's/.*\(v[0-9]\).*/\1/p')"
if [[ "$SECRET" == *"length"* ]]; then
echo "Generating $abra__secret_, version: $abra__version_, length: $abra__length_" echo "Generating $abra__secret_, version: $abra__version_, length: $abra__length_"
else
echo "Generating $abra__secret_, version: $abra__version_"
fi
sub_app_secret_generate sub_app_secret_generate
} }
stack_logs (){
# Note(decentral1se): see https://github.com/moby/moby/issues/31458#issuecomment-617871046
STACK="$1"
services=$(docker stack services "${STACK}" --format "{{.ID}}")
# shellcheck disable=SC2154
trap 'jobs=$(jobs -p) && test -n "$jobs" && kill $jobs' EXIT
for item in ${services//\\n/$'\n'}; do
docker service logs -f -t --tail 10 "$item" &
done
sleep infinity
}
####################################### #######################################
# abra app .. # abra app ..
####################################### #######################################
###### .. app (list|ls) ###### .. app ls
help_app_ls (){
help_app_list
}
sub_app_ls (){ sub_app_ls (){
sub_app_list sub_app_list
} }
help_app_list (){
echo "abra [options] app (list|ls) [--status] [--server=<server>]
List your exciting apps
OPTIONS
--status Show whether apps are deployed (warning! slow!)
--server=<server> Filter to a specific server"
}
sub_app_list (){ sub_app_list (){
SERVER="$abra___server" SERVER="$abra___server"
if [ -z "$SERVER" ]; then if [ -z "$SERVER" ]; then
@ -510,10 +565,14 @@ sub_app_list (){
FILE="${PARTS[-1]}" FILE="${PARTS[-1]}"
SERVER="${PARTS[-2]}" SERVER="${PARTS[-2]}"
DOMAIN="${FILE%.env}" DOMAIN="${FILE%.env}"
set -a
# shellcheck disable=SC1090 # shellcheck disable=SC1090
APP="$(source "$ENV_FILE" && echo "$APP")" APP="$(source "$ENV_FILE" && echo "$APP")"
# shellcheck disable=SC1090 # shellcheck disable=SC1090
APP_STACK_NAME="$(source "$ENV_FILE" && echo "$STACK_NAME")" APP_STACK_NAME="$(source "$ENV_FILE" && echo "$STACK_NAME")"
set +a
if [ -z "$APP_STACK_NAME" ]; then if [ -z "$APP_STACK_NAME" ]; then
APP_STACK_NAME="${DOMAIN//./_}" APP_STACK_NAME="${DOMAIN//./_}"
fi fi
@ -525,19 +584,36 @@ sub_app_list (){
# Align table `-t` based on tab characters -s`^V<Tab>` # Align table `-t` based on tab characters -s`^V<Tab>`
} }
###### .. app new [--domain=<domain> --server=<server>] ###### .. app new
help_app_new (){
echo "abra [options] app new [--server=<server>] [--domain=<domain>] [--pass] [--auto] <type>
Create a new app of <type> (e.g. wordpress or custom-html)
OPTIONS
--server=<server> Specify which server to use (default: prompt)
--domain=<domain> Set the domain name (default: prompt)
--auto Auto-generate secrets (default: no)
--pass Store generated secrets in pass (default: no)"
}
sub_app_new (){ sub_app_new (){
require_abra_dir require_abra_dir
get_servers get_servers
APP=$abra__app_ # Note(decentral1se): we are overloading the use of the word "app" on this
# the interface and therefore try to use the word "type" to refer to "a type
# of app" vs. "an instance of an app"
TYPE=$abra__type_
SERVER=$abra___server SERVER=$abra___server
DOMAIN=$abra___domain DOMAIN=$abra___domain
APP_NAME=$abra___app_name
require_app_clone "$APP" require_app_latest "$TYPE"
if [ -z "$SERVER" ]; then if [ -z "$SERVER" ]; then
echo "Where would you like to put $APP?" echo "Where would you like to put $TYPE?"
select SERVER_ITEM in "${SERVERS[@]##*/}"; do select SERVER_ITEM in "${SERVERS[@]##*/}"; do
if [ 1 -le "$REPLY" ] && [ "$REPLY" -le ${#SERVERS[@]} ]; then if [ 1 -le "$REPLY" ] && [ "$REPLY" -le ${#SERVERS[@]} ]; then
@ -553,24 +629,32 @@ sub_app_new (){
error "Server '$SERVER' not found" error "Server '$SERVER' not found"
fi fi
APP_DIR="$ABRA_DIR/apps/$APP" APP_DIR="$ABRA_DIR/apps/$TYPE"
if [ -z "$DOMAIN" ]; then if [ -z "$DOMAIN" ]; then
read -rp "Domain name: " DOMAIN read -rp "Domain name: " DOMAIN
fi fi
ENV_FILE="$SERVER/$DOMAIN.env" if [ -z "$APP_NAME" ]; then
DEFAULT_NAME="${TYPE/-/}_${DOMAIN//./_}"
read -rp "App name [$DEFAULT_NAME]: " APP_NAME
if [ -z "$APP_NAME" ]; then
APP_NAME="$DEFAULT_NAME"
fi
fi
ENV_FILE="$SERVER/$APP_NAME.env"
if [ -f "$ENV_FILE" ]; then if [ -f "$ENV_FILE" ]; then
error "$ENV_FILE already exists" error "$ENV_FILE already exists"
# FIXME 3wc: offer to user $STACK_$DOMAIN.env name instead # FIXME 3wc: offer to user $STACK_$DOMAIN.env name instead
fi fi
cp "$APP_DIR/.envrc.sample" "$ENV_FILE" cp "$APP_DIR/.env.sample" "$ENV_FILE"
sed -i "s/$APP\.example\.com/$DOMAIN/g" "$ENV_FILE" sed -i "s/$TYPE\.example\.com/$DOMAIN/g" "$ENV_FILE"
sed -i "s/example\.com/$DOMAIN/g" "$ENV_FILE" sed -i "s/example\.com/$DOMAIN/g" "$ENV_FILE"
abra__domain_="$DOMAIN" abra__app_="$APP_NAME"
get_app_secrets get_app_secrets
@ -578,19 +662,31 @@ sub_app_new (){
sub_app_secret_auto sub_app_secret_auto
fi fi
echo "$(tput setaf 4)Your new '$APP' is ready for action:$(tput sgr0)" echo "$(tput setaf 4)Your new '$TYPE' is ready for action:$(tput sgr0)"
echo " $(tput setaf 3)Customise the configuration:" echo " $(tput setaf 3)Customise the configuration:"
echo " abra app $DOMAIN config$(tput sgr0)" echo " abra app $APP_NAME config$(tput sgr0)"
echo " $(tput setaf 2)Deploy it:" echo " $(tput setaf 2)Deploy it:"
echo " abra app $DOMAIN deploy$(tput sgr0)" echo " abra app $APP_NAME deploy$(tput sgr0)"
} }
###### .. app <domain> deploy ###### .. app <domain> deploy
help_app_deploy (){
echo "abra [options] app <domain> deploy [--skip-check]
Deploy app <domain> to the configured server
OPTIONS
--skip-check Don't check whether app definition is up-to-date first"
}
sub_app_deploy (){ sub_app_deploy (){
load_instance load_instance
load_instance_env load_instance_env
require_app_clone "$APP" require_app_latest "$TYPE"
if [ "$abra___skip_check" == "false" ]; then
sub_app_check
fi
echo "About to deploy:" echo "About to deploy:"
echo " Server: $(tput setaf 4)${SERVER}$(tput sgr0)" echo " Server: $(tput setaf 4)${SERVER}$(tput sgr0)"
@ -612,7 +708,6 @@ sub_app_deploy (){
prompt_confirm prompt_confirm
APP=$(basename "$APP_DIR") APP=$(basename "$APP_DIR")
require_app_clone "$APP"
( (
cd "$APP_DIR" || error "\$APP_DIR '$APP_DIR' not found" cd "$APP_DIR" || error "\$APP_DIR '$APP_DIR' not found"
@ -644,6 +739,31 @@ sub_app_undeploy (){
sub_app_config (){ sub_app_config (){
load_instance load_instance
if [ -z "$EDITOR" ]; then
warning "\$EDITOR not set; which text editor would you like to use?"
EDITORS_ALL=(vi vim nano pico emacs)
declare -a EDITORS_AVAILABLE
for EDITOR in "${EDITORS_ALL[@]}"; do
if type "$EDITOR" > /dev/null 2>&1; then
EDITORS_AVAILABLE+=("$EDITOR")
fi
done
if [ ${#EDITORS_AVAILABLE[@]} = 0 ]; then
error "No text editors found! Are you using a magnetised needle? 🤪"
fi
select EDITOR in "${EDITORS_AVAILABLE[@]}"; do
if [ 1 -le "$REPLY" ] && [ "$REPLY" -le ${#EDITORS_AVAILABLE[@]} ]; then
SERVER="$EDITOR"
success "Using '${EDITOR}'; Add 'export EDITOR=${EDITOR}' to your ~/.bashrc to set as default"
break
fi
done
fi
$EDITOR "$ENV_FILE" $EDITOR "$ENV_FILE"
} }
@ -652,15 +772,17 @@ sub_app_check (){
load_instance load_instance
load_instance_env load_instance_env
APP_ENV=$(grep -v '^#' "$ENV_FILE" | sed 's/^.* \([^=]\+\)=.*/\1/' | sort) #APP_ENV=$(grep -v '^#' "$ENV_FILE" | sed 's/^.* \([^=]\+\)=.*/\1/' | sort)
STACK_ENV=$(grep -v '^#' "$APP_DIR/.envrc.sample" | sed 's/^.* \([^=]\+\)=.*/\1/' | sort) APP_ENV=$(grep -v '^#' "$ENV_FILE" | cut -d' ' -f2 | cut -d'=' -f1 | sort)
#STACK_ENV=$(grep -v '^#' "$APP_DIR/.env.sample" | sed 's/^.* \([^=]\+\)=.*/\1/' | sort)
STACK_ENV=$(grep -v '^#' "$APP_DIR/.env.sample" | cut -d' ' -f2 | cut -d'=' -f1 | sort)
# Only show "1", items in STACK_ENV which aren't in APP_ENV # Only show "1", items in STACK_ENV which aren't in APP_ENV
MISSING_VARS=$(comm -23 <(echo "$STACK_ENV") <(echo "$APP_ENV")) MISSING_VARS=$(comm -23 <(echo "$STACK_ENV") <(echo "$APP_ENV"))
if [ -z "$MISSING_VARS" ]; then if [ -z "$MISSING_VARS" ]; then
success "Yay! All the necessary basic variables are defined" success "Yay! All the necessary basic variables are defined"
exit 0 return 0
fi fi
error "Found missing variables: $MISSING_VARS" error "Found missing variables: $MISSING_VARS"
@ -758,8 +880,10 @@ sub_app_secret_generate(){
LENGTH="$abra__length_" LENGTH="$abra__length_"
if [[ "$SECRET" == *"password"* ]]; then if [[ "$SECRET" == *"password"* ]]; then
require_pwqgen
PWGEN="${abra__cmd_:-pwqgen}" PWGEN="${abra__cmd_:-pwqgen}"
else else
require_pwgen
PWGEN=${abra__cmd_:-pwgen -s "$LENGTH" 1} PWGEN=${abra__cmd_:-pwgen -s "$LENGTH" 1}
fi fi
@ -824,27 +948,6 @@ sub_app_run(){
return return
} }
###### .. app <domain> multilogs
sub_app_multilogs() {
# Inspired by https://github.com/moby/moby/issues/31458#issuecomment-475411564
require_multitail
load_instance
load_instance_env
# Get a list of the service names
SERVICES=$(docker stack services --format "{{.Name}}" "${STACK_NAME}")
# Sort the service names
SERVICES=$(echo "${SERVICES}" | sort)
# Create the command to run
COMMAND='multitail --mergeall'
for SERVICE in ${SERVICES}; do
COMMAND="${COMMAND} -L 'docker service logs --tail 20 -f ${SERVICE}'"
done
# Run the command
bash -c "${COMMAND}"
}
###### .. app <domain> logs <service> <args>... ###### .. app <domain> logs <service> <args>...
sub_app_logs (){ sub_app_logs (){
load_instance load_instance
@ -853,8 +956,7 @@ sub_app_logs (){
SERVICE="${abra__service_}" SERVICE="${abra__service_}"
if [ -z "$SERVICE" ]; then if [ -z "$SERVICE" ]; then
warning "No \$SERVICE provided, running multilogs" stack_logs "${STACK_NAME}"
sub_app_multilogs
return return
fi fi
@ -969,11 +1071,6 @@ sub_server_delete() {
docker context rm "$abra__host_" docker context rm "$abra__host_"
} }
###### .. server <host> use
sub_server_use() {
docker context use "$abra__host_"
}
###### .. server <host> apps ###### .. server <host> apps
sub_server_apps() { sub_server_apps() {
abra___server="$abra__host_" abra___server="$abra__host_"
@ -989,6 +1086,34 @@ sub_upgrade() {
curl https://install.abra.autonomic.zone | bash curl https://install.abra.autonomic.zone | bash
} }
###### .. version
sub_version() {
echo "$ABRA_VERSION"
}
###### .. help
sub_help() {
SUBCOMMAND=$(IFS="_"; echo "${abra__subcommands_[*]}")
if [ -z "$SUBCOMMAND" ]; then
printf "%s" "$DOC"
exit
fi
HELP_CMD="help_${SUBCOMMAND}"
if type "$HELP_CMD" > /dev/null 2>&1; then
"$HELP_CMD"
else
HELP_COMMANDS=$(declare -Ff | grep 'help_' | cut -d' ' -f3 | sed 's/_/ /g')
error "No help found for '$abra__subcommands_'
Try one of these:
${HELP_COMMANDS//help /}"
fi
}
#######################################
# cheeky docker aliases
#######################################
###### .. stack <args>... ###### .. stack <args>...
sub_stack() { sub_stack() {
# shellcheck disable=SC2068 # shellcheck disable=SC2068
@ -1016,9 +1141,10 @@ abra() {
# the place to handle the dynamically-defined vars # the place to handle the dynamically-defined vars
declare abra___stack abra___env abra__command_ abra__args_ \ declare abra___stack abra___env abra__command_ abra__args_ \
abra__secret_ abra__version_ abra__data_ abra___user abra__host_ \ abra__secret_ abra__version_ abra__data_ abra___user abra__host_ \
abra__app_ abra__port_ abra__user_ abra__service_ abra__src_ abra__dst_ \ abra__type_ abra__port_ abra__user_ abra__service_ abra__src_ abra__dst_ \
abra__domain_ abra___server abra___domain abra___force abra___pass \ abra___server abra___domain abra___force abra___pass \
abra___auto abra___status abra___no_tty abra___auto abra___status abra___no_tty abra___skip_check \
abra__subcommands_ abra___app_name
if ! type tput > /dev/null 2>&1; then if ! type tput > /dev/null 2>&1; then
tput() { tput() {
@ -1034,8 +1160,10 @@ abra() {
# --env <env> # --env <env>
if [ -n "$abra___env" ]; then if [ -n "$abra___env" ]; then
set -a
# shellcheck disable=SC1090 # shellcheck disable=SC1090
source "$abra___env" || error "Unable to load env from '$abra___env'" source "$abra___env" || error "Unable to load env from '$abra___env'"
set +a
fi fi
load_custom_commands load_custom_commands
@ -1061,7 +1189,6 @@ abra() {
# shellcheck disable=SC2086 # shellcheck disable=SC2086
"$CMD" ${abra__args_[*]} "$CMD" ${abra__args_[*]}
else else
"$CMD not found"
docopt_exit docopt_exit
fi fi
} }

View File

@ -91,7 +91,7 @@ _abra_complete()
case "$cmd" in case "$cmd" in
server) server)
# Offer exactly one server name completion. # Offer exactly one server name completion.
if (( COMP_CWORD == cmd_index + 2 )); then if (( COMP_CWORD == cmd_index + 1 )); then
_abra_complete_servers "$cur" _abra_complete_servers "$cur"
fi fi
;; ;;

View File

@ -1,49 +1,52 @@
#!/usr/bin/env bats #!/usr/bin/env bats
setup() { setup() {
mkdir -p ~/.abra/servers/default export ABRA_DIR=$(mktemp -d)
mkdir -p $ABRA_DIR/servers/default
} }
teardown() { teardown() {
rm -rf ~/.abra/servers/default rm -rf "$ABRA_DIR"
rm -rf ~/.abra/servers/swarm.test.com
} }
@test "abra server add/rm works" { @test "abra server (add|rm)" {
./abra server add swarm.test.com ./abra server add swarm.test.com
docker context ls | grep swarm.test.com docker context ls | grep swarm.test.com
[ -d ~/.abra/servers/swarm.test.com ] [ -d $ABRA_DIR/servers/swarm.test.com ]
./abra server swarm.test.com rm ./abra server swarm.test.com rm
./abra server add swarm.test.com foobar 12345 ./abra server add swarm.test.com foobar 12345
[ -d ~/.abra/servers/swarm.test.com ] [ -d $ABRA_DIR/servers/swarm.test.com ]
./abra server swarm.test.com rm ./abra server swarm.test.com rm
} }
@test "abra app new/rm works" { @test "abra server init" {
./abra app new --server default --domain traefik.test.com traefik ./abra server default init
[ -f ~/.abra/servers/default/traefik.test.com.env ] }
@test "abra app (new|rm)" {
./abra app new --server default --domain traefik.test.com --app-name traefik_test_com traefik
[ -f $ABRA_DIR/servers/default/traefik_test_com.env ]
# interactive prompt # interactive prompt
echo "y" | ./abra app traefik.test.com delete echo "y" | ./abra app traefik_test_com delete
[ ! -f ~/.abra/servers/default/traefik.test.com.env ] [ ! -f $ABRA_DIR/servers/default/traefik_test_com.env ]
# --force # --force
./abra app new --server default --domain traefik.test.com traefik ./abra app new --server default --domain traefik_test_com --app-name traefik_test_com traefik
./abra app traefik.test.com delete --force ./abra app traefik_test_com delete --force
[ ! -f ~/.abra/servers/default/traefik.test.com.env ] [ ! -f $ABRA_DIR/servers/default/traefik_test_com.env ]
} }
@test "abra app <domain> secret (insert|generate|rm)" { @test "abra app <domain> secret (insert|generate|rm)" {
# TODO 3wc: mock `server new` so we don't endlessly re-test it ./abra app new --server default --domain traefik_test_com --app-name traefik_test_com traefik
./abra app new --server default --domain traefik.test.com traefik
./abra app traefik.test.com secret insert foobar v1 "foobar" ./abra app traefik_test_com secret insert foobar v1 "foobar"
# interactive prompt # interactive prompt
echo "y" | ./abra app traefik.test.com secret rm foobar echo "y" | ./abra app traefik_test_com secret rm foobar
./abra app traefik.test.com secret insert foobar v1 "foobar" ./abra app traefik_test_com secret insert foobar v1 "foobar"
# prompt # prompt
./abra app traefik.test.com secret rm foobar --force ./abra app traefik_test_com secret rm foobar --force
} }