From 447ac524787b64732e5aafc0e5ac5ae5053dd51c Mon Sep 17 00:00:00 2001 From: ditahkk Date: Sun, 28 Dec 2025 08:53:17 -0500 Subject: [PATCH 1/6] fix: Update AWS SDK dependencies and improve signal handling in SSM manager --- ztictl/go.mod | 52 ++++++++++++++++---------------- ztictl/go.sum | 54 ++++++++++++++++++++++++++++++++++ ztictl/internal/ssm/manager.go | 16 ++++++++++ 3 files changed, 97 insertions(+), 25 deletions(-) diff --git a/ztictl/go.mod b/ztictl/go.mod index d32ecba..1abcfa5 100644 --- a/ztictl/go.mod +++ b/ztictl/go.mod @@ -5,45 +5,46 @@ go 1.24.5 replace github.com/ktr0731/go-fuzzyfinder => github.com/zsoftly/go-fuzzyfinder v0.0.0-20251011215817-623ae668e846 require ( - github.com/aws/aws-sdk-go-v2 v1.39.6 - github.com/aws/aws-sdk-go-v2/config v1.31.17 - github.com/aws/aws-sdk-go-v2/service/ec2 v1.262.0 - github.com/aws/aws-sdk-go-v2/service/iam v1.49.2 - github.com/aws/aws-sdk-go-v2/service/s3 v1.90.0 - github.com/aws/aws-sdk-go-v2/service/ssm v1.66.4 - github.com/aws/aws-sdk-go-v2/service/sso v1.30.1 - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5 - github.com/aws/aws-sdk-go-v2/service/sts v1.39.1 + github.com/aws/aws-sdk-go-v2 v1.41.0 + github.com/aws/aws-sdk-go-v2/config v1.32.6 + github.com/aws/aws-sdk-go-v2/service/ec2 v1.279.0 + github.com/aws/aws-sdk-go-v2/service/iam v1.53.1 + github.com/aws/aws-sdk-go-v2/service/s3 v1.95.0 + github.com/aws/aws-sdk-go-v2/service/ssm v1.67.7 + github.com/aws/aws-sdk-go-v2/service/sso v1.30.8 + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 + github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 github.com/fatih/color v1.18.0 github.com/ktr0731/go-fuzzyfinder v0.9.0 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c - github.com/spf13/cobra v1.10.1 + github.com/spf13/cobra v1.10.2 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.11.1 - golang.org/x/term v0.36.0 + golang.org/x/term v0.38.0 gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/atotto/clipboard v0.1.4 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.18.21 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.6 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 // indirect - github.com/aws/smithy-go v1.23.2 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.16 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.16 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 // indirect + github.com/aws/smithy-go v1.24.0 // indirect github.com/clipperhouse/stringish v0.1.1 // indirect github.com/clipperhouse/uax29/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/gdamore/encoding v1.0.1 // indirect - github.com/gdamore/tcell/v2 v2.9.0 // indirect + github.com/gdamore/tcell/v2 v2.13.5 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/ktr0731/go-ansisgr v0.1.0 // indirect @@ -55,6 +56,7 @@ require ( github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/sagikazarmark/locafero v0.12.0 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect @@ -62,7 +64,7 @@ require ( github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/sys v0.37.0 // indirect - golang.org/x/text v0.30.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect ) diff --git a/ztictl/go.sum b/ztictl/go.sum index 3d78ff9..baed37c 100644 --- a/ztictl/go.sum +++ b/ztictl/go.sum @@ -2,46 +2,88 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+Xsqk= github.com/aws/aws-sdk-go-v2 v1.39.6/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE= +github.com/aws/aws-sdk-go-v2 v1.41.0 h1:tNvqh1s+v0vFYdA1xq0aOJH+Y5cRyZ5upu6roPgPKd4= +github.com/aws/aws-sdk-go-v2 v1.41.0/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 h1:DHctwEM8P8iTXFxC/QK0MRjwEpWQeM9yzidCRjldUz0= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3/go.mod h1:xdCzcZEtnSTKVDOmUZs4l/j3pSV6rpo1WXl5ugNsL8Y= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 h1:489krEF9xIGkOaaX3CE/Be2uWjiXrkCH6gUX+bZA/BU= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4/go.mod h1:IOAPF6oT9KCsceNTvvYMNHy0+kMF8akOjeDvPENWxp4= github.com/aws/aws-sdk-go-v2/config v1.31.17 h1:QFl8lL6RgakNK86vusim14P2k8BFSxjvUkcWLDjgz9Y= github.com/aws/aws-sdk-go-v2/config v1.31.17/go.mod h1:V8P7ILjp/Uef/aX8TjGk6OHZN6IKPM5YW6S78QnRD5c= +github.com/aws/aws-sdk-go-v2/config v1.32.6 h1:hFLBGUKjmLAekvi1evLi5hVvFQtSo3GYwi+Bx4lpJf8= +github.com/aws/aws-sdk-go-v2/config v1.32.6/go.mod h1:lcUL/gcd8WyjCrMnxez5OXkO3/rwcNmvfno62tnXNcI= github.com/aws/aws-sdk-go-v2/credentials v1.18.21 h1:56HGpsgnmD+2/KpG0ikvvR8+3v3COCwaF4r+oWwOeNA= github.com/aws/aws-sdk-go-v2/credentials v1.18.21/go.mod h1:3YELwedmQbw7cXNaII2Wywd+YY58AmLPwX4LzARgmmA= +github.com/aws/aws-sdk-go-v2/credentials v1.19.6 h1:F9vWao2TwjV2MyiyVS+duza0NIRtAslgLUM0vTA1ZaE= +github.com/aws/aws-sdk-go-v2/credentials v1.19.6/go.mod h1:SgHzKjEVsdQr6Opor0ihgWtkWdfRAIwxYzSJ8O85VHY= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 h1:T1brd5dR3/fzNFAQch/iBKeX07/ffu/cLu+q+RuzEWk= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13/go.mod h1:Peg/GBAQ6JDt+RoBf4meB1wylmAipb7Kg2ZFakZTlwk= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 h1:80+uETIWS1BqjnN9uJ0dBUaETh+P1XwFy5vwHwK5r9k= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16/go.mod h1:wOOsYuxYuB/7FlnVtzeBYRcjSRtQpAW0hCP7tIULMwo= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 h1:a+8/MLcWlIxo1lF9xaGt3J/u3yOZx+CdSveSNwjhD40= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13/go.mod h1:oGnKwIYZ4XttyU2JWxFrwvhF6YKiK/9/wmE3v3Iu9K8= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 h1:rgGwPzb82iBYSvHMHXc8h9mRoOUBZIGFgKb9qniaZZc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16/go.mod h1:L/UxsGeKpGoIj6DxfhOWHWQ/kGKcd4I1VncE4++IyKA= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 h1:HBSI2kDkMdWz4ZM7FjwE7e/pWDEZ+nR95x8Ztet1ooY= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13/go.mod h1:YE94ZoDArI7awZqJzBAZ3PDD2zSfuP7w6P2knOzIn8M= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 h1:1jtGzuV7c82xnqOVfx2F0xmJcOw5374L7N6juGW6x6U= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16/go.mod h1:M2E5OQf+XLe+SZGmmpaI2yy+J326aFf6/+54PoxSANc= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13 h1:eg/WYAa12vqTphzIdWMzqYRVKKnCboVPRlvaybNCqPA= github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13/go.mod h1:/FDdxWhz1486obGrKKC1HONd7krpk38LBt+dutLcN9k= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.16 h1:CjMzUs78RDDv4ROu3JnJn/Ig1r6ZD7/T2DXLLRpejic= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.16/go.mod h1:uVW4OLBqbJXSHJYA9svT9BluSvvwbzLQ2Crf6UPzR3c= github.com/aws/aws-sdk-go-v2/service/ec2 v1.262.0 h1:5qBb1XV/D18qtCHd3bmmxoVglI+fZ4QWuS/EB8kIXYQ= github.com/aws/aws-sdk-go-v2/service/ec2 v1.262.0/go.mod h1:NDdDLLW5PtLLXN661gKcvJvqAH5OBXsfhMlmKVu1/pY= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.279.0 h1:o7eJKe6VYAnqERPlLAvDW5VKXV6eTKv1oxTpMoDP378= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.279.0/go.mod h1:Wg68QRgy2gEGGdmTPU/UbVpdv8sM14bUZmF64KFwAsY= github.com/aws/aws-sdk-go-v2/service/iam v1.49.2 h1:XeF6yEMX4/FxoSHCE1VNMOZ0t+mGnf/onqVe9dDVAlQ= github.com/aws/aws-sdk-go-v2/service/iam v1.49.2/go.mod h1:cuEMbL1mNtO1sUyT+DYDNIA8Y7aJG1oIdgHqUk29Uzk= +github.com/aws/aws-sdk-go-v2/service/iam v1.53.1 h1:xNCUk9XN6Pa9PyzbEfzgRpvEIVlqtth402yjaWvNMu4= +github.com/aws/aws-sdk-go-v2/service/iam v1.53.1/go.mod h1:GNQZL4JRSGH6L0/SNGOtffaB1vmlToYp3KtcUIB0NhI= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 h1:x2Ibm/Af8Fi+BH+Hsn9TXGdT+hKbDd5XOTZxTMxDk7o= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3/go.mod h1:IW1jwyrQgMdhisceG8fQLmQIydcT/jWY21rFhzgaKwo= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4 h1:NvMjwvv8hpGUILarKw7Z4Q0w1H9anXKsesMxtw++MA4= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4/go.mod h1:455WPHSwaGj2waRSpQp7TsnpOnBfw8iDfPfbwl7KPJE= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.7 h1:DIBqIrJ7hv+e4CmIk2z3pyKT+3B6qVMgRsawHiR3qso= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.7/go.mod h1:vLm00xmBke75UmpNvOcZQ/Q30ZFjbczeLFqGx5urmGo= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 h1:kDqdFvMY4AtKoACfzIGD8A0+hbT41KTKF//gq7jITfM= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13/go.mod h1:lmKuogqSU3HzQCwZ9ZtcqOc5XGMqtDK7OIc2+DxiUEg= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 h1:oHjJHeUy0ImIV0bsrX0X91GkV5nJAyv1l1CC9lnO0TI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16/go.mod h1:iRSNGgOYmiYwSCXxXaKb9HfOEj40+oTKn8pTxMlYkRM= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 h1:zhBJXdhWIFZ1acfDYIhu4+LCzdUS2Vbcum7D01dXlHQ= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13/go.mod h1:JaaOeCE368qn2Hzi3sEzY6FgAZVCIYcC2nwbro2QCh8= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.16 h1:NSbvS17MlI2lurYgXnCOLvCFX38sBW4eiVER7+kkgsU= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.16/go.mod h1:SwT8Tmqd4sA6G1qaGdzWCJN99bUmPGHfRwwq3G5Qb+A= github.com/aws/aws-sdk-go-v2/service/s3 v1.90.0 h1:ef6gIJR+xv/JQWwpa5FYirzoQctfSJm7tuDe3SZsUf8= github.com/aws/aws-sdk-go-v2/service/s3 v1.90.0/go.mod h1:+wArOOrcHUevqdto9k1tKOF5++YTe9JEcPSc9Tx2ZSw= +github.com/aws/aws-sdk-go-v2/service/s3 v1.95.0 h1:MIWra+MSq53CFaXXAywB2qg9YvVZifkk6vEGl/1Qor0= +github.com/aws/aws-sdk-go-v2/service/s3 v1.95.0/go.mod h1:79S2BdqCJpScXZA2y+cpZuocWsjGjJINyXnOsf5DTz8= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 h1:HpI7aMmJ+mm1wkSHIA2t5EaFFv5EFYXePW30p1EIrbQ= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.4/go.mod h1:C5RdGMYGlfM0gYq/tifqgn4EbyX99V15P2V3R+VHbQU= github.com/aws/aws-sdk-go-v2/service/ssm v1.66.4 h1:UmkF0ipNy0Ps6csJl/ZRJ3K+DWe9q0A7LT3xfxoHbgg= github.com/aws/aws-sdk-go-v2/service/ssm v1.66.4/go.mod h1:uNHuYAQazkHqpD+hVomA2+eDSuKJzerno7Fnha6N6/Y= +github.com/aws/aws-sdk-go-v2/service/ssm v1.67.7 h1:0q42w8/mywPCzQD1IoWIBUCYfBJc5+fLwtZNpHffBSM= +github.com/aws/aws-sdk-go-v2/service/ssm v1.67.7/go.mod h1:urlU9nfKJEfi0+8T9luB3f3Y0UnomH/yxI7tTrfH9es= github.com/aws/aws-sdk-go-v2/service/sso v1.30.1 h1:0JPwLz1J+5lEOfy/g0SURC9cxhbQ1lIMHMa+AHZSzz0= github.com/aws/aws-sdk-go-v2/service/sso v1.30.1/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.8 h1:aM/Q24rIlS3bRAhTyFurowU8A0SMyGDtEOY/l/s/1Uw= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.8/go.mod h1:+fWt2UHSb4kS7Pu8y+BMBvJF0EWx+4H0hzNwtDNRTrg= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5 h1:OWs0/j2UYR5LOGi88sD5/lhN6TDLG6SfA7CqsQO9zF0= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 h1:AHDr0DaHIAo8c9t1emrzAlVDFp+iMMKnPdYy6XO4MCE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12/go.mod h1:GQ73XawFFiWxyWXMHWfhiomvP3tXtdNar/fi8z18sx0= github.com/aws/aws-sdk-go-v2/service/sts v1.39.1 h1:mLlUgHn02ue8whiR4BmxxGJLR2gwU6s6ZzJ5wDamBUs= github.com/aws/aws-sdk-go-v2/service/sts v1.39.1/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 h1:SciGFVNZ4mHdm7gpD1dgZYnCuVdX1s+lFTg4+4DOy70= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.5/go.mod h1:iW40X4QBmUxdP+fZNOpfmkdMZqsovezbAeO+Ubiv2pk= github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM= github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= +github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= +github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= @@ -59,6 +101,8 @@ github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uh github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo= github.com/gdamore/tcell/v2 v2.9.0 h1:N6t+eqK7/xwtRPwxzs1PXeRWnm0H9l02CrgJ7DLn1ys= github.com/gdamore/tcell/v2 v2.9.0/go.mod h1:8/ZoqM9rxzYphT9tH/9LnunhV9oPBqwS8WHGYm5nrmo= +github.com/gdamore/tcell/v2 v2.13.5 h1:YvWYCSr6gr2Ovs84dXbZLjDuOfQchhj8buOEqY52rpA= +github.com/gdamore/tcell/v2 v2.13.5/go.mod h1:+Wfe208WDdB7INEtCsNrAN6O2m+wsTPk1RAovjaILlo= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= @@ -92,6 +136,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -103,6 +149,8 @@ github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -140,11 +188,15 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -152,6 +204,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= diff --git a/ztictl/internal/ssm/manager.go b/ztictl/internal/ssm/manager.go index 47f4115..26123cf 100644 --- a/ztictl/internal/ssm/manager.go +++ b/ztictl/internal/ssm/manager.go @@ -8,11 +8,13 @@ import ( "fmt" "os" "os/exec" + "os/signal" "path/filepath" "regexp" "runtime" "strings" "sync" + "syscall" "time" appconfig "ztictl/internal/config" @@ -132,6 +134,13 @@ func (m *Manager) StartSession(ctx context.Context, instanceIdentifier, region s cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr + // Ignore SIGINT in the parent process so it passes through to the AWS CLI subprocess. + // This fixes Ctrl+C handling in WSL2/Windows Terminal where the signal would otherwise + // kill the parent Go process and disconnect the SSM session. + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) + defer signal.Stop(sigChan) + if err := cmd.Run(); err != nil { return errors.NewSSMError("failed to start session", err) } @@ -396,6 +405,13 @@ func (m *Manager) ForwardPort(ctx context.Context, instanceIdentifier, region st fmt.Printf("Port forwarding: localhost:%d -> %s:%d\n", localPort, instanceID, remotePort) fmt.Printf("Press Ctrl+C to stop port forwarding\n\n") + // Ignore SIGINT in the parent process so it passes through to the AWS CLI subprocess. + // This fixes Ctrl+C handling in WSL2/Windows Terminal where the signal would otherwise + // kill the parent Go process and disconnect the port forwarding session. + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) + defer signal.Stop(sigChan) + if err := cmd.Run(); err != nil { return errors.NewSSMError("failed to start port forwarding", err) } From 11c71740de92a440301ecf31654634b5c61c7f10 Mon Sep 17 00:00:00 2001 From: ditahkk Date: Sun, 28 Dec 2025 09:34:05 -0500 Subject: [PATCH 2/6] feat: add RDS client support to client pool - Updated go.mod to include the RDS service from AWS SDK v2. - Modified client_pool.go to initialize and expose an RDS client in the ClientPool struct. - Added a new method GetRDSClient to retrieve the RDS client from the pool. --- ztictl/cmd/ztictl/rds.go | 29 ++ ztictl/cmd/ztictl/rds_power.go | 350 +++++++++++++++ ztictl/cmd/ztictl/rds_test.go | 685 +++++++++++++++++++++++++++++ ztictl/cmd/ztictl/ssm.go | 10 +- ztictl/cmd/ztictl/ssm_ssh.go | 426 ++++++++++++++++++ ztictl/cmd/ztictl/ssm_ssh_test.go | 580 ++++++++++++++++++++++++ ztictl/go.mod | 1 + ztictl/go.sum | 52 +-- ztictl/internal/ssm/client_pool.go | 11 + 9 files changed, 2092 insertions(+), 52 deletions(-) create mode 100644 ztictl/cmd/ztictl/rds.go create mode 100644 ztictl/cmd/ztictl/rds_power.go create mode 100644 ztictl/cmd/ztictl/rds_test.go create mode 100644 ztictl/cmd/ztictl/ssm_ssh.go create mode 100644 ztictl/cmd/ztictl/ssm_ssh_test.go diff --git a/ztictl/cmd/ztictl/rds.go b/ztictl/cmd/ztictl/rds.go new file mode 100644 index 0000000..046d136 --- /dev/null +++ b/ztictl/cmd/ztictl/rds.go @@ -0,0 +1,29 @@ +package main + +import ( + "github.com/spf13/cobra" +) + +// RDS command - Main orchestrator for RDS operations +var rdsCmd = &cobra.Command{ + Use: "rds", + Short: "RDS operations", + Long: `Manage AWS RDS (Relational Database Service) operations including +starting, stopping, rebooting, and listing database instances. + +Examples: + ztictl rds list # List RDS instances + ztictl rds start # Start a stopped RDS instance + ztictl rds stop # Stop a running RDS instance + ztictl rds reboot # Reboot an RDS instance`, +} + +func init() { + rootCmd.AddCommand(rdsCmd) + + // Add subcommands + rdsCmd.AddCommand(rdsListCmd) // rds_power.go + rdsCmd.AddCommand(rdsStartCmd) // rds_power.go + rdsCmd.AddCommand(rdsStopCmd) // rds_power.go + rdsCmd.AddCommand(rdsRebootCmd) // rds_power.go +} diff --git a/ztictl/cmd/ztictl/rds_power.go b/ztictl/cmd/ztictl/rds_power.go new file mode 100644 index 0000000..4418c12 --- /dev/null +++ b/ztictl/cmd/ztictl/rds_power.go @@ -0,0 +1,350 @@ +package main + +import ( + "context" + "fmt" + "os" + "strings" + "time" + + "ztictl/internal/ssm" + "ztictl/pkg/colors" + "ztictl/pkg/logging" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/rds" + "github.com/spf13/cobra" +) + +// rdsListCmd represents the rds list command +var rdsListCmd = &cobra.Command{ + Use: "list", + Short: "List RDS instances", + Long: `List all RDS database instances in the specified region. +Region supports shortcuts: cac1 (ca-central-1), use1 (us-east-1), euw1 (eu-west-1), etc. + +Examples: + ztictl rds list --region cac1 + ztictl rds list -r ca-central-1`, + Run: func(cmd *cobra.Command, args []string) { + regionCode, _ := cmd.Flags().GetString("region") + + if err := performRDSList(regionCode); err != nil { + logging.LogError("RDS list failed: %v", err) + os.Exit(1) + } + }, +} + +// rdsStartCmd represents the rds start command +var rdsStartCmd = &cobra.Command{ + Use: "start ", + Short: "Start a stopped RDS instance", + Long: `Start a stopped RDS database instance. +Region supports shortcuts: cac1 (ca-central-1), use1 (us-east-1), euw1 (eu-west-1), etc. + +Note: Starting an RDS instance can take several minutes. + +Examples: + ztictl rds start my-database --region cac1 + ztictl rds start prod-db -r ca-central-1 + ztictl rds start my-database --region cac1 --wait`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + regionCode, _ := cmd.Flags().GetString("region") + wait, _ := cmd.Flags().GetBool("wait") + dbIdentifier := args[0] + + if err := performRDSStart(regionCode, dbIdentifier, wait); err != nil { + logging.LogError("RDS start failed: %v", err) + os.Exit(1) + } + }, +} + +// rdsStopCmd represents the rds stop command +var rdsStopCmd = &cobra.Command{ + Use: "stop ", + Short: "Stop a running RDS instance", + Long: `Stop a running RDS database instance. +Region supports shortcuts: cac1 (ca-central-1), use1 (us-east-1), euw1 (eu-west-1), etc. + +Note: + - Stopping an RDS instance can take several minutes. + - RDS instances can only be stopped for up to 7 days before they are automatically restarted. + - Some instance types (e.g., Multi-AZ deployments with SQL Server) cannot be stopped. + +Examples: + ztictl rds stop my-database --region cac1 + ztictl rds stop dev-db -r ca-central-1 + ztictl rds stop my-database --region cac1 --wait`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + regionCode, _ := cmd.Flags().GetString("region") + wait, _ := cmd.Flags().GetBool("wait") + dbIdentifier := args[0] + + if err := performRDSStop(regionCode, dbIdentifier, wait); err != nil { + logging.LogError("RDS stop failed: %v", err) + os.Exit(1) + } + }, +} + +// rdsRebootCmd represents the rds reboot command +var rdsRebootCmd = &cobra.Command{ + Use: "reboot ", + Short: "Reboot an RDS instance", + Long: `Reboot an RDS database instance. +Region supports shortcuts: cac1 (ca-central-1), use1 (us-east-1), euw1 (eu-west-1), etc. + +Note: Rebooting an RDS instance will cause a brief outage. + +Examples: + ztictl rds reboot my-database --region cac1 + ztictl rds reboot prod-db -r ca-central-1 + ztictl rds reboot my-database --region cac1 --force-failover`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + regionCode, _ := cmd.Flags().GetString("region") + forceFailover, _ := cmd.Flags().GetBool("force-failover") + wait, _ := cmd.Flags().GetBool("wait") + dbIdentifier := args[0] + + if err := performRDSReboot(regionCode, dbIdentifier, forceFailover, wait); err != nil { + logging.LogError("RDS reboot failed: %v", err) + os.Exit(1) + } + }, +} + +// performRDSList lists all RDS instances in the region +func performRDSList(regionCode string) error { + region := resolveRegion(regionCode) + ctx := context.Background() + + clientPool := ssm.NewClientPool() + rdsClient, err := clientPool.GetRDSClient(ctx, region) + if err != nil { + return fmt.Errorf("failed to create RDS client: %w", err) + } + + logging.LogInfo("Listing RDS instances in region: %s", region) + + resp, err := rdsClient.DescribeDBInstances(ctx, &rds.DescribeDBInstancesInput{}) + if err != nil { + return fmt.Errorf("failed to describe RDS instances: %w", err) + } + + if len(resp.DBInstances) == 0 { + fmt.Printf("\nNo RDS instances found in region: %s\n", region) + return nil + } + + fmt.Printf("\n") + colors.PrintHeader("RDS Instances in %s:\n", region) + colors.PrintHeader("==============================\n") + colors.PrintHeader("%-30s %-15s %-15s %-20s %s\n", "DB Identifier", "Status", "Engine", "Class", "Endpoint") + colors.PrintHeader("%s\n", strings.Repeat("-", 110)) + + for _, db := range resp.DBInstances { + endpoint := "-" + if db.Endpoint != nil && db.Endpoint.Address != nil { + endpoint = fmt.Sprintf("%s:%d", aws.ToString(db.Endpoint.Address), aws.ToInt32(db.Endpoint.Port)) + } + + status := aws.ToString(db.DBInstanceStatus) + coloredStatus := colorizeRDSStatus(status) + + fmt.Printf("%-30s %-15s %-15s %-20s %s\n", + aws.ToString(db.DBInstanceIdentifier), + coloredStatus, + aws.ToString(db.Engine), + aws.ToString(db.DBInstanceClass), + endpoint, + ) + } + + fmt.Printf("\nTotal: %d instance(s)\n", len(resp.DBInstances)) + return nil +} + +// performRDSStart starts a stopped RDS instance +func performRDSStart(regionCode, dbIdentifier string, wait bool) error { + region := resolveRegion(regionCode) + ctx := context.Background() + + clientPool := ssm.NewClientPool() + rdsClient, err := clientPool.GetRDSClient(ctx, region) + if err != nil { + return fmt.Errorf("failed to create RDS client: %w", err) + } + + logging.LogInfo("Starting RDS instance %s in region: %s", dbIdentifier, region) + fmt.Printf("Starting RDS instance: %s\n", dbIdentifier) + + _, err = rdsClient.StartDBInstance(ctx, &rds.StartDBInstanceInput{ + DBInstanceIdentifier: aws.String(dbIdentifier), + }) + if err != nil { + return fmt.Errorf("failed to start RDS instance: %w", err) + } + + colors.PrintSuccess("✓ Start command sent for RDS instance: %s\n", dbIdentifier) + + if wait { + fmt.Printf("Waiting for instance to become available...\n") + if err := waitForRDSStatus(ctx, rdsClient, dbIdentifier, "available"); err != nil { + return err + } + colors.PrintSuccess("✓ RDS instance %s is now available\n", dbIdentifier) + } else { + fmt.Printf("Note: Instance startup may take several minutes. Use --wait to wait for completion.\n") + } + + return nil +} + +// performRDSStop stops a running RDS instance +func performRDSStop(regionCode, dbIdentifier string, wait bool) error { + region := resolveRegion(regionCode) + ctx := context.Background() + + clientPool := ssm.NewClientPool() + rdsClient, err := clientPool.GetRDSClient(ctx, region) + if err != nil { + return fmt.Errorf("failed to create RDS client: %w", err) + } + + logging.LogInfo("Stopping RDS instance %s in region: %s", dbIdentifier, region) + fmt.Printf("Stopping RDS instance: %s\n", dbIdentifier) + + _, err = rdsClient.StopDBInstance(ctx, &rds.StopDBInstanceInput{ + DBInstanceIdentifier: aws.String(dbIdentifier), + }) + if err != nil { + return fmt.Errorf("failed to stop RDS instance: %w", err) + } + + colors.PrintSuccess("✓ Stop command sent for RDS instance: %s\n", dbIdentifier) + + if wait { + fmt.Printf("Waiting for instance to stop...\n") + if err := waitForRDSStatus(ctx, rdsClient, dbIdentifier, "stopped"); err != nil { + return err + } + colors.PrintSuccess("✓ RDS instance %s is now stopped\n", dbIdentifier) + } else { + fmt.Printf("Note: Instance shutdown may take several minutes. Use --wait to wait for completion.\n") + fmt.Printf("Warning: RDS instances auto-restart after 7 days of being stopped.\n") + } + + return nil +} + +// performRDSReboot reboots an RDS instance +func performRDSReboot(regionCode, dbIdentifier string, forceFailover, wait bool) error { + region := resolveRegion(regionCode) + ctx := context.Background() + + clientPool := ssm.NewClientPool() + rdsClient, err := clientPool.GetRDSClient(ctx, region) + if err != nil { + return fmt.Errorf("failed to create RDS client: %w", err) + } + + logging.LogInfo("Rebooting RDS instance %s in region: %s (force-failover: %v)", dbIdentifier, region, forceFailover) + fmt.Printf("Rebooting RDS instance: %s\n", dbIdentifier) + + if forceFailover { + fmt.Printf("Force failover enabled (Multi-AZ only)\n") + } + + _, err = rdsClient.RebootDBInstance(ctx, &rds.RebootDBInstanceInput{ + DBInstanceIdentifier: aws.String(dbIdentifier), + ForceFailover: aws.Bool(forceFailover), + }) + if err != nil { + return fmt.Errorf("failed to reboot RDS instance: %w", err) + } + + colors.PrintSuccess("✓ Reboot command sent for RDS instance: %s\n", dbIdentifier) + + if wait { + fmt.Printf("Waiting for instance to become available...\n") + // First wait for rebooting status, then available + if err := waitForRDSStatus(ctx, rdsClient, dbIdentifier, "available"); err != nil { + return err + } + colors.PrintSuccess("✓ RDS instance %s is now available\n", dbIdentifier) + } else { + fmt.Printf("Note: Reboot may take several minutes. Use --wait to wait for completion.\n") + } + + return nil +} + +// waitForRDSStatus waits for an RDS instance to reach the specified status +func waitForRDSStatus(ctx context.Context, rdsClient *rds.Client, dbIdentifier, targetStatus string) error { + maxWait := 30 * time.Minute + pollInterval := 30 * time.Second + deadline := time.Now().Add(maxWait) + + for time.Now().Before(deadline) { + resp, err := rdsClient.DescribeDBInstances(ctx, &rds.DescribeDBInstancesInput{ + DBInstanceIdentifier: aws.String(dbIdentifier), + }) + if err != nil { + return fmt.Errorf("failed to check instance status: %w", err) + } + + if len(resp.DBInstances) == 0 { + return fmt.Errorf("instance %s not found", dbIdentifier) + } + + status := aws.ToString(resp.DBInstances[0].DBInstanceStatus) + fmt.Printf(" Current status: %s\n", status) + + if status == targetStatus { + return nil + } + + time.Sleep(pollInterval) + } + + return fmt.Errorf("timeout waiting for instance to reach %s status", targetStatus) +} + +// colorizeRDSStatus returns a colored status string for RDS status +func colorizeRDSStatus(status string) string { + switch status { + case "available": + return colors.ColorSuccess("%s", status) + case "stopped": + return colors.ColorWarning("%s", status) + case "starting", "stopping", "rebooting", "modifying", "backing-up": + return colors.ColorData("%s", status) + case "failed", "incompatible-restore", "incompatible-network": + return colors.ColorError("%s", status) + default: + return status + } +} + +func init() { + // List command flags + rdsListCmd.Flags().StringP("region", "r", "", "AWS region or shortcode (cac1, use1, euw1, etc.) - default from config") + + // Start command flags + rdsStartCmd.Flags().StringP("region", "r", "", "AWS region or shortcode (cac1, use1, euw1, etc.) - default from config") + rdsStartCmd.Flags().BoolP("wait", "w", false, "Wait for the instance to become available") + + // Stop command flags + rdsStopCmd.Flags().StringP("region", "r", "", "AWS region or shortcode (cac1, use1, euw1, etc.) - default from config") + rdsStopCmd.Flags().BoolP("wait", "w", false, "Wait for the instance to stop") + + // Reboot command flags + rdsRebootCmd.Flags().StringP("region", "r", "", "AWS region or shortcode (cac1, use1, euw1, etc.) - default from config") + rdsRebootCmd.Flags().BoolP("force-failover", "f", false, "Force a failover for Multi-AZ instances") + rdsRebootCmd.Flags().BoolP("wait", "w", false, "Wait for the instance to become available") +} diff --git a/ztictl/cmd/ztictl/rds_test.go b/ztictl/cmd/ztictl/rds_test.go new file mode 100644 index 0000000..72f229c --- /dev/null +++ b/ztictl/cmd/ztictl/rds_test.go @@ -0,0 +1,685 @@ +package main + +import ( + "bytes" + "strings" + "testing" + + "github.com/spf13/cobra" +) + +func TestRdsCmd(t *testing.T) { + tests := []struct { + name string + args []string + wantErr bool + contains string + }{ + { + name: "RDS help", + args: []string{"--help"}, + wantErr: false, + contains: "Manage AWS RDS", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create a fresh command to avoid state pollution + cmd := &cobra.Command{ + Use: rdsCmd.Use, + Short: rdsCmd.Short, + Long: rdsCmd.Long, + } + + buf := new(bytes.Buffer) + cmd.SetOut(buf) + cmd.SetErr(buf) + cmd.SetArgs(tt.args) + + err := cmd.Execute() + + if (err != nil) != tt.wantErr { + t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr) + } + + if tt.contains != "" { + output := buf.String() + if !strings.Contains(output, tt.contains) { + t.Errorf("Execute() output should contain %v, got %v", tt.contains, output) + } + } + }) + } +} + +func TestRdsCmdStructure(t *testing.T) { + if rdsCmd.Use != "rds" { + t.Errorf("Expected Use to be 'rds', got %s", rdsCmd.Use) + } + + if rdsCmd.Short == "" { + t.Error("Command should have a short description") + } + + if rdsCmd.Long == "" { + t.Error("Command should have a long description") + } + + // Verify subcommands are registered + subcommands := rdsCmd.Commands() + expectedSubcommands := []string{"list", "start", "stop", "reboot"} + + for _, expected := range expectedSubcommands { + found := false + for _, cmd := range subcommands { + if cmd.Name() == expected { + found = true + break + } + } + if !found { + t.Errorf("Expected subcommand %s not found", expected) + } + } +} + +func TestRdsListCmd(t *testing.T) { + tests := []struct { + name string + args []string + wantErr bool + contains string + }{ + { + name: "List help", + args: []string{"--help"}, + wantErr: false, + contains: "List all RDS database instances", + }, + { + name: "List with region flag", + args: []string{"--region", "cac1"}, + wantErr: false, + }, + { + name: "List with short region flag", + args: []string{"-r", "ca-central-1"}, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cmd := &cobra.Command{ + Use: rdsListCmd.Use, + Short: rdsListCmd.Short, + Long: rdsListCmd.Long, + Run: func(cmd *cobra.Command, args []string) { + // Mock list functionality + regionCode, _ := cmd.Flags().GetString("region") + if regionCode == "cac1" { + // Valid shortcode + } + }, + } + + cmd.Flags().StringP("region", "r", "", "AWS region") + + buf := new(bytes.Buffer) + cmd.SetOut(buf) + cmd.SetErr(buf) + cmd.SetArgs(tt.args) + + err := cmd.Execute() + + if (err != nil) != tt.wantErr { + t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr) + } + + if tt.contains != "" { + output := buf.String() + if !strings.Contains(output, tt.contains) { + t.Errorf("Execute() output should contain %v, got %v", tt.contains, output) + } + } + }) + } +} + +func TestRdsListCmdFlags(t *testing.T) { + tests := []struct { + flagName string + shorthand string + defaultValue string + }{ + {"region", "r", ""}, + } + + for _, tt := range tests { + t.Run(tt.flagName+" flag", func(t *testing.T) { + flag := rdsListCmd.Flags().Lookup(tt.flagName) + if flag == nil { + t.Errorf("Flag %s not found", tt.flagName) + return + } + + if flag.Shorthand != tt.shorthand { + t.Errorf("Flag %s shorthand = %s, want %s", tt.flagName, flag.Shorthand, tt.shorthand) + } + + if flag.DefValue != tt.defaultValue { + t.Errorf("Flag %s default = %s, want %s", tt.flagName, flag.DefValue, tt.defaultValue) + } + }) + } +} + +func TestRdsListCmdStructure(t *testing.T) { + if rdsListCmd.Use != "list" { + t.Errorf("Expected Use to be 'list', got %s", rdsListCmd.Use) + } + + if rdsListCmd.Short == "" { + t.Error("Command should have a short description") + } + + if rdsListCmd.Long == "" { + t.Error("Command should have a long description") + } + + if rdsListCmd.Run == nil { + t.Error("Command should have a Run function") + } +} + +func TestRdsStartCmd(t *testing.T) { + tests := []struct { + name string + args []string + wantErr bool + contains string + }{ + { + name: "Start help", + args: []string{"--help"}, + wantErr: false, + contains: "Start a stopped RDS database instance", + }, + { + name: "Start with db identifier", + args: []string{"my-database"}, + wantErr: false, + }, + { + name: "Start with region flag", + args: []string{"my-database", "--region", "cac1"}, + wantErr: false, + }, + { + name: "Start with wait flag", + args: []string{"my-database", "--wait"}, + wantErr: false, + }, + { + name: "Start without db identifier", + args: []string{}, + wantErr: true, + }, + { + name: "Start with too many args", + args: []string{"db1", "db2"}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cmd := &cobra.Command{ + Use: rdsStartCmd.Use, + Short: rdsStartCmd.Short, + Long: rdsStartCmd.Long, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + // Mock start functionality + wait, _ := cmd.Flags().GetBool("wait") + if wait { + // Wait mode + } + if len(args) > 0 { + dbIdentifier := args[0] + if dbIdentifier == "" { + t.Error("DB identifier should not be empty") + } + } + }, + } + + cmd.Flags().StringP("region", "r", "", "AWS region") + cmd.Flags().BoolP("wait", "w", false, "Wait for completion") + + buf := new(bytes.Buffer) + cmd.SetOut(buf) + cmd.SetErr(buf) + cmd.SetArgs(tt.args) + + err := cmd.Execute() + + if (err != nil) != tt.wantErr { + t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr) + } + + if tt.contains != "" { + output := buf.String() + if !strings.Contains(output, tt.contains) { + t.Errorf("Execute() output should contain %v, got %v", tt.contains, output) + } + } + }) + } +} + +func TestRdsStartCmdFlags(t *testing.T) { + tests := []struct { + flagName string + shorthand string + defaultValue string + }{ + {"region", "r", ""}, + {"wait", "w", "false"}, + } + + for _, tt := range tests { + t.Run(tt.flagName+" flag", func(t *testing.T) { + flag := rdsStartCmd.Flags().Lookup(tt.flagName) + if flag == nil { + t.Errorf("Flag %s not found", tt.flagName) + return + } + + if flag.Shorthand != tt.shorthand { + t.Errorf("Flag %s shorthand = %s, want %s", tt.flagName, flag.Shorthand, tt.shorthand) + } + + if flag.DefValue != tt.defaultValue { + t.Errorf("Flag %s default = %s, want %s", tt.flagName, flag.DefValue, tt.defaultValue) + } + }) + } +} + +func TestRdsStartCmdStructure(t *testing.T) { + if rdsStartCmd.Use != "start " { + t.Errorf("Expected Use to be 'start ', got %s", rdsStartCmd.Use) + } + + if rdsStartCmd.Short == "" { + t.Error("Command should have a short description") + } + + if rdsStartCmd.Long == "" { + t.Error("Command should have a long description") + } + + if rdsStartCmd.Run == nil { + t.Error("Command should have a Run function") + } + + // Test argument validation - requires exactly 1 argument + err := rdsStartCmd.Args(rdsStartCmd, []string{}) + if err == nil { + t.Error("Command should require exactly 1 argument") + } + + err = rdsStartCmd.Args(rdsStartCmd, []string{"my-database"}) + if err != nil { + t.Errorf("Command should allow 1 argument, got error: %v", err) + } + + err = rdsStartCmd.Args(rdsStartCmd, []string{"db1", "db2"}) + if err == nil { + t.Error("Command should not allow more than 1 argument") + } +} + +func TestRdsStopCmd(t *testing.T) { + tests := []struct { + name string + args []string + wantErr bool + contains string + }{ + { + name: "Stop help", + args: []string{"--help"}, + wantErr: false, + contains: "Stop a running RDS database instance", + }, + { + name: "Stop with db identifier", + args: []string{"my-database"}, + wantErr: false, + }, + { + name: "Stop with region flag", + args: []string{"my-database", "--region", "cac1"}, + wantErr: false, + }, + { + name: "Stop with wait flag", + args: []string{"my-database", "--wait"}, + wantErr: false, + }, + { + name: "Stop without db identifier", + args: []string{}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cmd := &cobra.Command{ + Use: rdsStopCmd.Use, + Short: rdsStopCmd.Short, + Long: rdsStopCmd.Long, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + // Mock stop functionality + wait, _ := cmd.Flags().GetBool("wait") + if wait { + // Wait mode + } + if len(args) > 0 { + dbIdentifier := args[0] + if dbIdentifier == "" { + t.Error("DB identifier should not be empty") + } + } + }, + } + + cmd.Flags().StringP("region", "r", "", "AWS region") + cmd.Flags().BoolP("wait", "w", false, "Wait for completion") + + buf := new(bytes.Buffer) + cmd.SetOut(buf) + cmd.SetErr(buf) + cmd.SetArgs(tt.args) + + err := cmd.Execute() + + if (err != nil) != tt.wantErr { + t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr) + } + + if tt.contains != "" { + output := buf.String() + if !strings.Contains(output, tt.contains) { + t.Errorf("Execute() output should contain %v, got %v", tt.contains, output) + } + } + }) + } +} + +func TestRdsStopCmdFlags(t *testing.T) { + tests := []struct { + flagName string + shorthand string + defaultValue string + }{ + {"region", "r", ""}, + {"wait", "w", "false"}, + } + + for _, tt := range tests { + t.Run(tt.flagName+" flag", func(t *testing.T) { + flag := rdsStopCmd.Flags().Lookup(tt.flagName) + if flag == nil { + t.Errorf("Flag %s not found", tt.flagName) + return + } + + if flag.Shorthand != tt.shorthand { + t.Errorf("Flag %s shorthand = %s, want %s", tt.flagName, flag.Shorthand, tt.shorthand) + } + + if flag.DefValue != tt.defaultValue { + t.Errorf("Flag %s default = %s, want %s", tt.flagName, flag.DefValue, tt.defaultValue) + } + }) + } +} + +func TestRdsStopCmdStructure(t *testing.T) { + if rdsStopCmd.Use != "stop " { + t.Errorf("Expected Use to be 'stop ', got %s", rdsStopCmd.Use) + } + + if rdsStopCmd.Short == "" { + t.Error("Command should have a short description") + } + + if rdsStopCmd.Long == "" { + t.Error("Command should have a long description") + } + + if rdsStopCmd.Run == nil { + t.Error("Command should have a Run function") + } +} + +func TestRdsRebootCmd(t *testing.T) { + tests := []struct { + name string + args []string + wantErr bool + contains string + }{ + { + name: "Reboot help", + args: []string{"--help"}, + wantErr: false, + contains: "Reboot an RDS database instance", + }, + { + name: "Reboot with db identifier", + args: []string{"my-database"}, + wantErr: false, + }, + { + name: "Reboot with region flag", + args: []string{"my-database", "--region", "cac1"}, + wantErr: false, + }, + { + name: "Reboot with wait flag", + args: []string{"my-database", "--wait"}, + wantErr: false, + }, + { + name: "Reboot with force-failover flag", + args: []string{"my-database", "--force-failover"}, + wantErr: false, + }, + { + name: "Reboot without db identifier", + args: []string{}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cmd := &cobra.Command{ + Use: rdsRebootCmd.Use, + Short: rdsRebootCmd.Short, + Long: rdsRebootCmd.Long, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + // Mock reboot functionality + wait, _ := cmd.Flags().GetBool("wait") + forceFailover, _ := cmd.Flags().GetBool("force-failover") + if wait { + // Wait mode + } + if forceFailover { + // Force failover mode + } + if len(args) > 0 { + dbIdentifier := args[0] + if dbIdentifier == "" { + t.Error("DB identifier should not be empty") + } + } + }, + } + + cmd.Flags().StringP("region", "r", "", "AWS region") + cmd.Flags().BoolP("wait", "w", false, "Wait for completion") + cmd.Flags().BoolP("force-failover", "f", false, "Force failover") + + buf := new(bytes.Buffer) + cmd.SetOut(buf) + cmd.SetErr(buf) + cmd.SetArgs(tt.args) + + err := cmd.Execute() + + if (err != nil) != tt.wantErr { + t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr) + } + + if tt.contains != "" { + output := buf.String() + if !strings.Contains(output, tt.contains) { + t.Errorf("Execute() output should contain %v, got %v", tt.contains, output) + } + } + }) + } +} + +func TestRdsRebootCmdFlags(t *testing.T) { + tests := []struct { + flagName string + shorthand string + defaultValue string + }{ + {"region", "r", ""}, + {"wait", "w", "false"}, + {"force-failover", "f", "false"}, + } + + for _, tt := range tests { + t.Run(tt.flagName+" flag", func(t *testing.T) { + flag := rdsRebootCmd.Flags().Lookup(tt.flagName) + if flag == nil { + t.Errorf("Flag %s not found", tt.flagName) + return + } + + if flag.Shorthand != tt.shorthand { + t.Errorf("Flag %s shorthand = %s, want %s", tt.flagName, flag.Shorthand, tt.shorthand) + } + + if flag.DefValue != tt.defaultValue { + t.Errorf("Flag %s default = %s, want %s", tt.flagName, flag.DefValue, tt.defaultValue) + } + }) + } +} + +func TestRdsRebootCmdStructure(t *testing.T) { + if rdsRebootCmd.Use != "reboot " { + t.Errorf("Expected Use to be 'reboot ', got %s", rdsRebootCmd.Use) + } + + if rdsRebootCmd.Short == "" { + t.Error("Command should have a short description") + } + + if rdsRebootCmd.Long == "" { + t.Error("Command should have a long description") + } + + if rdsRebootCmd.Run == nil { + t.Error("Command should have a Run function") + } +} + +func TestColorizeRDSStatus(t *testing.T) { + tests := []struct { + status string + notEmpty bool + }{ + {"available", true}, + {"stopped", true}, + {"starting", true}, + {"stopping", true}, + {"rebooting", true}, + {"modifying", true}, + {"backing-up", true}, + {"failed", true}, + {"incompatible-restore", true}, + {"incompatible-network", true}, + {"unknown-status", true}, + } + + for _, tt := range tests { + t.Run(tt.status, func(t *testing.T) { + result := colorizeRDSStatus(tt.status) + if tt.notEmpty && result == "" { + t.Errorf("colorizeRDSStatus(%s) should not be empty", tt.status) + } + // The result should contain the original status text + if !strings.Contains(result, tt.status) { + t.Errorf("colorizeRDSStatus(%s) should contain the status text, got %s", tt.status, result) + } + }) + } +} + +func TestRdsExamplesUseCorrectRegion(t *testing.T) { + // Verify that examples use cac1/ca-central-1 instead of use1/us-east-1 + commands := []*cobra.Command{rdsListCmd, rdsStartCmd, rdsStopCmd, rdsRebootCmd} + + for _, cmd := range commands { + t.Run(cmd.Name(), func(t *testing.T) { + longDesc := cmd.Long + + if strings.Contains(longDesc, "us-east-1") && !strings.Contains(longDesc, "ca-central-1") { + t.Errorf("RDS %s command examples should use ca-central-1, not us-east-1", cmd.Name()) + } + + if strings.Contains(longDesc, "-r use1") { + t.Errorf("RDS %s command examples should use -r cac1, not -r use1", cmd.Name()) + } + }) + } +} + +func TestRdsArgumentValidation(t *testing.T) { + // Test that start, stop, reboot require exactly 1 argument + commands := []*cobra.Command{rdsStartCmd, rdsStopCmd, rdsRebootCmd} + + for _, cmd := range commands { + t.Run(cmd.Name()+" requires 1 arg", func(t *testing.T) { + err := cmd.Args(cmd, []string{}) + if err == nil { + t.Errorf("RDS %s should require exactly 1 argument", cmd.Name()) + } + + err = cmd.Args(cmd, []string{"my-database"}) + if err != nil { + t.Errorf("RDS %s should accept 1 argument, got error: %v", cmd.Name(), err) + } + + err = cmd.Args(cmd, []string{"db1", "db2"}) + if err == nil { + t.Errorf("RDS %s should not accept more than 1 argument", cmd.Name()) + } + }) + } +} diff --git a/ztictl/cmd/ztictl/ssm.go b/ztictl/cmd/ztictl/ssm.go index 5a972c7..5eeabfb 100644 --- a/ztictl/cmd/ztictl/ssm.go +++ b/ztictl/cmd/ztictl/ssm.go @@ -8,12 +8,15 @@ import ( var ssmCmd = &cobra.Command{ Use: "ssm", Short: "SSM operations", - Long: `Manage AWS Systems Manager operations including instance connections, command execution, + Long: `Manage AWS Systems Manager operations including instance connections, command execution, file transfers, and port forwarding through SSM. Examples: ztictl ssm connect # Connect to instance via SSM - ztictl ssm list [filters] # List SSM-enabled instances + ztictl ssm ssh # SSH to instance via SSM tunnel + ztictl ssm ssh-config # Generate SSH config for SSM access + ztictl ssm rdp # RDP to Windows instance via SSM tunnel + ztictl ssm list [filters] # List SSM-enabled instances ztictl ssm forward # Port forwarding via SSM ztictl ssm transfer # File transfer via SSM ztictl ssm command # Execute command via SSM @@ -50,4 +53,7 @@ func init() { ssmCmd.AddCommand(ssmStartTaggedCmd) // ssm_power.go ssmCmd.AddCommand(ssmStopTaggedCmd) // ssm_power.go ssmCmd.AddCommand(ssmRebootTaggedCmd) // ssm_power.go + ssmCmd.AddCommand(ssmSSHCmd) // ssm_ssh.go + ssmCmd.AddCommand(ssmSSHConfigCmd) // ssm_ssh.go + ssmCmd.AddCommand(ssmRDPCmd) // ssm_ssh.go } diff --git a/ztictl/cmd/ztictl/ssm_ssh.go b/ztictl/cmd/ztictl/ssm_ssh.go new file mode 100644 index 0000000..f9e14b5 --- /dev/null +++ b/ztictl/cmd/ztictl/ssm_ssh.go @@ -0,0 +1,426 @@ +package main + +import ( + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + "ztictl/internal/ssm" + "ztictl/pkg/logging" + + "github.com/spf13/cobra" +) + +// ssmSSHCmd represents the ssm ssh command +var ssmSSHCmd = &cobra.Command{ + Use: "ssh [instance-identifier]", + Short: "SSH to an instance through SSM tunnel", + Long: `Connect to an EC2 instance via SSH using SSM Session Manager as the transport. +This allows SSH access without opening port 22 or managing bastion hosts. + +If no instance identifier is provided, an interactive fuzzy finder will be launched. +Instance identifier can be an instance ID (i-1234567890abcdef0) or instance name. +Region supports shortcuts: cac1 (ca-central-1), use1 (us-east-1), euw1 (eu-west-1), etc. + +Requirements: + - AWS CLI v2 with Session Manager plugin installed + - SSH client installed + - EC2 instance must have SSM agent running + +Examples: + ztictl ssm ssh i-1234567890abcdef0 --region ca-central-1 + ztictl ssm ssh my-server --region cac1 --user ubuntu + ztictl ssm ssh i-abc123 -r cac1 -i ~/.ssh/my-key.pem`, + Args: cobra.MaximumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + regionCode, _ := cmd.Flags().GetString("region") + user, _ := cmd.Flags().GetString("user") + identityFile, _ := cmd.Flags().GetString("identity") + sshArgs, _ := cmd.Flags().GetStringArray("ssh-arg") + + var instanceIdentifier string + if len(args) > 0 { + instanceIdentifier = args[0] + } + + if err := performSSHConnection(regionCode, instanceIdentifier, user, identityFile, sshArgs); err != nil { + logging.LogError("SSH connection failed: %v", err) + os.Exit(1) + } + }, +} + +// ssmSSHConfigCmd represents the ssm ssh-config command +var ssmSSHConfigCmd = &cobra.Command{ + Use: "ssh-config [instance-identifier]", + Short: "Generate SSH config entry for SSM-based SSH access", + Long: `Generate an SSH config entry that allows native SSH access through SSM. +After adding the config, you can use standard SSH commands like: + ssh + scp file.txt :/path/ + rsync -avz ./dir :/path/ + +If no instance identifier is provided, an interactive fuzzy finder will be launched. +Region supports shortcuts: cac1 (ca-central-1), use1 (us-east-1), euw1 (eu-west-1), etc. + +Examples: + ztictl ssm ssh-config i-1234567890abcdef0 --region ca-central-1 --name prod-web + ztictl ssm ssh-config my-server --region cac1 --name dev-api --user ubuntu + ztictl ssm ssh-config i-abc123 -r cac1 -n jump-box --append`, + Args: cobra.MaximumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + regionCode, _ := cmd.Flags().GetString("region") + name, _ := cmd.Flags().GetString("name") + user, _ := cmd.Flags().GetString("user") + identityFile, _ := cmd.Flags().GetString("identity") + appendToConfig, _ := cmd.Flags().GetBool("append") + + var instanceIdentifier string + if len(args) > 0 { + instanceIdentifier = args[0] + } + + if err := generateSSHConfig(regionCode, instanceIdentifier, name, user, identityFile, appendToConfig); err != nil { + logging.LogError("SSH config generation failed: %v", err) + os.Exit(1) + } + }, +} + +// performSSHConnection handles SSH over SSM connection +func performSSHConnection(regionCode, instanceIdentifier, user, identityFile string, extraArgs []string) error { + region := resolveRegion(regionCode) + ctx := context.Background() + ssmManager := ssm.NewManager(logger) + + // Use the shared instance selection logic + instanceID, err := ssmManager.GetInstanceService().SelectInstanceWithFallback( + ctx, + instanceIdentifier, + region, + nil, // No filters + ) + if err != nil { + return fmt.Errorf("instance selection failed: %w", err) + } + + // Validate instance state before attempting connection + if err := ValidateInstanceState(ctx, ssmManager, instanceID, region, InstanceValidationRequirements{ + AllowedStates: []string{"running"}, + RequireSSMOnline: true, + Operation: "ssh", + }); err != nil { + return err + } + + // Default user based on common AMIs + if user == "" { + user = "ec2-user" + } + + logging.LogInfo("Starting SSH connection to %s@%s via SSM in region: %s", user, instanceID, region) + + // Build SSH command with ProxyCommand + sshCmd := getSSHCommand() + proxyCommand := buildProxyCommand(instanceID, region) + + sshArgs := []string{ + "-o", fmt.Sprintf("ProxyCommand=%s", proxyCommand), + "-o", "StrictHostKeyChecking=accept-new", + } + + if identityFile != "" { + sshArgs = append(sshArgs, "-i", identityFile) + } + + // Add any extra SSH arguments + sshArgs = append(sshArgs, extraArgs...) + + // Add target + sshArgs = append(sshArgs, fmt.Sprintf("%s@%s", user, instanceID)) + + cmd := exec.CommandContext(ctx, sshCmd, sshArgs...) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + if err := cmd.Run(); err != nil { + return fmt.Errorf("SSH connection failed: %w", err) + } + + return nil +} + +// generateSSHConfig generates an SSH config entry for SSM-based SSH access +func generateSSHConfig(regionCode, instanceIdentifier, name, user, identityFile string, appendToConfig bool) error { + region := resolveRegion(regionCode) + ctx := context.Background() + ssmManager := ssm.NewManager(logger) + + // Use the shared instance selection logic + instanceID, err := ssmManager.GetInstanceService().SelectInstanceWithFallback( + ctx, + instanceIdentifier, + region, + nil, // No filters + ) + if err != nil { + return fmt.Errorf("instance selection failed: %w", err) + } + + // Use instance ID as name if not provided + if name == "" { + name = instanceID + } + + // Default user + if user == "" { + user = "ec2-user" + } + + proxyCommand := buildProxyCommand(instanceID, region) + + // Build SSH config entry + var configBuilder strings.Builder + configBuilder.WriteString(fmt.Sprintf("\n# Added by ztictl for SSM SSH access\n")) + configBuilder.WriteString(fmt.Sprintf("Host %s\n", name)) + configBuilder.WriteString(fmt.Sprintf(" HostName %s\n", instanceID)) + configBuilder.WriteString(fmt.Sprintf(" User %s\n", user)) + configBuilder.WriteString(fmt.Sprintf(" ProxyCommand %s\n", proxyCommand)) + configBuilder.WriteString(" StrictHostKeyChecking accept-new\n") + + if identityFile != "" { + configBuilder.WriteString(fmt.Sprintf(" IdentityFile %s\n", identityFile)) + } + + configEntry := configBuilder.String() + + if appendToConfig { + // Append to ~/.ssh/config + homeDir, err := os.UserHomeDir() + if err != nil { + return fmt.Errorf("failed to get home directory: %w", err) + } + + sshDir := filepath.Join(homeDir, ".ssh") + configPath := filepath.Join(sshDir, "config") + + // Ensure .ssh directory exists + if err := os.MkdirAll(sshDir, 0700); err != nil { + return fmt.Errorf("failed to create .ssh directory: %w", err) + } + + // Check if entry already exists + existingConfig, _ := os.ReadFile(configPath) + if strings.Contains(string(existingConfig), fmt.Sprintf("Host %s\n", name)) { + return fmt.Errorf("SSH config entry for '%s' already exists in %s", name, configPath) + } + + // Append to config file + f, err := os.OpenFile(configPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) + if err != nil { + return fmt.Errorf("failed to open SSH config: %w", err) + } + defer f.Close() + + if _, err := f.WriteString(configEntry); err != nil { + return fmt.Errorf("failed to write SSH config: %w", err) + } + + logging.LogInfo("SSH config entry added to %s", configPath) + fmt.Printf("\nYou can now connect using:\n") + fmt.Printf(" ssh %s\n", name) + fmt.Printf(" scp file.txt %s:/path/\n", name) + fmt.Printf(" rsync -avz ./dir %s:/path/\n", name) + } else { + // Print to stdout + fmt.Printf("# Add the following to your ~/.ssh/config:\n") + fmt.Print(configEntry) + fmt.Printf("\n# Or run with --append to automatically add it:\n") + fmt.Printf("# ztictl ssm ssh-config %s --region %s --name %s --append\n", instanceID, regionCode, name) + } + + return nil +} + +// ssmRDPCmd represents the ssm rdp command +var ssmRDPCmd = &cobra.Command{ + Use: "rdp [instance-identifier]", + Short: "RDP to a Windows instance through SSM tunnel", + Long: `Connect to a Windows EC2 instance via RDP using SSM Session Manager as the transport. +This allows RDP access without opening port 3389 or managing bastion hosts. + +The command sets up port forwarding from a local port to the instance's RDP port (3389), +then optionally launches your RDP client. + +If no instance identifier is provided, an interactive fuzzy finder will be launched. +Instance identifier can be an instance ID (i-1234567890abcdef0) or instance name. +Region supports shortcuts: cac1 (ca-central-1), use1 (us-east-1), euw1 (eu-west-1), etc. + +Requirements: + - AWS CLI v2 with Session Manager plugin installed + - Windows instance with RDP enabled + - EC2 instance must have SSM agent running + +Examples: + ztictl ssm rdp i-1234567890abcdef0 --region ca-central-1 + ztictl ssm rdp my-windows-server --region cac1 + ztictl ssm rdp i-abc123 -r cac1 --local-port 13389 + ztictl ssm rdp i-abc123 -r cac1 --launch`, + Args: cobra.MaximumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + regionCode, _ := cmd.Flags().GetString("region") + localPort, _ := cmd.Flags().GetInt("local-port") + launch, _ := cmd.Flags().GetBool("launch") + + var instanceIdentifier string + if len(args) > 0 { + instanceIdentifier = args[0] + } + + if err := performRDPConnection(regionCode, instanceIdentifier, localPort, launch); err != nil { + logging.LogError("RDP connection failed: %v", err) + os.Exit(1) + } + }, +} + +// performRDPConnection handles RDP over SSM connection +func performRDPConnection(regionCode, instanceIdentifier string, localPort int, launch bool) error { + region := resolveRegion(regionCode) + ctx := context.Background() + ssmManager := ssm.NewManager(logger) + + // Use the shared instance selection logic + instanceID, err := ssmManager.GetInstanceService().SelectInstanceWithFallback( + ctx, + instanceIdentifier, + region, + nil, // No filters + ) + if err != nil { + return fmt.Errorf("instance selection failed: %w", err) + } + + // Validate instance state before attempting connection + if err := ValidateInstanceState(ctx, ssmManager, instanceID, region, InstanceValidationRequirements{ + AllowedStates: []string{"running"}, + RequireSSMOnline: true, + Operation: "rdp", + }); err != nil { + return err + } + + // Default local port + if localPort == 0 { + localPort = 33389 // Use non-standard port to avoid conflicts + } + + remotePort := 3389 // Standard RDP port + + logging.LogInfo("Starting RDP tunnel to %s via SSM in region: %s", instanceID, region) + fmt.Printf("\nRDP tunnel: localhost:%d -> %s:%d\n", localPort, instanceID, remotePort) + fmt.Printf("\nConnect your RDP client to: localhost:%d\n", localPort) + + if launch { + // Launch RDP client in background before starting the tunnel + go func() { + // Give the tunnel a moment to start + launchRDPClient(localPort) + }() + } else { + fmt.Printf("\nTo auto-launch RDP client, use: --launch\n") + if runtime.GOOS == "windows" { + fmt.Printf("Or manually run: mstsc /v:localhost:%d\n", localPort) + } else if runtime.GOOS == "darwin" { + fmt.Printf("Or manually run: open rdp://localhost:%d\n", localPort) + } else { + fmt.Printf("Or manually run: xfreerdp /v:localhost:%d /u:Administrator\n", localPort) + } + } + + fmt.Printf("\nPress Ctrl+C to stop the tunnel\n\n") + + // Start port forwarding (this blocks until interrupted) + if err := ssmManager.ForwardPort(ctx, instanceID, region, localPort, remotePort); err != nil { + return fmt.Errorf("port forwarding failed: %w", err) + } + + return nil +} + +// launchRDPClient attempts to launch the system's RDP client +func launchRDPClient(port int) { + var cmd *exec.Cmd + + switch runtime.GOOS { + case "windows": + // Windows: use mstsc (Microsoft Remote Desktop) + cmd = exec.Command("mstsc", fmt.Sprintf("/v:localhost:%d", port)) + case "darwin": + // macOS: use open with rdp:// URL (requires Microsoft Remote Desktop app) + cmd = exec.Command("open", fmt.Sprintf("rdp://localhost:%d", port)) + default: + // Linux: try xfreerdp, rdesktop, or remmina + if _, err := exec.LookPath("xfreerdp"); err == nil { + cmd = exec.Command("xfreerdp", fmt.Sprintf("/v:localhost:%d", port), "/u:Administrator") + } else if _, err := exec.LookPath("rdesktop"); err == nil { + cmd = exec.Command("rdesktop", fmt.Sprintf("localhost:%d", port)) + } else if _, err := exec.LookPath("remmina"); err == nil { + cmd = exec.Command("remmina", "-c", fmt.Sprintf("rdp://localhost:%d", port)) + } else { + logging.LogWarn("No RDP client found. Install xfreerdp, rdesktop, or remmina") + return + } + } + + if cmd != nil { + if err := cmd.Start(); err != nil { + logging.LogWarn("Failed to launch RDP client: %v", err) + } + } +} + +// buildProxyCommand builds the AWS SSM ProxyCommand for SSH +func buildProxyCommand(instanceID, region string) string { + awsCmd := "aws" + if runtime.GOOS == "windows" { + awsCmd = "aws.exe" + } + + // Using AWS-StartSSHSession document for SSH over SSM + return fmt.Sprintf("%s ssm start-session --target %s --document-name AWS-StartSSHSession --parameters portNumber=%%p --region %s", + awsCmd, instanceID, region) +} + +// getSSHCommand returns the platform-appropriate SSH command +func getSSHCommand() string { + if runtime.GOOS == "windows" { + return "ssh.exe" + } + return "ssh" +} + +func init() { + // SSH command flags + ssmSSHCmd.Flags().StringP("region", "r", "", "AWS region or shortcode (cac1, use1, euw1, etc.) - default from config") + ssmSSHCmd.Flags().StringP("user", "u", "", "SSH username (default: ec2-user)") + ssmSSHCmd.Flags().StringP("identity", "i", "", "Path to SSH private key file") + ssmSSHCmd.Flags().StringArrayP("ssh-arg", "o", []string{}, "Additional SSH arguments (can be specified multiple times)") + + // SSH config command flags + ssmSSHConfigCmd.Flags().StringP("region", "r", "", "AWS region or shortcode (cac1, use1, euw1, etc.) - default from config") + ssmSSHConfigCmd.Flags().StringP("name", "n", "", "Friendly name for SSH config entry (default: instance ID)") + ssmSSHConfigCmd.Flags().StringP("user", "u", "", "SSH username (default: ec2-user)") + ssmSSHConfigCmd.Flags().StringP("identity", "i", "", "Path to SSH private key file") + ssmSSHConfigCmd.Flags().BoolP("append", "a", false, "Append config entry to ~/.ssh/config") + + // RDP command flags + ssmRDPCmd.Flags().StringP("region", "r", "", "AWS region or shortcode (cac1, use1, euw1, etc.) - default from config") + ssmRDPCmd.Flags().IntP("local-port", "p", 33389, "Local port to forward (default: 33389)") + ssmRDPCmd.Flags().BoolP("launch", "l", false, "Automatically launch RDP client") +} diff --git a/ztictl/cmd/ztictl/ssm_ssh_test.go b/ztictl/cmd/ztictl/ssm_ssh_test.go new file mode 100644 index 0000000..3082e8a --- /dev/null +++ b/ztictl/cmd/ztictl/ssm_ssh_test.go @@ -0,0 +1,580 @@ +package main + +import ( + "bytes" + "runtime" + "strings" + "testing" + + "github.com/spf13/cobra" +) + +func TestSsmSSHCmd(t *testing.T) { + tests := []struct { + name string + args []string + wantErr bool + contains string + }{ + { + name: "SSH help", + args: []string{"--help"}, + wantErr: false, + contains: "Connect to an EC2 instance via SSH using SSM Session Manager", + }, + { + name: "SSH with instance ID", + args: []string{"i-1234567890abcdef0"}, + wantErr: false, + }, + { + name: "SSH with instance name", + args: []string{"web-server-1"}, + wantErr: false, + }, + { + name: "SSH with region flag", + args: []string{"i-1234567890abcdef0", "--region", "ca-central-1"}, + wantErr: false, + }, + { + name: "SSH with region shortcode", + args: []string{"i-1234567890abcdef0", "-r", "cac1"}, + wantErr: false, + }, + { + name: "SSH with user flag", + args: []string{"i-1234567890abcdef0", "--user", "ubuntu"}, + wantErr: false, + }, + { + name: "SSH with identity flag", + args: []string{"i-1234567890abcdef0", "-i", "~/.ssh/my-key.pem"}, + wantErr: false, + }, + { + name: "SSH with too many args", + args: []string{"instance1", "instance2"}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cmd := &cobra.Command{ + Use: ssmSSHCmd.Use, + Short: ssmSSHCmd.Short, + Long: ssmSSHCmd.Long, + Args: cobra.MaximumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + // Mock SSH functionality + regionCode, _ := cmd.Flags().GetString("region") + user, _ := cmd.Flags().GetString("user") + identity, _ := cmd.Flags().GetString("identity") + + // Validate flags are retrievable + if regionCode == "cac1" { + // Valid shortcode + } + if user == "ubuntu" { + // Valid user + } + if identity != "" { + // Identity file provided + } + + if len(args) > 0 { + instanceIdentifier := args[0] + if instanceIdentifier == "" { + t.Error("Instance identifier should not be empty") + } + } + }, + } + + cmd.Flags().StringP("region", "r", "", "AWS region") + cmd.Flags().StringP("user", "u", "", "SSH username") + cmd.Flags().StringP("identity", "i", "", "Path to SSH private key") + cmd.Flags().StringArrayP("ssh-arg", "o", []string{}, "Additional SSH arguments") + + buf := new(bytes.Buffer) + cmd.SetOut(buf) + cmd.SetErr(buf) + cmd.SetArgs(tt.args) + + err := cmd.Execute() + + if (err != nil) != tt.wantErr { + t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr) + } + + if tt.contains != "" { + output := buf.String() + if !strings.Contains(output, tt.contains) { + t.Errorf("Execute() output should contain %v, got %v", tt.contains, output) + } + } + }) + } +} + +func TestSsmSSHCmdFlags(t *testing.T) { + tests := []struct { + flagName string + shorthand string + defaultValue string + }{ + {"region", "r", ""}, + {"user", "u", ""}, + {"identity", "i", ""}, + } + + for _, tt := range tests { + t.Run(tt.flagName+" flag", func(t *testing.T) { + flag := ssmSSHCmd.Flags().Lookup(tt.flagName) + if flag == nil { + t.Errorf("Flag %s not found", tt.flagName) + return + } + + if flag.Shorthand != tt.shorthand { + t.Errorf("Flag %s shorthand = %s, want %s", tt.flagName, flag.Shorthand, tt.shorthand) + } + + if flag.DefValue != tt.defaultValue { + t.Errorf("Flag %s default = %s, want %s", tt.flagName, flag.DefValue, tt.defaultValue) + } + }) + } +} + +func TestSsmSSHCmdStructure(t *testing.T) { + if ssmSSHCmd.Use != "ssh [instance-identifier]" { + t.Errorf("Expected Use to be 'ssh [instance-identifier]', got %s", ssmSSHCmd.Use) + } + + if ssmSSHCmd.Short == "" { + t.Error("Command should have a short description") + } + + if ssmSSHCmd.Long == "" { + t.Error("Command should have a long description") + } + + if ssmSSHCmd.Run == nil { + t.Error("Command should have a Run function") + } + + // Test argument validation + err := ssmSSHCmd.Args(ssmSSHCmd, []string{}) + if err != nil { + t.Errorf("Command should allow 0 arguments for fuzzy finder, got error: %v", err) + } + + err = ssmSSHCmd.Args(ssmSSHCmd, []string{"i-1234567890abcdef0"}) + if err != nil { + t.Errorf("Command should allow 1 argument, got error: %v", err) + } + + err = ssmSSHCmd.Args(ssmSSHCmd, []string{"instance1", "instance2"}) + if err == nil { + t.Error("Command should not allow more than 1 argument") + } +} + +func TestSsmSSHConfigCmd(t *testing.T) { + tests := []struct { + name string + args []string + wantErr bool + contains string + }{ + { + name: "SSH-config help", + args: []string{"--help"}, + wantErr: false, + contains: "Generate an SSH config entry", + }, + { + name: "SSH-config with instance ID", + args: []string{"i-1234567890abcdef0"}, + wantErr: false, + }, + { + name: "SSH-config with name flag", + args: []string{"i-1234567890abcdef0", "--name", "prod-web"}, + wantErr: false, + }, + { + name: "SSH-config with region flag", + args: []string{"i-1234567890abcdef0", "--region", "cac1"}, + wantErr: false, + }, + { + name: "SSH-config with append flag", + args: []string{"i-1234567890abcdef0", "--append"}, + wantErr: false, + }, + { + name: "SSH-config with too many args", + args: []string{"instance1", "instance2"}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cmd := &cobra.Command{ + Use: ssmSSHConfigCmd.Use, + Short: ssmSSHConfigCmd.Short, + Long: ssmSSHConfigCmd.Long, + Args: cobra.MaximumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + // Mock SSH config functionality + name, _ := cmd.Flags().GetString("name") + appendFlag, _ := cmd.Flags().GetBool("append") + + if name == "prod-web" { + // Valid name + } + if appendFlag { + // Append mode + } + + if len(args) > 0 { + instanceIdentifier := args[0] + if instanceIdentifier == "" { + t.Error("Instance identifier should not be empty") + } + } + }, + } + + cmd.Flags().StringP("region", "r", "", "AWS region") + cmd.Flags().StringP("name", "n", "", "Friendly name") + cmd.Flags().StringP("user", "u", "", "SSH username") + cmd.Flags().StringP("identity", "i", "", "Path to SSH private key") + cmd.Flags().BoolP("append", "a", false, "Append to config") + + buf := new(bytes.Buffer) + cmd.SetOut(buf) + cmd.SetErr(buf) + cmd.SetArgs(tt.args) + + err := cmd.Execute() + + if (err != nil) != tt.wantErr { + t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr) + } + + if tt.contains != "" { + output := buf.String() + if !strings.Contains(output, tt.contains) { + t.Errorf("Execute() output should contain %v, got %v", tt.contains, output) + } + } + }) + } +} + +func TestSsmSSHConfigCmdFlags(t *testing.T) { + tests := []struct { + flagName string + shorthand string + defaultValue string + }{ + {"region", "r", ""}, + {"name", "n", ""}, + {"user", "u", ""}, + {"identity", "i", ""}, + {"append", "a", "false"}, + } + + for _, tt := range tests { + t.Run(tt.flagName+" flag", func(t *testing.T) { + flag := ssmSSHConfigCmd.Flags().Lookup(tt.flagName) + if flag == nil { + t.Errorf("Flag %s not found", tt.flagName) + return + } + + if flag.Shorthand != tt.shorthand { + t.Errorf("Flag %s shorthand = %s, want %s", tt.flagName, flag.Shorthand, tt.shorthand) + } + + if flag.DefValue != tt.defaultValue { + t.Errorf("Flag %s default = %s, want %s", tt.flagName, flag.DefValue, tt.defaultValue) + } + }) + } +} + +func TestSsmSSHConfigCmdStructure(t *testing.T) { + if ssmSSHConfigCmd.Use != "ssh-config [instance-identifier]" { + t.Errorf("Expected Use to be 'ssh-config [instance-identifier]', got %s", ssmSSHConfigCmd.Use) + } + + if ssmSSHConfigCmd.Short == "" { + t.Error("Command should have a short description") + } + + if ssmSSHConfigCmd.Long == "" { + t.Error("Command should have a long description") + } + + if ssmSSHConfigCmd.Run == nil { + t.Error("Command should have a Run function") + } +} + +func TestSsmRDPCmd(t *testing.T) { + tests := []struct { + name string + args []string + wantErr bool + contains string + }{ + { + name: "RDP help", + args: []string{"--help"}, + wantErr: false, + contains: "Connect to a Windows EC2 instance via RDP", + }, + { + name: "RDP with instance ID", + args: []string{"i-1234567890abcdef0"}, + wantErr: false, + }, + { + name: "RDP with instance name", + args: []string{"windows-server-1"}, + wantErr: false, + }, + { + name: "RDP with region flag", + args: []string{"i-1234567890abcdef0", "--region", "cac1"}, + wantErr: false, + }, + { + name: "RDP with local-port flag", + args: []string{"i-1234567890abcdef0", "--local-port", "13389"}, + wantErr: false, + }, + { + name: "RDP with launch flag", + args: []string{"i-1234567890abcdef0", "--launch"}, + wantErr: false, + }, + { + name: "RDP with too many args", + args: []string{"instance1", "instance2"}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cmd := &cobra.Command{ + Use: ssmRDPCmd.Use, + Short: ssmRDPCmd.Short, + Long: ssmRDPCmd.Long, + Args: cobra.MaximumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + // Mock RDP functionality + localPort, _ := cmd.Flags().GetInt("local-port") + launch, _ := cmd.Flags().GetBool("launch") + + if localPort == 13389 { + // Custom port + } + if launch { + // Auto-launch mode + } + + if len(args) > 0 { + instanceIdentifier := args[0] + if instanceIdentifier == "" { + t.Error("Instance identifier should not be empty") + } + } + }, + } + + cmd.Flags().StringP("region", "r", "", "AWS region") + cmd.Flags().IntP("local-port", "p", 33389, "Local port") + cmd.Flags().BoolP("launch", "l", false, "Auto-launch RDP client") + + buf := new(bytes.Buffer) + cmd.SetOut(buf) + cmd.SetErr(buf) + cmd.SetArgs(tt.args) + + err := cmd.Execute() + + if (err != nil) != tt.wantErr { + t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr) + } + + if tt.contains != "" { + output := buf.String() + if !strings.Contains(output, tt.contains) { + t.Errorf("Execute() output should contain %v, got %v", tt.contains, output) + } + } + }) + } +} + +func TestSsmRDPCmdFlags(t *testing.T) { + tests := []struct { + flagName string + shorthand string + defaultValue string + }{ + {"region", "r", ""}, + {"local-port", "p", "33389"}, + {"launch", "l", "false"}, + } + + for _, tt := range tests { + t.Run(tt.flagName+" flag", func(t *testing.T) { + flag := ssmRDPCmd.Flags().Lookup(tt.flagName) + if flag == nil { + t.Errorf("Flag %s not found", tt.flagName) + return + } + + if flag.Shorthand != tt.shorthand { + t.Errorf("Flag %s shorthand = %s, want %s", tt.flagName, flag.Shorthand, tt.shorthand) + } + + if flag.DefValue != tt.defaultValue { + t.Errorf("Flag %s default = %s, want %s", tt.flagName, flag.DefValue, tt.defaultValue) + } + }) + } +} + +func TestSsmRDPCmdStructure(t *testing.T) { + if ssmRDPCmd.Use != "rdp [instance-identifier]" { + t.Errorf("Expected Use to be 'rdp [instance-identifier]', got %s", ssmRDPCmd.Use) + } + + if ssmRDPCmd.Short == "" { + t.Error("Command should have a short description") + } + + if ssmRDPCmd.Long == "" { + t.Error("Command should have a long description") + } + + if ssmRDPCmd.Run == nil { + t.Error("Command should have a Run function") + } + + // Test argument validation + err := ssmRDPCmd.Args(ssmRDPCmd, []string{}) + if err != nil { + t.Errorf("Command should allow 0 arguments for fuzzy finder, got error: %v", err) + } + + err = ssmRDPCmd.Args(ssmRDPCmd, []string{"i-1234567890abcdef0"}) + if err != nil { + t.Errorf("Command should allow 1 argument, got error: %v", err) + } + + err = ssmRDPCmd.Args(ssmRDPCmd, []string{"instance1", "instance2"}) + if err == nil { + t.Error("Command should not allow more than 1 argument") + } +} + +func TestBuildProxyCommand(t *testing.T) { + tests := []struct { + instanceID string + region string + contains []string + }{ + { + instanceID: "i-1234567890abcdef0", + region: "ca-central-1", + contains: []string{ + "ssm start-session", + "--target i-1234567890abcdef0", + "--document-name AWS-StartSSHSession", + "--region ca-central-1", + }, + }, + { + instanceID: "i-abcdef1234567890", + region: "us-east-1", + contains: []string{ + "--target i-abcdef1234567890", + "--region us-east-1", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.instanceID, func(t *testing.T) { + result := buildProxyCommand(tt.instanceID, tt.region) + + for _, s := range tt.contains { + if !strings.Contains(result, s) { + t.Errorf("buildProxyCommand() should contain %s, got %s", s, result) + } + } + }) + } +} + +func TestGetSSHCommand(t *testing.T) { + result := getSSHCommand() + + if runtime.GOOS == "windows" { + if result != "ssh.exe" { + t.Errorf("getSSHCommand() on Windows should return ssh.exe, got %s", result) + } + } else { + if result != "ssh" { + t.Errorf("getSSHCommand() on Unix should return ssh, got %s", result) + } + } +} + +func TestSSHExamplesUseCorrectRegion(t *testing.T) { + // Verify that examples use cac1/ca-central-1 instead of use1/us-east-1 + longDesc := ssmSSHCmd.Long + + if strings.Contains(longDesc, "us-east-1") && !strings.Contains(longDesc, "ca-central-1") { + t.Error("SSH command examples should use ca-central-1, not us-east-1") + } + + if strings.Contains(longDesc, "-r use1") { + t.Error("SSH command examples should use -r cac1, not -r use1") + } +} + +func TestSSHConfigExamplesUseCorrectRegion(t *testing.T) { + longDesc := ssmSSHConfigCmd.Long + + if strings.Contains(longDesc, "us-east-1") && !strings.Contains(longDesc, "ca-central-1") { + t.Error("SSH-config command examples should use ca-central-1, not us-east-1") + } + + if strings.Contains(longDesc, "-r use1") { + t.Error("SSH-config command examples should use -r cac1, not -r use1") + } +} + +func TestRDPExamplesUseCorrectRegion(t *testing.T) { + longDesc := ssmRDPCmd.Long + + if strings.Contains(longDesc, "us-east-1") && !strings.Contains(longDesc, "ca-central-1") { + t.Error("RDP command examples should use ca-central-1, not us-east-1") + } + + if strings.Contains(longDesc, "-r use1") { + t.Error("RDP command examples should use -r cac1, not -r use1") + } +} diff --git a/ztictl/go.mod b/ztictl/go.mod index 1abcfa5..97cf10d 100644 --- a/ztictl/go.mod +++ b/ztictl/go.mod @@ -9,6 +9,7 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.32.6 github.com/aws/aws-sdk-go-v2/service/ec2 v1.279.0 github.com/aws/aws-sdk-go-v2/service/iam v1.53.1 + github.com/aws/aws-sdk-go-v2/service/rds v1.113.1 github.com/aws/aws-sdk-go-v2/service/s3 v1.95.0 github.com/aws/aws-sdk-go-v2/service/ssm v1.67.7 github.com/aws/aws-sdk-go-v2/service/sso v1.30.8 diff --git a/ztictl/go.sum b/ztictl/go.sum index baed37c..6f7fb40 100644 --- a/ztictl/go.sum +++ b/ztictl/go.sum @@ -1,87 +1,49 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+Xsqk= -github.com/aws/aws-sdk-go-v2 v1.39.6/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE= github.com/aws/aws-sdk-go-v2 v1.41.0 h1:tNvqh1s+v0vFYdA1xq0aOJH+Y5cRyZ5upu6roPgPKd4= github.com/aws/aws-sdk-go-v2 v1.41.0/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 h1:DHctwEM8P8iTXFxC/QK0MRjwEpWQeM9yzidCRjldUz0= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3/go.mod h1:xdCzcZEtnSTKVDOmUZs4l/j3pSV6rpo1WXl5ugNsL8Y= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 h1:489krEF9xIGkOaaX3CE/Be2uWjiXrkCH6gUX+bZA/BU= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4/go.mod h1:IOAPF6oT9KCsceNTvvYMNHy0+kMF8akOjeDvPENWxp4= -github.com/aws/aws-sdk-go-v2/config v1.31.17 h1:QFl8lL6RgakNK86vusim14P2k8BFSxjvUkcWLDjgz9Y= -github.com/aws/aws-sdk-go-v2/config v1.31.17/go.mod h1:V8P7ILjp/Uef/aX8TjGk6OHZN6IKPM5YW6S78QnRD5c= github.com/aws/aws-sdk-go-v2/config v1.32.6 h1:hFLBGUKjmLAekvi1evLi5hVvFQtSo3GYwi+Bx4lpJf8= github.com/aws/aws-sdk-go-v2/config v1.32.6/go.mod h1:lcUL/gcd8WyjCrMnxez5OXkO3/rwcNmvfno62tnXNcI= -github.com/aws/aws-sdk-go-v2/credentials v1.18.21 h1:56HGpsgnmD+2/KpG0ikvvR8+3v3COCwaF4r+oWwOeNA= -github.com/aws/aws-sdk-go-v2/credentials v1.18.21/go.mod h1:3YELwedmQbw7cXNaII2Wywd+YY58AmLPwX4LzARgmmA= github.com/aws/aws-sdk-go-v2/credentials v1.19.6 h1:F9vWao2TwjV2MyiyVS+duza0NIRtAslgLUM0vTA1ZaE= github.com/aws/aws-sdk-go-v2/credentials v1.19.6/go.mod h1:SgHzKjEVsdQr6Opor0ihgWtkWdfRAIwxYzSJ8O85VHY= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 h1:T1brd5dR3/fzNFAQch/iBKeX07/ffu/cLu+q+RuzEWk= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13/go.mod h1:Peg/GBAQ6JDt+RoBf4meB1wylmAipb7Kg2ZFakZTlwk= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 h1:80+uETIWS1BqjnN9uJ0dBUaETh+P1XwFy5vwHwK5r9k= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16/go.mod h1:wOOsYuxYuB/7FlnVtzeBYRcjSRtQpAW0hCP7tIULMwo= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 h1:a+8/MLcWlIxo1lF9xaGt3J/u3yOZx+CdSveSNwjhD40= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13/go.mod h1:oGnKwIYZ4XttyU2JWxFrwvhF6YKiK/9/wmE3v3Iu9K8= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 h1:rgGwPzb82iBYSvHMHXc8h9mRoOUBZIGFgKb9qniaZZc= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16/go.mod h1:L/UxsGeKpGoIj6DxfhOWHWQ/kGKcd4I1VncE4++IyKA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 h1:HBSI2kDkMdWz4ZM7FjwE7e/pWDEZ+nR95x8Ztet1ooY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13/go.mod h1:YE94ZoDArI7awZqJzBAZ3PDD2zSfuP7w6P2knOzIn8M= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 h1:1jtGzuV7c82xnqOVfx2F0xmJcOw5374L7N6juGW6x6U= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16/go.mod h1:M2E5OQf+XLe+SZGmmpaI2yy+J326aFf6/+54PoxSANc= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13 h1:eg/WYAa12vqTphzIdWMzqYRVKKnCboVPRlvaybNCqPA= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13/go.mod h1:/FDdxWhz1486obGrKKC1HONd7krpk38LBt+dutLcN9k= github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.16 h1:CjMzUs78RDDv4ROu3JnJn/Ig1r6ZD7/T2DXLLRpejic= github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.16/go.mod h1:uVW4OLBqbJXSHJYA9svT9BluSvvwbzLQ2Crf6UPzR3c= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.262.0 h1:5qBb1XV/D18qtCHd3bmmxoVglI+fZ4QWuS/EB8kIXYQ= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.262.0/go.mod h1:NDdDLLW5PtLLXN661gKcvJvqAH5OBXsfhMlmKVu1/pY= github.com/aws/aws-sdk-go-v2/service/ec2 v1.279.0 h1:o7eJKe6VYAnqERPlLAvDW5VKXV6eTKv1oxTpMoDP378= github.com/aws/aws-sdk-go-v2/service/ec2 v1.279.0/go.mod h1:Wg68QRgy2gEGGdmTPU/UbVpdv8sM14bUZmF64KFwAsY= -github.com/aws/aws-sdk-go-v2/service/iam v1.49.2 h1:XeF6yEMX4/FxoSHCE1VNMOZ0t+mGnf/onqVe9dDVAlQ= -github.com/aws/aws-sdk-go-v2/service/iam v1.49.2/go.mod h1:cuEMbL1mNtO1sUyT+DYDNIA8Y7aJG1oIdgHqUk29Uzk= github.com/aws/aws-sdk-go-v2/service/iam v1.53.1 h1:xNCUk9XN6Pa9PyzbEfzgRpvEIVlqtth402yjaWvNMu4= github.com/aws/aws-sdk-go-v2/service/iam v1.53.1/go.mod h1:GNQZL4JRSGH6L0/SNGOtffaB1vmlToYp3KtcUIB0NhI= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 h1:x2Ibm/Af8Fi+BH+Hsn9TXGdT+hKbDd5XOTZxTMxDk7o= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3/go.mod h1:IW1jwyrQgMdhisceG8fQLmQIydcT/jWY21rFhzgaKwo= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4 h1:NvMjwvv8hpGUILarKw7Z4Q0w1H9anXKsesMxtw++MA4= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4/go.mod h1:455WPHSwaGj2waRSpQp7TsnpOnBfw8iDfPfbwl7KPJE= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.7 h1:DIBqIrJ7hv+e4CmIk2z3pyKT+3B6qVMgRsawHiR3qso= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.7/go.mod h1:vLm00xmBke75UmpNvOcZQ/Q30ZFjbczeLFqGx5urmGo= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 h1:kDqdFvMY4AtKoACfzIGD8A0+hbT41KTKF//gq7jITfM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13/go.mod h1:lmKuogqSU3HzQCwZ9ZtcqOc5XGMqtDK7OIc2+DxiUEg= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 h1:oHjJHeUy0ImIV0bsrX0X91GkV5nJAyv1l1CC9lnO0TI= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16/go.mod h1:iRSNGgOYmiYwSCXxXaKb9HfOEj40+oTKn8pTxMlYkRM= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 h1:zhBJXdhWIFZ1acfDYIhu4+LCzdUS2Vbcum7D01dXlHQ= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13/go.mod h1:JaaOeCE368qn2Hzi3sEzY6FgAZVCIYcC2nwbro2QCh8= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.16 h1:NSbvS17MlI2lurYgXnCOLvCFX38sBW4eiVER7+kkgsU= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.16/go.mod h1:SwT8Tmqd4sA6G1qaGdzWCJN99bUmPGHfRwwq3G5Qb+A= -github.com/aws/aws-sdk-go-v2/service/s3 v1.90.0 h1:ef6gIJR+xv/JQWwpa5FYirzoQctfSJm7tuDe3SZsUf8= -github.com/aws/aws-sdk-go-v2/service/s3 v1.90.0/go.mod h1:+wArOOrcHUevqdto9k1tKOF5++YTe9JEcPSc9Tx2ZSw= +github.com/aws/aws-sdk-go-v2/service/rds v1.113.1 h1:/vV0g/Su8rCTqT57UUYiFU/aRrPXz//fGDn1dkXblG4= +github.com/aws/aws-sdk-go-v2/service/rds v1.113.1/go.mod h1:q02df+DL73LN+jDXzj86tMsI6kKf1kfv61nB684H+o8= github.com/aws/aws-sdk-go-v2/service/s3 v1.95.0 h1:MIWra+MSq53CFaXXAywB2qg9YvVZifkk6vEGl/1Qor0= github.com/aws/aws-sdk-go-v2/service/s3 v1.95.0/go.mod h1:79S2BdqCJpScXZA2y+cpZuocWsjGjJINyXnOsf5DTz8= github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 h1:HpI7aMmJ+mm1wkSHIA2t5EaFFv5EFYXePW30p1EIrbQ= github.com/aws/aws-sdk-go-v2/service/signin v1.0.4/go.mod h1:C5RdGMYGlfM0gYq/tifqgn4EbyX99V15P2V3R+VHbQU= -github.com/aws/aws-sdk-go-v2/service/ssm v1.66.4 h1:UmkF0ipNy0Ps6csJl/ZRJ3K+DWe9q0A7LT3xfxoHbgg= -github.com/aws/aws-sdk-go-v2/service/ssm v1.66.4/go.mod h1:uNHuYAQazkHqpD+hVomA2+eDSuKJzerno7Fnha6N6/Y= github.com/aws/aws-sdk-go-v2/service/ssm v1.67.7 h1:0q42w8/mywPCzQD1IoWIBUCYfBJc5+fLwtZNpHffBSM= github.com/aws/aws-sdk-go-v2/service/ssm v1.67.7/go.mod h1:urlU9nfKJEfi0+8T9luB3f3Y0UnomH/yxI7tTrfH9es= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.1 h1:0JPwLz1J+5lEOfy/g0SURC9cxhbQ1lIMHMa+AHZSzz0= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.1/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k= github.com/aws/aws-sdk-go-v2/service/sso v1.30.8 h1:aM/Q24rIlS3bRAhTyFurowU8A0SMyGDtEOY/l/s/1Uw= github.com/aws/aws-sdk-go-v2/service/sso v1.30.8/go.mod h1:+fWt2UHSb4kS7Pu8y+BMBvJF0EWx+4H0hzNwtDNRTrg= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5 h1:OWs0/j2UYR5LOGi88sD5/lhN6TDLG6SfA7CqsQO9zF0= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 h1:AHDr0DaHIAo8c9t1emrzAlVDFp+iMMKnPdYy6XO4MCE= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12/go.mod h1:GQ73XawFFiWxyWXMHWfhiomvP3tXtdNar/fi8z18sx0= -github.com/aws/aws-sdk-go-v2/service/sts v1.39.1 h1:mLlUgHn02ue8whiR4BmxxGJLR2gwU6s6ZzJ5wDamBUs= -github.com/aws/aws-sdk-go-v2/service/sts v1.39.1/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk= github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 h1:SciGFVNZ4mHdm7gpD1dgZYnCuVdX1s+lFTg4+4DOy70= github.com/aws/aws-sdk-go-v2/service/sts v1.41.5/go.mod h1:iW40X4QBmUxdP+fZNOpfmkdMZqsovezbAeO+Ubiv2pk= -github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM= -github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= @@ -99,8 +61,6 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw= github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo= -github.com/gdamore/tcell/v2 v2.9.0 h1:N6t+eqK7/xwtRPwxzs1PXeRWnm0H9l02CrgJ7DLn1ys= -github.com/gdamore/tcell/v2 v2.9.0/go.mod h1:8/ZoqM9rxzYphT9tH/9LnunhV9oPBqwS8WHGYm5nrmo= github.com/gdamore/tcell/v2 v2.13.5 h1:YvWYCSr6gr2Ovs84dXbZLjDuOfQchhj8buOEqY52rpA= github.com/gdamore/tcell/v2 v2.13.5/go.mod h1:+Wfe208WDdB7INEtCsNrAN6O2m+wsTPk1RAovjaILlo= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= @@ -147,8 +107,6 @@ github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= -github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -186,15 +144,11 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= -golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -202,8 +156,6 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/ztictl/internal/ssm/client_pool.go b/ztictl/internal/ssm/client_pool.go index cf5d787..07d7c5f 100644 --- a/ztictl/internal/ssm/client_pool.go +++ b/ztictl/internal/ssm/client_pool.go @@ -9,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/aws/aws-sdk-go-v2/service/iam" + "github.com/aws/aws-sdk-go-v2/service/rds" "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/ssm" "github.com/aws/aws-sdk-go-v2/service/sts" @@ -25,6 +26,7 @@ type clientSet struct { IAMClient *iam.Client S3Client *s3.Client STSClient *sts.Client + RDSClient *rds.Client } type ClientPool struct { @@ -80,6 +82,7 @@ func (p *ClientPool) createClientSet(ctx context.Context, region string) (*clien IAMClient: iam.NewFromConfig(cfg), S3Client: s3.NewFromConfig(cfg), STSClient: sts.NewFromConfig(cfg), + RDSClient: rds.NewFromConfig(cfg), } return clients, nil @@ -125,6 +128,14 @@ func (p *ClientPool) GetSTSClient(ctx context.Context, region string) (*sts.Clie return clients.STSClient, nil } +func (p *ClientPool) GetRDSClient(ctx context.Context, region string) (*rds.Client, error) { + clients, err := p.GetClients(ctx, region) + if err != nil { + return nil, err + } + return clients.RDSClient, nil +} + func (p *ClientPool) GetPlatformClients(ctx context.Context, region string) (*ssm.Client, *ec2.Client, error) { clients, err := p.GetClients(ctx, region) if err != nil { From b936bba64f495c142ee3d505f1113046adaa57b4 Mon Sep 17 00:00:00 2001 From: ditahkk Date: Sun, 28 Dec 2025 11:09:37 -0500 Subject: [PATCH 3/6] Enhance QA documentation and improve code quality - Updated QA_SSM_TESTS.md to include additional formatting and clarity improvements. - Refined README.md in tools directory for better option descriptions. - Modernized .golangci.yml configuration for improved linting practices. - Modified Makefile to auto-detect version from git tags and added formatting target. - Enhanced README.md in ztictl with clearer installation and feature descriptions. - Optimized RDS command handling by reusing client pool for efficiency. - Improved SSH command handling by adding a delay for RDP client launch. - Updated fuzzy finder documentation to clarify usage and validation rules. - Enhanced validation documentation for instance states and SSM agent statuses. - Implemented signal draining in session management to prevent channel overflow. --- .github/ISSUE_TEMPLATE/bug_report.md | 14 +- .github/ISSUE_TEMPLATE/feature_request.md | 5 +- .github/ISSUE_TEMPLATE/region_request.md | 5 +- .github/workflows/auto-generate-docs.yml | 96 +-- .github/workflows/build.yml | 823 +++++++++------------- .prettierignore | 8 + .prettierrc | 7 + CHANGELOG.md | 77 +- CLAUDE.md | 64 +- CONTRIBUTING.md | 22 +- INSTALLATION.md | 57 +- Makefile | 17 +- README.md | 42 +- RELEASE_NOTES.md | 45 ++ RELEASE_NOTES.txt | 42 -- docs/CI_CD_AUTHENTICATION.md | 54 +- docs/CI_CD_PIPELINE.md | 300 ++++---- docs/COMMANDS.md | 74 +- docs/CONFIGURATION.md | 150 ++-- docs/IAM_PERMISSIONS.md | 52 +- docs/LOGGING.md | 6 +- docs/MULTI_REGION.md | 21 +- docs/NOTIFICATIONS.md | 35 +- docs/PRODUCTION_VERSION_FIX.md | 6 + docs/QUICK_START.md | 12 +- docs/REGIONS.md | 18 +- docs/TROUBLESHOOTING.md | 9 + docs/VERSION_CHECKING.md | 21 +- docs/development/BUILD_ARTIFACTS.md | 11 +- docs/development/RELEASE.md | 12 + docs/examples/github-actions-oidc.yml | 6 +- docs/examples/gitlab-ci-oidc.yml | 4 +- docs/ztiaws-demo.md | 191 +++-- scripts/README.md | 8 +- scripts/install.ps1 | 80 +++ scripts/install.sh | 92 +++ tests/QA_AUTHAWS_TESTS.md | 39 + tests/QA_SSM_TESTS.md | 44 +- tools/README.md | 20 +- ztictl/.golangci.yml | 56 +- ztictl/Makefile | 25 +- ztictl/README.md | 41 +- ztictl/cmd/ztictl/rds_power.go | 28 +- ztictl/cmd/ztictl/ssm_ssh.go | 5 +- ztictl/docs/FUZZY_FINDER_FEATURES.md | 27 +- ztictl/docs/VALIDATION.md | 39 +- ztictl/internal/ssm/manager.go | 14 + 47 files changed, 1717 insertions(+), 1107 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 RELEASE_NOTES.md delete mode 100644 RELEASE_NOTES.txt create mode 100644 scripts/install.ps1 create mode 100755 scripts/install.sh diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 98dc71d..0b6f389 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -5,11 +5,13 @@ title: '[BUG] ' labels: bug assignees: '' --- + **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Run command '...' 2. With arguments '....' 3. See error @@ -18,12 +20,14 @@ Steps to reproduce the behavior: A clear and concise description of what you expected to happen. **Environment (please complete the following information):** - - OS: [e.g. Ubuntu 20.04] - - Shell: [e.g. bash, zsh, PowerShell] - - AWS CLI Version: [e.g. 2.11.0] - - SSM Plugin Version: [e.g. 1.2.463.0] + +- OS: [e.g. Ubuntu 20.04] +- Shell: [e.g. bash, zsh, PowerShell] +- AWS CLI Version: [e.g. 2.11.0] +- SSM Plugin Version: [e.g. 1.2.463.0] **Output of tool check command** + ``` # For SSM issues: Paste the output of `ssm check` here @@ -33,4 +37,4 @@ Paste the output of `authaws check` here ``` **Additional context** -Add any other context about the problem here. \ No newline at end of file +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index e56d5cd..f20e46a 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -5,6 +5,7 @@ title: '[FEATURE] ' labels: enhancement assignees: '' --- + **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] @@ -15,6 +16,7 @@ A clear and concise description of what you want to happen. A clear and concise description of any alternative solutions or features you've considered. **Additional context** + - Tool affected: [e.g., ssm, authaws, both] - AWS Region(s) affected: [e.g., all regions, specific regions] - OS Environment: [e.g., Linux, MacOS, Windows] @@ -28,8 +30,9 @@ If applicable, add examples of how the feature would work, maybe with mock comma Optional: If you have any ideas about how this could be implemented, share them here. **Feature checklist** + - [ ] This feature maintains backward compatibility - [ ] This feature requires documentation updates - [ ] This feature requires new dependencies - [ ] This feature affects multiple regions -- [ ] This feature requires AWS CLI/SSM plugin changes \ No newline at end of file +- [ ] This feature requires AWS CLI/SSM plugin changes diff --git a/.github/ISSUE_TEMPLATE/region_request.md b/.github/ISSUE_TEMPLATE/region_request.md index c1f9a44..5f151ea 100644 --- a/.github/ISSUE_TEMPLATE/region_request.md +++ b/.github/ISSUE_TEMPLATE/region_request.md @@ -5,7 +5,9 @@ title: '[REGION] ' labels: enhancement, region assignees: '' --- + **AWS Region Details** + - AWS Region Name: [e.g. eu-west-2] - Location/City: [e.g. London] - Proposed Shortcode: [e.g. euw2] @@ -14,9 +16,10 @@ assignees: '' Link to AWS documentation confirming this region. **Region Status** + - [ ] Region is generally available - [ ] Region supports SSM - [ ] Region is accessible in my account **Additional context** -Add any other context about the region request here. \ No newline at end of file +Add any other context about the region request here. diff --git a/.github/workflows/auto-generate-docs.yml b/.github/workflows/auto-generate-docs.yml index bc06c78..6d188d0 100644 --- a/.github/workflows/auto-generate-docs.yml +++ b/.github/workflows/auto-generate-docs.yml @@ -12,52 +12,52 @@ jobs: runs-on: ubuntu-latest permissions: contents: write - + steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Need full history for changelog generation - - - name: Extract version from branch name - id: version - run: | - VERSION=${GITHUB_REF#refs/heads/release/} - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "Extracted version: $VERSION" - - - name: Get latest release tag - id: latest_tag - run: | - LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") - echo "latest_tag=$LATEST_TAG" >> $GITHUB_OUTPUT - echo "Latest tag: ${LATEST_TAG:-'(none found)'}" - - - name: Generate release documentation - run: | - # Make script executable - chmod +x tools/02_release_docs_generator.sh - - # Run the release documentation generator - if [ -n "${{ steps.latest_tag.outputs.latest_tag }}" ]; then - ./tools/02_release_docs_generator.sh \ - --version "${{ steps.version.outputs.version }}" \ - --latest-tag "${{ steps.latest_tag.outputs.latest_tag }}" \ - --force - else - ./tools/02_release_docs_generator.sh \ - --version "${{ steps.version.outputs.version }}" \ - --force - fi - - - name: Commit generated documentation and version updates - run: | - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - git add CHANGELOG.md RELEASE_NOTES.txt ztictl/Makefile ztictl/cmd/ztictl/root.go - if git diff --staged --quiet; then - echo "No changes to commit" - else - git commit -m "Auto-generate changelog, release notes, and update version to ${{ steps.version.outputs.version }}" - git push - fi \ No newline at end of file + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Need full history for changelog generation + + - name: Extract version from branch name + id: version + run: | + VERSION=${GITHUB_REF#refs/heads/release/} + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Extracted version: $VERSION" + + - name: Get latest release tag + id: latest_tag + run: | + LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") + echo "latest_tag=$LATEST_TAG" >> $GITHUB_OUTPUT + echo "Latest tag: ${LATEST_TAG:-'(none found)'}" + + - name: Generate release documentation + run: | + # Make script executable + chmod +x tools/02_release_docs_generator.sh + + # Run the release documentation generator + if [ -n "${{ steps.latest_tag.outputs.latest_tag }}" ]; then + ./tools/02_release_docs_generator.sh \ + --version "${{ steps.version.outputs.version }}" \ + --latest-tag "${{ steps.latest_tag.outputs.latest_tag }}" \ + --force + else + ./tools/02_release_docs_generator.sh \ + --version "${{ steps.version.outputs.version }}" \ + --force + fi + + - name: Commit generated documentation and version updates + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add CHANGELOG.md RELEASE_NOTES.txt ztictl/Makefile ztictl/cmd/ztictl/root.go + if git diff --staged --quiet; then + echo "No changes to commit" + else + git commit -m "Auto-generate changelog, release notes, and update version to ${{ steps.version.outputs.version }}" + git push + fi diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a398839..b9989f1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,398 +1,275 @@ name: CI/CD Pipeline -# Git Flow Pattern: -# 1. Create release branch from main (e.g., release/v2.0.0) -# 2. Make release preparations (version updates, changelog, etc.) -# 3. Push tag to trigger this workflow (e.g., v2.0.0) -# 4. Workflow builds, tests, and creates GitHub release -# 5. MANUAL: Merge release branch back to main when ready -# 6. Main branch stays up-to-date with all release changes +# Efficient path-based triggering: +# - Go code changes → test + security jobs +# - Script changes → lint-scripts job +# - Tags → build + release jobs only on: push: - branches: [ main, 'feature/*', 'feat/*', 'issue/*', 'release/*' ] + branches: [main, 'feature/*', 'feat/*', 'issue/*', 'release/*'] tags: - - 'v*' - paths: - - 'ztictl/**' - - 'authaws' - - 'ssm' - - 'src/**' - - 'tests/**' - - 'tools/**' - - '01_install.sh' - - '02_uninstall.sh' - - '.github/workflows/build.yml' - - 'go.mod' - - 'go.sum' - - 'Makefile' - - 'CHANGELOG.md' + - '[0-9]*' pull_request: - branches: [ main, 'release/*' ] - paths: - - 'ztictl/**' - - 'authaws' - - 'ssm' - - 'src/**' - - 'tests/**' - - 'tools/**' - - '01_install.sh' - - '02_uninstall.sh' - - '.github/workflows/build.yml' - - 'go.mod' - - 'go.sum' - - 'Makefile' - - 'CHANGELOG.md' + branches: [main, 'release/*'] workflow_dispatch: jobs: - test-shell: - name: Test Shell Scripts + # Determine which jobs to run based on changed files + changes: + name: 1. Detect Changes + runs-on: ubuntu-latest + if: github.event_name != 'workflow_dispatch' && !startsWith(github.ref, 'refs/tags/') + outputs: + go: ${{ steps.filter.outputs.go }} + scripts: ${{ steps.filter.outputs.scripts }} + workflow: ${{ steps.filter.outputs.workflow }} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + go: + - 'ztictl/**' + - 'go.mod' + - 'go.sum' + - 'Makefile' + scripts: + - 'authaws' + - 'ssm' + - 'src/**' + - 'scripts/**' + - 'tools/**' + - '01_install.sh' + - '02_uninstall.sh' + workflow: + - '.github/workflows/**' + + lint-scripts: + name: 2a. Lint Scripts runs-on: ${{ matrix.os }} - if: github.event_name == 'pull_request' || (github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')) - + needs: changes + if: needs.changes.outputs.scripts == 'true' || needs.changes.outputs.workflow == 'true' + strategy: matrix: os: [ubuntu-latest, macos-latest] - + steps: - - uses: actions/checkout@v4 - - - name: Install shellcheck - run: | - if [ "$RUNNER_OS" == "Linux" ]; then - sudo apt-get update && sudo apt-get install -y shellcheck - else - brew install shellcheck - fi - shell: bash - - - name: Run shellcheck on shell scripts - run: | - echo "Checking shell scripts..." - shellcheck -x authaws ssm src/*.sh tools/*.sh scripts/*.sh 01_install.sh 02_uninstall.sh - echo "Shell scripts passed validation" - - - name: Test shell script syntax - run: | - echo "Testing shell script syntax..." - for script in authaws ssm src/*.sh tools/*.sh scripts/*.sh 01_install.sh 02_uninstall.sh; do - if [ -f "$script" ]; then - bash -n "$script" || exit 1 + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install shellcheck + run: | + if [ "$RUNNER_OS" == "Linux" ]; then + sudo apt-get update && sudo apt-get install -y shellcheck + else + brew install shellcheck fi - done - echo "Shell script syntax is valid" - - - name: Notify on shell test failure - if: failure() && matrix.os == 'ubuntu-latest' && github.event_name == 'pull_request' - env: - GOOGLE_CHAT_WEBHOOK: ${{ secrets.GOOGLE_CHAT_WEBHOOK }} - run: | - if [[ -n "$GOOGLE_CHAT_WEBHOOK" ]]; then - ./scripts/send-pr-notification.sh \ - --pr-title '${{ github.event.pull_request.title }}' \ - --pr-number '${{ github.event.pull_request.number }}' \ - --pr-url '${{ github.event.pull_request.html_url }}' \ - --author '${{ github.event.pull_request.user.login }}' \ - --repository '${{ github.repository }}' \ - --status "failure" \ - --message "❌ Shell script tests failed - PR needs attention" || true - fi - continue-on-error: true - - test-go: - name: Test Go Code + shell: bash + + - name: Lint shell scripts (shellcheck) + run: | + echo "Checking shell scripts with shellcheck..." + shellcheck -x authaws ssm src/*.sh tools/*.sh scripts/*.sh 01_install.sh 02_uninstall.sh + echo "Shell scripts passed shellcheck" + + - name: Validate shell script syntax + run: | + echo "Validating shell script syntax..." + for script in authaws ssm src/*.sh tools/*.sh scripts/*.sh 01_install.sh 02_uninstall.sh; do + if [ -f "$script" ]; then + bash -n "$script" || exit 1 + fi + done + echo "Shell script syntax is valid" + + - name: Notify on shell test failure + if: failure() && matrix.os == 'ubuntu-latest' && github.event_name == 'pull_request' + env: + GOOGLE_CHAT_WEBHOOK: ${{ secrets.GOOGLE_CHAT_WEBHOOK }} + run: | + if [[ -n "$GOOGLE_CHAT_WEBHOOK" ]]; then + ./scripts/send-pr-notification.sh \ + --pr-title '${{ github.event.pull_request.title }}' \ + --pr-number '${{ github.event.pull_request.number }}' \ + --pr-url '${{ github.event.pull_request.html_url }}' \ + --author '${{ github.event.pull_request.user.login }}' \ + --repository '${{ github.repository }}' \ + --status "failure" \ + --message "Shell script tests failed - PR needs attention" || true + fi + continue-on-error: true + + test: + name: 2b. Test (${{ matrix.os }}) runs-on: ${{ matrix.os }} - if: github.event_name == 'pull_request' || (github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')) - + needs: changes + if: needs.changes.outputs.go == 'true' || needs.changes.outputs.workflow == 'true' + strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] - + steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version: '1.25' - - - name: Get dependencies - working-directory: ./ztictl - run: go mod download - - - name: Run tests - working-directory: ./ztictl - env: - # Use mock AWS credentials for testing - matches internal/testutil/aws.go - AWS_EC2_METADATA_DISABLED: "true" - AWS_ACCESS_KEY_ID: "AKIAIOSFODNN7EXAMPLE" - AWS_SECRET_ACCESS_KEY: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" - AWS_SESSION_TOKEN: "test-session-token" - AWS_REGION: "ca-central-1" - run: go test ./... - - - name: Run go vet - working-directory: ./ztictl - run: go vet ./... - - - name: Check formatting - working-directory: ./ztictl - shell: bash - run: | - # Ensure consistent line endings across platforms - if [[ "${{ matrix.os }}" == "windows-latest" ]]; then - # Configure git to handle line endings properly for this check - git config core.autocrlf false - # Re-checkout files with correct line endings - git checkout HEAD -- . - fi - - # Check if any files need formatting - UNFORMATTED=$(gofmt -s -l .) - if [ -n "$UNFORMATTED" ]; then - echo "The following files are not formatted correctly:" - echo "$UNFORMATTED" - echo "" - echo "Please run 'gofmt -s -w .' to fix formatting issues." - exit 1 - fi - echo "All Go files are properly formatted." - - - name: Test build - working-directory: ./ztictl - run: go build -o ztictl${{ matrix.os == 'windows-latest' && '.exe' || '' }} ./cmd/ztictl - - - name: Test CLI commands (no auth required) - working-directory: ./ztictl - shell: bash - run: | - # Set executable name based on OS - if [[ "${{ matrix.os }}" == "windows-latest" ]]; then - ZTICTL_EXE="./ztictl.exe" - else - ZTICTL_EXE="./ztictl" - fi - - # Test help commands - $ZTICTL_EXE --help - $ZTICTL_EXE config --help - $ZTICTL_EXE auth --help - $ZTICTL_EXE ssm --help - - # Test version - $ZTICTL_EXE --version - - # Test config validation (should fail gracefully without config) - $ZTICTL_EXE config validate || echo "Expected to fail without config" - - # Test config show (should show defaults) - $ZTICTL_EXE config show || echo "Expected to work with defaults" - - # Test system check (should work without AWS) - $ZTICTL_EXE config check || echo "Expected to show missing dependencies" - - - name: Notify on Go test failure - if: failure() && matrix.os == 'ubuntu-latest' && github.event_name == 'pull_request' - env: - GOOGLE_CHAT_WEBHOOK: ${{ secrets.GOOGLE_CHAT_WEBHOOK }} - run: | - if [[ -n "$GOOGLE_CHAT_WEBHOOK" ]]; then - ./scripts/send-pr-notification.sh \ - --pr-title '${{ github.event.pull_request.title }}' \ - --pr-number '${{ github.event.pull_request.number }}' \ - --pr-url '${{ github.event.pull_request.html_url }}' \ - --author '${{ github.event.pull_request.user.login }}' \ - --repository '${{ github.repository }}' \ - --status "failure" \ - --message "❌ Go tests failed - PR needs attention" || true - fi - continue-on-error: true + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.24' + + - name: Get dependencies + working-directory: ./ztictl + run: go mod download + + - name: Run tests + working-directory: ./ztictl + env: + AWS_EC2_METADATA_DISABLED: 'true' + AWS_ACCESS_KEY_ID: 'AKIAIOSFODNN7EXAMPLE' + AWS_SECRET_ACCESS_KEY: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY' + AWS_SESSION_TOKEN: 'test-session-token' + AWS_REGION: 'ca-central-1' + run: go test -v ./... + + - name: Run go vet + working-directory: ./ztictl + run: go vet ./... + + - name: Check formatting + working-directory: ./ztictl + shell: bash + run: | + if [[ "${{ matrix.os }}" == "windows-latest" ]]; then + git config core.autocrlf false + git checkout HEAD -- . + fi + + UNFORMATTED=$(gofmt -s -l .) + if [ -n "$UNFORMATTED" ]; then + echo "The following files are not formatted correctly:" + echo "$UNFORMATTED" + echo "" + echo "Please run 'gofmt -s -w .' to fix formatting issues." + exit 1 + fi + echo "All Go files are properly formatted." + + - name: Test build + working-directory: ./ztictl + run: go build -o ztictl${{ matrix.os == 'windows-latest' && '.exe' || '' }} ./cmd/ztictl + + - name: Test CLI commands + working-directory: ./ztictl + shell: bash + run: | + if [[ "${{ matrix.os }}" == "windows-latest" ]]; then + ZTICTL_EXE="./ztictl.exe" + else + ZTICTL_EXE="./ztictl" + fi + + $ZTICTL_EXE --help + $ZTICTL_EXE config --help + $ZTICTL_EXE auth --help + $ZTICTL_EXE ssm --help + $ZTICTL_EXE --version + $ZTICTL_EXE config validate || echo "Expected to fail without config" + $ZTICTL_EXE config show || echo "Expected to work with defaults" + $ZTICTL_EXE config check || echo "Expected to show missing dependencies" + + - name: Notify on Go test failure + if: failure() && matrix.os == 'ubuntu-latest' && github.event_name == 'pull_request' + env: + GOOGLE_CHAT_WEBHOOK: ${{ secrets.GOOGLE_CHAT_WEBHOOK }} + run: | + if [[ -n "$GOOGLE_CHAT_WEBHOOK" ]]; then + ./scripts/send-pr-notification.sh \ + --pr-title '${{ github.event.pull_request.title }}' \ + --pr-number '${{ github.event.pull_request.number }}' \ + --pr-url '${{ github.event.pull_request.html_url }}' \ + --author '${{ github.event.pull_request.user.login }}' \ + --repository '${{ github.repository }}' \ + --status "failure" \ + --message "Go tests failed - PR needs attention" || true + fi + continue-on-error: true security: - name: Security Analysis + name: 3. Security Analysis runs-on: ubuntu-latest - needs: [test-shell, test-go] - if: github.event_name == 'pull_request' && github.base_ref == 'main' - env: - GOPROXY: proxy.golang.org,direct - GOSUMDB: sum.golang.org - + needs: [changes, test] + if: | + github.event_name == 'pull_request' && + github.base_ref == 'main' && + (needs.changes.outputs.go == 'true' || needs.changes.outputs.workflow == 'true') + steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version: '1.25' - - - name: Download dependencies - working-directory: ./ztictl - run: go mod download - - - name: Install security tools - run: | - # Install govulncheck for official Go vulnerability checking - go install golang.org/x/vuln/cmd/govulncheck@latest - - # Install golangci-lint v2.4.0 for comprehensive linting and security checks - # Using specific version to match local development environment - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.4.0 - - # Verify installations - govulncheck -version || echo "govulncheck installation failed" - $(go env GOPATH)/bin/golangci-lint version || echo "golangci-lint installation failed" - - - name: Run Trivy vulnerability scanner - uses: aquasecurity/trivy-action@master - with: - scan-type: 'fs' - scan-ref: './ztictl' - format: 'table' - exit-code: '0' # Don't fail the build - continue-on-error: true - - - name: Run GoSec security scan - working-directory: ./ztictl - run: | - echo "🔒 Running comprehensive security analysis..." - - # Run golangci-lint with security linters enabled - echo "Running golangci-lint with security checks..." - $(go env GOPATH)/bin/golangci-lint run --enable gosec --out-format=json > golangci-security-report.json || true - $(go env GOPATH)/bin/golangci-lint run --enable gosec || true - - # Try to install and run standalone gosec as backup - echo "Installing standalone GoSec..." - curl -sfL https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s -- -b $(go env GOPATH)/bin latest || true - - if [ -f "$(go env GOPATH)/bin/gosec" ]; then - echo "Running standalone GoSec analysis..." - $(go env GOPATH)/bin/gosec -fmt json -out gosec-report.json ./... || true - $(go env GOPATH)/bin/gosec -fmt table ./... || true - else - echo "Standalone GoSec installation failed, using golangci-lint results only" - fi - - echo "📊 Security analysis completed" - continue-on-error: true - - - name: Upload security reports - if: always() - uses: actions/upload-artifact@v4 - with: - name: security-reports - path: | - ztictl/gosec-report.json - ztictl/golangci-security-report.json - retention-days: 30 - continue-on-error: true - - - name: Run Go vulnerability check - working-directory: ./ztictl - run: | - echo "🔍 Running Go vulnerability check..." - govulncheck ./... || true - echo "" - echo "📊 Go vulnerability check completed" - continue-on-error: true - - - name: Check for outdated dependencies - working-directory: ./ztictl - run: | - echo "🔍 Checking for outdated Go dependencies..." - echo "Current dependencies:" - go list -m all - echo "" - echo "Checking for updates (this is informational only):" - go list -u -m all | grep -E '\[.*\]' || echo "All dependencies appear to be up to date" - echo "" - echo "📊 Dependency update check completed" - continue-on-error: true - - - name: Run comprehensive quality checks - working-directory: ./ztictl - run: | - echo "🔍 Running comprehensive quality checks..." - - # Standard Go quality checks - echo "Running go vet..." - go vet ./... || echo "go vet found issues" - - echo "Running go fmt check..." - UNFORMATTED=$(gofmt -s -l .) - if [ -n "$UNFORMATTED" ]; then - echo "⚠️ The following files need formatting:" - echo "$UNFORMATTED" - else - echo "✅ All Go files are properly formatted" - fi - - # Build test to ensure everything compiles - echo "Testing compilation..." - go build ./... || echo "❌ Build failed" - - echo "📊 Quality checks completed" - continue-on-error: true - - - name: Security scan summary - run: | - echo "🛡️ Security & Quality Analysis Summary" - echo "========================================" - echo "✅ Trivy filesystem scan completed" - echo "✅ golangci-lint with GoSec enabled completed" - echo "✅ Standalone GoSec analysis attempted" - echo "✅ Go vulnerability check completed" - echo "✅ Dependency update check completed" - echo "✅ Comprehensive quality checks completed" - echo "" - echo "ℹ️ This job runs security and quality scans for informational purposes." - echo "ℹ️ Review the output above for any security findings or quality issues." - echo "ℹ️ Security reports are uploaded as artifacts for detailed review." - echo "ℹ️ Consider updating outdated dependencies regularly." - - - name: Notify on security failure - if: failure() && github.event_name == 'pull_request' - env: - GOOGLE_CHAT_WEBHOOK: ${{ secrets.GOOGLE_CHAT_WEBHOOK }} - run: | - if [[ -n "$GOOGLE_CHAT_WEBHOOK" ]]; then - ./scripts/send-pr-notification.sh \ - --pr-title '${{ github.event.pull_request.title }}' \ - --pr-number '${{ github.event.pull_request.number }}' \ - --pr-url '${{ github.event.pull_request.html_url }}' \ - --author '${{ github.event.pull_request.user.login }}' \ - --repository '${{ github.repository }}' \ - --status "failure" \ - --message "❌ Security analysis failed - PR needs attention" || true - fi - continue-on-error: true - - - name: Notify on all tests success - if: success() && github.event_name == 'pull_request' - env: - GOOGLE_CHAT_WEBHOOK: ${{ secrets.GOOGLE_CHAT_WEBHOOK }} - run: | - if [[ -n "$GOOGLE_CHAT_WEBHOOK" ]]; then - ./scripts/send-pr-notification.sh \ - --pr-title '${{ github.event.pull_request.title }}' \ - --pr-number '${{ github.event.pull_request.number }}' \ - --pr-url '${{ github.event.pull_request.html_url }}' \ - --author '${{ github.event.pull_request.user.login }}' \ - --repository '${{ github.repository }}' \ - --status "success" \ - --message "✅ All tests passed - PR is ready for review" || true - fi - continue-on-error: true + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.24' + + - name: Download dependencies + working-directory: ./ztictl + run: go mod download + - name: Install security tools + run: | + go install golang.org/x/vuln/cmd/govulncheck@latest + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.61.0 + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@0.33.1 + with: + scan-type: 'fs' + scan-ref: './ztictl' + format: 'table' + exit-code: '0' + continue-on-error: true + + - name: Run golangci-lint with security checks + working-directory: ./ztictl + run: $(go env GOPATH)/bin/golangci-lint run --enable gosec || true + continue-on-error: true + + - name: Run Go vulnerability check + working-directory: ./ztictl + run: govulncheck ./... || true + continue-on-error: true + + - name: Run go vet + working-directory: ./ztictl + run: go vet ./... + + - name: Notify on security success + if: success() && github.event_name == 'pull_request' + env: + GOOGLE_CHAT_WEBHOOK: ${{ secrets.GOOGLE_CHAT_WEBHOOK }} + run: | + if [[ -n "$GOOGLE_CHAT_WEBHOOK" ]]; then + ./scripts/send-pr-notification.sh \ + --pr-title '${{ github.event.pull_request.title }}' \ + --pr-number '${{ github.event.pull_request.number }}' \ + --pr-url '${{ github.event.pull_request.html_url }}' \ + --author '${{ github.event.pull_request.user.login }}' \ + --repository '${{ github.repository }}' \ + --status "success" \ + --message "All tests passed - PR is ready for review" || true + fi + continue-on-error: true build: - name: Cross-Platform Build + name: 4. Build (${{ matrix.goos }}-${{ matrix.goarch }}) runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' - + strategy: matrix: include: @@ -408,138 +285,136 @@ jobs: goarch: amd64 - goos: windows goarch: arm64 - + steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version: '1.25' - - - name: Get dependencies - working-directory: ./ztictl - run: go mod download - - - name: Build binary - working-directory: ./ztictl - env: - GOOS: ${{ matrix.goos }} - GOARCH: ${{ matrix.goarch }} - run: | - # Set version from tag or default with git commit - GIT_COMMIT=${GITHUB_SHA::8} - if [[ "$GITHUB_REF" == refs/tags/* ]]; then - BASE_VERSION=${GITHUB_REF#refs/tags/v} - VERSION="${BASE_VERSION}-${GIT_COMMIT}" - else - VERSION="dev-${GIT_COMMIT}" - fi - - echo "Building version: ${VERSION}" - - # Set output filename - BINARY_NAME="ztictl-${{ matrix.goos }}-${{ matrix.goarch }}" - if [ "${{ matrix.goos }}" = "windows" ]; then - BINARY_NAME="${BINARY_NAME}.exe" - fi - - # Build with version and optimization flags - go build \ - -ldflags "-X main.Version=${VERSION} -s -w" \ - -o "${BINARY_NAME}" \ - ./cmd/ztictl - - # Verify binary - if [ "${{ matrix.goos }}" = "linux" ] && [ "${{ matrix.goarch }}" = "amd64" ]; then - ./"${BINARY_NAME}" --version - fi - - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: ztictl-${{ matrix.goos }}-${{ matrix.goarch }} - path: ztictl/ztictl-${{ matrix.goos }}-${{ matrix.goarch }}* - retention-days: 30 + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.24' + + - name: Get dependencies + working-directory: ./ztictl + run: go mod download + + - name: Build binary + working-directory: ./ztictl + env: + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} + run: | + GIT_COMMIT=${GITHUB_SHA::8} + if [[ "$GITHUB_REF" == refs/tags/* ]]; then + VERSION="${GITHUB_REF#refs/tags/}" + else + VERSION="dev-${GIT_COMMIT}" + fi + + echo "Building version: ${VERSION}" + + BINARY_NAME="ztictl-${{ matrix.goos }}-${{ matrix.goarch }}" + if [ "${{ matrix.goos }}" = "windows" ]; then + BINARY_NAME="${BINARY_NAME}.exe" + fi + + go build \ + -ldflags "-X main.Version=${VERSION} -s -w" \ + -o "${BINARY_NAME}" \ + ./cmd/ztictl + + if [ "${{ matrix.goos }}" = "linux" ] && [ "${{ matrix.goarch }}" = "amd64" ]; then + ./"${BINARY_NAME}" --version + fi + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ztictl-${{ matrix.goos }}-${{ matrix.goarch }} + path: ztictl/ztictl-${{ matrix.goos }}-${{ matrix.goarch }}* + retention-days: 30 release: - name: GitHub Release + name: 5. GitHub Release runs-on: ubuntu-latest needs: [build] - if: startsWith(github.ref, 'refs/tags/v') - + if: startsWith(github.ref, 'refs/tags/') + permissions: + contents: write + steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Download all artifacts - uses: actions/download-artifact@v4 - with: - path: artifacts/ - - - name: Prepare release binaries - run: | - mkdir -p release/ - - # Copy binaries directly for easier installation - for dir in artifacts/*/; do - platform=$(basename "$dir") - cd "$dir" - - # Copy binary directly (users expect direct download) - cp ./* "../../release/" - - # Also create archives for those who prefer them - if [[ "$platform" == *"windows"* ]]; then - zip -r "../../release/${platform}.zip" ./* - else - tar -czf "../../release/${platform}.tar.gz" ./* - fi - - cd - > /dev/null - done - - echo "Direct binaries and archives:" - ls -la release/ - - - name: Create GitHub Release - uses: softprops/action-gh-release@v1 - with: - files: release/* - draft: false - prerelease: false - body_path: RELEASE_NOTES.txt - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - # Notification for releases (when version tags are pushed) + - name: Checkout code + uses: actions/checkout@v4 + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts/ + + - name: Prepare release binaries + run: | + mkdir -p release/ + + for dir in artifacts/*/; do + platform=$(basename "$dir") + cd "$dir" + cp ./* "../../release/" + if [[ "$platform" == *"windows"* ]]; then + zip -r "../../release/${platform}.zip" ./* + else + tar -czf "../../release/${platform}.tar.gz" ./* + fi + cd - > /dev/null + done + + cp scripts/install.sh release/ + cp scripts/install.ps1 release/ + + echo "Release binaries:" + ls -la release/ + + - name: Generate checksums + run: | + cd release + sha256sum * > checksums.txt + cat checksums.txt + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + files: release/* + body_path: RELEASE_NOTES.md + draft: false + prerelease: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + release-notification: + name: 6. Release Notification runs-on: ubuntu-latest - if: startsWith(github.ref, 'refs/tags/v') + if: startsWith(github.ref, 'refs/tags/') needs: [release] + steps: - name: Checkout code uses: actions/checkout@v4 - + - name: Send release notification env: GOOGLE_CHAT_WEBHOOK: ${{ secrets.GOOGLE_CHAT_WEBHOOK }} run: | set -euo pipefail - - # Extract version from tag with validation - if [[ "$GITHUB_REF" =~ ^refs/tags/v.+ ]]; then + + if [[ "$GITHUB_REF" =~ ^refs/tags/[0-9].+ ]]; then VERSION=${GITHUB_REF#refs/tags/} else echo "Error: Invalid tag format in GITHUB_REF: $GITHUB_REF" exit 1 fi - - # Create release URL + RELEASE_URL="https://github.com/${{ github.repository }}/releases/tag/${VERSION}" - + ./scripts/send-release-notification.sh \ --version "$VERSION" \ --release-url "$RELEASE_URL" \ diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..3ccf060 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,8 @@ +ztictl/bin/ +ztictl/builds/ +*.go +go.mod +go.sum +*.exe +*.test +coverage.* diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..65aec79 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": false, + "singleQuote": true, + "tabWidth": 2, + "useTabs": false, + "proseWrap": "preserve" +} diff --git a/CHANGELOG.md b/CHANGELOG.md index a857075..baa5b68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## [v2.11.0] - 2025-11-13 ### Added + - Enhance AWS SSO and CI/CD Integration - Enhance AWS SSO and CI/CD Integration - Enhance power operation handling and validation @@ -14,11 +15,13 @@ - feat: Update EC2 Test Manager script with default subnet and security group IDs, add dry run option, and improve resource discovery logging ### Fixed + - fix: Update build scripts and enhance error handling in OIDC examples - fix: Update script paths and improve error handling in notification scripts - fix: Increase HTTP client timeout for EC2 instance detection and add warning for proxy parsing errors ### Changed + - Wrap commands in proper markdown code blocks - docs: finalize ZTiAWS demo documentation with consistent numbering and formatting - Docs: add proper Markdown headers and syntax highlighting for config section @@ -54,6 +57,7 @@ ## [v2.9.0] - 2025-10-09 ### Added + - **Enhanced Account/Role Selector UI**: Clean, compact fuzzy finder interface displays 5 items by default (previously showed all accounts, flooding terminal) - **Customizable Display Height**: New `ZTICTL_SELECTOR_HEIGHT` environment variable (range: 1-20, default: 5) allows users to control selector size - **Improved Visual Layout**: Bottom-positioned bordered selector with integrated preview panel showing account/role details @@ -61,48 +65,52 @@ - **Better UX**: Matches familiar fzf `--height=20%` behavior for consistent user experience ### Fixed + - Merge conflict resolution in fuzzy finder implementation with proper bounds checking - Added documentation comments explaining pointer usage with `WithHotReload()` option **Full Changelog**: https://github.com/zsoftly/ztiaws/compare/v2.8.2...v2.9.0 - ## [v2.8.2] - 2025-10-07 ### Added + - AWS SSO pagination support: Fetch all available accounts and roles instead of only first page (5-10 items) - Users can now search and select from complete account/role inventory using fuzzy finder - Comprehensive pagination tests covering single/multiple pages, empty results, and error scenarios - Modern Go error handling patterns using `errors.As()` throughout codebase ### Fixed + - Session Manager Plugin detection: Correctly validate exit code 255 instead of accepting any exit error - Authentication error handling: Return `(false, nil)` on config errors to maintain function contract - Import ordering: Standardized to stdlib, external, internal per Go conventions - Code quality: Removed empty else blocks, improved error wrapping with `%w` format verb - ## [v2.8.1] - 2025-10-03 ### Added + - feat: add automatic version update check (queries GitHub for latest release) - feat: implement 24-hour version check caching to reduce API calls ### Fixed + - fix: correct semantic version comparison by numerically comparing major.minor.patch (e.g., 2.10.0 > 2.2.0) - fix: clean shell completion output by skipping splash/log output during completion requests - fix: change "Using config file" log message from Info to Debug level to prevent unwanted output - ## [v2.8.0] - 2025-09-15 ### Added + - feat: enhance multi-OS support and automatic platform detection in ztictl - feat: enhance Windows command validation and update breaking changes in platform builders - Implement Linux and Windows command builders for platform-specific operations - feat: add cross-platform test writing guidelines and improve error handling in sample config tests ### Fixed + - fix: update AMI ID handling and improve logging for EC2 instance creation - fix: ensure Unix-style path handling in LinuxBuilder commands - fix: Resolve Windows test isolation issues in TestAuthLoginCmd @@ -112,6 +120,7 @@ - fix: Resolve config validation issues in ztictl ### Changed + - refactor: remove outdated breaking change comments and improve code clarity - Refactor SSM client management and enhance platform builders - Refactor config tests for improved clarity and coverage @@ -123,10 +132,10 @@ - Fix Windows CI: Add missing test isolation to auth tests - Fix Windows CI: Handle config loading gracefully in CI environments - ## [v2.7.0] - 2025-09-10 ### Added + - feat: auto-update version in Makefile and root.go during release - Enhance bash completion installation logic: add support for system path installation with sudo, improve path validation, and update test cases for mocked sudo behavior. - Add AWS credential handling to tests: disable EC2 IMDS to prevent timeouts and CI/CD failures @@ -137,6 +146,7 @@ - Add multi-region command execution tests and region configuration setup ### Changed + - Refactor test credentials: replace hardcoded AWS credentials with mock values from testutil for improved test reliability and maintainability - Refactor test functions: rename TestPathValidation to TestTransferPathValidation for clarity and consistency - Refactor AWS credential handling in tests: centralize mock credentials, improve test environment setup, and remove deprecated code @@ -145,10 +155,10 @@ - Refactor AWS SSO configuration and validation - chore: remove deprecated v2.6.1 section from CHANGELOG.md - ## [v2.6.1] - 2025-09-08 ### Added + - feat: update Go version to 1.25 and enhance golangci-lint configuration for improved code quality - feat: add golangci-lint configuration and update SHA1 usage comments for AWS CLI compatibility - feat: switch from SHA256 to SHA1 for cache filename generation to ensure AWS CLI compatibility @@ -193,10 +203,11 @@ - feat: Enhance SSM list command to display all EC2 instances with their SSM status - feat: Introduce version management module and update version references in authaws and ssm scripts - feat: Add flag-based parameter support to authaws -- Enhance branch protection: allow pull requests to main from 'release/*' branches -- Enhance CI/CD workflow: include 'release/*' branches in push and pull request triggers +- Enhance branch protection: allow pull requests to main from 'release/\*' branches +- Enhance CI/CD workflow: include 'release/\*' branches in push and pull request triggers ### Fixed + - fix: update golangci-lint and gosec installation scripts for accuracy and consistency - fix: update instance tag filtering to use awssdk.String for better clarity and consistency - fix: Enhance environment variable handling for cross-platform compatibility in tests @@ -233,6 +244,7 @@ - fix: Resolve shellcheck warnings and bash compatibility issues ### Changed + - chore: remove deprecated v2.6.0 section from CHANGELOG.md - chore: update version to 2.5.2 in changelog and source files - test: enhance validation in various test cases for improved coverage and reliability @@ -272,6 +284,7 @@ ## [v2.5.2] - 2025-09-07 ### Security + - **Critical Directory Traversal Fix**: Enhanced cross-platform path validation security - **Directory Traversal Prevention**: Fixed Windows-style directory traversal vulnerability (`..\\` patterns) - **Cross-Platform Security**: Added comprehensive Windows (`\`) and Unix (`/`) path separator validation @@ -280,6 +293,7 @@ - **Runtime Adaptation**: Tests automatically adapt to current OS for platform-specific attack vector validation ### Technical + - **Code Quality**: Improved boolean condition readability in security validation logic - **Cross-Platform Compatibility**: Replaced string concatenation with `filepath.Join()` for proper path handling - **Test Coverage**: Enhanced security package with Windows and Unix specific validation scenarios @@ -287,12 +301,14 @@ ## [v2.5.0] - 2025-09-07 ### Added + - **Enhanced User Interface**: Complete ASCII-only splash screen redesign - Beautiful ASCII art banner for universal terminal compatibility - Updated feature showcase highlighting latest capabilities - Clean, professional appearance without Unicode dependencies ### Security + - **Comprehensive Security Hardening**: Multiple vulnerability fixes and enhancements - **G204 (Command Injection)**: Added input validation for all `exec.CommandContext()` calls with regex-based parameter validation - **G304 (Directory Traversal)**: Implemented path validation to prevent file inclusion attacks across all file operations @@ -300,7 +316,7 @@ - **G104 (Error Handling)**: Added proper error handling for cleanup operations - **G401 (Weak Cryptography)**: Migrated from SHA-1 to SHA-256 for all hash operations - **G306 (File Permissions)**: Secured configuration files with 0600 permissions -- **Input Validation Framework**: +- **Input Validation Framework**: - Instance ID validation (`i-[0-9a-f]{8,17}`) - AWS region format validation (`us-east-1`, `eu-west-2`, etc.) - Port number range validation (1-65535) @@ -308,11 +324,13 @@ - **Test Coverage**: Added 45+ security-focused test cases covering all validation scenarios ### Enhanced + - **Code Quality Improvements**: String concatenation optimization and code cleanup - **Error Handling**: Comprehensive error handling improvements across the codebase - **Documentation**: Updated feature descriptions to highlight security and performance improvements ### Technical + - **Platform Compatibility**: Enhanced cross-platform support with ASCII-only interface elements - **Performance**: Optimized string operations and reduced complexity in hot paths - **Maintainability**: Improved code structure with centralized validation functions @@ -320,9 +338,10 @@ ## [v2.4.0] - 2025-09-07 ### Added + - **EC2 Power Management Commands**: Complete suite of instance power control operations - `ztictl ssm start [instance-id]` - Start stopped EC2 instances - - `ztictl ssm stop [instance-id]` - Stop running EC2 instances + - `ztictl ssm stop [instance-id]` - Stop running EC2 instances - `ztictl ssm reboot [instance-id]` - Reboot running EC2 instances - `ztictl ssm start-tagged --tags ` - Start multiple instances by tag - `ztictl ssm stop-tagged --tags ` - Stop multiple instances by tag @@ -334,11 +353,13 @@ - **Extensive Test Coverage**: 35+ test scenarios covering all power management functionality ### Enhanced + - **README Documentation**: Updated with power management examples and feature descriptions - **Help System**: All new commands integrated into ztictl help system - **Error Handling**: Clear, user-friendly error messages for all validation scenarios ### Examples + ```bash # Start/stop single instances ztictl ssm start i-1234567890abcdef0 --region cac1 @@ -355,6 +376,7 @@ ztictl ssm reboot --instances i-123,i-456,i-789 --parallel 3 --region cac1 ## [v2.3.0] - 2025-09-06 ### Added + - **Parallel execution for exec-tagged command** - All commands now run in parallel by default for massive performance improvements at scale - **Instance ID filtering** - New `--instances` flag to explicitly target specific instance IDs (comma-separated) - **Configurable parallelism** - New `--parallel ` flag to control maximum concurrent executions (default: CPU cores) @@ -362,22 +384,25 @@ ztictl ssm reboot --instances i-123,i-456,i-789 --parallel 3 --region cac1 - **Mutual exclusion validation** - Prevent conflicting usage of `--tags` and `--instances` flags ### Changed + - **Breaking: Sequential execution removed** - All exec-tagged operations now run in parallel for better performance - **Improved scalability** - Worker pool pattern handles large instance sets efficiently with configurable limits - **Enhanced error handling** - Better validation and user-friendly error messages for invalid parallel values ### Performance + - **Dramatic speed improvements** - Commands on 20+ instances complete in seconds instead of minutes - **Resource control** - Configurable parallelism prevents system overload while maximizing throughput - **Real-time feedback** - Individual execution timing and progress visibility ### Examples + ```bash # Parallel execution with tags (default CPU cores) ztictl ssm exec-tagged cac1 --tags Environment=production "uptime" # Custom parallelism for large environments -ztictl ssm exec-tagged use1 --tags Owner=Ditah --parallel 15 "df -h" +ztictl ssm exec-tagged use1 --tags Owner=Ditah --parallel 15 "df -h" # Direct instance targeting ztictl ssm exec-tagged cac1 --instances i-123,i-456,i-789 --parallel 5 "systemctl status nginx" @@ -386,26 +411,30 @@ ztictl ssm exec-tagged cac1 --instances i-123,i-456,i-789 --parallel 5 "systemct ## [v2.2.0] - 2025-09-06 ### Added + - **Multi-tag filtering for ztictl exec-tagged** - Enhanced `--tags` flag supporting multiple tag filters with AND logic - **Comprehensive test coverage** - Added unit tests for tag parsing and integration tests for multi-tag functionality - **Backward compatibility** - Maintained support for legacy single-tag filtering alongside new multi-tag syntax ### Changed + - **Breaking: exec-tagged command syntax** - Changed from positional ` ` to `--tags key=value,key2=value2` flag format - **Enhanced documentation** - Updated README files with multi-tag examples and usage patterns - **Improved error handling** - Better validation and error messages for malformed tag filters ### Examples + ```bash # Single tag filtering ztictl ssm exec-tagged cac1 --tags Environment=production "df -h" -# Multiple tag filtering (AND logic) +# Multiple tag filtering (AND logic) ztictl ssm exec-tagged use1 --tags Environment=dev,Component=fts,Team=backend "systemctl status nginx" ``` ## [v1.6.0] - 2025-08-19 ### Added + - **Flag-based parameter support for SSM tool** - New enterprise-friendly syntax with `--region`, `--instance`, `--command` flags - **Mixed syntax support** - Combination of positional and flag-based parameters (e.g., `ssm cac1 --instance i-1234`) - **Enhanced user experience** - Self-documenting commands with clear parameter names @@ -415,12 +444,14 @@ ztictl ssm exec-tagged use1 --tags Environment=dev,Component=fts,Team=backend "s - **Enhanced error messages** - Actionable guidance for missing modules and setup issues ### Changed + - **Backward compatibility maintained** - All existing positional syntax continues to work unchanged - **Dynamic command naming** - Both `ssm` and `authaws` now use `$(basename "$0")` for consistent help text - **Improved documentation** - Updated README.md with clear user vs developer installation paths - **Enhanced testing** - New `tests/QA_SSM_TESTS.md` with comprehensive test scenarios ### Fixed + - **Critical port forwarding bug** - Fixed instance name resolution for port forwarding operations - **Region validation consistency** - Resolved inconsistent region validation across different SSM commands - **Duplicate PATH prevention** - Fixed Makefile to prevent duplicate PATH entries in development setup @@ -429,6 +460,7 @@ ztictl ssm exec-tagged use1 --tags Environment=dev,Component=fts,Team=backend "s ## [v2.1.0] - 2025-08-04 ### Added + - feat(ci): add automated Google Chat notifications for PRs and releases with shell scripts - feat(scripts): add professional notification scripts with embedded Google Chat App Cards - feat(notifications): implement rich visual formatting following zsoftly-services pattern @@ -443,15 +475,17 @@ ztictl ssm exec-tagged use1 --tags Environment=dev,Component=fts,Team=backend "s - feat: Enhance SSM list command to display all EC2 instances with their SSM status - feat: Introduce version management module and update version references in authaws and ssm scripts - feat: Add flag-based parameter support to authaws -- Enhance branch protection: allow pull requests to main from 'release/*' branches -- Enhance CI/CD workflow: include 'release/*' branches in push and pull request triggers +- Enhance branch protection: allow pull requests to main from 'release/\*' branches +- Enhance CI/CD workflow: include 'release/\*' branches in push and pull request triggers ### Fixed + - fix: Update help message and improve default behavior for no arguments in authaws - fix: Address PR review feedback and improve validation - fix: Resolve shellcheck warnings and bash compatibility issues ### Changed + - refactor: Improve ANSI code handling and enhance logging error messages - refactor: Implement dynamic table formatting for EC2 instance display - refactor: Enhance logging messages for clarity and consistency across IAM and S3 lifecycle management @@ -465,7 +499,6 @@ ztictl ssm exec-tagged use1 --tags Environment=dev,Component=fts,Team=backend "s - refactor: Migrate logging to centralized package and remove legacy logger - docs: Enhance authaws section with flag-based parameter support and usage examples - All notable changes to the ZTiAWS project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), @@ -474,6 +507,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [v2.0.0] - 2025-07-28 ### Added + - **Initial release of ztictl** - the Go version of ZTiAWS CLI - **Cross-platform support** - Native binaries for Linux, macOS, Windows (AMD64 and ARM64) - **Interactive UI enhancements** - Colorized output, progress bars, and animated splash screens @@ -486,6 +520,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Enhanced error handling** - Detailed error messages with troubleshooting hints ### Changed + - **Complete rewrite from shell scripts to Go** for better performance and maintainability - **Unified command interface** - Single binary replacing multiple shell scripts - **Improved user experience** - Interactive menus, real-time feedback, and better error messages @@ -493,9 +528,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Performance improvements** - Significantly faster execution compared to shell scripts ### Migrated Features + - AWS SSO authentication (enhanced with interactive selection) - SSM Session Manager connections -- SSM instance listing and management +- SSM instance listing and management - Remote command execution via SSM (`exec` and `exec-tagged` commands) - File transfer through SSM with S3 support for large files - Port forwarding through SSM tunnels @@ -504,6 +540,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [v1.4.2] - 2025-05-10 ### Added + - Remote command execution capabilities - New `exec` command to run commands on individual EC2 instances - New `exec-tagged` command to run commands across multiple instances with the same tag @@ -512,6 +549,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Enhanced error handling for missing instances and command failures ### Fixed + - Improved AWS SSO token management with better cache file detection - Fixed "base64: invalid input" errors when using exec-tagged command - Resolved ShellCheck warnings for improved CI pipeline reliability @@ -519,20 +557,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [v1.4.1] - 2025-05-10 ### Added + - Enhanced error handling for access token retrieval in authaws script ### Fixed + - Improved error messages for SSO configuration issues ## [v1.4.0] - 2025-03-30 ### Added + - Renamed auth script from auth_aws to authaws for better usability - Ensured PATH updates with proper commenting and new line handling ## [v1.3.1] - 2025-03-31 ### Changed + - Rebranded repository to ZTiAWS from quickssm - Updated documentation, README, and issue templates to reflect new branding - Improved installation and troubleshooting guides @@ -540,16 +582,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [v1.3.0] - 2025-05-10 ### Added + - Support for running commands on EC2 instances (initially named run_command) - Improved tests for the SSM script ### Fixed + - Syntax error in detect_os function - ShellCheck directive placement in cleanup function ## [v1.1.2] - 2025-03-28 ### Added + - New auth_aws script for simplified AWS SSO login - Improved logging functionality - Better error handling @@ -557,6 +602,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [v1.1.0] - 2025-02-06 ### Added + - Support for Singapore region (ap-southeast-1) - Formatted EC2 instance output with clear columns - PowerShell profile for Windows users @@ -564,6 +610,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [v1.0.0] - 2025-01-20 (Initial Release) ### Added + - Core SSM Session Manager functionality - Support for multiple regions - Auto-install prompt for AWS SSM plugin diff --git a/CLAUDE.md b/CLAUDE.md index f0d02dd..b4e5a91 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,9 +1,11 @@ # ZTiAWS - Claude Memory File ## Repository Overview + **ZTiAWS** (ZSoftly Tools for AWS) - A collection of AWS management CLI tools by ZSoftly that simplify AWS SSO authentication and EC2 Systems Manager operations. ## Architecture + - **Legacy Production**: Bash scripts (`authaws`, `ssm`) - currently in production - **Next-Gen Go Tool**: `ztictl` - modern replacement with enhanced features - **Migration Strategy**: Both tools coexist during transition period @@ -11,12 +13,14 @@ ## Key Directories ### Root Level -- `src/` - Bash script modules (00_*.sh, 01_*.sh, etc.) + +- `src/` - Bash script modules (00*\*.sh, 01*\*.sh, etc.) - `ztictl/` - Go implementation (primary focus) - `docs/` - Documentation (IAM_PERMISSIONS.md, TROUBLESHOOTING.md, etc.) - `tools/` - Utility scripts for testing and releases ### Go Implementation (`ztictl/`) + ``` ztictl/ ├── cmd/ztictl/ # CLI commands @@ -31,7 +35,7 @@ ztictl/ │ ├── config/config.go # Configuration management │ ├── splash/splash.go # Welcome screen │ └── ssm/ # SSM managers (IAM, S3 lifecycle) -├── pkg/ # Public packages +├── pkg/ # Public packages │ ├── aws/ # AWS clients & utilities │ ├── colors/ # Terminal colors │ ├── errors/ # Error handling @@ -42,6 +46,7 @@ ztictl/ ## Key Features ### ztictl (Go Version) + - **Cross-platform**: Linux, macOS, Windows (AMD64/ARM64) - **Smart file transfers**: <1MB direct SSM, ≥1MB via S3 with lifecycle management - **Advanced IAM**: Temporary policies with automatic cleanup and filesystem locking @@ -49,21 +54,24 @@ ztictl/ - **Modern CLI**: Cobra/Viper with comprehensive help and validation ### Legacy Bash Tools + - **authaws**: AWS SSO authentication with interactive account/role selection - **ssm**: EC2 instance management via SSM (connect, exec, file transfer, port forwarding) ## Common Commands ### Development + ```bash # Build Go version cd ztictl && make build-local -# Run tests +# Run tests make test ``` ### **Standard Development Quality Checklist** + **IMPORTANT**: Run these commands after ANY development work in the `ztictl/` directory: ```bash @@ -88,17 +96,21 @@ golangci-lint run ./... ``` **Installation of golangci-lint** (one-time setup): + ```bash curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin latest ``` **These quality checks are MANDATORY before:** + - Creating commits -- Opening pull requests +- Opening pull requests - Considering development work "complete" ### Post-Development Cleanup + After any development session, run: + ```bash # Clean test artifacts go clean -testcache @@ -110,6 +122,7 @@ find . -name "test_*.sh" -delete ``` ### Usage Examples + ```bash # ztictl (recommended) ztictl config check @@ -124,6 +137,7 @@ ssm i-1234abcd # Connect to instance ``` ## Configuration + - **ztictl**: `~/.ztictl.yaml` (YAML format) - Default region: `ca-central-1` (both for SSO and operations) - SSO setup: Only asks for domain ID (e.g., `d-1234567890` or `zsoftly`) @@ -131,18 +145,21 @@ ssm i-1234abcd # Connect to instance - **Legacy tools**: `.env` file with SSO_START_URL, SSO_REGION, DEFAULT_PROFILE ## Build & Release + - **Build system**: Makefiles for both root and ztictl - **CI/CD**: GitHub Actions for cross-platform builds - **Release process**: Git tags trigger automated builds and releases ## Important Files + - `README.md` - Main documentation -- `ztictl/README.md` - Go tool documentation +- `ztictl/README.md` - Go tool documentation - `INSTALLATION.md` - Platform-specific setup instructions - `CONTRIBUTING.md` - Development guidelines - `docs/IAM_PERMISSIONS.md` - Required AWS permissions ## Development Notes + - Repository uses MIT License - Active development focused on ztictl Go implementation - Bash tools maintained for backward compatibility @@ -150,6 +167,7 @@ ssm i-1234abcd # Connect to instance - AWS CLI and Session Manager plugin required ## Testing Commands + ```bash # Check system requirements ztictl config check # or authaws --check @@ -161,7 +179,9 @@ ssm --help ``` ## Test Infrastructure + **AWS Credential Handling in Tests**: + - Centralized test utilities in `internal/testutil/aws.go` define mock AWS credentials - Each test package has an `init_test.go` file that calls `testutil.SetupAWSTestEnvironment()` - Mock credentials (AWS documentation examples): @@ -179,7 +199,8 @@ ssm --help **Cross-Platform Test Writing Guidelines**: **CRITICAL**: All tests MUST be platform-agnostic and work on Unix (Linux/macOS) AND Windows: -- **File Paths**: + +- **File Paths**: - ALWAYS use `filepath.Join()` instead of hardcoding paths with `/` or `\` - NEVER use absolute Unix paths like `/tmp` or `/var/log` - use `t.TempDir()` or `os.TempDir()` - NEVER use Windows-specific paths like `C:\` or `D:\` @@ -188,7 +209,7 @@ ssm --help - Use `filepath.ToSlash()` to convert paths to forward slashes for URLs/YAML - Use `filepath.FromSlash()` to convert from forward slashes to OS format - **Invalid Path Testing**: - - Don't rely on Unix-specific invalid paths like `/nonexistent` + - Don't rely on Unix-specific invalid paths like `/nonexistent` - Create reliably invalid paths by placing files where directories are expected - Use permission-based failures cautiously as they behave differently across OS - **Home Directory**: @@ -198,24 +219,27 @@ ssm --help - Be aware that Windows doesn't support Unix permission bits the same way - Read-only directories behave differently on Windows - **Test Examples**: + ```go // BAD - Unix-specific path invalidPath := "/nonexistent/directory/config.yaml" - + // GOOD - Platform-agnostic approach invalidPath := filepath.Join(t.TempDir(), "subdir", "config.yaml") // Then create a file at "subdir" to make the path invalid ``` ## Test File Management Guidelines + **IMPORTANT**: When creating test files or scripts during development: ### Temporary Files to Clean Up + 1. **One-time test files**: Delete after use - `*_repair_test.go`, `*_init_test.go` (unless part of permanent suite) - `test_coverage.sh`, `run_tests.sh` (temporary scripts) - 2. **Coverage artifacts**: Remove after review + ```bash rm -rf coverage/ *.out *.html ``` @@ -227,18 +251,22 @@ ssm --help ``` ### Permanent Test Files (Keep These) + - `*_test.go` files that test actual functionality - Test fixtures in `testdata/` directories - Benchmark tests for performance validation ### Best Practices + - **Before committing**: Clean up all temporary test artifacts - **After debugging**: Remove one-off test files - **Duplicate tests**: Update existing tests rather than creating new files -- **Use .gitignore**: Ensure coverage/, *.out, *.test are ignored +- **Use .gitignore**: Ensure coverage/, _.out, _.test are ignored ## Code Maintenance Guidelines + **CRITICAL**: When removing deprecated, redundant, or obsolete code: + - **NO COMMENTS**: Never leave comments explaining what was removed or why - **CLEAN REMOVAL**: Completely remove all traces of deprecated functionality - **NO DEAD CODE**: Remove entire functions, variables, and imports that are no longer needed @@ -251,18 +279,22 @@ ssm --help - Documentation references - Configuration options - **Examples of what NOT to do**: + ```go // BAD - Don't do this: // Removed deprecated auth method // func oldAuthMethod() { } // Deprecated - + // GOOD - Just remove it completely with no trace ``` + - **Principal**: When deprecating/removing code, act as a principal engineer - leave the codebase cleaner with no remnants of removed functionality ## Security Validation Guidelines + **IMPORTANT**: Security validations must be comprehensive: -- **Path Traversal Protection**: + +- **Path Traversal Protection**: - **Use `ztictl/pkg/security` package**: The project has a comprehensive security package - Call `security.ContainsUnsafePath()` for path validation - Call `security.ValidateFilePath()` for directory-scoped validation @@ -277,8 +309,10 @@ ssm --help - Validate base64 data before embedding in commands ## Logging Best Practices + **CRITICAL**: Proper logging configuration: -- **Logger Instances**: + +- **Logger Instances**: - Use dependency injection for loggers (pass them as parameters) - Never hardcode debug levels in production code - Provide factory methods like `NewDetectorWithLogger()` for custom loggers @@ -288,7 +322,9 @@ ssm --help - Log command length or hash at Info level instead ## Resource Management + **IMPORTANT**: Prevent resource leaks: + - **AWS Client Pooling**: - Reuse AWS clients across operations - Implement client pools with proper synchronization @@ -296,4 +332,4 @@ ssm --help - **Thread Safety**: - Always use mutexes for shared map access - Prefer RWMutex for read-heavy operations - - Lock before checking and modifying shared state \ No newline at end of file + - Lock before checking and modifying shared state diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cb7dd3e..89d6f80 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,16 +7,20 @@ Thank you for your interest in contributing to ZTiAWS! We especially welcome con 1. Fork the repository 2. Add your region to `src/regions.sh` 3. Follow this format: + ```bash case "$1" in "SHORTCODE") echo "aws-region-name" ;; # Location/City ``` + Example: + ```bash "euw2") echo "eu-west-2" ;; # London ``` ### Region Code Guidelines + - Use 4 characters: area (2) + region number (2) - Examples: - `use1` - US East 1 @@ -24,7 +28,9 @@ Example: - `aps1` - Asia Pacific Singapore ### Required Information + When submitting a new region, include: + 1. AWS Region name (e.g., `eu-west-2`) 2. Location/City (e.g., "London") 3. AWS documentation reference @@ -34,11 +40,14 @@ When submitting a new region, include: 1. Fork the repository 2. Create a feature branch + ```bash git checkout -b feature/add-region-euw2 ``` + 3. Make your changes 4. Run tests locally + ```bash # For Go code (ztictl) cd ztictl && make test @@ -46,6 +55,7 @@ cd ztictl && make test # For shell scripts ./tests/test_ssm.sh ``` + 5. Submit a Pull Request > **📚 CI/CD Information:** See [docs/CI_CD_PIPELINE.md](docs/CI_CD_PIPELINE.md) for details on our automated testing and build process. @@ -59,8 +69,8 @@ cd ztictl && make test - **Security scans** run on PRs to main branch - **Builds** are triggered only for releases 4. Update documentation if needed -3. Ensure all tests pass -4. Update documentation if needed +5. Ensure all tests pass +6. Update documentation if needed ## Code Style @@ -72,6 +82,7 @@ cd ztictl && make test ## Testing Test your changes: + ```bash ./tests/test_ssm.sh ``` @@ -79,6 +90,7 @@ Test your changes: ## Commit Messages Format: + ``` type(scope): description @@ -87,6 +99,7 @@ type(scope): description ``` Types: + - feat: New feature - fix: Bug fix - docs: Documentation @@ -94,6 +107,7 @@ Types: - chore: Maintenance Example: + ``` feat(regions): add EU West 2 London region @@ -118,6 +132,7 @@ ZTiAWS uses an automated CI/CD pipeline for releases. See [docs/CI_CD_PIPELINE.m ### Quick Release Process: 1. **Prepare release** on main branch: + ```bash git checkout main && git pull origin main ``` @@ -128,6 +143,7 @@ ZTiAWS uses an automated CI/CD pipeline for releases. See [docs/CI_CD_PIPELINE.m - `RELEASE_NOTES.txt` (release description) 3. **Create and push tag**: + ```bash git add . && git commit -m "Bump version to vX.Y.Z" git tag -a vX.Y.Z -m "Version X.Y.Z: Brief description" @@ -140,4 +156,4 @@ ZTiAWS uses an automated CI/CD pipeline for releases. See [docs/CI_CD_PIPELINE.m - Binary distribution - Release notes from RELEASE_NOTES.txt -> **💡 Pro tip:** Use semantic versioning (major.minor.patch) and watch the GitHub Actions pipeline for build status. \ No newline at end of file +> **💡 Pro tip:** Use semantic versioning (major.minor.patch) and watch the GitHub Actions pipeline for build status. diff --git a/INSTALLATION.md b/INSTALLATION.md index b24fe60..f31b5f9 100644 --- a/INSTALLATION.md +++ b/INSTALLATION.md @@ -5,17 +5,20 @@ > **📦 Primary Tool:** We recommend using the **Go binary (ztictl)** for new installations. The bash tools are maintained for legacy users but are being phased out. ## Prerequisites + - AWS CLI configured with appropriate credentials - EC2 instances with SSM agent installed and proper IAM roles ## Quick Install (Recommended) **Linux/macOS - One-liner with automatic platform detection:** + ```bash curl -L -o /tmp/ztictl "https://github.com/zsoftly/ztiaws/releases/latest/download/ztictl-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')" && chmod +x /tmp/ztictl && sudo mv /tmp/ztictl /usr/local/bin/ztictl && ztictl --version ``` **Windows PowerShell - Full setup:** + ```powershell # Download and setup ztictl Invoke-WebRequest -Uri "https://github.com/zsoftly/ztiaws/releases/latest/download/ztictl-windows-amd64.exe" -OutFile "ztictl.exe" @@ -40,12 +43,14 @@ ztictl --version ### Linux **AMD64 (Intel/AMD):** + ```bash curl -L -o /tmp/ztictl https://github.com/zsoftly/ztiaws/releases/latest/download/ztictl-linux-amd64 chmod +x /tmp/ztictl && sudo mv /tmp/ztictl /usr/local/bin/ztictl ``` **ARM64 (ARM processors):** + ```bash curl -L -o /tmp/ztictl https://github.com/zsoftly/ztiaws/releases/latest/download/ztictl-linux-arm64 chmod +x /tmp/ztictl && sudo mv /tmp/ztictl /usr/local/bin/ztictl @@ -54,12 +59,14 @@ chmod +x /tmp/ztictl && sudo mv /tmp/ztictl /usr/local/bin/ztictl ### macOS **Intel Macs:** + ```bash curl -L -o /tmp/ztictl https://github.com/zsoftly/ztiaws/releases/latest/download/ztictl-darwin-amd64 chmod +x /tmp/ztictl && sudo mv /tmp/ztictl /usr/local/bin/ztictl ``` **Apple Silicon (M1/M2/M3):** + ```bash curl -L -o /tmp/ztictl https://github.com/zsoftly/ztiaws/releases/latest/download/ztictl-darwin-arm64 chmod +x /tmp/ztictl && sudo mv /tmp/ztictl /usr/local/bin/ztictl @@ -68,11 +75,13 @@ chmod +x /tmp/ztictl && sudo mv /tmp/ztictl /usr/local/bin/ztictl ### Windows **Option 1: Manual Download** + 1. Download: https://github.com/zsoftly/ztiaws/releases/latest/download/ztictl-windows-amd64.exe 2. Rename to `ztictl.exe` 3. Follow the PATH setup instructions below **Option 2: PowerShell (Recommended)** + ```powershell # Download Invoke-WebRequest -Uri "https://github.com/zsoftly/ztiaws/releases/latest/download/ztictl-windows-amd64.exe" -OutFile "ztictl.exe" @@ -88,6 +97,7 @@ Move-Item "ztictl.exe" "$toolsDir\ztictl.exe" ## Windows PATH Setup **Method 1: PowerShell (Recommended)** + ```powershell $toolsDir = "$env:USERPROFILE\Tools" $currentPath = [Environment]::GetEnvironmentVariable("PATH", "User") @@ -99,6 +109,7 @@ if ($currentPath -notlike "*$toolsDir*") { ``` **Method 2: GUI** + 1. Press `Win + R`, type `sysdm.cpl`, press Enter 2. Click "Environment Variables" 3. Under "User variables", select "Path" and click "Edit" @@ -109,6 +120,7 @@ if ($currentPath -notlike "*$toolsDir*") { ## Usage ### Quick Start + ```bash # Check system requirements ztictl config check @@ -130,6 +142,7 @@ ztictl ssm transfer upload i-1234567890abcdef0 large-file.zip /opt/data.zip --re ``` ### Configuration Management + ```bash # Show current configuration ztictl config show @@ -147,12 +160,14 @@ ztictl ssm --help ### Updating ztictl **Simple Update (Recommended):** + ```bash # Download to a temporary location to avoid conflicts curl -L -o /tmp/ztictl "https://github.com/zsoftly/ztiaws/releases/latest/download/ztictl-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')" && chmod +x /tmp/ztictl && sudo mv /tmp/ztictl /usr/local/bin/ztictl && ztictl --version ``` **Step-by-step Update:** + ```bash # 1. Download latest version to temporary location curl -L -o /tmp/ztictl "https://github.com/zsoftly/ztiaws/releases/latest/download/ztictl-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')" @@ -168,6 +183,7 @@ ztictl --version ``` **Windows Update:** + ```powershell # Download to temporary location $tempFile = "$env:TEMP\ztictl.exe" @@ -183,35 +199,41 @@ ztictl --version **Alternative: Platform-specific updates** -*Linux AMD64:* +_Linux AMD64:_ + ```bash curl -L -o /tmp/ztictl https://github.com/zsoftly/ztiaws/releases/latest/download/ztictl-linux-amd64 && chmod +x /tmp/ztictl && sudo mv /tmp/ztictl /usr/local/bin/ztictl ``` -*Linux ARM64:* +_Linux ARM64:_ + ```bash curl -L -o /tmp/ztictl https://github.com/zsoftly/ztiaws/releases/latest/download/ztictl-linux-arm64 && chmod +x /tmp/ztictl && sudo mv /tmp/ztictl /usr/local/bin/ztictl ``` -*macOS Intel:* +_macOS Intel:_ + ```bash curl -L -o /tmp/ztictl https://github.com/zsoftly/ztiaws/releases/latest/download/ztictl-darwin-amd64 && chmod +x /tmp/ztictl && sudo mv /tmp/ztictl /usr/local/bin/ztictl ``` -*macOS Apple Silicon:* +_macOS Apple Silicon:_ + ```bash curl -L -o /tmp/ztictl https://github.com/zsoftly/ztiaws/releases/latest/download/ztictl-darwin-arm64 && chmod +x /tmp/ztictl && sudo mv /tmp/ztictl /usr/local/bin/ztictl ``` **Troubleshooting Updates:** -*If you get "Is a directory" error:* +_If you get "Is a directory" error:_ + ```bash # This happens when there's a directory named 'ztictl' in current folder # Solution: Always download to /tmp/ as shown above ``` -*If you get "cannot overwrite non-directory" error:* +_If you get "cannot overwrite non-directory" error:_ + ```bash # Check what ztictl currently is file /usr/local/bin/ztictl @@ -222,18 +244,21 @@ sudo rm -rf /usr/local/bin/ztictl # Then retry the installation ``` -*To check current version before updating:* +_To check current version before updating:_ + ```bash ztictl --version ``` -*To install a specific version (if needed):* +_To install a specific version (if needed):_ + ```bash # Replace v2.1.0 with desired version curl -L -o /tmp/ztictl "https://github.com/zsoftly/ztiaws/releases/download/v2.1.0/ztictl-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')" && chmod +x /tmp/ztictl && sudo mv /tmp/ztictl /usr/local/bin/ztictl ``` ### Updating Legacy Bash Tools + Navigate to your cloned repository directory and pull the latest changes: ```bash @@ -251,10 +276,12 @@ If updating from pre-March 2025 (when repository was named "quickssm"), see [doc If you prefer to build ztictl from source instead of using pre-built binaries: **Prerequisites:** + - Go 1.24 or later - Git **Build Steps:** + ```bash # Clone the repository git clone https://github.com/zsoftly/ztiaws.git @@ -276,6 +303,7 @@ ztictl --version ``` **Cross-platform builds:** + ```bash # Build for all platforms make build @@ -308,6 +336,7 @@ ssm --help ``` The installation script automatically: + - ✅ Installs `authaws` and `ssm` commands globally - ✅ Copies all required modules to `/usr/local/bin/src/` - ✅ Sets up proper permissions @@ -353,11 +382,12 @@ authaws check ``` **Basic Usage:** + ```bash # List instances ssm cac1 # Canada Central region -# Connect to instance +# Connect to instance ssm i-1234567890abcdef0 # Authenticate with AWS SSO @@ -367,6 +397,7 @@ authaws ## Troubleshooting ### Command Not Found + ```bash # Check installation which ztictl @@ -380,6 +411,7 @@ echo $env:PATH.Split(';') | Select-String "Tools" ``` ### Permission Issues + ```bash # Linux/macOS: Ensure executable sudo chmod +x /usr/local/bin/ztictl @@ -389,6 +421,7 @@ Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser ``` ### Architecture Issues + ```bash # Check your system architecture uname -m # Linux/macOS @@ -396,10 +429,11 @@ $env:PROCESSOR_ARCHITECTURE # Windows # Common mappings: # x86_64 / AMD64 → use amd64 binary -# aarch64 / arm64 → use arm64 binary +# aarch64 / arm64 → use arm64 binary ``` ### AWS Configuration + ```bash # Verify AWS CLI setup aws --version @@ -419,4 +453,7 @@ ztictl config check - **📊 Better logging**: Thread-safe, timestamped logs with debug capabilities **Migrate from bash tools** by simply installing ztictl and using similar commands with modern flag syntax! + +``` + ``` diff --git a/Makefile b/Makefile index dbde350..ef78c78 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,10 @@ # ZTiAWS Makefile # Simple installation and development management -.PHONY: install uninstall dev clean test help +# Auto-detect version from git tags (e.g., "2.11.0" or "2.11.0-3-g1a2b3c4" for dev builds) +VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev") + +.PHONY: install uninstall dev clean test fmt help # Default target help: @@ -12,6 +15,7 @@ help: @echo " uninstall - Remove installed tools from /usr/local/bin" @echo " dev - Set up development environment (adds local PATH)" @echo " test - Run tests and validation" + @echo " fmt - Format all code (Go + Markdown/JSON/YAML)" @echo " clean - Clean up temporary files" @echo " help - Show this help message" @echo "" @@ -62,6 +66,17 @@ test: ./ssm --help > /dev/null @echo "✅ Tests passed!" +# Format all code (Go + Markdown/JSON/YAML) +fmt: + @echo "ztiaws $(VERSION)" + @echo "" + @echo "Formatting Go code..." + @cd ztictl && go fmt ./... + @echo "Formatting Markdown/JSON/YAML files..." + @npx prettier --write "**/*.md" "**/*.json" "**/*.yaml" "**/*.yml" 2>/dev/null || echo "Note: Install Node.js for Prettier support" + @echo "" + @echo "✅ Formatting complete!" + # Clean up clean: @echo "Cleaning up temporary files..." diff --git a/README.md b/README.md index 9c71eb4..a33b2a9 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ ## 🚀 Key Features **ztictl (Primary Tool):** + - **🌍 Cross-platform**: Native binaries for Linux, macOS, and Windows - **🔍 Interactive fuzzy finder**: Real-time instance search and selection with keyboard navigation - **🖥️ Multi-OS support**: Full Linux (bash) and Windows Server (PowerShell) command execution @@ -31,6 +32,7 @@ - **🎨 Clean UI**: Intuitive fuzzy finder interface for both SSM operations and AWS SSO account/role selection **Legacy bash tools (deprecated):** + - **ssm**: Connect to EC2 instances, execute commands, power management, tag-based operations - **authaws**: AWS SSO authentication with interactive account/role selection - Color-coded output and region shortcodes for faster workflows @@ -47,11 +49,13 @@ ### Quick Install - ztictl (Recommended) **Linux/macOS:** + ```bash curl -L -o /tmp/ztictl "https://github.com/zsoftly/ztiaws/releases/latest/download/ztictl-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')" && chmod +x /tmp/ztictl && sudo mv /tmp/ztictl /usr/local/bin/ztictl && ztictl --version ``` **Windows PowerShell:** + ```powershell Invoke-WebRequest -Uri "https://github.com/zsoftly/ztiaws/releases/latest/download/ztictl-windows-amd64.exe" -OutFile "$env:TEMP\ztictl.exe"; New-Item -ItemType Directory -Force "$env:USERPROFILE\Tools" | Out-Null; Move-Item "$env:TEMP\ztictl.exe" "$env:USERPROFILE\Tools\ztictl.exe"; [Environment]::SetEnvironmentVariable("PATH", "$env:PATH;$env:USERPROFILE\Tools", "User"); $env:PATH += ";$env:USERPROFILE\Tools"; ztictl --version ``` @@ -59,6 +63,7 @@ Invoke-WebRequest -Uri "https://github.com/zsoftly/ztiaws/releases/latest/downlo ### Other Installation Options See [INSTALLATION.md](INSTALLATION.md) for: + - **Platform-specific binaries** (Linux, macOS, Windows - AMD64/ARM64) - **Building from source** (requires Go 1.24+) - **Legacy bash tools** (for existing users) @@ -71,6 +76,7 @@ See [INSTALLATION.md](INSTALLATION.md) for: To update to the latest version, see the update instructions in [INSTALLATION.md](INSTALLATION.md). **Quick update:** + - **ztictl**: Re-run the installation command from INSTALLATION.md - **Bash tools**: `git pull origin main` in your cloned directory @@ -79,11 +85,13 @@ To update to the latest version, see the update instructions in [INSTALLATION.md ### ztictl (Recommended) > **📚 Complete Documentation:** +> > - [Command Reference](docs/COMMANDS.md) - All commands with examples > - [Configuration Guide](docs/CONFIGURATION.md) - Setup and configuration > - [Multi-Region Operations](docs/MULTI_REGION.md) - Cross-region execution #### Quick Start + ```bash # Initialize configuration interactively (simplified setup) ztictl config init --interactive @@ -110,6 +118,7 @@ ztictl ssm exec --tags "Environment=prod" "uptime" --region euw1 #### New Features (v2.4+) **🔋 Power Management:** + ```bash # Start/stop instances ztictl ssm start i-1234567890abcdef0 --region cac1 @@ -121,6 +130,7 @@ ztictl ssm stop-tagged --tags "Environment=dev" --force --region cac1 ``` **🌍 Multi-Region Operations (v2.6+):** + ```bash # Execute across multiple regions ztictl ssm exec-multi cac1,use1,euw1 --tags "App=web" "health-check" @@ -135,6 +145,7 @@ ztictl ssm exec-multi --region-group production --tags "Critical=true" "backup.s See [docs/COMMANDS.md](docs/COMMANDS.md) for complete command reference. #### Configuration Management + ```bash # Interactive setup (recommended for first-time users) ztictl config init --interactive @@ -160,6 +171,7 @@ See [docs/CONFIGURATION.md](docs/CONFIGURATION.md) for detailed configuration op > **Important:** AWS SSO (`ztictl auth login`) requires browser interaction and **cannot be used in CI/CD**. Use IAM-based authentication instead (OIDC, EC2 instance profiles, or IAM access keys). **Quick Start for CI/CD:** + ```bash # ztictl auto-detects CI environments (GitHub Actions, GitLab CI, Jenkins, etc.) # and disables interactive prompts automatically @@ -181,18 +193,21 @@ ztictl ssm power start --tag Environment=test ``` **Supported CI/CD Platforms:** + - GitHub Actions (OIDC recommended) - GitLab CI (OIDC recommended) - Jenkins (EC2 instance profile or IAM keys) - CircleCI, AWS CodeBuild, and others **Complete Guide:** See [docs/CI_CD_AUTHENTICATION.md](docs/CI_CD_AUTHENTICATION.md) for: + - Authentication methods (OIDC, instance profiles, IAM keys) - Platform-specific examples (GitHub Actions, GitLab CI, Jenkins) - Environment variable reference - Troubleshooting CI/CD issues **Example GitHub Actions Workflow:** + ```yaml - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v2 @@ -217,6 +232,7 @@ See [docs/examples/](docs/examples/) for complete CI/CD workflow examples. **Available flags**: `--region`, `--instance`, `--command`, `--tag-key`, `--tag-value`, `--local-file`, `--remote-file`, `--local-path`, `--remote-path`, `--local-port`, `--remote-port`, `--exec`, `--exec-tagged`, `--upload`, `--download`, `--forward`, `--list`, `--connect`, `--check`, `--help`, `--version`, `--debug` ##### Check System Requirements + ```bash # Traditional syntax (backward compatible) ssm check @@ -226,6 +242,7 @@ ssm --check ``` ##### List Instances in a Region + ```bash # Traditional syntax (backward compatible) ssm cac1 # Lists instances in Canada Central @@ -236,6 +253,7 @@ ssm --region cac1 # Equivalent to --list ``` ##### Connect to an Instance + ```bash # Traditional syntax (backward compatible) ssm i-1234abcd # Connect to instance in default region (Canada Central) @@ -251,7 +269,9 @@ ssm --region use1 i-1234abcd ``` ##### Execute Commands Remotely + Execute commands on a single instance: + ```bash # Traditional syntax (backward compatible) ssm exec cac1 i-1234 "systemctl status nginx" @@ -264,6 +284,7 @@ ssm exec cac1 --instance i-1234 --command "systemctl status nginx" ``` Execute commands on instances matching specific tags: + ```bash # Traditional syntax (backward compatible) ssm exec-tagged use1 Role web "df -h" @@ -276,11 +297,12 @@ ssm exec-tagged use1 --tag-key Role --tag-value web --command "df -h" ``` **🆕 ztictl Multi-Tag Filtering (Enhanced)** + ```bash # Single tag filtering ztictl ssm exec-tagged use1 --tags Environment=production "df -h" # Multiple tag filtering (AND logic) -ztictl ssm exec-tagged use1 --tags Environment=prod,Role=web,Team=backend "df -h" +ztictl ssm exec-tagged use1 --tags Environment=prod,Role=web,Team=backend "df -h" # Short flag syntax ztictl ssm exec-tagged use1 -t "Owner=james,Environment=dev" "systemctl status nginx" ``` @@ -288,7 +310,9 @@ ztictl ssm exec-tagged use1 -t "Owner=james,Environment=dev" "systemctl status n This will run `df -h` on all instances that match **ALL** specified tags. The script provides clear feedback if no instances match the specified tags. ##### File Transfer Operations + Upload and download files with automatic size-based routing: + ```bash # Traditional syntax (backward compatible) ssm upload cac1 i-1234 ./config.txt /etc/app/config.txt @@ -303,6 +327,7 @@ ssm upload cac1 --instance i-1234 --local-file ./config.txt --remote-path /etc/a ``` ##### Show Help + ```bash # Traditional syntax (backward compatible) ssm help @@ -315,6 +340,7 @@ ssm -h #### AWS SSO Authentication Tool #### First-time Setup + ```bash authaws check # Check dependencies authaws help # Show help information @@ -323,6 +349,7 @@ authaws help # Show help information **Available flags**: `--profile`, `--region`, `--sso-url`, `--export`, `--list-profiles`, `--debug`, `--help`, `--version`, `--check`, `--creds` Before using `authaws`, set up a `.env` file in the same directory with the following content: + ``` SSO_START_URL="https://your-sso-url.awsapps.com/start" SSO_REGION="your-region" @@ -332,6 +359,7 @@ DEFAULT_PROFILE="your-default-profile" You can create a template file by running `authaws` without a valid .env file. #### Log in to AWS SSO + ```bash # Traditional syntax (backward compatible) authaws # Use default profile from .env @@ -344,6 +372,7 @@ authaws --profile dev --sso-url https://alt.awsapps.com/start # Override SSO UR ``` The tool will: + 1. Check for valid cached credentials 2. Initiate AWS SSO login if needed 3. Show an interactive list of accounts @@ -351,6 +380,7 @@ The tool will: 5. Configure your AWS profile with the selected account and role #### View AWS Credentials + ```bash # Traditional syntax authaws creds # Show credentials for current profile @@ -371,6 +401,7 @@ This will display your AWS access key, secret key, and session token for the spe > **✅ Production Ready:** ztictl is now the primary tool. The bash tools are maintained for legacy compatibility but new features are only added to ztictl. ### Why Choose ztictl: + - **🌍 Cross-platform**: Native binaries for Linux, macOS, and Windows (AMD64/ARM64) - **⚡ Enhanced performance**: Intelligent file transfer routing and S3 integration - **🔒 Advanced security**: Comprehensive IAM lifecycle management and automatic cleanup @@ -382,6 +413,7 @@ This will display your AWS access key, secret key, and session token for the spe **Installation:** See [INSTALLATION.md](INSTALLATION.md) for complete setup instructions. **Usage Examples:** + ```bash # Check system requirements ztictl config check @@ -399,7 +431,7 @@ ztictl ssm connect i-1234567890abcdef0 --region ca-central-1 # Linux instance - uses bash ztictl ssm exec ca-central-1 i-linux123 "echo 'Hello Linux'; uname -a" -# Windows instance - uses PowerShell +# Windows instance - uses PowerShell ztictl ssm exec ca-central-1 i-windows456 "Write-Output 'Hello Windows'; Get-ComputerInfo" # Cross-platform file transfers @@ -422,6 +454,7 @@ For CI/CD pipeline architecture and development workflow, see [docs/CI_CD_PIPELI ## 📚 Documentation ### Core Documentation + - **[Command Reference](docs/COMMANDS.md)** - Complete list of all commands with examples - **[Configuration Guide](docs/CONFIGURATION.md)** - Detailed configuration file reference - **[Multi-Region Operations](docs/MULTI_REGION.md)** - Guide for cross-region command execution @@ -432,6 +465,7 @@ For CI/CD pipeline architecture and development workflow, see [docs/CI_CD_PIPELI - **[IAM Permissions](docs/IAM_PERMISSIONS.md)** - Required AWS permissions ### Additional Resources + - **[CI/CD Pipeline](docs/CI_CD_PIPELINE.md)** - Automated build and release process - **[Release Notifications](docs/NOTIFICATIONS.md)** - Google Chat integration - **[QA Test Guide](tests/QA_SSM_TESTS.md)** - Testing procedures @@ -441,6 +475,7 @@ For CI/CD pipeline architecture and development workflow, see [docs/CI_CD_PIPELI We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. Key areas for contribution: + - Adding support for new regions - Improving documentation - Adding new features @@ -459,13 +494,16 @@ For maintainers who want to create a new release, please see [CONTRIBUTING.md](C ## 🔐 Security ### Built-in Security Features + - **Directory Traversal Protection**: Cross-platform path validation prevents unauthorized file access - **Input Validation**: Comprehensive validation for AWS resource identifiers and parameters - **IAM Lifecycle Management**: Automatic cleanup of temporary policies and permissions - **Secure File Handling**: Protected file operations with permission validation ### Best Practices + These tools require AWS credentials and access to your AWS resources. Always: + - Keep your AWS credentials secure - Use appropriate IAM permissions with least privilege - Review security best practices in the [AWS Security Documentation](https://docs.aws.amazon.com/security/) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md new file mode 100644 index 0000000..5e1df9e --- /dev/null +++ b/RELEASE_NOTES.md @@ -0,0 +1,45 @@ +# ztictl v2.11.0 Release Notes + +**Installation:** [Installation Guide](https://github.com/zsoftly/ztiaws/blob/release/v2.11.0/INSTALLATION.md) + +**Release Date:** November 13, 2025 + +## Overview + +ztictl is a unified AWS SSM management tool that provides both Go binary and bash script implementations. The Go version (`ztictl`) is the primary implementation with enhanced features, while the bash scripts (`authaws`, `ssm`) are maintained for backward compatibility only. + +**Note:** The bash scripts are no longer receiving new features or updates. All development efforts are focused on the Go implementation. + +## New Features + +- Enhance AWS SSO and CI/CD Integration +- Enhance AWS SSO and CI/CD Integration +- Enhance power operation handling and validation +- Add finalized ZTiAWS demo images and update Markdown paths +- Add files via upload +- Create .gitkeep +- Create .gitkeep +- Add ZTiAWS Demo Documentation (Installation, Authentication, and Use Cases) +- feat: Update EC2 Test Manager script with default subnet and security group IDs, add dry run option, and improve resource discovery logging + +## Bug Fixes + +- fix: Update build scripts and enhance error handling in OIDC examples +- fix: Update script paths and improve error handling in notification scripts +- fix: Increase HTTP client timeout for EC2 instance detection and add warning for proxy parsing errors + +## Other Changes + +- Wrap commands in proper markdown code blocks +- docs: finalize ZTiAWS demo documentation with consistent numbering and formatting +- Docs: add proper Markdown headers and syntax highlighting for config section +- docs(README): removed emojis +- docs(ztiaws-demo): finalize demo walkthrough with screenshots and stakeholder-focused updates +- Rename 04-confirm ssm-ec2.png to 04-confirm-ssm-ec2.png +- Rename 12-uploading- loca-to-ec2-file.png to 12-uploading-local-to-ec2-file.png +- Rename 11-creating- folder-file.png to 11-creating-folder-file.png +- docs: reordered demo flow, updated region codes, and refined next steps per review feedback +- refactor: Remove unused file modification time check in IAM cache validation +- Refs #130: Refactor EC2 Manager Script to Dynamically Fetch VPC Resources + +**Full Changelog**: https://github.com/zsoftly/ztiaws/compare/v2.10.0...v2.11.0 diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt deleted file mode 100644 index 209893e..0000000 --- a/RELEASE_NOTES.txt +++ /dev/null @@ -1,42 +0,0 @@ -# ztictl v2.11.0 Release Notes - -**Installation:** [Installation Guide](https://github.com/zsoftly/ztiaws/blob/release/v2.11.0/INSTALLATION.md) - -**Release Date:** November 13, 2025 - -## Overview - -ztictl is a unified AWS SSM management tool that provides both Go binary and bash script implementations. The Go version (`ztictl`) is the primary implementation with enhanced features, while the bash scripts (`authaws`, `ssm`) are maintained for backward compatibility only. - -**Note:** The bash scripts are no longer receiving new features or updates. All development efforts are focused on the Go implementation. - -## New Features -* Enhance AWS SSO and CI/CD Integration -* Enhance AWS SSO and CI/CD Integration -* Enhance power operation handling and validation -* Add finalized ZTiAWS demo images and update Markdown paths -* Add files via upload -* Create .gitkeep -* Create .gitkeep -* Add ZTiAWS Demo Documentation (Installation, Authentication, and Use Cases) -* feat: Update EC2 Test Manager script with default subnet and security group IDs, add dry run option, and improve resource discovery logging - -## Bug Fixes -* fix: Update build scripts and enhance error handling in OIDC examples -* fix: Update script paths and improve error handling in notification scripts -* fix: Increase HTTP client timeout for EC2 instance detection and add warning for proxy parsing errors - -## Other Changes -* Wrap commands in proper markdown code blocks -* docs: finalize ZTiAWS demo documentation with consistent numbering and formatting -* Docs: add proper Markdown headers and syntax highlighting for config section -* docs(README): removed emojis -* docs(ztiaws-demo): finalize demo walkthrough with screenshots and stakeholder-focused updates -* Rename 04-confirm ssm-ec2.png to 04-confirm-ssm-ec2.png -* Rename 12-uploading- loca-to-ec2-file.png to 12-uploading-local-to-ec2-file.png -* Rename 11-creating- folder-file.png to 11-creating-folder-file.png -* docs: reordered demo flow, updated region codes, and refined next steps per review feedback -* refactor: Remove unused file modification time check in IAM cache validation -* Refs #130: Refactor EC2 Manager Script to Dynamically Fetch VPC Resources - -**Full Changelog**: https://github.com/zsoftly/ztiaws/compare/v2.10.0...v2.11.0 diff --git a/docs/CI_CD_AUTHENTICATION.md b/docs/CI_CD_AUTHENTICATION.md index 3b35058..38d8336 100644 --- a/docs/CI_CD_AUTHENTICATION.md +++ b/docs/CI_CD_AUTHENTICATION.md @@ -28,6 +28,7 @@ AWS SSO uses the **OAuth2 Device Authorization Grant** flow (RFC 8628), which re 5. Browser redirects and provides credentials **CI/CD pipelines cannot:** + - Open web browsers - Display interactive prompts - Wait for user input @@ -44,17 +45,20 @@ AWS SSO uses the **OAuth2 Device Authorization Grant** flow (RFC 8628), which re **Best for:** GitHub Actions, GitLab CI, modern CI/CD platforms **Advantages:** + - No long-lived credentials stored - Automatic credential rotation - Fine-grained permission control - Audit trail via CloudTrail **How it works:** + 1. CI/CD platform provides OIDC token 2. AWS STS exchanges token for temporary credentials 3. `ztictl` uses temporary credentials via AWS SDK **Platforms with OIDC support:** + - GitHub Actions - GitLab CI/CD - CircleCI @@ -66,11 +70,13 @@ AWS SSO uses the **OAuth2 Device Authorization Grant** flow (RFC 8628), which re **Best for:** Self-hosted runners on EC2 instances **Advantages:** + - No credential management needed - Automatic credential rotation - Seamless integration with AWS services **How it works:** + 1. Attach IAM role to EC2 instance 2. Instance metadata service provides credentials 3. `ztictl` automatically uses instance credentials @@ -80,16 +86,19 @@ AWS SSO uses the **OAuth2 Device Authorization Grant** flow (RFC 8628), which re **Best for:** Quick testing, legacy systems **Disadvantages:** + - Long-lived credentials (security risk) - Manual rotation required - Stored in secrets manager (additional exposure) **Use only when:** + - OIDC not available - Not running on AWS infrastructure - Temporary solution during migration **How it works:** + 1. Create IAM user with access keys 2. Store keys in CI/CD secrets 3. Export as environment variables @@ -100,11 +109,13 @@ AWS SSO uses the **OAuth2 Device Authorization Grant** flow (RFC 8628), which re **Best for:** Containerized CI/CD on ECS/Fargate **Advantages:** + - No credential management - Automatic credential rotation - Container-level isolation **How it works:** + 1. Define task role in ECS task definition 2. ECS provides credentials to container 3. `ztictl` uses credentials automatically @@ -118,6 +129,7 @@ AWS SSO uses the **OAuth2 Device Authorization Grant** flow (RFC 8628), which re **Recommended:** OIDC Federation **Required IAM Role Trust Policy:** + ```json { "Version": "2012-10-17", @@ -142,12 +154,13 @@ AWS SSO uses the **OAuth2 Device Authorization Grant** flow (RFC 8628), which re ``` **Workflow Example:** + ```yaml name: Deploy with ztictl on: [push] permissions: - id-token: write # Required for OIDC + id-token: write # Required for OIDC contents: read jobs: @@ -184,6 +197,7 @@ See: [docs/examples/github-actions-oidc.yml](examples/github-actions-oidc.yml) **Recommended:** OIDC Federation **Required IAM Role Trust Policy:** + ```json { "Version": "2012-10-17", @@ -208,6 +222,7 @@ See: [docs/examples/github-actions-oidc.yml](examples/github-actions-oidc.yml) ``` **Pipeline Example:** + ```yaml deploy: image: alpine:latest @@ -239,6 +254,7 @@ See: [docs/examples/gitlab-ci-oidc.yml](examples/gitlab-ci-oidc.yml) **Recommended:** EC2 Instance Profile (if self-hosted on EC2) or IAM Access Keys **Option A: EC2 Instance Profile** + ```groovy pipeline { agent { label 'ec2' } // EC2-based Jenkins agent @@ -263,6 +279,7 @@ pipeline { ``` **Option B: IAM Access Keys (stored in Jenkins credentials)** + ```groovy pipeline { agent any @@ -298,6 +315,7 @@ See: [docs/examples/jenkins-iam-keys.groovy](examples/jenkins-iam-keys.groovy) **Recommended:** OIDC Federation or IAM Access Keys **OIDC Example:** + ```yaml version: 2.1 @@ -339,6 +357,7 @@ workflows: **Recommended:** Service Role (automatic) **buildspec.yml:** + ```yaml version: 0.2 @@ -361,7 +380,7 @@ phases: env: variables: - ZTICTL_NON_INTERACTIVE: "true" + ZTICTL_NON_INTERACTIVE: 'true' ``` **Note:** CodeBuild automatically provides credentials via the service role attached to the build project. @@ -375,6 +394,7 @@ env: The IAM role or user must have permissions for the operations you plan to perform: **SSM Operations:** + ```json { "Version": "2012-10-17", @@ -401,6 +421,7 @@ For complete permission requirements, see: [IAM_PERMISSIONS.md](IAM_PERMISSIONS. ### Environment Variables **Required for ztictl:** + ```bash # AWS credentials (provided automatically by most CI/CD platforms) AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE # (or via OIDC/instance profile) @@ -413,6 +434,7 @@ ZTICTL_NON_INTERACTIVE=true # Disable all interactive prompts ``` **Optional:** + ```bash ZTICTL_LOG_ENABLED=false # Disable file logging in CI ZTICTL_LOG_LEVEL=info # Log verbosity @@ -429,12 +451,14 @@ ZTICTL_INSTANCE_ID=i-1234567890abcdef0 # Default instance for SSM command 3. `--non-interactive` flag is used **In non-interactive mode:** + - Splash screen is suppressed - All interactive prompts are skipped - Commands requiring input will fail with clear error messages - Operations use environment variables or fail fast **Example:** + ```bash # Explicit non-interactive mode ztictl --non-interactive ssm list --table @@ -579,12 +603,14 @@ jobs: **Problem:** AWS SDK cannot find credentials **Solutions:** + 1. Verify OIDC configuration is correct 2. Check that `id-token: write` permission is set (GitHub Actions) 3. Verify IAM role trust policy allows your CI/CD platform 4. Check environment variables are set correctly **Debug:** + ```bash # Check which credentials are being used aws sts get-caller-identity @@ -598,11 +624,13 @@ aws ec2 describe-instances --region ca-central-1 --max-items 1 **Problem:** IAM role/user lacks required permissions **Solutions:** + 1. Review [IAM_PERMISSIONS.md](IAM_PERMISSIONS.md) for required permissions 2. Attach appropriate IAM policies to role/user 3. Verify resource-based policies allow access **Debug:** + ```bash # Check current identity aws sts get-caller-identity @@ -616,12 +644,15 @@ aws ssm describe-instance-information --region ca-central-1 --max-items 1 **Problem:** SSM command needs explicit instance identifier in CI **Solutions:** + 1. Provide instance ID as argument: + ```bash ztictl ssm connect i-1234567890abcdef0 --region ca-central-1 ``` 2. Use instance name (auto-resolved): + ```bash ztictl ssm connect web-server-prod --region ca-central-1 ``` @@ -638,6 +669,7 @@ aws ssm describe-instance-information --region ca-central-1 --max-items 1 **Solution:** Don't use SSO in CI/CD. Use IAM-based authentication instead (see above). This error appears when: + - Running `ztictl auth login` in a CI environment - CI environment is detected (CI=true) but no AWS credentials exist @@ -650,18 +682,21 @@ This error appears when: **Solutions:** **Ubuntu/Debian:** + ```bash curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_64bit/session-manager-plugin.deb" -o "session-manager-plugin.deb" sudo dpkg -i session-manager-plugin.deb ``` **Amazon Linux/RHEL:** + ```bash curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/linux_64bit/session-manager-plugin.rpm" -o "session-manager-plugin.rpm" sudo yum install -y session-manager-plugin.rpm ``` **Docker (add to Dockerfile):** + ```dockerfile RUN curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_64bit/session-manager-plugin.deb" -o "/tmp/session-manager-plugin.deb" && \ dpkg -i /tmp/session-manager-plugin.deb && \ @@ -681,6 +716,7 @@ ztictl ssm list ``` If issue persists, verify CI environment variable is set: + ```bash export CI=true ``` @@ -690,17 +726,20 @@ export CI=true ## Best Practices ### 1. Use OIDC When Possible + - No credential management - Automatic rotation - Better security posture - Audit trail ### 2. Minimize Permission Scope + - Use least-privilege IAM policies - Scope permissions to specific resources when possible - Separate roles for different environments ### 3. Use Tag-Based Operations + - More resilient than instance IDs - Works with auto-scaling groups - Easier to manage @@ -714,6 +753,7 @@ ztictl ssm exec ca-central-1 i-1234567890abcdef0 "deploy.sh" ``` ### 4. Set Explicit Regions + - Don't rely on defaults - Specify region in commands or environment variables @@ -724,6 +764,7 @@ env: ``` ### 5. Enable Debug Logging for Troubleshooting + ```yaml env: ZTICTL_LOG_ENABLED: "true" @@ -739,12 +780,14 @@ env: ``` ### 6. Use `--table` Output for Parsing + ```bash # Machine-readable table output ztictl ssm list --table --region ca-central-1 | grep running ``` ### 7. Test with `--dry-run` When Available + ```bash ztictl ssm cleanup --region ca-central-1 --dry-run ``` @@ -754,17 +797,21 @@ ztictl ssm cleanup --region ca-central-1 --dry-run ## Security Considerations ### 1. Rotate Access Keys Regularly + If using IAM access keys: + - Rotate every 90 days minimum - Use AWS Secrets Manager or CI/CD secrets - Never commit keys to source control ### 2. Use Temporary Credentials + - OIDC provides short-lived credentials (1 hour default) - Instance profiles rotate automatically - Avoid long-lived access keys ### 3. Restrict OIDC Trust Policies + ```json { "Condition": { @@ -779,16 +826,19 @@ If using IAM access keys: ``` This restricts role assumption to: + - Specific repository - Specific branch (main) - Prevents unauthorized use ### 4. Audit CloudTrail Logs + - Monitor API calls from CI/CD - Set up alerts for unusual activity - Review access patterns regularly ### 5. Separate Roles by Environment + ``` GitHubActions-Dev-Role → Limited permissions, dev resources GitHubActions-Prod-Role → Full permissions, prod resources diff --git a/docs/CI_CD_PIPELINE.md b/docs/CI_CD_PIPELINE.md index 5b0a0bd..4ce61c3 100644 --- a/docs/CI_CD_PIPELINE.md +++ b/docs/CI_CD_PIPELINE.md @@ -7,86 +7,100 @@ ZTiAWS uses a streamlined, intelligent CI/CD pipeline with two optimized workflo ## Design Philosophy ### **Smart Conditional Execution** + Jobs run only when needed, optimizing CI/CD resource usage and developer experience: -* **Quick feedback** for feature development -* **Comprehensive validation** for production releases -* **Security scanning** for critical changes -* **Cross-platform builds** only when necessary +- **Quick feedback** for feature development +- **Comprehensive validation** for production releases +- **Security scanning** for critical changes +- **Cross-platform builds** only when necessary ### **DRY (Don't Repeat Yourself) Principles** -* **Single workflow file** instead of multiple redundant workflows -* **Conditional jobs** that adapt to different scenarios -* **Shared setup steps** with consistent Go version and dependencies -* **Consolidated security tooling** avoiding duplicate vulnerability scans +- **Single workflow file** instead of multiple redundant workflows +- **Conditional jobs** that adapt to different scenarios +- **Shared setup steps** with consistent Go version and dependencies +- **Consolidated security tooling** avoiding duplicate vulnerability scans ## Pipeline Architecture The ZTiAWS CI/CD pipeline consists of two specialized workflows: ### Primary Workflow: `.github/workflows/build.yml` (Unified CI/CD) + **Focus**: Complete testing, security, and release pipeline for all components #### **Triggers** + ```yaml on: push: - branches: [ main, 'feature/*', 'feat/*', 'issue/*', 'release/*' ] - tags: [ 'v*' ] - paths: [ - 'ztictl/**', 'authaws', 'ssm', 'src/**', 'tools/**', - '01_install.sh', '02_uninstall.sh', 'go.mod', 'go.sum', 'Makefile' - ] + branches: [main, 'feature/*', 'feat/*', 'issue/*', 'release/*'] + tags: ['v*'] + paths: + [ + 'ztictl/**', + 'authaws', + 'ssm', + 'src/**', + 'tools/**', + '01_install.sh', + '02_uninstall.sh', + 'go.mod', + 'go.sum', + 'Makefile', + ] pull_request: - branches: [ main, 'release/*' ] - paths: [ /* same as above */ ] + branches: [main, 'release/*'] + paths: [/* same as above */] workflow_dispatch: ``` **Why this unified design:** -* **Comprehensive path filtering** covers all project components -* **Single workflow** eliminates duplicate testing overhead -* **Branch patterns** support complete Git flow -* **Tag-based releases** enable automated release creation -* **Dependency awareness** (go.mod, Makefile changes trigger rebuilds) +- **Comprehensive path filtering** covers all project components +- **Single workflow** eliminates duplicate testing overhead +- **Branch patterns** support complete Git flow +- **Tag-based releases** enable automated release creation +- **Dependency awareness** (go.mod, Makefile changes trigger rebuilds) #### Job Architecture ```mermaid graph TD A[Trigger Event] --> B{Event Type?} - + B -->|PR or Push| C[test-shell + test-go] B -->|PR to main| D[test-shell + test-go + security] B -->|Tag Push v*| E[build → release → notify] B -->|Manual Dispatch| F[build only] B -->|Push to release/*| G[auto-generate-docs] - + C --> H[Parallel Testing] D --> I[Sequential: Tests → Security] - + H -->|Fail| J[Immediate Failure Notification] I -->|All Pass| K[Success Notification] I -->|Any Fail| L[Failure Notification] - + E --> M[6-Platform Build] M --> N[GitHub Release] N --> O[Release Notification] - + F --> P[6-Platform Build Only] - + G --> Q[Generate CHANGELOG] Q --> R[Auto-commit to Branch] ``` ### Documentation Workflow: `.github/workflows/auto-generate-docs.yml` + **Focus**: Automated release documentation generation **Triggers**: Push to `release/*` branches **Purpose**: Automatically generates CHANGELOG.md and RELEASE_NOTES.txt when a release branch is created **Process**: + 1. Extracts version from branch name (e.g., `release/v2.7.0` → `v2.7.0`) 2. Finds latest git tag for comparison 3. Runs `tools/02_release_docs_generator.sh` to generate documentation @@ -96,6 +110,7 @@ graph TD ### **Job Details** #### 1. **test-shell** - Shell Script Validation + ```yaml if: github.event_name == 'pull_request' || (github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')) strategy: @@ -107,16 +122,18 @@ strategy: **When it runs:** All PRs and feature branch pushes (not tags) **What it does:** -* ShellCheck static analysis (`shellcheck -x`) -* Syntax validation (`bash -n`) -* Cross-platform compatibility testing +- ShellCheck static analysis (`shellcheck -x`) +- Syntax validation (`bash -n`) +- Cross-platform compatibility testing **Why Ubuntu + macOS:** -* Primary shell script platforms (bash/zsh) -* Windows users utilize ztictl.exe (Go binary) -* Covers 95% of shell script usage scenarios + +- Primary shell script platforms (bash/zsh) +- Windows users utilize ztictl.exe (Go binary) +- Covers 95% of shell script usage scenarios #### 2. **test-go** - Go Code Testing + ```yaml if: github.event_name == 'pull_request' || (github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')) strategy: @@ -128,18 +145,20 @@ strategy: **When it runs:** All PRs and feature branch pushes (not tags) **What it does:** -* Unit tests (`go test -v ./...`) -* Static analysis (`go vet ./...`) -* Code formatting validation (`gofmt -s -l .`) -* Build verification for each platform -* CLI command testing (help, version, config commands) +- Unit tests (`go test -v ./...`) +- Static analysis (`go vet ./...`) +- Code formatting validation (`gofmt -s -l .`) +- Build verification for each platform +- CLI command testing (help, version, config commands) **Why 3 platforms:** -* Full cross-platform validation (Linux, Windows, macOS) -* Platform-specific path handling verification -* Line ending compatibility checks (CRLF vs LF) + +- Full cross-platform validation (Linux, Windows, macOS) +- Platform-specific path handling verification +- Line ending compatibility checks (CRLF vs LF) #### 3. **security** - Security Analysis + ```yaml if: github.event_name == 'pull_request' && github.base_ref == 'main' needs: [test-shell, test-go] @@ -151,28 +170,29 @@ needs: [test-shell, test-go] **Skip Behavior:** Automatically skipped if prerequisite tests fail (prevents blocking PR notifications) **Tools used:** -* **Trivy**: Filesystem vulnerability scanning by Aqua Security -* **golangci-lint v2.4.0**: With GoSec security linters enabled -* **Standalone GoSec**: Backup security analysis (latest version) -* **govulncheck**: Official Go vulnerability database checker -* **Dependency audit**: Lists outdated packages with `go list -u -m all` +- **Trivy**: Filesystem vulnerability scanning by Aqua Security +- **golangci-lint v2.4.0**: With GoSec security linters enabled +- **Standalone GoSec**: Backup security analysis (latest version) +- **govulncheck**: Official Go vulnerability database checker +- **Dependency audit**: Lists outdated packages with `go list -u -m all` **Why this conditional approach:** -* Security scans are resource-intensive -* Main branch PRs need highest security validation -* Feature branch work doesn't require full security scan -* Prevents CI/CD bottlenecks during development -* **Fail fast**: Only runs if tests pass +- Security scans are resource-intensive +- Main branch PRs need highest security validation +- Feature branch work doesn't require full security scan +- Prevents CI/CD bottlenecks during development +- **Fail fast**: Only runs if tests pass **Security Tools Configuration:** -* **Non-blocking design**: All scans use `continue-on-error: true` -* **Layered approach**: Multiple tools for comprehensive coverage -* **Report artifacts**: JSON reports uploaded for 30-day retention -* **Informational only**: Security findings don't block PRs (by design) +- **Non-blocking design**: All scans use `continue-on-error: true` +- **Layered approach**: Multiple tools for comprehensive coverage +- **Report artifacts**: JSON reports uploaded for 30-day retention +- **Informational only**: Security findings don't block PRs (by design) #### 4. **build** - Cross-Platform Build + ```yaml if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' strategy: @@ -190,18 +210,19 @@ strategy: **When it runs:** Version tags or manual dispatch only **Features:** -* Version injection: `${VERSION}` from git tag or `dev-${COMMIT_SHA}` -* Optimized builds: `-ldflags "-X main.Version=${VERSION} -s -w"` -* Binary verification: Test run on Linux AMD64 only -* Artifact upload: Individual platform artifacts with 30-day retention +- Version injection: `${VERSION}` from git tag or `dev-${COMMIT_SHA}` +- Optimized builds: `-ldflags "-X main.Version=${VERSION} -s -w"` +- Binary verification: Test run on Linux AMD64 only +- Artifact upload: Individual platform artifacts with 30-day retention **Why 6 platforms:** -* Comprehensive platform support for end users -* ARM64 support for modern hardware (Apple Silicon, ARM servers) -* Future-proofing for emerging architectures +- Comprehensive platform support for end users +- ARM64 support for modern hardware (Apple Silicon, ARM servers) +- Future-proofing for emerging architectures #### 5. **release** - GitHub Release + ```yaml if: startsWith(github.ref, 'refs/tags/v') needs: [build] @@ -220,9 +241,11 @@ needs: [build] 6. Attach both direct binaries and archives #### 5. **Embedded Notifications** - PR Status Reporting + **Architecture:** Notification steps embedded within each critical job **Implementation Pattern:** + ```yaml - name: Notify on Test Failure if: failure() && matrix.os == 'ubuntu-latest' && github.event_name == 'pull_request' @@ -233,22 +256,26 @@ needs: [build] **No Dependencies:** Each job can notify independently, preventing cascade failures **Notification Points:** -* **Shell Test Job**: Notifies immediately on shell test failure -* **Go Test Job**: Notifies immediately on Go test failure -* **Security Job**: Notifies on security failure OR final success (all tests passed) + +- **Shell Test Job**: Notifies immediately on shell test failure +- **Go Test Job**: Notifies immediately on Go test failure +- **Security Job**: Notifies on security failure OR final success (all tests passed) **Message Types:** -* **Individual failures**: "❌ [Shell/Go/Security] tests failed - PR needs attention" -* **Final success**: "✅ All tests passed - PR is ready for review" -* **Immediate feedback**: No waiting for other jobs to complete + +- **Individual failures**: "❌ [Shell/Go/Security] tests failed - PR needs attention" +- **Final success**: "✅ All tests passed - PR is ready for review" +- **Immediate feedback**: No waiting for other jobs to complete **Benefits:** -* **Fail-fast notifications**: Immediate alerts when issues occur -* **No dependency chains**: Each job notifies independently -* **Guaranteed delivery**: Cannot be blocked by other job failures -* **Single runner**: Only Ubuntu runners send notifications (prevents duplicates) + +- **Fail-fast notifications**: Immediate alerts when issues occur +- **No dependency chains**: Each job notifies independently +- **Guaranteed delivery**: Cannot be blocked by other job failures +- **Single runner**: Only Ubuntu runners send notifications (prevents duplicates) #### 6. **release-notification** - Release Notifications + ```yaml if: startsWith(github.ref, 'refs/tags/v') needs: [release] @@ -264,51 +291,52 @@ needs: [release] ## Workflow Behavior Matrix -| Scenario | Triggered Jobs | Execution & Notifications | -|----------|---------------|---------------------------| -| **Feature branch push** | `test-shell` + `test-go` | Parallel execution, no notifications | -| **PR to any branch** | `test-shell` + `test-go` | Parallel, failure notifications to Google Chat | -| **PR to main (pass)** | `test-shell` + `test-go` → `security` | Sequential, success notification after security | -| **PR to main (fail)** | `test-shell` + `test-go` | Tests run, security skipped, failure notification | -| **Release branch push** | `auto-generate-docs` | Generates and commits CHANGELOG.md | -| **Tag push (v*)** | `build` → `release` → `notification` | Full release pipeline | -| **Manual dispatch** | `build` only | Cross-platform builds without release | +| Scenario | Triggered Jobs | Execution & Notifications | +| ----------------------- | ------------------------------------- | ------------------------------------------------- | +| **Feature branch push** | `test-shell` + `test-go` | Parallel execution, no notifications | +| **PR to any branch** | `test-shell` + `test-go` | Parallel, failure notifications to Google Chat | +| **PR to main (pass)** | `test-shell` + `test-go` → `security` | Sequential, success notification after security | +| **PR to main (fail)** | `test-shell` + `test-go` | Tests run, security skipped, failure notification | +| **Release branch push** | `auto-generate-docs` | Generates and commits CHANGELOG.md | +| **Tag push (v\*)** | `build` → `release` → `notification` | Full release pipeline | +| **Manual dispatch** | `build` only | Cross-platform builds without release | ## Performance Optimizations ### **Fail Fast Strategy** -* **Tests fail** → Security scans are skipped automatically -* **Build fails** → Release is skipped automatically -* **Early feedback** → Developers get test results first -* **Resource savings** → Expensive jobs only run when prerequisites pass -* **Immediate notifications** → Each job notifies on failure, no waiting for dependent jobs +- **Tests fail** → Security scans are skipped automatically +- **Build fails** → Release is skipped automatically +- **Early feedback** → Developers get test results first +- **Resource savings** → Expensive jobs only run when prerequisites pass +- **Immediate notifications** → Each job notifies on failure, no waiting for dependent jobs ### **Resource Efficiency** -* **Conditional execution**: Jobs run only when needed -* **Path filtering**: Skip irrelevant changes -* **Matrix optimization**: Tests use 2 platforms, builds use 6 -* **Smart dependencies**: Security analysis only after successful tests +- **Conditional execution**: Jobs run only when needed +- **Path filtering**: Skip irrelevant changes +- **Matrix optimization**: Tests use 2 platforms, builds use 6 +- **Smart dependencies**: Security analysis only after successful tests ### **Developer Experience** -* **Fast feedback loop**: Quick tests complete in ~3-5 minutes -* **Non-blocking security**: Information-only security scans -* **Clear status reporting**: Descriptive job names and summaries -* **Immediate failure alerts**: Instant notifications when any test fails -* **No cascade delays**: Notifications sent immediately, not after all jobs complete -* **Single final success**: Only one success notification after all tests pass +- **Fast feedback loop**: Quick tests complete in ~3-5 minutes +- **Non-blocking security**: Information-only security scans +- **Clear status reporting**: Descriptive job names and summaries +- **Immediate failure alerts**: Instant notifications when any test fails +- **No cascade delays**: Notifications sent immediately, not after all jobs complete +- **Single final success**: Only one success notification after all tests pass ### **CI/CD Cost Optimization** -* **Smart triggering**: Expensive builds only for releases -* **Artifact retention**: 30-day cleanup prevents storage bloat -* **Efficient caching**: Go module caching via actions/setup-go +- **Smart triggering**: Expensive builds only for releases +- **Artifact retention**: 30-day cleanup prevents storage bloat +- **Efficient caching**: Go module caching via actions/setup-go ## Release Process Integration ### **Git Flow with Automation** + The CI/CD pipeline integrates with the release process defined in [RELEASE.md](development/RELEASE.md): 1. **Create release branch**: `git checkout -b release/v2.7.0` @@ -319,63 +347,64 @@ The CI/CD pipeline integrates with the release process defined in [RELEASE.md](d 6. **Merge back**: Complete Git flow by merging to main ### **Automation Benefits** -* **Zero manual changelog editing**: Git history drives documentation -* **Consistent versioning**: Branch names control versions -* **Fail-safe process**: Each step validates before proceeding -* **Audit trail**: All changes tracked in git + +- **Zero manual changelog editing**: Git history drives documentation +- **Consistent versioning**: Branch names control versions +- **Fail-safe process**: Each step validates before proceeding +- **Audit trail**: All changes tracked in git ### **Benefits of Consolidation** -* **Single source of truth** for CI/CD logic -* **Reduced maintenance overhead** -* **Consistent Go version** across all jobs -* **Eliminated redundant testing** -* **Clearer job relationships** +- **Single source of truth** for CI/CD logic +- **Reduced maintenance overhead** +- **Consistent Go version** across all jobs +- **Eliminated redundant testing** +- **Clearer job relationships** ## Security Considerations ### **SAST (Static Application Security Testing)** -* **Non-failing approach**: Security issues reported but don't block development -* **Comprehensive coverage**: Multiple tools for different vulnerability types -* **Dependency tracking**: Automated alerts for outdated packages +- **Non-failing approach**: Security issues reported but don't block development +- **Comprehensive coverage**: Multiple tools for different vulnerability types +- **Dependency tracking**: Automated alerts for outdated packages ### **Build Security** -* **Pinned action versions**: `@v4` for stability and security -* **Minimal permissions**: Jobs use least-privilege access -* **Artifact signing**: Future consideration for binary integrity +- **Pinned action versions**: `@v4` for stability and security +- **Minimal permissions**: Jobs use least-privilege access +- **Artifact signing**: Future consideration for binary integrity ## Maintenance ### **Regular Updates** -* **Go version**: Currently 1.25, update in all workflow files -* **Action versions**: Using v4 for checkout, setup-go actions -* **Security tools**: golangci-lint pinned at v2.4.0 for stability -* **Build dependencies**: Regular `go mod tidy` and updates +- **Go version**: Currently 1.25, update in all workflow files +- **Action versions**: Using v4 for checkout, setup-go actions +- **Security tools**: golangci-lint pinned at v2.4.0 for stability +- **Build dependencies**: Regular `go mod tidy` and updates ### **Monitoring** -* **Build status badges**: Visible in README -* **Failed build notifications**: GitHub notifications -* **Performance tracking**: Monitor CI/CD execution times +- **Build status badges**: Visible in README +- **Failed build notifications**: GitHub notifications +- **Performance tracking**: Monitor CI/CD execution times ## Future Enhancements ### **Potential Improvements** -* **Go version**: Upgrade to Go 1.26 when stable -* **golangci-lint**: Update from v2.4.0 to latest version -* **Test coverage**: Add coverage reporting and badges -* **AWS integration tests**: Mock AWS services for deeper testing -* **Binary signing**: GPG signing for release artifacts +- **Go version**: Upgrade to Go 1.26 when stable +- **golangci-lint**: Update from v2.4.0 to latest version +- **Test coverage**: Add coverage reporting and badges +- **AWS integration tests**: Mock AWS services for deeper testing +- **Binary signing**: GPG signing for release artifacts ### **Scalability Considerations** -* **Self-hosted runners**: For faster builds if needed -* **Matrix optimization**: Dynamic platform selection based on changes -* **Artifact distribution**: CDN distribution for popular releases +- **Self-hosted runners**: For faster builds if needed +- **Matrix optimization**: Dynamic platform selection based on changes +- **Artifact distribution**: CDN distribution for popular releases --- @@ -383,14 +412,15 @@ The CI/CD pipeline integrates with the release process defined in [RELEASE.md](d **Key Files:** -* Main pipeline: `.github/workflows/build.yml` -* Documentation: `.github/workflows/auto-generate-docs.yml` -* Release process: `docs/development/RELEASE.md` -* Build config: `ztictl/Makefile` -* Dependencies: `ztictl/go.mod`, `ztictl/go.sum` -* Notification scripts: `scripts/send-*.sh` +- Main pipeline: `.github/workflows/build.yml` +- Documentation: `.github/workflows/auto-generate-docs.yml` +- Release process: `docs/development/RELEASE.md` +- Build config: `ztictl/Makefile` +- Dependencies: `ztictl/go.mod`, `ztictl/go.sum` +- Notification scripts: `scripts/send-*.sh` **Common Commands:** + ```bash # Trigger test validation git push origin feature/my-change diff --git a/docs/COMMANDS.md b/docs/COMMANDS.md index 10e1d9f..f72b6be 100644 --- a/docs/COMMANDS.md +++ b/docs/COMMANDS.md @@ -3,6 +3,7 @@ This document provides comprehensive documentation for all ZTiAWS commands. For installation instructions, see [INSTALLATION.md](../INSTALLATION.md). ## Table of Contents + - [ztictl Commands](#ztictl-commands) - [Authentication](#authentication-commands) - [Configuration](#configuration-commands) @@ -18,6 +19,7 @@ This document provides comprehensive documentation for all ZTiAWS commands. For ### Authentication Commands #### `ztictl auth login` + Authenticate with AWS SSO and select account/role interactively. ```bash @@ -29,6 +31,7 @@ ztictl auth login --profile production ``` #### `ztictl auth whoami` + Display current AWS identity and credentials status. ```bash @@ -36,6 +39,7 @@ ztictl auth whoami ``` #### `ztictl auth logout` + Clear AWS SSO cached credentials. ```bash @@ -45,6 +49,7 @@ ztictl auth logout ### Configuration Commands #### `ztictl config init` + Initialize ztictl configuration interactively. ```bash @@ -56,6 +61,7 @@ ztictl config init --force ``` #### `ztictl config check` + Verify system requirements and configuration. ```bash @@ -67,6 +73,7 @@ ztictl config check --fix ``` #### `ztictl config show` + Display current configuration settings. ```bash @@ -74,6 +81,7 @@ ztictl config show ``` #### `ztictl config validate` + Validate configuration file syntax and required fields. ```bash @@ -81,6 +89,7 @@ ztictl config validate ``` #### `ztictl config repair` + **New in v2.5+** - Interactively fix configuration issues. ```bash @@ -93,9 +102,11 @@ ztictl config repair > **🔍 Interactive Fuzzy Finder**: Many SSM commands now feature an interactive fuzzy finder for enhanced user experience. The fuzzy finder provides real-time search, instance details preview, and keyboard navigation. #### `ztictl ssm list` + **🔍 Interactive Fuzzy Finder** - Browse and search EC2 instances with real-time filtering. The fuzzy finder provides: + - **Real-time search**: Type to filter instances by name or ID - **Instance details**: Shows state, platform, IP addresses, and SSM status - **Browse mode**: Navigate through instances without connecting @@ -116,18 +127,22 @@ ztictl ssm list --region cac1 --table ``` **Flags:** + - `--table` - Display instances in traditional table format (for scripts/automation) #### `ztictl ssm connect` + **🔍 Interactive Connection** - Connect to instances via Session Manager with fuzzy finder support. **Interactive Mode** (no instance specified): + - Launches interactive fuzzy finder to search and select instances - Real-time filtering by typing instance names or IDs - Shows instance details including SSM status and platform - Press Enter to connect, Escape to cancel **Direct Mode** (instance specified): + - Connects directly to the specified instance by ID or name ```bash @@ -137,11 +152,12 @@ ztictl ssm connect --region euw1 # 🎯 Direct mode - Connect using instance ID ztictl ssm connect i-1234567890abcdef0 --region use1 -# 🎯 Direct mode - Connect using instance name +# 🎯 Direct mode - Connect using instance name ztictl ssm connect prod-web-01 --region cac1 ``` #### `ztictl ssm exec` + Execute commands on instances. ```bash @@ -156,6 +172,7 @@ ztictl ssm exec --tags "Environment=prod" "systemctl status nginx" --region euw1 ``` #### `ztictl ssm transfer` + Transfer files to/from instances. ```bash @@ -171,6 +188,7 @@ ztictl ssm transfer i-1234567890abcdef0:/remote/file.txt /local/path/ --region c **New in v2.4+** - EC2 instance power management commands. #### `ztictl ssm start` + Start stopped EC2 instances. ```bash @@ -185,6 +203,7 @@ ztictl ssm start --instances "i-1234,i-5678,i-9012" --parallel 2 --region euw1 ``` #### `ztictl ssm stop` + Stop running EC2 instances. ```bash @@ -196,6 +215,7 @@ ztictl ssm stop --instances "i-1234,i-5678" --region use1 ``` #### `ztictl ssm start-tagged` + Start instances by tags (parallel execution). ```bash @@ -210,6 +230,7 @@ ztictl ssm start-tagged --tags "AutoStart=true" --parallel 5 --region euw1 ``` #### `ztictl ssm stop-tagged` + Stop instances by tags (parallel execution). ```bash @@ -225,6 +246,7 @@ ztictl ssm stop-tagged --tags "AutoStop=true" --force --region use1 **New in v2.6+** - Execute commands across multiple AWS regions simultaneously. #### `ztictl ssm exec-multi` + Execute commands across multiple regions. See [MULTI_REGION.md](MULTI_REGION.md) for detailed configuration. ```bash @@ -251,9 +273,10 @@ ztictl ssm exec-multi --all-regions --tags "Type=cache" "redis-cli ping" --paral **New in v2.9+** - Enhanced interactive instance selection with real-time search capabilities. ### Features + - **🔍 Real-time Search**: Type to instantly filter instances by name or ID - **📋 Instance Details**: Preview instance information before connecting -- **⌨️ Keyboard Navigation**: +- **⌨️ Keyboard Navigation**: - Arrow keys or `j/k` to navigate - Enter to select instance - Escape or `q` to cancel @@ -262,12 +285,15 @@ ztictl ssm exec-multi --all-regions --tags "Type=cache" "redis-cli ping" --paral - **🏷️ Tag Support**: Pre-filter instances by tags before launching fuzzy finder ### When Fuzzy Finder Launches + The interactive fuzzy finder automatically launches when: + - `ztictl ssm list` - Always launches for browsing instances - `ztictl ssm connect` - Launches when no instance identifier is provided - Any command where instance selection is needed but not specified ### Example Workflow + ```bash # 1. Launch fuzzy finder for region ztictl ssm connect --region ca-central-1 @@ -282,6 +308,7 @@ ztictl ssm connect --region ca-central-1 ``` ### Instance Information Displayed + - **Instance ID**: e.g., `i-1234567890abcdef0` - **Name**: Instance name from Name tag - **State**: running, stopped, pending, etc. @@ -296,6 +323,7 @@ ztictl ssm connect --region ca-central-1 > ⚠️ **Note:** These bash-based commands remain in production use. Consider migrating to `ztictl` for enhanced features and cross-platform support. ### authaws + AWS SSO authentication tool. ```bash @@ -310,6 +338,7 @@ authaws --version ``` ### ssm + Session Manager operations tool. ```bash @@ -335,19 +364,19 @@ ssm i-1234567890abcdef0 -d /remote/file.txt:/local/path/ Both `ztictl` and legacy tools support region shortcodes: -| Shortcode | Region Name | Location | -|-----------|------------|----------| -| `cac1` | ca-central-1 | Canada (Montreal) | -| `use1` | us-east-1 | US East (N. Virginia) | -| `use2` | us-east-2 | US East (Ohio) | -| `usw1` | us-west-1 | US West (N. California) | -| `usw2` | us-west-2 | US West (Oregon) | -| `euw1` | eu-west-1 | EU (Ireland) | -| `euw2` | eu-west-2 | EU (London) | -| `euc1` | eu-central-1 | EU (Frankfurt) | -| `apse1` | ap-southeast-1 | Asia Pacific (Singapore) | -| `apse2` | ap-southeast-2 | Asia Pacific (Sydney) | -| `apne1` | ap-northeast-1 | Asia Pacific (Tokyo) | +| Shortcode | Region Name | Location | +| --------- | -------------- | ------------------------ | +| `cac1` | ca-central-1 | Canada (Montreal) | +| `use1` | us-east-1 | US East (N. Virginia) | +| `use2` | us-east-2 | US East (Ohio) | +| `usw1` | us-west-1 | US West (N. California) | +| `usw2` | us-west-2 | US West (Oregon) | +| `euw1` | eu-west-1 | EU (Ireland) | +| `euw2` | eu-west-2 | EU (London) | +| `euc1` | eu-central-1 | EU (Frankfurt) | +| `apse1` | ap-southeast-1 | Asia Pacific (Singapore) | +| `apse2` | ap-southeast-2 | Asia Pacific (Sydney) | +| `apne1` | ap-northeast-1 | Asia Pacific (Tokyo) | See full list in the source code: `ztictl/pkg/aws/regions.go` @@ -356,6 +385,7 @@ See full list in the source code: `ztictl/pkg/aws/regions.go` ## Exit Codes All commands follow standard exit code conventions: + - `0`: Success - `1`: General error - `2`: Misuse of command (invalid arguments) @@ -365,12 +395,12 @@ All commands follow standard exit code conventions: ## Environment Variables -| Variable | Description | Default | -|----------|-------------|---------| -| `AWS_PROFILE` | AWS profile to use | default | -| `AWS_REGION` | Default AWS region | us-east-1 | -| `ZTICTL_CONFIG` | Config file path | ~/.ztictl.yaml | -| `ZTICTL_LOG_LEVEL` | Logging level | info | +| Variable | Description | Default | +| ------------------ | ------------------ | -------------- | +| `AWS_PROFILE` | AWS profile to use | default | +| `AWS_REGION` | Default AWS region | us-east-1 | +| `ZTICTL_CONFIG` | Config file path | ~/.ztictl.yaml | +| `ZTICTL_LOG_LEVEL` | Logging level | info | --- @@ -378,4 +408,4 @@ All commands follow standard exit code conventions: - [CONFIGURATION.md](CONFIGURATION.md) - Configuration file reference - [MULTI_REGION.md](MULTI_REGION.md) - Multi-region operations guide -- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Common issues and solutions \ No newline at end of file +- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Common issues and solutions diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index e70b944..7e644b9 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -4,10 +4,10 @@ Complete reference for `ztictl` configuration file (`~/.ztictl.yaml`). ## Configuration File Location -| Platform | Default Location | -|----------|-----------------| -| Linux/macOS | `~/.ztictl.yaml` | -| Windows | `%USERPROFILE%\.ztictl.yaml` | +| Platform | Default Location | +| ----------- | ---------------------------- | +| Linux/macOS | `~/.ztictl.yaml` | +| Windows | `%USERPROFILE%\.ztictl.yaml` | Override with environment variable: `ZTICTL_CONFIG=/path/to/config.yaml` @@ -18,27 +18,27 @@ Override with environment variable: `ZTICTL_CONFIG=/path/to/config.yaml` # AWS SSO Configuration sso: - start_url: "https://d-1234567890.awsapps.com/start" # Required for SSO - region: "ca-central-1" # SSO service region (default) + start_url: 'https://d-1234567890.awsapps.com/start' # Required for SSO + region: 'ca-central-1' # SSO service region (default) # Default AWS region for operations -default_region: "ca-central-1" # Default region for all operations +default_region: 'ca-central-1' # Default region for all operations # Multi-region configuration regions: # List of enabled regions (shortcodes or full names) enabled: - - cac1 # ca-central-1 - - use1 # us-east-1 - - use2 # us-east-2 - - euw1 # eu-west-1 - - euw2 # eu-west-2 - - apse1 # ap-southeast-1 - - apse2 # ap-southeast-2 - + - cac1 # ca-central-1 + - use1 # us-east-1 + - use2 # us-east-2 + - euw1 # eu-west-1 + - euw2 # eu-west-2 + - apse1 # ap-southeast-1 + - apse2 # ap-southeast-2 + # Region groups for logical organization groups: - all: # Special group containing all enabled regions + all: # Special group containing all enabled regions - cac1 - use1 - use2 @@ -66,21 +66,21 @@ regions: # Logging configuration logging: - directory: "~/logs" # Log file directory - file_logging: true # Enable file logging - level: "info" # Log level: debug, info, warn, error - max_size: 100 # Max log file size in MB - max_backups: 5 # Number of old log files to keep - max_age: 30 # Days to keep old log files + directory: '~/logs' # Log file directory + file_logging: true # Enable file logging + level: 'info' # Log level: debug, info, warn, error + max_size: 100 # Max log file size in MB + max_backups: 5 # Number of old log files to keep + max_age: 30 # Days to keep old log files # System configuration system: - iam_propagation_delay: 5 # Seconds to wait for IAM changes - file_size_threshold: 1048576 # Bytes (1MB) - files larger use S3 - s3_bucket_prefix: "ztictl-ssm-file-transfer" - temp_directory: "/tmp" # Temporary file directory - parallel_operations: 5 # Default parallelism for multi-operations - command_timeout: 30 # Default command timeout in seconds + iam_propagation_delay: 5 # Seconds to wait for IAM changes + file_size_threshold: 1048576 # Bytes (1MB) - files larger use S3 + s3_bucket_prefix: 'ztictl-ssm-file-transfer' + temp_directory: '/tmp' # Temporary file directory + parallel_operations: 5 # Default parallelism for multi-operations + command_timeout: 30 # Default command timeout in seconds ``` ## Configuration Sections @@ -91,15 +91,17 @@ Controls AWS SSO authentication settings. ```yaml sso: - start_url: "https://d-1234567890.awsapps.com/start" # Your AWS SSO portal URL - region: "ca-central-1" # Region where SSO is configured (default) + start_url: 'https://d-1234567890.awsapps.com/start' # Your AWS SSO portal URL + region: 'ca-central-1' # Region where SSO is configured (default) ``` -**Required for**: +**Required for**: + - `ztictl auth login` - Any operation requiring AWS credentials **How to find your SSO domain ID**: + 1. Log into AWS SSO portal 2. Look at the URL: `https://YOUR-DOMAIN-ID.awsapps.com/start` 3. The domain ID is the part between `https://` and `.awsapps.com` @@ -110,10 +112,11 @@ sso: Sets the default AWS region for operations when not specified via `--region` flag. ```yaml -default_region: "ca-central-1" # Can use shortcode or full name +default_region: 'ca-central-1' # Can use shortcode or full name ``` **Precedence order**: + 1. Command flag: `--region` 2. Environment variable: `AWS_REGION` 3. Config file: `default_region` @@ -125,17 +128,18 @@ Defines regions for multi-region operations. ```yaml regions: - enabled: # Regions available for --all-regions - - cac1 # Shortcodes supported - - us-east-1 # Full names supported - - groups: # Logical groupings for --region-group + enabled: # Regions available for --all-regions + - cac1 # Shortcodes supported + - us-east-1 # Full names supported + + groups: # Logical groupings for --region-group production: - use1 - euw1 ``` **Special groups**: + - `all`: Automatically created, contains all enabled regions ### Logging Configuration @@ -144,21 +148,23 @@ Controls logging behavior and output. ```yaml logging: - directory: "~/logs" # Where to store log files - file_logging: true # Enable/disable file logging - level: "info" # Verbosity level - max_size: 100 # MB per log file - max_backups: 5 # Number of old files to keep - max_age: 30 # Days to retain logs + directory: '~/logs' # Where to store log files + file_logging: true # Enable/disable file logging + level: 'info' # Verbosity level + max_size: 100 # MB per log file + max_backups: 5 # Number of old files to keep + max_age: 30 # Days to retain logs ``` **Log levels** (least to most verbose): + - `error`: Only errors - `warn`: Warnings and errors - `info`: Informational messages (default) - `debug`: Detailed debugging information **Log file locations**: + - Linux/macOS: `~/logs/ztictl-YYYYMMDD.log` - Windows: `%USERPROFILE%\logs\ztictl-YYYYMMDD.log` @@ -168,12 +174,12 @@ Advanced system behavior settings. ```yaml system: - iam_propagation_delay: 5 # Seconds to wait after IAM changes - file_size_threshold: 1048576 # Bytes - threshold for S3 transfer - s3_bucket_prefix: "ztictl" # Prefix for temporary S3 buckets - temp_directory: "/tmp" # Temporary file storage - parallel_operations: 5 # Default parallelism - command_timeout: 30 # Default timeout in seconds + iam_propagation_delay: 5 # Seconds to wait after IAM changes + file_size_threshold: 1048576 # Bytes - threshold for S3 transfer + s3_bucket_prefix: 'ztictl' # Prefix for temporary S3 buckets + temp_directory: '/tmp' # Temporary file storage + parallel_operations: 5 # Default parallelism + command_timeout: 30 # Default timeout in seconds ``` ## Initial Setup @@ -214,6 +220,7 @@ ztictl config validate ## Configuration Management Commands ### Initialize Configuration + ```bash # Interactive setup (recommended) ztictl config init --interactive @@ -226,24 +233,28 @@ ztictl config init --force ``` ### Validate Configuration + ```bash # Check for syntax and required fields ztictl config validate ``` ### Show Configuration + ```bash # Display current settings ztictl config show ``` ### Repair Configuration + ```bash # Fix invalid configuration interactively ztictl config repair ``` ### Check Requirements + ```bash # Verify system requirements ztictl config check @@ -256,14 +267,14 @@ ztictl config check --fix Environment variables can override configuration file settings: -| Variable | Overrides | Example | -|----------|-----------|---------| -| `AWS_PROFILE` | AWS profile selection | `AWS_PROFILE=production` | -| `AWS_REGION` | `default_region` | `AWS_REGION=eu-west-1` | -| `ZTICTL_CONFIG` | Config file location | `ZTICTL_CONFIG=/etc/ztictl.yaml` | -| `ZTICTL_LOG_LEVEL` | `logging.level` | `ZTICTL_LOG_LEVEL=debug` | -| `ZTICTL_LOG_DIR` | `logging.directory` | `ZTICTL_LOG_DIR=/var/log` | -| `ZTICTL_SELECTOR_HEIGHT` | Fuzzy finder display height | `ZTICTL_SELECTOR_HEIGHT=10` | +| Variable | Overrides | Example | +| ------------------------ | --------------------------- | -------------------------------- | +| `AWS_PROFILE` | AWS profile selection | `AWS_PROFILE=production` | +| `AWS_REGION` | `default_region` | `AWS_REGION=eu-west-1` | +| `ZTICTL_CONFIG` | Config file location | `ZTICTL_CONFIG=/etc/ztictl.yaml` | +| `ZTICTL_LOG_LEVEL` | `logging.level` | `ZTICTL_LOG_LEVEL=debug` | +| `ZTICTL_LOG_DIR` | `logging.directory` | `ZTICTL_LOG_DIR=/var/log` | +| `ZTICTL_SELECTOR_HEIGHT` | Fuzzy finder display height | `ZTICTL_SELECTOR_HEIGHT=10` | ### UI Customization @@ -283,11 +294,13 @@ ztictl auth login ``` **Settings**: + - **Default**: 5 items (equivalent to fzf `--height=20%`) - **Range**: 1-20 items - **Behavior**: Invalid values log a warning and use default **Features**: + - All accounts/roles remain searchable regardless of display height - Full keyboard navigation through entire list with arrow keys - Single bordered rectangle at bottom of terminal @@ -298,6 +311,7 @@ ztictl auth login ### Automatic Validation Configuration is validated automatically when: + - Running any `ztictl` command - Using `ztictl config validate` - After `ztictl config init` @@ -305,12 +319,14 @@ Configuration is validated automatically when: ### Common Validation Errors 1. **Invalid SSO URL** + ``` Error: SSO start URL must begin with https:// Fix: ztictl config repair ``` 2. **Invalid Region** + ``` Error: 'caa-central-1' is not a valid AWS region Fix: Use 'ca-central-1' or shortcode 'cac1' @@ -342,6 +358,7 @@ ztictl config init --interactive ### Automatic Migration On first run, `ztictl` will: + 1. Check for `~/.ztictl.yaml` 2. If not found, check for `.env` 3. Offer to migrate settings @@ -357,13 +374,13 @@ Future support for multiple profiles: profiles: default: sso: - start_url: "https://personal.awsapps.com/start" - default_region: "ca-central-1" - + start_url: 'https://personal.awsapps.com/start' + default_region: 'ca-central-1' + work: sso: - start_url: "https://company.awsapps.com/start" - default_region: "ca-central-1" + start_url: 'https://company.awsapps.com/start' + default_region: 'ca-central-1' ``` ### Custom Commands (Planned) @@ -372,13 +389,14 @@ Define command aliases: ```yaml aliases: - prod-status: "exec-multi --region-group production --tags Environment=prod" - dev-deploy: "exec-multi --region-group development --tags App=web deploy.sh" + prod-status: 'exec-multi --region-group production --tags Environment=prod' + dev-deploy: 'exec-multi --region-group development --tags App=web deploy.sh' ``` ## Troubleshooting ### Configuration Not Found + ```bash # Check file location ls -la ~/.ztictl.yaml @@ -388,6 +406,7 @@ ztictl config init ``` ### Invalid Configuration + ```bash # Validate and show errors ztictl config validate @@ -400,6 +419,7 @@ nano ~/.ztictl.yaml ``` ### Permission Issues + ```bash # Fix file permissions chmod 600 ~/.ztictl.yaml @@ -434,4 +454,4 @@ chmod 700 ~/logs - [COMMANDS.md](COMMANDS.md) - Command reference - [MULTI_REGION.md](MULTI_REGION.md) - Multi-region operations -- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Common issues \ No newline at end of file +- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Common issues diff --git a/docs/IAM_PERMISSIONS.md b/docs/IAM_PERMISSIONS.md index 68cba74..826cc46 100644 --- a/docs/IAM_PERMISSIONS.md +++ b/docs/IAM_PERMISSIONS.md @@ -3,38 +3,40 @@ This document outlines the necessary IAM permissions for ZTiAWS tools. ## For SSM Session Manager (`ssm` tool): + ```json { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "ssm:StartSession", - "ssm:TerminateSession", - "ssm:ResumeSession", - "ec2:DescribeInstances" - ], - "Resource": "*" - } - ] + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ssm:StartSession", + "ssm:TerminateSession", + "ssm:ResumeSession", + "ec2:DescribeInstances" + ], + "Resource": "*" + } + ] } ``` ## For AWS SSO Authentication (`authaws` tool): + ```json { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "sso:GetRoleCredentials", - "sso:ListAccountRoles", - "sso:ListAccounts" - ], - "Resource": "*" - } - ] + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "sso:GetRoleCredentials", + "sso:ListAccountRoles", + "sso:ListAccounts" + ], + "Resource": "*" + } + ] } ``` diff --git a/docs/LOGGING.md b/docs/LOGGING.md index 9e83cdc..ca72ca2 100644 --- a/docs/LOGGING.md +++ b/docs/LOGGING.md @@ -14,18 +14,20 @@ The ztiaws toolkit implements a centralized logging system to provide consistent ## Logging Levels - `log_info` - Informational messages (green) -- `log_warn` - Warning messages (yellow) +- `log_warn` - Warning messages (yellow) - `log_error` - Error messages (red) - `log_debug` - Debug messages (cyan) ## Script Behavior ### authaws + - **Purpose**: Authentication workflows require audit trails - **Logging**: File logging enabled by default - **Location**: `$HOME/logs/authaws-YYYY-MM-DD.log` ### ssm + - **Purpose**: Interactive session management prioritizes clean console output - **Logging**: Console-only by default - **Override**: `ENABLE_SSM_LOGGING=true` enables file logging when needed @@ -33,10 +35,12 @@ The ztiaws toolkit implements a centralized logging system to provide consistent ## Configuration ### Environment Variables + - `LOG_DIR` - Override default log directory (`$HOME/logs`) - `ENABLE_SSM_LOGGING` - Enable file logging for SSM operations ### File Format + - Timestamped entries: `YYYY-MM-DD HH:MM:SS - [LEVEL] message` - Execution boundaries marked with session headers - Daily rotation by filename diff --git a/docs/MULTI_REGION.md b/docs/MULTI_REGION.md index 3cdf59e..3e864b9 100644 --- a/docs/MULTI_REGION.md +++ b/docs/MULTI_REGION.md @@ -17,12 +17,12 @@ Multi-region operations are configured in `~/.ztictl.yaml`: regions: # List of enabled regions (shortcodes or full names) enabled: - - cac1 # ca-central-1 - - use1 # us-east-1 - - use2 # us-east-2 - - euw1 # eu-west-1 - - apse1 # ap-southeast-1 - + - cac1 # ca-central-1 + - use1 # us-east-1 + - use2 # us-east-2 + - euw1 # eu-west-1 + - apse1 # ap-southeast-1 + # Region groups for logical organization groups: production: @@ -144,6 +144,7 @@ ztictl ssm exec-multi --region-group production \ ### Region-Specific Behavior The command automatically handles region-specific aspects: + - Validates region names and converts shortcodes - Establishes separate AWS clients per region - Handles region-specific errors gracefully @@ -203,6 +204,7 @@ ztictl ssm exec-multi --all-regions \ ## Use Cases ### 1. Security Patching + ```bash # Apply security updates across all regions ztictl ssm exec-multi --all-regions \ @@ -212,6 +214,7 @@ ztictl ssm exec-multi --all-regions \ ``` ### 2. Configuration Auditing + ```bash # Check configuration across regions ztictl ssm exec-multi --all-regions \ @@ -220,6 +223,7 @@ ztictl ssm exec-multi --all-regions \ ``` ### 3. Service Status Monitoring + ```bash # Check service status globally ztictl ssm exec-multi --region-group production \ @@ -228,6 +232,7 @@ ztictl ssm exec-multi --region-group production \ ``` ### 4. Log Collection + ```bash # Gather logs from all regions ztictl ssm exec-multi --all-regions \ @@ -239,6 +244,7 @@ ztictl ssm exec-multi --all-regions \ ## Best Practices 1. **Test in Development First** + ```bash ztictl ssm exec-multi --region-group development --tags "App=test" "your-command" ``` @@ -249,6 +255,7 @@ ztictl ssm exec-multi --all-regions \ - Use sequential (1) for critical changes 3. **Implement Health Checks** + ```bash ztictl ssm exec-multi --all-regions \ --tags "App=web" \ @@ -307,4 +314,4 @@ ztictl ssm exec-multi --all-regions \ - [COMMANDS.md](COMMANDS.md) - Complete command reference - [CONFIGURATION.md](CONFIGURATION.md) - Configuration file details -- [Region Codes](COMMANDS.md#region-shortcodes) - List of all region shortcodes \ No newline at end of file +- [Region Codes](COMMANDS.md#region-shortcodes) - List of all region shortcodes diff --git a/docs/NOTIFICATIONS.md b/docs/NOTIFICATIONS.md index 8227e26..e9bf0b6 100644 --- a/docs/NOTIFICATIONS.md +++ b/docs/NOTIFICATIONS.md @@ -13,12 +13,14 @@ ZTiAWS uses automated notifications to keep the team informed about important re **Channel:** Google Chat room (same as zsoftly-services) **Message format:** Google Chat App Card with: + - **Header:** "New Pull Request" with GitHub avatar - **Key-Value sections:** PR title, author, repository, PR number - **Action buttons:** "🔍 Review PR" and "📁 View Files" - **Professional styling** with icons and structured layout -**Purpose:** +**Purpose:** + - Keep team aware of new contributions - Enable quick review and collaboration - Maintain visibility into main branch changes @@ -30,12 +32,14 @@ ZTiAWS uses automated notifications to keep the team informed about important re **Channel:** Google Chat room (same as zsoftly-services) **Message format:** Google Chat App Card with: + - **Header:** "🚀 New Release Available" with GitHub avatar - **Key-Value sections:** Version number, repository, deployment status - **Action buttons:** "📋 View Release", "⬇️ Download", "📝 Changelog" - **Professional styling** with icons and structured layout **Purpose:** + - Announce new releases to stakeholders - Provide direct access to release notes and binaries - Coordinate deployment activities @@ -56,12 +60,14 @@ The notification system uses **Google Chat App Cards** instead of simple text me ### App Card Structure **PR Notifications:** + - Header with "New Pull Request" title and GitHub avatar - Structured sections showing PR details (title, author, repository, number) - Action buttons for reviewing PR and viewing changed files **Release Notifications:** -- Header with "New Release Available" title and GitHub avatar + +- Header with "New Release Available" title and GitHub avatar - Structured sections showing release details (version, status) - Action buttons for viewing release, downloading, and checking changelog @@ -91,8 +97,9 @@ The notification system uses **embedded notifications** architecture in the main ### Shell Script Benefits **Why Shell Scripts Over Inline Curl:** + - **🎨 Rich Google Chat App Cards** with embedded styling -- **🔧 Maintainable code** separated from workflow logic +- **🔧 Maintainable code** separated from workflow logic - **🛡️ Enhanced security** with base64 webhook encoding - **📝 Comprehensive logging** with colored output and debug mode - **⚡ Reusable components** for different notification types @@ -102,12 +109,14 @@ The notification system uses **embedded notifications** architecture in the main ### Configuration **Webhook Secret:** `GOOGLE_CHAT_WEBHOOK` + - Stored in GitHub repository secrets - Can be base64 encoded for enhanced security (following zsoftly-services pattern) - Shared across ZSoftly repositories for consistency - Points to team's Google Chat room **Embedded Notification Conditions:** + ```yaml # Test Failure Notifications (immediate) if: failure() && matrix.os == 'ubuntu-latest' && github.event_name == 'pull_request' @@ -115,7 +124,7 @@ if: failure() && matrix.os == 'ubuntu-latest' && github.event_name == 'pull_requ # Success Notification (final, from security job) if: success() && github.event_name == 'pull_request' -# Release Notification +# Release Notification if: startsWith(github.ref, 'refs/tags/v') ``` @@ -124,18 +133,21 @@ if: startsWith(github.ref, 'refs/tags/v') ### **Why Embedded vs Centralized?** **Previous Design Issues:** + - Centralized `notify` job created dependency chain failures - No notifications if any prerequisite job was skipped - Single point of failure blocked all notifications - Only worked for PRs targeting `main` branch **New Embedded Design Benefits:** + - **Fail-fast**: Immediate notifications when tests fail -- **Independent**: Each job notifies without dependencies +- **Independent**: Each job notifies without dependencies - **Reliable**: Cannot be blocked by other job failures - **Comprehensive**: Works for all PRs, any branch target ### **Implementation Pattern:** + ```yaml - name: Notify on test failure if: failure() && matrix.os == 'ubuntu-latest' && github.event_name == 'pull_request' @@ -151,6 +163,7 @@ if: startsWith(github.ref, 'refs/tags/v') ## Notification Flow ### Pull Request Flow + 1. Developer opens PR (to any branch) 2. CI/CD triggers: `test-shell` + `test-go` jobs run in parallel 3. **Immediate failure notifications**: If any test fails, notification sent immediately @@ -159,6 +172,7 @@ if: startsWith(github.ref, 'refs/tags/v') 6. **Fail-fast feedback**: Team gets alerts as soon as issues occur ### Release Flow + 1. Maintainer pushes version tag (e.g., `git push origin v1.0.0`) 2. CI/CD triggers: `build` → `release` → `release-notification` 3. After successful release creation, notification is sent @@ -167,6 +181,7 @@ if: startsWith(github.ref, 'refs/tags/v') ## Benefits ### For Development Team + - **Fail-fast notifications** - Immediate alerts when any test fails (no waiting for all jobs) - **Single success notification** - Clean, consolidated "✅" message when all tests pass - **No dependency chain failures** - Each job notifies independently @@ -176,6 +191,7 @@ if: startsWith(github.ref, 'refs/tags/v') - **Integration with existing workflows** (same chat room as zsoftly-services) ### For Project Management + - **Visibility into development velocity** (PR frequency) - **Release tracking** with direct links to release notes - **Stakeholder communication** through automated announcements @@ -185,6 +201,7 @@ if: startsWith(github.ref, 'refs/tags/v') ### Notifications Not Received 1. **Check workflow execution:** + ```bash # View recent workflow runs gh run list --workflow=build.yml --limit=5 @@ -194,11 +211,13 @@ gh run view [RUN_ID] --log ``` 2. **Verify webhook configuration:** + - Ensure `GOOGLE_CHAT_WEBHOOK` secret is configured - Verify webhook URL is active and accessible - Check Google Chat room permissions 3. **Check job conditions:** + - PR notifications only trigger for PRs to `main` branch - Release notifications only trigger for version tags starting with `v` - Jobs must complete successfully in correct order @@ -206,12 +225,14 @@ gh run view [RUN_ID] --log ### Missing Notifications **Common causes:** + - Previous job failed (notifications won't run) - Incorrect branch target (feature PRs don't trigger notifications) - Tag format doesn't match `v*` pattern - Webhook secret misconfigured **Debugging steps:** + 1. Check GitHub Actions logs for failed jobs 2. Verify PR target branch is `main` 3. Confirm tag format: `v1.0.0` (not `1.0.0` or `release-1.0.0`) @@ -220,12 +241,14 @@ gh run view [RUN_ID] --log ### Testing Notifications **Test PR notification:** + 1. Create a test branch 2. Open PR to `main` branch 3. Wait for security scans to complete 4. Check Google Chat for notification **Test release notification:** + 1. Create a test tag: `git tag v0.0.0-test` 2. Push tag: `git push origin v0.0.0-test` 3. Wait for release creation @@ -248,4 +271,4 @@ gh run view [RUN_ID] --log --- -**Note:** This notification system follows the same pattern as the zsoftly-services repository to maintain consistency across ZSoftly projects. \ No newline at end of file +**Note:** This notification system follows the same pattern as the zsoftly-services repository to maintain consistency across ZSoftly projects. diff --git a/docs/PRODUCTION_VERSION_FIX.md b/docs/PRODUCTION_VERSION_FIX.md index 8ed0819..5d288db 100644 --- a/docs/PRODUCTION_VERSION_FIX.md +++ b/docs/PRODUCTION_VERSION_FIX.md @@ -1,7 +1,9 @@ # Version Check Production Fix ## Problem + Version check failed silently in production due to: + - No proxy support (corporate firewalls block api.github.com) - No TLS certificate handling (corporate proxies with custom CAs) - GitHub rate limiting (60/hour unauthenticated) @@ -9,7 +11,9 @@ Version check failed silently in production due to: - Silent error suppression ## Solution + Production-ready HTTP client with: + - `http.ProxyFromEnvironment` - respects standard proxy vars - TLS 1.2+ with optional InsecureSkipVerify for corporate CAs - GitHub token auth (5000/hour rate limit) @@ -18,10 +22,12 @@ Production-ready HTTP client with: - Debug mode for troubleshooting ## Files Modified + - `ztictl/pkg/version/checker.go` - Added createHTTPClient() with production features - `docs/VERSION_CHECKING.md` - Environment variable reference ## Environment Variables + - `ZTICTL_SKIP_VERSION_CHECK` - disable check - `ZTICTL_DEBUG` - show errors - `ZTICTL_INSECURE_SKIP_VERIFY` - skip TLS verification (corporate CAs only) diff --git a/docs/QUICK_START.md b/docs/QUICK_START.md index 4ac0f90..7dc12cd 100644 --- a/docs/QUICK_START.md +++ b/docs/QUICK_START.md @@ -5,6 +5,7 @@ > **Choose Your Version**: ZTiAWS offers both stable shell scripts and a new unified Go binary ### 🐚 Shell Scripts (Production Stable) + ```bash # Download production-stable shell scripts curl -L -o ssm https://raw.githubusercontent.com/zsoftly/ztiaws/main/ssm @@ -23,6 +24,7 @@ done ### 🚀 Go Binary (New Unified Tool) #### Linux/macOS + ```bash # Download and install latest ztictl platform="linux" # or "darwin" for macOS @@ -36,6 +38,7 @@ rm ztictl.tar.gz ``` #### Windows (PowerShell) + ```powershell # Download and install latest ztictl Invoke-WebRequest -Uri "https://github.com/zsoftly/ztiaws/releases/latest/download/ztictl-windows-amd64.zip" -OutFile "ztictl.zip" @@ -47,12 +50,13 @@ Remove-Item ztictl.zip ``` ### Verify Installation + ```bash # Shell scripts ssm --version authaws --version -# Go binary +# Go binary ztictl --version # Test functionality @@ -63,6 +67,7 @@ ztictl ssm list --region us-east-1 # Go binary ## 📦 Quick Release Process ### 1. Create Tag and Release + ```bash # Update version and create tag git tag -a v1.1.0 -m "Release v1.1.0" @@ -70,12 +75,14 @@ git push origin v1.1.0 ``` ### 2. GitHub Actions Automatically: + - ✅ Builds for all platforms (Linux, macOS, Windows) -- ✅ Runs tests and quality checks +- ✅ Runs tests and quality checks - ✅ Creates GitHub release with binaries - ✅ Makes binaries available for download ### 3. Verify Release + ```bash # Check build status gh run list --workflow=build.yml @@ -85,6 +92,7 @@ curl -I https://github.com/zsoftly/ztiaws/releases/latest/download/ztictl-linux- ``` ## 📚 Full Documentation + - **Installation Guide**: [INSTALLATION.md](../INSTALLATION.md) - **Release Process**: [RELEASE.md](development/RELEASE.md) - **User Guide**: [README.md](../README.md) diff --git a/docs/REGIONS.md b/docs/REGIONS.md index 810587b..5d2841a 100644 --- a/docs/REGIONS.md +++ b/docs/REGIONS.md @@ -4,13 +4,13 @@ This document lists all supported AWS regions in quickssm. ## Currently Supported Regions -| Shortcode | AWS Region | Location | Status | -|-----------|---------------|------------------|--------| -| cac1 | ca-central-1 | Montreal | Active | -| caw1 | ca-west-1 | Calgary | Active | -| use1 | us-east-1 | N. Virginia | Active | -| usw1 | us-west-1 | N. California | Active | -| euw1 | eu-west-1 | Ireland | Active | +| Shortcode | AWS Region | Location | Status | +| --------- | ------------ | ------------- | ------ | +| cac1 | ca-central-1 | Montreal | Active | +| caw1 | ca-west-1 | Calgary | Active | +| use1 | us-east-1 | N. Virginia | Active | +| usw1 | us-west-1 | N. California | Active | +| euw1 | eu-west-1 | Ireland | Active | ## Adding New Regions @@ -25,10 +25,12 @@ Please see [CONTRIBUTING.md](../CONTRIBUTING.md) for instructions on how to add ## Region Naming Convention Shortcodes follow this pattern: + - First 3 characters: Geographic area (e.g., 'use' for US East) - Last character: Region number Examples: + - `use1` = US East 1 - `cac1` = Canada Central 1 -- `euw1` = EU West 1 \ No newline at end of file +- `euw1` = EU West 1 diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index c66a64b..f163192 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -3,32 +3,41 @@ This document provides solutions to common issues encountered while using ZTiAWS. ### AWS CLI Not Found + If AWS CLI is not installed, follow the [official AWS CLI installation guide](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html). ### Session Manager Plugin Missing + Run `ssm check` to install the plugin interactively, or follow the [manual installation instructions](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html). ### Missing jq or fzf + For Ubuntu/Debian: `sudo apt-get install jq fzf` For macOS: `brew install jq fzf` ### AWS Credentials Not Configured + Run `aws configure` to set up your AWS credentials. ### Permission Errors + Ensure your AWS user/role has the required IAM permissions. Refer to [IAM_PERMISSIONS.md](IAM_PERMISSIONS.md) for details. ### Shell Configuration + If the commands aren't available after installation, make sure you've added them to your PATH in the correct shell configuration file: + - For Bash users: `~/.bashrc` - For Zsh users: `~/.zshrc` You may need to restart your terminal or run `source ~/.bashrc` (or `source ~/.zshrc` for Zsh) for the changes to take effect. ### Script Name Changed + If you're getting "command not found" for `auth_aws`, note that the script has been renamed to `authaws` in v1.4.0+. Update your scripts and aliases accordingly. ### "Failed" Status with `systemctl status` Commands + When using `ssm exec` with `systemctl status` commands, you may see a "Failed" status even though the command executed successfully. This occurs because `systemctl status` returns exit code `3` when a service is stopped/inactive, which AWS SSM interprets as a failure. To force a success status regardless of the service state, append `; exit 0` to your command: diff --git a/docs/VERSION_CHECKING.md b/docs/VERSION_CHECKING.md index 8acf968..4305c97 100644 --- a/docs/VERSION_CHECKING.md +++ b/docs/VERSION_CHECKING.md @@ -7,27 +7,32 @@ Version checker uses production-ready HTTP client to check GitHub releases API. ### Design Decisions **Proxy Support** + - `http.ProxyFromEnvironment` respects standard HTTP_PROXY, HTTPS_PROXY, NO_PROXY - Custom override via ZTICTL_HTTPS_PROXY for tool-specific proxy configuration - Rationale: Corporate environments require proxy support without modifying system-wide settings **TLS Configuration** + - Minimum TLS 1.2 - Optional InsecureSkipVerify for corporate proxy with custom certificates - Rationale: Balance security with corporate environment compatibility **Retry Logic** + - 3 attempts with exponential backoff (1s base delay) - Skip retry on 4xx client errors - Rationale: Transient network failures common in production; client errors are permanent **Rate Limiting** + - Unauthenticated: 60 requests/hour (GitHub limit) - With GITHUB_TOKEN: 5000 requests/hour - 24-hour cache to minimize API calls - Rationale: Prevent rate limit exhaustion in CI/CD environments **Error Handling** + - Silent by default (non-blocking) - Debug mode (ZTICTL_DEBUG) shows detailed errors - Disable option (ZTICTL_SKIP_VERSION_CHECK) for air-gapped environments @@ -35,14 +40,14 @@ Version checker uses production-ready HTTP client to check GitHub releases API. ## Environment Variables -| Variable | Purpose | -|----------|---------| -| ZTICTL_SKIP_VERSION_CHECK | Disable version checking | -| ZTICTL_DEBUG | Show detailed errors | -| ZTICTL_VERBOSE_VERSION | Show success messages | -| ZTICTL_HTTPS_PROXY | Custom proxy override | -| ZTICTL_INSECURE_SKIP_VERIFY | Skip TLS verification | -| GITHUB_TOKEN | GitHub API authentication | +| Variable | Purpose | +| --------------------------- | ------------------------- | +| ZTICTL_SKIP_VERSION_CHECK | Disable version checking | +| ZTICTL_DEBUG | Show detailed errors | +| ZTICTL_VERBOSE_VERSION | Show success messages | +| ZTICTL_HTTPS_PROXY | Custom proxy override | +| ZTICTL_INSECURE_SKIP_VERIFY | Skip TLS verification | +| GITHUB_TOKEN | GitHub API authentication | ## Implementation diff --git a/docs/development/BUILD_ARTIFACTS.md b/docs/development/BUILD_ARTIFACTS.md index f1fc8d8..7a7ffb4 100644 --- a/docs/development/BUILD_ARTIFACTS.md +++ b/docs/development/BUILD_ARTIFACTS.md @@ -5,13 +5,15 @@ The following build artifacts should **NEVER** be committed to source control: ### Local Builds + - `ztictl/ztictl` - Local binary for current platform - `ztictl/ztictl.exe` - Windows binary - `ztictl/builds/` - Directory containing all cross-platform binaries ### Cross-Platform Builds + - `ztictl-linux-amd64` -- `ztictl-linux-arm64` +- `ztictl-linux-arm64` - `ztictl-darwin-amd64` - `ztictl-darwin-arm64` - `ztictl-windows-amd64.exe` @@ -22,6 +24,7 @@ The following build artifacts should **NEVER** be committed to source control: These files should be committed to help others build the project: ### Build Scripts & Configuration + - `ztictl/Makefile` - Build automation - `ztictl/build.sh` - Cross-platform build script - `ztictl/go.mod` & `ztictl/go.sum` - Go dependencies @@ -44,6 +47,7 @@ The `.gitignore` file automatically excludes build artifacts: ## 🎯 **Why This Matters** ### **Problems with Committing Binaries:** + - **Repository bloat**: Binaries are large (20-30MB each) - **Platform conflicts**: Different OS/architecture binaries - **Version confusion**: Outdated binaries vs current code @@ -51,6 +55,7 @@ The `.gitignore` file automatically excludes build artifacts: - **Security risks**: Executables in source control ### **Benefits of Ignoring:** + - **Clean repository**: Only source code tracked - **Platform independence**: Each developer builds for their system - **Always current**: Binaries match current code state @@ -59,6 +64,7 @@ The `.gitignore` file automatically excludes build artifacts: ## 🔄 **Recommended Workflow** ### During Development + ```bash # Build locally for testing make build-local @@ -74,6 +80,7 @@ git commit -m "Your changes" ``` ### For Releases + ```bash # Tag triggers automated builds git tag v1.1.0 @@ -103,6 +110,6 @@ git status --ignored ## ✨ **Summary** - **Source code**: Always commit ✅ -- **Build scripts**: Always commit ✅ +- **Build scripts**: Always commit ✅ - **Binaries**: Never commit ❌ - **Let CI/CD handle distribution** 🚀 diff --git a/docs/development/RELEASE.md b/docs/development/RELEASE.md index 93da279..71c883f 100644 --- a/docs/development/RELEASE.md +++ b/docs/development/RELEASE.md @@ -7,6 +7,7 @@ Simple release process for CLI tools where users control their updates. ## Release Process ### 1. Create Release Branch + ```bash # Create and push release branch from latest main git checkout main @@ -16,6 +17,7 @@ git push -u origin release/v ``` ### 2. Wait for Auto-Generated Documentation + - GitHub Actions automatically detects new `release/*` branch - Runs documentation generator script - Creates unified CHANGELOG.md and RELEASE_NOTES.txt @@ -23,6 +25,7 @@ git push -u origin release/v - **Wait for this to complete before next step!** ### 3. Pull Auto-Generated Changes + ```bash # Pull the auto-generated documentation git pull origin release/v @@ -33,6 +36,7 @@ cat RELEASE_NOTES.txt ``` ### 4. Create Release Tag + ```bash # Create and push tag - this triggers the build and release git tag v @@ -40,6 +44,7 @@ git push origin v ``` ### 5. Merge Back to Main + ```bash # After release is successful, merge back to main git checkout main @@ -51,17 +56,20 @@ git branch -d release/v ## Why This Process is Better ### For CLI Tools Specifically + - **Users control updates**: No forced upgrades, users choose when to update - **No hotfixes needed**: Critical issues can wait for next planned release - **Simpler maintenance**: Less overhead for a tool that doesn't break user workflows ### Automation Benefits + - **Consistent formatting**: Automated changelog generation - **Reduced errors**: No manual version number management - **Faster releases**: Less manual work, more frequent releases possible - **Git-based**: Changelog reflects actual development history ### Process Benefits + - **Clear separation**: Preparation vs. release vs. merge back - **Review opportunity**: Generated content can be reviewed and edited - **Rollback friendly**: Release branch can be abandoned if needed @@ -81,6 +89,7 @@ git branch -d release/v - [ ] Clean up release branch ### Emergency Release (Rare) + - [ ] Create hotfix branch from latest release tag - [ ] Apply minimal fix and test - [ ] Follow standard release process from hotfix branch @@ -89,6 +98,7 @@ git branch -d release/v ## Troubleshooting Releases ### Build Failures + ```bash # Check workflow logs gh run view --log @@ -99,11 +109,13 @@ make build ``` ### Missing Binaries + - Check GitHub Actions workflow completion - Verify all platforms built successfully - Check artifact upload step ### Version Issues + - Ensure tag format matches `v*` pattern - Verify version embedding in binary - Check go.mod version compatibility diff --git a/docs/examples/github-actions-oidc.yml b/docs/examples/github-actions-oidc.yml index 2f50081..7a03bb4 100644 --- a/docs/examples/github-actions-oidc.yml +++ b/docs/examples/github-actions-oidc.yml @@ -42,8 +42,8 @@ on: # Required for OIDC authentication permissions: - id-token: write # Required to request OIDC token - contents: read # Required to checkout code + id-token: write # Required to request OIDC token + contents: read # Required to checkout code env: AWS_REGION: ca-central-1 @@ -98,7 +98,7 @@ jobs: ztictl config init --non-interactive env: ZTICTL_DEFAULT_REGION: ${{ env.AWS_REGION }} - ZTICTL_LOG_ENABLED: "false" + ZTICTL_LOG_ENABLED: 'false' # Verify configuration - name: Verify ztictl configuration diff --git a/docs/examples/gitlab-ci-oidc.yml b/docs/examples/gitlab-ci-oidc.yml index 2bee259..ddf481b 100644 --- a/docs/examples/gitlab-ci-oidc.yml +++ b/docs/examples/gitlab-ci-oidc.yml @@ -36,8 +36,8 @@ variables: AWS_REGION: ca-central-1 ZTICTL_DEFAULT_REGION: ca-central-1 - ZTICTL_NON_INTERACTIVE: "true" - ZTICTL_LOG_ENABLED: "false" + ZTICTL_NON_INTERACTIVE: 'true' + ZTICTL_LOG_ENABLED: 'false' stages: - setup diff --git a/docs/ztiaws-demo.md b/docs/ztiaws-demo.md index dd91fe2..607c6e2 100644 --- a/docs/ztiaws-demo.md +++ b/docs/ztiaws-demo.md @@ -1,77 +1,73 @@ -# ZTiAWS Product Demo — Simplifying AWS for Modern Teams -**Prepared by:** Modupe Ilesanmi +# ZTiAWS Product Demo — Simplifying AWS for Modern Teams + +**Prepared by:** Modupe Ilesanmi **Product:** ZTiAWS (ZSoftly Tools for AWS) -**Duration:** ~45 minutes +**Duration:** ~45 minutes --- -## Executive Summary +## Executive Summary Modern cloud teams spend too much time managing AWS instead of innovating on it. -**ZTiAWS (ZSoftly Tools for AWS)** was built to change that. +**ZTiAWS (ZSoftly Tools for AWS)** was built to change that. -It’s an open-source, automation-focused CLI suite that helps engineers and DevOps teams **manage AWS faster, more securely, and with fewer errors.** +It’s an open-source, automation-focused CLI suite that helps engineers and DevOps teams **manage AWS faster, more securely, and with fewer errors.** With ZTiAWS, repetitive tasks like starting SSM sessions, transferring files, or managing instances across multiple regions are reduced to simple one-line operations — improving **team productivity, security compliance, and developer experience.** -> “ZTiAWS empowers teams to focus on outcomes, not syntax.” — *ZSoftly Engineering Team* - +> “ZTiAWS empowers teams to focus on outcomes, not syntax.” — _ZSoftly Engineering Team_ --- +## Why ZTiAWS Matters -## Why ZTiAWS Matters - -| Challenge | Traditional Approach | With ZTiAWS | -|------------|----------------------|--------------| -| Complex AWS CLI syntax | Long commands and manual lookups | One-liners with smart defaults | -| Inefficient multi-region ops | Custom scripts and loops | Tag-based parallel execution | -| Security management | Manual key handling | Built-in CLI/SSO integration | -| Developer onboarding | High learning curve | Interactive guided commands | -| Time-to-action | Minutes per task | Seconds — start to finish | - +| Challenge | Traditional Approach | With ZTiAWS | +| ---------------------------- | -------------------------------- | ------------------------------ | +| Complex AWS CLI syntax | Long commands and manual lookups | One-liners with smart defaults | +| Inefficient multi-region ops | Custom scripts and loops | Tag-based parallel execution | +| Security management | Manual key handling | Built-in CLI/SSO integration | +| Developer onboarding | High learning curve | Interactive guided commands | +| Time-to-action | Minutes per task | Seconds — start to finish | **In short:** -ZTiAWS helps organizations **reduce cloud operation time**, **enforce secure access by default**, and **improve the developer experience** — without requiring deep CLI expertise. - +ZTiAWS helps organizations **reduce cloud operation time**, **enforce secure access by default**, and **improve the developer experience** — without requiring deep CLI expertise. --- +## Overview -## Overview +ZTiAWS (ZSoftly Tools for AWS) is a suite of open-source CLI tools that simplify AWS management through automation, smart defaults, and a modern user experience. -ZTiAWS (ZSoftly Tools for AWS) is a suite of open-source CLI tools that simplify AWS management through automation, smart defaults, and a modern user experience. - -> “Life’s too short for long AWS commands.” — *ZSoftly Team* +> “Life’s too short for long AWS commands.” — _ZSoftly Team_ This demo introduces **ZTiAWS end-to-end**, covering: -- Installation (Linux and Windows) -- Configuration & Authentication (AWS CLI or SSO) -- Practical use cases using the `ztictl` CLI tool -- Summary, benefits, and business value + +- Installation (Linux and Windows) +- Configuration & Authentication (AWS CLI or SSO) +- Practical use cases using the `ztictl` CLI tool +- Summary, benefits, and business value --- -## Demo Agenda +## Demo Agenda -| Segment | Duration | Focus | -|----------|-----------|--------| -| 1. Introduction | 5 min | What ZTiAWS is and why it matters | -| 2. Installation | 10 min | Setup on Linux and Windows | -| 3. Authentication | 10 min | AWS CLI/SSO configuration | -| 4. Use Cases | 20 min | Real-world operations and automation | -| 5. Wrap-Up | 5 min | Key benefits and adoption message | +| Segment | Duration | Focus | +| ----------------- | -------- | ------------------------------------ | +| 1. Introduction | 5 min | What ZTiAWS is and why it matters | +| 2. Installation | 10 min | Setup on Linux and Windows | +| 3. Authentication | 10 min | AWS CLI/SSO configuration | +| 4. Use Cases | 20 min | Real-world operations and automation | +| 5. Wrap-Up | 5 min | Key benefits and adoption message | --- - -## 1. Introduction +## 1. Introduction ZTiAWS was built to make AWS management **faster, safer, and simpler.** It reduces the friction of using the AWS CLI by abstracting complex commands into clear, human-friendly operations. -**Example:** +**Example:** ```bash # Traditional AWS CLI @@ -81,19 +77,16 @@ aws ssm start-session --target i-1234567890abcdef0 ztictl ssm connect i-1234567890abcdef0 ``` - Key Features -- Cross-platform: Native binaries for Linux, macOS, and Windows (AMD64/ARM64) -- Interactive fuzzy finder: Real-time instance selection with keyboard navigation +- Cross-platform: Native binaries for Linux, macOS, and Windows (AMD64/ARM64) +- Interactive fuzzy finder: Real-time instance selection with keyboard navigation - Secure AWS SSO authentication: Built-in session caching for streamlined logins -- Smart operations: OS detection and automatic command adaptation -- S3-backed file transfers: Integrated lifecycle management for uploads and downloads -- Tag-based automation: Multi-instance and multi-region control through AWS tags -- Professional logging: Thread-safe, structured, and easily filterable logs -- Modern CLI: Clean flag-based syntax with built-in validation and help support - - +- Smart operations: OS detection and automatic command adaptation +- S3-backed file transfers: Integrated lifecycle management for uploads and downloads +- Tag-based automation: Multi-instance and multi-region control through AWS tags +- Professional logging: Thread-safe, structured, and easily filterable logs +- Modern CLI: Clean flag-based syntax with built-in validation and help support ## 2. Installation & Setup @@ -106,6 +99,7 @@ curl -L -o /tmp/ztictl \ ``` Windows + ```powershell Invoke-WebRequest -Uri "https://github.com/zsoftly/ztiaws/releases/latest/download/ztictl-windows-amd64.exe" -OutFile "$env:TEMP\ztictl.exe" New-Item -ItemType Directory -Force "$env:USERPROFILE\Tools" | Out-Null @@ -114,10 +108,7 @@ Move-Item "$env:TEMP\ztictl.exe" "$env:USERPROFILE\Tools\ztictl.exe" ztictl --version ``` - “Installation is one command across all platforms — no dependency hell, no setup pain.” - - - +“Installation is one command across all platforms — no dependency hell, no setup pain.” ## 3. Configuration & Authentication @@ -140,8 +131,7 @@ Checks required components (AWS CLI, SSM plugin) Prompts for SSO login with interactive account/role selection Stores temporary credentials securely - “Unlike the AWS CLI, ZTiAWS provides a guided SSO flow that securely manages temporary credentials and IAM role selection.” - +“Unlike the AWS CLI, ZTiAWS provides a guided SSO flow that securely manages temporary credentials and IAM role selection.” ## 4. Use Cases — Demonstration Scenarios @@ -159,22 +149,25 @@ Auto-detects OS (Linux vs Windows) Benefit: Fast, secure access with zero SSH key management. - ### Use Case 2: Execute Cross-Platform Commands + ### Linux Instance + ```bash ztictl ssm exec ca-central-1 i-linux123 "uname -a" ``` + ### Windows Instance -``` powershell + +```powershell ztictl ssm exec ca-central-1 i-windows456 "Get-ComputerInfo" ``` Benefit: Runs OS-specific commands automatically using Bash or PowerShell — no manual detection needed. - ### Use Case 3: Multi-Region and Tag-Based Operations + ```bash ztictl ssm exec-tagged us-east-1 --tags Environment=prod,Role=web "df -h" ztictl ssm exec-multi ca-central-1,us-east-1,eu-west-1 --tags "App=web" "uptime" @@ -183,15 +176,16 @@ ztictl ssm exec-multi ca-central-1,us-east-1,eu-west-1 --tags "App=web" "uptime" Benefit: Execute parallel commands across multiple regions and tagged instances with a single command. - ### Use Case 4: Smart File Transfers ### Upload to remote instance + ```bash ztictl ssm transfer upload i-linux123 ./config.txt /etc/app/config.txt ``` ### Download logs + ```bash ztictl ssm transfer download i-windows456 C:\logs\sys.log ./sys.log ``` @@ -199,8 +193,8 @@ ztictl ssm transfer download i-windows456 C:\logs\sys.log ./sys.log Benefit: Automatic S3 routing for large files with secure lifecycle cleanup. - ### Use Case 5: Instance Power Management + ```bash ztictl ssm start-tagged --tags "AutoStart=true" --region euw1 ztictl ssm stop-tagged --tags "Environment=dev" --region cac1 @@ -211,104 +205,84 @@ Start or stop multiple EC2 instances by tag or environment from a single termina --- - ## Demo Walkthrough (ZTiAWS Setup & Validation) Below are the step-by-step screenshots showing installation, configuration, and usage. - - ### 1. Installing ZTiAWS CLI (ztictl) -![Installing ztictl](./images/01-installing-ztictl.png) - +![Installing ztictl](./images/01-installing-ztictl.png) ### 2. Initialize Configuration -![Initialize Configuration](./images/02-initialize-configuration.png) - - +![Initialize Configuration](./images/02-initialize-configuration.png) ### 3. Configuration Verification -![Configuration](./images/03-configuration.png) - +![Configuration](./images/03-configuration.png) ### 4. Confirm SSM Connection to EC2 -![Confirm SSM EC2](./images/04-confirm-ssm-ec2.png) - +![Confirm SSM EC2](./images/04-confirm-ssm-ec2.png) ### 5. List EC2 Instances -![List Instances](./images/05-list-instances.png) - +![List Instances](./images/05-list-instances.png) ### 6. AWS Console — SSM Managed Instance -![SSM Managed Instance](./images/06-ssm-with-managed-instance.png) - +![SSM Managed Instance](./images/06-ssm-with-managed-instance.png) ### 7. ZTiAWS Connect to EC2 -![ztictl SSM Connect](./images/07-ztictl-ssm-connect-ec2.png) +![ztictl SSM Connect](./images/07-ztictl-ssm-connect-ec2.png) ### 8. Using Connect and Exec Commands -![Connect and Exec](./images/08-connect-and-exec-commands.png) - +![Connect and Exec](./images/08-connect-and-exec-commands.png) ### 9. Executing Commands Remotely -![Exec Command](./images/09-exec-command.png) - +![Exec Command](./images/09-exec-command.png) ### 10. Linux Commands Output -![Linux Commands](./images/10-linux-commands.png) - +![Linux Commands](./images/10-linux-commands.png) ### 11. Creating Folder and File -![Creating Folder and File](./images/11-creating-folder-file.png) - +![Creating Folder and File](./images/11-creating-folder-file.png) ### 12. Uploading Local File to EC2 -![Upload Local File](./images/12-uploading-local-to-ec2-file.png) - - - +![Upload Local File](./images/12-uploading-local-to-ec2-file.png) --- - Business Value & Impact + +Business Value & Impact ZTiAWS isn’t just a CLI tool — it’s a productivity multiplier for DevOps and cloud engineering teams. By simplifying AWS operations through automation and smart defaults, ZTiAWS helps organizations: -- **Reduce cloud operation time** — connect, execute, and transfer in seconds. -- **Focus on automation, not syntax** — less time memorizing CLI commands, more time building. -- **Enforce secure access by design** — integrates with AWS CLI or SSO, reducing IAM key exposure. -- **Manage multi-account environments easily** — handle multiple regions and infrastructures via tags. -- **Accelerate onboarding for new engineers** — interact with AWS safely without deep CLI experience. - +- **Reduce cloud operation time** — connect, execute, and transfer in seconds. +- **Focus on automation, not syntax** — less time memorizing CLI commands, more time building. +- **Enforce secure access by design** — integrates with AWS CLI or SSO, reducing IAM key exposure. +- **Manage multi-account environments easily** — handle multiple regions and infrastructures via tags. +- **Accelerate onboarding for new engineers** — interact with AWS safely without deep CLI experience. Adoption Insight: Used internally at ZSoftly by engineering teams managing multi-account AWS environments — proving its reliability and real-world value. - - Summary of Benefits -| Feature | Traditional AWS CLI | With ZTiAWS | +| Feature | Traditional AWS CLI | With ZTiAWS | | ---------------- | ------------------- | ----------------------------- | -| Authentication | Manual SSO setup | Guided or auto-detected login | -| Instance Access | Long IDs, SSH | Interactive fuzzy finder | -| OS Detection | Manual scripts | Auto-detect and adapt | -| Multi-Region Ops | Loops & scripts | Single command | -| File Transfers | Manual S3 upload | Smart routing with S3 | -| Power Control | Console or SDK | Tag-based automation | - +| Authentication | Manual SSO setup | Guided or auto-detected login | +| Instance Access | Long IDs, SSH | Interactive fuzzy finder | +| OS Detection | Manual scripts | Auto-detect and adapt | +| Multi-Region Ops | Loops & scripts | Single command | +| File Transfers | Manual S3 upload | Smart routing with S3 | +| Power Control | Console or SDK | Tag-based automation | --- @@ -333,11 +307,10 @@ Repository: https://github.com/zsoftly/ztiaws --- ## Recommended Next Steps + 1. Clone the repository. 2. Follow this demo to install and test ZTiAWS locally. 3. Share feedback via the #ztiaws engineering channel. 4. (Optional) Contribute new regions or commands via PR. - - - End of Document +End of Document diff --git a/scripts/README.md b/scripts/README.md index 609fe4c..3427109 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -7,13 +7,14 @@ Automation scripts for the ZTiAWS project, focusing on **Google Chat notificatio These scripts send **professional, interactive notifications** instead of plain text because: - **Rich visual formatting** with headers, icons, and structured sections -- **Interactive elements** like clickable buttons for direct actions +- **Interactive elements** like clickable buttons for direct actions - **Enterprise-grade appearance** that matches professional tools - **Quick access** to related resources without copy/paste ## Notification Scripts ### `send-pr-notification.sh` + **Purpose:** Notify team when PRs are opened to the main branch **Triggered:** After security scans complete in CI/CD **Benefits:** Enables quick review and collaboration @@ -28,7 +29,8 @@ These scripts send **professional, interactive notifications** instead of plain --repository "org/repo" ``` -### `send-release-notification.sh` +### `send-release-notification.sh` + **Purpose:** Announce new releases to stakeholders **Triggered:** After GitHub releases are created **Benefits:** Coordinates deployment activities and provides direct access to release assets @@ -53,4 +55,4 @@ These scripts send **professional, interactive notifications** instead of plain Set `GOOGLE_CHAT_WEBHOOK` in repository secrets (supports plain text or base64 encoding). -For implementation details, see individual script help: `script-name.sh --help` \ No newline at end of file +For implementation details, see individual script help: `script-name.sh --help` diff --git a/scripts/install.ps1 b/scripts/install.ps1 new file mode 100644 index 0000000..e46980d --- /dev/null +++ b/scripts/install.ps1 @@ -0,0 +1,80 @@ +<# +.SYNOPSIS + One-liner installer for ztictl on Windows. + +.DESCRIPTION + Downloads and installs ztictl to %USERPROFILE%\Tools and adds to PATH. + +.EXAMPLE + irm https://github.com/zsoftly/ztiaws/releases/latest/download/install.ps1 | iex +#> + +# PSScriptAnalyzer: Write-Host is intentional for colored installer output +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '')] +param() + +$ErrorActionPreference = "Stop" + +$repo = "zsoftly/ztiaws" +$installDir = "$env:USERPROFILE\Tools" +$binaryName = "ztictl.exe" + +# Detect architecture (64-bit only) +if (-not [Environment]::Is64BitOperatingSystem) { + Write-Host " [ERROR] 32-bit Windows is not supported" -ForegroundColor Red + exit 1 +} + +$arch = if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") { "arm64" } else { "amd64" } + +$assetName = "ztictl-windows-$arch.exe" +$downloadUrl = "https://github.com/$repo/releases/latest/download/$assetName" + +Write-Host "" +Write-Host " ztictl installer" -ForegroundColor Cyan +Write-Host " ----------------" -ForegroundColor Cyan +Write-Host "" + +# Create install directory +if (-not (Test-Path $installDir)) { + Write-Host " Creating $installDir..." -ForegroundColor Gray + New-Item -ItemType Directory -Force $installDir | Out-Null +} + +# Download binary +$destPath = Join-Path $installDir $binaryName +Write-Host " Downloading $assetName..." -ForegroundColor Gray + +try { + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + Invoke-WebRequest -Uri $downloadUrl -OutFile $destPath -UseBasicParsing +} catch { + Write-Host " [ERROR] Download failed: $_" -ForegroundColor Red + exit 1 +} + +# Add to PATH if not already there (persists across sessions) +$userPath = [Environment]::GetEnvironmentVariable("PATH", "User") +if ($userPath -notlike "*$installDir*") { + Write-Host " Adding to PATH..." -ForegroundColor Gray + [Environment]::SetEnvironmentVariable("PATH", "$userPath;$installDir", "User") +} + +# Verify +try { + $version = & $destPath --version 2>&1 + Write-Host "" + Write-Host " [OK] Installed successfully!" -ForegroundColor Green + Write-Host " $version" -ForegroundColor Gray + Write-Host "" + Write-Host " Usage:" -ForegroundColor Yellow + Write-Host " ztictl auth login" + Write-Host " ztictl ssm list --region cac1" + Write-Host " ztictl --help" + Write-Host "" + Write-Host " [!] Restart your terminal for PATH changes" -ForegroundColor Yellow + Write-Host "" +} catch { + Write-Host " [OK] Installed to $destPath" -ForegroundColor Green + Write-Host " [!] Restart terminal, then: ztictl --version" -ForegroundColor Yellow +} diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..b0bce82 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash +# +# One-liner installer for ztictl on macOS, Linux, WSL. +# +# Usage: +# curl -fsSL https://github.com/zsoftly/ztiaws/releases/latest/download/install.sh | bash +# + +set -euo pipefail + +REPO="zsoftly/ztiaws" +INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}" +BINARY_NAME="ztictl" + +# Colors +info() { printf "\033[0;36m %s\033[0m\n" "$1"; } +ok() { printf "\033[0;32m [OK] %s\033[0m\n" "$1"; } +error() { printf "\033[0;31m [ERROR] %s\033[0m\n" "$1"; exit 1; } + +echo "" +echo " ztictl installer" +echo " ----------------" +echo "" + +# Detect OS and architecture +OS=$(uname -s | tr '[:upper:]' '[:lower:]') +ARCH=$(uname -m) + +case "$OS" in + darwin) OS="darwin" ;; + linux) OS="linux" ;; + mingw*|msys*|cygwin*) error "Use PowerShell installer on Windows" ;; + *) error "Unsupported OS: $OS" ;; +esac + +case "$ARCH" in + x86_64|amd64) ARCH="amd64" ;; + aarch64|arm64) ARCH="arm64" ;; + *) error "Unsupported architecture: $ARCH" ;; +esac + +ASSET_NAME="ztictl-${OS}-${ARCH}" +DOWNLOAD_URL="https://github.com/${REPO}/releases/latest/download/${ASSET_NAME}" + +# Download +info "Downloading ${ASSET_NAME}..." +TMP_FILE=$(mktemp) +trap 'rm -f "$TMP_FILE"' EXIT + +if command -v curl &> /dev/null; then + curl -fsSL "$DOWNLOAD_URL" -o "$TMP_FILE" +elif command -v wget &> /dev/null; then + wget -q "$DOWNLOAD_URL" -O "$TMP_FILE" +else + error "curl or wget required" +fi + +chmod +x "$TMP_FILE" + +# Install +DEST_PATH="${INSTALL_DIR}/${BINARY_NAME}" +info "Installing to ${DEST_PATH}..." + +if [[ -w "$INSTALL_DIR" ]]; then + mv "$TMP_FILE" "$DEST_PATH" +else + sudo mv "$TMP_FILE" "$DEST_PATH" + sudo chmod +x "$DEST_PATH" +fi + +# Verify +if command -v ztictl &> /dev/null; then + VERSION=$(ztictl --version 2>&1 || echo "ztictl (version unavailable)") + echo "" + ok "Installed successfully!" + echo " $VERSION" +else + echo "" + ok "Installed to $DEST_PATH" + if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then + echo "" + echo " Add to PATH:" + echo " export PATH=\"\$PATH:$INSTALL_DIR\"" + fi +fi + +echo "" +echo " Usage:" +echo " ztictl auth login" +echo " ztictl ssm list --region cac1" +echo " ztictl --help" +echo "" diff --git a/tests/QA_AUTHAWS_TESTS.md b/tests/QA_AUTHAWS_TESTS.md index c1159b9..d652342 100644 --- a/tests/QA_AUTHAWS_TESTS.md +++ b/tests/QA_AUTHAWS_TESTS.md @@ -12,6 +12,7 @@ This document outlines the comprehensive test suite for the new flag-based param ## Test Environment Requirements ### Prerequisites + - AWS CLI v2.x installed and configured - `jq` and `fzf` dependencies installed - Valid AWS SSO configuration in `.env` file @@ -19,6 +20,7 @@ This document outlines the comprehensive test suite for the new flag-based param - Access to AWS SSO portal ### Test Data Setup + ```bash # Create test .env file # ⚠️ WARNING: Replace with your actual SSO configuration values @@ -41,9 +43,11 @@ aws configure set profile.test-profile-2.sso_region "us-west-2" ### 1. Backward Compatibility Tests #### Test 1.1: Positional Parameter Support + **Objective**: Verify existing positional syntax continues to work **Test Cases**: + ```bash # Test 1.1.1: No arguments (default profile) authaws @@ -69,9 +73,11 @@ Expected: Uses current AWS_PROFILE or default **Pass Criteria**: All existing positional commands work exactly as before #### Test 1.2: Existing Functionality Preservation + **Objective**: Ensure all existing features work with new parser **Test Cases**: + ```bash # Test 1.2.1: Help command authaws help @@ -89,9 +95,11 @@ Expected: Validates system requirements ### 2. Flag-Based Parameter Tests #### Test 2.1: Basic Flag Support + **Objective**: Verify new flag-based syntax works correctly **Test Cases**: + ```bash # Test 2.1.1: Profile flag authaws --profile test-profile-1 @@ -119,9 +127,11 @@ Expected: Shows version ``` #### Test 2.2: Command Flags + **Objective**: Test command-specific flags **Test Cases**: + ```bash # Test 2.2.1: Check flag authaws --check @@ -141,9 +151,11 @@ Expected: Lists available AWS profiles ``` #### Test 2.3: Advanced Flags + **Objective**: Test new advanced functionality **Test Cases**: + ```bash # Test 2.3.1: Region override authaws --profile test-profile-1 --region us-west-2 @@ -165,9 +177,11 @@ Expected: Shows debug information during execution ### 3. Mixed Syntax Tests #### Test 3.1: Positional + Flag Combinations + **Objective**: Test mixed syntax scenarios **Test Cases**: + ```bash # Test 3.1.1: Positional profile + flag authaws test-profile-1 --region us-west-2 @@ -185,9 +199,11 @@ Expected: Error - unexpected positional argument ### 4. Error Handling Tests #### Test 4.1: Invalid Flags + **Objective**: Test error handling for invalid parameters **Test Cases**: + ```bash # Test 4.1.1: Unknown flag authaws --unknown-flag @@ -207,9 +223,11 @@ Expected: Error message about invalid SSO URL ``` #### Test 4.2: Conflicting Commands + **Objective**: Test validation of mutually exclusive commands **Test Cases**: + ```bash # Test 4.2.1: Multiple commands authaws --check --creds @@ -223,9 +241,11 @@ Expected: Error message about unexpected arguments ### 5. Edge Case Tests #### Test 5.1: Boundary Conditions + **Objective**: Test edge cases and boundary conditions **Test Cases**: + ```bash # Test 5.1.1: Empty profile name authaws --profile "" @@ -245,9 +265,11 @@ Expected: Handles Unicode characters correctly ``` #### Test 5.2: Environment Variables + **Objective**: Test interaction with environment variables **Test Cases**: + ```bash # Test 5.2.1: AWS_PROFILE environment variable export AWS_PROFILE=env-profile @@ -263,9 +285,11 @@ Expected: Uses flag-profile, ignores AWS_PROFILE ### 6. Integration Tests #### Test 6.1: Full Authentication Flow + **Objective**: Test complete authentication workflow with new syntax **Test Cases**: + ```bash # Test 6.1.1: Full login with flags authaws --profile test-profile-1 --region us-west-2 @@ -281,9 +305,11 @@ Expected: Authenticates and exports credentials ``` #### Test 6.2: Profile Management + **Objective**: Test profile listing and management **Test Cases**: + ```bash # Test 6.2.1: List profiles authaws --list-profiles @@ -297,9 +323,11 @@ Expected: Creates new profile with specified settings ## Performance Tests ### Test 7.1: Parser Performance + **Objective**: Ensure parameter parsing doesn't impact performance **Test Cases**: + ```bash # Test 7.1.1: Parse time measurement time authaws --help @@ -313,9 +341,11 @@ Expected: Handles multiple flags efficiently ## Regression Tests ### Test 8.1: Existing Scripts + **Objective**: Ensure existing automation scripts continue to work **Test Cases**: + ```bash # Test 8.1.1: CI/CD script compatibility ./ci-script.sh # Uses authaws with positional parameters @@ -330,6 +360,7 @@ Expected: Alias works unchanged ## Test Execution Checklist ### Pre-Test Setup + - [ ] AWS CLI configured with test credentials - [ ] Test .env file created with valid SSO configuration - [ ] Test profiles created in AWS config @@ -337,6 +368,7 @@ Expected: Alias works unchanged - [ ] Test environment isolated from production ### Test Execution + - [ ] Run all backward compatibility tests - [ ] Run all flag-based parameter tests - [ ] Run all mixed syntax tests @@ -347,6 +379,7 @@ Expected: Alias works unchanged - [ ] Run regression tests ### Post-Test Validation + - [ ] All tests pass - [ ] No performance degradation - [ ] Error messages are clear and helpful @@ -356,6 +389,7 @@ Expected: Alias works unchanged ## Expected Results ### Success Criteria + 1. **100% Backward Compatibility**: All existing commands work unchanged 2. **Flag Support**: All new flag-based syntax works correctly 3. **Mixed Syntax**: Positional + flag combinations work as expected @@ -364,6 +398,7 @@ Expected: Alias works unchanged 6. **Documentation**: Help text accurately reflects both syntaxes ### Failure Criteria + 1. Any existing command fails or behaves differently 2. Flag-based syntax doesn't work as documented 3. Error messages are unclear or unhelpful @@ -373,6 +408,7 @@ Expected: Alias works unchanged ## Test Data Cleanup After testing, clean up test data: + ```bash # Remove test profiles aws configure set profile.test-profile-1.sso_start_url "" @@ -389,6 +425,7 @@ aws sso logout --profile test-profile-2 ## Reporting ### Test Report Template + ``` AuthAWS Flag-Based Parameters Test Report ======================================== @@ -418,12 +455,14 @@ Status: [PASS/FAIL/NEEDS_REVISION] ## Maintenance ### Ongoing Testing + - Run regression tests after any authaws changes - Test new flag combinations as they're added - Monitor performance impact over time - Update test cases for new features ### Test Case Updates + - Add test cases for new flags - Update expected results for changed behavior - Remove obsolete test cases diff --git a/tests/QA_SSM_TESTS.md b/tests/QA_SSM_TESTS.md index a2b3416..e5d417e 100644 --- a/tests/QA_SSM_TESTS.md +++ b/tests/QA_SSM_TESTS.md @@ -12,11 +12,13 @@ This document outlines the comprehensive test suite for the new flag-based param ## Command Usage Context **Development Testing** (used in this QA suite): + ```bash ./ssm --help # Tests local development version ``` **Production Usage** (after `make install`): + ```bash ssm --help # Uses globally installed version ``` @@ -26,6 +28,7 @@ This QA document uses `./ssm` to test the local development version before insta ## Test Environment Requirements ### Prerequisites + - AWS CLI v2.x installed and configured - AWS Session Manager plugin installed - `jq` dependency installed (for some operations) @@ -36,6 +39,7 @@ This QA document uses `./ssm` to test the local development version before insta ### Test Data Setup **Step 1: Create Environment File** + ```bash # Create a test configuration file (NOT committed to version control) cat > test-config.env << 'EOF' @@ -53,6 +57,7 @@ source test-config.env ``` **Step 2: Validate Test Data** + ```bash # Validate instance ID format before running tests validate_instance_id() { @@ -80,9 +85,11 @@ mkdir -p test-downloads ### 1. Backward Compatibility Tests #### Test 1.1: Positional Parameter Support + **Objective**: Verify existing positional syntax continues to work **Test Cases**: + ```bash # Test 1.1.1: No arguments (show help) ./ssm @@ -126,9 +133,11 @@ Expected: Downloads file from instance **Pass Criteria**: All existing positional commands work exactly as before #### Test 1.2: Existing Functionality Preservation + **Objective**: Ensure all existing features work with new parser **Test Cases**: + ```bash # Test 1.2.1: Help variations ./ssm help @@ -152,9 +161,11 @@ Expected: Shows installation instructions ### 2. Flag-Based Parameter Tests #### Test 2.1: Basic Flag Support + **Objective**: Verify new flag-based syntax works correctly **Test Cases**: + ```bash # Test 2.1.1: List with region flag ./ssm --region $TEST_REGION --list @@ -185,9 +196,11 @@ Expected: Runs system requirements check ``` #### Test 2.2: Operation Flags + **Objective**: Test operation-specific flags **Test Cases**: + ```bash # Test 2.2.1: Exec operation ./ssm --exec --region $TEST_REGION --instance $TEST_INSTANCE_1 --command "uptime" @@ -211,9 +224,11 @@ Expected: Shows debug information during execution ``` #### Test 2.3: Advanced Operations + **Objective**: Test advanced flag functionality **Test Cases**: + ```bash # Test 2.3.1: Port forwarding (if supported) ./ssm --forward --region $TEST_REGION --instance $TEST_INSTANCE_1 --local-port 8080 --remote-port 80 @@ -231,9 +246,11 @@ Expected: Executes complex command with debug output ### 3. Mixed Syntax Tests #### Test 3.1: Positional + Flag Combinations + **Objective**: Test mixed syntax scenarios **Test Cases**: + ```bash # Test 3.1.1: Positional region + flag instance ./ssm $TEST_REGION --instance $TEST_INSTANCE_1 @@ -259,9 +276,11 @@ Expected: Error - too many positional arguments ### 4. Error Handling Tests #### Test 4.1: Invalid Flags + **Objective**: Test error handling for invalid parameters **Test Cases**: + ```bash # Test 4.1.1: Unknown flag ./ssm --unknown-flag @@ -285,9 +304,11 @@ Expected: Error message about missing command ``` #### Test 4.2: Conflicting Operations + **Objective**: Test validation of mutually exclusive operations **Test Cases**: + ```bash # Test 4.2.1: Multiple operations ./ssm --exec --upload --region $TEST_REGION --instance $TEST_INSTANCE_1 @@ -305,9 +326,11 @@ Expected: Error message about invalid instance format ### 5. Edge Case Tests #### Test 5.1: Boundary Conditions + **Objective**: Test edge cases and boundary conditions **Test Cases**: + ```bash # Test 5.1.1: Empty values ./ssm --region "" --list @@ -327,9 +350,11 @@ Expected: Handles Unicode paths correctly ``` #### Test 5.2: Environment Variables + **Objective**: Test interaction with environment variables **Test Cases**: + ```bash # Test 5.2.1: SSM_DEBUG environment variable export SSM_DEBUG=true @@ -348,9 +373,11 @@ unset SSM_DEBUG AWS_DEFAULT_REGION ### 6. Integration Tests #### Test 6.1: Full Workflow Tests + **Objective**: Test complete workflows with new syntax **Test Cases**: + ```bash # Test 6.1.1: Complete file transfer workflow ./ssm --upload --region $TEST_REGION --instance $TEST_INSTANCE_1 --local-file test-upload.txt --remote-path /tmp/integration-test.txt @@ -370,9 +397,11 @@ Expected: Mixed syntax works in workflow ``` #### Test 6.2: Instance Management + **Objective**: Test instance discovery and management **Test Cases**: + ```bash # Test 6.2.1: List instances ./ssm --region $TEST_REGION --list @@ -390,9 +419,11 @@ Expected: Executes on all instances matching tag ## Performance Tests ### Test 7.1: Parser Performance + **Objective**: Ensure parameter parsing doesn't impact performance **Test Cases**: + ```bash # Test 7.1.1: Parse time measurement time ./ssm --help @@ -410,9 +441,11 @@ Expected: Consistent performance across multiple runs ## Regression Tests ### Test 8.1: Existing Scripts + **Objective**: Ensure existing automation scripts continue to work **Test Cases**: + ```bash # Test 8.1.1: CI/CD script compatibility # Create test script using old syntax @@ -439,6 +472,7 @@ unalias quick-connect ## Test Execution Checklist ### Pre-Test Setup + - [ ] AWS CLI configured with test credentials - [ ] Test instances available and SSM-enabled - [ ] Test region and instance IDs configured @@ -446,6 +480,7 @@ unalias quick-connect - [ ] Test environment isolated from production ### Test Execution + - [ ] Run all backward compatibility tests - [ ] Run all flag-based parameter tests - [ ] Run all mixed syntax tests @@ -456,6 +491,7 @@ unalias quick-connect - [ ] Run regression tests ### Post-Test Validation + - [ ] All tests pass - [ ] No performance degradation - [ ] Error messages are clear and helpful @@ -465,6 +501,7 @@ unalias quick-connect ## Expected Results ### Success Criteria + 1. **100% Backward Compatibility**: All existing commands work unchanged 2. **Flag Support**: All new flag-based syntax works correctly 3. **Mixed Syntax**: Positional + flag combinations work as expected @@ -473,6 +510,7 @@ unalias quick-connect 6. **Documentation**: Help text accurately reflects both syntaxes ### Failure Criteria + 1. Any existing command fails or behaves differently 2. Flag-based syntax doesn't work as documented 3. Error messages are unclear or unhelpful @@ -482,6 +520,7 @@ unalias quick-connect ## Test Data Cleanup After testing, clean up test data: + ```bash # Remove test files rm -f test-upload.txt @@ -497,6 +536,7 @@ unset TEST_REGION TEST_INSTANCE_1 TEST_INSTANCE_2 TEST_TAG_KEY TEST_TAG_VALUE ## Reporting ### Test Report Template + ``` SSM Flag-Based Parameters Test Report ===================================== @@ -537,13 +577,15 @@ Status: [PASS/FAIL/NEEDS_REVISION] ## Maintenance ### Ongoing Testing + - Run regression tests after any SSM changes - Test new flag combinations as they're added - Monitor performance impact over time - Update test cases for new features ### Test Case Updates + - Add test cases for new flags - Update expected results for changed behavior - Remove obsolete test cases -- Add performance benchmarks for new features \ No newline at end of file +- Add performance benchmarks for new features diff --git a/tools/README.md b/tools/README.md index 61ce91d..e0556b9 100644 --- a/tools/README.md +++ b/tools/README.md @@ -29,12 +29,12 @@ The `02_release_docs_generator.sh` script automatically generates `CHANGELOG.md` ### Options -| Option | Description | -|--------|-------------| -| `-v, --version VERSION` | Release version (required, format: v1.2.3 or 1.2.3) | -| `-t, --latest-tag TAG` | Latest release tag for comparison (auto-detected if not provided) | -| `-f, --force` | Force regeneration even if files exist | -| `-h, --help` | Show help message | +| Option | Description | +| ----------------------- | ----------------------------------------------------------------- | +| `-v, --version VERSION` | Release version (required, format: v1.2.3 or 1.2.3) | +| `-t, --latest-tag TAG` | Latest release tag for comparison (auto-detected if not provided) | +| `-f, --force` | Force regeneration even if files exist | +| `-h, --help` | Show help message | ## EC2 Test Manager Script @@ -68,11 +68,11 @@ The `01_ec2_test_manager.sh` script was created to streamline EC2 instance manag ### Commands -| Command | Purpose | -|---------|---------| +| Command | Purpose | +| -------- | ---------------------------------------------- | | `create` | Create EC2 instances with SSM-enabled IAM role | -| `verify` | Show status of tracked instances | -| `delete` | Terminate and cleanup all tracked instances | +| `verify` | Show status of tracked instances | +| `delete` | Terminate and cleanup all tracked instances | ### Key Options diff --git a/ztictl/.golangci.yml b/ztictl/.golangci.yml index 48b5b8f..c34187d 100644 --- a/ztictl/.golangci.yml +++ b/ztictl/.golangci.yml @@ -1,7 +1,7 @@ # golangci-lint configuration for ztictl # Compatible with golangci-lint v2.4.0 - modernized configuration -version: "2" +version: '2' run: build-tags: @@ -11,25 +11,25 @@ run: linters: enable: # Core linters - - errcheck # checks for unchecked errors - - gosec # inspects source code for security problems - - ineffassign # detects when assignments to existing variables are not used - - staticcheck # advanced static analyzer - - unused # checks for unused constants, variables, functions and types - - govet # reports suspicious constructs + - errcheck # checks for unchecked errors + - gosec # inspects source code for security problems + - ineffassign # detects when assignments to existing variables are not used + - staticcheck # advanced static analyzer + - unused # checks for unused constants, variables, functions and types + - govet # reports suspicious constructs # Additional modern linters for better code quality - - bodyclose # checks whether HTTP response body is closed - - contextcheck # checks for common mistakes in using contexts - - errorlint # finds code that will cause problems with Go 1.13 error wrapping - - gocritic # provides diagnostics that check for bugs, performance and style issues - - misspell # finds commonly misspelled English words in comments - - nilerr # finds the code that returns nil even if it checks that the error is not nil - - nolintlint # reports ill-formed or insufficient nolint directives - - prealloc # finds slice declarations that could potentially be pre-allocated - - revive # fast, configurable, extensible, flexible, and beautiful linter for Go - - rowserrcheck # checks whether Err of rows is checked successfully - - unconvert # removes unnecessary type conversions - - wastedassign # finds wasted assignment statements + - bodyclose # checks whether HTTP response body is closed + - contextcheck # checks for common mistakes in using contexts + - errorlint # finds code that will cause problems with Go 1.13 error wrapping + - gocritic # provides diagnostics that check for bugs, performance and style issues + - misspell # finds commonly misspelled English words in comments + - nilerr # finds the code that returns nil even if it checks that the error is not nil + - nolintlint # reports ill-formed or insufficient nolint directives + - prealloc # finds slice declarations that could potentially be pre-allocated + - revive # fast, configurable, extensible, flexible, and beautiful linter for Go + - rowserrcheck # checks whether Err of rows is checked successfully + - unconvert # removes unnecessary type conversions + - wastedassign # finds wasted assignment statements settings: gocritic: @@ -47,13 +47,13 @@ linters: - dupCase - flagDeref - ifElseChain - + gosec: # To select a subset of rules to exclude excludes: - G401 # Use of weak cryptographic primitive - SHA1 required for AWS CLI compatibility - G505 # Import blocklist: crypto/sha1 - SHA1 required for AWS CLI compatibility - + revive: # Set minimum confidence for revive to reduce false positives confidence: 0.8 @@ -71,7 +71,7 @@ linters: - gosec path: internal/auth/sso\.go text: (G401|G505) - + # Allow unchecked errors in test files for cleanup operations - linters: - errcheck @@ -83,12 +83,12 @@ linters: - errcheck path: \.go source: defer.*\.Close\(\) - + # Allow longer lines in generated files - linters: - lll path: .*\.pb\.go$ - + paths: - third_party$ - builtin$ @@ -96,12 +96,12 @@ linters: formatters: enable: - - gofmt # formats Go programs - - goimports # updates Go import lines - + - gofmt # formats Go programs + - goimports # updates Go import lines + exclusions: generated: lax paths: - third_party$ - builtin$ - - examples$ \ No newline at end of file + - examples$ diff --git a/ztictl/Makefile b/ztictl/Makefile index 9a0d19f..8eb783d 100644 --- a/ztictl/Makefile +++ b/ztictl/Makefile @@ -1,15 +1,13 @@ # ztictl Makefile # Cross-platform build automation -.PHONY: all build clean test help install +.PHONY: all build clean test fmt help install # Default target all: build -# Version - can be overridden via environment -BASE_VERSION ?= 2.11.0 -GIT_COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown") -VERSION := $(BASE_VERSION)-$(GIT_COMMIT) +# Auto-detect version from git tags (e.g., "2.11.0" or "2.11.0-3-g1a2b3c4" for dev builds) +VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev") # Build directories BUILD_DIR := builds @@ -97,6 +95,17 @@ test: go test -v ./... @echo "✓ Tests completed" +# Format code (Go + Markdown/JSON/YAML) +fmt: + @echo "ztictl $(VERSION)" + @echo "" + @echo "Formatting Go code..." + @go fmt ./... + @echo "Formatting Markdown/JSON/YAML files..." + @cd .. && npx prettier --write "**/*.md" "**/*.json" "**/*.yaml" "**/*.yml" 2>/dev/null || echo "Install Node.js for Prettier support: npm install -g prettier" + @echo "" + @echo "✓ Formatting completed" + # Install locally (for development) install: build-local @echo "Installing ztictl to local system..." @@ -119,16 +128,16 @@ help: @echo " build-local - Build for current platform only" @echo " clean - Clean build artifacts" @echo " test - Run tests" + @echo " fmt - Format code (Go + Markdown/JSON/YAML)" @echo " install - Install locally (requires sudo)" @echo " help - Show this help" @echo "" - @echo "Environment variables:" - @echo " VERSION - Set build version (default: $(VERSION))" + @echo "Version: $(VERSION) (auto-detected from git tags)" @echo "" @echo "Examples:" @echo " make # Build all platforms" @echo " make build-local # Build for current platform" - @echo " VERSION=2.1.0 make # Build with custom version" + @echo " make fmt # Format Go + Markdown/JSON/YAML" # Function to build for a specific platform define build_platform diff --git a/ztictl/README.md b/ztictl/README.md index 8d150aa..17565ad 100644 --- a/ztictl/README.md +++ b/ztictl/README.md @@ -16,9 +16,10 @@ ## Why ztictl? ### 🎯 **Enhanced Features** + - **🖥️ Multi-OS instance support**: Full Linux (bash) and Windows Server (PowerShell) command execution - **🤖 Automatic platform detection**: Detects instance OS via SSM/EC2 APIs and adapts commands -- **🛡️ Advanced security**: PowerShell injection protection, UNC path validation, path traversal prevention +- **🛡️ Advanced security**: PowerShell injection protection, UNC path validation, path traversal prevention - **Advanced file transfers**: Intelligent routing (direct <1MB, S3 ≥1MB) with automatic cleanup - **Comprehensive IAM management**: Temporary policies with lifecycle tracking and emergency cleanup - **S3 lifecycle integration**: Automatic bucket management with expiration policies @@ -27,16 +28,19 @@ ### 🌍 **Cross-Platform Support** **Client Platforms** (where ztictl runs): + - **Linux**: AMD64 and ARM64 (Intel/AMD and ARM processors) - **macOS**: Intel and Apple Silicon (M1/M2/M3) - **Windows**: AMD64 and ARM64 architectures **Target Instance Support** (what instances you can manage): + - **✅ Linux instances**: Amazon Linux, Ubuntu, RHEL, CentOS, SUSE (bash commands) - **✅ Windows instances**: Windows Server 2016/2019/2022 (PowerShell commands) - **🤖 Auto-detection**: Automatically detects instance OS and uses appropriate command syntax ### ⚡ **Performance Benefits** + - **Native binaries**: No runtime dependencies or script interpretation - **Optimized transfers**: Efficient handling of large files via S3 intermediary - **Concurrent operations**: Safe multi-instance operations with filesystem locking @@ -49,6 +53,7 @@ See [INSTALLATION.md](../INSTALLATION.md) for detailed installation instructions. **Quick Install (Linux/macOS):** + ```bash curl -L -o /tmp/ztictl "https://github.com/zsoftly/ztiaws/releases/latest/download/ztictl-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')" chmod +x /tmp/ztictl @@ -95,6 +100,7 @@ ztictl ssm stop --region cac1 ``` **Features:** + - 🔍 **Fuzzy search** - Type to filter instances by name, ID, or tags - ⌨️ **Keyboard shortcuts** - Vim-style navigation (j/k), arrow keys, Page Up/Down - 🖱️ **Mouse support** - Click to select, scroll to navigate @@ -130,11 +136,13 @@ ztictl auth whoami ``` **Best for:** + - Local development - Manual operations - Multi-account access with role switching **Requirements:** + - AWS SSO configured - Browser access for authentication - Interactive terminal @@ -143,14 +151,15 @@ ztictl auth whoami For automated pipelines, AWS SSO **cannot be used** (requires browser interaction). Use IAM-based authentication instead: -| Method | When to Use | Security | Setup Complexity | -|--------|-------------|----------|------------------| -| **OIDC Federation** | GitHub Actions, GitLab CI, modern platforms | ⭐⭐⭐⭐⭐ Best | Medium | -| **EC2 Instance Profile** | Self-hosted runners on EC2 | ⭐⭐⭐⭐⭐ Best | Easy | -| **ECS Task Role** | Containerized CI/CD on ECS/Fargate | ⭐⭐⭐⭐⭐ Best | Easy | -| **IAM Access Keys** | Legacy systems, quick testing | ⭐⭐ Poor | Easy | +| Method | When to Use | Security | Setup Complexity | +| ------------------------ | ------------------------------------------- | --------------- | ---------------- | +| **OIDC Federation** | GitHub Actions, GitLab CI, modern platforms | ⭐⭐⭐⭐⭐ Best | Medium | +| **EC2 Instance Profile** | Self-hosted runners on EC2 | ⭐⭐⭐⭐⭐ Best | Easy | +| **ECS Task Role** | Containerized CI/CD on ECS/Fargate | ⭐⭐⭐⭐⭐ Best | Easy | +| **IAM Access Keys** | Legacy systems, quick testing | ⭐⭐ Poor | Easy | **OIDC Federation Example (Recommended):** + ```yaml # GitHub Actions - name: Configure AWS Credentials @@ -166,6 +175,7 @@ For automated pipelines, AWS SSO **cannot be used** (requires browser interactio ``` **EC2 Instance Profile Example:** + ```bash # No credential configuration needed - automatic from instance metadata ztictl config init --non-interactive @@ -173,6 +183,7 @@ ztictl ssm list --table ``` **IAM Access Keys Example (Not Recommended):** + ```bash # Set environment variables (store in CI/CD secrets) export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE" @@ -186,11 +197,13 @@ ztictl ssm list --table ### Non-Interactive Mode `ztictl` automatically detects CI/CD environments and enables non-interactive mode when: + - `CI` environment variable is set (most platforms set this automatically) - `ZTICTL_NON_INTERACTIVE=true` is set - `--non-interactive` flag is used **In non-interactive mode:** + - No splash screen or prompts - Commands requiring instance selection will fail with clear error messages - Use explicit instance IDs or tag-based commands @@ -212,6 +225,7 @@ ztictl ssm connect web-server-prod # ✅ Works (name lookup) ``` **Complete Guide:** See [docs/CI_CD_AUTHENTICATION.md](../docs/CI_CD_AUTHENTICATION.md) for: + - Detailed authentication setup for each platform - IAM permission requirements - Complete workflow examples @@ -238,7 +252,7 @@ ztictl ssm exec --tags "Environment=prod" "uptime" --region euw1 ztictl ssm exec cac1 i-linux123 "ps aux | grep nginx" ztictl ssm exec cac1 i-linux123 "cat /var/log/app.log | tail -10" -# Windows instances - PowerShell commands +# Windows instances - PowerShell commands ztictl ssm exec cac1 i-windows456 "Get-Process | Where-Object {$_.Name -like '*iis*'}" ztictl ssm exec cac1 i-windows456 "Get-EventLog -LogName Application -Newest 10" @@ -257,13 +271,13 @@ ztictl ssm exec-multi --all-regions --tags "Type=api" "status" For complete command documentation, see [docs/COMMANDS.md](../docs/COMMANDS.md). -``` +```` ## Advanced Features ### 🔒 **Security & IAM** - **Temporary IAM policies**: Automatically created and cleaned up -- **Filesystem locking**: Prevents concurrent policy conflicts +- **Filesystem locking**: Prevents concurrent policy conflicts - **Registry tracking**: Complete audit trail of temporary resources - **Emergency procedures**: Comprehensive cleanup capabilities @@ -296,9 +310,10 @@ make test # Clean artifacts make clean -``` +```` ### 🚀 **Release Process** + ```bash # Create and push tag (triggers automated builds) git tag v1.1.0 @@ -306,7 +321,7 @@ git push origin v1.1.0 # GitHub Actions automatically: # ✅ Builds for all platforms -# ✅ Runs comprehensive tests +# ✅ Runs comprehensive tests # ✅ Creates GitHub release # ✅ Uploads cross-platform binaries ``` @@ -328,11 +343,13 @@ See the main [CONTRIBUTING.md](../CONTRIBUTING.md) for guidelines. ## Support & Compatibility ### 🔧 **Requirements** + - **AWS CLI** configured with appropriate credentials - **Session Manager plugin** for SSM connections - **EC2 instances** with SSM agent and proper IAM roles ### 🆘 **Getting Help** + ```bash # Built-in help ztictl --help diff --git a/ztictl/cmd/ztictl/rds_power.go b/ztictl/cmd/ztictl/rds_power.go index 4418c12..6be05ce 100644 --- a/ztictl/cmd/ztictl/rds_power.go +++ b/ztictl/cmd/ztictl/rds_power.go @@ -16,6 +16,9 @@ import ( "github.com/spf13/cobra" ) +// rdsClientPool is reused across RDS commands for efficiency +var rdsClientPool = ssm.NewClientPool() + // rdsListCmd represents the rds list command var rdsListCmd = &cobra.Command{ Use: "list", @@ -123,8 +126,7 @@ func performRDSList(regionCode string) error { region := resolveRegion(regionCode) ctx := context.Background() - clientPool := ssm.NewClientPool() - rdsClient, err := clientPool.GetRDSClient(ctx, region) + rdsClient, err := rdsClientPool.GetRDSClient(ctx, region) if err != nil { return fmt.Errorf("failed to create RDS client: %w", err) } @@ -174,8 +176,7 @@ func performRDSStart(regionCode, dbIdentifier string, wait bool) error { region := resolveRegion(regionCode) ctx := context.Background() - clientPool := ssm.NewClientPool() - rdsClient, err := clientPool.GetRDSClient(ctx, region) + rdsClient, err := rdsClientPool.GetRDSClient(ctx, region) if err != nil { return fmt.Errorf("failed to create RDS client: %w", err) } @@ -210,8 +211,7 @@ func performRDSStop(regionCode, dbIdentifier string, wait bool) error { region := resolveRegion(regionCode) ctx := context.Background() - clientPool := ssm.NewClientPool() - rdsClient, err := clientPool.GetRDSClient(ctx, region) + rdsClient, err := rdsClientPool.GetRDSClient(ctx, region) if err != nil { return fmt.Errorf("failed to create RDS client: %w", err) } @@ -247,8 +247,7 @@ func performRDSReboot(regionCode, dbIdentifier string, forceFailover, wait bool) region := resolveRegion(regionCode) ctx := context.Background() - clientPool := ssm.NewClientPool() - rdsClient, err := clientPool.GetRDSClient(ctx, region) + rdsClient, err := rdsClientPool.GetRDSClient(ctx, region) if err != nil { return fmt.Errorf("failed to create RDS client: %w", err) } @@ -289,14 +288,25 @@ func waitForRDSStatus(ctx context.Context, rdsClient *rds.Client, dbIdentifier, maxWait := 30 * time.Minute pollInterval := 30 * time.Second deadline := time.Now().Add(maxWait) + consecutiveErrors := 0 + maxConsecutiveErrors := 3 for time.Now().Before(deadline) { resp, err := rdsClient.DescribeDBInstances(ctx, &rds.DescribeDBInstancesInput{ DBInstanceIdentifier: aws.String(dbIdentifier), }) if err != nil { - return fmt.Errorf("failed to check instance status: %w", err) + consecutiveErrors++ + if consecutiveErrors > maxConsecutiveErrors { + return fmt.Errorf("failed to check instance status after %d retries: %w", maxConsecutiveErrors, err) + } + // Exponential backoff for transient errors + backoff := pollInterval * time.Duration(consecutiveErrors) + fmt.Printf(" Error checking status, retrying in %v... (%d/%d)\n", backoff, consecutiveErrors, maxConsecutiveErrors) + time.Sleep(backoff) + continue } + consecutiveErrors = 0 if len(resp.DBInstances) == 0 { return fmt.Errorf("instance %s not found", dbIdentifier) diff --git a/ztictl/cmd/ztictl/ssm_ssh.go b/ztictl/cmd/ztictl/ssm_ssh.go index f9e14b5..6bfb72f 100644 --- a/ztictl/cmd/ztictl/ssm_ssh.go +++ b/ztictl/cmd/ztictl/ssm_ssh.go @@ -8,6 +8,7 @@ import ( "path/filepath" "runtime" "strings" + "time" "ztictl/internal/ssm" "ztictl/pkg/logging" @@ -327,9 +328,9 @@ func performRDPConnection(regionCode, instanceIdentifier string, localPort int, fmt.Printf("\nConnect your RDP client to: localhost:%d\n", localPort) if launch { - // Launch RDP client in background before starting the tunnel + // Launch RDP client in background after giving tunnel time to establish go func() { - // Give the tunnel a moment to start + time.Sleep(2 * time.Second) // Wait for tunnel to be ready launchRDPClient(localPort) }() } else { diff --git a/ztictl/docs/FUZZY_FINDER_FEATURES.md b/ztictl/docs/FUZZY_FINDER_FEATURES.md index b381cf0..917518e 100644 --- a/ztictl/docs/FUZZY_FINDER_FEATURES.md +++ b/ztictl/docs/FUZZY_FINDER_FEATURES.md @@ -5,9 +5,11 @@ The `ztictl` fuzzy finder provides interactive selection across multiple command ## Available in Commands ### Authentication + - **`ztictl auth login`** - Interactive account/role selection ### SSM Instance Operations (v2.1+) + All SSM commands support interactive instance selection by omitting the instance identifier: - **`ztictl ssm connect [--region ]`** - Connect to an instance @@ -20,6 +22,7 @@ All SSM commands support interactive instance selection by omitting the instance - **`ztictl ssm reboot [--region ]`** - Reboot a running instance ### Usage Example + ```bash # Traditional way (still supported) ztictl ssm connect i-1234567890abcdef0 --region cac1 @@ -34,14 +37,14 @@ When using the fuzzy finder for SSM operations, `ztictl` automatically validates ### Validation Rules -| Operation | Required Instance State | Requires SSM Agent Online | -|-----------|------------------------|---------------------------| -| `connect` | `running` | ✅ Yes | -| `exec` | `running` | ✅ Yes | -| `transfer` | `running` | ✅ Yes | -| `start` | `stopped` | ❌ No | -| `stop` | `running` | ❌ No | -| `reboot` | `running` | ❌ No | +| Operation | Required Instance State | Requires SSM Agent Online | +| ---------- | ----------------------- | ------------------------- | +| `connect` | `running` | ✅ Yes | +| `exec` | `running` | ✅ Yes | +| `transfer` | `running` | ✅ Yes | +| `start` | `stopped` | ❌ No | +| `stop` | `running` | ❌ No | +| `reboot` | `running` | ❌ No | ### Error Feedback @@ -78,6 +81,7 @@ Instance Details: ## Keyboard Shortcuts ### Text Editing + - **Ctrl+V** - Paste text from clipboard into search box - **Ctrl+X** - Cut selected text to clipboard - **Ctrl+C** - Copy selected text to clipboard @@ -85,6 +89,7 @@ Instance Details: - **Backspace/Delete** - Delete selected text (if any) or single character ### Navigation (Text) + - **Ctrl+A** / **Home** - Move cursor to beginning of search box - **Ctrl+E** / **End** - Move cursor to end of search box - **Ctrl+B** / **Left Arrow** - Move cursor left one character @@ -93,6 +98,7 @@ Instance Details: - **Ctrl+U** - Delete everything before cursor (kill line backward) ### Navigation (List) + - **Up Arrow** / **Ctrl+K** / **Ctrl+P** - Move selection up - **Down Arrow** / **Ctrl+J** / **Ctrl+N** - Move selection down - **Page Up** - Scroll up one page @@ -100,18 +106,21 @@ Instance Details: - **Tab** - Toggle selection (in multi-select mode) ### Actions + - **Enter** - Confirm selection - **Ctrl+D** / **Ctrl+Q** / **Esc** - Quit/abort the finder ## Mouse Support ### Search Box Interactions + - **Left-click on prompt** - Position cursor at click location in search box - **Left-click + drag on prompt** - Select text by dragging in search box - **Double-click on prompt** - Select word under cursor in search box - **Right-click on prompt** - Paste text from clipboard into search box ### List Interactions + - **Left-click on item** - Select that item in the list - **Mouse wheel up/down** - Scroll through the item list @@ -161,11 +170,13 @@ The application will exit cleanly with code 1 instead of crashing with a stack t ## Platform Support All features are supported on: + - ✅ Linux - ✅ macOS - ✅ Windows (Command Prompt, PowerShell, Windows Terminal) Clipboard operations require: + - Linux: X11 or Wayland (xclip/xsel/wl-clipboard) - macOS: Built-in (pbcopy/pbpaste) - Windows: Built-in diff --git a/ztictl/docs/VALIDATION.md b/ztictl/docs/VALIDATION.md index 25857ed..98008be 100644 --- a/ztictl/docs/VALIDATION.md +++ b/ztictl/docs/VALIDATION.md @@ -46,6 +46,7 @@ func ValidateInstanceState( ``` **Process:** + 1. Fetches instance details from AWS 2. Validates EC2 instance state 3. Validates SSM agent status (if required) @@ -64,10 +65,12 @@ func ValidateInstanceState( ### SSM Operations (Connect, Exec, Transfer) **Requirements:** + - Instance State: `running` - SSM Agent: `Online` **Example:** + ```go ValidateInstanceState(ctx, ssmManager, instanceID, region, InstanceValidationRequirements{ AllowedStates: []string{"running"}, @@ -79,10 +82,12 @@ ValidateInstanceState(ctx, ssmManager, instanceID, region, InstanceValidationReq ### Power Operations - Start **Requirements:** + - Instance State: `stopped` - SSM Agent: Not required **Example:** + ```go ValidateInstanceState(ctx, ssmManager, instanceID, region, InstanceValidationRequirements{ AllowedStates: []string{"stopped"}, @@ -94,10 +99,12 @@ ValidateInstanceState(ctx, ssmManager, instanceID, region, InstanceValidationReq ### Power Operations - Stop/Reboot **Requirements:** + - Instance State: `running` - SSM Agent: Not required **Example:** + ```go ValidateInstanceState(ctx, ssmManager, instanceID, region, InstanceValidationRequirements{ AllowedStates: []string{"running"}, @@ -108,22 +115,22 @@ ValidateInstanceState(ctx, ssmManager, instanceID, region, InstanceValidationReq ## EC2 Instance States -| State | Symbol | Color | Description | Can Start | Can Stop | Can Connect | -|-------|--------|-------|-------------|-----------|----------|-------------| -| `running` | ● | Green | Instance is operational | ❌ | ✅ | ✅ | -| `stopped` | ○ | Red | Instance is stopped | ✅ | ❌ | ❌ | -| `stopping` | ◑ | Yellow | Instance is stopping | ❌ | ❌ | ❌ | -| `pending` | ◐ | Yellow | Instance is starting | ❌ | ❌ | ❌ | -| `shutting-down` | ◑ | Yellow | Instance is shutting down | ❌ | ❌ | ❌ | -| `terminated` | ✗ | Red | Instance is deleted | ❌ | ❌ | ❌ | +| State | Symbol | Color | Description | Can Start | Can Stop | Can Connect | +| --------------- | ------ | ------ | ------------------------- | --------- | -------- | ----------- | +| `running` | ● | Green | Instance is operational | ❌ | ✅ | ✅ | +| `stopped` | ○ | Red | Instance is stopped | ✅ | ❌ | ❌ | +| `stopping` | ◑ | Yellow | Instance is stopping | ❌ | ❌ | ❌ | +| `pending` | ◐ | Yellow | Instance is starting | ❌ | ❌ | ❌ | +| `shutting-down` | ◑ | Yellow | Instance is shutting down | ❌ | ❌ | ❌ | +| `terminated` | ✗ | Red | Instance is deleted | ❌ | ❌ | ❌ | ## SSM Agent States -| Status | Symbol | Color | Description | Can Connect | -|--------|--------|-------|-------------|-------------| -| `Online` | ✓ | Green | Agent is connected and ready | ✅ | -| `ConnectionLost` | ⚠ | Yellow | Agent was online but disconnected | ⚠️ Allowed | -| `No Agent` | ✗ | Red | Agent not installed or not running | ❌ | +| Status | Symbol | Color | Description | Can Connect | +| ---------------- | ------ | ------ | ---------------------------------- | ----------- | +| `Online` | ✓ | Green | Agent is connected and ready | ✅ | +| `ConnectionLost` | ⚠ | Yellow | Agent was online but disconnected | ⚠️ Allowed | +| `No Agent` | ✗ | Red | Agent not installed or not running | ❌ | **Note:** Operations are allowed with `ConnectionLost` status but may fail. Users receive a warning. @@ -240,6 +247,7 @@ Tests for validation are in `cmd/ztictl/ssm_power_test.go` and related test file ### 1. Always Validate Before Operations **Do:** + ```go // Validate first if err := ValidateInstanceState(...); err != nil { @@ -250,6 +258,7 @@ result, err := performOperation(...) ``` **Don't:** + ```go // Operate without validation - may fail with cryptic AWS errors result, err := performOperation(...) @@ -258,11 +267,13 @@ result, err := performOperation(...) ### 2. Use Descriptive Operation Names **Do:** + ```go Operation: "execute commands" // Clear and user-friendly ``` **Don't:** + ```go Operation: "exec" // Too terse for error messages ``` @@ -270,11 +281,13 @@ Operation: "exec" // Too terse for error messages ### 3. Specify Exact Required States **Do:** + ```go AllowedStates: []string{"stopped"} // Explicit ``` **Don't:** + ```go AllowedStates: []string{"stopped", "stopping"} // Ambiguous ``` diff --git a/ztictl/internal/ssm/manager.go b/ztictl/internal/ssm/manager.go index 26123cf..997d029 100644 --- a/ztictl/internal/ssm/manager.go +++ b/ztictl/internal/ssm/manager.go @@ -141,6 +141,13 @@ func (m *Manager) StartSession(ctx context.Context, instanceIdentifier, region s signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) defer signal.Stop(sigChan) + // Drain signals to prevent channel from filling up + go func() { + for range sigChan { + // Ignore signals - they pass through to subprocess + } + }() + if err := cmd.Run(); err != nil { return errors.NewSSMError("failed to start session", err) } @@ -412,6 +419,13 @@ func (m *Manager) ForwardPort(ctx context.Context, instanceIdentifier, region st signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) defer signal.Stop(sigChan) + // Drain signals to prevent channel from filling up + go func() { + for range sigChan { + // Ignore signals - they pass through to subprocess + } + }() + if err := cmd.Run(); err != nil { return errors.NewSSMError("failed to start port forwarding", err) } From 9381b9c085079f9e751ebbb0e2b9e86196642e99 Mon Sep 17 00:00:00 2001 From: ditahkk Date: Sun, 28 Dec 2025 11:50:18 -0500 Subject: [PATCH 4/6] fix: update issue templates for clarity and consistency; enhance contributing guidelines and CI/CD documentation --- .github/ISSUE_TEMPLATE/bug_report.md | 18 +++++++---- .github/workflows/build.yml | 45 ++++++++++++++++++---------- CONTRIBUTING.md | 19 +++++++----- INSTALLATION.md | 4 --- docs/CI_CD_PIPELINE.md | 8 +++-- 5 files changed, 59 insertions(+), 35 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 0b6f389..2174e50 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -6,27 +6,32 @@ labels: bug assignees: '' --- -**Describe the bug** +## Describe the bug + A clear and concise description of what the bug is. -**To Reproduce** +## To Reproduce + Steps to reproduce the behavior: 1. Run command '...' 2. With arguments '....' 3. See error -**Expected behavior** +## Expected behavior + A clear and concise description of what you expected to happen. -**Environment (please complete the following information):** +## Environment + +Please complete the following information: - OS: [e.g. Ubuntu 20.04] - Shell: [e.g. bash, zsh, PowerShell] - AWS CLI Version: [e.g. 2.11.0] - SSM Plugin Version: [e.g. 1.2.463.0] -**Output of tool check command** +## Output of tool check command ``` # For SSM issues: @@ -36,5 +41,6 @@ Paste the output of `ssm check` here Paste the output of `authaws check` here ``` -**Additional context** +## Additional context + Add any other context about the problem here. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b9989f1..bcac77d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -89,14 +89,19 @@ jobs: if: failure() && matrix.os == 'ubuntu-latest' && github.event_name == 'pull_request' env: GOOGLE_CHAT_WEBHOOK: ${{ secrets.GOOGLE_CHAT_WEBHOOK }} + PR_TITLE: ${{ github.event.pull_request.title }} + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_URL: ${{ github.event.pull_request.html_url }} + PR_AUTHOR: ${{ github.event.pull_request.user.login }} + REPO: ${{ github.repository }} run: | if [[ -n "$GOOGLE_CHAT_WEBHOOK" ]]; then ./scripts/send-pr-notification.sh \ - --pr-title '${{ github.event.pull_request.title }}' \ - --pr-number '${{ github.event.pull_request.number }}' \ - --pr-url '${{ github.event.pull_request.html_url }}' \ - --author '${{ github.event.pull_request.user.login }}' \ - --repository '${{ github.repository }}' \ + --pr-title "$PR_TITLE" \ + --pr-number "$PR_NUMBER" \ + --pr-url "$PR_URL" \ + --author "$PR_AUTHOR" \ + --repository "$REPO" \ --status "failure" \ --message "Shell script tests failed - PR needs attention" || true fi @@ -185,14 +190,19 @@ jobs: if: failure() && matrix.os == 'ubuntu-latest' && github.event_name == 'pull_request' env: GOOGLE_CHAT_WEBHOOK: ${{ secrets.GOOGLE_CHAT_WEBHOOK }} + PR_TITLE: ${{ github.event.pull_request.title }} + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_URL: ${{ github.event.pull_request.html_url }} + PR_AUTHOR: ${{ github.event.pull_request.user.login }} + REPO: ${{ github.repository }} run: | if [[ -n "$GOOGLE_CHAT_WEBHOOK" ]]; then ./scripts/send-pr-notification.sh \ - --pr-title '${{ github.event.pull_request.title }}' \ - --pr-number '${{ github.event.pull_request.number }}' \ - --pr-url '${{ github.event.pull_request.html_url }}' \ - --author '${{ github.event.pull_request.user.login }}' \ - --repository '${{ github.repository }}' \ + --pr-title "$PR_TITLE" \ + --pr-number "$PR_NUMBER" \ + --pr-url "$PR_URL" \ + --author "$PR_AUTHOR" \ + --repository "$REPO" \ --status "failure" \ --message "Go tests failed - PR needs attention" || true fi @@ -252,14 +262,19 @@ jobs: if: success() && github.event_name == 'pull_request' env: GOOGLE_CHAT_WEBHOOK: ${{ secrets.GOOGLE_CHAT_WEBHOOK }} + PR_TITLE: ${{ github.event.pull_request.title }} + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_URL: ${{ github.event.pull_request.html_url }} + PR_AUTHOR: ${{ github.event.pull_request.user.login }} + REPO: ${{ github.repository }} run: | if [[ -n "$GOOGLE_CHAT_WEBHOOK" ]]; then ./scripts/send-pr-notification.sh \ - --pr-title '${{ github.event.pull_request.title }}' \ - --pr-number '${{ github.event.pull_request.number }}' \ - --pr-url '${{ github.event.pull_request.html_url }}' \ - --author '${{ github.event.pull_request.user.login }}' \ - --repository '${{ github.repository }}' \ + --pr-title "$PR_TITLE" \ + --pr-number "$PR_NUMBER" \ + --pr-url "$PR_URL" \ + --author "$PR_AUTHOR" \ + --repository "$REPO" \ --status "success" \ --message "All tests passed - PR is ready for review" || true fi diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 89d6f80..cda59f8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,8 +52,8 @@ git checkout -b feature/add-region-euw2 # For Go code (ztictl) cd ztictl && make test -# For shell scripts -./tests/test_ssm.sh +# For shell scripts - run shellcheck +shellcheck -x authaws ssm src/*.sh ``` 5. Submit a Pull Request @@ -64,13 +64,11 @@ cd ztictl && make test 1. Update REGIONS.md with new region details (if adding regions) 2. Update tests to cover new functionality -3. Ensure all CI checks pass: +3. Update documentation if needed +4. Ensure all CI checks pass: - **Quick tests** run automatically on all PRs - **Security scans** run on PRs to main branch - **Builds** are triggered only for releases -4. Update documentation if needed -5. Ensure all tests pass -6. Update documentation if needed ## Code Style @@ -84,7 +82,14 @@ cd ztictl && make test Test your changes: ```bash -./tests/test_ssm.sh +# Run shell linting +make test + +# Or manually run shellcheck +shellcheck -x authaws ssm src/*.sh + +# For Go code +cd ztictl && make test ``` ## Commit Messages diff --git a/INSTALLATION.md b/INSTALLATION.md index f31b5f9..bda51cd 100644 --- a/INSTALLATION.md +++ b/INSTALLATION.md @@ -453,7 +453,3 @@ ztictl config check - **📊 Better logging**: Thread-safe, timestamped logs with debug capabilities **Migrate from bash tools** by simply installing ztictl and using similar commands with modern flag syntax! - -``` - -``` diff --git a/docs/CI_CD_PIPELINE.md b/docs/CI_CD_PIPELINE.md index 4ce61c3..ce35cb5 100644 --- a/docs/CI_CD_PIPELINE.md +++ b/docs/CI_CD_PIPELINE.md @@ -379,11 +379,13 @@ The CI/CD pipeline integrates with the release process defined in [RELEASE.md](d ### **Regular Updates** -- **Go version**: Currently 1.25, update in all workflow files -- **Action versions**: Using v4 for checkout, setup-go actions -- **Security tools**: golangci-lint pinned at v2.4.0 for stability +- **Go version**: Currently 1.24, update in all workflow files when changing +- **Action versions**: Using v4 for checkout, v5 for setup-go +- **Security tools**: golangci-lint pinned at v1.61.0 for stability - **Build dependencies**: Regular `go mod tidy` and updates +> **Note:** Keep these values in sync with `.github/workflows/build.yml` + ### **Monitoring** - **Build status badges**: Visible in README From 8c6064cf70d17e6460d4be1006149c185c38d88c Mon Sep 17 00:00:00 2001 From: ditahkk Date: Sun, 28 Dec 2025 11:52:49 -0500 Subject: [PATCH 5/6] docs: clarify Go version policy and rationale for staying on Go 1.24 --- docs/CI_CD_PIPELINE.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/CI_CD_PIPELINE.md b/docs/CI_CD_PIPELINE.md index ce35cb5..ffb26f6 100644 --- a/docs/CI_CD_PIPELINE.md +++ b/docs/CI_CD_PIPELINE.md @@ -379,13 +379,24 @@ The CI/CD pipeline integrates with the release process defined in [RELEASE.md](d ### **Regular Updates** -- **Go version**: Currently 1.24, update in all workflow files when changing +- **Go version**: Currently **1.24** (intentionally, see note below) - **Action versions**: Using v4 for checkout, v5 for setup-go - **Security tools**: golangci-lint pinned at v1.61.0 for stability - **Build dependencies**: Regular `go mod tidy` and updates > **Note:** Keep these values in sync with `.github/workflows/build.yml` +### **Go Version Policy** + +As of December 2025, Go 1.25.5 is the latest stable release. This project **intentionally stays on Go 1.24.x** for the following reasons: + +- **Stability**: Go 1.24 is battle-tested and well-supported +- **Compatibility**: Ensures broader compatibility with user environments +- **AWS SDK**: Validated against AWS SDK v2 on Go 1.24 +- **Conservative approach**: Upgrade after Go 1.25 has more production exposure + +**Upgrade plan**: Evaluate Go 1.25 upgrade when Go 1.26 is released (expected Feb 2026), ensuring one major version buffer for stability. + ### **Monitoring** - **Build status badges**: Visible in README From 6c53fee587d945e9ae90e3876247a1feb76b4d96 Mon Sep 17 00:00:00 2001 From: ditahkk Date: Sun, 28 Dec 2025 14:03:17 -0500 Subject: [PATCH 6/6] fix: update golangci-lint version and refine Go version upgrade notes in CI/CD documentation --- docs/CI_CD_PIPELINE.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/CI_CD_PIPELINE.md b/docs/CI_CD_PIPELINE.md index ffb26f6..f59cf95 100644 --- a/docs/CI_CD_PIPELINE.md +++ b/docs/CI_CD_PIPELINE.md @@ -171,7 +171,7 @@ needs: [test-shell, test-go] **Tools used:** - **Trivy**: Filesystem vulnerability scanning by Aqua Security -- **golangci-lint v2.4.0**: With GoSec security linters enabled +- **golangci-lint v1.61.0**: With GoSec security linters enabled - **Standalone GoSec**: Backup security analysis (latest version) - **govulncheck**: Official Go vulnerability database checker - **Dependency audit**: Lists outdated packages with `go list -u -m all` @@ -407,8 +407,8 @@ As of December 2025, Go 1.25.5 is the latest stable release. This project **inte ### **Potential Improvements** -- **Go version**: Upgrade to Go 1.26 when stable -- **golangci-lint**: Update from v2.4.0 to latest version +- **Go version**: Upgrade to Go 1.25+ when stable (see Go Version Policy above) +- **golangci-lint**: Update from v1.61.0 to latest stable (sync with Maintenance section) - **Test coverage**: Add coverage reporting and badges - **AWS integration tests**: Mock AWS services for deeper testing - **Binary signing**: GPG signing for release artifacts