Register SSM copy command for AWS feature
ober
f08e0fb95818872f56b4bf65aa055841af1941d6
--- a/build-jsh-cross.ss +++ b/build-jsh-cross.ss @@ -316,7 +316,7 @@ "logs/api" "logs/operations" "sns/api" "sns/operations" "sqs/api" "sqs/operations" - "ssm/api" "ssm/operations" "pssm" + "ssm/api" "ssm/operations" "pssm" "ssmcp" "rds/api" "rds/db-instances" "elbv2/api" "elbv2/operations" "cfn/api" "cfn/stacks" --- a/build-jsh-freebsd.ss +++ b/build-jsh-freebsd.ss @@ -387,7 +387,7 @@ "logs/api" "logs/operations" "sns/api" "sns/operations" "sqs/api" "sqs/operations" - "ssm/api" "ssm/operations" "pssm" + "ssm/api" "ssm/operations" "pssm" "ssmcp" "rds/api" "rds/db-instances" "elbv2/api" "elbv2/operations" "cfn/api" "cfn/stacks" @@ -859,6 +859,7 @@ "jerboa-aws/sns/api" "jerboa-aws/sns/operations" "jerboa-aws/sqs/api" "jerboa-aws/sqs/operations" "jerboa-aws/ssm/api" "jerboa-aws/ssm/operations" "jerboa-aws/pssm" + "jerboa-aws/ssmcp" "jerboa-aws/rds/api" "jerboa-aws/rds/db-instances" "jerboa-aws/elbv2/api" "jerboa-aws/elbv2/operations" "jerboa-aws/cfn/api" "jerboa-aws/cfn/stacks" --- a/build-jsh-macos.ss +++ b/build-jsh-macos.ss @@ -558,7 +558,7 @@ "logs/api" "logs/operations" "sns/api" "sns/operations" "sqs/api" "sqs/operations" - "ssm/api" "ssm/operations" "pssm" + "ssm/api" "ssm/operations" "pssm" "ssmcp" "rds/api" "rds/db-instances" "elbv2/api" "elbv2/operations" "cfn/api" "cfn/stacks" --- a/build-jsh-musl.ss +++ b/build-jsh-musl.ss @@ -331,7 +331,7 @@ "logs/api" "logs/operations" "sns/api" "sns/operations" "sqs/api" "sqs/operations" - "ssm/api" "ssm/operations" "pssm" + "ssm/api" "ssm/operations" "pssm" "ssmcp" "rds/api" "rds/db-instances" "elbv2/api" "elbv2/operations" "cfn/api" "cfn/stacks" @@ -769,6 +769,7 @@ "jerboa-aws/sns/api" "jerboa-aws/sns/operations" "jerboa-aws/sqs/api" "jerboa-aws/sqs/operations" "jerboa-aws/ssm/api" "jerboa-aws/ssm/operations" "jerboa-aws/pssm" + "jerboa-aws/ssmcp" "jerboa-aws/rds/api" "jerboa-aws/rds/db-instances" "jerboa-aws/elbv2/api" "jerboa-aws/elbv2/operations" "jerboa-aws/cfn/api" "jerboa-aws/cfn/stacks" --- a/features.def +++ b/features.def @@ -309,7 +309,7 @@ ;; AWS — AWS CLI integration ;; ═══════════════════════════════════════════════════════════════════ (aws - (description . "AWS CLI (,aws ,pssm)") + (description . "AWS CLI (,aws ,pssm ,ssmcp)") (modules "optional-static" "aws" "aws-static") (boot-so) (boot-so-jsh "aws") @@ -320,7 +320,7 @@ ;; (see src/jsh/aws.sls); force into the static closure so jerbuild ;; bundles them. Each entry is (<library> <one-export>) for retention. (bundle-libs ((jsh aws-static) aws-static-registered?)) - (commands "aws" "pssm")) + (commands "aws" "pssm" "ssmcp")) ;; ═══════════════════════════════════════════════════════════════════ ;; WORM — jerboa-wormhole text/file transfer --- a/jerboa-src/src/jsh/aws-static.ss +++ b/jerboa-src/src/jsh/aws-static.ss @@ -6,9 +6,11 @@ (import (rnrs) (jsh optional-static) (prefix (jerboa-aws cli main) %aws-cli-) - (prefix (jerboa-aws pssm) %aws-pssm-)) + (prefix (jerboa-aws pssm) %aws-pssm-) + (prefix (jerboa-aws ssmcp) %aws-ssmcp-)) (define aws-static-registered? #t) (optional-static-register! 'aws 'main %aws-cli-main) (optional-static-register! 'aws 'pssm-main %aws-pssm-pssm-main) +(optional-static-register! 'aws 'ssmcp-main %aws-ssmcp-ssmcp-main) --- a/jerboa-src/src/jsh/aws.ss +++ b/jerboa-src/src/jsh/aws.ss @@ -1,8 +1,8 @@ ;;; (jsh aws) — AWS comma commands for jsh ;;; -;;; Self-registers ",aws" and ",pssm" with the meta-command registry at +;;; Self-registers ",aws", ",pssm", and ",ssmcp" with the meta-command registry at ;;; module init. The actual implementations live in the jerboa-aws package -;;; ((jerboa-aws cli main) and (jerboa-aws pssm)). When that package is not +;;; ((jerboa-aws cli main), (jerboa-aws pssm), and (jerboa-aws ssmcp)). When that package is not ;;; built into the binary, the registered handlers report "not available" ;;; instead of crashing the shell. ;;; @@ -19,6 +19,7 @@ (define *aws-cli-main* #f) (define *pssm-main* #f) +(define *ssmcp-main* #f) (define *aws-loaded?* #f) (define *aws-load-attempted?* #f) @@ -29,11 +30,13 @@ [else (set! *aws-load-attempted?* #t) (let ([static-main (optional-static-ref 'aws 'main)] - [static-pssm (optional-static-ref 'aws 'pssm-main)]) - (if (and static-main static-pssm) + [static-pssm (optional-static-ref 'aws 'pssm-main)] + [static-ssmcp (optional-static-ref 'aws 'ssmcp-main)]) + (if (and static-main static-pssm static-ssmcp) (begin (set! *aws-cli-main* static-main) (set! *pssm-main* static-pssm) + (set! *ssmcp-main* static-ssmcp) (set! *aws-loaded?* #t) #t) (guard (e [#t #f]) @@ -41,6 +44,8 @@ (eval 'main (environment '(jerboa-aws cli main)))) (set! *pssm-main* (eval 'pssm-main (environment '(jerboa-aws pssm)))) + (set! *ssmcp-main* + (eval 'ssmcp-main (environment '(jerboa-aws ssmcp)))) (set! *aws-loaded?* #t) #t)))])) @@ -132,6 +137,16 @@ (tokenize-args args-string))]) (run-with-capture 'pssm *pssm-main* args))])) +(define (ssmcp-handler args-string) + (cond + [(not (aws-load!)) + (cons "ssmcp: jerboa-aws not available in this build" 1)] + [else + (let ([args (if (string=? args-string "") + '("--help") + (tokenize-args args-string))]) + (run-with-capture 'ssmcp *ssmcp-main* args))])) + ;; ---- Self-registration ---- ;; Runs at module init when jsh.ss imports (jsh aws). @@ -146,3 +161,5 @@ "AWS CLI (ec2, s3, sts, iam, ...) — provided by jerboa-aws package") (meta-register! "pssm" pssm-handler "parallel SSM across EC2 instances — provided by jerboa-aws package") +(meta-register! "ssmcp" ssmcp-handler + "recursive upload over AWS SSM — provided by jerboa-aws package") --- a/jsh.ss +++ b/jsh.ss @@ -5,7 +5,7 @@ (jsh limits) (jsh coreutils) ;; Optional packages — imported for their meta-register! init side-effects. - ;; (jsh aws) registers ,aws and ,pssm if jerboa-aws is built in; + ;; (jsh aws) registers ,aws, ,pssm, and ,ssmcp if jerboa-aws is built in; ;; otherwise the registered handlers report "not available" gracefully. (jsh aws) ;; (jsh pass) registers ,pass and ,pass-store. Both are gated on the @@ -253,7 +253,8 @@ " ,play --live node://host:port/cookie")) (help-lines-if 'aws '(" ,aws <service> <action> AWS CLI (ec2, s3, sts, iam, ...)" - " ,pssm <pattern> <cmd> parallel SSM across EC2 instances")) + " ,pssm <pattern> <cmd> parallel SSM across EC2 instances" + " ,ssmcp <pattern> SRC DST recursive upload over AWS SSM")) (help-lines-if 'yubikey '(" ,yk show YubiKey presence" " ,yubi show YubiKey presence" @@ -746,7 +747,7 @@ entries) (embed-file-set! "jsh-history" (get-output-string port)))))) -;; ,aws and ,pssm are registered by (jsh aws) at module init via meta-register!. +;; ,aws, ,pssm, and ,ssmcp are registered by (jsh aws) at module init via meta-register!. ;; If jerboa-aws is not built into this binary, the registered handlers ;; gracefully report "not available" instead of crashing the shell. ;; Reference the sentinel so Chez actually invokes the library — without a @@ -6419,7 +6420,7 @@ (else (fprintf (current-error-port) ",vault: unknown subcommand '~a'~n" subcmd) (cons "" 2)))))))) - ;; ,aws and ,pssm are dispatched via the meta-command registry — + ;; ,aws, ,pssm, and ,ssmcp are dispatched via the meta-command registry — ;; (jsh aws) self-registers them at module init. ;; Normal Jerboa eval (with special handling for with-sandbox) (#t --- a/test/test-features-commands.sh +++ b/test/test-features-commands.sh @@ -102,7 +102,7 @@ while read -r feature_name cmds; do done done < "$TMPFILE" -# Explicit positive assertions for aws and pssm when aws is enabled +# Explicit positive assertions for aws, pssm, and ssmcp when aws is enabled echo "" echo "--- Explicit AWS assertions ---" if feature_enabled "aws"; then @@ -120,8 +120,15 @@ if feature_enabled "aws"; then echo "FAIL: ,pssm --help should work (aws enabled)" FAILURES=$((FAILURES + 1)) fi + if "$JSH" -c ',ssmcp --help' >/dev/null 2>&1; then + echo "PASS: ,ssmcp --help (aws enabled, explicit check)" + PASSES=$((PASSES + 1)) + else + echo "FAIL: ,ssmcp --help should work (aws enabled)" + FAILURES=$((FAILURES + 1)) + fi else - for cmd in aws pssm; do + for cmd in aws pssm ssmcp; do if output=$("$JSH" -c ",${cmd}" 2>&1); then rc=0; else rc=$?; fi if [ "$rc" -ne 0 ] && echo "$output" | grep -qi "not available"; then echo "PASS: ,$cmd reports 'not available' (aws disabled)"