diff --git a/CHANGELOG.md b/CHANGELOG.md index 18885d3..b7c386b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- fixed `delete-before-apply` annotation behaviour to call namespace-scoped API -- update to go 1.22.4 +### Changed + +- complete rewrite of the cli +- changed interpolation implementation for future improvements +- use configmap instead of secret as inventory storage +- update to go 1.22.5 + +### Added + +- wait for resource status after apply ## [1.2.3] - 2023-08-24 diff --git a/go.mod b/go.mod index 346e95f..8861890 100644 --- a/go.mod +++ b/go.mod @@ -1,50 +1,57 @@ module github.com/mia-platform/mlp -go 1.22.4 +go 1.22.5 require ( github.com/MakeNowJust/heredoc/v2 v2.0.1 github.com/blang/semver/v4 v4.0.0 github.com/distribution/reference v0.6.0 + github.com/external-secrets/external-secrets v0.8.11 github.com/go-logr/logr v1.4.2 github.com/go-logr/stdr v1.2.2 - github.com/mia-platform/jpl v0.3.0 + github.com/mia-platform/jpl v0.4.0 github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.9.0 - k8s.io/api v0.28.11 - k8s.io/apimachinery v0.28.11 - k8s.io/cli-runtime v0.28.11 - k8s.io/client-go v0.28.11 - k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 - sigs.k8s.io/kustomize/api v0.17.2 - sigs.k8s.io/kustomize/kyaml v0.17.1 + k8s.io/api v0.28.12 + k8s.io/apimachinery v0.28.12 + k8s.io/cli-runtime v0.28.12 + k8s.io/client-go v0.28.12 + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 + sigs.k8s.io/kustomize/api v0.17.3 + sigs.k8s.io/kustomize/kyaml v0.17.2 sigs.k8s.io/yaml v1.4.0 ) require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // 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/evanphx/json-patch v5.6.0+incompatible // indirect + github.com/evanphx/json-patch/v5 v5.7.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-errors/errors v1.4.2 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonpointer v0.20.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.0.1 // 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/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.3.1 // indirect + github.com/google/uuid v1.4.0 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect - github.com/imdario/mergo v0.3.6 // indirect + github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect @@ -53,24 +60,32 @@ require ( github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.17.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/xlab/treeprint v1.2.0 // indirect go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect + golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb // indirect golang.org/x/net v0.23.0 // indirect - golang.org/x/oauth2 v0.8.0 // indirect + golang.org/x/oauth2 v0.15.0 // indirect golang.org/x/sync v0.5.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.3.0 // indirect - google.golang.org/appengine v1.6.7 // indirect + golang.org/x/time v0.5.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/appengine v1.6.8 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.28.11 // indirect - k8s.io/klog/v2 v2.100.1 // indirect - k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect + k8s.io/apiextensions-apiserver v0.28.12 // indirect + k8s.io/component-base v0.28.12 // indirect + k8s.io/klog/v2 v2.110.1 // indirect + k8s.io/kube-openapi v0.0.0-20231206194836-bf4651e18aa8 // indirect + sigs.k8s.io/controller-runtime v0.16.6 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect ) diff --git a/go.sum b/go.sum index fad3b89..09c260c 100644 --- a/go.sum +++ b/go.sum @@ -2,9 +2,13 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/MakeNowJust/heredoc/v2 v2.0.1 h1:rlCHh70XXXv7toz95ajQWOWQnN4WNLt0TdpZYIR/J6A= github.com/MakeNowJust/heredoc/v2 v2.0.1/go.mod h1:6/2Abh5s+hc3g9nbWLe9ObDIOhaRrqsyY9MWy+4JdRM= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -23,16 +27,25 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.7.0 h1:nJqP7uwL84RJInrohHfW0Fx3awjbm8qZeFv0nW9SYGc= +github.com/evanphx/json-patch/v5 v5.7.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/external-secrets/external-secrets v0.8.11 h1:HUtUPT6lDwlZBqYnjBhXt/WxP3Hv2wsZW2MNjIsJkzo= +github.com/external-secrets/external-secrets v0.8.11/go.mod h1:PG0WX4VF6mwclk51fpm1eQviMKYUI/CA0ICwyWAFYQE= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 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-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= +github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= 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= @@ -43,9 +56,10 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 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.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -53,6 +67,8 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= @@ -65,22 +81,23 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20231205033806-a5a03c77bf08 h1:PxlBVtIFHR/mtWk2i0gTEdCz+jBnqiuHNSki0epDbVs= +github.com/google/pprof v0.0.0-20231205033806-a5a03c77bf08/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -100,8 +117,10 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhn github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mia-platform/jpl v0.3.0 h1:CCBlz6nPTxbbwEmWUMz7fE1063IjgngEKGyddld19T8= -github.com/mia-platform/jpl v0.3.0/go.mod h1:T3T29QoV/bkaMU4mbCB2kJJVdPtR1rgESKHJRbnNz8c= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= +github.com/mia-platform/jpl v0.4.0 h1:xlefS8UWNyRqFkZIWV6FP585mKDJQxD2rJv+wqb73Zs= +github.com/mia-platform/jpl v0.4.0/go.mod h1:to9a4EDI2riTwZRQvkkJ8SeVqfLraNWY4VUbrBu6Sp4= 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= @@ -111,10 +130,10 @@ github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/ github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= 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/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE= -github.com/onsi/ginkgo/v2 v2.9.4/go.mod h1:gCQYp2Q+kSoIj7ykSVb9nskRSsR6PUj4AiLywzIhbKM= -github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= -github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs= +github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= +github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= +github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= @@ -124,7 +143,15 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE 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/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -150,58 +177,75 @@ github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 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.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8= +golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 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/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 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.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= +golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/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/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= 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.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -210,16 +254,19 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn 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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= 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= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= @@ -234,6 +281,8 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -251,29 +300,33 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.28.11 h1:2qFr3jSpjy/9QirmlRP0LZeomexuwyRlE8CWUn9hPNY= -k8s.io/api v0.28.11/go.mod h1:nQSGyxQ2sbS73i1zEJyaktFvFfD72z/7nU+LqxzNnXk= -k8s.io/apiextensions-apiserver v0.28.11 h1:EbHGxLgPVupsobUhwY9kLYES0yRLn65fV/aAuW52+xo= -k8s.io/apiextensions-apiserver v0.28.11/go.mod h1:eKJx8UVKgeaqFZWdU39Q8bNOnv21aFK55+riFdUhAJg= -k8s.io/apimachinery v0.28.11 h1:Ovrx7IOkKSgFJn8+d5BXOC7POzP4i7kOAVlx46iRQ04= -k8s.io/apimachinery v0.28.11/go.mod h1:zUG757HaKs6Dc3iGtKjzIpBfqTM4yiRsEe3/E7NX15o= -k8s.io/cli-runtime v0.28.11 h1:aSTxYi+ALwhhv6GV/SNfnSdw7PTQF2rdRgw+W9LbAIM= -k8s.io/cli-runtime v0.28.11/go.mod h1:1QHA0FKP6G/heylN+AUSncOh3HUreL/L0XTvySWBCCI= -k8s.io/client-go v0.28.11 h1:YHtF6Bg4/DdYHHsx6f5Ti/0giwoo19t3DbBYYmo9xks= -k8s.io/client-go v0.28.11/go.mod h1:yi2BW8PQhFDLGmZ3WbyTJYX5J8YM6n3WUj1fvL7pJ4g= -k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= -k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= -k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= -k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak= -k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/api v0.28.12 h1:C2hpsaso18pqn0Dmkfnbv/YCctozTC3KGGuZ6bF7zhQ= +k8s.io/api v0.28.12/go.mod h1:qjswI+whxvf9LAKD4sEYHfy+WgHGWeH+H5sCRQMwZAQ= +k8s.io/apiextensions-apiserver v0.28.12 h1:6GA64rylk5q0mbXfHHFVgfL1jx/4p6RU+Y+ni2DUuZc= +k8s.io/apiextensions-apiserver v0.28.12/go.mod h1:Len29ySvb/fnrXvioTxg2l6iFi97B53Bm3/jBMBllCE= +k8s.io/apimachinery v0.28.12 h1:VepMEVOi9o7L/4wMAXJq+3BK9tqBIeerTB+HSOTKeo0= +k8s.io/apimachinery v0.28.12/go.mod h1:zUG757HaKs6Dc3iGtKjzIpBfqTM4yiRsEe3/E7NX15o= +k8s.io/cli-runtime v0.28.12 h1:Skn6b49B+khoscC85o+aVIT9u8uDW2/3KNP4ZLCg7jA= +k8s.io/cli-runtime v0.28.12/go.mod h1:BUnm03LagRdLl+YeOa9BEspaqNq0oy5aWlku8yLY5/s= +k8s.io/client-go v0.28.12 h1:li7iRPRQF3vDki6gTxT/kXWJvw3BkJSdjVPVhDTZQec= +k8s.io/client-go v0.28.12/go.mod h1:yEzH2Z+nEGlrnKyHJWcJsbOr5tGdIj04dj1TVQOg0wE= +k8s.io/component-base v0.28.12 h1:ZNq6QFFGCPjaAzWqYHaQRoAY5seoK3vP0pZOjgxOzNc= +k8s.io/component-base v0.28.12/go.mod h1:8zI5TmGuHX6R5Lay61Ox7wb+dsEENl0NBmVSiHMQu1c= +k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= +k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= +k8s.io/kube-openapi v0.0.0-20231206194836-bf4651e18aa8 h1:vzKzxN5uyJZLY8HL1/OovW7BJefnsBIWt8T7Gjh2boQ= +k8s.io/kube-openapi v0.0.0-20231206194836-bf4651e18aa8/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +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/controller-runtime v0.16.6 h1:FiXwTuFF5ZJKmozfP2Z0j7dh6kmxP4Ou1KLfxgKKC3I= +sigs.k8s.io/controller-runtime v0.16.6/go.mod h1:+dQzkZxnylD0u49e0a+7AR+vlibEBaThmPca7lTyUsI= 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/kustomize/api v0.17.2 h1:E7/Fjk7V5fboiuijoZHgs4aHuexi5Y2loXlVOAVAG5g= -sigs.k8s.io/kustomize/api v0.17.2/go.mod h1:UWTz9Ct+MvoeQsHcJ5e+vziRRkwimm3HytpZgIYqye0= -sigs.k8s.io/kustomize/kyaml v0.17.1 h1:TnxYQxFXzbmNG6gOINgGWQt09GghzgTP6mIurOgrLCQ= -sigs.k8s.io/kustomize/kyaml v0.17.1/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/kustomize/api v0.17.3 h1:6GCuHSsxq7fN5yhF2XrC+AAr8gxQwhexgHflOAD/JJU= +sigs.k8s.io/kustomize/api v0.17.3/go.mod h1:TuDH4mdx7jTfK61SQ/j1QZM/QWR+5rmEiNjvYlhzFhc= +sigs.k8s.io/kustomize/kyaml v0.17.2 h1:+AzvoJUY0kq4QAhH/ydPHHMRLijtUKiyVyh7fOSshr0= +sigs.k8s.io/kustomize/kyaml v0.17.2/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U= +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/pkg/cmd/deploy/deploy.go b/pkg/cmd/deploy/deploy.go index 0b40fd8..bdb1a22 100644 --- a/pkg/cmd/deploy/deploy.go +++ b/pkg/cmd/deploy/deploy.go @@ -30,6 +30,7 @@ import ( "github.com/mia-platform/jpl/pkg/generator" "github.com/mia-platform/jpl/pkg/resourcereader" "github.com/mia-platform/jpl/pkg/util" + "github.com/mia-platform/mlp/pkg/extensions" "github.com/spf13/cobra" "github.com/spf13/pflag" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -56,7 +57,7 @@ const ( inputPathsFlagUsage = "the files and/or folders that contain the configurations to apply. Use '-' for reading from stdin" deployTypeFlagName = "deploy-type" - deployTypeDefaultValue = deployAll + deployTypeDefaultValue = extensions.DeployAll deployTypeFlagUsage = "set the deployment mode (accepted values: deploy_all, smart_deploy)" forceDeployFlagName = "force-deploy-when-no-semver" @@ -74,6 +75,13 @@ const ( stdinToken = "-" fieldManager = "mlp" inventoryName = "eu.mia-platform.mlp" + + jobGeneratorLabel = "mia-platform.eu/autocreate" + jobGeneratorValue = "true" +) + +var ( + validDeployTypeValues = []string{extensions.DeployAll, extensions.DeploySmart} ) // Flags contains all the flags for the `deploy` command. They will be converted to Options @@ -230,10 +238,12 @@ func (o *Options) Run(ctx context.Context) error { WithInventory(inventory). WithGenerators(generator.NewJobGenerator(jobGeneratorLabel, jobGeneratorValue)). WithMutator( - NewDependenciesMutator(resources), - NewDeployMutator(o.deployType, o.forceDeploy, checksumFromData(deployIdentifier)), + extensions.NewDependenciesMutator(resources), + extensions.NewDeployMutator(o.deployType, o.forceDeploy, extensions.ChecksumFromData(deployIdentifier)), + extensions.NewExternalSecretsMutator(resources), ). - WithFilters(NewDeployOnceFilter()). + WithFilters(extensions.NewDeployOnceFilter()). + WithCustomStatusChecker(extensions.ExternalSecretStatusCheckers()). Build() if err != nil { return err diff --git a/pkg/cmd/deploy/deploy_test.go b/pkg/cmd/deploy/deploy_test.go index 1e064b9..cfa0473 100644 --- a/pkg/cmd/deploy/deploy_test.go +++ b/pkg/cmd/deploy/deploy_test.go @@ -29,6 +29,7 @@ import ( "testing" "time" + extsecv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" jplresource "github.com/mia-platform/jpl/pkg/resource" jpltesting "github.com/mia-platform/jpl/pkg/testing" "github.com/mia-platform/jpl/pkg/util" @@ -36,8 +37,10 @@ import ( "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" fcv1beta3 "k8s.io/api/flowcontrol/v1beta3" + "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/resource" dynamicfake "k8s.io/client-go/dynamic/fake" @@ -170,8 +173,18 @@ func TestRun(t *testing.T) { method: http.MethodPatch, expectedFilePath: filepath.Join(testdata, "expectations", "job.yaml"), }, + { + path: fmt.Sprintf("/namespaces/%s/externalsecrets/external-secret", namespace), + method: http.MethodPatch, + expectedFilePath: filepath.Join(testdata, "expectations", "external-secret.yaml"), + }, + { + path: fmt.Sprintf("/namespaces/%s/secretstores/secret-store", namespace), + method: http.MethodPatch, + expectedFilePath: filepath.Join(testdata, "expectations", "store.yaml"), + }, }, - expectedCallsNumber: 7, + expectedCallsNumber: 9, }, "error reading files": { options: &Options{ @@ -219,11 +232,22 @@ func TestRun(t *testing.T) { }), } tf.FakeDynamicClient = fakeDynamicClient - + mapper, err := tf.ToRESTMapper() + require.NoError(t, err) + crdGV := extsecv1beta1.SchemeGroupVersion + crdMapper := meta.NewDefaultRESTMapper([]schema.GroupVersion{crdGV}) + crdMapper.AddSpecific(extsecv1beta1.ExtSecretGroupVersionKind, + crdGV.WithResource("externalsecrets"), + crdGV.WithResource("externalsecret"), meta.RESTScopeNamespace) + crdMapper.AddSpecific(extsecv1beta1.SecretStoreGroupVersionKind, + crdGV.WithResource("secretstores"), + crdGV.WithResource("secretstore"), meta.RESTScopeNamespace) + mapper = meta.MultiRESTMapper([]meta.RESTMapper{mapper, crdMapper}) + tf.RESTMapper = mapper test.options.clientFactory = tf test.options.writer = stringBuilder - err := test.options.Run(ctx) + err = test.options.Run(ctx) t.Log(stringBuilder.String()) assert.Equal(t, test.expectedCallsNumber, callsCounter) @@ -340,7 +364,7 @@ func validationRoundTripper(t *testing.T, resources []*resourceValidation, r *ht require.NoError(t, err) cm := new(corev1.ConfigMap) require.NoError(t, runtime.DecodeInto(codec, bodyData, cm)) - assert.Equal(t, 5, len(cm.Data)) + assert.Equal(t, 7, len(cm.Data)) return &http.Response{ StatusCode: http.StatusOK, Header: jpltesting.DefaultHeaders(), @@ -389,7 +413,6 @@ func (rv *resourceValidation) validateBody(t *testing.T, body io.ReadCloser) []b } } - require.Equal(t, expected, obj) - + assert.Equal(t, expected, obj) return bodyData } diff --git a/pkg/cmd/deploy/testdata/expectations/deployment.yaml b/pkg/cmd/deploy/testdata/expectations/deployment.yaml index 431bdc4..4d5dd53 100644 --- a/pkg/cmd/deploy/testdata/expectations/deployment.yaml +++ b/pkg/cmd/deploy/testdata/expectations/deployment.yaml @@ -3,7 +3,8 @@ kind: Deployment metadata: name: example namespace: mlp-deploy-test - annotations: {} + annotations: + config.kubernetes.io/depends-on: external-secrets.io/namespaces/mlp-deploy-test/ExternalSecret/external-secret spec: selector: matchLabels: @@ -23,6 +24,12 @@ spec: limits: cpu: 500m memory: 128Mi + env: + - name: ENV + valueFrom: + secretKeyRef: + key: secret-key + name: external-secret volumes: - name: example configMap: diff --git a/pkg/cmd/deploy/testdata/expectations/external-secret.yaml b/pkg/cmd/deploy/testdata/expectations/external-secret.yaml new file mode 100644 index 0000000..f8a7028 --- /dev/null +++ b/pkg/cmd/deploy/testdata/expectations/external-secret.yaml @@ -0,0 +1,20 @@ +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: external-secret + namespace: mlp-deploy-test + annotations: + config.kubernetes.io/depends-on: external-secrets.io/namespaces/mlp-deploy-test/SecretStore/secret-store + mia-platform.eu/deploy-checksum: a2d1ace0489d09c0ca26a1ab8a8bc9b11e4365cb4f904c434565a59119f3eb15 +spec: + refreshInterval: 1h + secretStoreRef: + name: secret-store + target: + creationPolicy: Owner + data: + - secretKey: secret-key + remoteRef: + key: provider-key + version: provider-key-version + property: provider-key-property diff --git a/pkg/cmd/deploy/testdata/expectations/store.yaml b/pkg/cmd/deploy/testdata/expectations/store.yaml new file mode 100644 index 0000000..530a59a --- /dev/null +++ b/pkg/cmd/deploy/testdata/expectations/store.yaml @@ -0,0 +1,19 @@ +apiVersion: external-secrets.io/v1beta1 +kind: SecretStore +metadata: + name: secret-store + namespace: mlp-deploy-test + annotations: {} +spec: + provider: + aws: + service: SecretsManager + region: us-east-1 + auth: + secretRef: + accessKeyIDSecretRef: + name: awssm-secret + key: access-key + secretAccessKeySecretRef: + name: awssm-secret + key: secret-access-key diff --git a/pkg/cmd/deploy/testdata/resources/deployment.yaml b/pkg/cmd/deploy/testdata/resources/deployment.yaml index 129410f..2f4a9f3 100644 --- a/pkg/cmd/deploy/testdata/resources/deployment.yaml +++ b/pkg/cmd/deploy/testdata/resources/deployment.yaml @@ -20,6 +20,12 @@ spec: limits: memory: "128Mi" cpu: "500m" + env: + - name: ENV + valueFrom: + secretKeyRef: + key: secret-key + name: external-secret volumes: - name: example configMap: diff --git a/pkg/cmd/deploy/testdata/resources/external-secret.yaml b/pkg/cmd/deploy/testdata/resources/external-secret.yaml new file mode 100644 index 0000000..45515ac --- /dev/null +++ b/pkg/cmd/deploy/testdata/resources/external-secret.yaml @@ -0,0 +1,17 @@ +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: external-secret + namespace: mlp-deploy-test +spec: + refreshInterval: 1h + secretStoreRef: + name: secret-store + target: + creationPolicy: Owner + data: + - secretKey: secret-key + remoteRef: + key: provider-key + version: provider-key-version + property: provider-key-property diff --git a/pkg/cmd/deploy/testdata/resources/store.yaml b/pkg/cmd/deploy/testdata/resources/store.yaml new file mode 100644 index 0000000..a89f723 --- /dev/null +++ b/pkg/cmd/deploy/testdata/resources/store.yaml @@ -0,0 +1,18 @@ +apiVersion: external-secrets.io/v1beta1 +kind: SecretStore +metadata: + name: secret-store + namespace: mlp-deploy-test +spec: + provider: + aws: + service: SecretsManager + region: us-east-1 + auth: + secretRef: + accessKeyIDSecretRef: + name: awssm-secret + key: access-key + secretAccessKeySecretRef: + name: awssm-secret + key: secret-access-key diff --git a/pkg/cmd/deploy/dependenciesmutator.go b/pkg/extensions/dependenciesmutator.go similarity index 94% rename from pkg/cmd/deploy/dependenciesmutator.go rename to pkg/extensions/dependenciesmutator.go index 4284ae5..a2be5bf 100644 --- a/pkg/cmd/deploy/dependenciesmutator.go +++ b/pkg/extensions/dependenciesmutator.go @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package deploy +package extensions import ( "maps" @@ -92,7 +92,7 @@ func (m *dependenciesMutator) Mutate(obj *unstructured.Unstructured, _ cache.Rem return err } - annotations[checksumAnnotation] = checksumFromData(checksums) + annotations[checksumAnnotation] = ChecksumFromData(checksums) return unstructured.SetNestedStringMap(obj.Object, annotations, podAnnotationsFields...) } @@ -113,15 +113,15 @@ func checksumsFromConfigMap(obj *unstructured.Unstructured) map[string]string { totalData := make(map[string][]byte) maps.Copy(totalData, cm.BinaryData) for key, value := range cm.Data { - checksums[checksumObjectKey(configMapGK.Kind, obj.GetName(), obj.GetNamespace(), key)] = checksumFromData(value) + checksums[checksumObjectKey(configMapGK.Kind, obj.GetName(), obj.GetNamespace(), key)] = ChecksumFromData(value) totalData[key] = []byte(value) } for key, value := range cm.BinaryData { - checksums[checksumObjectKey(configMapGK.Kind, obj.GetName(), obj.GetNamespace(), key)] = checksumFromData(value) + checksums[checksumObjectKey(configMapGK.Kind, obj.GetName(), obj.GetNamespace(), key)] = ChecksumFromData(value) } - checksums[checksumObjectKey(configMapGK.Kind, obj.GetName(), obj.GetNamespace(), "")] = checksumFromData(totalData) + checksums[checksumObjectKey(configMapGK.Kind, obj.GetName(), obj.GetNamespace(), "")] = ChecksumFromData(totalData) return checksums } @@ -139,15 +139,15 @@ func checksumsFromSecret(obj *unstructured.Unstructured) map[string]string { totalData := make(map[string][]byte) maps.Copy(totalData, sec.Data) for key, value := range sec.Data { - checksums[checksumObjectKey(secretGK.Kind, obj.GetName(), obj.GetNamespace(), key)] = checksumFromData(value) + checksums[checksumObjectKey(secretGK.Kind, obj.GetName(), obj.GetNamespace(), key)] = ChecksumFromData(value) } for key, value := range sec.StringData { - checksums[checksumObjectKey(secretGK.Kind, obj.GetName(), obj.GetNamespace(), key)] = checksumFromData(value) + checksums[checksumObjectKey(secretGK.Kind, obj.GetName(), obj.GetNamespace(), key)] = ChecksumFromData(value) totalData[key] = []byte(value) } - checksums[checksumObjectKey(secretGK.Kind, obj.GetName(), obj.GetNamespace(), "")] = checksumFromData(totalData) + checksums[checksumObjectKey(secretGK.Kind, obj.GetName(), obj.GetNamespace(), "")] = ChecksumFromData(totalData) return checksums } diff --git a/pkg/cmd/deploy/dependenciesmutator_test.go b/pkg/extensions/dependenciesmutator_test.go similarity index 99% rename from pkg/cmd/deploy/dependenciesmutator_test.go rename to pkg/extensions/dependenciesmutator_test.go index cb1d38b..21f0339 100644 --- a/pkg/cmd/deploy/dependenciesmutator_test.go +++ b/pkg/extensions/dependenciesmutator_test.go @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package deploy +package extensions import ( "path/filepath" diff --git a/pkg/cmd/deploy/deploymutator.go b/pkg/extensions/deploymutator.go similarity index 92% rename from pkg/cmd/deploy/deploymutator.go rename to pkg/extensions/deploymutator.go index 11d529b..032eb56 100644 --- a/pkg/cmd/deploy/deploymutator.go +++ b/pkg/extensions/deploymutator.go @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package deploy +package extensions import ( "context" @@ -62,6 +62,8 @@ func (m *deployMutator) CanHandleResource(obj *metav1.PartialObjectMetadata) boo return true case podGK: return true + case extsecGK: + return true } return false @@ -69,6 +71,16 @@ func (m *deployMutator) CanHandleResource(obj *metav1.PartialObjectMetadata) boo // Mutate implement mutator.Interface interface func (m *deployMutator) Mutate(obj *unstructured.Unstructured, getter cache.RemoteResourceGetter) error { + if obj.GroupVersionKind().GroupKind() == extsecGK { + extSecAnnotationsFields := []string{"metadata", "annotations"} + annotations, err := annotationsFromUnstructuredFields(obj, extSecAnnotationsFields) + if err != nil { + return err + } + annotations[deployChecksumAnnotation] = m.identifier + return unstructured.SetNestedStringMap(obj.Object, annotations, extSecAnnotationsFields...) + } + podSpecFields, podAnnotationsFields, err := podFieldsForGroupKind(obj.GroupVersionKind()) if err != nil { return err @@ -77,12 +89,12 @@ func (m *deployMutator) Mutate(obj *unstructured.Unstructured, getter cache.Remo addAnnotation := false value := "" switch m.deployType { - case deploySmart: + case DeploySmart: addAnnotation, value, err = m.smartDeployAnnotation(obj, podSpecFields, podAnnotationsFields, getter) if err != nil { return err } - case deployAll: + case DeployAll: addAnnotation = true value = m.identifier } diff --git a/pkg/cmd/deploy/deploymutator_test.go b/pkg/extensions/deploymutator_test.go similarity index 95% rename from pkg/cmd/deploy/deploymutator_test.go rename to pkg/extensions/deploymutator_test.go index f88749d..df5d442 100644 --- a/pkg/cmd/deploy/deploymutator_test.go +++ b/pkg/extensions/deploymutator_test.go @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package deploy +package extensions import ( "context" @@ -33,7 +33,7 @@ import ( func TestNewDeployMutator(t *testing.T) { t.Parallel() - mutator := NewDeployMutator(deployAll, true, "identifier") + mutator := NewDeployMutator(DeployAll, true, "identifier") assert.NotNil(t, mutator) } @@ -115,41 +115,41 @@ func TestDeployMutatorMutate(t *testing.T) { }{ "deployment": { resource: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "deployment.yaml")), - deployType: deployAll, + deployType: DeployAll, expectedResult: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "expected-deployment.yaml")), }, "sts": { resource: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "sts.yaml")), - deployType: deploySmart, + deployType: DeploySmart, expectedResult: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "expected-sts.yaml")), }, "daemonset": { resource: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "daemonset.yaml")), - deployType: deploySmart, + deployType: DeploySmart, forceNoSemver: true, expectedResult: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "expected-daemonset.yaml")), }, "pod": { resource: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "pod.yaml")), - deployType: deploySmart, + deployType: DeploySmart, forceNoSemver: true, expectedResult: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "expected-pod.yaml")), }, "deployment smart deploy": { resource: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "deployment-smart.yaml")), - deployType: deploySmart, + deployType: DeploySmart, forceNoSemver: true, expectedResult: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "expected-deployment-smart.yaml")), }, "deployment smart deploy with remote annotation": { resource: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "deployment-smart-remote.yaml")), - deployType: deploySmart, + deployType: DeploySmart, forceNoSemver: true, expectedResult: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "expected-deployment-smart-remote.yaml")), }, "error getting resource from remote": { resource: remoteErrorObject, - deployType: deploySmart, + deployType: DeploySmart, expectedResult: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "error-remote.yaml")), expectedError: "error from remote", }, @@ -160,7 +160,7 @@ func TestDeployMutatorMutate(t *testing.T) { }, "wrong image string": { resource: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "wrong-image.yaml")), - deployType: deploySmart, + deployType: DeploySmart, forceNoSemver: true, expectedResult: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "wrong-image.yaml")), expectedError: `couldn't parse image name "busybox:sha256:5be7104a4306abe768359a5379e6050ef69a29e9a5f99fcf7f46d5f7e9ba29a2"`, diff --git a/pkg/cmd/deploy/deployoncefilter.go b/pkg/extensions/deployoncefilter.go similarity index 99% rename from pkg/cmd/deploy/deployoncefilter.go rename to pkg/extensions/deployoncefilter.go index 85d8027..c19d922 100644 --- a/pkg/cmd/deploy/deployoncefilter.go +++ b/pkg/extensions/deployoncefilter.go @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package deploy +package extensions import ( "context" diff --git a/pkg/cmd/deploy/deployoncefilter_test.go b/pkg/extensions/deployoncefilter_test.go similarity index 99% rename from pkg/cmd/deploy/deployoncefilter_test.go rename to pkg/extensions/deployoncefilter_test.go index b4a1241..c6a7659 100644 --- a/pkg/cmd/deploy/deployoncefilter_test.go +++ b/pkg/extensions/deployoncefilter_test.go @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package deploy +package extensions import ( "fmt" diff --git a/pkg/extensions/externalsecretmutator.go b/pkg/extensions/externalsecretmutator.go new file mode 100644 index 0000000..547e888 --- /dev/null +++ b/pkg/extensions/externalsecretmutator.go @@ -0,0 +1,225 @@ +// Copyright Mia srl +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package extensions + +import ( + "maps" + "slices" + + extsecv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" + "github.com/mia-platform/jpl/pkg/client/cache" + "github.com/mia-platform/jpl/pkg/mutator" + "github.com/mia-platform/jpl/pkg/resource" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/sets" +) + +type externalSecretsMutator struct { + externalSecretMap map[string]*unstructured.Unstructured + secretsStores map[string]*unstructured.Unstructured +} + +func NewExternalSecretsMutator(objects []*unstructured.Unstructured) mutator.Interface { + externalSecretMap := make(map[string]*unstructured.Unstructured) + secretsStores := make(map[string]*unstructured.Unstructured) + + for _, obj := range objects { + switch obj.GroupVersionKind().GroupKind() { + case extsecGK: + maps.Copy(externalSecretMap, secretForExternalSecret(obj)) + case extSecStoreGK: + secretsStores[externalSecretStoreKey(obj.GroupVersionKind().Kind, obj.GetName(), obj.GetNamespace())] = obj + } + } + + return &externalSecretsMutator{ + externalSecretMap: externalSecretMap, + secretsStores: secretsStores, + } +} + +// CanHandleResource implement mutator.Interface interface +func (m *externalSecretsMutator) CanHandleResource(obj *metav1.PartialObjectMetadata) bool { + if len(m.externalSecretMap) == 0 { + return false + } + + switch obj.GroupVersionKind().GroupKind() { + case deployGK: + return true + case dsGK: + return true + case stsGK: + return true + case podGK: + return true + case extsecGK: + return true + } + + return false +} + +// Mutate implement mutator.Interface interface +func (m *externalSecretsMutator) Mutate(obj *unstructured.Unstructured, _ cache.RemoteResourceGetter) error { + if obj.GroupVersionKind().GroupKind() == extsecGK { + return m.annotateExternalSecret(obj) + } + + podSpecFields, _, err := podFieldsForGroupKind(obj.GroupVersionKind()) + if err != nil { + return err + } + + podSpec, err := podSpecFromUnstructured(obj, podSpecFields) + if err != nil { + return err + } + + externalSecrets := m.externalSecretsForPodSpec(podSpec, obj.GetNamespace()) + if len(externalSecrets) == 0 { + return nil + } + + return resource.SetObjectExplicitDependencies(obj, externalSecrets) +} + +// keep it to always check if dependenciesMutator implement correctly the mutator.Interface interface +var _ mutator.Interface = &dependenciesMutator{} + +// secretForExternalSecret return a map of secret name and external secret resources +func secretForExternalSecret(obj *unstructured.Unstructured) map[string]*unstructured.Unstructured { + externalSecrets := make(map[string]*unstructured.Unstructured) + + extsec := new(extsecv1beta1.ExternalSecret) + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, extsec); err != nil { + return externalSecrets + } + + secretName := extsec.Spec.Target.Name + if len(secretName) == 0 { + secretName = extsec.ObjectMeta.Name + } + externalSecrets[secretKey(secretName, obj.GetNamespace())] = obj + + return externalSecrets +} + +func secretKey(name, namespace string) string { + return name + ":" + namespace +} + +// externalSecretStoreKey return a key rappresentation for a secret store given its kind, name and eventual namespace +func externalSecretStoreKey(kind, name, namespace string) string { + if len(kind) == 0 { + kind = extsecv1beta1.SecretStoreKind + } + + return kind + ":" + name + ":" + namespace +} + +// annotateExternalSecret mutate the ExternalSecret obj with the depends-on annotation with all the stores +// referred inside it. +func (m *externalSecretsMutator) annotateExternalSecret(obj *unstructured.Unstructured) error { + extsec := new(extsecv1beta1.ExternalSecret) + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, extsec); err != nil { + return err + } + stores := make(sets.Set[resource.ObjectMetadata]) + + // check if there is a default secret store set for all external secret + if len(extsec.Spec.SecretStoreRef.Name) != 0 { + storeKey := externalSecretStoreKey(extsec.Spec.SecretStoreRef.Kind, extsec.Spec.SecretStoreRef.Name, extsec.Namespace) + if obj, found := m.secretsStores[storeKey]; found { + stores.Insert(resource.ObjectMetadataFromUnstructured(obj)) + } + } + + extractStoreFromSourceRef := func(sourceRef *extsecv1beta1.SecretStoreRef) (*unstructured.Unstructured, bool) { + if sourceRef == nil || len(sourceRef.Name) == 0 { + return nil, false + } + storeKey := externalSecretStoreKey(sourceRef.Kind, sourceRef.Name, extsec.Namespace) + obj, found := m.secretsStores[storeKey] + return obj, found + } + + // check if there are specific stores for every data + for _, data := range extsec.Spec.Data { + if data.SourceRef == nil { + continue + } + if obj, found := extractStoreFromSourceRef(data.SourceRef.SecretStoreRef); found { + stores.Insert(resource.ObjectMetadataFromUnstructured(obj)) + } + } + + // check if there are specific stores for every dataFrom + for _, dataFrom := range extsec.Spec.DataFrom { + if dataFrom.SourceRef == nil { + continue + } + if obj, found := extractStoreFromSourceRef(dataFrom.SourceRef.SecretStoreRef); found { + stores.Insert(resource.ObjectMetadataFromUnstructured(obj)) + } + } + + return resource.SetObjectExplicitDependencies(obj, stores.UnsortedList()) +} + +func (m *externalSecretsMutator) externalSecretsForPodSpec(pod corev1.PodSpec, namespace string) []resource.ObjectMetadata { + secretsReferences := make([]string, 0) + for _, volume := range pod.Volumes { + fromSecret := volume.Secret + if fromSecret != nil { + secretsReferences = append(secretsReferences, secretKey(fromSecret.SecretName, namespace)) + continue + } + } + + fromContainers := func(containers []corev1.Container) { + for _, container := range containers { + for _, env := range container.Env { + if env.ValueFrom == nil { + continue + } + + if env.ValueFrom.SecretKeyRef != nil { + name := env.ValueFrom.SecretKeyRef.LocalObjectReference.Name + secretsReferences = append(secretsReferences, secretKey(name, namespace)) + } + } + } + } + + fromContainers(pod.InitContainers) + fromContainers(pod.Containers) + + externalSecrets := make([]resource.ObjectMetadata, 0) + for _, key := range secretsReferences { + if externalSecret, found := m.externalSecretMap[key]; found { + objMeta := resource.ObjectMetadataFromUnstructured(externalSecret) + if !slices.Contains(externalSecrets, objMeta) { + externalSecrets = append(externalSecrets, objMeta) + } + } + } + + return externalSecrets +} diff --git a/pkg/extensions/externalsecretmutator_test.go b/pkg/extensions/externalsecretmutator_test.go new file mode 100644 index 0000000..be1f6c0 --- /dev/null +++ b/pkg/extensions/externalsecretmutator_test.go @@ -0,0 +1,235 @@ +// Copyright Mia srl +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package extensions + +import ( + "path/filepath" + "testing" + + extsecv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" + jpltesting "github.com/mia-platform/jpl/pkg/testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +func TestNewExternalSecretMutator(t *testing.T) { + t.Parallel() + + testdata := filepath.Join("testdata", "externalsecret-mutator") + deployment := jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "deployment.yaml")) + store := jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "store.yaml")) + extSec := jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "external-secret.yaml")) + extSec2 := jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "external-secret-secret-name.yaml")) + configmap := jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "configmap.yaml")) + + tests := map[string]struct { + objects []*unstructured.Unstructured + expectedExternalSeceretMap map[string]*unstructured.Unstructured + expectedSecretsStoreMap map[string]*unstructured.Unstructured + }{ + "empty objects": { + expectedExternalSeceretMap: map[string]*unstructured.Unstructured{}, + expectedSecretsStoreMap: map[string]*unstructured.Unstructured{}, + }, + "no external secrets or stores in objects": { + objects: []*unstructured.Unstructured{ + deployment, + }, + expectedExternalSeceretMap: map[string]*unstructured.Unstructured{}, + expectedSecretsStoreMap: map[string]*unstructured.Unstructured{}, + }, + "mixed objects": { + objects: []*unstructured.Unstructured{ + deployment, + store, + extSec, + extSec2, + configmap, + }, + expectedExternalSeceretMap: map[string]*unstructured.Unstructured{ + "external-secret:externalsecret-test": extSec, + "custom-secret-name:externalsecret-test": extSec2, + }, + expectedSecretsStoreMap: map[string]*unstructured.Unstructured{ + "SecretStore:secret-store:externalsecret-test": store, + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + m := NewExternalSecretsMutator(test.objects) + esm, ok := m.(*externalSecretsMutator) + require.True(t, ok) + assert.Equal(t, test.expectedExternalSeceretMap, esm.externalSecretMap) + assert.Equal(t, test.expectedSecretsStoreMap, esm.secretsStores) + }) + } +} + +func TestExternalSecretMutatorCanHandleResource(t *testing.T) { + t.Parallel() + + testdata := filepath.Join("testdata", "externalsecret-mutator") + externalSecretMap := map[string]*unstructured.Unstructured{ + "key": jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "deployment.yaml")), + } + + tests := map[string]struct { + externalSecretMap map[string]*unstructured.Unstructured + obj *metav1.PartialObjectMetadata + expectedResult bool + }{ + "empty external secret map always returns false": { + externalSecretMap: make(map[string]*unstructured.Unstructured), + expectedResult: false, + }, + "config map is not handled": { + externalSecretMap: externalSecretMap, + obj: &metav1.PartialObjectMetadata{ + TypeMeta: metav1.TypeMeta{ + Kind: configMapGK.Kind, + APIVersion: corev1.SchemeGroupVersion.String(), + }, + }, + expectedResult: false, + }, + "deployment return true": { + externalSecretMap: externalSecretMap, + obj: &metav1.PartialObjectMetadata{ + TypeMeta: metav1.TypeMeta{ + Kind: deployGK.Kind, + APIVersion: appsv1.SchemeGroupVersion.String(), + }, + }, + expectedResult: true, + }, + "daemonset return true": { + externalSecretMap: externalSecretMap, + obj: &metav1.PartialObjectMetadata{ + TypeMeta: metav1.TypeMeta{ + Kind: dsGK.Kind, + APIVersion: appsv1.SchemeGroupVersion.String(), + }, + }, + expectedResult: true, + }, + "stateful return true": { + externalSecretMap: externalSecretMap, + obj: &metav1.PartialObjectMetadata{ + TypeMeta: metav1.TypeMeta{ + Kind: stsGK.Kind, + APIVersion: appsv1.SchemeGroupVersion.String(), + }, + }, + expectedResult: true, + }, + "pod return true": { + externalSecretMap: externalSecretMap, + obj: &metav1.PartialObjectMetadata{ + TypeMeta: metav1.TypeMeta{ + Kind: podGK.Kind, + APIVersion: corev1.SchemeGroupVersion.String(), + }, + }, + expectedResult: true, + }, + "external secret return true": { + externalSecretMap: externalSecretMap, + obj: &metav1.PartialObjectMetadata{ + TypeMeta: metav1.TypeMeta{ + Kind: extsecGK.Kind, + APIVersion: extsecv1beta1.SchemeGroupVersion.String(), + }, + }, + expectedResult: true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + esm := externalSecretsMutator{externalSecretMap: test.externalSecretMap} + assert.Equal(t, test.expectedResult, esm.CanHandleResource(test.obj)) + }) + } +} + +func TestExternalSecretMutatorMutate(t *testing.T) { + t.Parallel() + + testdata := filepath.Join("testdata", "externalsecret-mutator") + externalSecretsMap := map[string]*unstructured.Unstructured{ + "external-secret:externalsecret-test": jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "external-secret.yaml")), + "custom-secret-name:externalsecret-test": jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "external-secret-secret-name.yaml")), + } + secretsStores := map[string]*unstructured.Unstructured{ + "SecretStore:secret-store:externalsecret-test": jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "store.yaml")), + } + + tests := map[string]struct { + resource *unstructured.Unstructured + expectedResult *unstructured.Unstructured + expectedError string + }{ + "external-secret": { + resource: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "external-secret.yaml")), + expectedResult: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "expected-external-secret.yaml")), + }, + "deployment": { + resource: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "deployment.yaml")), + expectedResult: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "expected-deployment.yaml")), + }, + "sts": { + resource: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "sts.yaml")), + expectedResult: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "expected-sts.yaml")), + }, + "daemonset": { + resource: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "daemonset.yaml")), + expectedResult: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "expected-daemonset.yaml")), + }, + "pod": { + resource: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "pod.yaml")), + expectedResult: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "expected-pod.yaml")), + }, + "wrong resource": { + resource: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "wrong-resource.yaml")), + expectedResult: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "wrong-resource.yaml")), + expectedError: `unsupported object type for dependencies mutator: "v1, Service"`, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + mutator := &externalSecretsMutator{ + externalSecretMap: externalSecretsMap, + secretsStores: secretsStores, + } + + err := mutator.Mutate(test.resource, nil) + switch len(test.expectedError) { + case 0: + require.NoError(t, err) + default: + assert.ErrorContains(t, err, test.expectedError) + } + assert.Equal(t, test.expectedResult, test.resource) + }) + } +} diff --git a/pkg/extensions/externalsecretpoller.go b/pkg/extensions/externalsecretpoller.go new file mode 100644 index 0000000..f454d05 --- /dev/null +++ b/pkg/extensions/externalsecretpoller.go @@ -0,0 +1,110 @@ +// Copyright Mia srl +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package extensions + +import ( + extsecv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" + "github.com/mia-platform/jpl/pkg/poller" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" +) + +func ExternalSecretStatusCheckers() poller.CustomStatusCheckers { + return poller.CustomStatusCheckers{ + extsecGK: externalSecretStatusChecker, + extSecStoreGK: secretStoreStatusChecker, + } +} + +// externalSecretStatusChecker contains the logic for checking if an ExternalSecret has finished to sync its data +func externalSecretStatusChecker(object *unstructured.Unstructured) (*poller.Result, error) { + externalSecret := new(extsecv1beta1.ExternalSecret) + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(object.Object, externalSecret); err != nil { + return nil, err + } + + for _, condition := range externalSecret.Status.Conditions { + switch condition.Type { + case extsecv1beta1.ExternalSecretReady: + switch condition.Status { + case corev1.ConditionTrue: + return &poller.Result{ + Status: poller.StatusCurrent, + Message: condition.Message, + }, nil + case corev1.ConditionFalse: + return &poller.Result{ + Status: poller.StatusInProgress, + Message: condition.Message, + }, nil + case corev1.ConditionUnknown: + return &poller.Result{ + Status: poller.StatusInProgress, + Message: condition.Message, + }, nil + } + case extsecv1beta1.ExternalSecretDeleted: + if condition.Status == corev1.ConditionTrue { + return &poller.Result{ + Status: poller.StatusTerminating, + Message: condition.Message, + }, nil + } + } + } + + return &poller.Result{ + Status: poller.StatusInProgress, + Message: "ExternalSecret sync is in progress", + }, nil +} + +// secretStoreStatusChecker contains the logic for checking if an SecretStore has +func secretStoreStatusChecker(object *unstructured.Unstructured) (*poller.Result, error) { + secretStore := new(extsecv1beta1.SecretStore) + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(object.Object, secretStore); err != nil { + return nil, err + } + + for _, condition := range secretStore.Status.Conditions { + if condition.Type != extsecv1beta1.SecretStoreReady { + continue + } + switch condition.Status { + case corev1.ConditionTrue: + return &poller.Result{ + Status: poller.StatusCurrent, + Message: condition.Message, + }, nil + case corev1.ConditionFalse: + return &poller.Result{ + Status: poller.StatusInProgress, + Message: condition.Message, + }, nil + case corev1.ConditionUnknown: + return &poller.Result{ + Status: poller.StatusInProgress, + Message: condition.Message, + }, nil + } + } + + return &poller.Result{ + Status: poller.StatusInProgress, + Message: "SecretStore is in progress", + }, nil +} diff --git a/pkg/extensions/externalsecretpoller_test.go b/pkg/extensions/externalsecretpoller_test.go new file mode 100644 index 0000000..a9fb4a9 --- /dev/null +++ b/pkg/extensions/externalsecretpoller_test.go @@ -0,0 +1,134 @@ +// Copyright Mia srl +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package extensions + +import ( + "path/filepath" + "testing" + + "github.com/mia-platform/jpl/pkg/poller" + jpltesting "github.com/mia-platform/jpl/pkg/testing" + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +func TestExtendedPoller(t *testing.T) { + t.Parallel() + + customCheckers := ExternalSecretStatusCheckers() + assert.Equal(t, 2, len(customCheckers)) +} + +func TestExternalSecretStatusChecker(t *testing.T) { + t.Parallel() + + testdata := filepath.Join("testdata", "custom-pollers") + tests := map[string]struct { + object *unstructured.Unstructured + expectedResult *poller.Result + expectedError string + }{ + "resource secret deleted condition is terminating": { + object: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "extsec-terminating.yaml")), + expectedResult: &poller.Result{ + Status: poller.StatusTerminating, + Message: "custom message", + }, + }, + "resource with ready condition true is current": { + object: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "extsec-ready-true.yaml")), + expectedResult: &poller.Result{ + Status: poller.StatusCurrent, + Message: "custom message", + }, + }, + "resource with ready condition false is in progress": { + object: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "extsec-ready-false.yaml")), + expectedResult: &poller.Result{ + Status: poller.StatusInProgress, + Message: "custom message", + }, + }, + "resource without status is in progress": { + object: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "extsec-no-status.yaml")), + expectedResult: &poller.Result{ + Status: poller.StatusInProgress, + Message: "ExternalSecret sync is in progress", + }, + }, + } + + for testName, testCase := range tests { + t.Run(testName, func(t *testing.T) { + result, err := externalSecretStatusChecker(testCase.object) + if len(testCase.expectedError) > 0 { + assert.ErrorContains(t, err, testCase.expectedError) + assert.Equal(t, testCase.expectedResult, result) + return + } + + assert.NoError(t, err) + assert.Equal(t, testCase.expectedResult, result) + }) + } +} + +func TestSecretStoreStatusChecker(t *testing.T) { + t.Parallel() + + testdata := filepath.Join("testdata", "custom-pollers") + tests := map[string]struct { + object *unstructured.Unstructured + expectedResult *poller.Result + expectedError string + }{ + "resource with ready condition true is current": { + object: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "secstore-ready-true.yaml")), + expectedResult: &poller.Result{ + Status: poller.StatusCurrent, + Message: "custom message", + }, + }, + "resource with ready condition false is in progress": { + object: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "secstore-ready-false.yaml")), + expectedResult: &poller.Result{ + Status: poller.StatusInProgress, + Message: "custom message", + }, + }, + "resource without status is in progress": { + object: jpltesting.UnstructuredFromFile(t, filepath.Join(testdata, "secstore-no-status.yaml")), + expectedResult: &poller.Result{ + Status: poller.StatusInProgress, + Message: "SecretStore is in progress", + }, + }, + } + + for testName, testCase := range tests { + t.Run(testName, func(t *testing.T) { + result, err := secretStoreStatusChecker(testCase.object) + if len(testCase.expectedError) > 0 { + assert.ErrorContains(t, err, testCase.expectedError) + assert.Equal(t, testCase.expectedResult, result) + return + } + + assert.NoError(t, err) + assert.Equal(t, testCase.expectedResult, result) + }) + } +} diff --git a/pkg/extensions/testdata/custom-pollers/extsec-no-status.yaml b/pkg/extensions/testdata/custom-pollers/extsec-no-status.yaml new file mode 100644 index 0000000..c512804 --- /dev/null +++ b/pkg/extensions/testdata/custom-pollers/extsec-no-status.yaml @@ -0,0 +1,4 @@ +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: external-secret diff --git a/pkg/extensions/testdata/custom-pollers/extsec-ready-false.yaml b/pkg/extensions/testdata/custom-pollers/extsec-ready-false.yaml new file mode 100644 index 0000000..99ca3e9 --- /dev/null +++ b/pkg/extensions/testdata/custom-pollers/extsec-ready-false.yaml @@ -0,0 +1,9 @@ +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: external-secret +status: + conditions: + - type: Ready + status: "False" + message: "custom message" diff --git a/pkg/extensions/testdata/custom-pollers/extsec-ready-true.yaml b/pkg/extensions/testdata/custom-pollers/extsec-ready-true.yaml new file mode 100644 index 0000000..f0f1e19 --- /dev/null +++ b/pkg/extensions/testdata/custom-pollers/extsec-ready-true.yaml @@ -0,0 +1,9 @@ +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: external-secret +status: + conditions: + - type: Ready + status: "True" + message: "custom message" diff --git a/pkg/extensions/testdata/custom-pollers/extsec-terminating.yaml b/pkg/extensions/testdata/custom-pollers/extsec-terminating.yaml new file mode 100644 index 0000000..eb665e9 --- /dev/null +++ b/pkg/extensions/testdata/custom-pollers/extsec-terminating.yaml @@ -0,0 +1,9 @@ +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: external-secret +status: + conditions: + - type: Deleted + status: "True" + message: "custom message" diff --git a/pkg/extensions/testdata/custom-pollers/secstore-no-status.yaml b/pkg/extensions/testdata/custom-pollers/secstore-no-status.yaml new file mode 100644 index 0000000..e9425d8 --- /dev/null +++ b/pkg/extensions/testdata/custom-pollers/secstore-no-status.yaml @@ -0,0 +1,4 @@ +apiVersion: external-secrets.io/v1beta1 +kind: SecretStore +metadata: + name: store diff --git a/pkg/extensions/testdata/custom-pollers/secstore-ready-false.yaml b/pkg/extensions/testdata/custom-pollers/secstore-ready-false.yaml new file mode 100644 index 0000000..c1a65bc --- /dev/null +++ b/pkg/extensions/testdata/custom-pollers/secstore-ready-false.yaml @@ -0,0 +1,9 @@ +apiVersion: external-secrets.io/v1beta1 +kind: SecretStore +metadata: + name: store +status: + conditions: + - type: Ready + status: "False" + message: "custom message" diff --git a/pkg/extensions/testdata/custom-pollers/secstore-ready-true.yaml b/pkg/extensions/testdata/custom-pollers/secstore-ready-true.yaml new file mode 100644 index 0000000..00db5ba --- /dev/null +++ b/pkg/extensions/testdata/custom-pollers/secstore-ready-true.yaml @@ -0,0 +1,9 @@ +apiVersion: external-secrets.io/v1beta1 +kind: SecretStore +metadata: + name: store +status: + conditions: + - type: Ready + status: "True" + message: "custom message" diff --git a/pkg/cmd/deploy/testdata/dependency-mutator/cm-other-namespace.yaml b/pkg/extensions/testdata/dependency-mutator/cm-other-namespace.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/dependency-mutator/cm-other-namespace.yaml rename to pkg/extensions/testdata/dependency-mutator/cm-other-namespace.yaml diff --git a/pkg/cmd/deploy/testdata/dependency-mutator/configmap.yaml b/pkg/extensions/testdata/dependency-mutator/configmap.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/dependency-mutator/configmap.yaml rename to pkg/extensions/testdata/dependency-mutator/configmap.yaml diff --git a/pkg/cmd/deploy/testdata/dependency-mutator/daemonset.yaml b/pkg/extensions/testdata/dependency-mutator/daemonset.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/dependency-mutator/daemonset.yaml rename to pkg/extensions/testdata/dependency-mutator/daemonset.yaml diff --git a/pkg/cmd/deploy/testdata/dependency-mutator/deployment.yaml b/pkg/extensions/testdata/dependency-mutator/deployment.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/dependency-mutator/deployment.yaml rename to pkg/extensions/testdata/dependency-mutator/deployment.yaml diff --git a/pkg/cmd/deploy/testdata/dependency-mutator/expected-daemonset.yaml b/pkg/extensions/testdata/dependency-mutator/expected-daemonset.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/dependency-mutator/expected-daemonset.yaml rename to pkg/extensions/testdata/dependency-mutator/expected-daemonset.yaml diff --git a/pkg/cmd/deploy/testdata/dependency-mutator/expected-deployment.yaml b/pkg/extensions/testdata/dependency-mutator/expected-deployment.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/dependency-mutator/expected-deployment.yaml rename to pkg/extensions/testdata/dependency-mutator/expected-deployment.yaml diff --git a/pkg/cmd/deploy/testdata/dependency-mutator/expected-pod.yaml b/pkg/extensions/testdata/dependency-mutator/expected-pod.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/dependency-mutator/expected-pod.yaml rename to pkg/extensions/testdata/dependency-mutator/expected-pod.yaml diff --git a/pkg/cmd/deploy/testdata/dependency-mutator/expected-sts.yaml b/pkg/extensions/testdata/dependency-mutator/expected-sts.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/dependency-mutator/expected-sts.yaml rename to pkg/extensions/testdata/dependency-mutator/expected-sts.yaml diff --git a/pkg/cmd/deploy/testdata/dependency-mutator/pod.yaml b/pkg/extensions/testdata/dependency-mutator/pod.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/dependency-mutator/pod.yaml rename to pkg/extensions/testdata/dependency-mutator/pod.yaml diff --git a/pkg/cmd/deploy/testdata/dependency-mutator/secret.yaml b/pkg/extensions/testdata/dependency-mutator/secret.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/dependency-mutator/secret.yaml rename to pkg/extensions/testdata/dependency-mutator/secret.yaml diff --git a/pkg/cmd/deploy/testdata/dependency-mutator/sts.yaml b/pkg/extensions/testdata/dependency-mutator/sts.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/dependency-mutator/sts.yaml rename to pkg/extensions/testdata/dependency-mutator/sts.yaml diff --git a/pkg/cmd/deploy/testdata/dependency-mutator/wrong-resource.yaml b/pkg/extensions/testdata/dependency-mutator/wrong-resource.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/dependency-mutator/wrong-resource.yaml rename to pkg/extensions/testdata/dependency-mutator/wrong-resource.yaml diff --git a/pkg/cmd/deploy/testdata/deploy-mutator/daemonset.yaml b/pkg/extensions/testdata/deploy-mutator/daemonset.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/deploy-mutator/daemonset.yaml rename to pkg/extensions/testdata/deploy-mutator/daemonset.yaml diff --git a/pkg/cmd/deploy/testdata/deploy-mutator/deployment-smart-remote.yaml b/pkg/extensions/testdata/deploy-mutator/deployment-smart-remote.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/deploy-mutator/deployment-smart-remote.yaml rename to pkg/extensions/testdata/deploy-mutator/deployment-smart-remote.yaml diff --git a/pkg/cmd/deploy/testdata/deploy-mutator/deployment-smart.yaml b/pkg/extensions/testdata/deploy-mutator/deployment-smart.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/deploy-mutator/deployment-smart.yaml rename to pkg/extensions/testdata/deploy-mutator/deployment-smart.yaml diff --git a/pkg/cmd/deploy/testdata/deploy-mutator/deployment.yaml b/pkg/extensions/testdata/deploy-mutator/deployment.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/deploy-mutator/deployment.yaml rename to pkg/extensions/testdata/deploy-mutator/deployment.yaml diff --git a/pkg/cmd/deploy/testdata/deploy-mutator/error-remote.yaml b/pkg/extensions/testdata/deploy-mutator/error-remote.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/deploy-mutator/error-remote.yaml rename to pkg/extensions/testdata/deploy-mutator/error-remote.yaml diff --git a/pkg/cmd/deploy/testdata/deploy-mutator/expected-daemonset.yaml b/pkg/extensions/testdata/deploy-mutator/expected-daemonset.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/deploy-mutator/expected-daemonset.yaml rename to pkg/extensions/testdata/deploy-mutator/expected-daemonset.yaml diff --git a/pkg/cmd/deploy/testdata/deploy-mutator/expected-deployment-smart-remote.yaml b/pkg/extensions/testdata/deploy-mutator/expected-deployment-smart-remote.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/deploy-mutator/expected-deployment-smart-remote.yaml rename to pkg/extensions/testdata/deploy-mutator/expected-deployment-smart-remote.yaml diff --git a/pkg/cmd/deploy/testdata/deploy-mutator/expected-deployment-smart.yaml b/pkg/extensions/testdata/deploy-mutator/expected-deployment-smart.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/deploy-mutator/expected-deployment-smart.yaml rename to pkg/extensions/testdata/deploy-mutator/expected-deployment-smart.yaml diff --git a/pkg/cmd/deploy/testdata/deploy-mutator/expected-deployment.yaml b/pkg/extensions/testdata/deploy-mutator/expected-deployment.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/deploy-mutator/expected-deployment.yaml rename to pkg/extensions/testdata/deploy-mutator/expected-deployment.yaml diff --git a/pkg/cmd/deploy/testdata/deploy-mutator/expected-pod.yaml b/pkg/extensions/testdata/deploy-mutator/expected-pod.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/deploy-mutator/expected-pod.yaml rename to pkg/extensions/testdata/deploy-mutator/expected-pod.yaml diff --git a/pkg/cmd/deploy/testdata/deploy-mutator/expected-sts.yaml b/pkg/extensions/testdata/deploy-mutator/expected-sts.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/deploy-mutator/expected-sts.yaml rename to pkg/extensions/testdata/deploy-mutator/expected-sts.yaml diff --git a/pkg/cmd/deploy/testdata/deploy-mutator/pod.yaml b/pkg/extensions/testdata/deploy-mutator/pod.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/deploy-mutator/pod.yaml rename to pkg/extensions/testdata/deploy-mutator/pod.yaml diff --git a/pkg/cmd/deploy/testdata/deploy-mutator/remote-status.yaml b/pkg/extensions/testdata/deploy-mutator/remote-status.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/deploy-mutator/remote-status.yaml rename to pkg/extensions/testdata/deploy-mutator/remote-status.yaml diff --git a/pkg/cmd/deploy/testdata/deploy-mutator/sts.yaml b/pkg/extensions/testdata/deploy-mutator/sts.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/deploy-mutator/sts.yaml rename to pkg/extensions/testdata/deploy-mutator/sts.yaml diff --git a/pkg/cmd/deploy/testdata/deploy-mutator/wrong-image.yaml b/pkg/extensions/testdata/deploy-mutator/wrong-image.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/deploy-mutator/wrong-image.yaml rename to pkg/extensions/testdata/deploy-mutator/wrong-image.yaml diff --git a/pkg/cmd/deploy/testdata/deploy-mutator/wrong-resource.yaml b/pkg/extensions/testdata/deploy-mutator/wrong-resource.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/deploy-mutator/wrong-resource.yaml rename to pkg/extensions/testdata/deploy-mutator/wrong-resource.yaml diff --git a/pkg/extensions/testdata/externalsecret-mutator/configmap.yaml b/pkg/extensions/testdata/externalsecret-mutator/configmap.yaml new file mode 100644 index 0000000..407db9d --- /dev/null +++ b/pkg/extensions/testdata/externalsecret-mutator/configmap.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: example + namespace: externalsecret-test +data: + key: value diff --git a/pkg/extensions/testdata/externalsecret-mutator/daemonset.yaml b/pkg/extensions/testdata/externalsecret-mutator/daemonset.yaml new file mode 100644 index 0000000..046f035 --- /dev/null +++ b/pkg/extensions/testdata/externalsecret-mutator/daemonset.yaml @@ -0,0 +1,30 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: example + namespace: externalsecret-test +spec: + selector: + matchLabels: + name: example + template: + metadata: + annotations: + existing: annotation + labels: + name: example + spec: + initContainers: + - name: example + image: busybox + containers: + - name: example + image: busybox:v1.0.0 + volumes: + - name: varlog + hostPath: + path: /var/log + - name: example + secret: + secretName: custom-secret-name + optional: true diff --git a/pkg/extensions/testdata/externalsecret-mutator/deployment.yaml b/pkg/extensions/testdata/externalsecret-mutator/deployment.yaml new file mode 100644 index 0000000..1bfc24c --- /dev/null +++ b/pkg/extensions/testdata/externalsecret-mutator/deployment.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: example + namespace: externalsecret-test +spec: + selector: + matchLabels: + app: example + template: + metadata: + labels: + app: example + spec: + initContainers: + - name: example + image: busybox + env: + - name: ENV + valueFrom: + secretKeyRef: + key: data + name: external-secret + containers: + - name: example + image: busybox + resources: + limits: + memory: "128Mi" + cpu: "500m" + volumes: + - name: volume + configMap: + name: example diff --git a/pkg/extensions/testdata/externalsecret-mutator/expected-daemonset.yaml b/pkg/extensions/testdata/externalsecret-mutator/expected-daemonset.yaml new file mode 100644 index 0000000..7c7c74b --- /dev/null +++ b/pkg/extensions/testdata/externalsecret-mutator/expected-daemonset.yaml @@ -0,0 +1,32 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: example + namespace: externalsecret-test + annotations: + config.kubernetes.io/depends-on: external-secrets.io/namespaces/externalsecret-test/ExternalSecret/external-secret2 +spec: + selector: + matchLabels: + name: example + template: + metadata: + annotations: + existing: annotation + labels: + name: example + spec: + initContainers: + - name: example + image: busybox + containers: + - name: example + image: busybox:v1.0.0 + volumes: + - name: varlog + hostPath: + path: /var/log + - name: example + secret: + secretName: custom-secret-name + optional: true diff --git a/pkg/extensions/testdata/externalsecret-mutator/expected-deployment.yaml b/pkg/extensions/testdata/externalsecret-mutator/expected-deployment.yaml new file mode 100644 index 0000000..a253fbc --- /dev/null +++ b/pkg/extensions/testdata/externalsecret-mutator/expected-deployment.yaml @@ -0,0 +1,36 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: example + namespace: externalsecret-test + annotations: + config.kubernetes.io/depends-on: external-secrets.io/namespaces/externalsecret-test/ExternalSecret/external-secret +spec: + selector: + matchLabels: + app: example + template: + metadata: + labels: + app: example + spec: + initContainers: + - name: example + image: busybox + env: + - name: ENV + valueFrom: + secretKeyRef: + key: data + name: external-secret + containers: + - name: example + image: busybox + resources: + limits: + memory: "128Mi" + cpu: "500m" + volumes: + - name: volume + configMap: + name: example diff --git a/pkg/extensions/testdata/externalsecret-mutator/expected-external-secret.yaml b/pkg/extensions/testdata/externalsecret-mutator/expected-external-secret.yaml new file mode 100644 index 0000000..e7973cb --- /dev/null +++ b/pkg/extensions/testdata/externalsecret-mutator/expected-external-secret.yaml @@ -0,0 +1,47 @@ +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: external-secret + namespace: externalsecret-test + annotations: + config.kubernetes.io/depends-on: external-secrets.io/namespaces/externalsecret-test/SecretStore/secret-store +spec: + refreshInterval: 1h + secretStoreRef: + name: secret-store + target: + creationPolicy: Owner + data: + - secretKey: secret-key + remoteRef: + key: provider-key + version: provider-key-version + property: provider-key-property + sourceRef: + # point to a SecretStore that should be used to fetch a secret. + # must be defined if no spec.secretStoreRef is defined. + storeRef: + name: secret-store + kind: ClusterSecretStore + - secretKey: + remoteRef: + key: provider-key2 + version: provider-key-version2 + property: provider-key-property2 + sourceRef: + generatorRef: + apiVersion: generators.external-secrets.io/v1alpha1 + kind: ECRAuthorizationToken + name: "my-ecr" + dataFrom: + - sourceRef: + # point to a SecretStore that should be used to fetch a secret. + # must be defined if no spec.secretStoreRef is defined. + storeRef: + name: secret-store + kind: SecretStore + - sourceRef: + generatorRef: + apiVersion: generators.external-secrets.io/v1alpha1 + kind: ECRAuthorizationToken + name: "my-ecr" diff --git a/pkg/extensions/testdata/externalsecret-mutator/expected-pod.yaml b/pkg/extensions/testdata/externalsecret-mutator/expected-pod.yaml new file mode 100644 index 0000000..127af6c --- /dev/null +++ b/pkg/extensions/testdata/externalsecret-mutator/expected-pod.yaml @@ -0,0 +1,33 @@ +apiVersion: v1 +kind: Pod +metadata: + name: example + namespace: externalsecret-test + annotations: + config.kubernetes.io/depends-on: external-secrets.io/namespaces/externalsecret-test/ExternalSecret/external-secret,external-secrets.io/namespaces/externalsecret-test/ExternalSecret/external-secret2 + labels: + name: example +spec: + initContainers: + - name: init + image: busybox + env: + - name: ENV + valueFrom: + secretKeyRef: + key: otherkey + name: external-secret + containers: + - name: example + image: busybox + resources: + limits: + memory: "128Mi" + cpu: "500m" + volumes: + - name: volume + secret: + secretName: external-secret + - name: volume + secret: + secretName: custom-secret-name diff --git a/pkg/extensions/testdata/externalsecret-mutator/expected-sts.yaml b/pkg/extensions/testdata/externalsecret-mutator/expected-sts.yaml new file mode 100644 index 0000000..0b5e02c --- /dev/null +++ b/pkg/extensions/testdata/externalsecret-mutator/expected-sts.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: example + namespace: externalsecret-test +spec: + selector: + matchLabels: + app: example + serviceName: example + replicas: 2 + template: + metadata: + annotations: + existing: annotation + labels: + app: example + spec: + containers: + - name: example + image: busybox + volumes: + - name: volume + secret: + secretName: missing + volumeClaimTemplates: + - metadata: + name: www + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi diff --git a/pkg/extensions/testdata/externalsecret-mutator/external-secret-secret-name.yaml b/pkg/extensions/testdata/externalsecret-mutator/external-secret-secret-name.yaml new file mode 100644 index 0000000..e70224d --- /dev/null +++ b/pkg/extensions/testdata/externalsecret-mutator/external-secret-secret-name.yaml @@ -0,0 +1,19 @@ +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: external-secret2 + namespace: externalsecret-test +spec: + refreshInterval: 1h + secretStoreRef: + name: secret-store + kind: SecretStore + target: + name: custom-secret-name + creationPolicy: Owner + data: + - secretKey: secret-key + remoteRef: + key: provider-key + version: provider-key-version + property: provider-key-property diff --git a/pkg/extensions/testdata/externalsecret-mutator/external-secret.yaml b/pkg/extensions/testdata/externalsecret-mutator/external-secret.yaml new file mode 100644 index 0000000..e936fb1 --- /dev/null +++ b/pkg/extensions/testdata/externalsecret-mutator/external-secret.yaml @@ -0,0 +1,45 @@ +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: external-secret + namespace: externalsecret-test +spec: + refreshInterval: 1h + secretStoreRef: + name: secret-store + target: + creationPolicy: Owner + data: + - secretKey: secret-key + remoteRef: + key: provider-key + version: provider-key-version + property: provider-key-property + sourceRef: + # point to a SecretStore that should be used to fetch a secret. + # must be defined if no spec.secretStoreRef is defined. + storeRef: + name: secret-store + kind: ClusterSecretStore + - secretKey: + remoteRef: + key: provider-key2 + version: provider-key-version2 + property: provider-key-property2 + sourceRef: + generatorRef: + apiVersion: generators.external-secrets.io/v1alpha1 + kind: ECRAuthorizationToken + name: "my-ecr" + dataFrom: + - sourceRef: + # point to a SecretStore that should be used to fetch a secret. + # must be defined if no spec.secretStoreRef is defined. + storeRef: + name: secret-store + kind: SecretStore + - sourceRef: + generatorRef: + apiVersion: generators.external-secrets.io/v1alpha1 + kind: ECRAuthorizationToken + name: "my-ecr" diff --git a/pkg/extensions/testdata/externalsecret-mutator/pod.yaml b/pkg/extensions/testdata/externalsecret-mutator/pod.yaml new file mode 100644 index 0000000..0a14786 --- /dev/null +++ b/pkg/extensions/testdata/externalsecret-mutator/pod.yaml @@ -0,0 +1,31 @@ +apiVersion: v1 +kind: Pod +metadata: + name: example + namespace: externalsecret-test + labels: + name: example +spec: + initContainers: + - name: init + image: busybox + env: + - name: ENV + valueFrom: + secretKeyRef: + key: otherkey + name: external-secret + containers: + - name: example + image: busybox + resources: + limits: + memory: "128Mi" + cpu: "500m" + volumes: + - name: volume + secret: + secretName: external-secret + - name: volume + secret: + secretName: custom-secret-name diff --git a/pkg/extensions/testdata/externalsecret-mutator/store.yaml b/pkg/extensions/testdata/externalsecret-mutator/store.yaml new file mode 100644 index 0000000..c5b3085 --- /dev/null +++ b/pkg/extensions/testdata/externalsecret-mutator/store.yaml @@ -0,0 +1,18 @@ +apiVersion: external-secrets.io/v1beta1 +kind: SecretStore +metadata: + name: secret-store + namespace: externalsecret-test +spec: + provider: + aws: + service: SecretsManager + region: us-east-1 + auth: + secretRef: + accessKeyIDSecretRef: + name: awssm-secret + key: access-key + secretAccessKeySecretRef: + name: awssm-secret + key: secret-access-key diff --git a/pkg/extensions/testdata/externalsecret-mutator/sts.yaml b/pkg/extensions/testdata/externalsecret-mutator/sts.yaml new file mode 100644 index 0000000..0b5e02c --- /dev/null +++ b/pkg/extensions/testdata/externalsecret-mutator/sts.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: example + namespace: externalsecret-test +spec: + selector: + matchLabels: + app: example + serviceName: example + replicas: 2 + template: + metadata: + annotations: + existing: annotation + labels: + app: example + spec: + containers: + - name: example + image: busybox + volumes: + - name: volume + secret: + secretName: missing + volumeClaimTemplates: + - metadata: + name: www + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi diff --git a/pkg/extensions/testdata/externalsecret-mutator/wrong-resource.yaml b/pkg/extensions/testdata/externalsecret-mutator/wrong-resource.yaml new file mode 100644 index 0000000..72d9183 --- /dev/null +++ b/pkg/extensions/testdata/externalsecret-mutator/wrong-resource.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Service +metadata: + name: example +spec: + selector: + app: example + ports: + - port: 80 + targetPort: 8080 diff --git a/pkg/cmd/deploy/testdata/filter/configmap.yaml b/pkg/extensions/testdata/filter/configmap.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/filter/configmap.yaml rename to pkg/extensions/testdata/filter/configmap.yaml diff --git a/pkg/cmd/deploy/testdata/filter/deployment.yaml b/pkg/extensions/testdata/filter/deployment.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/filter/deployment.yaml rename to pkg/extensions/testdata/filter/deployment.yaml diff --git a/pkg/cmd/deploy/testdata/filter/filtered.yaml b/pkg/extensions/testdata/filter/filtered.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/filter/filtered.yaml rename to pkg/extensions/testdata/filter/filtered.yaml diff --git a/pkg/cmd/deploy/testdata/filter/secret.yaml b/pkg/extensions/testdata/filter/secret.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/filter/secret.yaml rename to pkg/extensions/testdata/filter/secret.yaml diff --git a/pkg/cmd/deploy/testdata/filter/unfiltered.yaml b/pkg/extensions/testdata/filter/unfiltered.yaml similarity index 100% rename from pkg/cmd/deploy/testdata/filter/unfiltered.yaml rename to pkg/extensions/testdata/filter/unfiltered.yaml diff --git a/pkg/cmd/deploy/utils.go b/pkg/extensions/utils.go similarity index 89% rename from pkg/cmd/deploy/utils.go rename to pkg/extensions/utils.go index 5e8d447..b4cf724 100644 --- a/pkg/cmd/deploy/utils.go +++ b/pkg/extensions/utils.go @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package deploy +package extensions import ( "crypto/sha512" @@ -21,6 +21,7 @@ import ( "fmt" "reflect" + extsecv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -39,23 +40,21 @@ const ( checksumAnnotation = miaPlatformPrefix + "dependencies-checksum" - jobGeneratorLabel = miaPlatformPrefix + "autocreate" - jobGeneratorValue = "true" - - deployAll = "deploy_all" - deploySmart = "smart_deploy" + DeployAll = "deploy_all" + DeploySmart = "smart_deploy" ) var ( configMapGK = corev1.SchemeGroupVersion.WithKind(reflect.TypeOf(corev1.ConfigMap{}).Name()).GroupKind() secretGK = corev1.SchemeGroupVersion.WithKind(reflect.TypeOf(corev1.Secret{}).Name()).GroupKind() + extsecGK = extsecv1beta1.SchemeGroupVersion.WithKind(extsecv1beta1.ExtSecretKind).GroupKind() + extSecStoreGK = extsecv1beta1.SchemeGroupVersion.WithKind(extsecv1beta1.SecretStoreKind).GroupKind() + deployGK = appsv1.SchemeGroupVersion.WithKind(reflect.TypeOf(appsv1.Deployment{}).Name()).GroupKind() dsGK = appsv1.SchemeGroupVersion.WithKind(reflect.TypeOf(appsv1.DaemonSet{}).Name()).GroupKind() stsGK = appsv1.SchemeGroupVersion.WithKind(reflect.TypeOf(appsv1.StatefulSet{}).Name()).GroupKind() podGK = corev1.SchemeGroupVersion.WithKind(reflect.TypeOf(corev1.Pod{}).Name()).GroupKind() - - validDeployTypeValues = []string{"deploy_all", "smart_deploy"} ) // podFieldsForGroupKind return the pieces of the path for the pod spec and pod annotations for an unstructured @@ -114,8 +113,8 @@ func annotationsFromUnstructuredFields(obj *unstructured.Unstructured, fields [] return annotations, nil } -// checksumFromData create a Sum512_256 checksum for arbitrary data -func checksumFromData(data interface{}) string { +// ChecksumFromData create a Sum512_256 checksum for arbitrary data +func ChecksumFromData(data interface{}) string { encoded, err := yaml.Marshal(data) if err != nil { return ""