Compare commits

...

561 Commits

Author SHA1 Message Date
2fbef41a3a
test: clean up properly
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2025-04-24 14:29:54 +02:00
6fb41e5300 fix: dont parse chaos version
All checks were successful
continuous-integration/drone/push Build is passing
See #547
2025-04-24 11:24:14 +00:00
1432f480c7 fix: -T/--tty disables TTY remote request
All checks were successful
continuous-integration/drone/push Build is passing
See #499
2025-04-24 08:57:53 +00:00
83af39771b
test: drop unused tagHash
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-24 10:57:34 +02:00
4d1333202e
test: flaky test when no RC is available
All checks were successful
continuous-integration/drone/push Build is passing
Fixes https://build.coopcloud.tech/toolshed/abra/2760/1/5
2025-04-24 10:33:49 +02:00
55c24f070c
feat: cancel git clone ops gracefully
All checks were successful
continuous-integration/drone/push Build is passing
See #528
2025-04-22 22:56:10 +02:00
229e8eb9da feat: --ssh/--force for recipe fetch
All checks were successful
continuous-integration/drone/push Build is passing
See toolshed/organising#546
2025-04-22 09:35:36 +00:00
b3ab95750e
fix: trim final newline on release note
All checks were successful
continuous-integration/drone/push Build is passing
Follow-up #544
2025-04-22 09:23:28 +02:00
de009921a2 fix: show release notes once
All checks were successful
continuous-integration/drone/push Build is passing
See #543
2025-04-22 05:47:03 +00:00
d081bbaefa
feat: auto select single server
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See toolshed/organising#513
2025-04-21 21:06:29 +02:00
515b5466ca
docs: add missing arg
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See #540
2025-04-21 20:17:39 +02:00
6965799bdc
chore: publish 0.10.0-beta
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2025-04-21 19:11:31 +02:00
f75c9a6259
test: clean up test server correctly
All checks were successful
continuous-integration/drone/push Build is passing
Fixes https://build.coopcloud.tech/toolshed/abra/2723/1/5
2025-04-21 19:03:49 +02:00
a43a092ba7 fix: fetch recipe for "app list -S"
All checks were successful
continuous-integration/drone/push Build is passing
2025-04-19 07:28:15 +00:00
fa084a61d2 fix(lint): Improves error message if a lint rule errors
All checks were successful
continuous-integration/drone/push Build is passing
This was detected while debugging #534
2025-04-16 05:12:19 +00:00
895a7fe7d6
fix: don't overwrite recipeVersion
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
Fixes https://build.coopcloud.tech/toolshed/abra/2709/1/5
2025-04-15 10:51:53 +02:00
742a726778
fix: latest commit for new recipe version
All checks were successful
continuous-integration/drone/push Build is passing
See #527
2025-04-14 23:55:19 +02:00
2b9a185aff
build: go mod tidy 2025-03-23 11:10:05 +01:00
b7c1e87c0b
build: go mod vendor
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2025-03-23 11:08:17 +01:00
cdfb8a08bb
chore: publish 0.10.0-rc2-beta
Some checks failed
continuous-integration/drone/tag Build is failing
continuous-integration/drone/push Build is passing
2025-03-23 11:05:03 +01:00
8943cea13f
test: get latest via helper 2025-03-23 11:00:04 +01:00
6d64e0edd3
fix: sshPkg.Fatal has more nuance
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See #507
2025-03-23 10:27:58 +01:00
47045ca8f1 feat: improved deploy progress reporting
All checks were successful
continuous-integration/drone/push Build is passing
See #478
2025-03-23 09:13:36 +00:00
d0f982456e add charset modifier to secret generation (#521)
All checks were successful
continuous-integration/drone/push Build is passing
since we need special chars in passwords for a recipe we are working on, i have added the option to specify a charset in the same way as the length can be setted.
i did not change anything in the behaviour, so if length is not specified, the charset gets ignored whether it is there or not.

you can specify the following:
`charset=default` - Results in passgen.AlphabetDefault being used
`charset=special` - Results in passgen.AlphabetSpecial being used
`charset=safespecial` - Results in `!@#%^&*_-+=` being used (so it is AlphabetSpecial without the dollar sign)
`charset=default,special` or `charset=special,default` - Results in passgen.AlphabetDefault + passgen.AlphabetSpecial being used
`charset=default,safespecial` or `charset=safespecial,default` - Results in passgen.AlphabetDefault + `!@#%^&*_-+=` being used ((so it is AlphabetSpecial without the dollar sign)

PR for the docs: toolshed/docs.coopcloud.tech#271

Co-authored-by: p4u1 <p4u1@noreply.git.coopcloud.tech>
Reviewed-on: #521
Reviewed-by: p4u1 <p4u1@noreply.git.coopcloud.tech>
Co-authored-by: Apfelwurm <Alexander@volzit.de>
Co-committed-by: Apfelwurm <Alexander@volzit.de>
2025-03-21 10:29:21 +00:00
80ad6c6681 fix(app): Properly detects release notes added after a release when upgrading an app (#523)
All checks were successful
continuous-integration/drone/push Build is passing
Fixes #488

Reviewed-on: #523
Reviewed-by: decentral1se <decentral1se@noreply.git.coopcloud.tech>
Co-authored-by: p4u1 <p4u1_f4u1@riseup.net>
Co-committed-by: p4u1 <p4u1_f4u1@riseup.net>
2025-03-21 10:25:46 +00:00
cb63cfe9c2
refactor: chaos redundant, shorter message
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-03-16 13:54:07 +01:00
d1e49d17ce
test: on-demand integration tests
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-03-16 13:22:50 +01:00
1574aa0631
refactor!: status between service/image
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See #487 (comment)
2025-03-16 12:42:09 +01:00
1723025fbf
build: go 1.24
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
We were running behind and there were quite some deprecations to update.
This was mostly in the upstream copy/pasta package but seems quite
minimal.
2025-03-16 12:31:45 +01:00
a2b678caf6
test: reset after undeploy for a clean env version
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
Follows #510.
See https://build.coopcloud.tech/toolshed/abra/2620/1/5.
2025-03-16 11:49:38 +01:00
0a371ec360 fix: integration tests
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-03-13 08:31:11 +01:00
e58a716fe1 feat(deploy): Simplifies deploy overview (#508)
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
This simplifies the deploy overview, to only show 3 version fields:
- CURRENT DEPLOYMENT
- CURRENT ENV
- NEW DEPLOYMENT

It also fixes a few errors around version detection

Reviewed-on: #508
Co-authored-by: p4u1 <p4u1_f4u1@riseup.net>
Co-committed-by: p4u1 <p4u1_f4u1@riseup.net>
2025-03-12 16:13:24 +00:00
d09a19a385 fix: Adds chaos flag to restart command
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-11 10:01:44 +00:00
cee808ff06 fix: Changes how the deploy version is detected in app deploy command 2025-02-11 10:01:44 +00:00
4326d1d259 fix: Sorts git tags with tagcmp 2025-02-11 10:01:44 +00:00
b976872f77 fix(overview): Adds linebreak after compose file in deploy overview
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-11 09:57:09 +00:00
7b6ea76437 fix(secret): Checks for enough arguments
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-11 09:55:03 +00:00
9069758969 fix(cmd): Uses uppercase t for tty shorthand flag
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-02-10 15:13:26 +01:00
15d6b1a2a5 fix: app new with chaos should just take the local repo as it is (#495)
All checks were successful
continuous-integration/drone/push Build is passing
Fixes #494

Reviewed-on: #495
Co-authored-by: p4u1 <p4u1_f4u1@riseup.net>
Co-committed-by: p4u1 <p4u1_f4u1@riseup.net>
2025-02-10 14:00:42 +00:00
8a7fe4ca07
fix: prompt, skip adding if next present
All checks were successful
continuous-integration/drone/push Build is passing
#486
2025-01-17 17:46:41 +01:00
64ad60663f test: adjust for new abra-test-recipe version
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See #470
2025-01-09 13:14:47 +00:00
cb3f46b46e fix: redirect to stderr for machine output
All checks were successful
continuous-integration/drone/push Build is passing
See #477
2025-01-09 11:23:36 +00:00
41e514ae9a
test: reset after deploy
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-01-09 11:54:39 +01:00
086b4828ff
docs: better comments, remove redundant output check 2025-01-09 11:54:38 +01:00
ed263854d4
fix: show N/A if env version unknown
See #478
2025-01-09 11:54:37 +01:00
eb6fe4ba6e
fix: dont set chaos label if no chaos
See #478
2025-01-09 11:54:36 +01:00
993172d31b test: ensure .env version written
All checks were successful
continuous-integration/drone/push Build is passing
2025-01-08 13:42:35 +00:00
c70b6e72a7 test: ensure unstaged changes preserved 2025-01-08 13:42:35 +00:00
22e4dd7fca fix: app new from chaos changes
See #471
2025-01-08 13:42:35 +00:00
b6009057a8
docs: note temp autocomplete, less whitespace
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-01-08 12:10:17 +01:00
b978f04910
fix: use "sudo tee" to avoid permissions error
See #474
2025-01-08 12:09:51 +01:00
3ac29d54d9
chore: go update des/vendor
All checks were successful
continuous-integration/drone/push Build is passing
2025-01-07 16:59:56 +01:00
877c17fab5
test: re-enable this one
All checks were successful
continuous-integration/drone/push Build is passing
2025-01-05 16:46:48 +01:00
f01fd26ce3
test: git status output 2025-01-05 16:46:38 +01:00
273c165a41
docs: --chaos/-C handling for catalogue generate 2025-01-05 16:46:20 +01:00
c88fc66c99
test: moar chaos stability 😌 [ci skip] 2025-01-05 16:12:06 +01:00
9b271a6963
docs: moar authors [ci skip] 2025-01-05 15:53:17 +01:00
8af87aa382
chore: upgrade goreleaser
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2025-01-05 12:47:46 +01:00
ac0b9cd052
chore: new RC
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is failing
2025-01-05 12:42:42 +01:00
4923984e84
fix: not flaky catalogue generate
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See #464
2025-01-05 12:08:10 +01:00
2bc77de751
test: ensure main branch on new recipe 2025-01-05 10:38:34 +01:00
b3a2402cec
chore: remove redundant logging 2025-01-05 10:38:24 +01:00
a773fd4256
chore: spacing 2025-01-05 10:38:13 +01:00
b1a0d54bd3
fix: default to main then master 2025-01-05 10:37:30 +01:00
3869d6bce9
Revert "test: try uppercase naming (following UI)"
All checks were successful
continuous-integration/drone/push Build is passing
This reverts commit 0ff07ab224e3e2195c502b729b09da87d1312957.

Wrong UI, trying again via Drone.
2025-01-04 11:55:13 +01:00
0ff07ab224
test: try uppercase naming (following UI)
Some checks failed
continuous-integration/drone/push Build is failing
2025-01-04 11:47:33 +01:00
936c1b0626
fix: use new syntax
Some checks failed
continuous-integration/drone/push Build is failing
2025-01-04 11:20:17 +01:00
b576cba227
fix: use abra-bot
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2025-01-04 11:09:14 +01:00
d087f3debf
chore: go mod tidy
Some checks failed
continuous-integration/drone Build is failing
2025-01-03 21:25:11 +01:00
e57a6d87a3
test: use recipes url 2025-01-03 20:35:19 +01:00
74b64099de
fix: skip example && fix generate 2025-01-03 20:24:49 +01:00
354712ca46
fix: remove old docstring 2025-01-03 20:23:48 +01:00
81cdc843ec
fix: coop-cloud -> toolshed 2025-01-03 20:23:27 +01:00
d2931e3af0
fix: drop warning, can use this now 2025-01-03 20:21:20 +01:00
b9f2d1f568
chore: go mod vendor / tidy 2025-01-03 20:21:06 +01:00
a379b31a19 refactor: dont use topics
See coop-cloud/organising#377
See coop-cloud/organising#569
2025-01-03 17:01:37 +00:00
17e15dba77
chore: spacing / wording on log message [ci skip] 2025-01-03 17:53:22 +01:00
1194f3b228
refactor!: maintain "domain"
All checks were successful
continuous-integration/drone/push Build is passing
See toolshed/organising#636
2025-01-03 08:24:03 +01:00
2dc8034c16
fix: no dot dirs for server selection
All checks were successful
continuous-integration/drone/push Build is passing
2025-01-03 08:16:30 +01:00
c5ddeb2d8a
fix: dont update catalogue on autocomplete 2025-01-03 08:10:57 +01:00
0a63f9ce27
fix: undeploy handles chaos/unstaged in overview
Follows 3a71dc47f8afa8e64adb86868a17650ea98bb842
2025-01-02 21:50:23 +01:00
3a71dc47f8
fix: more env version write tests
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See toolshed/organising#661
2025-01-02 21:20:40 +01:00
f07c64f7b8
fix: sort abra app env output
All checks were successful
continuous-integration/drone/push Build is passing
2025-01-02 16:40:23 +01:00
dd03c40e10
feat: abra app env 2025-01-02 16:32:32 +01:00
48198d55bd
chore: rename [ci skip] 2025-01-02 11:31:15 +01:00
c0931b96d8
fix: use same wording 2025-01-02 11:31:04 +01:00
64ea0f9684
test: drop, version is written on app new [ci skip] 2025-01-02 11:26:27 +01:00
b0cd8ccbb9
refactor/fix: deploy/upgrade/rollback
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See coop-cloud/abra#461
2025-01-02 11:12:38 +01:00
5975be6870
fix: unstaged changes handling
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See toolshed/organising#651
2024-12-31 16:37:02 +01:00
bfed51a69c
fix: no newline on status in logs
All checks were successful
continuous-integration/drone/push Build is passing
2024-12-31 08:26:35 +01:00
5d0faf5e13
fix: only log once for the loaded app 2024-12-31 08:26:19 +01:00
cd6af9708c
docs: <> -> [] 2024-12-31 08:26:01 +01:00
ef95bce1e4
fix: use default styles
All checks were successful
continuous-integration/drone/push Build is passing
2024-12-30 18:10:01 +01:00
a159583874
chore: make format
Some checks failed
continuous-integration/drone/push Build is failing
2024-12-30 18:07:58 +01:00
e3b0500875
fix: dont output error twice
Some checks failed
continuous-integration/drone/push Build is failing
2024-12-30 18:05:26 +01:00
994310a4ff
refactor!: use charm defaults 2024-12-30 18:05:04 +01:00
74108b0dd9 fix: create release dir in recipe if not exists #660
All checks were successful
continuous-integration/drone/push Build is passing
2024-12-29 18:12:53 +00:00
3727c7fa78
test: ensure catalogue
All checks were successful
continuous-integration/drone/push Build is passing
2024-12-29 00:44:47 +01:00
9a4414fd13
test: fix failing upgrade test
All checks were successful
continuous-integration/drone/push Build is passing
2024-12-29 00:14:16 +01:00
9f189680f3
fix: less newline
All checks were successful
continuous-integration/drone/push Build is passing
2024-12-28 23:47:50 +01:00
356e527f1f
refactor!: upgrade/rollback vertical render / ui fixes
All checks were successful
continuous-integration/drone/push Build is passing
See toolshed/organising#658
2024-12-28 23:35:47 +01:00
7ec61c6d03
fix: sort versions upgrade/rollback/list
All checks were successful
continuous-integration/drone/push Build is passing
See toolshed/organising#649
2024-12-28 23:10:22 +01:00
fab93a559a
fix: more robust <app> autocomplete + error handling
All checks were successful
continuous-integration/drone/push Build is passing
See toolshed/organising#652
2024-12-28 22:22:13 +01:00
8ac31330be
fix: error out if missing "deploy.labels"
See toolshed/organising#643
2024-12-28 21:55:02 +01:00
03000c25e0
refactor: parametrize default value 2024-12-28 21:54:14 +01:00
3f32dbb1a3
fix: better "server add" failure
See toolshed/organising#570
2024-12-28 21:17:51 +01:00
27f68b1dc5
refactor!: recipe fetch [recipe | --all]
See toolshed/organising#639
2024-12-28 20:55:25 +01:00
a0da5299fe feat: write undeploy version
All checks were successful
continuous-integration/drone/push Build is passing
See toolshed/organising#633
2024-12-28 19:42:01 +00:00
866c5c4536
test: even moar integration suite patches
All checks were successful
continuous-integration/drone/push Build is passing
2024-12-28 17:16:53 +01:00
dc4c6784cb
test: integration test patches
All checks were successful
continuous-integration/drone/push Build is passing
2024-12-28 16:39:58 +01:00
97959ef5da refactor!: vertical render & UI/UX fixes
All checks were successful
continuous-integration/drone/push Build is passing
See coop-cloud/abra#454
2024-12-28 15:00:31 +00:00
b6573720ec fix: Adds chaos flag to app/cp command
All checks were successful
continuous-integration/drone/push Build is passing
2024-12-28 13:56:43 +01:00
4e8995cc0e
fix: moar integration test patches
All checks were successful
continuous-integration/drone/push Build is passing
See toolshed/organising#650
2024-12-27 21:55:01 +01:00
efb3fd8759
test: moar fixes
All checks were successful
continuous-integration/drone/push Build is passing
See toolshed/organising#650
2024-12-27 21:16:15 +01:00
008582c3d9
test: fixes for test suite post-cobra migrate
All checks were successful
continuous-integration/drone/push Build is passing
See toolshed/organising#650
2024-12-27 20:44:07 +01:00
8fa20e2c7f
feat: new backup/restore
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-12-27 19:27:56 +01:00
aa1dc795ef
fix: disable default complete func
All checks were successful
continuous-integration/drone/push Build is passing
2024-12-27 13:55:45 +01:00
18df498295
chore: deps and vendor
All checks were successful
continuous-integration/drone/push Build is passing
2024-12-27 13:47:45 +01:00
671e1ca276
refactor!: cobra migrate
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-12-27 13:32:29 +01:00
0df2b15c33
fix: reinstate no-input as a global flag
All checks were successful
continuous-integration/drone/push Build is passing
2024-12-23 11:27:11 +01:00
3f29084664
chore: refactor / docstrings
All checks were successful
continuous-integration/drone/push Build is passing
2024-12-21 19:22:26 +01:00
0bb25a00ec
test: migrated server 2024-12-21 19:21:50 +01:00
Ammar Hussein
28c7676413 replace code-descriptive comments with method level comments
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2024-12-15 09:53:28 -08:00
Ammar Hussein
730fef09a3 add test for SwitchToMain
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2024-12-14 18:41:34 -08:00
Ammar Hussein
8d076a308a bubble up errors on branch switch
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2024-12-14 18:26:22 -08:00
Ammar Hussein
9510c04aeb new recipe default branch main instead of master
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2024-12-12 19:08:18 -08:00
d9e60afd71
chore: upgrade go version
All checks were successful
continuous-integration/drone/push Build is passing
2024-12-02 01:47:45 +01:00
31fa9b1a7a
chore: make deps, go mod vendor
Some checks failed
continuous-integration/drone/push Build is failing
2024-12-02 01:45:06 +01:00
Ammar Hussein
f664599836 [fix] chaos mode always fails deploy
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-11-30 20:10:04 -08:00
Ammar Hussein
bba1640913 Merge branch 'ammaratef45-removeDomainCheck'
All checks were successful
continuous-integration/drone/push Build is passing
2024-11-27 11:48:12 -08:00
Ammar Hussein
7b54c2b5b9 remove whitespace
Some checks failed
continuous-integration/drone/pr Build is failing
2024-11-27 11:38:49 -08:00
Ammar Hussein
8ee1947fe9 remove -D on server add
Some checks failed
continuous-integration/drone/pr Build is failing
2024-11-25 17:23:00 -08:00
b313b0a145
fix: use old auto-completion for 0.9.x compat
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See toolshed/organising#644

Partial revert of 1f8662cd9518c3d1e14d058f2a88d438b59170b2
2024-10-27 08:54:43 +01:00
1f9b863be0
fix: appease formatter, ignore vendor
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-10-21 16:46:39 +02:00
3b3ce85ef9
fix: rebase coop-cloud/organising#533
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2024-10-21 16:39:36 +02:00
1f8662cd95
refactor: urfave v3 2024-10-21 16:39:27 +02:00
375e17a4a0
refactor: urfave v2 2024-10-21 11:00:35 +02:00
04aec8232f
chore: vendor
Some checks failed
continuous-integration/drone/push Build is failing
2024-08-04 11:06:58 +02:00
2a5985e44e
build: drop 2MB with GCFLAGS [ci skip] 2024-07-27 12:56:43 +02:00
c65be64e7d
fix: dont checkout version for abra app undeploy
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See coop-cloud/organising#628
2024-07-24 16:09:27 +02:00
fd8652e26d
fix: --chaos/--offline for abra app ps
See coop-cloud/organising#628
See coop-cloud/organising#629
2024-07-24 16:09:03 +02:00
518c5795f4
fix: avoid overwriting non version env vars
See coop-cloud/organising#630
2024-07-24 16:07:08 +02:00
827edcb0da
test: full width for CI testing [ci skip]
Also clean up the .env.sample.
2024-07-18 11:03:02 +02:00
05489a129c
test: re-create serer for setup [ci skip] 2024-07-17 14:32:53 +02:00
c02e11eb0a
test: fix order of teardown [ci skip] 2024-07-17 14:15:03 +02:00
8b8e158664
test: int suite fixes
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-17 14:05:46 +02:00
e5a6dea10c
fix: catch ctrl-c again; less cryptic logging
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-17 10:09:09 +02:00
1132b09b5b
fix: error out for invalid env versions 2024-07-17 10:08:51 +02:00
b2436174b0
chore: more logging for env versions 2024-07-17 10:08:32 +02:00
ea10019068
fix: "secret insert" respects env version 2024-07-17 10:08:13 +02:00
9b0b3c2e4c
fix: override version from CLI
See coop-cloud/organising#541
2024-07-17 10:07:47 +02:00
8084bff104
test: env version tests
See coop-cloud/organising#541
2024-07-17 10:06:46 +02:00
d22e2c38ce
test: just build main
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-17 08:29:58 +02:00
e945087f79
test: env version writing tests 2024-07-17 08:27:12 +02:00
7734dd555d
fix: spacer between multiple versions 2024-07-17 02:12:26 +02:00
aedf5e5ff7
fix: dont write commented out versions
See coop-cloud/organising#626
2024-07-17 01:56:28 +02:00
95c598d030
feat: "app new" supports writing env files
And, automagically, chaos commit hashes.
2024-07-17 01:45:19 +02:00
56068362e8
fix: write versions on deploy/upgrade/rollback
See coop-cloud/organising#625
2024-07-17 01:29:49 +02:00
cf14731b46
refactor: "false" -> CHAOS_DEFAULT 2024-07-17 01:23:12 +02:00
486cfa68d8
test: explode on failures
All checks were successful
continuous-integration/drone/push Build is passing
Closes coop-cloud/organising#623
2024-07-17 00:16:47 +02:00
1718903834
test: reset recipe before undeploying [ci skip] 2024-07-17 00:00:06 +02:00
eb9894e5bb
test: dont clone if exists [ci skip] 2024-07-16 23:51:28 +02:00
a2116774e8
test: ensure catalogue in place [ci skip] 2024-07-16 23:46:02 +02:00
d2efdf8bf5
test: adjust output checking [ci skip] 2024-07-16 23:39:10 +02:00
b15c05929c
test: adjust output checking [ci skip] 2024-07-16 23:32:12 +02:00
f167a91868
test: skip for now, it's flaking again [ci skip]
We need to solve coop-cloud/organising#541
2024-07-16 23:28:18 +02:00
8cded8752a
test: ensure correct server for diffing [ci skip] 2024-07-16 23:25:17 +02:00
d1876e2fae
test: do exact diff of JSON for integration
All checks were successful
continuous-integration/drone/push Build is passing
See coop-cloud/organising#627
2024-07-16 23:19:36 +02:00
e42a1bca29
fix: add chaos/deploy versiosn back to ps output
All checks were successful
continuous-integration/drone/push Build is passing
Fix to support alakazam parsing
2024-07-16 22:47:47 +02:00
b5493ba059
refactor: CreateTable2 -> CreateTable [ci skip] 2024-07-16 22:45:03 +02:00
a41a36b8fd
fix: dont lock existing version on rollback
All checks were successful
continuous-integration/drone/push Build is passing
Otherwise, we can't select previous versions.
2024-07-16 17:35:15 +02:00
de006782b6
refactor: tablewriter -> lipgloss
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
Also the jsontable impl. is dropped also. Output is unchanged.
2024-07-16 16:22:47 +02:00
f28cffe6d8
refactor: vertical deploy overview 2024-07-16 09:37:10 +02:00
d3ede0f0f6
refactor: logging with background/padding 2024-07-15 22:55:02 +02:00
ae4653f5e3
build: add full install target [ci skip] 2024-07-13 15:30:38 +02:00
f
7f0a74d3c3
fix: source autocompletion on the current terminal
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-11 12:02:38 -03:00
f
e99114e695
fix: setup should be run once 2024-07-11 12:02:22 -03:00
f
b1208f9db5
fix: sometimes the completion directories already exist 2024-07-11 12:01:21 -03:00
b8e1a3b75f
test: remote recipe tests
All checks were successful
continuous-integration/drone/push Build is passing
See coop-cloud/abra#432
2024-07-10 16:03:28 +02:00
ff90b43929
fix: use struct data for HEAD retrieval
All checks were successful
continuous-integration/drone/push Build is passing
See ce7dda1eaea5665da7155b4fc2d0a5f885237b1a
2024-07-10 15:51:11 +02:00
c5724d56f8 fix(config): Removes config file name from abra dir
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-10 13:42:24 +00:00
ce7dda1eae
fix: use recipe struct data
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
Follow up for coop-cloud/abra#432
2024-07-10 15:40:45 +02:00
d38f3ab7f5
test: speed up test
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-10 13:27:58 +02:00
4be8c8daed
test: fix outputs [ci skip]
See https://build.coopcloud.tech/coop-cloud/abra/2035/1/5
2024-07-10 13:20:39 +02:00
3c9405a4ed
refactor!: --problems/p goes away
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
Follow up for coop-cloud/abra#413
2024-07-10 13:06:46 +02:00
f6b7510da6 feat: introduce remote recipes
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: coop-cloud/abra#432
2024-07-10 10:25:06 +00:00
7596982282 feat: update new version in env file
All checks were successful
continuous-integration/drone/pr Build is passing
2024-07-10 12:12:43 +02:00
4085eb6654 feat: define recipe version inside app env file 2024-07-10 12:11:46 +02:00
790dbca362 feat!: remove all catalogue reads from app commands 2024-07-10 12:06:57 +02:00
d7a870b887 feat: remote recipes 2024-07-10 12:06:44 +02:00
1a3ec7a107
fix: pass recipe name for listing cmds
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-09 17:23:06 +02:00
7f910b4e5b
test: recipe test fixups
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-09 11:34:20 +02:00
b82ac3bd63
refactor: make IsChaos an actual bool 2024-07-09 11:34:01 +02:00
00d60f7114
fix: ensure force upgrade/rollback works 2024-07-09 11:33:33 +02:00
71d93cbbea
refactor: debug logging and errors for version issues 2024-07-09 11:33:07 +02:00
2fb5493ab5
feat: support chaos commits on deploy
See coop-cloud/organising#517
2024-07-09 11:31:52 +02:00
0ff8e49cfd
docs: pass on sub-command help 2024-07-09 09:43:18 +02:00
addbda9145
test: fixups for the changepocalypse
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-09 09:41:49 +02:00
c33ca1c6bc
fix!: chaos consistency (deploy/undeploy/rollback/upgrade)
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See coop-cloud/organising#559

--chaos for rollback/upgrade goes away.
2024-07-08 17:23:49 +02:00
4580df72cb
fix: use recipe name
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-08 14:58:57 +02:00
f003430a8d
fix: use recipe name, not app name
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-08 14:54:15 +02:00
5426464092
refactor!: drop version, show versions in ps
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See coop-cloud/organising#526
See coop-cloud/organising#502
2024-07-08 14:41:46 +02:00
72c021c727
fix: remove old commands from deploy fail help
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-08 14:29:51 +02:00
f2e076b35f
fix: set default logger on kadabra 2024-07-08 14:26:27 +02:00
4ccb4198d6
fix: "recipe version" handles non-catalogue recipes 2024-07-08 14:26:26 +02:00
a9f7579ca9
fix: remove old logrus calls 2024-07-08 14:21:17 +02:00
9cd1fe658b refactor(recipe): create a recipe struct that gets used everywhere #430
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: coop-cloud/abra#430
2024-07-08 12:18:58 +00:00
41c16db670 test: fix test failure
All checks were successful
continuous-integration/drone/pr Build is passing
2024-07-08 14:10:17 +02:00
87ecc05962 refactor(recipe): remove direct usage of config.RECIPE_DIR
Some checks failed
continuous-integration/drone/pr Build is failing
2024-07-08 13:48:02 +02:00
f14d49cc64 refactor(recipe): rename Recipe2 -> Recipe 2024-07-08 13:19:40 +02:00
f638b6a16b refator(recipe): remove old struct 2024-07-08 13:16:47 +02:00
5617a9ba07 refactor(recipe): remove remaining usage of old recipe struct 2024-07-08 13:15:20 +02:00
c1b03bcbd7 refactor(recipe): load load compoes config where its used 2024-07-08 12:31:39 +02:00
99da8d4e57 refactor(recipe): move GetComposeFiles to new struct 2024-07-08 12:06:58 +02:00
ca1db33e97 refactor(recipe): remove Dir method on old struct 2024-07-08 11:48:53 +02:00
eb62e0ecc3 refactor(recipe): move Tags method to new struct 2024-07-08 11:45:47 +02:00
6f90fc3025 refactor(recipe): don't use README.md path directly 2024-07-08 11:43:18 +02:00
c861c09cce refactor(recipe): use method or variable for .env.sample 2024-07-08 11:41:26 +02:00
2f41b6d8b4 refactor(recipe): store sample env path in new struct 2024-07-08 11:31:55 +02:00
73e9b818b4 refactor(recipe): move SampleEnv method to new struct 2024-07-08 11:02:43 +02:00
f268e5893b refactor(recipe): move functions that operate on the git repo to new file 2024-07-08 11:00:50 +02:00
47013c63d6 refactor(recipe): use template for ssh url 2024-07-08 10:56:08 +02:00
4cf6155fb8 refactor(recipe): introduce Dir var 2024-07-08 10:56:08 +02:00
01f3f4be17 refactor(recipe): use new recipe.Ensure method 2024-07-08 10:55:55 +02:00
eee2ecda06 refactor(recipe): add offline and chaos options to Ensure method 2024-07-08 10:55:55 +02:00
950f85e2b4 refactor(recipe): introduce new recipe struct and move some methods 2024-07-08 10:55:43 +02:00
9ef64778f5
chore: go deps update
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-08 01:52:17 +02:00
735f521bc0
refactor(errors)!: remove WIP/broken command
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-08 01:33:06 +02:00
96a25425a4
refactor(ps)!: remove -w, "watch ..." does it better
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-08 01:10:58 +02:00
1a8dca9804
fix(deploy): only output when actually waiting
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-08 01:01:14 +02:00
465827d5ee
log: no additional newlines 2024-07-08 01:01:14 +02:00
cde06f4f00
log: output caller on debug, use stdout as default 2024-07-08 01:01:13 +02:00
050a479df7
refactor: "service name" -> "service" 2024-07-08 00:38:54 +02:00
ef108d63e1
refactor: use central logger
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-08 00:01:28 +02:00
cf8ff410cc
feat: central log config
See coop-cloud/organising#422
2024-07-08 00:01:27 +02:00
6ec678208f
chore: formatting 2024-07-07 22:40:06 +02:00
a001be3021
docs: better "app ps" description 2024-07-07 22:39:57 +02:00
6712bd446f
chore: add upstream link
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-07 21:52:45 +02:00
1097daa69f
fix: "abra app restart" docs + --all-services
See coop-cloud/organising#605
2024-07-07 21:52:24 +02:00
beaa233421
test: only publish image on main merge
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2024-07-07 12:21:51 +02:00
f871f9beee
test: reduce duplication
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-07 12:13:07 +02:00
0f8f0f908f
test: ensure catalogue
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-07 12:03:43 +02:00
c5211fbd7e
test: fix imports 2024-07-07 12:03:37 +02:00
0076b31253 new package envfile and move GetComposeFiles to recipe package
Some checks failed
continuous-integration/drone/pr Build is failing
2024-07-06 16:37:16 +02:00
37aff723c0 move GetComposeFiles 2024-07-06 16:37:16 +02:00
f18c642226 refactor: move app files from config to app package 2024-07-06 16:37:16 +02:00
ac695ae28e feat: introduce abra config file and load abra dir from it (!419)
All checks were successful
continuous-integration/drone/push Build is passing
This is the first step to introduce a configuration file for abra. The config file must be named `abra.yaml` or àbra.yml`. abra look for the config file in the current directory and when not found traverses the directory tree up until it is found or the home/root directory is reached.

For now there is only one setting that is made configurable: `abraDir`. The new logic for setting the abra dir is the following:
1. lookup `$ABRA_DIR` env
2. look for config file and take value from there
3. `$HOME/.abra` as fallback

See coop-cloud/organising#303.

Reviewed-on: coop-cloud/abra#419
Reviewed-by: decentral1se <decentral1se@noreply.git.coopcloud.tech>
Co-authored-by: p4u1 <p4u1_f4u1@riseup.net>
Co-committed-by: p4u1 <p4u1_f4u1@riseup.net>
2024-07-06 14:36:31 +00:00
ac87898005
test: run versioned script [ci skip] 2024-07-03 10:02:04 +02:00
32ae2499b6
test: add CI integration script [ci skip] 2024-07-03 09:57:22 +02:00
1136ec5dcd
build: remove old release scripts 2024-07-03 09:57:06 +02:00
6a2db1abaa
test: run int suite on remote server via cron
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-02 17:18:05 +02:00
9554ad40c8
refactor: use adapted upstream detach=false logic [ci skip]
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See coop-cloud/organising#607.
2024-07-02 14:52:12 +02:00
2014cd6622
test: less fragile integration suite [ci skip]
See coop-cloud/organising#584
See coop-cloud/organising#595
2024-07-02 12:16:58 +02:00
a9ce2106c6
test: skip test for now
All checks were successful
continuous-integration/drone/push Build is passing
Also, don't build image if tests fail.
2024-06-28 06:12:32 +02:00
34de38928a
test: include catalogue
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-26 23:46:35 +02:00
f58522d822
fix: dont always download the catalogue
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
See coop-cloud/organising#592
2024-06-25 16:48:41 +02:00
712ebfb701
test: update and fix cleanup for "server add"
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-25 16:24:44 +02:00
1fe601cd16
fix: custom timeout only for "server add" 2024-06-25 16:13:57 +02:00
7b7e1bfa97
refactor!: server add/rm has better UI/UX
Less confusing logging messages, clear "is created" / "already exists"
output. Move the majority of logging to debug output to not confuse the
situation. Some code cleanups also in there.
2024-06-25 09:48:53 +02:00
1a12bef53e
docs: better "server add" help output 2024-06-25 09:24:01 +02:00
d787f71215
fix: more accurate dns errors
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-25 00:27:48 +02:00
9bf44c15ed
fix: clean up if failed to create context 2024-06-25 00:27:34 +02:00
349cacc1f2
docs: explain -D for "server add" 2024-06-25 00:27:16 +02:00
938534f5ac feat: support non-TLD resolving server domains
All checks were successful
continuous-integration/drone/push Build is passing
See coop-cloud/organising#566
2024-06-24 22:07:16 +00:00
6cd331ebd6 secret: allow inserting secret from file and add trim flag
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-22 16:49:59 +00:00
40517171f7
test: separate test for git name/email
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See coop-cloud/abra#405
2024-06-22 18:46:28 +02:00
b2485cc122 feat: add git-user and git-email flags to recipe new
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-22 16:38:32 +00:00
9ec99c7712 test: return/echo from git helper functions
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-22 17:04:33 +02:00
aa3910f8df
refactor!: drop all SSH opts / config handling
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See coop-cloud/organising#601
See coop-cloud/organising#482
2024-06-21 17:16:41 +02:00
43990b6fae
test: use more plumbung for git output
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-21 17:10:12 +02:00
91ea2c01a5
fix: fix old app version deploy wrt. compose files
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See coop-cloud/organising#617
2024-06-21 16:14:40 +02:00
316fdd3643
fix: abra app new checks out latest version
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See coop-cloud/organising#618
2024-06-21 15:51:34 +02:00
e07ae8cccd
chore: make format/check
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-19 19:17:22 +02:00
300a4ead01
fix: stop using deprecated APIs
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2024-06-19 19:14:52 +02:00
f209b6f564
chore: go get -u -t 2024-06-19 19:14:44 +02:00
791183adfe
build: new deps target 2024-06-19 19:14:31 +02:00
e6b35e8524 fix(upgrade): make upgrade --chaos working again
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-22 10:21:31 +02:00
8a0274cac0 fix(recipe): output correct formatted json for recipe version
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-21 16:59:59 +02:00
e609924af0 feat(upgrade): add --releasenotes: show release notes and skip upgrading
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-21 13:49:36 +02:00
70e2943301 fix(upgrade): only show release notes relevant for the upgrade 2024-05-21 13:49:11 +02:00
0590c1824d checkout deployed version
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-05-14 00:07:58 +02:00
459abecfa5 only show container that should be deployed
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2024-05-13 23:26:02 +02:00
183ad8f576 machine readable ps output
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2024-05-13 22:08:03 +02:00
03f94da2d8
docs: add fauno [ci skip] 2024-05-01 01:20:25 +02:00
f
766f69b0fd
feat: strip debug symbols
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
to produce smaller binaries
2024-04-30 14:05:03 -03:00
004cd70aed
fix: use unique rule number & wording [ci skip] 2024-04-06 23:52:56 +02:00
a4de446f58
test: more verbose failure msg, use contains [ci skip] 2024-04-06 23:48:22 +02:00
d21c35965d fix: add warning for long secret names (!359)
All checks were successful
continuous-integration/drone/push Build is passing
A start of a fix for coop-cloud/organising#463
Putting some code out to start a discussion.  I've added a linting rule for recipes to establish a general principal but I want to put some validation into cli/app/new.go as that's the point we have both the recipe and the domain and can say for sure whether or not the secret names lengths cause a problem but that will have to wait for a bit.  Let me know if I've missed the mark somewhere

Reviewed-on: coop-cloud/abra#359
Reviewed-by: decentral1se <decentral1se@noreply.git.coopcloud.tech>
Co-authored-by: Rich M <r.p.makepeace@gmail.com>
Co-committed-by: Rich M <r.p.makepeace@gmail.com>
2024-04-06 21:41:37 +00:00
63ea58ffaa add relevant command to error message
All checks were successful
continuous-integration/drone/push Build is passing
2024-04-01 18:51:53 +01:00
2ecace3e90
fix: add missing packages on final layer
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
Closes coop-cloud/organising#598
2024-04-01 13:57:51 +02:00
d5ac3958a4 feat: add retries to app volume remove
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-27 05:38:24 +00:00
3wc
72c20e0039 fix: make installer work again
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-26 21:07:38 -03:00
575f9905f1
Revert "Revert "feat: backup revolution""
All checks were successful
continuous-integration/drone/push Build is passing
This reverts commit 2c515ce70adbcb808895b8bb1efd6036e51092c1.
2024-03-12 10:34:40 +01:00
e3a0af5840
build: upgrade goreleaser
All checks were successful
continuous-integration/drone/push Build is passing
Closes coop-cloud/organising#474
2024-03-12 10:11:14 +01:00
9a3a39a185
chore: new 0.9.x series
Some checks reported errors
continuous-integration/drone/push Build was killed
2024-03-12 10:05:31 +01:00
cea56dddde
fix: drop deprecated stanza (goreleaser) 2024-03-12 10:04:50 +01:00
2c515ce70a
Revert "feat: backup revolution"
This reverts commit c5687dfbd7413f87ad202a4a275da495edae4730.

This is a temporary measure to facilitate a release which won't
completely explode peoples workflows (missing command logic). We
re-instate this commit after the first 0.9.x release.
2024-03-12 10:03:42 +01:00
40c0fb4bac fix-integration-tests (!403)
All checks were successful
continuous-integration/drone/push Build is passing
In preparation for the new abra release, let's fix all integration tests

After merging, this needs to be cherry-picked into the release-0-9 branch.

  - [x] app_backup.bats (skip this one)
  - [x] app_check.bats (fixed by bd21014fed)
  - [x] app_cmd.bats (partially fixed in 08232b74f6), has known regression coop-cloud/organising#581
  - [x] app_config.bats (no changes needed)
  - [x] app_cp.bats (no changes needed)
  - [x] app_deploy.bats
  - [x] app_errors.bats (no changes needed)
  - [x] app_list.bats (no changes needed)
  - [x] app_logs.bats (no changes needed)
  - [x] app_new.bats (no changes needed)
  - [x] app_ps.bats (no changes needed)
  - [x] app_remove.bats (fixed by [2f29fbeb2e](coop-cloud/abra#403/commits/2f29fbeb2e018656413fa25f8615b7a98cdcb083))
  - [x] app_restart.bats (no changes needed
  - [x] app_restore.bats (fixed by [f2dd5afc38](coop-cloud/abra#403/commits/f2dd5afc38a25a8316899fa0c6d59499445868d7))
  - [x] app_rollback.bats (partially fixed by 6e99b74c24)
  - [x] app_run.bats (no changes needed)
  - [x] app_secret.bats (fixed by bd069d32f6)
  - [x] app_services.bats (no changes needed)
  - [x] app_undeploy.bats (no changes needed)
  - [x] app_upgrade.bats (no changes needed)
  - [x] app_version.bats (partially fixed by ad323ad2bd)
  - [x] app_volume.bats (fixed by [03c3823770](coop-cloud/abra#403/commits/03c38237707ae795b723180eb07a7edc84a8de35))
  - [x] autocomplete.bats (no changes needed)
  - [x] catalogue.bats (no changes needed)
  - [x] dirs.bats (no changes needed)
  - [x] install.bats (failes, but is expected)
  - [x] recipe_diff.bats (no changes needed)
  - [x] recipe_fetch.bats (no changes needed)
  - [x] recipe_lint.bats (fixed by [b6b0808066](coop-cloud/abra#403/commits/b6b0808066a11e4bcd77517ec39600d500bcb944))
  - [x] recipe_list.bats (no changes needed)
  - [x] recipe_new.bats (fixed by [0aac464ded](coop-cloud/abra#403/commits/0aac464ded6b43afb3ec37ade2f64d6191b9838f))
  - [x] recipe_release.bats (no changes needed)
  - [x] recipe_reset.bats (no changes needed)
  - [x] recipe_sync.bats (no changes needed)
  - [x] recipe_upgrade.bats (fixed by [ab86904cf4](coop-cloud/abra#403/commits/ab86904cf45db89c7c189ca1fd9971909bd446dd))
  - [x] recipe_version.bats (fixed by 81897bf4da)
  - [x] server_add.bats
  - [x] server_list.bats
  - [x] server_prune.bats (no changes needed)
  - [x] server_remove.bats
  - [x] upgrade.bats
  - [x] version.bats (no changes needed)

Co-authored-by: decentral1se <cellarspoon@riseup.net>
Reviewed-on: coop-cloud/abra#403
Co-authored-by: p4u1 <p4u1_f4u1@riseup.net>
Co-committed-by: p4u1 <p4u1_f4u1@riseup.net>
2024-03-11 13:27:21 +00:00
0643df6d73 feat: fetch all recipes when no recipe is specified (!401)
All checks were successful
continuous-integration/drone/push Build is passing
Closes coop-cloud/organising#530

Reviewed-on: coop-cloud/abra#401
Reviewed-by: decentral1se <decentral1se@noreply.git.coopcloud.tech>
Co-authored-by: p4u1 <p4u1_f4u1@riseup.net>
Co-committed-by: p4u1 <p4u1_f4u1@riseup.net>
2024-01-24 15:01:33 +00:00
e9b99fe921 make installer save abra-download to /tmp/ directory
All checks were successful
continuous-integration/drone/push Build is passing
the current location of download is ~/.local/bin/ but this
conflicts with some security tools
2024-01-24 14:27:09 +00:00
4920dfedb3 fix: retry docker volume remove (!399)
All checks were successful
continuous-integration/drone/push Build is passing
Closes coop-cloud/organising#509

Reviewed-on: coop-cloud/abra#399
Reviewed-by: decentral1se <decentral1se@noreply.git.coopcloud.tech>
Co-authored-by: p4u1 <p4u1_f4u1@riseup.net>
Co-committed-by: p4u1 <p4u1_f4u1@riseup.net>
2024-01-19 15:09:00 +00:00
0a3624c15b feat: add version input to abra app new (!400)
All checks were successful
continuous-integration/drone/push Build is passing
Closes coop-cloud/organising#519

Reviewed-on: coop-cloud/abra#400
Reviewed-by: decentral1se <decentral1se@noreply.git.coopcloud.tech>
Co-authored-by: p4u1 <p4u1_f4u1@riseup.net>
Co-committed-by: p4u1 <p4u1_f4u1@riseup.net>
2024-01-19 15:08:41 +00:00
c5687dfbd7
feat: backup revolution
All checks were successful
continuous-integration/drone/push Build is passing
See coop-cloud/organising#485
2024-01-12 22:01:08 +01:00
ca91abbed9 fix: correct append service name logic in Filters function (!396)
All checks were successful
continuous-integration/drone/push Build is passing
This fixes a regression introduced by #395

Reviewed-on: coop-cloud/abra#396
Co-authored-by: p4u1 <p4u1_f4u1@riseup.net>
Co-committed-by: p4u1 <p4u1_f4u1@riseup.net>
2023-12-22 12:08:12 +00:00
d4727db8f9 feat: abra app logs shows task errors (!395)
All checks were successful
continuous-integration/drone/push Build is passing
The log command now checks for the ready state in the task list. If it is not ready. It shows the task logs. This might look like this:
```
ERRO[0000] Service abra-test-recipe_default_app: State rejected: No such image: ngaaaax:1.21.0
ERRO[0000] Service abra-test-recipe_default_app: State preparing:
ERRO[0000] Service abra-test-recipe_default_app: State rejected: No such image: ngaaaax:1.21.0
ERRO[0000] Service abra-test-recipe_default_app: State rejected: No such image: ngaaaax:1.21.0
ERRO[0000] Service abra-test-recipe_default_app: State rejected: No such image: ngaaaax:1.21.0
```

Closes coop-cloud/organising#518

Reviewed-on: coop-cloud/abra#395
Reviewed-by: decentral1se <decentral1se@noreply.git.coopcloud.tech>
Co-authored-by: p4u1 <p4u1_f4u1@riseup.net>
Co-committed-by: p4u1 <p4u1_f4u1@riseup.net>
2023-12-14 13:15:24 +00:00
af8cd1f67a feat: abra release now asks for a release note (!393)
All checks were successful
continuous-integration/drone/push Build is passing
This implements coop-cloud/organising#540 by checking if a`release/next` file exists and if so moves it to `release/<tag>`. When no release notes exists it prompts for them.

Reviewed-on: coop-cloud/abra#393
Reviewed-by: moritz <moritz.m@local-it.org>
Co-authored-by: p4u1 <p4u1_f4u1@riseup.net>
Co-committed-by: p4u1 <p4u1_f4u1@riseup.net>
2023-12-12 14:46:20 +00:00
cdd7516e54
chore: go mod tidy [ci skip] 2023-12-04 22:56:58 +01:00
test
99e3ed416f fix: secret name generation when secretId is not part of the secret name
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-04 21:52:09 +00:00
02b726db02 add comments to better explain how the length modifier gets added to the secret
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-04 17:30:26 +00:00
2de6934322 feat: abra app cp enhancements
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-02 15:39:27 +00:00
cb49cf06d1
chore: drop old godotenv pointers [ci skip]
Follows 9affda8a70270632ecea60ef592e7f3287bd0374
2023-12-02 13:02:24 +01:00
9affda8a70
chore: update godotenv fork commit pointer
All checks were successful
continuous-integration/drone/push Build is passing
Follows coop-cloud/abra#391
2023-12-02 12:59:42 +01:00
3957b7c965 proper env modifiers support
All checks were successful
continuous-integration/drone/push Build is passing
This implements proper modifier support in the env file using this new fork of the godotenv library. The modifier implementation is quite basic for but can be improved later if needed. See this commit for the actual implementation.

Because we are now using proper modifer parsing, it does not affect the parsing of value, so this is possible again:
```
MY_VAR="#foo"
```
Closes coop-cloud/organising#535
2023-12-01 11:03:52 +00:00
0d83339d80 fix(ssh): increase connection timeout #482
All checks were successful
continuous-integration/drone/push Build is passing
see coop-cloud/organising#482
2023-11-30 16:35:53 +01:00
6e54ec7213
test: skip failing test for now
All checks were successful
continuous-integration/drone/push Build is passing
See coop-cloud/organising#535.
2023-11-28 11:42:36 +01:00
66b40a9189
fix: just run it in place [ci skip] 2023-11-27 11:25:01 +01:00
049f02f063
docs: add p4u1 [ci skip] 2023-11-27 11:23:03 +01:00
15857e6453
fix: clean up after cp'ing script [ci skip]
Follows 31e0ed75b0084dfc05388ee749537b1e0c0b4a39.
2023-11-27 11:21:46 +01:00
31e0ed75b0
build: target for docker building
Some checks failed
continuous-integration/drone/push Build is failing
Adapted from coop-cloud/abra#384.

Thanks @cas.
2023-11-27 11:15:59 +01:00
b1d3fcbb0b add integration test
Some checks failed
continuous-integration/drone/push Build is failing
2023-11-27 10:01:33 +00:00
7b6134f35e add bash completion for abra cmd 2023-11-27 10:01:33 +00:00
316b59b465
test: support local-first testing
Some checks failed
continuous-integration/drone/push Build is failing
Cherry-picked from coop-cloud/abra#389

Thanks @p4u1.
2023-11-27 10:41:46 +01:00
92b073d5b6
chore: go mod tidy
Some checks failed
continuous-integration/drone/push Build is failing
2023-11-27 10:28:43 +01:00
9b0dd933b5 chore(deps): update module github.com/schollz/progressbar/v3 to v3.14.1
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-11-10 08:00:52 +00:00
f255fa1555 chore(deps): update module github.com/hashicorp/go-retryablehttp to v0.7.5
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-11-09 08:00:33 +00:00
74200318ab chore(deps): update module github.com/schollz/progressbar/v3 to v3.14.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-11-07 08:01:11 +00:00
609656b4e1 chore(deps): update module golang.org/x/sys to v0.14.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-11-06 08:00:33 +00:00
856c9f2f7d
chore: go mod tidy
Some checks failed
continuous-integration/drone/push Build is failing
2023-11-04 09:37:15 +01:00
bd5cdd3443 chore(deps): update module github.com/docker/docker to v24.0.7
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-10-30 08:00:53 +00:00
79d274e074 chore(deps): update module github.com/docker/cli to v24.0.7
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-10-27 07:01:16 +00:00
51e3df17f1 chore(deps): update module github.com/go-git/go-git/v5 to v5.10.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-10-26 07:00:33 +00:00
ccf0215495 hotfix: parse values starting with # correctly
Some checks failed
continuous-integration/drone/push Build is failing
2023-10-23 19:21:45 +02:00
254df7f2be
feat: app cmd ls
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See coop-cloud/organising#484
2023-10-17 21:16:31 +02:00
6a673ef101
refactor: filter by topic when building catalogue
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See coop-cloud/organising#377
2023-10-16 18:42:38 +02:00
7f7f7224c6
feat: diff on release flow
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
Also, don't commit unstaged files.
2023-10-16 18:31:22 +02:00
f96bf9a8ac
feat: recipe reset, recipe diff
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See coop-cloud/organising#511
2023-10-15 12:56:52 +02:00
dcecf32999
chore: bump version for installer script [ci skip] 2023-10-11 19:31:28 +02:00
bc88dac150
test: reset before changing files
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-11 19:29:19 +02:00
704c0e9c74
test: adapt failing tests to new changes 2023-10-11 18:34:08 +02:00
c9bb7e15c2
fix: bring back docker build
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-10 07:27:49 +02:00
d90c9b88f1
fix: include ca-certs to avoid x509 error [ci skip] 2023-10-10 00:50:43 +02:00
69ce07f81f
fix: ignore build files for docker [ci skip] 2023-10-09 23:40:41 +02:00
85b90ef80c fix: bail if --chaos and specific version
All checks were successful
continuous-integration/drone/push Build is passing
See coop-cloud/organising#503.
2023-10-09 20:54:44 +00:00
3e511446aa
refactor: use app check emoji here too
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-09 22:53:46 +02:00
7566b4262b
fix: set go version to 1.21
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-10-09 22:07:30 +02:00
c249c6ae9c
fix: fix: trim comments that are not modifers
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See coop-cloud/organising#505
2023-10-09 14:42:05 +02:00
be693e9df0
fix: trim comments that are not modifers
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
See coop-cloud/organising#505
2023-10-08 22:42:34 +02:00
a43125701c
test: optimise default make target for abra hacking [ci skip] 2023-10-07 10:32:42 +02:00
b57edb440a
fix: improve app check
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See coop-cloud/organising#446
2023-10-06 10:56:33 +02:00
6fc4573a71
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-06 09:49:03 +02:00
cbe6676881 chore(deps): update module golang.org/x/sys to v0.13.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-10-06 07:00:49 +00:00
b4fd39828f
test: abra-integration-test-recipe -> abra-test-recipe
All checks were successful
continuous-integration/drone/push Build is passing
See coop-cloud/abra-test-recipe#3
2023-10-05 14:22:11 +02:00
14f2d72aba
refactor!: lowercase, hyphenate keys
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
This will potentially break scripts, so time to discuss!
2023-10-05 08:36:01 +02:00
57692ec3c9
feat: add --machine to secret ls
See coop-cloud/organising#481
2023-10-04 23:08:39 +02:00
47d3b77003
refactor: not generating here, skipping
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-10-04 15:13:15 +02:00
8078e91e52
fix: warn if secrets not generated
See coop-cloud/organising#499
2023-10-04 15:13:14 +02:00
dc5d3a8dd6
test: build, init & test in one stage
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-10-04 14:37:09 +02:00
ab6107610c
test: skip build step, test will do it 2023-10-04 14:36:59 +02:00
e837835e00
test: remove duplicate call to EnsureCatalogue
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2023-10-04 14:05:02 +02:00
c646263e9e
fix: validate COMPOSE_FILE
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is passing
See coop-cloud/organising#468.
See coop-cloud/organising#376.
2023-10-04 13:27:04 +02:00
422c642949 fix: ensure ipv4 is checked, not sometimes ipv6
All checks were successful
continuous-integration/drone/push Build is passing
See coop-cloud/organising#490
2023-10-04 09:29:10 +00:00
379915587c
fix: don't export from within function
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
Also, don't explode on command function which has "export" in the name!

See coop-cloud/organising#498
2023-10-04 11:20:50 +02:00
970ae0fc4e
test: use _test to avoid cyclic imports 2023-10-04 02:36:44 +02:00
d11ad61efb
docs: make chaos flag description more generic [ci skip] 2023-10-04 01:34:53 +02:00
54dc696c69
build: fix targets for small local builds
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-03 09:31:57 +02:00
7e3ce9c42a
chore: go mod tidy 2023-10-03 09:30:26 +02:00
7751423c7d chore(deps): update module github.com/docker/distribution to v2.8.3
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2023-10-03 07:00:43 +00:00
f18f0b6f82
build: set ABRA_DIR explicitly
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-09-30 08:26:20 +02:00
892f6c0730
test: ensure catalogue is cloned 2023-09-30 08:19:16 +02:00
b53fd2689c
test: add unit test for TestEnsureDomainsResolveSameIPv4 2023-09-30 08:19:02 +02:00
906bf65d47
test: moar domain check tests [ci skip] 2023-09-29 09:31:25 +02:00
1e6a6e6174
fix: app logs retrieves recipe
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-09-27 09:19:57 +02:00
1e4f1b4ade
build: disable publish image for now
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone Build is failing
It's failing for unknown reasons and block releases.

See coop-cloud/recipes-catalogue-json#6
2023-09-25 17:51:30 +02:00
306fe02d1c
chore: tag 0.8.x series
Some checks reported errors
continuous-integration/drone/push Build was killed
2023-09-25 17:33:09 +02:00
e4610f8ad5
test: make int test script lighter [ci skip] 2023-09-25 16:45:08 +02:00
e1f900de14
test: fix app_secret generate tests [ci skip] 2023-09-25 16:32:16 +02:00
d5b18d74ef
fix: use secretId to match secret names in configs
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-25 15:51:15 +02:00
776a83d8d1
fix: use new GetComposeFiles API 2023-09-25 15:51:03 +02:00
810cea8269
test: bats does output for us [ci skip] 2023-09-25 12:14:35 +02:00
c0f3e6f2a4
test: integration test script [ci skip] 2023-09-25 12:00:39 +02:00
7b240059b0
test: fix app_backup recipe cleanups [ci skip] 2023-09-25 11:50:29 +02:00
c456d13881
test: fix recipe_* tests [ci skip] 2023-09-25 11:27:36 +02:00
c7c553164d
test: fix refute output check [ci skip] 2023-09-25 11:21:36 +02:00
7616528f4e
test: ensure app cleanup 2023-09-25 11:20:56 +02:00
6cd85f7239
test: dont assert_success for check [ci skip] 2023-09-25 11:11:29 +02:00
b1774cc44b
test: fix app_check tests 2023-09-25 10:52:47 +02:00
e438fc6e8e
test: reset recipe in file teardown 2023-09-25 10:52:27 +02:00
c065ceb1f0
test: secret generation & --offline/chaos handling tests
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2023-09-25 10:33:15 +02:00
ce4b775428
build: require 1.18 due to slices.Contains usage 2023-09-25 10:32:41 +02:00
d02f659bf8
fix: secrets from config, --offline/chaos handling, typos
See coop-cloud/organising#464
2023-09-25 10:31:59 +02:00
f3ded88ed8
fix: app version includes tags, sorts & tests
All checks were successful
continuous-integration/drone/push Build is passing
See coop-cloud/organising#442
2023-09-24 11:19:27 +02:00
bf648eeb5d
fix: recipe versions sorts, aligns & spaces 2023-09-24 11:18:26 +02:00
533edbf172
fix: recipe versions lists correctly (also -m) 2023-09-24 10:56:02 +02:00
78b8cf9725
test: fix git tag command [ci skip] 2023-09-24 00:56:00 +02:00
f0560ca975
test: no args for helpers, fix recipe_* tests [ci skip] 2023-09-23 23:57:52 +02:00
ce7b4733d7
test: tag/git helpers & refactor [ci skip] 2023-09-23 23:19:49 +02:00
575bfbb0fb
test: test arguments, notes, local tag lookup
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-09-23 09:17:24 +02:00
510ce66570
feat: version arguments, local tag lookups & release notes
See:
* coop-cloud/organising#441
* coop-cloud/organising#204
* coop-cloud/organising#493
2023-09-23 09:15:27 +02:00
82631d9ab1
fix: don't output if not tags 2023-09-23 09:15:17 +02:00
358490e939
refactor: deploy output wording 2023-09-23 09:14:45 +02:00
79b9cc9be7
fix: --offline/--chaos handlings for backup/check/cmd/restore
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-22 09:47:36 +02:00
9b6eb613aa
test: woops, keep unit test target default
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-21 12:06:41 +02:00
8f1231e409
test: integration test for abra app upgrade [ci skip] 2023-09-21 11:52:58 +02:00
aa37c936eb
test: pass arg to _checkout_recipe 2023-09-21 11:52:21 +02:00
3d1158a425
fix: don't read TIMEOUT for version= label
Some checks failed
continuous-integration/drone/push Build is failing
Closes coop-cloud/organising#451
2023-09-21 11:33:45 +02:00
8788558cf1
fix: only sync version label once
Some checks failed
continuous-integration/drone/push Build is failing
Closes coop-cloud/organising#492
2023-09-21 10:58:17 +02:00
76035e003e
fix: recipe workflow with integration tests
Some checks failed
continuous-integration/drone/push Build is failing
2023-09-21 10:36:53 +02:00
b708382d26
feat: recipe lint supports --chaos 2023-09-21 09:07:00 +02:00
557b670fc5
docs: improve recipe fetch usage/desc [ci skip] 2023-09-21 08:46:33 +02:00
e116148c49
test: ensure catalogue --chaos works [ci skip]
Closes coop-cloud/organising#462.
2023-09-20 14:19:49 +02:00
d5593b69e0
test: ensure 3 commits behind, ignore output on fail [ci skip] 2023-09-20 14:10:07 +02:00
0be532692d
test: moar integration tests [ci skip]
Some checks failed
continuous-integration/drone/pr Build is failing
2023-09-20 13:51:06 +02:00
7a9224b2b2
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-19 12:38:02 +02:00
e73d1a8359 chore(deps): update module gotest.tools/v3 to v3.5.1
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-09-19 07:02:01 +00:00
f8c49c82c8
fix: skip "abra-integration-test-recipe" also
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-18 14:02:38 +02:00
ab7edd2a62
refactor!: drop "record" & "server new" command
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
These were alpha prototypes and we'll reconsider once other layers of
Abra are more stable.
2023-09-14 16:45:01 +02:00
b1888dcf0f
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-14 09:39:28 +02:00
e5e122296f chore(deps): update module github.com/go-git/go-git/v5 to v5.9.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-09-13 07:01:51 +00:00
83bf148304
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-07 14:34:40 +02:00
d80b882b83 chore(deps): update module github.com/docker/docker to v24.0.6
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2023-09-07 07:02:43 +00:00
c345c6f5f1 chore(deps): update module github.com/docker/cli to v24.0.6
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-09-06 07:01:56 +00:00
f8c4fd72a3
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-05 13:56:34 +02:00
10f612f998
test: more integration tests
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-09-05 13:03:38 +02:00
58e78e4d7c fix: overridable ABRA_DIR
Some checks failed
continuous-integration/drone/push Build is failing
2023-09-05 09:58:13 +00:00
25258d3d64 fix: separate abra/kababra makefile targets 2023-09-05 09:58:13 +00:00
b3bd058962 chore: don't join if nothing to join 2023-09-05 09:58:13 +00:00
b4fd7fd77c fix: clone catalogue on initial run 2023-09-05 09:58:13 +00:00
64cfdae6b7 fix: only load client if creating secrets 2023-09-05 09:58:13 +00:00
0a765794f2 test: write initial automatic integration tests 2023-09-05 09:58:13 +00:00
18dc6e9434 feat: support abra testing mode 2023-09-05 09:58:13 +00:00
4ba4107288 chore(deps): update module golang.org/x/sys to v0.12.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-09-04 07:02:01 +00:00
d9b4f4ef3b
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-26 09:58:46 +02:00
c365dcf96d chore(deps): update module github.com/hetznercloud/hcloud-go to v1.50.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2023-08-25 07:02:00 +00:00
0c6a7cc0b8 chore(deps): update module github.com/hetznercloud/hcloud-go to v1.49.1
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-08-18 07:01:42 +00:00
6640cfab64
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-13 17:42:24 +02:00
71addcd1b2 chore(deps): update module github.com/hetznercloud/hcloud-go to v1.49.0
Some checks failed
continuous-integration/drone/push Build is failing
2023-08-13 15:41:44 +00:00
60c0e55e3d fix: don't specify refs when pulling tags
Some checks failed
continuous-integration/drone/push Build is failing
See coop-cloud/organising#477
2023-08-13 12:07:37 +00:00
e42139fd83 chore(deps): update golang docker tag to v1.21
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-08-09 07:02:07 +00:00
2d826e47d0 chore(deps): update module golang.org/x/sys to v0.11.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-08-07 07:01:55 +00:00
2db172ea5a Further changes to messages.
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-04 19:22:48 +00:00
2077658f6a Attempt to replace the deploy completed message. 2023-08-04 19:22:48 +00:00
502e26b534 Change message when starting to poll for deployment status. 2023-08-04 19:22:48 +00:00
e22b692ada Add os hook for interrupt signal while waiting for service to converge. 2023-08-04 19:22:48 +00:00
5ae73f700e
Merge branch 'fix-deploy-no-catalogue'
Some checks reported errors
continuous-integration/drone/push Build was killed
2023-08-02 10:48:54 +02:00
63d419caae
Merge branch 'fix-478' 2023-08-02 10:48:46 +02:00
179b66d65c
Merge branch 'fix-476' 2023-08-02 10:48:37 +02:00
c9144d90f3
refactor: integration -> manual 2023-08-02 08:45:24 +02:00
ebf5d82c56
fix: failover if no recipe meta available
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2023-08-02 00:48:27 +02:00
8bb98ed0ed
fix: deploy fresh recipe without versions
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
See coop-cloud/organising#476
2023-08-01 21:47:34 +02:00
23f5745cb8
fix: skip recipe clone / up to date sync for some commands
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Continues work of 3dc5662821c5b54e7f7f34e2e0d09b239a145a0b.
2023-08-01 21:19:20 +02:00
2cd453ae8d
build: attempt to ignore goreleaser upgrades
Some checks failed
continuous-integration/drone/push Build is failing
See e42cc0f91d1ac50c21454d64ca585fa23c839a63.
2023-08-01 19:33:36 +02:00
e42cc0f91d
Revert "chore(deps): update goreleaser/goreleaser docker tag to v1.19.2"
This reverts commit 1de45a6508925e6c39bb82561574024af2fdaee8.

See 8fa9419c9920889e38bcc81e2f923c8e29b668f7.
2023-08-01 19:31:51 +02:00
1de45a6508 chore(deps): update goreleaser/goreleaser docker tag to v1.19.2
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2023-07-31 07:02:04 +00:00
55c7aca3c0
chore: publish 0.8.0-rc2-beta
Some checks reported errors
continuous-integration/drone/push Build was killed
2023-07-29 00:31:49 +02:00
8fa9419c99
build: pin to goreleaser v18 [ci skip]
See coop-cloud/organising#474
2023-07-29 00:22:01 +02:00
73ad0a802e
Revert "build: replacements is deprecated"
This reverts commit 473cae0146163e992ae1903ab7cb654c0121a067.

Aiming to freeze on an old version of goreleaser for now.
2023-07-29 00:14:08 +02:00
798fd2336c
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-27 21:48:49 +02:00
70e65d6667 chore(deps): update module github.com/go-git/go-git/v5 to v5.8.1
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2023-07-27 07:06:22 +00:00
efc9602808
chore: welcome comrade rix [ci skip] 2023-07-26 09:59:22 +02:00
1e110f1375
docs: wording [ci skip] 2023-07-26 09:58:30 +02:00
473cae0146
build: replacements is deprecated
Some checks reported errors
continuous-integration/drone/push Build was killed
2023-07-26 09:18:52 +02:00
2da859896a
fix: point to rc1 [ci skip] 2023-07-26 08:53:39 +02:00
ab00578ee1
chore: publish 0.8.0-rc1-beta
Some checks reported errors
continuous-integration/drone/push Build was killed
2023-07-26 08:52:33 +02:00
3dc5662821
fix: improved offline support
All checks were successful
continuous-integration/drone/push Build is passing
Closes coop-cloud/organising#471.
2023-07-26 08:16:07 +02:00
ab64eb2e8d
fix: only use git to update local catalogue
See coop-cloud/organising#321.
2023-07-25 21:13:04 +02:00
4f22228aab
feat: lint for lightweight tags
See coop-cloud/organising#433
2023-07-25 20:38:29 +02:00
a7f1af7476
refactor: drop internal deploy package 2023-07-25 18:03:37 +02:00
949510d4c3 revert: always clone latest recipe changes
Some checks failed
continuous-integration/drone/push Build is failing
This change was about trying to optimise for offline scenarios but
caused a lot of issues for the online case. It needs to be thought
through again.

See coop-cloud/organising#471.

Closes coop-cloud/organising#432.
2023-07-25 15:05:01 +00:00
9f478dac1d
fix: list downgrades/upgrades in correct order
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
Now that we have correct sorting of versions:

  coop-cloud/organising#427

We don't need to reverse sort. Only for showing prompts when the
latest should be the first. Otherwise, logic can follow the sorted
order, the last item in the list is the latest upgrade.

Related:

  coop-cloud/organising#444

Also fix `upgrade` to actually show the latest version
2023-07-25 15:08:32 +02:00
69f38ea445
fix: always show overview, even with -f
coop-cloud/organising#444
2023-07-25 15:08:10 +02:00
0582147874
fix: better error message for missing local tag
Aiming to help the following scenario better:

coop-cloud/organising#444 (comment)
2023-07-25 15:07:29 +02:00
bdeeb75973
fix: upgrade force logic parity with deploy force logic
coop-cloud/organising#444 (comment)
2023-07-25 15:06:50 +02:00
2518e65e3e
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-25 10:22:02 +02:00
8354c92654
Merge remote-tracking branch 'origin/renovate/main-github.com-docker-docker-24.x' 2023-07-25 10:21:16 +02:00
173e81b885 chore(deps): update module github.com/docker/docker to v24.0.5
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2023-07-25 07:05:53 +00:00
d91731518b chore(deps): update module github.com/docker/cli to v24.0.5
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2023-07-25 07:05:47 +00:00
2bfee5058d chore(deps): update module github.com/go-git/go-git/v5 to v5.8.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-07-24 07:03:29 +00:00
a7ce71d6cf Fix formatting.
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-15 08:15:46 +00:00
10f60fee1d Replace deprecated system.TempFileSequential with os.CreateTemp 2023-07-15 08:15:46 +00:00
6025ab443f Update volume list options. 2023-07-15 08:15:46 +00:00
43ecf35449 Change CommonOptions (deprecated) to ClientOptions and remove unneeded parameters. 2023-07-15 08:15:46 +00:00
4d2a1065d2 Replace types.volume with new volume type 2023-07-15 08:15:46 +00:00
0b67500cab Add docker v24 and associated dependencies. 2023-07-15 08:15:46 +00:00
e0c3a06182
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-14 19:47:09 +02:00
a86ba4e97b chore(deps): update module github.com/hetznercloud/hcloud-go to v2
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-07-14 07:03:04 +00:00
b5b3395138 chore(deps): update module github.com/hetznercloud/hcloud-go to v1.48.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-07-13 07:03:02 +00:00
502b78ef5c
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-05 09:50:38 +02:00
3e2b4dae6a chore(deps): update module golang.org/x/sys to v0.10.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-07-05 07:02:41 +00:00
573fe403b3 chore(deps): update module gotest.tools/v3 to v3.5.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-06-30 07:02:40 +00:00
76862e9d66
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-22 16:44:53 +02:00
e8e337a608 chore(deps): update module github.com/hetznercloud/hcloud-go to v1.47.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2023-06-22 07:02:07 +00:00
500389c5f5 chore(deps): update module github.com/hetznercloud/hcloud-go to v1.46.1
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-06-19 07:02:59 +00:00
dea665652c
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-16 18:10:02 +02:00
e8cf84b523 chore(deps): update module golang.org/x/sys to v0.9.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-06-16 07:03:47 +00:00
fab25a6124 chore(deps): update module github.com/hetznercloud/hcloud-go to v1.46.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-06-15 07:03:10 +00:00
e71377539c chore(deps): update module github.com/alecaivazis/survey/v2 to v2.3.7
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-06-14 07:02:51 +00:00
497ecf476a
docs: wording [ci skip] 2023-06-12 00:09:52 +02:00
ff1c043ec5
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-07 10:45:17 +02:00
c4d2e297f8
Merge remote-tracking branch 'origin/renovate/main-coopcloud.tech-libcapsul-digest' 2023-06-07 10:44:35 +02:00
e98b8e3666 chore(deps): update module github.com/hashicorp/go-retryablehttp to v0.7.4
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2023-06-07 07:02:31 +00:00
f5835fe404 chore(deps): update coopcloud.tech/libcapsul digest to 878af47
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-06-07 07:02:13 +00:00
07bbe9394f chore(deps): update module github.com/sirupsen/logrus to v1.9.3
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-06-05 07:02:38 +00:00
6974681af5 fix: improve error message
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-29 14:57:41 +02:00
73250fb899 chore(deps): update module github.com/go-git/go-git/v5 to v5.7.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-05-24 07:02:28 +00:00
4ce377cffe chore(deps): update module github.com/sirupsen/logrus to v1.9.2
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-05-19 07:03:02 +00:00
c7dd029689 chore(deps): update module github.com/docker/cli to v20.10.25
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-05-18 07:02:30 +00:00
51319d2ae2 chore(deps): update module github.com/docker/docker to v20.10.25
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-05-16 07:03:23 +00:00
d1c2343a54 chore(deps): update module github.com/hetznercloud/hcloud-go to v1.45.1
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-05-15 07:03:03 +00:00
135ffde0e5 chore(deps): update module github.com/docker/distribution to v2.8.2
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-05-12 07:03:02 +00:00
6e4dd51b27
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-08 11:42:50 +02:00
81b652718b chore(deps): update module github.com/hetznercloud/hcloud-go to v1.44.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-05-08 07:02:38 +00:00
442f46e17f
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-05 10:59:22 +02:00
574794d4e8
Merge remote-tracking branch 'origin/renovate/main-golang.org-x-sys-0.x' 2023-05-05 10:58:25 +02:00
88184125c4 chore(deps): update module golang.org/x/sys to v0.8.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-05-05 07:03:15 +00:00
8a4baa66ee chore(deps): update module github.com/klauspost/pgzip to v1.2.6
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-05-05 07:03:01 +00:00
16ecbd0291 chore(deps): update module github.com/moby/term to v0.5.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-05-03 07:02:46 +00:00
f65b262c11 chore(deps): update module github.com/hetznercloud/hcloud-go to v1.43.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-04-28 07:03:28 +00:00
c5d9d88359 Add some minor tweaks to machine readable pathway in recipe upgrade
Some checks failed
continuous-integration/drone/push Build is failing
2023-04-27 16:45:57 +00:00
87e5909363 Make -m imply -n in recipe/upgrade 2023-04-27 16:45:57 +00:00
152c5d4563 Add machine output for recipe/upgrade
- Normal faff related to calling external libraries with structs thnx go
- Ouputs json now
2023-04-27 16:45:57 +00:00
34b274bc52 recipe/upgrade: Refactor upgradability list to make output easier
For future, we can print the struct as JSON.
2023-04-27 16:45:57 +00:00
62f8103fc2 recipe/upgrade: Add non-interactive mode.
Add support for -n which just outputs the list of compatible tags for each image.
2023-04-27 16:45:57 +00:00
2dcbfa1d65 chore(deps): update module github.com/coreos/go-semver to v0.3.1
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-04-26 07:02:52 +00:00
049da94629 fix(version): semver version ordering (!293)
All checks were successful
continuous-integration/drone/push Build is passing
Solves coop-cloud/organising#427

This fix sorts the recipe versions at the catalogue generation and the versions that are received from the catalogue.

Co-authored-by: Moritz <moritz.m@local-it.org>
Reviewed-on: coop-cloud/abra#293
2023-04-26 06:38:15 +00:00
b2739dcdf2 fix(deploy) post deploy cmds
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-18 19:05:46 +02:00
343b2bfb91
docs: go doc badge [ci skip] 2023-04-14 23:31:21 +02:00
17aeed6dbd
chore: go mod tidy [ci skip] 2023-04-14 19:09:53 +02:00
27cac81830 fix(app): fix app list chaos field
All checks were successful
continuous-integration/drone/push Build is passing
show only the chaos version if the app is a chaos deploy
2023-04-14 18:01:08 +02:00
31ec322c55 feat(deploy): set timeout via label (!290)
All checks were successful
continuous-integration/drone/push Build is passing
Solves coop-cloud/organising#437

A timeout can be specified globally for a recipe using this label:
`coop-cloud.${STACK_NAME}.timeout=${TIMEOUT:-120}`. This sets the default timeout to 120s. An app specific timeout can be set using the env `TIMEOUT`.

Co-authored-by: Moritz <moritz.m@local-it.org>
Reviewed-on: coop-cloud/abra#290
2023-04-14 14:44:18 +00:00
18615eaaef Post-deploy abra.sh hooks (!292)
All checks were successful
continuous-integration/drone/push Build is passing
This solves coop-cloud/organising#235

Co-authored-by: Moritz <moritz.m@local-it.org>
Reviewed-on: coop-cloud/abra#292
2023-04-14 14:12:31 +00:00
5e508538f3 chore(deps): update module github.com/hetznercloud/hcloud-go to v1.42.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-04-13 07:02:01 +00:00
9e05000476 fix(kadabra): always pull new recipe version
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-06 17:22:33 +02:00
f088a0d327
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-06 10:00:14 +02:00
3832403c97 chore(deps): update module github.com/docker/docker to v20.10.24
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2023-04-06 07:01:59 +00:00
47058c897c chore(deps): update module github.com/docker/cli to v20.10.24
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-04-05 07:02:26 +00:00
5d4c7f8ef0 chore(deps): update module golang.org/x/sys to v0.7.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-04-04 16:33:38 +00:00
ee4315adb3 fix(rm): remove volumes correctly during app removal
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-30 13:40:44 +02:00
9ade250f01 feat(cmd): add --tty flag to run commands from a script
Some checks failed
continuous-integration/drone/push Build is failing
2023-03-29 14:25:08 +02:00
81b032be85
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-21 09:19:02 +01:00
5409990a68 chore(deps): update module github.com/go-git/go-git/v5 to v5.6.1
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-03-17 08:03:41 +00:00
b1595c0ec9 chore(deps): update module github.com/schollz/progressbar/v3 to v3.13.1
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-03-15 08:02:29 +00:00
6c99a2980b
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-07 16:35:20 +01:00
a9405a36c6
Merge remote-tracking branch 'origin/renovate/main-github.com-hetznercloud-hcloud-go-1.x' 2023-03-07 16:34:23 +01:00
15a417d9bd fix(list): fix output of chaos + chaos-version merge
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-07 13:46:19 +01:00
0ce8b3a5c2 Merge pull request 'app ls --status shows more detailles about the deployment state' (!280) from detailed_app_list into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: coop-cloud/abra#280
2023-03-07 12:31:38 +00:00
edff63b446 Revert "review: change label autoupdate -> auto-update"
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
This reverts commit 74baa76f5ee5e5dd7b71b1f14be97cc40dfc611b.
2023-03-07 13:24:46 +01:00
d5979436c1 review: merge chaos + chaos_version column 2023-03-07 13:24:46 +01:00
cb33edaac3 review: change label autoupdate -> auto-update 2023-03-07 13:24:46 +01:00
e9879e2226 review: label convention chaos_version -> chaos-version 2023-03-07 13:24:46 +01:00
5428ebf43b review: avoid stackName recalculation 2023-03-07 13:24:46 +01:00
d120299929 feat(list): show autoupdate state 2023-03-07 13:24:46 +01:00
3753357ef8 feat(list): show chaos status and chaos version 2023-03-07 13:24:46 +01:00
611430aab2 Set chaos version label for each deployed or upgraded app 2023-03-07 13:24:46 +01:00
f56b02b951 chore(deps): update module github.com/hetznercloud/hcloud-go to v1.41.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2023-03-07 08:02:02 +00:00
f29278f80a
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-06 15:04:15 +01:00
a9a294cbb7 chore(deps): update module golang.org/x/sys to v0.6.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-03-06 08:02:17 +00:00
73004789a4
chore: go mod tidy
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-04 08:50:24 +01:00
440aba77d5 chore(deps): update module github.com/go-git/go-git/v5 to v5.6.0
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-03-01 08:02:11 +00:00
e4a89bcc4f fix(kadabra): only warn if a deployed app has no published release
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-28 15:34:27 +01:00
eb07617e73
chore: publish new release 0.7.0-beta
Some checks reported errors
continuous-integration/drone/push Build was killed
2023-02-22 08:47:43 +01:00
9fca4e56fb
docs: add comrade vera [ci skip] 2023-02-19 11:00:43 +01:00
3988 changed files with 1040727 additions and 8869 deletions

View File

@ -1,4 +1,8 @@
Dockerfile
.dockerignore
*.swp
*.swo
*.swp
.dockerignore
Dockerfile
abra
dist
kadabra
tags

View File

@ -3,20 +3,17 @@ kind: pipeline
name: coopcloud.tech/abra
steps:
- name: make check
image: golang:1.20
image: golang:1.24
commands:
- make check
- name: make build
image: golang:1.20
commands:
- make build
depends_on:
- make check
- name: make test
image: golang:1.20
image: golang:1.24
environment:
CATL_URL: https://git.coopcloud.tech/toolshed/recipes-catalogue-json.git
commands:
- mkdir -p $HOME/.abra
- git clone $CATL_URL $HOME/.abra/catalogue
- make test
depends_on:
- make check
@ -27,13 +24,12 @@ steps:
- git fetch --tags
depends_on:
- make check
- make build
- make test
when:
event: tag
- name: release
image: golang:1.20
image: goreleaser/goreleaser:v2.5.1
environment:
GITEA_TOKEN:
from_secret: goreleaser_gitea_token
@ -41,7 +37,7 @@ steps:
- name: deps
path: /go
commands:
- curl -sL https://git.io/goreleaser | bash
- goreleaser release
depends_on:
- fetch
when:
@ -51,19 +47,72 @@ steps:
image: plugins/docker
settings:
auto_tag: true
username: 3wordchant
username: abra-bot
password:
from_secret: git_coopcloud_tech_token_3wc
repo: git.coopcloud.tech/coop-cloud/abra
from_secret: git_coopcloud_tech_token_abra_bot
repo: git.coopcloud.tech/toolshed/abra
tags: dev
registry: git.coopcloud.tech
when:
event:
exclude:
- pull_request
branch:
- main
depends_on:
- make check
- make test
- name: on-demand integration test
image: appleboy/drone-ssh
settings:
host:
- int.coopcloud.tech
username: abra
key:
from_secret: abra_int_private_key
port: 22
command_timeout: 60m
script_stop: true
request_pty: true
script:
- |
wget https://git.coopcloud.tech/toolshed/abra/raw/branch/main/scripts/tests/run-ci-int -O run-ci-int
chmod +x run-ci-int
sh run-ci-int
when:
ref:
- refs/heads/int-*
depends_on:
- make check
- make test
- name: nightly integration test
image: appleboy/drone-ssh
settings:
host:
- int.coopcloud.tech
username: abra
key:
from_secret: abra_int_private_key
port: 22
command_timeout: 60m
script_stop: true
request_pty: true
script:
- |
wget https://git.coopcloud.tech/toolshed/abra/raw/branch/main/scripts/tests/run-ci-int -O run-ci-int
chmod +x run-ci-int
sh run-ci-int
when:
event:
- cron:
cron:
# @daily https://docs.drone.io/cron/
- integration
volumes:
- name: deps
temp: {}
trigger:
action:
exclude:
- synchronized

View File

@ -1,4 +0,0 @@
GANDI_TOKEN=...
HCLOUD_TOKEN=...
REGISTRY_PASSWORD=...
REGISTRY_USERNAME=...

View File

@ -1,6 +1,7 @@
go env -w GOPRIVATE=coopcloud.tech
# integration test suite
# export ABRA_DIR="$HOME/.abra_test"
# export ABRA_TEST_DOMAIN=test.example.com
# export ABRA_CI=1
# export PASSWORD_STORE_DIR=$(pwd)/../../autonomic/passwords/passwords/
# export HCLOUD_TOKEN=$(pass show logins/hetzner/cicd/api_key)
# export CAPSUL_TOKEN=...
# export GITEA_TOKEN=...
# release automation
# export GITEA_TOKEN=

View File

@ -1,8 +0,0 @@
---
name: "Do not use this issue tracker"
about: "Do not use this issue tracker"
title: "Do not use this issue tracker"
labels: []
---
Please report your issue on [`coop-cloud/organising`](https://git.coopcloud.tech/coop-cloud/organising)

5
.gitignore vendored
View File

@ -2,8 +2,7 @@
.e2e.env
.envrc
.vscode/
/abra
/kadabra
abra
dist/
tests/integration/.abra/catalogue
vendor/
tests/integration/.bats

View File

@ -29,6 +29,8 @@ builds:
ldflags:
- "-X 'main.Commit={{ .Commit }}'"
- "-X 'main.Version={{ .Version }}'"
- "-s"
- "-w"
- id: kadabra
binary: kadabra
@ -47,15 +49,13 @@ builds:
- 5
- 6
- 7
gcflags:
- "all=-l -B"
ldflags:
- "-X 'main.Commit={{ .Commit }}'"
- "-X 'main.Version={{ .Version }}'"
archives:
- replacements:
386: i386
amd64: x86_64
format: binary
- "-s"
- "-w"
checksum:
name_template: "checksums.txt"

View File

@ -1,15 +1,22 @@
# authors
> If you're looking at this and you hack on `abra` and you're not listed here,
> please do add yourself! This is a community project, let's show some :heart:
> please do add yourself! This is a community project, let's show some 💞
- 3wordchant
- ammaratef45
- cassowary
- codegod100
- decentral1se
- fauno
- frando
- kawaiipunk
- knoflook
- moritz
- p4u1
- rix
- roxxers
- vera
- yksflip
- basebuilder
- mayel

View File

@ -1,8 +1,13 @@
FROM golang:1.20-alpine AS build
# Build image
FROM golang:1.24-alpine AS build
ENV GOPRIVATE coopcloud.tech
ENV GOPRIVATE=coopcloud.tech
RUN apk add --no-cache make git gcc musl-dev
RUN apk add --no-cache \
gcc \
git \
make \
musl-dev
COPY . /app
@ -10,7 +15,15 @@ WORKDIR /app
RUN CGO_ENABLED=0 make build
FROM scratch
# Release image ("slim")
FROM alpine:3.19.1
RUN apk add --no-cache \
ca-certificates \
git \
openssh
RUN update-ca-certificates
COPY --from=build /app/abra /abra

View File

@ -2,35 +2,53 @@ ABRA := ./cmd/abra
KADABRA := ./cmd/kadabra
COMMIT := $(shell git rev-list -1 HEAD)
GOPATH := $(shell go env GOPATH)
GOVERSION := 1.24
LDFLAGS := "-X 'main.Commit=$(COMMIT)'"
DIST_LDFLAGS := $(LDFLAGS)" -s -w"
GCFLAGS := "all=-l -B"
export GOPRIVATE=coopcloud.tech
all: format check build test
# NOTE(d1): default `make` optimised for Abra hacking
all: format check build-abra test
run:
@go run -ldflags=$(LDFLAGS) $(ABRA)
run-abra:
@go run -gcflags=$(GCFLAGS) -ldflags=$(LDFLAGS) $(ABRA)
install:
@go install -ldflags=$(LDFLAGS) $(ABRA)
run-kadabra:
@go run -gcflags=$(GCFLAGS) -ldflags=$(LDFLAGS) $(KADABRA)
build-dev:
@go build -v -ldflags=$(LDFLAGS) $(ABRA)
install-abra:
@go install -gcflags=$(GCFLAGS) -ldflags=$(LDFLAGS) $(ABRA)
build:
@go build -v -ldflags=$(DIST_LDFLAGS) $(ABRA)
@go build -v -ldflags=$(DIST_LDFLAGS) $(KADABRA)
install-kadabra:
@go install -gcflags=$(GCFLAGS) -ldflags=$(LDFLAGS) $(KADABRA)
install: install-abra install-kadabra
build-abra:
@go build -v -gcflags=$(GCFLAGS) -ldflags=$(DIST_LDFLAGS) $(ABRA)
build-kadabra:
@go build -v -gcflags=$(GCFLAGS) -ldflags=$(DIST_LDFLAGS) $(KADABRA)
build: build-abra build-kadabra
build-docker-abra:
@docker run -it -v $(PWD):/abra golang:$(GOVERSION) \
bash -c 'cd /abra; ./scripts/docker/build.sh'
build-docker: build-docker-abra
clean:
@rm '$(GOPATH)/bin/abra'
@rm '$(GOPATH)/bin/kadabra'
format:
@gofmt -s -w .
@gofmt -s -w $$(find . -type f -name '*.go' | grep -v "/vendor/")
check:
@test -z $$(gofmt -l .) || \
@test -z $$(gofmt -l $$(find . -type f -name '*.go' | grep -v "/vendor/")) || \
(echo "gofmt: formatting issue - run 'make format' to resolve" && exit 1)
test:
@ -39,9 +57,5 @@ test:
loc:
@find . -name "*.go" | xargs wc -l
loc-author:
@git ls-files -z | \
xargs -0rn 1 -P "$$(nproc)" -I{} sh -c 'git blame -w -M -C -C --line-porcelain -- {} | grep -I --line-buffered "^author "' | \
sort -f | \
uniq -ic | \
sort -n
deps:
@go get -t -u ./...

View File

@ -1,12 +1,13 @@
# `abra`
[![Build Status](https://build.coopcloud.tech/api/badges/coop-cloud/abra/status.svg?ref=refs/heads/main)](https://build.coopcloud.tech/coop-cloud/abra)
[![Go Report Card](https://goreportcard.com/badge/git.coopcloud.tech/coop-cloud/abra)](https://goreportcard.com/report/git.coopcloud.tech/coop-cloud/abra)
[![Build Status](https://build.coopcloud.tech/api/badges/toolshed/abra/status.svg?ref=refs/heads/main)](https://build.coopcloud.tech/toolshed/abra)
[![Go Report Card](https://goreportcard.com/badge/git.coopcloud.tech/toolshed/abra)](https://goreportcard.com/report/git.coopcloud.tech/toolshed/abra)
[![Go Reference](https://pkg.go.dev/badge/coopcloud.tech/abra.svg)](https://pkg.go.dev/coopcloud.tech/abra)
The Co-op Cloud utility belt 🎩🐇
<a href="https://github.com/egonelbre/gophers"><img align="right" width="150" src="https://github.com/egonelbre/gophers/raw/master/.thumb/sketch/adventure/poking-fire.png"/></a>
`abra` is the flagship client & command-line tool for Co-op Cloud. It has been developed specifically for the purpose of making the day-to-day operations of [operators](https://docs.coopcloud.tech/operators/) and [maintainers](https://docs.coopcloud.tech/maintainers/) pleasant & convenient. It is libre software, written in [Go](https://go.dev) and maintained and extended by the community :heart:
`abra` is the flagship client & command-line tool for Co-op Cloud. It has been developed specifically for the purpose of making the day-to-day operations of [operators](https://docs.coopcloud.tech/operators/) and [maintainers](https://docs.coopcloud.tech/maintainers/) pleasant & convenient. It is libre software, written in [Go](https://go.dev) and maintained and extended by the community 💖
Please see [docs.coopcloud.tech/abra](https://docs.coopcloud.tech/abra) for help on install, upgrade, hacking, troubleshooting & more!

View File

@ -1,37 +1,11 @@
package app
import (
"github.com/urfave/cli"
"github.com/spf13/cobra"
)
var AppCommand = cli.Command{
Name: "app",
Aliases: []string{"a"},
Usage: "Manage apps",
ArgsUsage: "<domain>",
Description: "Functionality for managing the life cycle of your apps",
Subcommands: []cli.Command{
appBackupCommand,
appCheckCommand,
appCmdCommand,
appConfigCommand,
appCpCommand,
appDeployCommand,
appErrorsCommand,
appListCommand,
appLogsCommand,
appNewCommand,
appPsCommand,
appRemoveCommand,
appRestartCommand,
appRestoreCommand,
appRollbackCommand,
appRunCommand,
appSecretCommand,
appServicesCommand,
appUndeployCommand,
appUpgradeCommand,
appVersionCommand,
appVolumeCommand,
},
var AppCommand = &cobra.Command{
Use: "app [cmd] [args] [flags]",
Aliases: []string{"a"},
Short: "Manage apps",
}

View File

@ -1,399 +1,307 @@
package app
import (
"archive/tar"
"context"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"time"
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/config"
containerPkg "coopcloud.tech/abra/pkg/container"
"coopcloud.tech/abra/pkg/recipe"
"coopcloud.tech/abra/pkg/runtime"
"coopcloud.tech/abra/pkg/upstream/container"
"github.com/docker/cli/cli/command"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
dockerClient "github.com/docker/docker/client"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/system"
"github.com/klauspost/pgzip"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
"coopcloud.tech/abra/pkg/log"
"github.com/spf13/cobra"
)
type backupConfig struct {
preHookCmd string
postHookCmd string
backupPaths []string
}
var appBackupCommand = cli.Command{
Name: "backup",
Aliases: []string{"bk"},
Usage: "Run app backup",
ArgsUsage: "<domain> [<service>]",
Flags: []cli.Flag{
internal.DebugFlag,
var AppBackupListCommand = &cobra.Command{
Use: "list <domain> [flags]",
Aliases: []string{"ls"},
Short: "List the contents of a snapshot",
Args: cobra.ExactArgs(1),
ValidArgsFunction: func(
cmd *cobra.Command,
args []string,
toComplete string) ([]string, cobra.ShellCompDirective) {
return autocomplete.AppNameComplete()
},
Before: internal.SubCommandBefore,
BashComplete: autocomplete.AppNameComplete,
Description: `
Run an app backup.
A backup command and pre/post hook commands are defined in the recipe
configuration. Abra reads this configuration and run the comands in the context
of the deployed services. Pass <service> if you only want to back up a single
service. All backups are placed in the ~/.abra/backups directory.
A single backup file is produced for all backup paths specified for a service.
If we have the following backup configuration:
- "backupbot.backup.path=/var/lib/foo,/var/lib/bar"
And we run "abra app backup example.com app", Abra will produce a file that
looks like:
~/.abra/backups/example_com_app_609341138.tar.gz
This file is a compressed archive which contains all backup paths. To see paths, run:
tar -tf ~/.abra/backups/example_com_app_609341138.tar.gz
(Make sure to change the name of the backup file)
This single file can be used to restore your app. See "abra app restore" for more.
`,
Action: func(c *cli.Context) error {
app := internal.ValidateApp(c)
conf := runtime.New()
Run: func(cmd *cobra.Command, args []string) {
app := internal.ValidateApp(args)
cl, err := client.New(app.Server)
if err != nil {
logrus.Fatal(err)
log.Fatal(err)
}
recipe, err := recipe.Get(app.Recipe, conf)
targetContainer, err := internal.RetrieveBackupBotContainer(cl)
if err != nil {
logrus.Fatal(err)
log.Fatal(err)
}
backupConfigs := make(map[string]backupConfig)
for _, service := range recipe.Config.Services {
if backupsEnabled, ok := service.Deploy.Labels["backupbot.backup"]; ok {
if backupsEnabled == "true" {
fullServiceName := fmt.Sprintf("%s_%s", app.StackName(), service.Name)
bkConfig := backupConfig{}
logrus.Debugf("backup config detected for %s", fullServiceName)
if paths, ok := service.Deploy.Labels["backupbot.backup.path"]; ok {
logrus.Debugf("detected backup paths for %s: %s", fullServiceName, paths)
bkConfig.backupPaths = strings.Split(paths, ",")
}
if preHookCmd, ok := service.Deploy.Labels["backupbot.backup.pre-hook"]; ok {
logrus.Debugf("detected pre-hook command for %s: %s", fullServiceName, preHookCmd)
bkConfig.preHookCmd = preHookCmd
}
if postHookCmd, ok := service.Deploy.Labels["backupbot.backup.post-hook"]; ok {
logrus.Debugf("detected post-hook command for %s: %s", fullServiceName, postHookCmd)
bkConfig.postHookCmd = postHookCmd
}
backupConfigs[service.Name] = bkConfig
}
}
execEnv := []string{
fmt.Sprintf("SERVICE=%s", app.Domain),
"MACHINE_LOGS=true",
}
serviceName := c.Args().Get(1)
if serviceName != "" {
backupConfig, ok := backupConfigs[serviceName]
if !ok {
logrus.Fatalf("no backup config for %s? does %s exist?", serviceName, serviceName)
}
logrus.Infof("running backup for the %s service", serviceName)
if err := runBackup(cl, app, serviceName, backupConfig); err != nil {
logrus.Fatal(err)
}
} else {
if len(backupConfigs) == 0 {
logrus.Fatalf("no backup configs discovered for %s?", app.Name)
}
for serviceName, backupConfig := range backupConfigs {
logrus.Infof("running backup for the %s service", serviceName)
if err := runBackup(cl, app, serviceName, backupConfig); err != nil {
logrus.Fatal(err)
}
}
if snapshot != "" {
log.Debugf("including SNAPSHOT=%s in backupbot exec invocation", snapshot)
execEnv = append(execEnv, fmt.Sprintf("SNAPSHOT=%s", snapshot))
}
return nil
if showAllPaths {
log.Debugf("including SHOW_ALL=%v in backupbot exec invocation", showAllPaths)
execEnv = append(execEnv, fmt.Sprintf("SHOW_ALL=%v", showAllPaths))
}
if timestamps {
log.Debugf("including TIMESTAMPS=%v in backupbot exec invocation", timestamps)
execEnv = append(execEnv, fmt.Sprintf("TIMESTAMPS=%v", timestamps))
}
if _, err = internal.RunBackupCmdRemote(cl, "ls", targetContainer.ID, execEnv); err != nil {
log.Fatal(err)
}
},
}
// TimeStamp generates a file name friendly timestamp.
func TimeStamp() string {
ts := time.Now().UTC().Format(time.RFC3339)
return strings.Replace(ts, ":", "-", -1)
}
var AppBackupDownloadCommand = &cobra.Command{
Use: "download <domain> [flags]",
Aliases: []string{"d"},
Short: "Download a snapshot",
Long: `Downloads a backup.tar.gz to the current working directory.
// runBackup does the actual backup logic.
func runBackup(cl *dockerClient.Client, app config.App, serviceName string, bkConfig backupConfig) error {
if len(bkConfig.backupPaths) == 0 {
return fmt.Errorf("backup paths are empty for %s?", serviceName)
}
"--volumes/-v" includes data contained in volumes alongide paths specified in
"backupbot.backup.path" labels.`,
Args: cobra.ExactArgs(1),
ValidArgsFunction: func(
cmd *cobra.Command,
args []string,
toComplete string) ([]string, cobra.ShellCompDirective) {
return autocomplete.AppNameComplete()
},
Run: func(cmd *cobra.Command, args []string) {
app := internal.ValidateApp(args)
// FIXME: avoid instantiating a new CLI
dcli, err := command.NewDockerCli()
if err != nil {
return err
}
filters := filters.NewArgs()
filters.Add("name", fmt.Sprintf("^%s_%s", app.StackName(), serviceName))
targetContainer, err := containerPkg.GetContainer(context.Background(), cl, filters, true)
if err != nil {
return err
}
fullServiceName := fmt.Sprintf("%s_%s", app.StackName(), serviceName)
if bkConfig.preHookCmd != "" {
splitCmd := internal.SafeSplit(bkConfig.preHookCmd)
logrus.Debugf("split pre-hook command for %s into %s", fullServiceName, splitCmd)
preHookExecOpts := types.ExecConfig{
AttachStderr: true,
AttachStdin: true,
AttachStdout: true,
Cmd: splitCmd,
Detach: false,
Tty: true,
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
log.Fatal(err)
}
if err := container.RunExec(dcli, cl, targetContainer.ID, &preHookExecOpts); err != nil {
return fmt.Errorf("failed to run %s on %s: %s", bkConfig.preHookCmd, targetContainer.ID, err.Error())
}
logrus.Infof("succesfully ran %s pre-hook command: %s", fullServiceName, bkConfig.preHookCmd)
}
var tempBackupPaths []string
for _, remoteBackupPath := range bkConfig.backupPaths {
sanitisedPath := strings.ReplaceAll(remoteBackupPath, "/", "_")
localBackupPath := filepath.Join(config.BACKUP_DIR, fmt.Sprintf("%s%s_%s.tar.gz", fullServiceName, sanitisedPath, TimeStamp()))
logrus.Debugf("temporarily backing up %s:%s to %s", fullServiceName, remoteBackupPath, localBackupPath)
logrus.Infof("backing up %s:%s", fullServiceName, remoteBackupPath)
content, _, err := cl.CopyFromContainer(context.Background(), targetContainer.ID, remoteBackupPath)
cl, err := client.New(app.Server)
if err != nil {
logrus.Debugf("failed to copy %s from container: %s", remoteBackupPath, err.Error())
if err := cleanupTempArchives(tempBackupPaths); err != nil {
return fmt.Errorf("failed to clean up temporary archives: %s", err.Error())
}
return fmt.Errorf("failed to copy %s from container: %s", remoteBackupPath, err.Error())
}
defer content.Close()
_, srcBase := archive.SplitPathDirEntry(remoteBackupPath)
preArchive := archive.RebaseArchiveEntries(content, srcBase, remoteBackupPath)
if err := copyToFile(localBackupPath, preArchive); err != nil {
logrus.Debugf("failed to create tar archive (%s): %s", localBackupPath, err.Error())
if err := cleanupTempArchives(tempBackupPaths); err != nil {
return fmt.Errorf("failed to clean up temporary archives: %s", err.Error())
}
return fmt.Errorf("failed to create tar archive (%s): %s", localBackupPath, err.Error())
log.Fatal(err)
}
tempBackupPaths = append(tempBackupPaths, localBackupPath)
}
logrus.Infof("compressing and merging archives...")
if err := mergeArchives(tempBackupPaths, fullServiceName); err != nil {
logrus.Debugf("failed to merge archive files: %s", err.Error())
if err := cleanupTempArchives(tempBackupPaths); err != nil {
return fmt.Errorf("failed to clean up temporary archives: %s", err.Error())
}
return fmt.Errorf("failed to merge archive files: %s", err.Error())
}
if err := cleanupTempArchives(tempBackupPaths); err != nil {
return fmt.Errorf("failed to clean up temporary archives: %s", err.Error())
}
if bkConfig.postHookCmd != "" {
splitCmd := internal.SafeSplit(bkConfig.postHookCmd)
logrus.Debugf("split post-hook command for %s into %s", fullServiceName, splitCmd)
postHookExecOpts := types.ExecConfig{
AttachStderr: true,
AttachStdin: true,
AttachStdout: true,
Cmd: splitCmd,
Detach: false,
Tty: true,
targetContainer, err := internal.RetrieveBackupBotContainer(cl)
if err != nil {
log.Fatal(err)
}
if err := container.RunExec(dcli, cl, targetContainer.ID, &postHookExecOpts); err != nil {
return err
execEnv := []string{
fmt.Sprintf("SERVICE=%s", app.Domain),
"MACHINE_LOGS=true",
}
logrus.Infof("succesfully ran %s post-hook command: %s", fullServiceName, bkConfig.postHookCmd)
}
if snapshot != "" {
log.Debugf("including SNAPSHOT=%s in backupbot exec invocation", snapshot)
execEnv = append(execEnv, fmt.Sprintf("SNAPSHOT=%s", snapshot))
}
return nil
if includePath != "" {
log.Debugf("including INCLUDE_PATH=%s in backupbot exec invocation", includePath)
execEnv = append(execEnv, fmt.Sprintf("INCLUDE_PATH=%s", includePath))
}
if includeSecrets {
log.Debugf("including SECRETS=%v in backupbot exec invocation", includeSecrets)
execEnv = append(execEnv, fmt.Sprintf("SECRETS=%v", includeSecrets))
}
if includeVolumes {
log.Debugf("including VOLUMES=%v in backupbot exec invocation", includeVolumes)
execEnv = append(execEnv, fmt.Sprintf("VOLUMES=%v", includeVolumes))
}
if _, err := internal.RunBackupCmdRemote(cl, "download", targetContainer.ID, execEnv); err != nil {
log.Fatal(err)
}
remoteBackupDir := "/tmp/backup.tar.gz"
currentWorkingDir := "."
if err = CopyFromContainer(cl, targetContainer.ID, remoteBackupDir, currentWorkingDir); err != nil {
log.Fatal(err)
}
},
}
func copyToFile(outfile string, r io.Reader) error {
tmpFile, err := system.TempFileSequential(filepath.Dir(outfile), ".tar_temp")
if err != nil {
return err
}
var AppBackupCreateCommand = &cobra.Command{
Use: "create <domain> [flags]",
Aliases: []string{"c"},
Short: "Create a new snapshot",
Args: cobra.ExactArgs(1),
ValidArgsFunction: func(
cmd *cobra.Command,
args []string,
toComplete string) ([]string, cobra.ShellCompDirective) {
return autocomplete.AppNameComplete()
},
Run: func(cmd *cobra.Command, args []string) {
app := internal.ValidateApp(args)
tmpPath := tmpFile.Name()
_, err = io.Copy(tmpFile, r)
tmpFile.Close()
if err != nil {
os.Remove(tmpPath)
return err
}
if err = os.Rename(tmpPath, outfile); err != nil {
os.Remove(tmpPath)
return err
}
return nil
}
func cleanupTempArchives(tarPaths []string) error {
for _, tarPath := range tarPaths {
if err := os.RemoveAll(tarPath); err != nil {
return err
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
log.Fatal(err)
}
logrus.Debugf("remove temporary archive file %s", tarPath)
}
cl, err := client.New(app.Server)
if err != nil {
log.Fatal(err)
}
return nil
targetContainer, err := internal.RetrieveBackupBotContainer(cl)
if err != nil {
log.Fatal(err)
}
execEnv := []string{
fmt.Sprintf("SERVICE=%s", app.Domain),
"MACHINE_LOGS=true",
}
if retries != "" {
log.Debugf("including RETRIES=%s in backupbot exec invocation", retries)
execEnv = append(execEnv, fmt.Sprintf("RETRIES=%s", retries))
}
if _, err := internal.RunBackupCmdRemote(cl, "create", targetContainer.ID, execEnv); err != nil {
log.Fatal(err)
}
},
}
func mergeArchives(tarPaths []string, serviceName string) error {
var out io.Writer
var cout *pgzip.Writer
var AppBackupSnapshotsCommand = &cobra.Command{
Use: "snapshots <domain> [flags]",
Aliases: []string{"s"},
Short: "List all snapshots",
Args: cobra.ExactArgs(1),
ValidArgsFunction: func(
cmd *cobra.Command,
args []string,
toComplete string) ([]string, cobra.ShellCompDirective) {
return autocomplete.AppNameComplete()
},
Run: func(cmd *cobra.Command, args []string) {
app := internal.ValidateApp(args)
localBackupPath := filepath.Join(config.BACKUP_DIR, fmt.Sprintf("%s_%s.tar.gz", serviceName, TimeStamp()))
fout, err := os.Create(localBackupPath)
if err != nil {
return fmt.Errorf("Failed to open %s: %s", localBackupPath, err)
}
defer fout.Close()
out = fout
cout = pgzip.NewWriter(out)
out = cout
tw := tar.NewWriter(out)
for _, tarPath := range tarPaths {
if err := addTar(tw, tarPath); err != nil {
return fmt.Errorf("failed to merge %s: %v", tarPath, err)
cl, err := client.New(app.Server)
if err != nil {
log.Fatal(err)
}
}
if err := tw.Close(); err != nil {
return fmt.Errorf("failed to close tar writer %v", err)
}
if cout != nil {
if err := cout.Flush(); err != nil {
return fmt.Errorf("failed to flush: %s", err)
} else if err = cout.Close(); err != nil {
return fmt.Errorf("failed to close compressed writer: %s", err)
targetContainer, err := internal.RetrieveBackupBotContainer(cl)
if err != nil {
log.Fatal(err)
}
}
logrus.Infof("backed up %s to %s", serviceName, localBackupPath)
execEnv := []string{
fmt.Sprintf("SERVICE=%s", app.Domain),
"MACHINE_LOGS=true",
}
return nil
if _, err = internal.RunBackupCmdRemote(cl, "snapshots", targetContainer.ID, execEnv); err != nil {
log.Fatal(err)
}
},
}
func addTar(tw *tar.Writer, pth string) (err error) {
var tr *tar.Reader
var rc io.ReadCloser
var hdr *tar.Header
if tr, rc, err = openTarFile(pth); err != nil {
return
}
for {
if hdr, err = tr.Next(); err != nil {
if err == io.EOF {
err = nil
}
break
}
if err = tw.WriteHeader(hdr); err != nil {
break
} else if _, err = io.Copy(tw, tr); err != nil {
break
}
}
if err == nil {
err = rc.Close()
} else {
rc.Close()
}
return
var AppBackupCommand = &cobra.Command{
Use: "backup [cmd] [args] [flags]",
Aliases: []string{"b"},
Short: "Manage app backups",
}
func openTarFile(pth string) (tr *tar.Reader, rc io.ReadCloser, err error) {
var fin *os.File
var n int
buff := make([]byte, 1024)
var (
snapshot string
retries string
includePath string
showAllPaths bool
timestamps bool
includeSecrets bool
includeVolumes bool
)
if fin, err = os.Open(pth); err != nil {
return
}
func init() {
AppBackupListCommand.Flags().StringVarP(
&snapshot,
"snapshot",
"s",
"",
"list specific snapshot",
)
if n, err = fin.Read(buff); err != nil {
fin.Close()
return
} else if n == 0 {
fin.Close()
err = fmt.Errorf("%s is empty", pth)
return
}
AppBackupListCommand.Flags().BoolVarP(
&showAllPaths,
"all",
"a",
false,
"show all paths",
)
if _, err = fin.Seek(0, 0); err != nil {
fin.Close()
return
}
AppBackupListCommand.Flags().BoolVarP(
&timestamps,
"timestamps",
"t",
false,
"include timestamps",
)
rc = fin
tr = tar.NewReader(rc)
AppBackupDownloadCommand.Flags().StringVarP(
&snapshot,
"snapshot",
"s",
"",
"list specific snapshot",
)
return tr, rc, nil
AppBackupDownloadCommand.Flags().StringVarP(
&includePath,
"path",
"p",
"",
"volumes path",
)
AppBackupDownloadCommand.Flags().BoolVarP(
&includeSecrets,
"secrets",
"S",
false,
"include secrets",
)
AppBackupDownloadCommand.Flags().BoolVarP(
&includeVolumes,
"volumes",
"v",
false,
"include volumes",
)
AppBackupDownloadCommand.Flags().BoolVarP(
&internal.Chaos,
"chaos",
"C",
false,
"ignore uncommitted recipes changes",
)
AppBackupCreateCommand.Flags().StringVarP(
&retries,
"retries",
"r",
"1",
"number of retry attempts",
)
AppBackupCreateCommand.Flags().BoolVarP(
&internal.Chaos,
"chaos",
"C",
false,
"ignore uncommitted recipes changes",
)
}

View File

@ -1,57 +1,91 @@
package app
import (
"os"
"path"
"strings"
"fmt"
"coopcloud.tech/abra/cli/internal"
appPkg "coopcloud.tech/abra/pkg/app"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/config"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
"coopcloud.tech/abra/pkg/formatter"
"coopcloud.tech/abra/pkg/log"
"github.com/charmbracelet/lipgloss"
"github.com/spf13/cobra"
)
var appCheckCommand = cli.Command{
Name: "check",
Aliases: []string{"chk"},
Usage: "Check if app is configured correctly",
ArgsUsage: "<domain>",
Flags: []cli.Flag{
internal.DebugFlag,
},
Before: internal.SubCommandBefore,
Action: func(c *cli.Context) error {
app := internal.ValidateApp(c)
var AppCheckCommand = &cobra.Command{
Use: "check <domain> [flags]",
Aliases: []string{"chk"},
Short: "Ensure an app is well configured",
Long: `Compare env vars in both the app ".env" and recipe ".env.sample" file.
envSamplePath := path.Join(config.RECIPES_DIR, app.Recipe, ".env.sample")
if _, err := os.Stat(envSamplePath); err != nil {
if os.IsNotExist(err) {
logrus.Fatalf("%s does not exist?", envSamplePath)
}
logrus.Fatal(err)
The goal is to ensure that recipe ".env.sample" env vars are defined in your
app ".env" file. Only env var definitions in the ".env.sample" which are
uncommented, e.g. "FOO=bar" are checked. If an app ".env" file does not include
these env vars, then "check" will complain.
Recipe maintainers may or may not provide defaults for env vars within their
recipes regardless of commenting or not (e.g. through the use of
${FOO:<default>} syntax). "check" does not confirm or deny this for you.`,
Args: cobra.ExactArgs(1),
ValidArgsFunction: func(
cmd *cobra.Command,
args []string,
toComplete string) ([]string, cobra.ShellCompDirective) {
return autocomplete.AppNameComplete()
},
Run: func(cmd *cobra.Command, args []string) {
app := internal.ValidateApp(args)
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
log.Fatal(err)
}
envSample, err := config.ReadEnv(envSamplePath)
table, err := formatter.CreateTable()
if err != nil {
logrus.Fatal(err)
log.Fatal(err)
}
var missing []string
for k := range envSample {
if _, ok := app.Env[k]; !ok {
missing = append(missing, k)
table.
Headers(
fmt.Sprintf("%s .env.sample", app.Recipe.Name),
fmt.Sprintf("%s.env", app.Name),
).
StyleFunc(func(row, col int) lipgloss.Style {
switch {
case col == 1:
return lipgloss.NewStyle().Padding(0, 1, 0, 1).Align(lipgloss.Center)
default:
return lipgloss.NewStyle().Padding(0, 1, 0, 1)
}
})
envVars, err := appPkg.CheckEnv(app)
if err != nil {
log.Fatal(err)
}
for _, envVar := range envVars {
if envVar.Present {
val := []string{envVar.Name, "✅"}
table.Row(val...)
} else {
val := []string{envVar.Name, "❌"}
table.Row(val...)
}
}
if len(missing) > 0 {
missingEnvVars := strings.Join(missing, ", ")
logrus.Fatalf("%s is missing %s", app.Path, missingEnvVars)
if err := formatter.PrintTable(table); err != nil {
log.Fatal(err)
}
logrus.Infof("all necessary environment variables defined for %s", app.Name)
return nil
},
BashComplete: autocomplete.AppNameComplete,
}
func init() {
AppCheckCommand.Flags().BoolVarP(
&internal.Chaos,
"chaos",
"C",
false,
"ignore uncommitted recipes changes",
)
}

View File

@ -1,146 +1,218 @@
package app
import (
"context"
"errors"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
"slices"
"sort"
"strings"
"coopcloud.tech/abra/cli/internal"
appPkg "coopcloud.tech/abra/pkg/app"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/config"
containerPkg "coopcloud.tech/abra/pkg/container"
"coopcloud.tech/abra/pkg/formatter"
"coopcloud.tech/abra/pkg/upstream/container"
"github.com/docker/cli/cli/command"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
dockerClient "github.com/docker/docker/client"
"github.com/docker/docker/pkg/archive"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
"coopcloud.tech/abra/pkg/log"
"github.com/spf13/cobra"
)
var appCmdCommand = cli.Command{
Name: "command",
var AppCmdCommand = &cobra.Command{
Use: "command <domain> [service | --local] <cmd> [[args] [flags] | [flags] -- [args]]",
Aliases: []string{"cmd"},
Usage: "Run app commands",
Description: `
Run an app specific command.
Short: "Run app commands",
Long: `Run an app specific command.
These commands are bash functions, defined in the abra.sh of the recipe itself.
They can be run within the context of a service (e.g. app) or locally on your
work station by passing "--local". Arguments can be passed into these functions
using the "-- <args>" syntax.
work station by passing "--local/-l".
Example:
N.B. If using the "--" style to pass arguments, flags (e.g. "--local/-l") must
be passed *before* the "--". It is possible to pass arguments without the "--"
as long as no dashes are present (i.e. "foo" works without "--", "-foo"
does not).`,
Example: ` # pass <cmd> args/flags without "--"
abra app cmd 1312.net app my_cmd_arg foo --user bar
abra app cmd example.com app create_user -- me@example.com
`,
ArgsUsage: "<domain> [<service>] <command> [-- <args>]",
Flags: []cli.Flag{
internal.DebugFlag,
internal.LocalCmdFlag,
internal.RemoteUserFlag,
# pass <cmd> args/flags with "--"
abra app cmd 1312.net app my_cmd_args --user bar -- foo -vvv
# drop the [service] arg if using "--local/-l"
abra app cmd 1312.net my_cmd --local`,
Args: func(cmd *cobra.Command, args []string) error {
if local {
if !(len(args) >= 2) {
return errors.New("requires at least 2 arguments with --local/-l")
}
if slices.Contains(os.Args, "--") {
if cmd.ArgsLenAtDash() > 2 {
return errors.New("accepts at most 2 args with --local/-l")
}
}
// NOTE(d1): it is unclear how to correctly validate this case
//
// abra app cmd 1312.net app test_cmd_args foo --local
// FATAL <recipe> doesn't have a app function
//
// "app" should not be there, but there is no reliable way to detect arg
// count when the user can pass an arbitrary amount of recipe command
// arguments
return nil
}
if !(len(args) >= 3) {
return errors.New("requires at least 3 arguments")
}
return nil
},
BashComplete: autocomplete.AppNameComplete,
Before: internal.SubCommandBefore,
Action: func(c *cli.Context) error {
app := internal.ValidateApp(c)
ValidArgsFunction: func(
cmd *cobra.Command,
args []string,
toComplete string) ([]string, cobra.ShellCompDirective) {
switch l := len(args); l {
case 0:
return autocomplete.AppNameComplete()
case 1:
if !local {
return autocomplete.ServiceNameComplete(args[0])
}
return autocomplete.CommandNameComplete(args[0])
case 2:
if !local {
return autocomplete.CommandNameComplete(args[0])
}
return nil, cobra.ShellCompDirectiveDefault
default:
return nil, cobra.ShellCompDirectiveError
}
},
Run: func(cmd *cobra.Command, args []string) {
app := internal.ValidateApp(args)
cl, err := client.New(app.Server)
if err != nil {
logrus.Fatal(err)
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
log.Fatal(err)
}
if internal.LocalCmd && internal.RemoteUser != "" {
internal.ShowSubcommandHelpAndError(c, errors.New("cannot use --local & --user together"))
if local && remoteUser != "" {
log.Fatal("cannot use --local & --user together")
}
hasCmdArgs, parsedCmdArgs := parseCmdArgs(c.Args(), internal.LocalCmd)
hasCmdArgs, parsedCmdArgs := parseCmdArgs(args, local)
abraSh := path.Join(config.RECIPES_DIR, app.Recipe, "abra.sh")
if _, err := os.Stat(abraSh); err != nil {
if _, err := os.Stat(app.Recipe.AbraShPath); err != nil {
if os.IsNotExist(err) {
logrus.Fatalf("%s does not exist for %s?", abraSh, app.Name)
log.Fatalf("%s does not exist for %s?", app.Recipe.AbraShPath, app.Name)
}
logrus.Fatal(err)
log.Fatal(err)
}
if internal.LocalCmd {
cmdName := c.Args().Get(1)
if err := ensureCommand(abraSh, app.Recipe, cmdName); err != nil {
logrus.Fatal(err)
if local {
cmdName := args[1]
if err := internal.EnsureCommand(app.Recipe.AbraShPath, app.Recipe.Name, cmdName); err != nil {
log.Fatal(err)
}
logrus.Debugf("--local detected, running %s on local work station", cmdName)
log.Debugf("--local detected, running %s on local work station", cmdName)
var exportEnv string
for k, v := range app.Env {
exportEnv = exportEnv + fmt.Sprintf("%s='%s'; ", k, v)
}
var sourceAndExec string
if hasCmdArgs {
logrus.Debugf("parsed following command arguments: %s", parsedCmdArgs)
sourceAndExec = fmt.Sprintf("TARGET=local; APP_NAME=%s; STACK_NAME=%s; %s . %s; %s %s", app.Name, app.StackName(), exportEnv, abraSh, cmdName, parsedCmdArgs)
log.Debugf("parsed following command arguments: %s", parsedCmdArgs)
sourceAndExec = fmt.Sprintf("TARGET=local; APP_NAME=%s; STACK_NAME=%s; %s . %s; %s %s", app.Name, app.StackName(), exportEnv, app.Recipe.AbraShPath, cmdName, parsedCmdArgs)
} else {
logrus.Debug("did not detect any command arguments")
sourceAndExec = fmt.Sprintf("TARGET=local; APP_NAME=%s; STACK_NAME=%s; %s . %s; %s", app.Name, app.StackName(), exportEnv, abraSh, cmdName)
log.Debug("did not detect any command arguments")
sourceAndExec = fmt.Sprintf("TARGET=local; APP_NAME=%s; STACK_NAME=%s; %s . %s; %s", app.Name, app.StackName(), exportEnv, app.Recipe.AbraShPath, cmdName)
}
shell := "/bin/bash"
if _, err := os.Stat(shell); errors.Is(err, os.ErrNotExist) {
logrus.Debugf("%s does not exist locally, use /bin/sh as fallback", shell)
log.Debugf("%s does not exist locally, use /bin/sh as fallback", shell)
shell = "/bin/sh"
}
cmd := exec.Command(shell, "-c", sourceAndExec)
if err := internal.RunCmd(cmd); err != nil {
logrus.Fatal(err)
}
} else {
targetServiceName := c.Args().Get(1)
cmdName := c.Args().Get(2)
if err := ensureCommand(abraSh, app.Recipe, cmdName); err != nil {
logrus.Fatal(err)
log.Fatal(err)
}
serviceNames, err := config.GetAppServiceNames(app.Name)
if err != nil {
logrus.Fatal(err)
}
return
}
matchingServiceName := false
for _, serviceName := range serviceNames {
if serviceName == targetServiceName {
matchingServiceName = true
}
}
cmdName := args[2]
if err := internal.EnsureCommand(app.Recipe.AbraShPath, app.Recipe.Name, cmdName); err != nil {
log.Fatal(err)
}
if !matchingServiceName {
logrus.Fatalf("no service %s for %s?", targetServiceName, app.Name)
}
serviceNames, err := appPkg.GetAppServiceNames(app.Name)
if err != nil {
log.Fatal(err)
}
logrus.Debugf("running command %s within the context of %s_%s", cmdName, app.StackName(), targetServiceName)
if hasCmdArgs {
logrus.Debugf("parsed following command arguments: %s", parsedCmdArgs)
} else {
logrus.Debug("did not detect any command arguments")
}
if err := runCmdRemote(cl, app, abraSh, targetServiceName, cmdName, parsedCmdArgs); err != nil {
logrus.Fatal(err)
matchingServiceName := false
targetServiceName := args[1]
for _, serviceName := range serviceNames {
if serviceName == targetServiceName {
matchingServiceName = true
}
}
return nil
if !matchingServiceName {
log.Fatalf("no service %s for %s?", targetServiceName, app.Name)
}
log.Debugf("running command %s within the context of %s_%s", cmdName, app.StackName(), targetServiceName)
if hasCmdArgs {
log.Debugf("parsed following command arguments: %s", parsedCmdArgs)
} else {
log.Debug("did not detect any command arguments")
}
cl, err := client.New(app.Server)
if err != nil {
log.Fatal(err)
}
if err := internal.RunCmdRemote(
cl,
app,
disableTTY,
app.Recipe.AbraShPath,
targetServiceName, cmdName, parsedCmdArgs, remoteUser); err != nil {
log.Fatal(err)
}
},
}
var AppCmdListCommand = &cobra.Command{
Use: "list <domain> [flags]",
Aliases: []string{"ls"},
Short: "List all available commands",
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
app := internal.ValidateApp(args)
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
log.Fatal(err)
}
cmdNames, err := appPkg.ReadAbraShCmdNames(app.Recipe.AbraShPath)
if err != nil {
log.Fatal(err)
}
sort.Strings(cmdNames)
for _, cmdName := range cmdNames {
fmt.Println(cmdName)
}
},
}
@ -163,83 +235,42 @@ func parseCmdArgs(args []string, isLocal bool) (bool, string) {
return hasCmdArgs, parsedCmdArgs
}
func ensureCommand(abraSh, recipeName, execCmd string) error {
bytes, err := ioutil.ReadFile(abraSh)
if err != nil {
return err
}
var (
local bool
remoteUser string
disableTTY bool
)
if !strings.Contains(string(bytes), execCmd) {
return fmt.Errorf("%s doesn't have a %s function", recipeName, execCmd)
}
func init() {
AppCmdCommand.Flags().BoolVarP(
&local,
"local",
"l",
false,
"run command locally",
)
return nil
}
func runCmdRemote(cl *dockerClient.Client, app config.App, abraSh, serviceName, cmdName, cmdArgs string) error {
filters := filters.NewArgs()
filters.Add("name", fmt.Sprintf("^%s_%s", app.StackName(), serviceName))
targetContainer, err := containerPkg.GetContainer(context.Background(), cl, filters, true)
if err != nil {
return err
}
logrus.Debugf("retrieved %s as target container on %s", formatter.ShortenID(targetContainer.ID), app.Server)
toTarOpts := &archive.TarOptions{NoOverwriteDirNonDir: true, Compression: archive.Gzip}
content, err := archive.TarWithOptions(abraSh, toTarOpts)
if err != nil {
return err
}
copyOpts := types.CopyToContainerOptions{AllowOverwriteDirWithFile: false, CopyUIDGID: false}
if err := cl.CopyToContainer(context.Background(), targetContainer.ID, "/tmp", content, copyOpts); err != nil {
return err
}
// FIXME: avoid instantiating a new CLI
dcli, err := command.NewDockerCli()
if err != nil {
return err
}
shell := "/bin/bash"
findShell := []string{"test", "-e", shell}
execCreateOpts := types.ExecConfig{
AttachStderr: true,
AttachStdin: true,
AttachStdout: true,
Cmd: findShell,
Detach: false,
Tty: false,
}
if err := container.RunExec(dcli, cl, targetContainer.ID, &execCreateOpts); err != nil {
logrus.Infof("%s does not exist for %s, use /bin/sh as fallback", shell, app.Name)
shell = "/bin/sh"
}
var cmd []string
if cmdArgs != "" {
cmd = []string{shell, "-c", fmt.Sprintf("TARGET=%s; APP_NAME=%s; STACK_NAME=%s; . /tmp/abra.sh; %s %s", serviceName, app.Name, app.StackName(), cmdName, cmdArgs)}
} else {
cmd = []string{shell, "-c", fmt.Sprintf("TARGET=%s; APP_NAME=%s; STACK_NAME=%s; . /tmp/abra.sh; %s", serviceName, app.Name, app.StackName(), cmdName)}
}
logrus.Debugf("running command: %s", strings.Join(cmd, " "))
if internal.RemoteUser != "" {
logrus.Debugf("running command with user %s", internal.RemoteUser)
execCreateOpts.User = internal.RemoteUser
}
execCreateOpts.Cmd = cmd
execCreateOpts.Tty = true
if err := container.RunExec(dcli, cl, targetContainer.ID, &execCreateOpts); err != nil {
return err
}
return nil
AppCmdCommand.Flags().StringVarP(
&remoteUser,
"user",
"u",
"",
"request remote user",
)
AppCmdCommand.Flags().BoolVarP(
&disableTTY,
"tty",
"T",
false,
"disable remote TTY",
)
AppCmdCommand.Flags().BoolVarP(
&internal.Chaos,
"chaos",
"C",
false,
"ignore uncommitted recipes changes",
)
}

View File

@ -13,7 +13,7 @@ func TestParseCmdArgs(t *testing.T) {
}{
// `--` is not parsed when passed in from the command-line e.g. -- foo bar baz
// so we need to eumlate that as missing when testing if bash args are passed in
// see https://git.coopcloud.tech/coop-cloud/organising/issues/336 for more
// see https://git.coopcloud.tech/toolshed/organising/issues/336 for more
{[]string{"foo.com", "app", "test"}, false, ""},
{[]string{"foo.com", "app", "test", "foo"}, true, "foo "},
{[]string{"foo.com", "app", "test", "foo", "bar", "baz"}, true, "foo bar baz "},

View File

@ -1,64 +1,57 @@
package app
import (
"errors"
"os"
"os/exec"
"coopcloud.tech/abra/cli/internal"
appPkg "coopcloud.tech/abra/pkg/app"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/log"
"github.com/AlecAivazis/survey/v2"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
"github.com/spf13/cobra"
)
var appConfigCommand = cli.Command{
Name: "config",
Aliases: []string{"cfg"},
Usage: "Edit app config",
ArgsUsage: "<domain>",
Flags: []cli.Flag{
internal.DebugFlag,
var AppConfigCommand = &cobra.Command{
Use: "config <domain> [flags]",
Aliases: []string{"cfg"},
Short: "Edit app config",
Example: " abra config 1312.net",
Args: cobra.ExactArgs(1),
ValidArgsFunction: func(
cmd *cobra.Command,
args []string,
toComplete string) ([]string, cobra.ShellCompDirective) {
return autocomplete.AppNameComplete()
},
Before: internal.SubCommandBefore,
Action: func(c *cli.Context) error {
appName := c.Args().First()
if appName == "" {
internal.ShowSubcommandHelpAndError(c, errors.New("no app provided"))
}
files, err := config.LoadAppFiles("")
Run: func(cmd *cobra.Command, args []string) {
files, err := appPkg.LoadAppFiles("")
if err != nil {
logrus.Fatal(err)
log.Fatal(err)
}
appName := args[0]
appFile, exists := files[appName]
if !exists {
logrus.Fatalf("cannot find app with name %s", appName)
log.Fatalf("cannot find app with name %s", appName)
}
ed, ok := os.LookupEnv("EDITOR")
if !ok {
edPrompt := &survey.Select{
Message: "Which editor do you wish to use?",
Message: "which editor do you wish to use?",
Options: []string{"vi", "vim", "nvim", "nano", "pico", "emacs"},
}
if err := survey.AskOne(edPrompt, &ed); err != nil {
logrus.Fatal(err)
log.Fatal(err)
}
}
cmd := exec.Command(ed, appFile.Path)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
logrus.Fatal(err)
c := exec.Command(ed, appFile.Path)
c.Stdin = os.Stdin
c.Stdout = os.Stdout
c.Stderr = os.Stderr
if err := c.Run(); err != nil {
log.Fatal(err)
}
return nil
},
BashComplete: autocomplete.AppNameComplete,
}

View File

@ -2,152 +2,382 @@ package app
import (
"context"
"errors"
"fmt"
"io"
"os"
"path"
"path/filepath"
"strings"
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/container"
containerPkg "coopcloud.tech/abra/pkg/container"
"coopcloud.tech/abra/pkg/formatter"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"coopcloud.tech/abra/pkg/log"
"coopcloud.tech/abra/pkg/upstream/container"
"github.com/docker/cli/cli/command"
containertypes "github.com/docker/docker/api/types/container"
dockerClient "github.com/docker/docker/client"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/pkg/archive"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
"github.com/spf13/cobra"
)
var appCpCommand = cli.Command{
Name: "cp",
Aliases: []string{"c"},
ArgsUsage: "<domain> <src> <dst>",
Flags: []cli.Flag{
internal.DebugFlag,
internal.NoInputFlag,
var AppCpCommand = &cobra.Command{
Use: "cp <domain> <src> <dst> [flags]",
Aliases: []string{"c"},
Short: "Copy files to/from a deployed app service",
Example: ` # copy myfile.txt to the root of the app service
abra app cp 1312.net myfile.txt app:/
# copy that file back to your current working directory locally
abra app cp 1312.net app:/myfile.txt ./`,
Args: cobra.ExactArgs(3),
ValidArgsFunction: func(
cmd *cobra.Command,
args []string,
toComplete string) ([]string, cobra.ShellCompDirective) {
switch l := len(args); l {
case 0:
return autocomplete.AppNameComplete()
default:
return nil, cobra.ShellCompDirectiveDefault
}
},
Before: internal.SubCommandBefore,
Usage: "Copy files to/from a running app service",
Description: `
Copy files to and from any app service file system.
Run: func(cmd *cobra.Command, args []string) {
app := internal.ValidateApp(args)
If you want to copy a myfile.txt to the root of the app service:
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
log.Fatal(err)
}
abra app cp <domain> myfile.txt app:/
And if you want to copy that file back to your current working directory locally:
abra app cp <domain> app:/myfile.txt .
`,
Action: func(c *cli.Context) error {
app := internal.ValidateApp(c)
src := args[1]
dst := args[2]
srcPath, dstPath, service, toContainer, err := parseSrcAndDst(src, dst)
if err != nil {
log.Fatal(err)
}
cl, err := client.New(app.Server)
if err != nil {
logrus.Fatal(err)
log.Fatal(err)
}
src := c.Args().Get(1)
dst := c.Args().Get(2)
if src == "" {
logrus.Fatal("missing <src> argument")
} else if dst == "" {
logrus.Fatal("missing <dest> argument")
container, err := containerPkg.GetContainerFromStackAndService(cl, app.StackName(), service)
if err != nil {
log.Fatal(err)
}
log.Debugf("retrieved %s as target container on %s", formatter.ShortenID(container.ID), app.Server)
parsedSrc := strings.SplitN(src, ":", 2)
parsedDst := strings.SplitN(dst, ":", 2)
errorMsg := "one of <src>/<dest> arguments must take $SERVICE:$PATH form"
if len(parsedSrc) == 2 && len(parsedDst) == 2 {
logrus.Fatal(errorMsg)
} else if len(parsedSrc) != 2 {
if len(parsedDst) != 2 {
logrus.Fatal(errorMsg)
}
} else if len(parsedDst) != 2 {
if len(parsedSrc) != 2 {
logrus.Fatal(errorMsg)
}
if toContainer {
err = CopyToContainer(cl, container.ID, srcPath, dstPath)
} else {
err = CopyFromContainer(cl, container.ID, srcPath, dstPath)
}
var service string
var srcPath string
var dstPath string
isToContainer := false // <container:src> <dst>
if len(parsedSrc) == 2 {
service = parsedSrc[0]
srcPath = parsedSrc[1]
dstPath = dst
logrus.Debugf("assuming transfer is coming FROM the container")
} else if len(parsedDst) == 2 {
service = parsedDst[0]
dstPath = parsedDst[1]
srcPath = src
isToContainer = true // <src> <container:dst>
logrus.Debugf("assuming transfer is going TO the container")
if err != nil {
log.Fatal(err)
}
if !isToContainer {
if _, err := os.Stat(dstPath); os.IsNotExist(err) {
logrus.Fatalf("%s does not exist locally?", dstPath)
}
}
if err := configureAndCp(c, cl, app, srcPath, dstPath, service, isToContainer); err != nil {
logrus.Fatal(err)
}
return nil
},
BashComplete: autocomplete.AppNameComplete,
}
func configureAndCp(
c *cli.Context,
cl *dockerClient.Client,
app config.App,
srcPath string,
dstPath string,
service string,
isToContainer bool) error {
filters := filters.NewArgs()
filters.Add("name", fmt.Sprintf("^%s_%s", app.StackName(), service))
var errServiceMissing = errors.New("one of <src>/<dest> arguments must take $SERVICE:$PATH form")
container, err := container.GetContainer(context.Background(), cl, filters, internal.NoInput)
// parseSrcAndDst parses src and dest string. One of src or dst must be of the form $SERVICE:$PATH
func parseSrcAndDst(src, dst string) (srcPath string, dstPath string, service string, toContainer bool, err error) {
parsedSrc := strings.SplitN(src, ":", 2)
parsedDst := strings.SplitN(dst, ":", 2)
if len(parsedSrc)+len(parsedDst) != 3 {
return "", "", "", false, errServiceMissing
}
if len(parsedSrc) == 2 {
return parsedSrc[1], dst, parsedSrc[0], false, nil
}
if len(parsedDst) == 2 {
return src, parsedDst[1], parsedDst[0], true, nil
}
return "", "", "", false, errServiceMissing
}
// CopyToContainer copies a file or directory from the local file system to the container.
// See the possible copy modes and their documentation.
func CopyToContainer(cl *dockerClient.Client, containerID, srcPath, dstPath string) error {
srcStat, err := os.Stat(srcPath)
if err != nil {
logrus.Fatal(err)
return fmt.Errorf("local %s ", err)
}
logrus.Debugf("retrieved %s as target container on %s", formatter.ShortenID(container.ID), app.Server)
if isToContainer {
if _, err := os.Stat(srcPath); err != nil {
logrus.Fatalf("%s does not exist?", srcPath)
dstStat, err := cl.ContainerStatPath(context.Background(), containerID, dstPath)
dstExists := true
if err != nil {
if errdefs.IsNotFound(err) {
dstExists = false
} else {
return fmt.Errorf("remote path: %s", err)
}
}
toTarOpts := &archive.TarOptions{NoOverwriteDirNonDir: true, Compression: archive.Gzip}
content, err := archive.TarWithOptions(srcPath, toTarOpts)
mode, err := copyMode(srcPath, dstPath, srcStat.Mode(), dstStat.Mode, dstExists)
if err != nil {
return err
}
movePath := ""
switch mode {
case CopyModeDirToDir:
// Add the src directory to the destination path
_, srcDir := path.Split(srcPath)
dstPath = path.Join(dstPath, srcDir)
// Make sure the dst directory exits.
dcli, err := command.NewDockerCli()
if err != nil {
logrus.Fatal(err)
return err
}
if _, err := container.RunExec(dcli, cl, containerID, &containertypes.ExecOptions{
AttachStderr: true,
AttachStdin: true,
AttachStdout: true,
Cmd: []string{"mkdir", "-p", dstPath},
Detach: false,
Tty: true,
}); err != nil {
return fmt.Errorf("create remote directory: %s", err)
}
case CopyModeFileToFile:
// Remove the file component from the path, since docker can only copy
// to a directory.
dstPath, _ = path.Split(dstPath)
case CopyModeFileToFileRename:
// Copy the file to the temp directory and move it to its dstPath
// afterwards.
movePath = dstPath
dstPath = "/tmp"
}
copyOpts := types.CopyToContainerOptions{AllowOverwriteDirWithFile: false, CopyUIDGID: false}
if err := cl.CopyToContainer(context.Background(), container.ID, dstPath, content, copyOpts); err != nil {
logrus.Fatal(err)
}
} else {
content, _, err := cl.CopyFromContainer(context.Background(), container.ID, srcPath)
toTarOpts := &archive.TarOptions{IncludeSourceDir: true, NoOverwriteDirNonDir: true, Compression: archive.Gzip}
content, err := archive.TarWithOptions(srcPath, toTarOpts)
if err != nil {
return err
}
log.Debugf("copy %s from local to %s on container", srcPath, dstPath)
copyOpts := containertypes.CopyToContainerOptions{AllowOverwriteDirWithFile: false, CopyUIDGID: false}
if err := cl.CopyToContainer(context.Background(), containerID, dstPath, content, copyOpts); err != nil {
return err
}
if movePath != "" {
_, srcFile := path.Split(srcPath)
dcli, err := command.NewDockerCli()
if err != nil {
logrus.Fatal(err)
return err
}
defer content.Close()
fromTarOpts := &archive.TarOptions{NoOverwriteDirNonDir: true, Compression: archive.Gzip}
if err := archive.Untar(content, dstPath, fromTarOpts); err != nil {
logrus.Fatal(err)
if _, err := container.RunExec(dcli, cl, containerID, &containertypes.ExecOptions{
AttachStderr: true,
AttachStdin: true,
AttachStdout: true,
Cmd: []string{"mv", path.Join("/tmp", srcFile), movePath},
Detach: false,
Tty: true,
}); err != nil {
return fmt.Errorf("create remote directory: %s", err)
}
}
return nil
}
// CopyFromContainer copies a file or directory from the given container to the local file system.
// See the possible copy modes and their documentation.
func CopyFromContainer(cl *dockerClient.Client, containerID, srcPath, dstPath string) error {
srcStat, err := cl.ContainerStatPath(context.Background(), containerID, srcPath)
if err != nil {
if errdefs.IsNotFound(err) {
return fmt.Errorf("remote: %s does not exist", srcPath)
} else {
return fmt.Errorf("remote path: %s", err)
}
}
dstStat, err := os.Stat(dstPath)
dstExists := true
var dstMode os.FileMode
if err != nil {
if os.IsNotExist(err) {
dstExists = false
} else {
return fmt.Errorf("remote path: %s", err)
}
} else {
dstMode = dstStat.Mode()
}
mode, err := copyMode(srcPath, dstPath, srcStat.Mode, dstMode, dstExists)
if err != nil {
return err
}
moveDstDir := ""
moveDstFile := ""
switch mode {
case CopyModeFileToFile:
// Remove the file component from the path, since docker can only copy
// to a directory.
dstPath, _ = path.Split(dstPath)
case CopyModeFileToFileRename:
// Copy the file to the temp directory and move it to its dstPath
// afterwards.
moveDstFile = dstPath
dstPath = "/tmp"
case CopyModeFilesToDir:
// Copy the directory to the temp directory and move it to its
// dstPath afterwards.
moveDstDir = path.Join(dstPath, "/")
dstPath = "/tmp"
// Make sure the temp directory always gets removed
defer os.Remove(path.Join("/tmp"))
}
content, _, err := cl.CopyFromContainer(context.Background(), containerID, srcPath)
if err != nil {
return fmt.Errorf("copy: %s", err)
}
defer content.Close()
if err := archive.Untar(content, dstPath, &archive.TarOptions{
NoOverwriteDirNonDir: true,
Compression: archive.Gzip,
NoLchown: true,
}); err != nil {
return fmt.Errorf("untar: %s", err)
}
if moveDstFile != "" {
_, srcFile := path.Split(strings.TrimSuffix(srcPath, "/"))
if err := moveFile(path.Join("/tmp", srcFile), moveDstFile); err != nil {
return err
}
}
if moveDstDir != "" {
_, srcDir := path.Split(strings.TrimSuffix(srcPath, "/"))
if err := moveDir(path.Join("/tmp", srcDir), moveDstDir); err != nil {
return err
}
}
return nil
}
var (
ErrCopyDirToFile = fmt.Errorf("can't copy dir to file")
ErrDstDirNotExist = fmt.Errorf("destination directory does not exist")
)
type CopyMode int
const (
// Copy a src file to a dest file. The src and dest file names are the same.
// <dir_src>/<file> + <dir_dst>/<file> -> <dir_dst>/<file>
CopyModeFileToFile = CopyMode(iota)
// Copy a src file to a dest file. The src and dest file names are not the same.
// <dir_src>/<file_src> + <dir_dst>/<file_dst> -> <dir_dst>/<file_dst>
CopyModeFileToFileRename
// Copy a src file to dest directory. The dest file gets created in the dest
// folder with the src filename.
// <dir_src>/<file> + <dir_dst> -> <dir_dst>/<file>
CopyModeFileToDir
// Copy a src directory to dest directory.
// <dir_src> + <dir_dst> -> <dir_dst>/<dir_src>
CopyModeDirToDir
// Copy all files in the src directory to the dest directory. This works recursively.
// <dir_src>/ + <dir_dst> -> <dir_dst>/<files_from_dir_src>
CopyModeFilesToDir
)
// copyMode takes a src and dest path and file mode to determine the copy mode.
// See the possible copy modes and their documentation.
func copyMode(srcPath, dstPath string, srcMode os.FileMode, dstMode os.FileMode, dstExists bool) (CopyMode, error) {
_, srcFile := path.Split(srcPath)
_, dstFile := path.Split(dstPath)
if srcMode.IsDir() {
if !dstExists {
return -1, ErrDstDirNotExist
}
if dstMode.IsDir() {
if strings.HasSuffix(srcPath, "/") {
return CopyModeFilesToDir, nil
}
return CopyModeDirToDir, nil
}
return -1, ErrCopyDirToFile
}
if dstMode.IsDir() {
return CopyModeFileToDir, nil
}
if srcFile != dstFile {
return CopyModeFileToFileRename, nil
}
return CopyModeFileToFile, nil
}
// moveDir moves all files from a source path to the destination path recursively.
func moveDir(sourcePath, destPath string) error {
return filepath.Walk(sourcePath, func(p string, info os.FileInfo, err error) error {
if err != nil {
return err
}
newPath := path.Join(destPath, strings.TrimPrefix(p, sourcePath))
if info.IsDir() {
err := os.Mkdir(newPath, info.Mode())
if err != nil {
if os.IsExist(err) {
return nil
}
return err
}
}
if info.Mode().IsRegular() {
return moveFile(p, newPath)
}
return nil
})
}
// moveFile moves a file from a source path to a destination path.
func moveFile(sourcePath, destPath string) error {
inputFile, err := os.Open(sourcePath)
if err != nil {
return err
}
outputFile, err := os.Create(destPath)
if err != nil {
inputFile.Close()
return err
}
defer outputFile.Close()
_, err = io.Copy(outputFile, inputFile)
inputFile.Close()
if err != nil {
return err
}
// Remove file after succesfull copy.
err = os.Remove(sourcePath)
if err != nil {
return err
}
return nil
}
func init() {
AppCpCommand.Flags().BoolVarP(
&internal.Chaos,
"chaos",
"C",
false,
"ignore uncommitted recipes changes",
)
}

113
cli/app/cp_test.go Normal file
View File

@ -0,0 +1,113 @@
package app
import (
"os"
"testing"
)
func TestParse(t *testing.T) {
tests := []struct {
src string
dst string
srcPath string
dstPath string
service string
toContainer bool
err error
}{
{src: "foo", dst: "bar", err: errServiceMissing},
{src: "app:foo", dst: "app:bar", err: errServiceMissing},
{src: "app:foo", dst: "bar", srcPath: "foo", dstPath: "bar", service: "app", toContainer: false},
{src: "foo", dst: "app:bar", srcPath: "foo", dstPath: "bar", service: "app", toContainer: true},
}
for i, tc := range tests {
srcPath, dstPath, service, toContainer, err := parseSrcAndDst(tc.src, tc.dst)
if srcPath != tc.srcPath {
t.Errorf("[%d] srcPath: want (%s), got(%s)", i, tc.srcPath, srcPath)
}
if dstPath != tc.dstPath {
t.Errorf("[%d] dstPath: want (%s), got(%s)", i, tc.dstPath, dstPath)
}
if service != tc.service {
t.Errorf("[%d] service: want (%s), got(%s)", i, tc.service, service)
}
if toContainer != tc.toContainer {
t.Errorf("[%d] toConainer: want (%t), got(%t)", i, tc.toContainer, toContainer)
}
if err == nil && tc.err != nil && err.Error() != tc.err.Error() {
t.Errorf("[%d] err: want (%s), got(%s)", i, tc.err, err)
}
}
}
func TestCopyMode(t *testing.T) {
tests := []struct {
srcPath string
dstPath string
srcMode os.FileMode
dstMode os.FileMode
dstExists bool
mode CopyMode
err error
}{
{
srcPath: "foo.txt",
dstPath: "foo.txt",
srcMode: os.ModePerm,
dstMode: os.ModePerm,
dstExists: true,
mode: CopyModeFileToFile,
},
{
srcPath: "foo.txt",
dstPath: "bar.txt",
srcMode: os.ModePerm,
dstExists: true,
mode: CopyModeFileToFileRename,
},
{
srcPath: "foo",
dstPath: "foo",
srcMode: os.ModeDir,
dstMode: os.ModeDir,
dstExists: true,
mode: CopyModeDirToDir,
},
{
srcPath: "foo/",
dstPath: "foo",
srcMode: os.ModeDir,
dstMode: os.ModeDir,
dstExists: true,
mode: CopyModeFilesToDir,
},
{
srcPath: "foo",
dstPath: "foo",
srcMode: os.ModeDir,
dstExists: false,
mode: -1,
err: ErrDstDirNotExist,
},
{
srcPath: "foo",
dstPath: "foo",
srcMode: os.ModeDir,
dstMode: os.ModePerm,
dstExists: true,
mode: -1,
err: ErrCopyDirToFile,
},
}
for i, tc := range tests {
mode, err := copyMode(tc.srcPath, tc.dstPath, tc.srcMode, tc.dstMode, tc.dstExists)
if mode != tc.mode {
t.Errorf("[%d] mode: want (%d), got(%d)", i, tc.mode, mode)
}
if err != tc.err {
t.Errorf("[%d] err: want (%s), got(%s)", i, tc.err, err)
}
}
}

View File

@ -1,36 +1,350 @@
package app
import (
"context"
"fmt"
"strings"
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/app"
"coopcloud.tech/abra/pkg/autocomplete"
"github.com/urfave/cli"
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/envfile"
"coopcloud.tech/abra/pkg/secret"
appPkg "coopcloud.tech/abra/pkg/app"
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/dns"
"coopcloud.tech/abra/pkg/formatter"
"coopcloud.tech/abra/pkg/lint"
"coopcloud.tech/abra/pkg/log"
"coopcloud.tech/abra/pkg/upstream/stack"
dockerClient "github.com/docker/docker/client"
"github.com/spf13/cobra"
)
var appDeployCommand = cli.Command{
Name: "deploy",
Aliases: []string{"d"},
Usage: "Deploy an app",
ArgsUsage: "<domain>",
Flags: []cli.Flag{
internal.DebugFlag,
internal.NoInputFlag,
internal.ForceFlag,
internal.ChaosFlag,
internal.NoDomainChecksFlag,
internal.DontWaitConvergeFlag,
var AppDeployCommand = &cobra.Command{
Use: "deploy <domain> [version] [flags]",
Aliases: []string{"d"},
Short: "Deploy an app",
Long: `Deploy an app.
This command supports chaos operations. Use "--chaos/-C" to deploy your recipe
checkout as-is. Recipe commit hashes are also supported as values for
"[version]". Please note, "upgrade"/"rollback" do not support chaos operations.`,
Example: ` # standard deployment
abra app deploy 1312.net
# chaos deployment
abra app deploy 1312.net --chaos
# deploy specific version
abra app deploy 1312.net 2.0.0+1.2.3
# deploy a specific git hash
abra app deploy 1312.net 886db76d`,
Args: cobra.RangeArgs(1, 2),
ValidArgsFunction: func(
cmd *cobra.Command,
args []string,
toComplete string,
) ([]string, cobra.ShellCompDirective) {
switch l := len(args); l {
case 0:
return autocomplete.AppNameComplete()
case 1:
app, err := appPkg.Get(args[0])
if err != nil {
errMsg := fmt.Sprintf("autocomplete failed: %s", err)
return []string{errMsg}, cobra.ShellCompDirectiveError
}
return autocomplete.RecipeVersionComplete(app.Recipe.Name)
default:
return nil, cobra.ShellCompDirectiveDefault
}
},
Before: internal.SubCommandBefore,
Description: `
Deploy an app. It does not support incrementing the version of a deployed app,
for this you need to look at the "abra app upgrade <domain>" command.
Run: func(cmd *cobra.Command, args []string) {
var (
deployWarnMessages []string
toDeployVersion string
)
You may pass "--force" to re-deploy the same version again. This can be useful
if the container runtime has gotten into a weird state.
app := internal.ValidateApp(args)
Chas mode ("--chaos") will deploy your local checkout of a recipe as-is,
including unstaged changes and can be useful for live hacking and testing new
recipes.
`,
Action: internal.DeployAction,
BashComplete: autocomplete.AppNameComplete,
if err := validateArgsAndFlags(args); err != nil {
log.Fatal(err)
}
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
log.Fatal(err)
}
cl, err := client.New(app.Server)
if err != nil {
log.Fatal(err)
}
log.Debugf("checking whether %s is already deployed", app.StackName())
deployMeta, err := stack.IsDeployed(context.Background(), cl, app.StackName())
if err != nil {
log.Fatal(err)
}
if deployMeta.IsDeployed && !(internal.Force || internal.Chaos) {
log.Fatalf("%s is already deployed", app.Name)
}
toDeployVersion, err = getDeployVersion(args, deployMeta, app)
if err != nil {
log.Fatal(fmt.Errorf("get deploy version: %s", err))
}
if !internal.Chaos {
_, err = app.Recipe.EnsureVersion(toDeployVersion)
if err != nil {
log.Fatalf("ensure recipe: %s", err)
}
}
if err := lint.LintForErrors(app.Recipe); err != nil {
log.Fatal(err)
}
if err := validateSecrets(cl, app); err != nil {
log.Fatal(err)
}
abraShEnv, err := envfile.ReadAbraShEnvVars(app.Recipe.AbraShPath)
if err != nil {
log.Fatal(err)
}
for k, v := range abraShEnv {
app.Env[k] = v
}
composeFiles, err := app.Recipe.GetComposeFiles(app.Env)
if err != nil {
log.Fatal(err)
}
stackName := app.StackName()
deployOpts := stack.Deploy{
Composefiles: composeFiles,
Namespace: stackName,
Prune: false,
ResolveImage: stack.ResolveImageAlways,
Detach: false,
}
compose, err := appPkg.GetAppComposeConfig(app.Name, deployOpts, app.Env)
if err != nil {
log.Fatal(err)
}
appPkg.ExposeAllEnv(stackName, compose, app.Env)
appPkg.SetRecipeLabel(compose, stackName, app.Recipe.Name)
appPkg.SetChaosLabel(compose, stackName, internal.Chaos)
if internal.Chaos {
appPkg.SetChaosVersionLabel(compose, stackName, toDeployVersion)
}
appPkg.SetUpdateLabel(compose, stackName, app.Env)
appPkg.SetVersionLabel(compose, stackName, toDeployVersion)
envVars, err := appPkg.CheckEnv(app)
if err != nil {
log.Fatal(err)
}
for _, envVar := range envVars {
if !envVar.Present {
deployWarnMessages = append(deployWarnMessages,
fmt.Sprintf("%s missing from %s.env", envVar.Name, app.Domain),
)
}
}
if !internal.NoDomainChecks {
if domainName, ok := app.Env["DOMAIN"]; ok {
if _, err = dns.EnsureDomainsResolveSameIPv4(domainName, app.Server); err != nil {
log.Fatal(err)
}
} else {
log.Debug("skipping domain checks, no DOMAIN=... configured")
}
} else {
log.Debug("skipping domain checks")
}
deployedVersion := config.NO_VERSION_DEFAULT
if deployMeta.IsDeployed {
deployedVersion = deployMeta.Version
}
if err := internal.DeployOverview(
app,
deployedVersion,
toDeployVersion,
"",
deployWarnMessages,
); err != nil {
log.Fatal(err)
}
stack.WaitTimeout, err = appPkg.GetTimeoutFromLabel(compose, stackName)
if err != nil {
log.Fatal(err)
}
log.Debugf("set waiting timeout to %d second(s)", stack.WaitTimeout)
serviceNames, err := appPkg.GetAppServiceNames(app.Name)
if err != nil {
log.Fatal(err)
}
f, err := app.Filters(true, false, serviceNames...)
if err != nil {
log.Fatal(err)
}
if err := stack.RunDeploy(
cl,
deployOpts,
compose,
app.Name,
app.Server,
internal.DontWaitConverge,
f,
); err != nil {
log.Fatal(err)
}
postDeployCmds, ok := app.Env["POST_DEPLOY_CMDS"]
if ok && !internal.DontWaitConverge {
log.Debugf("run the following post-deploy commands: %s", postDeployCmds)
if err := internal.PostCmds(cl, app, postDeployCmds); err != nil {
log.Fatalf("attempting to run post deploy commands, saw: %s", err)
}
}
if err := app.WriteRecipeVersion(toDeployVersion, false); err != nil {
log.Fatalf("writing recipe version failed: %s", err)
}
},
}
func getLatestVersionOrCommit(app app.App) (string, error) {
versions, err := app.Recipe.Tags()
if err != nil {
return "", err
}
if len(versions) > 0 && !internal.Chaos {
return versions[len(versions)-1], nil
}
head, err := app.Recipe.Head()
if err != nil {
return "", err
}
return formatter.SmallSHA(head.String()), nil
}
// validateArgsAndFlags ensures compatible args/flags.
func validateArgsAndFlags(args []string) error {
if len(args) == 2 && args[1] != "" && internal.Chaos {
return fmt.Errorf("cannot use [version] and --chaos together")
}
return nil
}
func validateSecrets(cl *dockerClient.Client, app app.App) error {
secStats, err := secret.PollSecretsStatus(cl, app)
if err != nil {
return err
}
for _, secStat := range secStats {
if !secStat.CreatedOnRemote {
return fmt.Errorf("secret not generated: %s", secStat.LocalName)
}
}
return nil
}
func getDeployVersion(cliArgs []string, deployMeta stack.DeployMeta, app app.App) (string, error) {
// Chaos mode overrides everything
if internal.Chaos {
v, err := app.Recipe.ChaosVersion()
if err != nil {
return "", err
}
log.Debugf("version: taking chaos version: %s", v)
return v, nil
}
// Check if the deploy version is set with a cli argument
if len(cliArgs) == 2 && cliArgs[1] != "" {
log.Debugf("version: taking version from cli arg: %s", cliArgs[1])
return cliArgs[1], nil
}
// Check if the recipe has a version in the .env file
if app.Recipe.EnvVersion != "" && !internal.IgnoreEnvVersion {
if strings.HasSuffix(app.Recipe.EnvVersionRaw, "+U") {
return "", fmt.Errorf("version: can not redeploy chaos version %s", app.Recipe.EnvVersionRaw)
}
log.Debugf("version: taking version from .env file: %s", app.Recipe.EnvVersion)
return app.Recipe.EnvVersion, nil
}
// Take deployed version
if deployMeta.IsDeployed {
log.Debugf("version: taking deployed version: %s", deployMeta.Version)
return deployMeta.Version, nil
}
v, err := getLatestVersionOrCommit(app)
log.Debugf("version: taking new recipe version: %s", v)
if err != nil {
return "", err
}
return v, nil
}
func init() {
AppDeployCommand.Flags().BoolVarP(
&internal.Chaos,
"chaos",
"C",
false,
"ignore uncommitted recipes changes",
)
AppDeployCommand.Flags().BoolVarP(
&internal.Force,
"force",
"f",
false,
"perform action without further prompt",
)
AppDeployCommand.Flags().BoolVarP(
&internal.NoDomainChecks,
"no-domain-checks",
"D",
false,
"disable public DNS checks",
)
AppDeployCommand.Flags().BoolVarP(
&internal.DontWaitConverge,
"no-converge-checks",
"c",
false,
"disable converge logic checks",
)
}

43
cli/app/env.go Normal file
View File

@ -0,0 +1,43 @@
package app
import (
"fmt"
"sort"
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/formatter"
"github.com/spf13/cobra"
)
var AppEnvCommand = &cobra.Command{
Use: "env <domain> [flags]",
Aliases: []string{"e"},
Short: "Show app .env values",
Example: " abra app env 1312.net",
Args: cobra.ExactArgs(1),
ValidArgsFunction: func(
cmd *cobra.Command,
args []string,
toComplete string) ([]string, cobra.ShellCompDirective) {
return autocomplete.AppNameComplete()
},
Run: func(cmd *cobra.Command, args []string) {
app := internal.ValidateApp(args)
var envKeys []string
for k := range app.Env {
envKeys = append(envKeys, k)
}
sort.Strings(envKeys)
var rows [][]string
for _, k := range envKeys {
rows = append(rows, []string{k, app.Env[k]})
}
overview := formatter.CreateOverview("ENV OVERVIEW", rows)
fmt.Println(overview)
},
}

View File

@ -1,145 +0,0 @@
package app
import (
"context"
"fmt"
"strconv"
"strings"
"time"
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/recipe"
"coopcloud.tech/abra/pkg/runtime"
stack "coopcloud.tech/abra/pkg/upstream/stack"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
dockerClient "github.com/docker/docker/client"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
var appErrorsCommand = cli.Command{
Name: "errors",
Usage: "List errors for a deployed app",
ArgsUsage: "<domain>",
Description: `
List errors for a deployed app.
This is a best-effort implementation and an attempt to gather a number of tips
& tricks for finding errors together into one convenient command. When an app
is failing to deploy or having issues, it could be a lot of things.
This command currently takes into account:
Is the service deployed?
Is the service killed by an OOM error?
Is the service reporting an error (like in "ps --no-trunc" output)
Is the service healthcheck failing? what are the healthcheck logs?
Got any more ideas? Please let us know:
https://git.coopcloud.tech/coop-cloud/organising/issues/new/choose
This command is best accompanied by "abra app logs <domain>" which may reveal
further information which can help you debug the cause of an app failure via
the logs.
`,
Aliases: []string{"e"},
Flags: []cli.Flag{
internal.DebugFlag,
internal.WatchFlag,
},
Before: internal.SubCommandBefore,
BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
app := internal.ValidateApp(c)
conf := runtime.New()
cl, err := client.New(app.Server)
if err != nil {
logrus.Fatal(err)
}
isDeployed, _, err := stack.IsDeployed(context.Background(), cl, app.StackName())
if err != nil {
logrus.Fatal(err)
}
if !isDeployed {
logrus.Fatalf("%s is not deployed?", app.Name)
}
if !internal.Watch {
if err := checkErrors(c, cl, app, conf); err != nil {
logrus.Fatal(err)
}
return nil
}
for {
if err := checkErrors(c, cl, app, conf); err != nil {
logrus.Fatal(err)
}
time.Sleep(2 * time.Second)
}
return nil
},
}
func checkErrors(c *cli.Context, cl *dockerClient.Client, app config.App, conf *runtime.Config) error {
recipe, err := recipe.Get(app.Recipe, conf)
if err != nil {
return err
}
for _, service := range recipe.Config.Services {
filters := filters.NewArgs()
filters.Add("name", fmt.Sprintf("^%s_%s", app.StackName(), service.Name))
containers, err := cl.ContainerList(context.Background(), types.ContainerListOptions{Filters: filters})
if err != nil {
return err
}
if len(containers) == 0 {
logrus.Warnf("%s is not up, something seems wrong", service.Name)
continue
}
container := containers[0]
containerState, err := cl.ContainerInspect(context.Background(), container.ID)
if err != nil {
logrus.Fatal(err)
}
if containerState.State.OOMKilled {
logrus.Warnf("%s has been killed due to an out of memory error", service.Name)
}
if containerState.State.Error != "" {
logrus.Warnf("%s reports this error: %s", service.Name, containerState.State.Error)
}
if containerState.State.Health != nil {
if containerState.State.Health.Status != "healthy" {
logrus.Warnf("%s healthcheck status is %s", service.Name, containerState.State.Health.Status)
logrus.Warnf("%s healthcheck has failed %s times", service.Name, strconv.Itoa(containerState.State.Health.FailingStreak))
for _, log := range containerState.State.Health.Log {
logrus.Warnf("%s healthcheck logs: %s", service.Name, strings.TrimSpace(log.Output))
}
}
}
}
return nil
}
func getServiceName(names []string) string {
containerName := strings.Join(names, " ")
trimmed := strings.TrimPrefix(containerName, "/")
return strings.Split(trimmed, ".")[0]
}

139
cli/app/labels.go Normal file
View File

@ -0,0 +1,139 @@
package app
import (
"context"
"fmt"
"sort"
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/formatter"
"coopcloud.tech/abra/pkg/log"
"coopcloud.tech/abra/pkg/upstream/convert"
composetypes "github.com/docker/cli/cli/compose/types"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
dockerClient "github.com/docker/docker/client"
"github.com/spf13/cobra"
)
var AppLabelsCommand = &cobra.Command{
Use: "labels <domain> [flags]",
Aliases: []string{"lb"},
Short: "Show deployment labels",
Long: "Both local recipe and live deployment labels are shown.",
Example: " abra app labels 1312.net",
Args: cobra.ExactArgs(1),
ValidArgsFunction: func(
cmd *cobra.Command,
args []string,
toComplete string) ([]string, cobra.ShellCompDirective) {
return autocomplete.AppNameComplete()
},
Run: func(cmd *cobra.Command, args []string) {
app := internal.ValidateApp(args)
if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
log.Fatal(err)
}
cl, err := client.New(app.Server)
if err != nil {
log.Fatal(err)
}
remoteLabels, err := getLabels(cl, app.StackName())
if err != nil {
log.Fatal(err)
}
rows := [][]string{
{"DEPLOYED LABELS", "---"},
}
remoteLabelKeys := make([]string, 0, len(remoteLabels))
for k := range remoteLabels {
remoteLabelKeys = append(remoteLabelKeys, k)
}
sort.Strings(remoteLabelKeys)
for _, k := range remoteLabelKeys {
rows = append(rows, []string{
k,
remoteLabels[k],
})
}
if len(remoteLabelKeys) == 0 {
rows = append(rows, []string{"unknown"})
}
rows = append(rows, []string{"RECIPE LABELS", "---"})
config, err := app.Recipe.GetComposeConfig(app.Env)
if err != nil {
log.Fatal(err)
}
var localLabelKeys []string
var appServiceConfig composetypes.ServiceConfig
for _, service := range config.Services {
if service.Name == "app" {
appServiceConfig = service
for k := range service.Deploy.Labels {
localLabelKeys = append(localLabelKeys, k)
}
}
}
sort.Strings(localLabelKeys)
for _, k := range localLabelKeys {
rows = append(rows, []string{
k,
appServiceConfig.Deploy.Labels[k],
})
}
overview := formatter.CreateOverview("LABELS OVERVIEW", rows)
fmt.Println(overview)
},
}
// getLabels reads docker labels from running services in the format of "coop-cloud.${STACK_NAME}.${LABEL}".
func getLabels(cl *dockerClient.Client, stackName string) (map[string]string, error) {
labels := make(map[string]string)
filter := filters.NewArgs()
filter.Add("label", fmt.Sprintf("%s=%s", convert.LabelNamespace, stackName))
services, err := cl.ServiceList(context.Background(), types.ServiceListOptions{Filters: filter})
if err != nil {
return labels, err
}
for _, service := range services {
if service.Spec.Name != fmt.Sprintf("%s_app", stackName) {
continue
}
for k, v := range service.Spec.Labels {
labels[k] = v
}
}
return labels, nil
}
func init() {
AppLabelsCommand.Flags().BoolVarP(
&internal.Chaos,
"chaos",
"C",
false,
"ignore uncommitted recipes changes",
)
}

View File

@ -4,48 +4,29 @@ import (
"encoding/json"
"fmt"
"sort"
"strconv"
"strings"
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/config"
appPkg "coopcloud.tech/abra/pkg/app"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/formatter"
"coopcloud.tech/abra/pkg/recipe"
"coopcloud.tech/abra/pkg/log"
"coopcloud.tech/tagcmp"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
"github.com/spf13/cobra"
)
var status bool
var statusFlag = &cli.BoolFlag{
Name: "status, S",
Usage: "Show app deployment status",
Destination: &status,
}
var recipeFilter string
var recipeFlag = &cli.StringFlag{
Name: "recipe, r",
Value: "",
Usage: "Show apps of a specific recipe",
Destination: &recipeFilter,
}
var listAppServer string
var listAppServerFlag = &cli.StringFlag{
Name: "server, s",
Value: "",
Usage: "Show apps of a specific server",
Destination: &listAppServer,
}
type appStatus struct {
Server string `json:"server"`
Recipe string `json:"recipe"`
AppName string `json:"appName"`
Domain string `json:"domain"`
Status string `json:"status"`
Version string `json:"version"`
Upgrade string `json:"upgrade"`
Server string `json:"server"`
Recipe string `json:"recipe"`
AppName string `json:"appName"`
Domain string `json:"domain"`
Status string `json:"status"`
Chaos string `json:"chaos"`
ChaosVersion string `json:"chaosVersion"`
AutoUpdate string `json:"autoUpdate"`
Version string `json:"version"`
Upgrade string `json:"upgrade"`
}
type serverStatus struct {
@ -57,41 +38,36 @@ type serverStatus struct {
UpgradeCount int `json:"upgradeCount"`
}
var appListCommand = cli.Command{
Name: "list",
var AppListCommand = &cobra.Command{
Use: "list [flags]",
Aliases: []string{"ls"},
Usage: "List all managed apps",
Description: `
Read the local file system listing of apps and servers (e.g. ~/.abra/) to
generate a report of all your apps.
Short: "List all managed apps",
Long: `Generate a report of all managed apps.
By passing the "--status/-S" flag, you can query all your servers for the
actual live deployment status. Depending on how many servers you manage, this
can take some time.
`,
Flags: []cli.Flag{
internal.DebugFlag,
internal.MachineReadableFlag,
statusFlag,
listAppServerFlag,
recipeFlag,
},
Before: internal.SubCommandBefore,
Action: func(c *cli.Context) error {
appFiles, err := config.LoadAppFiles(listAppServer)
Use "--status/-S" flag to query all servers for the live deployment status.`,
Example: ` # list apps of all servers without live status
abra app ls
# list apps of a specific server with live status
abra app ls -s 1312.net -S
# list apps of all servers which match a specific recipe
abra app ls -r gitea`,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
appFiles, err := appPkg.LoadAppFiles(listAppServer)
if err != nil {
logrus.Fatal(err)
log.Fatal(err)
}
apps, err := config.GetApps(appFiles, recipeFilter)
apps, err := appPkg.GetApps(appFiles, recipeFilter)
if err != nil {
logrus.Fatal(err)
log.Fatal(err)
}
sort.Sort(config.ByServerAndRecipe(apps))
sort.Sort(appPkg.ByServerAndRecipe(apps))
statuses := make(map[string]map[string]string)
var catl recipe.RecipeCatalogue
if status {
alreadySeen := make(map[string]bool)
for _, app := range apps {
@ -100,14 +76,9 @@ can take some time.
}
}
statuses, err = config.GetAppStatuses(apps, internal.MachineReadable)
statuses, err = appPkg.GetAppStatuses(apps, internal.MachineReadable)
if err != nil {
logrus.Fatal(err)
}
catl, err = recipe.ReadRecipeCatalogue()
if err != nil {
logrus.Fatal(err)
log.Fatal(err)
}
}
@ -125,7 +96,7 @@ can take some time.
}
}
if app.Recipe == recipeFilter || recipeFilter == "" {
if app.Recipe.Name == recipeFilter || recipeFilter == "" {
if recipeFilter != "" {
// only count server if matches filter
totalServersCount++
@ -138,12 +109,24 @@ can take some time.
if status {
status := "unknown"
version := "unknown"
chaos := "unknown"
chaosVersion := "unknown"
autoUpdate := "unknown"
if statusMeta, ok := statuses[app.StackName()]; ok {
if currentVersion, exists := statusMeta["version"]; exists {
if currentVersion != "" {
version = currentVersion
}
}
if chaosDeploy, exists := statusMeta["chaos"]; exists {
chaos = chaosDeploy
}
if chaosDeployVersion, exists := statusMeta["chaosVersion"]; exists {
chaosVersion = chaosDeployVersion
}
if autoUpdateState, exists := statusMeta["autoUpdate"]; exists {
autoUpdate = autoUpdateState
}
if statusMeta["status"] != "" {
status = statusMeta["status"]
}
@ -153,24 +136,31 @@ can take some time.
}
appStats.Status = status
appStats.Chaos = chaos
appStats.ChaosVersion = chaosVersion
appStats.Version = version
appStats.AutoUpdate = autoUpdate
var newUpdates []string
if version != "unknown" {
updates, err := recipe.GetRecipeCatalogueVersions(app.Recipe, catl)
if version != "unknown" && chaosVersion == "unknown" {
if err := app.Recipe.EnsureExists(); err != nil {
log.Fatalf("unable to clone %s: %s", app.Name, err)
}
updates, err := app.Recipe.Tags()
if err != nil {
logrus.Fatal(err)
log.Fatalf("unable to retrieve tags for %s: %s", app.Name, err)
}
parsedVersion, err := tagcmp.Parse(version)
if err != nil {
logrus.Fatal(err)
log.Fatal(err)
}
for _, update := range updates {
parsedUpdate, err := tagcmp.Parse(update)
if err != nil {
logrus.Fatal(err)
log.Fatal(err)
}
if update != version && parsedUpdate.IsGreaterThan(parsedVersion) {
@ -187,14 +177,14 @@ can take some time.
stats.LatestCount++
}
} else {
newUpdates = internal.ReverseStringList(newUpdates)
newUpdates = internal.SortVersionsDesc(newUpdates)
appStats.Upgrade = strings.Join(newUpdates, "\n")
stats.UpgradeCount++
}
}
appStats.Server = app.Server
appStats.Recipe = app.Recipe
appStats.Recipe = app.Recipe.Name
appStats.AppName = app.Name
appStats.Domain = app.Domain
@ -206,11 +196,12 @@ can take some time.
if internal.MachineReadable {
jsonstring, err := json.Marshal(allStats)
if err != nil {
logrus.Fatal(err)
log.Fatal(err)
} else {
fmt.Println(string(jsonstring))
}
return nil
return
}
alreadySeen := make(map[string]bool)
@ -221,50 +212,118 @@ can take some time.
serverStat := allStats[app.Server]
tableCol := []string{"recipe", "domain"}
headers := []string{"RECIPE", "DOMAIN", "SERVER"}
if status {
tableCol = append(tableCol, []string{"status", "version", "upgrade"}...)
headers = append(headers, []string{
"STATUS",
"CHAOS",
"VERSION",
"UPGRADE",
"AUTOUPDATE"}...,
)
}
table := formatter.CreateTable(tableCol)
table, err := formatter.CreateTable()
if err != nil {
log.Fatal(err)
}
table.Headers(headers...)
var rows [][]string
for _, appStat := range serverStat.Apps {
tableRow := []string{appStat.Recipe, appStat.Domain}
row := []string{appStat.Recipe, appStat.Domain, appStat.Server}
if status {
tableRow = append(tableRow, []string{appStat.Status, appStat.Version, appStat.Upgrade}...)
chaosStatus := appStat.Chaos
if chaosStatus != "unknown" {
chaosEnabled, err := strconv.ParseBool(chaosStatus)
if err != nil {
log.Fatal(err)
}
if chaosEnabled && appStat.ChaosVersion != "unknown" {
chaosStatus = appStat.ChaosVersion
}
}
row = append(row, []string{
appStat.Status,
chaosStatus,
appStat.Version,
appStat.Upgrade,
appStat.AutoUpdate}...,
)
}
table.Append(tableRow)
rows = append(rows, row)
}
if table.NumLines() > 0 {
table.Render()
table.Rows(rows...)
if status {
fmt.Println(fmt.Sprintf(
"server: %s | total apps: %v | versioned: %v | unversioned: %v | latest: %v | upgrade: %v",
app.Server,
serverStat.AppCount,
serverStat.VersionCount,
serverStat.UnversionedCount,
serverStat.LatestCount,
serverStat.UpgradeCount,
))
} else {
fmt.Println(fmt.Sprintf("server: %s | total apps: %v", app.Server, serverStat.AppCount))
if len(rows) > 0 {
if err := formatter.PrintTable(table); err != nil {
log.Fatal(err)
}
}
if len(allStats) > 1 && table.NumLines() > 0 {
fmt.Println() // newline separator for multiple servers
if len(allStats) > 1 && len(rows) > 0 {
fmt.Println() // newline separator for multiple servers
}
}
alreadySeen[app.Server] = true
}
if len(allStats) > 1 {
fmt.Println(fmt.Sprintf("total servers: %v | total apps: %v ", totalServersCount, totalAppsCount))
}
return nil
},
}
var (
status bool
recipeFilter string
listAppServer string
)
func init() {
AppListCommand.Flags().BoolVarP(
&status,
"status",
"S",
false,
"show app deployment status",
)
AppListCommand.Flags().StringVarP(
&recipeFilter,
"recipe",
"r",
"",
"show apps of a specific recipe",
)
AppListCommand.RegisterFlagCompletionFunc(
"recipe",
func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return autocomplete.RecipeNameComplete()
},
)
AppListCommand.Flags().BoolVarP(
&internal.MachineReadable,
"machine",
"m",
false,
"print machine-readable output",
)
AppListCommand.Flags().StringVarP(
&listAppServer,
"server",
"s",
"",
"show apps of a specific server",
)
AppListCommand.RegisterFlagCompletionFunc(
"server",
func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return autocomplete.ServerNameComplete()
},
)
}

View File

@ -3,133 +3,105 @@ package app
import (
"context"
"fmt"
"io"
"os"
"sync"
"coopcloud.tech/abra/cli/internal"
appPkg "coopcloud.tech/abra/pkg/app"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/runtime"
"coopcloud.tech/abra/pkg/service"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
dockerClient "github.com/docker/docker/client"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
"coopcloud.tech/abra/pkg/log"
"coopcloud.tech/abra/pkg/logs"
"coopcloud.tech/abra/pkg/upstream/stack"
"github.com/spf13/cobra"
)
var logOpts = types.ContainerLogsOptions{
ShowStderr: true,
ShowStdout: true,
Since: "",
Until: "",
Timestamps: true,
Follow: true,
Tail: "20",
Details: false,
}
// stackLogs lists logs for all stack services
func stackLogs(c *cli.Context, app config.App, client *dockerClient.Client) {
filters, err := app.Filters(true, false)
if err != nil {
logrus.Fatal(err)
}
serviceOpts := types.ServiceListOptions{Filters: filters}
services, err := client.ServiceList(context.Background(), serviceOpts)
if err != nil {
logrus.Fatal(err)
}
var wg sync.WaitGroup
for _, service := range services {
wg.Add(1)
go func(s string) {
if internal.StdErrOnly {
logOpts.ShowStdout = false
}
logs, err := client.ServiceLogs(context.Background(), s, logOpts)
var AppLogsCommand = &cobra.Command{
Use: "logs <domain> [service] [flags]",
Aliases: []string{"l"},
Short: "Tail app logs",
Args: cobra.RangeArgs(1, 2),
ValidArgsFunction: func(
cmd *cobra.Command,
args []string,
toComplete string) ([]string, cobra.ShellCompDirective) {
switch l := len(args); l {
case 0:
return autocomplete.AppNameComplete()
case 1:
app, err := appPkg.Get(args[0])
if err != nil {
logrus.Fatal(err)
errMsg := fmt.Sprintf("autocomplete failed: %s", err)
return []string{errMsg}, cobra.ShellCompDirectiveError
}
defer logs.Close()
_, err = io.Copy(os.Stdout, logs)
if err != nil && err != io.EOF {
logrus.Fatal(err)
}
}(service.ID)
}
wg.Wait()
os.Exit(0)
}
var appLogsCommand = cli.Command{
Name: "logs",
Aliases: []string{"l"},
ArgsUsage: "<domain> [<service>]",
Usage: "Tail app logs",
Flags: []cli.Flag{
internal.StdErrOnlyFlag,
internal.SinceLogsFlag,
internal.DebugFlag,
return autocomplete.ServiceNameComplete(app.Name)
default:
return nil, cobra.ShellCompDirectiveDefault
}
},
Before: internal.SubCommandBefore,
BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error {
app := internal.ValidateApp(c, runtime.WithEnsureRecipeExists(false))
Run: func(cmd *cobra.Command, args []string) {
app := internal.ValidateApp(args)
stackName := app.StackName()
if err := app.Recipe.EnsureExists(); err != nil {
log.Fatal(err)
}
cl, err := client.New(app.Server)
if err != nil {
logrus.Fatal(err)
log.Fatal(err)
}
logOpts.Since = internal.SinceLogs
serviceName := c.Args().Get(1)
if serviceName == "" {
logrus.Debugf("tailing logs for all %s services", app.Recipe)
stackLogs(c, app, cl)
} else {
logrus.Debugf("tailing logs for %s", serviceName)
if err := tailServiceLogs(c, cl, app, serviceName); err != nil {
logrus.Fatal(err)
}
deployMeta, err := stack.IsDeployed(context.Background(), cl, stackName)
if err != nil {
log.Fatal(err)
}
return nil
if !deployMeta.IsDeployed {
log.Fatalf("%s is not deployed?", app.Name)
}
var serviceNames []string
if len(args) == 2 {
serviceNames = []string{args[1]}
}
f, err := app.Filters(true, false, serviceNames...)
if err != nil {
log.Fatal(err)
}
opts := logs.TailOpts{
AppName: app.Name,
Services: serviceNames,
StdErr: stdErr,
Since: sinceLogs,
Filters: f,
}
if err := logs.TailLogs(cl, opts); err != nil {
log.Fatal(err)
}
},
}
func tailServiceLogs(c *cli.Context, cl *dockerClient.Client, app config.App, serviceName string) error {
filters := filters.NewArgs()
filters.Add("name", fmt.Sprintf("%s_%s", app.StackName(), serviceName))
var (
stdErr bool
sinceLogs string
)
chosenService, err := service.GetService(context.Background(), cl, filters, internal.NoInput)
if err != nil {
logrus.Fatal(err)
}
func init() {
AppLogsCommand.Flags().BoolVarP(
&stdErr,
"stderr",
"s",
false,
"only tail stderr",
)
if internal.StdErrOnly {
logOpts.ShowStdout = false
}
logs, err := cl.ServiceLogs(context.Background(), chosenService.ID, logOpts)
if err != nil {
logrus.Fatal(err)
}
defer logs.Close()
_, err = io.Copy(os.Stdout, logs)
if err != nil && err != io.EOF {
logrus.Fatal(err)
}
return nil
AppLogsCommand.Flags().StringVarP(
&sinceLogs,
"since",
"S",
"",
"tail logs since YYYY-MM-DDTHH:MM:SSZ",
)
}

View File

@ -1,21 +1,37 @@
package app
import (
"fmt"
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/app"
appPkg "coopcloud.tech/abra/pkg/app"
"coopcloud.tech/abra/pkg/autocomplete"
"github.com/urfave/cli"
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/formatter"
"coopcloud.tech/abra/pkg/log"
recipePkg "coopcloud.tech/abra/pkg/recipe"
"coopcloud.tech/abra/pkg/secret"
"github.com/AlecAivazis/survey/v2"
"github.com/charmbracelet/lipgloss/table"
dockerClient "github.com/docker/docker/client"
"github.com/spf13/cobra"
)
var appNewDescription = `
Take a recipe and uses it to create a new app. This new app configuration is
stored in your ~/.abra directory under the appropriate server.
var appNewDescription = `Creates a new app from a default recipe.
This new app configuration is stored in your $ABRA_DIR directory under the
appropriate server.
This command does not deploy your app for you. You will need to run "abra app
deploy <domain>" to do so.
You can see what recipes are available (i.e. values for the <recipe> argument)
You can see what recipes are available (i.e. values for the [recipe] argument)
by running "abra recipe ls".
Recipe commit hashes are supported values for "[version]".
Passing the "--secrets/-S" flag will automatically generate secrets for your
app and store them encrypted at rest on the chosen target server. These
generated secrets are only visible at generation time, so please take care to
@ -23,24 +39,345 @@ store them somewhere safe.
You can use the "--pass/-P" to store these generated passwords locally in a
pass store (see passwordstore.org for more). The pass command must be available
on your $PATH.
`
on your $PATH.`
var appNewCommand = cli.Command{
Name: "new",
Aliases: []string{"n"},
Usage: "Create a new app",
Description: appNewDescription,
Flags: []cli.Flag{
internal.DebugFlag,
internal.NoInputFlag,
internal.NewAppServerFlag,
internal.DomainFlag,
internal.PassFlag,
internal.SecretsFlag,
var AppNewCommand = &cobra.Command{
Use: "new [recipe] [version] [flags]",
Aliases: []string{"n"},
Short: "Create a new app",
Long: appNewDescription,
Args: cobra.RangeArgs(0, 2),
ValidArgsFunction: func(
cmd *cobra.Command,
args []string,
toComplete string) ([]string, cobra.ShellCompDirective) {
switch l := len(args); l {
case 0:
return autocomplete.RecipeNameComplete()
case 1:
recipe := internal.ValidateRecipe(args, cmd.Name())
return autocomplete.RecipeVersionComplete(recipe.Name)
default:
return nil, cobra.ShellCompDirectiveDefault
}
},
Run: func(cmd *cobra.Command, args []string) {
recipe := internal.ValidateRecipe(args, cmd.Name())
if len(args) == 2 && internal.Chaos {
log.Fatal("cannot use [version] and --chaos together")
}
var recipeVersion string
if len(args) == 2 {
recipeVersion = args[1]
}
chaosVersion := config.CHAOS_DEFAULT
if internal.Chaos {
var err error
chaosVersion, err = recipe.ChaosVersion()
if err != nil {
log.Fatal(err)
}
recipeVersion = chaosVersion
} else {
if err := recipe.EnsureIsClean(); err != nil {
log.Fatal(err)
}
var recipeVersions recipePkg.RecipeVersions
if recipeVersion == "" {
var err error
recipeVersions, _, err = recipe.GetRecipeVersions()
if err != nil {
log.Fatal(err)
}
}
if len(recipeVersions) > 0 {
latest := recipeVersions[len(recipeVersions)-1]
for tag := range latest {
recipeVersion = tag
}
if _, err := recipe.EnsureVersion(recipeVersion); err != nil {
log.Fatal(err)
}
} else {
if err := recipe.EnsureLatest(); err != nil {
log.Fatal(err)
}
if recipeVersion == "" {
head, err := recipe.Head()
if err != nil {
log.Fatalf("failed to retrieve latest commit for %s: %s", recipe.Name, err)
}
recipeVersion = formatter.SmallSHA(head.String())
}
}
}
if err