From 9636ac01168e30192fbe56acb277157e0e062bba Mon Sep 17 00:00:00 2001 From: TJ Miller Date: Tue, 3 Nov 2020 03:05:21 -0800 Subject: [PATCH] refactor and fix issues #3, #4, #8 --- go.mod | 5 +- go.sum | 56 +++++++++++ helm_wrapper.go | 203 +++++++++++++++++++++++++++++++++++++++ helm_wrapper_test.go | 111 ++++++++++++++++++++++ main.go | 219 ++----------------------------------------- utils.go | 24 +++++ utils_test.go | 43 +++++++++ 7 files changed, 448 insertions(+), 213 deletions(-) create mode 100644 helm_wrapper.go create mode 100644 helm_wrapper_test.go create mode 100644 utils.go create mode 100644 utils_test.go diff --git a/go.mod b/go.mod index cf63cc0..c3d98af 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module github.com/camptocamp/helm-sops go 1.13 -require go.mozilla.org/sops/v3 v3.5.0 +require ( + go.mozilla.org/sops/v3 v3.6.1 + gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 +) diff --git a/go.sum b/go.sum index b121903..2620deb 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,7 @@ cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7p contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA= github.com/Azure/azure-sdk-for-go v31.2.0+incompatible h1:kZFnTLmdQYNGfakatSivKHUfUnDZhqNdchHD4oIhp5k= github.com/Azure/azure-sdk-for-go v31.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg= github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= @@ -32,6 +33,8 @@ github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VY github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -41,12 +44,16 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.23.13 h1:l/NG+mgQFRGG3dsFzEj0jw9JIs/zYdtU6MXhY1WIDmM= github.com/aws/aws-sdk-go v1.23.13/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.33.18 h1:Ccy1SV2SsgJU3rfrD+SOhQ0jvuzfrFuja/oKI86ruPw= +github.com/aws/aws-sdk-go v1.33.18/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -54,6 +61,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= @@ -65,6 +74,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -79,6 +89,7 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -95,19 +106,26 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= github.com/goware/prefixer v0.0.0-20160118172347-395022866408 h1:Y9iQJfEqnN3/Nce9cOegemcy/9Ai5k3huT6E80F3zaw= github.com/goware/prefixer v0.0.0-20160118172347-395022866408/go.mod h1:PE1ycukgRPJ7bJ9a1fdfQ9j8i/cEcRAoLZzbxYpNB/s= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= +github.com/hashicorp/go-retryablehttp v0.5.4 h1:1BZvpawXoJCWX6pNtow9+rpEj+3itIlutiqnntI6jOE= github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.1 h1:DMo4fmknnz0E0evoNYnV48RjWndOsmd6OW+09R3cEP8= github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -115,8 +133,11 @@ github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/vault/api v1.0.4 h1:j08Or/wryXT4AcHj1oCbMd7IijXcKzYUGw59LGu9onU= github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= +github.com/hashicorp/vault/sdk v0.1.13 h1:mOEPeOhT7jl0J4AMl1E705+BcmeRs1VmKNb9F0sMLy8= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= @@ -125,6 +146,8 @@ github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= +github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -153,6 +176,7 @@ github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go. github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mozilla-services/yaml v0.0.0-20191106225358-5c216288813c h1:yE1NxRAZA3wF0laDWECtOe2J0tFjSHUI6MXXbMif+QY= @@ -162,12 +186,19 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/ory/dockertest v3.3.4+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -181,8 +212,10 @@ github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= @@ -195,10 +228,15 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/vektra/mockery v1.1.2/go.mod h1:VcfZjKaFOPO+MpN4ZvwPjs4c48lkq1o3Ym8yHZJu0jU= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a h1:N7VD+PwpJME2ZfQT8+ejxwA4Ow10IkGbU0MGf94ll8k= go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a/go.mod h1:YDKUvO0b//78PaaEro6CAPH6NqohCmL2Cwju5XI2HoE= go.mozilla.org/sops/v3 v3.5.0 h1:GpO9JRZhk6Kc+FVw5Q0vmnvDM6k956ZRh5tbG98T4XI= go.mozilla.org/sops/v3 v3.5.0/go.mod h1:9TY5PbZJtPWVHOUOvIetW31DLl2T7yNnoxmEGw/QNG0= +go.mozilla.org/sops/v3 v3.6.1 h1:SQXX2hXcZHBw4ZIPpvFbEns6hA7f2rE6kbN6E0heEkM= +go.mozilla.org/sops/v3 v3.6.1/go.mod h1:3KLncZfyE0cG/28CriTo0JJMARroeKToDIISBgN93xw= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -210,6 +248,8 @@ golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaE golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= @@ -219,6 +259,7 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -235,6 +276,9 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= @@ -245,6 +289,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -269,6 +314,7 @@ golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5f golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -283,6 +329,11 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200323144430-8dcfad9e016e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0 h1:9sdfJOzWlkqPltHAuzT2Cp+yrBeY1KRVYgms8soxMwM= @@ -318,12 +369,17 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/ini.v1 v1.44.0 h1:YRJzTUp0kSYWUVFF5XAbDFfyiqwsl0Vb9R8TVP5eRi0= gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/helm_wrapper.go b/helm_wrapper.go new file mode 100644 index 0000000..8bae607 --- /dev/null +++ b/helm_wrapper.go @@ -0,0 +1,203 @@ +package main + +import ( + "crypto/sha256" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path" + "regexp" + "sync" + "syscall" + + "go.mozilla.org/sops/v3/decrypt" +) + +type HelmWrapper struct { + Errors []error + errMutex sync.Mutex + + ExitCode int + + helmBinPath string + pipeWriterWaitGroup sync.WaitGroup + valuesArgRegexp *regexp.Regexp + temporaryDirectory string +} + +func NewHelmWrapper() (*HelmWrapper, error) { + c := HelmWrapper{} + + c.Errors = []error{} + c.pipeWriterWaitGroup = sync.WaitGroup{} + c.valuesArgRegexp = regexp.MustCompile("^(-f|--values)(?:=(.+))?$") + + // Determine the name of the helm binary by examining our binary name + helmBinName := "helm" + ourBinName := path.Base(os.Args[0]) + if ourBinName == "helm" || ourBinName == "helm2" || ourBinName == "helm3" { + helmBinName = fmt.Sprintf("_%s", ourBinName) + } + + var err error + c.helmBinPath, err = exec.LookPath(helmBinName) + if err != nil { + return nil, fmt.Errorf("failed to find Helm binary '%s': %s", helmBinName, err) + } + + return &c, nil +} + +func (c *HelmWrapper) errorf(msg string, a ...interface{}) error { + e := fmt.Errorf(msg, a...) + c.errMutex.Lock() + c.Errors = append(c.Errors, e) + c.errMutex.Unlock() + return e +} + +func (c *HelmWrapper) pipeWriter(outPipeName string, data []byte) { + c.pipeWriterWaitGroup.Add(1) + defer c.pipeWriterWaitGroup.Done() + + cleartextSecretFile, err := os.OpenFile(outPipeName, os.O_WRONLY, 0) + if err != nil { + c.errorf("failed to open cleartext secret pipe '%s' in pipe writer: %s", outPipeName, err) + return + } + defer func() { + err := cleartextSecretFile.Close() + if err != nil { + c.errorf("failed to close cleartext secret pipe '%s' in pipe writer: %s", outPipeName, err) + } + }() + + _, err = cleartextSecretFile.Write(data) + if err != nil { + c.errorf("failed to write cleartext secret to pipe '%s': %s", outPipeName, err) + } +} + +func (c *HelmWrapper) valuesArg(args []string) (string, string, error) { + valuesArgRegexpMatches := c.valuesArgRegexp.FindStringSubmatch(args[0]) + if valuesArgRegexpMatches == nil { + return "", "", nil + } + + var filename string + if len(valuesArgRegexpMatches[2]) > 0 { + // current arg is in the format --values=filename + filename = valuesArgRegexpMatches[2] + } else if len(args) > 1 { + // arg is in the format "-f filename" + filename = args[1] + } else { + return "", "", c.errorf("missing filename after -f or --values") + } + + cleartextSecretFilename := fmt.Sprintf("%s/%x", c.temporaryDirectory, sha256.Sum256([]byte(filename))) + + return filename, cleartextSecretFilename, nil +} + +func (c *HelmWrapper) replaceValueFileArg(args []string, cleartextSecretFilename string) { + valuesArgRegexpMatches := c.valuesArgRegexp.FindStringSubmatch(args[0]) + + // replace the filename with our pipe + if len(valuesArgRegexpMatches[2]) > 0 { + args[0] = fmt.Sprintf("%s=%s", valuesArgRegexpMatches[1], cleartextSecretFilename) + } else { + args[1] = cleartextSecretFilename + } +} + +func (c *HelmWrapper) mkTmpDir() (func(), error) { + var err error + c.temporaryDirectory, err = ioutil.TempDir("", fmt.Sprintf("%s.", path.Base(os.Args[0]))) + if err != nil { + return nil, c.errorf("failed to create temporary directory: %s", err) + } + return func() { + err := os.RemoveAll(c.temporaryDirectory) + if err != nil { + c.errorf("failed to remove temporary directory '%s': %s", c.temporaryDirectory, err) + } + }, nil +} + +func (c *HelmWrapper) mkPipe(cleartextSecretFilename string) (func(), error) { + err := syscall.Mkfifo(cleartextSecretFilename, 0600) + if err != nil { + return nil, c.errorf("failed to create cleartext secret pipe '%s': %s", cleartextSecretFilename, err) + } + return func() { + err := os.Remove(cleartextSecretFilename) + if err != nil { + c.errorf("failed to remove cleartext secret pipe '%s': %s", cleartextSecretFilename, err) + } + }, nil +} + +func (c *HelmWrapper) RunHelm() { + var err error + // Setup temporary directory and defer cleanup + cleanFn, err := c.mkTmpDir() + if err != nil { + return + } + defer cleanFn() + + // Loop through arguments looking for --values or -f. + // If we find a values argument, check if file has a sops section indicating it is encrypted. + // Setup a named pipe and write the decrypted data into that for helm. + for i := range os.Args { + args := os.Args[i:] + + filename, cleartextSecretFilename, err := c.valuesArg(args) + if err != nil { + return + } + if filename == "" { + continue + } + + encrypted, err := DetectSopsYaml(filename) + if err != nil { + c.errorf("error checking if file is encrypted: %s", err) + return + } + if !encrypted { + continue + } + + c.replaceValueFileArg(args, cleartextSecretFilename) + + cleartextSecrets, err := decrypt.File(filename, "yaml") + if err != nil { + c.errorf("failed to decrypt secret file '%s': %s", filename, err) + return + } + + cleanFn, err := c.mkPipe(cleartextSecretFilename) + if err != nil { + return + } + defer cleanFn() + + go c.pipeWriter(cleartextSecretFilename, cleartextSecrets) + } + defer c.pipeWriterWaitGroup.Wait() + + cmd := exec.Command(c.helmBinPath, os.Args[1:]...) + cmd.Env = os.Environ() + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + err = cmd.Run() + if err != nil { + c.ExitCode = cmd.ProcessState.ExitCode() + c.errorf("failed to run Helm: %s", err) + } +} diff --git a/helm_wrapper_test.go b/helm_wrapper_test.go new file mode 100644 index 0000000..d9226c5 --- /dev/null +++ b/helm_wrapper_test.go @@ -0,0 +1,111 @@ +package main + +import ( + "os" + "testing" +) + +var g_hw *HelmWrapper + +func init() { + g_hw, _ = NewHelmWrapper() +} + +func TestNewHelmWrapper(t *testing.T) { + // TODO +} + +func TestErrorf(t *testing.T) { + g_hw.Errors = []error{} + err := g_hw.errorf("test %s %d %t", "a", 1, true) + if g_hw.Errors[0] != err { + t.Errorf("errorf(test %%s %%d %%t, a, 1, true) = %s; want %s", g_hw.Errors[0], err) + } +} + +func TestPipeWriter(t *testing.T) { + // TODO +} + +func TestValuesArg(t *testing.T) { + res, _, err := g_hw.valuesArg([]string{"-f", "cat.yaml"}) + if res != "cat.yaml" || err != nil { + t.Errorf("valuesArg([]string{\"-f\", \"cat.yaml\"}) = %s, %s; want cat.yaml, ", res, "cat.yaml") + } + + res, _, err = g_hw.valuesArg([]string{"--values", "cat.yaml"}) + if res != "cat.yaml" || err != nil { + t.Errorf("valuesArg([]string{\"--valuse\", \"cat.yaml\"}) = %s, %s; want cat.yaml, ", res, "cat.yaml") + } + + res, _, err = g_hw.valuesArg([]string{"--values=cat.yaml"}) + if res != "cat.yaml" || err != nil { + t.Errorf("valuesArg([]string{\"--values=cat.yaml\"}) = %s, %s; want cat.yaml, ", res, "cat.yaml") + } +} + +func TestReplaceValueFileArg(t *testing.T) { + args := []string{"-f", "cat.yaml"} + g_hw.replaceValueFileArg(args, "dog.yaml") + if args[1] != "dog.yaml" { + t.Errorf("args[1] = %s; want dog.yaml", args[1]) + } + + args = []string{"--values", "cat.yaml"} + g_hw.replaceValueFileArg(args, "dog.yaml") + if args[1] != "dog.yaml" { + t.Errorf("args[1] = %s; want dog.yaml", args[1]) + } + + args = []string{"--values=cat.yaml"} + g_hw.replaceValueFileArg(args, "dog.yaml") + if args[0] != "--values=dog.yaml" { + t.Errorf("args[1] = %s; want --values=dog.yaml", args[1]) + } +} + +func TestMkTmpDir(t *testing.T) { + // ensure no errors + cleanFn, err := g_hw.mkTmpDir() + if err != nil { + t.Errorf("mkTmpDir error: %s", err) + } + + // dir exists + if _, err = os.Stat(g_hw.temporaryDirectory); err != nil { + t.Errorf("mkTmpDir stat error: %s", err) + } + + // ensure dir is deleted + cleanFn() + if _, err = os.Stat(g_hw.temporaryDirectory); err == nil { + t.Errorf("mkTmpDir cleanup func did not work") + } else if !os.IsNotExist(err) { + t.Errorf("mkTmpDir cleanup something went wrong: %s", err) + } +} + +func TestMkPipe(t *testing.T) { + // ensure no errors + cleanFn, err := g_hw.mkPipe("cat.yaml") + if err != nil { + t.Errorf("mkPipe error: %s", err) + } + + // file exists + if _, err = os.Stat("cat.yaml"); err != nil { + t.Errorf("mkPipe stat error: %s", err) + } + + // ensure file is deleted + cleanFn() + if _, err = os.Stat("cat.yaml"); err == nil { + t.Errorf("mkPipe cleanup func did not work") + } else if !os.IsNotExist(err) { + t.Errorf("mkPipe cleanup something went wrong: %s", err) + } +} + +func TestRunHelm(t *testing.T) { + // TODO +} diff --git a/main.go b/main.go index 31b0594..b0489f5 100644 --- a/main.go +++ b/main.go @@ -20,227 +20,22 @@ along with Helm Sops. If not, see . package main import ( - "crypto/sha256" "fmt" - "io/ioutil" "os" - "os/exec" - "path" - "regexp" - "sync" - "syscall" - - "go.mozilla.org/sops/v3/decrypt" -) - -var ( - valuesArgRegexp *regexp.Regexp - secretFilenameRegexp *regexp.Regexp ) -func init() { - valuesArgRegexp = regexp.MustCompile("^(-f|--values)(?:=(.+))?$") - secretFilenameRegexp = regexp.MustCompile("^((?:.*/)?secrets(?:(?:-|\\.|_).+)?.yaml)$") -} - -func runHelm() (errs []error) { - var helmPath string - var err error - - switch executableName := path.Base(os.Args[0]); executableName { - case "helm", "helm2", "helm3": - executableName = fmt.Sprintf("_%s", executableName) - - helmPath, err = exec.LookPath(executableName) - - if err != nil { - return append(errs, fmt.Errorf("failed to find Helm binary '%s'", executableName)) - } - default: - helmPath, err = exec.LookPath("helm") - - if err != nil { - return append(errs, fmt.Errorf("failed to find Helm binary 'helm'")) - } - } - - temporaryDirectory, err := ioutil.TempDir("", fmt.Sprintf("%s.", path.Base(os.Args[0]))) - - if err != nil { - return append(errs, fmt.Errorf("failed to create temporary directory: %s", err)) - } - - defer func() { - err := os.RemoveAll(temporaryDirectory) - - if err != nil { - errs = append(errs, fmt.Errorf("failed to remove temporary directory '%s': %s", temporaryDirectory, err)) - - return - } - }() - -loop: - for args := os.Args[1:]; len(args) > 0; args = args[1:] { - arg := args[0] - - if valuesArgRegexpMatches := valuesArgRegexp.FindStringSubmatch(arg); valuesArgRegexpMatches != nil { - var filename string - - switch { - case len(valuesArgRegexpMatches[2]) > 0: - filename = valuesArgRegexpMatches[2] - case len(args) > 1: - filename = args[1] - default: - break loop - } - - if secretFilenameRegexpMatches := secretFilenameRegexp.FindStringSubmatch(filename); secretFilenameRegexpMatches != nil { - secretFilename := secretFilenameRegexpMatches[0] - cleartextSecretFilename := fmt.Sprintf("%s/%x", temporaryDirectory, sha256.Sum256([]byte(secretFilename))) - - cleartextSecrets, err := decrypt.File(secretFilename, "yaml") - - if err != nil { - return append(errs, fmt.Errorf("failed to decrypt secret file '%s': %s", secretFilename, err)) - } - - err = syscall.Mkfifo(cleartextSecretFilename, 0600) - - if err != nil { - return append(errs, fmt.Errorf("failed to create cleartext secret pipe '%s': %s", cleartextSecretFilename, err)) - } - - defer func(cleartextSecretFilename string) { - err := os.Remove(cleartextSecretFilename) - - if err != nil { - errs = append(errs, fmt.Errorf("failed to remove cleartext secret pipe '%s': %s", cleartextSecretFilename, err)) - - return - } - }(cleartextSecretFilename) - - var errs1 []error - var errs2 []error - - pipeWriterWaitGroup := sync.WaitGroup{} - pipeCloseChannel := make(chan struct{}) - - defer func(errs1 *[]error, errs2 *[]error, pipeWriterWaitGroup *sync.WaitGroup, pipeCloseChannel chan struct{}) { - close(pipeCloseChannel) - - pipeWriterWaitGroup.Wait() - - errs = append(errs, *errs1...) - errs = append(errs, *errs2...) - }(&errs1, &errs2, &pipeWriterWaitGroup, pipeCloseChannel) - - pipeWriterWaitGroup.Add(2) - - pipeWriterUnlockedChannel := make(chan struct{}, 1) - - go func(cleartextSecretFilename string, cleartextSecrets []byte, errs *[]error, pipeWriterUnlockedChannel chan struct{}, pipeWriterWaitGroup *sync.WaitGroup) { - defer pipeWriterWaitGroup.Done() - - cleartextSecretFile, err := os.OpenFile(cleartextSecretFilename, os.O_WRONLY, 0) - - pipeWriterUnlockedChannel <- struct{}{} - - if err != nil { - *errs = append(*errs, fmt.Errorf("failed to open cleartext secret pipe '%s' in pipe writer: %s", cleartextSecretFilename, err)) - - return - } - - defer func() { - err := cleartextSecretFile.Close() - - if err != nil { - *errs = append(*errs, fmt.Errorf("failed to close cleartext secret pipe '%s' in pipe writer: %s", cleartextSecretFilename, err)) - - return - } - }() - - _, err = cleartextSecretFile.Write(cleartextSecrets) - - if err != nil { - *errs = append(*errs, fmt.Errorf("failed to write cleartext secret to pipe '%s': %s", cleartextSecretFilename, err)) - - return - } - }(cleartextSecretFilename, cleartextSecrets, &errs1, pipeWriterUnlockedChannel, &pipeWriterWaitGroup) - - go func(cleartextSecretFilename string, errs *[]error, pipeCloseChannel chan struct{}, pipeWriterUnlockedChannel chan struct{}, pipeWriterWaitGroup *sync.WaitGroup) { - defer pipeWriterWaitGroup.Done() - - <-pipeCloseChannel - - select { - case <-pipeWriterUnlockedChannel: - return - default: - } - - cleartextSecretFile, err := os.OpenFile(cleartextSecretFilename, os.O_RDWR, 0) - - if err != nil { - *errs = append(*errs, fmt.Errorf("failed to open cleartext secret pipe '%s' in pipe closer: %s", cleartextSecretFilename, err)) - - return - } - - <-pipeWriterUnlockedChannel - - defer func() { - err := cleartextSecretFile.Close() - - if err != nil { - *errs = append(*errs, fmt.Errorf("failed to close cleartext secret pipe '%s' in pipe closer: %s", cleartextSecretFilename, err)) - - return - } - }() - }(cleartextSecretFilename, &errs2, pipeCloseChannel, pipeWriterUnlockedChannel, &pipeWriterWaitGroup) - - if len(valuesArgRegexpMatches[2]) > 0 { - args[0] = fmt.Sprintf("%s=%s", valuesArgRegexpMatches[1], cleartextSecretFilename) - } else { - args[1] = cleartextSecretFilename - args = args[1:] - } - } - } - } - - cmd := exec.Command(helmPath, os.Args[1:]...) - - cmd.Env = os.Environ() - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - err = cmd.Run() - +func main() { + w, err := NewHelmWrapper() if err != nil { - return append(errs, fmt.Errorf("failed to run Helm: %s", err)) + fmt.Fprintf(os.Stderr, "[helm-sops] Error: %s\n", err) + os.Exit(1) } - return -} - -func main() { - errs := runHelm() - - exitCode := 0 + w.RunHelm() - for _, err := range errs { + for _, err := range w.Errors { fmt.Fprintf(os.Stderr, "[helm-sops] Error: %s\n", err) - - exitCode = 1 } - os.Exit(exitCode) + os.Exit(w.ExitCode) } diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..f8012b6 --- /dev/null +++ b/utils.go @@ -0,0 +1,24 @@ +package main + +import ( + "io/ioutil" + "gopkg.in/yaml.v3" +) + +func DetectSopsYaml(filename string) (bool, error) { + data, err := ioutil.ReadFile(filename) + if err != nil { + return false, err + } + + var sf map[string]interface{} + err = yaml.Unmarshal(data, &sf) + if err != nil { + return false, err + } + + if _, ok := sf["sops"]; ok { + return true, nil + } + return false, nil +} diff --git a/utils_test.go b/utils_test.go new file mode 100644 index 0000000..aef3a5a --- /dev/null +++ b/utils_test.go @@ -0,0 +1,43 @@ +package main + +import ( + "testing" + "io/ioutil" + "os" +) + +func TestDetectSopsFile(t *testing.T) { + + tmp, err := ioutil.TempFile("", "utils_test.*.yaml") + if err != nil { + t.Errorf("Error creating temp file") + } + defer os.Remove(tmp.Name()) + + // Test negative case + tmp.WriteString(`--- +secret: hello +`) + res, err := DetectSopsYaml(tmp.Name()) + if res != false || err != nil { + t.Errorf("DetectSopsYaml(tmp) = %t, %v; want false, ", res, err) + } + + // Test positive case + tmp.Seek(0, 0) + tmp.WriteString(`--- +secret: ENC[AES256_GCM,...] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + lastmodified: '2020-11-03T01:45:48Z' + pgp: [] + version: 3.6.1 +`) + res, err = DetectSopsYaml(tmp.Name()) + if res != true || err != nil { + t.Errorf("DetectSopsYaml(tmp) = %t, %v; want true, ", res, err) + } +}