diff --git a/doc/README.md b/doc/README.md index 3301ce5..e41fafc 100644 --- a/doc/README.md +++ b/doc/README.md @@ -20,6 +20,7 @@ - [(FR) - Ajouter un layer de type "file d'attente"](./fr/tutorials/add-queue-layer.md) - [(FR) - Amorçage d'un serveur Bouncer via la configuration](./fr/tutorials/bootstrapping.md) +- [(FR) - Intégration avec Kubernetes](./fr/tutorials/kubernetes-integration.md) ### Développement diff --git a/doc/fr/tutorials/kubernetes-integration.md b/doc/fr/tutorials/kubernetes-integration.md new file mode 100644 index 0000000..a6e3b02 --- /dev/null +++ b/doc/fr/tutorials/kubernetes-integration.md @@ -0,0 +1,61 @@ +# Intégration avec Kubernetes + +Dans le cadre du déploiement de Bouncer dans un environnement Kubernetes, il est possible d'activer un mode d'intégration permettant à Bouncer d'exposer des jetons d'authentification directement sous forme de [`Secret`](https://kubernetes.io/fr/docs/concepts/configuration/secret/). + +L'activation et configuration de l'intégration Kubernetes s'effectue dans le fichier de configuration du serveur d'administration via la section `integrations.kubernetes`: + +```yaml +# Section de configuration des intégrations +# avec des produits externes +integrations: + # Intégration avec Kubernetes + kubernetes: + # Activer/désactiver l'intégration Kubernetes + enabled: true + # Créer/mettre à jour un Secret automatiquement avec un jeton d'authentification + # avec le rôle "writer". + # Désactivé si l'attribut est vide ou absent. + writerTokenSecret: my-bouncer-admin-writer-token + # Namespace de destination du Secret pour le jeton d'authentification + # avec le rôle "reader". + # Utilise par défaut le namespace courant si absent ou vide. + writerTokenSecretNamespace: "my-namespace" + # Créer/mettre à jour un Secret automatiquement avec un jeton d'authentification + # avec le rôle "reader". + # Désactivé si l'attribut est vide ou absent. + readerTokenSecret: my-bouncer-admin-reader-token + # Namespace de destination du Secret pour le jeton d'authentification + # avec le rôle "reader". + # Utilise par défaut le namespace courant si absent ou vide. + readerTokenSecretNamespace: "my-namespace" + # Délai maximum alloué au verrou distribué pour la mise à jour + # des secrets. + lockTimeout: 30s +``` + +Vous devrez également définir un `ServiceAccount` pour votre `Pod` avec un `Role` équivalent au suivant (dans le cas nominal où le `Pod` créait les `Secrets` dans son même namespace): + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: bouncer-admin +rules: + - apiGroups: + - "" + - v1 + resources: + - secrets + verbs: + - create + - get + - update +``` + +> **Note** +> +> La génération des jetons d'authentification s'effectue à chaque démarrage du serveur d'administration. Un verrou partagé permet d'éviter que plusieurs instances fonctionnant en parallèle essayent de mettre à jour les ressources Kubernetes au même moment. +> +> De plus, les jetons seront laissés en l'état si la clé de génération n'a pas été modifiée pour éviter de changer les jetons à chaque redémarrage d'un `Pod` (voir l'annotation `bouncer.cadoles.com/public-key` sur les `Secrets` créés.). + +Un exemple fonctionnel de déploiement Kubernetes est disponible dans le répertoire `misc/k8s` du projet. diff --git a/go.mod b/go.mod index 0054e77..8b7abdf 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module forge.cadoles.com/cadoles/bouncer -go 1.20 +go 1.21 + +toolchain go1.22.0 require ( forge.cadoles.com/Cadoles/go-proxy v0.0.0-20230701194111-c6b3d482cca6 @@ -16,6 +18,9 @@ require ( github.com/prometheus/client_golang v1.16.0 github.com/qri-io/jsonschema v0.2.1 github.com/redis/go-redis/v9 v9.0.4 + k8s.io/api v0.29.3 + k8s.io/apimachinery v0.29.3 + k8s.io/client-go v0.29.3 ) require ( @@ -29,22 +34,35 @@ require ( github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/containerd/continuity v0.3.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/docker/cli v20.10.17+incompatible // indirect github.com/docker/docker v20.10.13+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.4.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/go-logr/logr v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/huandu/xstrings v1.3.3 // indirect github.com/imdario/mergo v0.3.12 // indirect - github.com/kr/text v0.2.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/reflectwalk v1.0.0 // indirect github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect github.com/opencontainers/runc v1.1.5 // indirect @@ -53,7 +71,6 @@ require ( github.com/prometheus/procfs v0.10.1 // indirect github.com/qri-io/jsonpointer v0.1.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect - github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect @@ -61,10 +78,21 @@ require ( github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/oauth2 v0.10.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 google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106 // indirect - google.golang.org/protobuf v1.30.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + k8s.io/klog/v2 v2.110.1 // indirect + k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) require ( @@ -98,10 +126,10 @@ require ( gitlab.com/wpetit/goweb v0.0.0-20230419082146-a94d9ed7202b go.opencensus.io v0.24.0 // indirect golang.org/x/crypto v0.17.0 // indirect - golang.org/x/mod v0.9.0 // indirect + golang.org/x/mod v0.14.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/term v0.15.0 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/tools v0.16.1 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect gopkg.in/go-playground/validator.v9 v9.29.1 // indirect gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index 9e7bfec..e01f4c2 100644 --- a/go.sum +++ b/go.sum @@ -83,7 +83,9 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd 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/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= +github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= +github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/bsm/redislock v0.9.4 h1:X/Wse1DPpiQgHbVYRE9zv6m070UcKoOGekgvpNhiSvw= github.com/bsm/redislock v0.9.4/go.mod h1:Epf7AJLiSFwLCiZcfi6pWFO/8eAYrYpQXFxEDPoDeAk= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= @@ -166,6 +168,8 @@ github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g= github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -191,9 +195,18 @@ github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITL github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= 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-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= @@ -201,6 +214,9 @@ github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEK github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -240,12 +256,14 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/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/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -259,7 +277,12 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/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.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +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/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -277,6 +300,7 @@ github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= @@ -310,7 +334,11 @@ github.com/jedib0t/go-pretty/v6 v6.4.6 h1:v6aG9h6Uby3IusSSEjHaZNXpHFhzqMmjXcPq1R github.com/jedib0t/go-pretty/v6 v6.4.6/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -319,6 +347,7 @@ github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -342,6 +371,8 @@ github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -369,17 +400,29 @@ github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +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/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +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/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= @@ -392,6 +435,7 @@ github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4a github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -438,6 +482,8 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -452,6 +498,7 @@ github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= @@ -535,8 +582,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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= @@ -579,7 +626,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -596,6 +644,8 @@ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= +golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= 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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -608,7 +658,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -702,6 +753,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +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/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= @@ -757,8 +810,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +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= @@ -801,6 +854,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -903,22 +957,26 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba 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.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +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= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc= gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= @@ -927,6 +985,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= +gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -934,6 +993,24 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw= +k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80= +k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= +k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= +k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg= +k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0= +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-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-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/internal/admin/bootstrap.go b/internal/admin/bootstrap.go new file mode 100644 index 0000000..4a2f11c --- /dev/null +++ b/internal/admin/bootstrap.go @@ -0,0 +1,112 @@ +package admin + +import ( + "context" + "time" + + "forge.cadoles.com/cadoles/bouncer/internal/config" + "forge.cadoles.com/cadoles/bouncer/internal/lock/redis" + "forge.cadoles.com/cadoles/bouncer/internal/schema" + "forge.cadoles.com/cadoles/bouncer/internal/setup" + "forge.cadoles.com/cadoles/bouncer/internal/store" + "github.com/pkg/errors" + "gitlab.com/wpetit/goweb/logger" +) + +func (s *Server) bootstrapProxies(ctx context.Context) error { + if err := s.validateBootstrap(ctx); err != nil { + return errors.Wrap(err, "could not validate bootstrapped proxies") + } + + proxyRepo := s.proxyRepository + layerRepo := s.layerRepository + + lockTimeout := time.Duration(s.bootstrapConfig.LockTimeout) + locker := redis.NewLocker(s.redisClient) + + err := locker.WithLock(ctx, "bouncer-admin-bootstrap", lockTimeout, func(ctx context.Context) error { + logger.Info(ctx, "bootstrapping proxies") + + for proxyName, proxyConfig := range s.bootstrapConfig.Proxies { + _, err := s.proxyRepository.GetProxy(ctx, proxyName) + if !errors.Is(err, store.ErrNotFound) { + if err != nil { + return errors.WithStack(err) + } + + logger.Info(ctx, "ignoring existing proxy", logger.F("proxyName", proxyName)) + continue + } + + logger.Info(ctx, "creating proxy", logger.F("proxyName", proxyName)) + + if _, err := proxyRepo.CreateProxy(ctx, proxyName, string(proxyConfig.To), proxyConfig.From...); err != nil { + return errors.WithStack(err) + } + + _, err = proxyRepo.UpdateProxy( + ctx, proxyName, + store.WithProxyUpdateEnabled(bool(proxyConfig.Enabled)), + store.WithProxyUpdateWeight(int(proxyConfig.Weight)), + ) + if err != nil { + return errors.WithStack(err) + } + + for layerName, layerConfig := range proxyConfig.Layers { + layerType := store.LayerType(layerConfig.Type) + layerOptions := store.LayerOptions(layerConfig.Options) + + if _, err := layerRepo.CreateLayer(ctx, proxyName, layerName, layerType, layerOptions); err != nil { + return errors.WithStack(err) + } + + _, err := layerRepo.UpdateLayer( + ctx, + proxyName, layerName, + store.WithLayerUpdateEnabled(bool(layerConfig.Enabled)), + store.WithLayerUpdateOptions(layerOptions), + store.WithLayerUpdateWeight(int(layerConfig.Weight)), + ) + if err != nil { + return errors.WithStack(err) + } + } + } + + return nil + }) + if err != nil { + return errors.WithStack(err) + } + + return nil +} + +const validateErrMessage = "could not validate proxy '%s': could not validate layer '%s'" + +func (s *Server) validateBootstrap(ctx context.Context) error { + for proxyName, proxyConf := range s.bootstrapConfig.Proxies { + for layerName, layerConf := range proxyConf.Layers { + layerType := store.LayerType(layerConf.Type) + if !setup.LayerTypeExists(layerType) { + return errors.Errorf(validateErrMessage+": could not find layer type '%s'", proxyName, layerName, layerType) + } + + layerOptionsSchema, err := setup.GetLayerOptionsSchema(layerType) + if err != nil { + return errors.Wrapf(err, validateErrMessage, proxyName, layerName) + } + + rawOptions := func(opts config.InterpolatedMap) map[string]any { + return opts + }(layerConf.Options) + + if err := schema.Validate(ctx, layerOptionsSchema, rawOptions); err != nil { + return errors.Wrapf(err, validateErrMessage, proxyName, layerName) + } + } + } + + return nil +} diff --git a/internal/admin/init.go b/internal/admin/init.go index 3881c22..81b9c4b 100644 --- a/internal/admin/init.go +++ b/internal/admin/init.go @@ -2,15 +2,9 @@ package admin import ( "context" - "time" - "forge.cadoles.com/cadoles/bouncer/internal/config" - "forge.cadoles.com/cadoles/bouncer/internal/schema" "forge.cadoles.com/cadoles/bouncer/internal/setup" - "forge.cadoles.com/cadoles/bouncer/internal/store" - "github.com/bsm/redislock" "github.com/pkg/errors" - "gitlab.com/wpetit/goweb/logger" ) func (s *Server) initRepositories(ctx context.Context) error { @@ -58,112 +52,3 @@ func (s *Server) initProxyRepository(ctx context.Context) error { return nil } - -const bootstrapLockKey = "bouncer-bootstrap" - -func (s *Server) bootstrapProxies(ctx context.Context) error { - if err := s.validateBootstrap(ctx); err != nil { - return errors.Wrap(err, "could not validate bootstrapped proxies") - } - - proxyRepo := s.proxyRepository - layerRepo := s.layerRepository - - locker := redislock.New(s.redisClient) - - backoff := redislock.ExponentialBackoff(time.Second, time.Duration(s.bootstrapConfig.LockTimeout)*2) - - logger.Debug(ctx, "acquiring proxies bootstrap lock", logger.F("lockTimeout", s.bootstrapConfig.LockTimeout)) - - lock, err := locker.Obtain(ctx, bootstrapLockKey, time.Duration(s.bootstrapConfig.LockTimeout), &redislock.Options{ - RetryStrategy: backoff, - }) - if err != nil { - return errors.WithStack(err) - } - - defer func() { - if err := lock.Release(ctx); err != nil { - logger.Error(ctx, "could not release lock", logger.E(errors.WithStack(err))) - } - }() - - logger.Info(ctx, "bootstrapping proxies") - - for proxyName, proxyConfig := range s.bootstrapConfig.Proxies { - _, err := s.proxyRepository.GetProxy(ctx, proxyName) - if !errors.Is(err, store.ErrNotFound) { - if err != nil { - return errors.WithStack(err) - } - - logger.Info(ctx, "ignoring existing proxy", logger.F("proxyName", proxyName)) - continue - } - - logger.Info(ctx, "creating proxy", logger.F("proxyName", proxyName)) - - if _, err := proxyRepo.CreateProxy(ctx, proxyName, string(proxyConfig.To), proxyConfig.From...); err != nil { - return errors.WithStack(err) - } - - _, err = proxyRepo.UpdateProxy( - ctx, proxyName, - store.WithProxyUpdateEnabled(bool(proxyConfig.Enabled)), - store.WithProxyUpdateWeight(int(proxyConfig.Weight)), - ) - if err != nil { - return errors.WithStack(err) - } - - for layerName, layerConfig := range proxyConfig.Layers { - layerType := store.LayerType(layerConfig.Type) - layerOptions := store.LayerOptions(layerConfig.Options) - - if _, err := layerRepo.CreateLayer(ctx, proxyName, layerName, layerType, layerOptions); err != nil { - return errors.WithStack(err) - } - - _, err := layerRepo.UpdateLayer( - ctx, - proxyName, layerName, - store.WithLayerUpdateEnabled(bool(layerConfig.Enabled)), - store.WithLayerUpdateOptions(layerOptions), - store.WithLayerUpdateWeight(int(layerConfig.Weight)), - ) - if err != nil { - return errors.WithStack(err) - } - } - } - - return nil -} - -const validateErrMessage = "could not validate proxy '%s': could not validate layer '%s'" - -func (s *Server) validateBootstrap(ctx context.Context) error { - for proxyName, proxyConf := range s.bootstrapConfig.Proxies { - for layerName, layerConf := range proxyConf.Layers { - layerType := store.LayerType(layerConf.Type) - if !setup.LayerTypeExists(layerType) { - return errors.Errorf(validateErrMessage+": could not find layer type '%s'", proxyName, layerName, layerType) - } - - layerOptionsSchema, err := setup.GetLayerOptionsSchema(layerType) - if err != nil { - return errors.Wrapf(err, validateErrMessage, proxyName, layerName) - } - - rawOptions := func(opts config.InterpolatedMap) map[string]any { - return opts - }(layerConf.Options) - - if err := schema.Validate(ctx, layerOptionsSchema, rawOptions); err != nil { - return errors.Wrapf(err, validateErrMessage, proxyName, layerName) - } - } - } - - return nil -} diff --git a/internal/admin/option.go b/internal/admin/option.go index ddf5954..758a96c 100644 --- a/internal/admin/option.go +++ b/internal/admin/option.go @@ -2,12 +2,14 @@ package admin import ( "forge.cadoles.com/cadoles/bouncer/internal/config" + "forge.cadoles.com/cadoles/bouncer/internal/integration" ) type Option struct { BootstrapConfig config.BootstrapConfig ServerConfig config.AdminServerConfig RedisConfig config.RedisConfig + Integrations []integration.Integration } type OptionFunc func(*Option) @@ -16,6 +18,7 @@ func defaultOption() *Option { return &Option{ ServerConfig: config.NewDefaultAdminServerConfig(), RedisConfig: config.NewDefaultRedisConfig(), + Integrations: make([]integration.Integration, 0), } } @@ -36,3 +39,9 @@ func WithBootstrapConfig(conf config.BootstrapConfig) OptionFunc { opt.BootstrapConfig = conf } } + +func WithIntegrations(integrations ...integration.Integration) OptionFunc { + return func(opt *Option) { + opt.Integrations = integrations + } +} diff --git a/internal/admin/server.go b/internal/admin/server.go index dc0b766..2bd2a69 100644 --- a/internal/admin/server.go +++ b/internal/admin/server.go @@ -11,6 +11,7 @@ import ( "forge.cadoles.com/cadoles/bouncer/internal/auth/jwt" bouncerChi "forge.cadoles.com/cadoles/bouncer/internal/chi" "forge.cadoles.com/cadoles/bouncer/internal/config" + "forge.cadoles.com/cadoles/bouncer/internal/integration" "forge.cadoles.com/cadoles/bouncer/internal/jwk" "forge.cadoles.com/cadoles/bouncer/internal/store" sentryhttp "github.com/getsentry/sentry-go/http" @@ -24,9 +25,13 @@ import ( ) type Server struct { - serverConfig config.AdminServerConfig - redisConfig config.RedisConfig - redisClient redis.UniversalClient + serverConfig config.AdminServerConfig + redisConfig config.RedisConfig + + redisClient redis.UniversalClient + + integrations []integration.Integration + bootstrapConfig config.BootstrapConfig proxyRepository store.ProxyRepository layerRepository store.LayerRepository @@ -62,6 +67,12 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e return } + if err := integration.RunOnStartup(ctx, s.integrations); err != nil { + errs <- errors.WithStack(err) + + return + } + listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", s.serverConfig.HTTP.Host, s.serverConfig.HTTP.Port)) if err != nil { errs <- errors.WithStack(err) @@ -187,5 +198,6 @@ func NewServer(funcs ...OptionFunc) *Server { serverConfig: opt.ServerConfig, redisConfig: opt.RedisConfig, bootstrapConfig: opt.BootstrapConfig, + integrations: opt.Integrations, } } diff --git a/internal/auth/jwt/jwt.go b/internal/auth/jwt/jwt.go index 47638fc..bf89e8f 100644 --- a/internal/auth/jwt/jwt.go +++ b/internal/auth/jwt/jwt.go @@ -16,6 +16,7 @@ const keyRole = "role" func parseToken(ctx context.Context, keys jwk.Set, issuer string, rawToken string, acceptableSkew time.Duration) (jwt.Token, error) { token, err := jwt.Parse( []byte(rawToken), + jwt.WithContext(ctx), jwt.WithKeySet(keys, jws.WithRequireKid(false)), jwt.WithIssuer(issuer), jwt.WithValidate(true), @@ -60,3 +61,17 @@ func GenerateToken(ctx context.Context, key jwk.Key, issuer, subject string, rol return string(rawToken), nil } + +func GenerateTokenWithPrivateKey(ctx context.Context, privateKeyFile string, issuer string, subject string, role Role) (string, jwk.Key, error) { + key, err := jwk.LoadOrGenerate(privateKeyFile, jwk.DefaultKeySize) + if err != nil { + return "", nil, errors.WithStack(err) + } + + token, err := GenerateToken(ctx, key, issuer, subject, role) + if err != nil { + return "", nil, errors.WithStack(err) + } + + return token, key, nil +} diff --git a/internal/command/auth/create_token.go b/internal/command/auth/create_token.go index d639ac9..f454dc3 100644 --- a/internal/command/auth/create_token.go +++ b/internal/command/auth/create_token.go @@ -5,7 +5,6 @@ import ( "forge.cadoles.com/cadoles/bouncer/internal/auth/jwt" "forge.cadoles.com/cadoles/bouncer/internal/command/common" - "forge.cadoles.com/cadoles/bouncer/internal/jwk" "github.com/lithammer/shortuuid/v4" "github.com/pkg/errors" "github.com/urfave/cli/v2" @@ -30,20 +29,15 @@ func CreateTokenCommand() *cli.Command { Action: func(ctx *cli.Context) error { conf, err := common.LoadConfig(ctx) if err != nil { - return errors.Wrap(err, "Could not load configuration") + return errors.Wrap(err, "could not load configuration") } subject := ctx.String("subject") role := ctx.String("role") - key, err := jwk.LoadOrGenerate(string(conf.Admin.Auth.PrivateKey), jwk.DefaultKeySize) + token, _, err := jwt.GenerateTokenWithPrivateKey(ctx.Context, string(conf.Admin.Auth.PrivateKey), string(conf.Admin.Auth.Issuer), subject, jwt.Role(role)) if err != nil { - return errors.WithStack(err) - } - - token, err := jwt.GenerateToken(ctx.Context, key, string(conf.Admin.Auth.Issuer), subject, jwt.Role(role)) - if err != nil { - return errors.WithStack(err) + return errors.Wrap(err, "could not generate token") } fmt.Println(token) diff --git a/internal/command/server/admin/run.go b/internal/command/server/admin/run.go index 33d39e6..7baf7c8 100644 --- a/internal/command/server/admin/run.go +++ b/internal/command/server/admin/run.go @@ -65,10 +65,16 @@ func RunCommand() *cli.Command { logger.SetLevel(logger.Level(conf.Logger.Level)) } + integrations, err := setup.SetupIntegrations(ctx.Context, conf) + if err != nil { + return errors.Wrap(err, "could not setup integrations") + } + srv := admin.NewServer( admin.WithServerConfig(conf.Admin), admin.WithRedisConfig(conf.Redis), admin.WithBootstrapConfig(conf.Bootstrap), + admin.WithIntegrations(integrations...), ) addrs, srvErrs := srv.Start(ctx.Context) diff --git a/internal/config/config.go b/internal/config/config.go index 8ccd40b..0769da0 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -10,12 +10,13 @@ import ( // Config definition type Config struct { - Admin AdminServerConfig `yaml:"admin"` - Proxy ProxyServerConfig `yaml:"proxy"` - Redis RedisConfig `yaml:"redis"` - Logger LoggerConfig `yaml:"logger"` - Layers LayersConfig `yaml:"layers"` - Bootstrap BootstrapConfig `yaml:"bootstrap"` + Admin AdminServerConfig `yaml:"admin"` + Proxy ProxyServerConfig `yaml:"proxy"` + Redis RedisConfig `yaml:"redis"` + Logger LoggerConfig `yaml:"logger"` + Layers LayersConfig `yaml:"layers"` + Bootstrap BootstrapConfig `yaml:"bootstrap"` + Integrations IntegrationsConfig `yaml:"integrations"` } // NewFromFile retrieves the configuration from the given file @@ -44,12 +45,13 @@ func NewDumpDefault() *Config { // NewDefault return new default configuration func NewDefault() *Config { return &Config{ - Admin: NewDefaultAdminServerConfig(), - Proxy: NewDefaultProxyServerConfig(), - Logger: NewDefaultLoggerConfig(), - Redis: NewDefaultRedisConfig(), - Layers: NewDefaultLayersConfig(), - Bootstrap: NewDefaultBootstrapConfig(), + Admin: NewDefaultAdminServerConfig(), + Proxy: NewDefaultProxyServerConfig(), + Logger: NewDefaultLoggerConfig(), + Redis: NewDefaultRedisConfig(), + Layers: NewDefaultLayersConfig(), + Bootstrap: NewDefaultBootstrapConfig(), + Integrations: NewDefaultIntegrationsConfig(), } } diff --git a/internal/config/integrations.go b/internal/config/integrations.go new file mode 100644 index 0000000..be8b51e --- /dev/null +++ b/internal/config/integrations.go @@ -0,0 +1,27 @@ +package config + +import "time" + +type IntegrationsConfig struct { + Kubernetes KubernetesConfig `yaml:"kubernetes"` +} + +func NewDefaultIntegrationsConfig() IntegrationsConfig { + return IntegrationsConfig{ + Kubernetes: KubernetesConfig{ + Enabled: false, + WriterTokenSecret: "", + ReaderTokenSecret: "", + LockTimeout: *NewInterpolatedDuration(30 * time.Second), + }, + } +} + +type KubernetesConfig struct { + Enabled InterpolatedBool `yaml:"enabled"` + WriterTokenSecret InterpolatedString `yaml:"writerTokenSecret"` + WriterTokenSecretNamespace InterpolatedString `yaml:"writerTokenSecretNamespace"` + ReaderTokenSecret InterpolatedString `yaml:"readerTokenSecret"` + ReaderTokenSecretNamespace InterpolatedString `yaml:"readerTokenSecretNamespace"` + LockTimeout InterpolatedDuration `yaml:"lockTimeout"` +} diff --git a/internal/integration/integration.go b/internal/integration/integration.go new file mode 100644 index 0000000..928048a --- /dev/null +++ b/internal/integration/integration.go @@ -0,0 +1,31 @@ +package integration + +import ( + "context" + + "github.com/pkg/errors" +) + +type Integration interface { + Integration() +} + +type OnStartup interface { + Integration + OnStartup(ctx context.Context) error +} + +func RunOnStartup(ctx context.Context, integrations []Integration) error { + for _, it := range integrations { + onStartup, ok := it.(OnStartup) + if !ok { + continue + } + + if err := onStartup.OnStartup(ctx); err != nil { + return errors.WithStack(err) + } + } + + return nil +} diff --git a/internal/integration/kubernetes/integration.go b/internal/integration/kubernetes/integration.go new file mode 100644 index 0000000..85a2cd0 --- /dev/null +++ b/internal/integration/kubernetes/integration.go @@ -0,0 +1,194 @@ +package kubernetes + +import ( + "context" + "crypto" + "fmt" + "os" + + "forge.cadoles.com/cadoles/bouncer/internal/auth/jwt" + "forge.cadoles.com/cadoles/bouncer/internal/integration" + "forge.cadoles.com/cadoles/bouncer/internal/jwk" + "github.com/pkg/errors" + "gitlab.com/wpetit/goweb/logger" + v1 "k8s.io/api/core/v1" + k8serr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" +) + +const ( + namespaceFile = "/var/run/secrets/kubernetes.io/serviceaccount/namespace" + writerTokenSubject = "bouncer-admin-kubernetes-writer" + readerTokenSubject = "bouncer-admin-kubernetes-reader" +) + +type Integration struct { + Options *Options +} + +// Integration implements integration.OnStartup. +func (i *Integration) Integration() {} + +// OnStartup implements integration.OnStartup. +func (i *Integration) OnStartup(ctx context.Context) error { + locker := i.Options.Locker + timeout := i.Options.LockTimeout + err := locker.WithLock(ctx, "bouncer-kubernetes-onstartup", timeout, func(ctx context.Context) error { + config, err := rest.InClusterConfig() + if err != nil { + return errors.WithStack(err) + } + + client, err := kubernetes.NewForConfig(config) + if err != nil { + return errors.WithStack(err) + } + + if i.Options.WriterTokenSecret != "" { + if err := i.upsertTokenSecret(ctx, client, i.Options.WriterTokenSecretNamespace, i.Options.WriterTokenSecret, writerTokenSubject, jwt.RoleWriter); err != nil { + return errors.Wrap(err, "could not upsert writer token secret") + } + } + + if i.Options.ReaderTokenSecret != "" { + if err := i.upsertTokenSecret(ctx, client, i.Options.ReaderTokenSecretNamespace, i.Options.ReaderTokenSecret, readerTokenSubject, jwt.RoleReader); err != nil { + return errors.Wrap(err, "could not upsert reader token secret") + } + } + + return nil + }) + if err != nil { + return errors.WithStack(err) + } + + return nil +} + +const ( + annotationPublicKey = "bouncer.cadoles.com/public-key" +) + +func (i *Integration) upsertTokenSecret(ctx context.Context, client *kubernetes.Clientset, namespace string, name string, subject string, role jwt.Role) error { + if namespace == "" { + defaultNamespace, err := i.getCurrentNamespace() + if err != nil { + return errors.WithStack(err) + } + + namespace = defaultNamespace + } + + ctx = logger.With(ctx, + logger.F("secretNamespace", namespace), + logger.F("secretName", name), + logger.F("tokenRole", role), + logger.F("tokenSubject", subject), + ) + + logger.Debug(ctx, "generating new token") + + key, err := jwk.LoadOrGenerate(i.Options.PrivateKey, jwk.DefaultKeySize) + if err != nil { + return errors.WithStack(err) + } + + publicKey, err := key.PublicKey() + if err != nil { + return errors.WithStack(err) + } + + publicKeyThumbprint, err := publicKey.Thumbprint(crypto.SHA256) + if err != nil { + return errors.WithStack(err) + } + + publicKeyHash := fmt.Sprintf("%x", publicKeyThumbprint) + + alreadyExists := true + secret, err := client.CoreV1().Secrets(namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + if k8serr.IsNotFound(err) { + alreadyExists = false + } else { + return errors.WithStack(err) + } + } + + if !alreadyExists { + token, err := jwt.GenerateToken(ctx, key, i.Options.Issuer, subject, role) + if err != nil { + return errors.WithStack(err) + } + + secret := &v1.Secret{ + Type: v1.SecretTypeOpaque, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Annotations: map[string]string{ + annotationPublicKey: publicKeyHash, + }, + }, + StringData: map[string]string{ + "token": token, + }, + } + + logger.Info(ctx, "creating token secret") + + if _, err := client.CoreV1().Secrets(namespace).Create(ctx, secret, metav1.CreateOptions{}); err != nil { + return errors.WithStack(err) + } + } else { + existingPublicKeyHash, exists := secret.Annotations[annotationPublicKey] + if !exists || publicKeyHash != existingPublicKeyHash { + token, err := jwt.GenerateToken(ctx, key, i.Options.Issuer, subject, role) + if err != nil { + return errors.WithStack(err) + } + + secret.StringData = map[string]string{ + "token": token, + } + + if secret.Annotations == nil { + secret.Annotations = make(map[string]string) + } + + secret.Annotations[annotationPublicKey] = publicKeyHash + + logger.Info(ctx, "updating token secret") + + if _, err := client.CoreV1().Secrets(namespace).Update(ctx, secret, metav1.UpdateOptions{}); err != nil { + return errors.WithStack(err) + } + } else { + logger.Info(ctx, "key did not changed, doing nothing") + } + } + + return nil +} + +func (i *Integration) getCurrentNamespace() (string, error) { + namespace, err := os.ReadFile(namespaceFile) + if err != nil { + return "", errors.Wrap(err, "could not retrieve current namespace") + } + + return string(namespace), nil +} + +func NewIntegration(funcs ...OptionFunc) *Integration { + opts := NewOptions(funcs...) + + return &Integration{ + Options: opts, + } +} + +var ( + _ integration.OnStartup = &Integration{} +) diff --git a/internal/integration/kubernetes/options.go b/internal/integration/kubernetes/options.go new file mode 100644 index 0000000..0a9c925 --- /dev/null +++ b/internal/integration/kubernetes/options.go @@ -0,0 +1,87 @@ +package kubernetes + +import ( + "time" + + "forge.cadoles.com/cadoles/bouncer/internal/lock" + "forge.cadoles.com/cadoles/bouncer/internal/lock/memory" +) + +type Options struct { + WriterTokenSecret string + WriterTokenSecretNamespace string + ReaderTokenSecret string + ReaderTokenSecretNamespace string + PrivateKey string + Issuer string + Locker lock.Locker + LockTimeout time.Duration +} + +type OptionFunc func(opts *Options) + +func NewOptions(funcs ...OptionFunc) *Options { + opts := &Options{ + WriterTokenSecret: "", + WriterTokenSecretNamespace: "", + ReaderTokenSecret: "", + ReaderTokenSecretNamespace: "", + PrivateKey: "", + Issuer: "", + Locker: memory.NewLocker(), + LockTimeout: 30 * time.Second, + } + for _, fn := range funcs { + fn(opts) + } + + return opts +} + +func WithWriterTokenSecret(secretName string) OptionFunc { + return func(opts *Options) { + opts.WriterTokenSecret = secretName + } +} + +func WithWriterTokenSecretNamespace(namespace string) OptionFunc { + return func(opts *Options) { + opts.WriterTokenSecretNamespace = namespace + } +} + +func WithReaderTokenSecret(secretName string) OptionFunc { + return func(opts *Options) { + opts.ReaderTokenSecret = secretName + } +} + +func WithReaderTokenSecretNamespace(namespace string) OptionFunc { + return func(opts *Options) { + opts.ReaderTokenSecretNamespace = namespace + } +} + +func WithPrivateKey(privateKeyFile string) OptionFunc { + return func(opts *Options) { + opts.PrivateKey = privateKeyFile + } +} + +func WithIssuer(issuer string) OptionFunc { + return func(opts *Options) { + opts.Issuer = issuer + } +} + +func WithLocker(locker lock.Locker) OptionFunc { + return func(opts *Options) { + opts.Locker = locker + } +} + +func WithLockTimeout(timeout time.Duration) OptionFunc { + return func(opts *Options) { + opts.LockTimeout = timeout + } +} diff --git a/internal/lock/locker.go b/internal/lock/locker.go new file mode 100644 index 0000000..7a33dd6 --- /dev/null +++ b/internal/lock/locker.go @@ -0,0 +1,10 @@ +package lock + +import ( + "context" + "time" +) + +type Locker interface { + WithLock(ctx context.Context, key string, timeout time.Duration, fn func(ctx context.Context) error) error +} diff --git a/internal/lock/memory/locker.go b/internal/lock/memory/locker.go new file mode 100644 index 0000000..883acbc --- /dev/null +++ b/internal/lock/memory/locker.go @@ -0,0 +1,45 @@ +package memory + +import ( + "context" + "time" + + "forge.cadoles.com/cadoles/bouncer/internal/lock" + "github.com/pkg/errors" +) + +var ( + ErrTimeout = errors.New("timeout") +) + +type Locker struct { + lock chan struct{} +} + +// WithLock implements lock.Locker. +func (l *Locker) WithLock(ctx context.Context, key string, timeout time.Duration, fn func(ctx context.Context) error) error { + select { + case l.lock <- struct{}{}: + defer func() { + <-l.lock + }() + if err := fn(ctx); err != nil { + return errors.WithStack(err) + } + case <-ctx.Done(): + return errors.WithStack(ctx.Err()) + + case <-time.After(timeout): + return errors.WithStack(ErrTimeout) + } + + return nil +} + +func NewLocker() *Locker { + return &Locker{ + lock: make(chan struct{}, 1), + } +} + +var _ lock.Locker = &Locker{} diff --git a/internal/lock/redis/locker.go b/internal/lock/redis/locker.go new file mode 100644 index 0000000..46e1d72 --- /dev/null +++ b/internal/lock/redis/locker.go @@ -0,0 +1,59 @@ +package redis + +import ( + "context" + "time" + + "forge.cadoles.com/cadoles/bouncer/internal/lock" + "github.com/bsm/redislock" + "github.com/pkg/errors" + "github.com/redis/go-redis/v9" + "gitlab.com/wpetit/goweb/logger" +) + +type Locker struct { + client redis.UniversalClient + timeout time.Duration +} + +// WithLock implements lock.Locker. +func (l *Locker) WithLock(ctx context.Context, key string, timeout time.Duration, fn func(ctx context.Context) error) error { + locker := redislock.New(l.client) + + backoff := redislock.ExponentialBackoff(time.Second, timeout*2) + + ctx = logger.With(ctx, logger.F("lockTimeout", timeout), logger.F("lockKey", key)) + + logger.Debug(ctx, "acquiring lock") + + lock, err := locker.Obtain(ctx, key, timeout, &redislock.Options{ + RetryStrategy: backoff, + }) + if err != nil { + return errors.WithStack(err) + } + + logger.Debug(ctx, "lock obtained") + + defer func() { + if err := lock.Release(ctx); err != nil { + logger.Error(ctx, "could not release lock", logger.E(errors.WithStack(err))) + } + + logger.Debug(ctx, "lock released") + }() + + if err := fn(ctx); err != nil { + return errors.WithStack(err) + } + + return nil +} + +func NewLocker(client redis.UniversalClient) *Locker { + return &Locker{ + client: client, + } +} + +var _ lock.Locker = &Locker{} diff --git a/internal/setup/integrations.go b/internal/setup/integrations.go new file mode 100644 index 0000000..92c5910 --- /dev/null +++ b/internal/setup/integrations.go @@ -0,0 +1,45 @@ +package setup + +import ( + "context" + "time" + + "forge.cadoles.com/cadoles/bouncer/internal/config" + "forge.cadoles.com/cadoles/bouncer/internal/integration" + "forge.cadoles.com/cadoles/bouncer/internal/integration/kubernetes" + "forge.cadoles.com/cadoles/bouncer/internal/lock/redis" + "github.com/pkg/errors" +) + +func SetupIntegrations(ctx context.Context, conf *config.Config) ([]integration.Integration, error) { + integrations := make([]integration.Integration, 0) + + if conf.Integrations.Kubernetes.Enabled { + kubernetes, err := setupKubernetesIntegration(ctx, conf) + if err != nil { + return nil, errors.Wrap(err, "could not setup kubernetes integration") + } + + integrations = append(integrations, kubernetes) + } + + return integrations, nil +} + +func setupKubernetesIntegration(ctx context.Context, conf *config.Config) (*kubernetes.Integration, error) { + client := newRedisClient(conf.Redis) + locker := redis.NewLocker(client) + + integration := kubernetes.NewIntegration( + kubernetes.WithReaderTokenSecret(string(conf.Integrations.Kubernetes.ReaderTokenSecret)), + kubernetes.WithReaderTokenSecretNamespace(string(conf.Integrations.Kubernetes.ReaderTokenSecretNamespace)), + kubernetes.WithWriterTokenSecret(string(conf.Integrations.Kubernetes.WriterTokenSecret)), + kubernetes.WithWriterTokenSecretNamespace(string(conf.Integrations.Kubernetes.WriterTokenSecretNamespace)), + kubernetes.WithIssuer(string(conf.Admin.Auth.Issuer)), + kubernetes.WithPrivateKey(string(conf.Admin.Auth.PrivateKey)), + kubernetes.WithLocker(locker), + kubernetes.WithLockTimeout(time.Duration(conf.Integrations.Kubernetes.LockTimeout)), + ) + + return integration, nil +} diff --git a/internal/setup/lock.go b/internal/setup/lock.go new file mode 100644 index 0000000..6946a16 --- /dev/null +++ b/internal/setup/lock.go @@ -0,0 +1,15 @@ +package setup + +import ( + "context" + + "forge.cadoles.com/cadoles/bouncer/internal/config" + "forge.cadoles.com/cadoles/bouncer/internal/lock" + "forge.cadoles.com/cadoles/bouncer/internal/lock/redis" +) + +func SetupLocker(ctx context.Context, conf *config.Config) (lock.Locker, error) { + client := newRedisClient(conf.Redis) + locker := redis.NewLocker(client) + return locker, nil +} diff --git a/misc/k8s/kustomization/base/resources/bouncer-admin/files/config.yml b/misc/k8s/kustomization/base/resources/bouncer-admin/files/config.yml index 826d585..be39024 100644 --- a/misc/k8s/kustomization/base/resources/bouncer-admin/files/config.yml +++ b/misc/k8s/kustomization/base/resources/bouncer-admin/files/config.yml @@ -38,3 +38,9 @@ logger: bootstrap: dir: /etc/bouncer/bootstrap.d lockTimeout: 30s + +integrations: + kubernetes: + enabled: true + writerTokenSecret: ${BOUNCER_WRITER_TOKEN_SECRET} + readerTokenSecret: ${BOUNCER_READER_TOKEN_SECRET} diff --git a/misc/k8s/kustomization/base/resources/bouncer-admin/kustomization.yaml b/misc/k8s/kustomization/base/resources/bouncer-admin/kustomization.yaml index ad1415a..09dceb9 100644 --- a/misc/k8s/kustomization/base/resources/bouncer-admin/kustomization.yaml +++ b/misc/k8s/kustomization/base/resources/bouncer-admin/kustomization.yaml @@ -4,6 +4,7 @@ kind: Kustomization resources: - ./resources/service.yaml - ./resources/deployment.yaml + - ./resources/serviceaccount.yaml configMapGenerator: - name: bouncer-admin-config @@ -14,3 +15,5 @@ configMapGenerator: - name: bouncer-admin-env literals: - BOUNCER_LOG_LEVEL=2 + - BOUNCER_WRITER_TOKEN_SECRET=bouncer-admin-writer-token + - BOUNCER_READER_TOKEN_SECRET=bouncer-admin-reader-token diff --git a/misc/k8s/kustomization/base/resources/bouncer-admin/resources/deployment.yaml b/misc/k8s/kustomization/base/resources/bouncer-admin/resources/deployment.yaml index ac51e09..ea48cfc 100644 --- a/misc/k8s/kustomization/base/resources/bouncer-admin/resources/deployment.yaml +++ b/misc/k8s/kustomization/base/resources/bouncer-admin/resources/deployment.yaml @@ -4,7 +4,8 @@ metadata: name: bouncer-admin labels: app: bouncer-admin - io.kompose.service: bouncer-admin + app.kubernetes.io/name: bouncer-admin + app.kubernetes.io/part-of: bouncer spec: replicas: 3 selector: @@ -14,8 +15,11 @@ spec: metadata: labels: app: bouncer-admin - io.kompose.service: bouncer-admin + app.kubernetes.io/name: bouncer-admin + app.kubernetes.io/part-of: bouncer spec: + restartPolicy: Always + serviceAccountName: bouncer-admin containers: - name: bouncer-admin image: bouncer diff --git a/misc/k8s/kustomization/base/resources/bouncer-admin/resources/service.yaml b/misc/k8s/kustomization/base/resources/bouncer-admin/resources/service.yaml index 258ac42..6dadc68 100644 --- a/misc/k8s/kustomization/base/resources/bouncer-admin/resources/service.yaml +++ b/misc/k8s/kustomization/base/resources/bouncer-admin/resources/service.yaml @@ -2,13 +2,14 @@ apiVersion: v1 kind: Service metadata: labels: - io.kompose.service: bouncer-admin + app.kubernetes.io/name: bouncer-admin + app.kubernetes.io/part-of: bouncer name: bouncer-admin spec: type: ClusterIP ports: - - name: bouncer-admin - port: 8081 - targetPort: bouncer-admin + - name: bouncer-admin + port: 8081 + targetPort: bouncer-admin selector: - io.kompose.service: bouncer-admin + app.kubernetes.io/name: bouncer-admin diff --git a/misc/k8s/kustomization/base/resources/bouncer-admin/resources/serviceaccount.yaml b/misc/k8s/kustomization/base/resources/bouncer-admin/resources/serviceaccount.yaml new file mode 100644 index 0000000..bed18a5 --- /dev/null +++ b/misc/k8s/kustomization/base/resources/bouncer-admin/resources/serviceaccount.yaml @@ -0,0 +1,32 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: bouncer-admin +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: bouncer-admin +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: bouncer-admin +subjects: + - kind: ServiceAccount + name: bouncer-admin +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: bouncer-admin +rules: + - apiGroups: + - "" + - v1 + resources: + - secrets + verbs: + - create + - get + - update diff --git a/misc/k8s/kustomization/base/resources/bouncer-server/resources/deployment.yaml b/misc/k8s/kustomization/base/resources/bouncer-server/resources/deployment.yaml index bbd20e3..1c714e8 100644 --- a/misc/k8s/kustomization/base/resources/bouncer-server/resources/deployment.yaml +++ b/misc/k8s/kustomization/base/resources/bouncer-server/resources/deployment.yaml @@ -4,7 +4,8 @@ metadata: name: bouncer-server labels: app: bouncer-server - io.kompose.service: bouncer-server + app.kubernetes.io/name: bouncer-server + app.kubernetes.io/part-of: bouncer spec: replicas: 3 selector: @@ -14,7 +15,8 @@ spec: metadata: labels: app: bouncer-server - io.kompose.service: bouncer-server + app.kubernetes.io/name: bouncer-server + app.kubernetes.io/part-of: bouncer spec: containers: - name: bouncer-server diff --git a/misc/k8s/kustomization/base/resources/bouncer-server/resources/service.yaml b/misc/k8s/kustomization/base/resources/bouncer-server/resources/service.yaml index 16e1ab3..78a0f1e 100644 --- a/misc/k8s/kustomization/base/resources/bouncer-server/resources/service.yaml +++ b/misc/k8s/kustomization/base/resources/bouncer-server/resources/service.yaml @@ -2,13 +2,14 @@ apiVersion: v1 kind: Service metadata: labels: - io.kompose.service: bouncer-server + app.kubernetes.io/name: bouncer-server + app.kubernetes.io/part-of: bouncer name: bouncer-server spec: type: ClusterIP ports: - - name: bouncer-server - port: 8080 - targetPort: bouncer-server + - name: bouncer-server + port: 8080 + targetPort: bouncer-server selector: - io.kompose.service: bouncer-server + app.kubernetes.io/name: bouncer-server diff --git a/skaffold.yaml b/skaffold.yaml index c63feb0..65d73f9 100644 --- a/skaffold.yaml +++ b/skaffold.yaml @@ -23,7 +23,7 @@ build: push: true tagPolicy: - sha256: {} + inputDigest: {} artifacts: - image: bouncer