From 83d485f4e935794021b44ed3ce36a7a0e32f63db Mon Sep 17 00:00:00 2001 From: Jacob Brewer Date: Mon, 11 Nov 2024 14:42:24 +0000 Subject: [PATCH 1/8] getting the vault pods dynamically from the service --- daemon.go | 2 +- example.vault-unseal.yaml | 4 ++ go.mod | 43 +++++++++++-- go.sum | 129 ++++++++++++++++++++++++++++++++++++-- main.go | 71 ++++++++++++++++++++- 5 files changed, 235 insertions(+), 14 deletions(-) diff --git a/daemon.go b/daemon.go index 77ad1d0..75f49be 100644 --- a/daemon.go +++ b/daemon.go @@ -24,7 +24,7 @@ func worker(ctx context.Context, wg *sync.WaitGroup, addr string) { var errDelay time.Duration for { - errDelay = (30 * time.Second) * time.Duration(errCount) + errDelay = (0 * time.Second) * time.Duration(errCount) if errDelay > conf.MaxCheckInterval { errDelay = conf.MaxCheckInterval } diff --git a/example.vault-unseal.yaml b/example.vault-unseal.yaml index e9f6c38..81f8389 100644 --- a/example.vault-unseal.yaml +++ b/example.vault-unseal.yaml @@ -17,6 +17,10 @@ vault_nodes: - https://1.2.3.5:8200 - https://1.2.3.6:8200 +# get the vault pods attached to a service, and use those as the vault nodes. +# this is useful if you're running vault in kubernetes. +vault_service: name.service.namespace.svc.cluster.local:some-port + # unseal tokens necessary to unseal any of the given vaults in the above node # list. # diff --git a/go.mod b/go.mod index eb251d3..76cbf16 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/lrstanley/vault-unseal -go 1.22 +go 1.22.0 + +toolchain go1.23.2 require ( github.com/apex/log v1.9.0 @@ -9,13 +11,29 @@ require ( github.com/joho/godotenv v1.5.1 gopkg.in/mail.v2 v2.3.1 gopkg.in/yaml.v3 v3.0.1 + k8s.io/api v0.31.2 + k8s.io/apimachinery v0.31.2 + k8s.io/client-go v0.31.2 ) require ( github.com/cenkalti/backoff/v3 v3.2.2 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-jose/go-jose/v4 v4.0.4 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.4 // indirect github.com/go-test/deep v1.0.7 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -25,17 +43,34 @@ require ( github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect github.com/hashicorp/go-sockaddr v1.0.6 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/kr/text v0.2.0 // indirect + github.com/imdario/mergo v0.3.11 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/x448/float16 v0.8.4 // indirect golang.org/x/crypto v0.26.0 // indirect golang.org/x/net v0.28.0 // indirect + golang.org/x/oauth2 v0.21.0 // indirect golang.org/x/sys v0.23.0 // indirect + golang.org/x/term v0.23.0 // indirect golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.6.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect - gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index 22c8861..b46ec86 100644 --- a/go.sum +++ b/go.sum @@ -9,24 +9,54 @@ github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTx github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E= github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M= github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= +github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -51,18 +81,31 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/hashicorp/vault/api v1.14.0 h1:Ah3CFLixD5jmjusOgm8grfN9M0d+Y8fVR2SW0K6pJLU= github.com/hashicorp/vault/api v1.14.0/go.mod h1:pV9YLxBGSz+cItFDd8Ii4G17waWOQ32zVjMWHe/cOqk= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4= github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -76,25 +119,45 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= +github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= 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/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/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= 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/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= @@ -104,42 +167,96 @@ github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao= github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 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.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +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= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk= gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 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.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0= +k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk= +k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw= +k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc= +k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/main.go b/main.go index 3f4c1ff..089b7c7 100644 --- a/main.go +++ b/main.go @@ -24,6 +24,11 @@ import ( flags "github.com/jessevdk/go-flags" _ "github.com/joho/godotenv/autoload" yaml "gopkg.in/yaml.v3" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" ) var ( @@ -33,9 +38,9 @@ var ( ) const ( - defaultCheckInterval = 30 * time.Second + defaultCheckInterval = 1 * time.Second defaultTimeout = 15 * time.Second - configRefreshInterval = 15 * time.Second + configRefreshInterval = 1 * time.Second minimumNodes = 3 ) @@ -62,6 +67,7 @@ type Config struct { Nodes []string `env:"NODES" long:"nodes" env-delim:"," description:"nodes to connect/provide tokens to (can be provided multiple times & uses comma-separated string for environment variable)" yaml:"vault_nodes"` TLSSkipVerify bool `env:"TLS_SKIP_VERIFY" long:"tls-skip-verify" description:"disables tls certificate validation: DO NOT DO THIS" yaml:"tls_skip_verify"` Tokens []string `env:"TOKENS" long:"tokens" env-delim:"," description:"tokens to provide to nodes (can be provided multiple times & uses comma-separated string for environment variable)" yaml:"unseal_tokens"` + VaultService string `env:"VAULT_SERVICE" long:"vault-service" env-delim:"," description:"service to get vault pods from" yaml:"vault_service"` NotifyMaxElapsed time.Duration `env:"NOTIFY_MAX_ELAPSED" long:"notify-max-elapsed" description:"max time before the notification can be queued before it is sent" yaml:"notify_max_elapsed"` NotifyQueueDelay time.Duration `env:"NOTIFY_QUEUE_DELAY" long:"notify-queue-delay" description:"time we queue the notification to allow as many notifications to be sent in one go (e.g. if no notification within X time, send all notifications)" yaml:"notify_queue_delay"` @@ -106,6 +112,53 @@ func newVault(addr string) (vault *vapi.Client) { return vault } +func getVaultPodsForService() ([]string, error) { + var ( + kubeconfig string + err error + ) + if kubeconfig = os.Getenv("KUBECONFIG"); kubeconfig == "" { + kubeconfig = os.ExpandEnv("$HOME/source/personal/b3-prod-1-config.yaml") + } + + config := new(rest.Config) + if config, err = clientcmd.BuildConfigFromFlags("", kubeconfig); err != nil { + return nil, fmt.Errorf("error building kubernetes config: %w", err) + } + + client := new(kubernetes.Clientset) + if client, err = kubernetes.NewForConfig(config); err != nil { + return nil, fmt.Errorf("error creating kubernetes client: %w", err) + } + + services, err := client.CoreV1().Services("vault").List(context.TODO(), metav1.ListOptions{ + LabelSelector: labels.Set{"app.kubernetes.io/name": "vault"}.String(), + }) + if err != nil { + return nil, fmt.Errorf("error getting services: %w", err) + } + + if len(services.Items) == 0 { + return nil, errors.New("no services found") + } + + podAddrs := make([]string, 0) + for _, service := range services.Items { + pods, err := client.CoreV1().Pods("vault").List(context.TODO(), metav1.ListOptions{ + LabelSelector: labels.Set{"app.kubernetes.io/name": "vault", "app.kubernetes.io/instance": service.Name}.String(), + }) + if err != nil { + return nil, fmt.Errorf("error getting pods for service %q: %w", service.Name, err) + } + + for _, pod := range pods.Items { + podAddrs = append(podAddrs, fmt.Sprintf("https://%s:%d", pod.Status.PodIP, 8200)) + } + } + + return podAddrs, nil +} + func main() { var err error if _, err = flags.Parse(conf); err != nil { @@ -170,6 +223,18 @@ func main() { var wg sync.WaitGroup ctx, cancel := context.WithCancel(context.Background()) + if len(conf.Nodes) == 0 { + conf.Nodes = make([]string, 0) + } + + // Get the vault pods that are attached to the given service. + podAddrs, err := getVaultPodsForService() + if err != nil { + logger.WithError(err).Fatal("error getting vault pods") + } else if len(podAddrs) > 0 { + conf.Nodes = append(conf.Nodes, podAddrs...) + } + for _, addr := range conf.Nodes { logger.WithField("addr", addr).Info("invoking worker") wg.Add(1) @@ -238,7 +303,7 @@ func readConfig(path string) error { conf.MaxCheckInterval = conf.CheckInterval * time.Duration(2) } - if len(conf.Nodes) < minimumNodes { + if len(conf.Nodes) < minimumNodes && conf.VaultService != "" { if !conf.AllowSingleNode { return fmt.Errorf("not enough nodes in node list (must have at least %d)", minimumNodes) } From 8dc5a63a0aee713921e7460814d3f4d43ee75b6d Mon Sep 17 00:00:00 2001 From: Jacob Brewer Date: Mon, 11 Nov 2024 15:35:09 +0000 Subject: [PATCH 2/8] addding a service monitor --- main.go | 177 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 148 insertions(+), 29 deletions(-) diff --git a/main.go b/main.go index 089b7c7..3494a52 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,7 @@ import ( "os" "os/signal" "runtime" + "strings" "sync" "syscall" "time" @@ -24,11 +25,11 @@ import ( flags "github.com/jessevdk/go-flags" _ "github.com/joho/godotenv/autoload" yaml "gopkg.in/yaml.v3" + core "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" ) var ( @@ -38,6 +39,7 @@ var ( ) const ( + defaultVaultName = "vault" defaultCheckInterval = 1 * time.Second defaultTimeout = 15 * time.Second configRefreshInterval = 1 * time.Second @@ -112,27 +114,29 @@ func newVault(addr string) (vault *vapi.Client) { return vault } -func getVaultPodsForService() ([]string, error) { - var ( - kubeconfig string - err error - ) - if kubeconfig = os.Getenv("KUBECONFIG"); kubeconfig == "" { - kubeconfig = os.ExpandEnv("$HOME/source/personal/b3-prod-1-config.yaml") +func getKubeClient() (*kubernetes.Clientset, error) { + kubeconfig, err := rest.InClusterConfig() + if err != nil { + logger.WithError(err).Warn("error getting in-cluster config, falling back to kubeconfig") + os.Exit(1) } - config := new(rest.Config) - if config, err = clientcmd.BuildConfigFromFlags("", kubeconfig); err != nil { - return nil, fmt.Errorf("error building kubernetes config: %w", err) + client := new(kubernetes.Clientset) + if client, err = kubernetes.NewForConfig(kubeconfig); err != nil { + logger.WithError(err).Fatal("error creating kubernetes client") } - client := new(kubernetes.Clientset) - if client, err = kubernetes.NewForConfig(config); err != nil { - return nil, fmt.Errorf("error creating kubernetes client: %w", err) + return client, nil +} + +func getVaultPodsForService() ([]string, error) { + client, err := getKubeClient() + if err != nil { + return nil, fmt.Errorf("error getting kubernetes client: %w", err) } - services, err := client.CoreV1().Services("vault").List(context.TODO(), metav1.ListOptions{ - LabelSelector: labels.Set{"app.kubernetes.io/name": "vault"}.String(), + services, err := client.CoreV1().Services(defaultVaultName).List(context.TODO(), metav1.ListOptions{ + LabelSelector: labels.Set{"app.kubernetes.io/name": defaultVaultName}.String(), }) if err != nil { return nil, fmt.Errorf("error getting services: %w", err) @@ -144,26 +148,132 @@ func getVaultPodsForService() ([]string, error) { podAddrs := make([]string, 0) for _, service := range services.Items { - pods, err := client.CoreV1().Pods("vault").List(context.TODO(), metav1.ListOptions{ - LabelSelector: labels.Set{"app.kubernetes.io/name": "vault", "app.kubernetes.io/instance": service.Name}.String(), + pods, err := client.CoreV1().Pods(defaultVaultName).List(context.TODO(), metav1.ListOptions{ + LabelSelector: labels.Set{ + "app.kubernetes.io/name": defaultVaultName, + "app.kubernetes.io/instance": service.Name, + }.String(), }) if err != nil { return nil, fmt.Errorf("error getting pods for service %q: %w", service.Name, err) } for _, pod := range pods.Items { - podAddrs = append(podAddrs, fmt.Sprintf("https://%s:%d", pod.Status.PodIP, 8200)) + podAddrs = append(podAddrs, getVaultAddr(service.Spec.Ports, pod.Status.PodIP)) } } return podAddrs, nil } +func getVaultAddr(service []core.ServicePort, ip string) string { + const targetScheme = "http" + + for _, port := range service { + if port.Name == targetScheme { + return fmt.Sprintf("%s://%s:%d", port.Protocol, ip, port.Port) + } + } + + return fmt.Sprintf("%s://%s:8200", targetScheme, ip) // Default to 8200 on http. +} + +// monitorService is a function that will monitor a service for changes to the pods attached to it. +func monitorService(ctx context.Context, workerIps *sync.Map, wg *sync.WaitGroup) { + client, err := getKubeClient() + if err != nil { + logger.WithError(err).Fatal("error getting kubernetes client") + } + + for { + select { + case <-ctx.Done(): + logger.Info("closing service monitor") + return + case <-time.After(5 * time.Second): + services, err := client.CoreV1().Services(defaultVaultName).List(context.TODO(), metav1.ListOptions{ + LabelSelector: labels.Set{ + "app.kubernetes.io/name": defaultVaultName, + }.String(), + }) + if err != nil { + logger.WithError(err).Error("error getting services") + continue + } + + if len(services.Items) == 0 { + logger.Warn("no services found") + continue + } + + fmt.Println(workerIps) + + svcName := strings.Split(conf.VaultService, ".")[1] + for _, service := range services.Items { + if service.Name != svcName { + continue + } + + pods, err := client.CoreV1().Pods(defaultVaultName).List(context.TODO(), metav1.ListOptions{ + LabelSelector: labels.Set{ + "app.kubernetes.io/name": defaultVaultName, + "app.kubernetes.io/instance": service.Name, + }.String(), + }) + if err != nil { + logger.WithError(err).Errorf("error getting pods for service %q", service.Name) + continue + } + + // Check for new pods. + for _, pod := range pods.Items { + addr := getVaultAddr(service.Spec.Ports, pod.Status.PodIP) + if _, ok := workerIps.Load(addr); !ok { + logger.WithField("addr", addr).Info("adding worker") + wg.Add(1) + workerCtx, workerCancel := context.WithCancel(ctx) + workerIps.Store(addr, workerCancel) + go worker(workerCtx, wg, addr) + } + } + + // Check for removed pods. + workerIps.Range(func(key, value interface{}) bool { + addr, ok := key.(string) + if !ok { + // This should never happen. Do not stop the loop. + return true + } + + found := false + for _, pod := range pods.Items { + podAddr := getVaultAddr(service.Spec.Ports, pod.Status.PodIP) + if addr == podAddr { + found = true + break + } + } + + if !found { + logger.WithField("addr", addr).Info("removing worker") + value.(context.CancelFunc)() + workerIps.Delete(addr) + } + + return true + }) + } + + fmt.Println(workerIps) + } + } +} + func main() { var err error if _, err = flags.Parse(conf); err != nil { var ferr *flags.Error - if errors.As(err, &ferr) && ferr.Type == flags.ErrHelp { + if errors.As(err, &ferr) && errors.Is(ferr.Type, flags.ErrHelp) { os.Exit(0) } os.Exit(1) @@ -182,13 +292,13 @@ func main() { initLogger.Level = log.MustParseLevel(conf.Log.Level) } - logWriters := []io.Writer{} + logWriters := make([]io.Writer, 0) if conf.Log.Path != "" { var logFileWriter *os.File logFileWriter, err = os.OpenFile(conf.Log.Path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o666) if err != nil { - fmt.Fprintf(os.Stderr, "error opening log file %q: %v", conf.Log.Path, err) + _, _ = fmt.Fprintf(os.Stderr, "error opening log file %q: %v", conf.Log.Path, err) os.Exit(1) } defer logFileWriter.Close() @@ -228,17 +338,26 @@ func main() { } // Get the vault pods that are attached to the given service. - podAddrs, err := getVaultPodsForService() - if err != nil { - logger.WithError(err).Fatal("error getting vault pods") - } else if len(podAddrs) > 0 { - conf.Nodes = append(conf.Nodes, podAddrs...) + if conf.VaultService != "" { + podAddrs, err := getVaultPodsForService() + if err != nil { + logger.WithError(err).Fatal("error getting vault pods") + } else if len(podAddrs) > 0 { + conf.Nodes = append(conf.Nodes, podAddrs...) + } } + workers := new(sync.Map) for _, addr := range conf.Nodes { logger.WithField("addr", addr).Info("invoking worker") wg.Add(1) - go worker(ctx, &wg, addr) + workerCtx, workerCancel := context.WithCancel(ctx) + workers.Store(addr, workerCancel) + go worker(workerCtx, &wg, addr) + } + + if conf.VaultService != "" { + go monitorService(ctx, workers, &wg) } go notifier(ctx, &wg) @@ -303,7 +422,7 @@ func readConfig(path string) error { conf.MaxCheckInterval = conf.CheckInterval * time.Duration(2) } - if len(conf.Nodes) < minimumNodes && conf.VaultService != "" { + if len(conf.Nodes) < minimumNodes && conf.VaultService == "" { if !conf.AllowSingleNode { return fmt.Errorf("not enough nodes in node list (must have at least %d)", minimumNodes) } From 1d9517f076e3237c2f749dab2151f38ccfb1eb2a Mon Sep 17 00:00:00 2001 From: Jacob Brewer Date: Mon, 11 Nov 2024 15:37:03 +0000 Subject: [PATCH 3/8] Updating comments --- main.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/main.go b/main.go index 3494a52..3e83674 100644 --- a/main.go +++ b/main.go @@ -114,6 +114,12 @@ func newVault(addr string) (vault *vapi.Client) { return vault } +// getKubeClient returns a kubernetes clientset. +// +// This function will attempt to get the in-cluster config. The ServiceAccount requires the +// following permissions: +// - `get` on `services` in the `default` namespace +// - `get` on `pods` in the `default` namespace func getKubeClient() (*kubernetes.Clientset, error) { kubeconfig, err := rest.InClusterConfig() if err != nil { From 10356c0192b2d4b69f0e5522d43c8b408c186614 Mon Sep 17 00:00:00 2001 From: Jacob Brewer Date: Mon, 11 Nov 2024 15:38:21 +0000 Subject: [PATCH 4/8] reverting to defaults --- daemon.go | 2 +- main.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/daemon.go b/daemon.go index 75f49be..77ad1d0 100644 --- a/daemon.go +++ b/daemon.go @@ -24,7 +24,7 @@ func worker(ctx context.Context, wg *sync.WaitGroup, addr string) { var errDelay time.Duration for { - errDelay = (0 * time.Second) * time.Duration(errCount) + errDelay = (30 * time.Second) * time.Duration(errCount) if errDelay > conf.MaxCheckInterval { errDelay = conf.MaxCheckInterval } diff --git a/main.go b/main.go index 3e83674..f9b0f4f 100644 --- a/main.go +++ b/main.go @@ -40,9 +40,9 @@ var ( const ( defaultVaultName = "vault" - defaultCheckInterval = 1 * time.Second + defaultCheckInterval = 30 * time.Second defaultTimeout = 15 * time.Second - configRefreshInterval = 1 * time.Second + configRefreshInterval = 15 * time.Second minimumNodes = 3 ) From cccb4eb672cd693d680a6bcacc7995e704c10361 Mon Sep 17 00:00:00 2001 From: Jacob Brewer Date: Mon, 11 Nov 2024 15:40:33 +0000 Subject: [PATCH 5/8] using consts --- main.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index f9b0f4f..a53470f 100644 --- a/main.go +++ b/main.go @@ -44,6 +44,9 @@ const ( defaultTimeout = 15 * time.Second configRefreshInterval = 15 * time.Second minimumNodes = 3 + + appNameLabel = "app.kubernetes.io/name" + instanceLabel = "app.kubernetes.io/instance" ) // Config is a combo of the flags passed to the cli and the configuration file (if used). @@ -142,7 +145,9 @@ func getVaultPodsForService() ([]string, error) { } services, err := client.CoreV1().Services(defaultVaultName).List(context.TODO(), metav1.ListOptions{ - LabelSelector: labels.Set{"app.kubernetes.io/name": defaultVaultName}.String(), + LabelSelector: labels.Set{ + appNameLabel: defaultVaultName, + }.String(), }) if err != nil { return nil, fmt.Errorf("error getting services: %w", err) @@ -156,8 +161,8 @@ func getVaultPodsForService() ([]string, error) { for _, service := range services.Items { pods, err := client.CoreV1().Pods(defaultVaultName).List(context.TODO(), metav1.ListOptions{ LabelSelector: labels.Set{ - "app.kubernetes.io/name": defaultVaultName, - "app.kubernetes.io/instance": service.Name, + appNameLabel: defaultVaultName, + instanceLabel: service.Name, }.String(), }) if err != nil { @@ -199,7 +204,7 @@ func monitorService(ctx context.Context, workerIps *sync.Map, wg *sync.WaitGroup case <-time.After(5 * time.Second): services, err := client.CoreV1().Services(defaultVaultName).List(context.TODO(), metav1.ListOptions{ LabelSelector: labels.Set{ - "app.kubernetes.io/name": defaultVaultName, + appNameLabel: defaultVaultName, }.String(), }) if err != nil { @@ -222,8 +227,8 @@ func monitorService(ctx context.Context, workerIps *sync.Map, wg *sync.WaitGroup pods, err := client.CoreV1().Pods(defaultVaultName).List(context.TODO(), metav1.ListOptions{ LabelSelector: labels.Set{ - "app.kubernetes.io/name": defaultVaultName, - "app.kubernetes.io/instance": service.Name, + appNameLabel: defaultVaultName, + instanceLabel: service.Name, }.String(), }) if err != nil { From 0b0f6835f5485549dfd1f183148c2150dc169b5f Mon Sep 17 00:00:00 2001 From: Jacob Brewer Date: Mon, 11 Nov 2024 16:11:25 +0000 Subject: [PATCH 6/8] Using a ticker --- main.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/main.go b/main.go index a53470f..8c453b7 100644 --- a/main.go +++ b/main.go @@ -39,11 +39,12 @@ var ( ) const ( - defaultVaultName = "vault" - defaultCheckInterval = 30 * time.Second - defaultTimeout = 15 * time.Second - configRefreshInterval = 15 * time.Second - minimumNodes = 3 + defaultVaultName = "vault" + defaultCheckInterval = 30 * time.Second + defaultTimeout = 15 * time.Second + configRefreshInterval = 15 * time.Second + defaultMonitoringInterval = configRefreshInterval / 2 + minimumNodes = 3 appNameLabel = "app.kubernetes.io/name" instanceLabel = "app.kubernetes.io/instance" @@ -196,12 +197,14 @@ func monitorService(ctx context.Context, workerIps *sync.Map, wg *sync.WaitGroup logger.WithError(err).Fatal("error getting kubernetes client") } - for { + ticker := time.NewTicker(defaultMonitoringInterval) + + for range ticker.C { select { case <-ctx.Done(): logger.Info("closing service monitor") return - case <-time.After(5 * time.Second): + default: services, err := client.CoreV1().Services(defaultVaultName).List(context.TODO(), metav1.ListOptions{ LabelSelector: labels.Set{ appNameLabel: defaultVaultName, From 1c8519f7a48b3e02668729944121aca7f2ad37a9 Mon Sep 17 00:00:00 2001 From: Jacob Brewer Date: Mon, 11 Nov 2024 16:20:04 +0000 Subject: [PATCH 7/8] using anmespace --- main.go | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/main.go b/main.go index 8c453b7..ca18da6 100644 --- a/main.go +++ b/main.go @@ -48,6 +48,8 @@ const ( appNameLabel = "app.kubernetes.io/name" instanceLabel = "app.kubernetes.io/instance" + + defaultNamespace = "vault" ) // Config is a combo of the flags passed to the cli and the configuration file (if used). @@ -122,13 +124,12 @@ func newVault(addr string) (vault *vapi.Client) { // // This function will attempt to get the in-cluster config. The ServiceAccount requires the // following permissions: -// - `get` on `services` in the `default` namespace -// - `get` on `pods` in the `default` namespace +// - `get` on `services` in all namespaces +// - `get` on `pods` in all namespaces func getKubeClient() (*kubernetes.Clientset, error) { kubeconfig, err := rest.InClusterConfig() if err != nil { - logger.WithError(err).Warn("error getting in-cluster config, falling back to kubeconfig") - os.Exit(1) + logger.WithError(err).Fatal("error getting in-cluster config") } client := new(kubernetes.Clientset) @@ -145,7 +146,7 @@ func getVaultPodsForService() ([]string, error) { return nil, fmt.Errorf("error getting kubernetes client: %w", err) } - services, err := client.CoreV1().Services(defaultVaultName).List(context.TODO(), metav1.ListOptions{ + services, err := client.CoreV1().Services(getNamespaceFromConfig()).List(context.TODO(), metav1.ListOptions{ LabelSelector: labels.Set{ appNameLabel: defaultVaultName, }.String(), @@ -160,7 +161,7 @@ func getVaultPodsForService() ([]string, error) { podAddrs := make([]string, 0) for _, service := range services.Items { - pods, err := client.CoreV1().Pods(defaultVaultName).List(context.TODO(), metav1.ListOptions{ + pods, err := client.CoreV1().Pods(getNamespaceFromConfig()).List(context.TODO(), metav1.ListOptions{ LabelSelector: labels.Set{ appNameLabel: defaultVaultName, instanceLabel: service.Name, @@ -205,7 +206,7 @@ func monitorService(ctx context.Context, workerIps *sync.Map, wg *sync.WaitGroup logger.Info("closing service monitor") return default: - services, err := client.CoreV1().Services(defaultVaultName).List(context.TODO(), metav1.ListOptions{ + services, err := client.CoreV1().Services(getNamespaceFromConfig()).List(context.TODO(), metav1.ListOptions{ LabelSelector: labels.Set{ appNameLabel: defaultVaultName, }.String(), @@ -228,7 +229,7 @@ func monitorService(ctx context.Context, workerIps *sync.Map, wg *sync.WaitGroup continue } - pods, err := client.CoreV1().Pods(defaultVaultName).List(context.TODO(), metav1.ListOptions{ + pods, err := client.CoreV1().Pods(getNamespaceFromConfig()).List(context.TODO(), metav1.ListOptions{ LabelSelector: labels.Set{ appNameLabel: defaultVaultName, instanceLabel: service.Name, @@ -283,6 +284,22 @@ func monitorService(ctx context.Context, workerIps *sync.Map, wg *sync.WaitGroup } } +// getNamespaceFromConfig returns the namespace from the config. +// +// E.g. name.service.namespace.svc.cluster.local:some-port +func getNamespaceFromConfig() string { + if conf.VaultService == "" { + return defaultNamespace + } + + elems := strings.Split(conf.VaultService, ".") + if len(elems) < 3 { + return defaultNamespace + } + + return elems[2] +} + func main() { var err error if _, err = flags.Parse(conf); err != nil { From 805c9e58004a3e11affeae87997dcf4532044349 Mon Sep 17 00:00:00 2001 From: Jacob Brewer Date: Mon, 11 Nov 2024 16:28:34 +0000 Subject: [PATCH 8/8] replacing todos with contexts --- main.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main.go b/main.go index ca18da6..05a8c8f 100644 --- a/main.go +++ b/main.go @@ -146,7 +146,7 @@ func getVaultPodsForService() ([]string, error) { return nil, fmt.Errorf("error getting kubernetes client: %w", err) } - services, err := client.CoreV1().Services(getNamespaceFromConfig()).List(context.TODO(), metav1.ListOptions{ + services, err := client.CoreV1().Services(getNamespaceFromConfig()).List(context.Background(), metav1.ListOptions{ LabelSelector: labels.Set{ appNameLabel: defaultVaultName, }.String(), @@ -161,7 +161,7 @@ func getVaultPodsForService() ([]string, error) { podAddrs := make([]string, 0) for _, service := range services.Items { - pods, err := client.CoreV1().Pods(getNamespaceFromConfig()).List(context.TODO(), metav1.ListOptions{ + pods, err := client.CoreV1().Pods(getNamespaceFromConfig()).List(context.Background(), metav1.ListOptions{ LabelSelector: labels.Set{ appNameLabel: defaultVaultName, instanceLabel: service.Name, @@ -206,7 +206,7 @@ func monitorService(ctx context.Context, workerIps *sync.Map, wg *sync.WaitGroup logger.Info("closing service monitor") return default: - services, err := client.CoreV1().Services(getNamespaceFromConfig()).List(context.TODO(), metav1.ListOptions{ + services, err := client.CoreV1().Services(getNamespaceFromConfig()).List(context.Background(), metav1.ListOptions{ LabelSelector: labels.Set{ appNameLabel: defaultVaultName, }.String(), @@ -229,7 +229,7 @@ func monitorService(ctx context.Context, workerIps *sync.Map, wg *sync.WaitGroup continue } - pods, err := client.CoreV1().Pods(getNamespaceFromConfig()).List(context.TODO(), metav1.ListOptions{ + pods, err := client.CoreV1().Pods(getNamespaceFromConfig()).List(context.Background(), metav1.ListOptions{ LabelSelector: labels.Set{ appNameLabel: defaultVaultName, instanceLabel: service.Name,