From 67efc9c79c196a5f5842b5593aaf4ca793acd24b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20Valais?= <mael@vls.dev>
Date: Wed, 3 Jul 2024 09:02:49 +0200
Subject: [PATCH 01/16] venconn: update the Go dependencies so that we can use
 venafi-connection-lib

The venafi-connection-lib library uses more recent versions of the
dependencies in the Venafi Kubernetes Agent. When I ran

  export GOPRIVATE=github.com/jetstack
  go get github.com/jetstack/venafi-connection-lib@latest

most of the dependencies in this project got updated. This commit
captures these changes.
---
 go.mod | 53 ++++++++++++++++++++++++++++-------------------------
 go.sum | 28 ++++++++++++++++++++++++++++
 2 files changed, 56 insertions(+), 25 deletions(-)

diff --git a/go.mod b/go.mod
index fc84bf7d..572e92a5 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,8 @@
 module github.com/jetstack/preflight
 
-go 1.21
+go 1.22.0
+
+toolchain go1.22.3
 
 require (
 	github.com/Jeffail/gabs/v2 v2.7.0
@@ -16,14 +18,14 @@ require (
 	github.com/microcosm-cc/bluemonday v1.0.26
 	github.com/pkg/errors v0.9.1
 	github.com/pmylund/go-cache v2.1.0+incompatible
-	github.com/prometheus/client_golang v1.18.0
+	github.com/prometheus/client_golang v1.19.1
 	github.com/spf13/cobra v1.8.0
 	github.com/spf13/pflag v1.0.5
 	gopkg.in/d4l3k/messagediff.v1 v1.2.1
 	gopkg.in/yaml.v2 v2.4.0
-	k8s.io/api v0.28.3
-	k8s.io/apimachinery v0.28.3
-	k8s.io/client-go v0.28.3
+	k8s.io/api v0.30.0
+	k8s.io/apimachinery v0.30.0
+	k8s.io/client-go v0.30.0
 	sigs.k8s.io/yaml v1.4.0
 )
 
@@ -31,23 +33,24 @@ require (
 	github.com/aymerick/douceur v0.2.0 // indirect
 	github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
 	github.com/gorilla/css v1.0.0 // indirect
+	github.com/jetstack/venafi-connection-lib v0.1.0 // indirect
 	github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
-	golang.org/x/net v0.17.0 // indirect
+	golang.org/x/net v0.24.0 // indirect
 )
 
 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.1 // indirect
-	github.com/emicklei/go-restful/v3 v3.11.0 // indirect
-	github.com/evanphx/json-patch v4.12.0+incompatible // indirect
-	github.com/go-logr/logr v1.2.4 // 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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
+	github.com/emicklei/go-restful/v3 v3.11.2 // indirect
+	github.com/evanphx/json-patch v5.9.0+incompatible // indirect
+	github.com/go-logr/logr v1.4.1 // indirect
+	github.com/go-openapi/jsonpointer v0.20.2 // indirect
+	github.com/go-openapi/jsonreference v0.20.4 // indirect
+	github.com/go-openapi/swag v0.22.9 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang-jwt/jwt/v4 v4.5.0
-	github.com/golang/protobuf v1.5.3 // indirect
+	github.com/golang/protobuf v1.5.4 // indirect
 	github.com/google/go-cmp v0.6.0 // indirect
 	github.com/google/gofuzz v1.2.0 // indirect
 	github.com/hashicorp/errwrap v1.1.0 // indirect
@@ -61,20 +64,20 @@ require (
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
 	github.com/prometheus/client_model v0.5.0 // indirect
-	github.com/prometheus/common v0.45.0 // indirect
+	github.com/prometheus/common v0.48.0 // indirect
 	github.com/prometheus/procfs v0.12.0 // indirect
-	golang.org/x/oauth2 v0.13.0 // indirect
-	golang.org/x/sys v0.15.0 // indirect
-	golang.org/x/term v0.13.0 // indirect
-	golang.org/x/text v0.13.0 // indirect
-	golang.org/x/time v0.3.0 // indirect
+	golang.org/x/oauth2 v0.16.0 // indirect
+	golang.org/x/sys v0.19.0 // indirect
+	golang.org/x/term v0.19.0 // indirect
+	golang.org/x/text v0.14.0 // indirect
+	golang.org/x/time v0.5.0 // indirect
 	google.golang.org/appengine v1.6.8 // indirect
-	google.golang.org/protobuf v1.31.0 // indirect
+	google.golang.org/protobuf v1.34.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1
-	k8s.io/klog/v2 v2.100.1 // indirect
-	k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
-	k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
+	k8s.io/klog/v2 v2.120.1 // indirect
+	k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f // indirect
+	k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 // indirect
 	sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
-	sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect
+	sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
 )
diff --git a/go.sum b/go.sum
index 4a9d1e8b..81858805 100644
--- a/go.sum
+++ b/go.sum
@@ -15,23 +15,30 @@ github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkE
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
 github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/emicklei/go-restful/v3 v3.11.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
 github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
 github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
 github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
 github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
 github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 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/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
 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/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4=
 github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
 github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
 github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE=
 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/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@@ -42,6 +49,7 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
 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/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
 github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU=
 github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -66,6 +74,8 @@ 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/jetstack/venafi-connection-lib v0.1.0 h1:lNPI3gdmXUONGyVlRHDU51T2D61FWid0D7zvTiiDN9w=
+github.com/jetstack/venafi-connection-lib v0.1.0/go.mod h1:lKfibKF2wyMzcaFXg8GIuLILK9zcQRk5RuxfsQYLVLI=
 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/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -105,6 +115,7 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
 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/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g=
 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/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -115,10 +126,12 @@ github.com/pmylund/go-cache v2.1.0+incompatible h1:n+7K51jLz6a3sCvff3BppuCAkixuD
 github.com/pmylund/go-cache v2.1.0+incompatible/go.mod h1:hmz95dGvINpbRZGsqPcd7B5xXY5+EKb5PpGhQY3NTHk=
 github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
 github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
+github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
 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/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
 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.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
@@ -155,8 +168,10 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
 golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
 golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY=
 golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0=
+golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
 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=
@@ -172,18 +187,22 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
 golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.19.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.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
 golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
+golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
 golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
 golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+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/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
@@ -201,6 +220,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
 google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4=
+google.golang.org/protobuf v1.34.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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -216,19 +237,26 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM=
 k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc=
+k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE=
 k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A=
 k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8=
+k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
 k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4=
 k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo=
+k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY=
 k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
 k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
+k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
 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/kube-openapi v0.0.0-20240430033511-f0e62f92d13f/go.mod h1:S9tOR0FxgyusSNR+MboCuiDpVWkAifZvaYI1Q2ubgro=
 k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
 k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
 sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
 sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
 sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk=
 sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
+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=

From 55c3ab1848c812eed5e01f4a3e217f1faa63638b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20Valais?= <mael@vls.dev>
Date: Tue, 16 Jul 2024 19:47:33 +0200
Subject: [PATCH 02/16] venconn: add support for VenafiConnection CRD with
 --venafi-connection=name

I've also added the flag --venafi-connection-namespace as well as the
flag --install-namespace that is only needed when running the agent
out-of-cluster (this is only useful for testing purposes).
---
 cmd/agent.go                                  |  18 ++
 .../templates/rbac.yaml                       |  27 +++
 go.mod                                        |  30 ++-
 go.sum                                        |  74 +++++++
 pkg/agent/run.go                              |  93 ++++++++-
 pkg/client/client_venconn.go                  | 190 ++++++++++++++++++
 6 files changed, 427 insertions(+), 5 deletions(-)
 create mode 100644 pkg/client/client_venconn.go

diff --git a/cmd/agent.go b/cmd/agent.go
index 9dee283c..acef4c73 100644
--- a/cmd/agent.go
+++ b/cmd/agent.go
@@ -138,6 +138,24 @@ func init() {
 		os.Getenv("API_TOKEN"),
 		"Token used for authentication when API tokens are in use on the backend",
 	)
+	agentCmd.PersistentFlags().StringVar(
+		&agent.VenConnName,
+		"venafi-connection",
+		"",
+		"Name of the VenafiConnection to be used. Using this flag will enable the VenafiConnection mode.",
+	)
+	agentCmd.PersistentFlags().StringVar(
+		&agent.VenConnNS,
+		"venafi-connection-namespace",
+		"",
+		"Namespace of the VenafiConnection to be used. It is only useful when the VenafiConnection isn't in the same namespace as the agent. The field `allowReferencesFrom` must be present on the cross-namespace VenafiConnection for the agent to use it.",
+	)
+	agentCmd.PersistentFlags().StringVar(
+		&agent.InstallNS,
+		"install-namespace",
+		"",
+		"Namespace in which the agent is running. Only needed when running the agent outside of Kubernetes.",
+	)
 	agentCmd.PersistentFlags().BoolVarP(
 		&agent.Profiling,
 		"enable-pprof",
diff --git a/deploy/charts/venafi-kubernetes-agent/templates/rbac.yaml b/deploy/charts/venafi-kubernetes-agent/templates/rbac.yaml
index c3c2fcc8..693623d6 100644
--- a/deploy/charts/venafi-kubernetes-agent/templates/rbac.yaml
+++ b/deploy/charts/venafi-kubernetes-agent/templates/rbac.yaml
@@ -1,5 +1,32 @@
 ---
 apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: {{ include "venafi-kubernetes-agent.fullname" . }}-venaficonnection
+  labels:
+    {{- include "venafi-kubernetes-agent.labels" . | nindent 4 }}
+rules:
+  - apiGroups: ["jetstack.io"]
+    resources:
+      - venaficonnections
+    verbs: ["get", "list", "watch", "update"]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: {{ include "venafi-kubernetes-agent.fullname" . }}-venaficonnection
+  labels:
+    {{- include "venafi-kubernetes-agent.labels" . | nindent 4 }}
+roleRef:
+  kind: ClusterRole
+  name: {{ include "venafi-kubernetes-agent.fullname" . }}-venaficonnection
+  apiGroup: rbac.authorization.k8s.io
+subjects:
+  - kind: ServiceAccount
+    name: {{ include "venafi-kubernetes-agent.serviceAccountName" . }}
+    namespace: {{ .Release.Namespace }}
+---
+apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRoleBinding
 metadata:
   name: {{ include "venafi-kubernetes-agent.fullname" . }}-cluster-viewer
diff --git a/go.mod b/go.mod
index 572e92a5..047af32d 100644
--- a/go.mod
+++ b/go.mod
@@ -1,8 +1,6 @@
 module github.com/jetstack/preflight
 
-go 1.22.0
-
-toolchain go1.22.3
+go 1.22
 
 require (
 	github.com/Jeffail/gabs/v2 v2.7.0
@@ -26,16 +24,40 @@ require (
 	k8s.io/api v0.30.0
 	k8s.io/apimachinery v0.30.0
 	k8s.io/client-go v0.30.0
+	sigs.k8s.io/controller-runtime v0.18.2
 	sigs.k8s.io/yaml v1.4.0
 )
 
 require (
+	github.com/Venafi/vcert/v5 v5.6.5-0.20240430163638-86272e17ec45 // indirect
+	github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect
+	github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
 	github.com/aymerick/douceur v0.2.0 // indirect
+	github.com/blang/semver/v4 v4.0.0 // indirect
+	github.com/evanphx/json-patch/v5 v5.9.0 // indirect
+	github.com/fsnotify/fsnotify v1.7.0 // indirect
+	github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a // indirect
+	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
+	github.com/google/cel-go v0.17.8 // indirect
 	github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
 	github.com/gorilla/css v1.0.0 // indirect
-	github.com/jetstack/venafi-connection-lib v0.1.0 // indirect
 	github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
+	github.com/sosodev/duration v1.2.0 // indirect
+	github.com/stoewer/go-strcase v1.3.0 // indirect
+	github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
+	go.uber.org/multierr v1.11.0 // indirect
+	go.uber.org/zap v1.26.0 // indirect
+	golang.org/x/crypto v0.22.0 // indirect
+	golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect
 	golang.org/x/net v0.24.0 // indirect
+	golang.org/x/sync v0.7.0 // indirect
+	gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe // indirect
+	gopkg.in/ini.v1 v1.67.0 // indirect
+	k8s.io/apiextensions-apiserver v0.30.0 // indirect
+	k8s.io/apiserver v0.30.0 // indirect
+	k8s.io/component-base v0.30.0 // indirect
 )
 
 require (
diff --git a/go.sum b/go.sum
index 81858805..289bcb6a 100644
--- a/go.sum
+++ b/go.sum
@@ -1,9 +1,17 @@
 github.com/Jeffail/gabs/v2 v2.7.0 h1:Y2edYaTcE8ZpRsR2AtmPu5xQdFDIthFG0jYhu5PY8kg=
 github.com/Jeffail/gabs/v2 v2.7.0/go.mod h1:dp5ocw1FvBBQYssgHsG7I1WYsiLRtkUaB1FEtSwvNUw=
+github.com/Venafi/vcert/v5 v5.6.5-0.20240430163638-86272e17ec45 h1:vGAu/uGLC0KupzIxEF28gmry0SaS2uVjlxOFSeR0Feg=
+github.com/Venafi/vcert/v5 v5.6.5-0.20240430163638-86272e17ec45/go.mod h1:6NgXvi7m0MJzma4vNDmoMt0Pj12pGPKLPr293kcdyEA=
+github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18=
+github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
+github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
+github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
 github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
 github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
 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/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
 github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
 github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
@@ -15,29 +23,42 @@ github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkE
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
 github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/emicklei/go-restful/v3 v3.11.2 h1:1onLa9DcsMYO9P+CXaL0dStDqQ2EHHXLiz+BtnqkLAU=
 github.com/emicklei/go-restful/v3 v3.11.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
 github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
 github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
 github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
+github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
 github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
 github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
+github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
+github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
+github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a h1:v6zMvHuY9yue4+QkG/HQ/W67wvtQmWJ4SDo9aK/GIno=
+github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a/go.mod h1:I79BieaU4fxrw4LMXby6q5OS9XnoR9UIKLOzDFjUmuw=
 github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
 github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
 github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 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/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
 github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
 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/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU=
 github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4=
 github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
 github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
 github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE=
 github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE=
 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=
@@ -45,11 +66,16 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
 github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+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/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.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/google/cel-go v0.17.8 h1:j9m730pMZt1Fc4oKhCLUHfjj6527LuhYcYw0Rl8gqto=
+github.com/google/cel-go v0.17.8/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY=
 github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU=
 github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -126,21 +152,27 @@ github.com/pmylund/go-cache v2.1.0+incompatible h1:n+7K51jLz6a3sCvff3BppuCAkixuD
 github.com/pmylund/go-cache v2.1.0+incompatible/go.mod h1:hmz95dGvINpbRZGsqPcd7B5xXY5+EKb5PpGhQY3NTHk=
 github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
 github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
+github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
 github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
 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/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
 github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
 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.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
 github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/sosodev/duration v1.2.0 h1:pqK/FLSjsAADWY74SyWDCjOcd5l7H8GSnnOGEB9A1Us=
+github.com/sosodev/duration v1.2.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
 github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
 github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
 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/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
+github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
 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=
@@ -150,13 +182,24 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
 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/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
+github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
 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.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-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 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/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
+golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
+golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
+golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
 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=
@@ -168,14 +211,18 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
 golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
 golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
 golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY=
 golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0=
+golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
 golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
 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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 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=
@@ -187,11 +234,13 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
 golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
 golang.org/x/sys v0.19.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.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
 golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
+golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
 golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -199,9 +248,11 @@ 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.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
 golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -214,8 +265,14 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
 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.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
 google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
+google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe h1:0poefMBYvYbs7g5UkjS6HcxBPaTRAmznle9jnxYoAI8=
+google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe h1:bQnxqljG/wqi4NTXu2+DJ3n7APcEA882QZ1JvhQAq9o=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
 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.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
@@ -229,6 +286,8 @@ gopkg.in/d4l3k/messagediff.v1 v1.2.1 h1:70AthpjunwzUiarMHyED52mj9UwtAnE89l1Gmrt3
 gopkg.in/d4l3k/messagediff.v1 v1.2.1/go.mod h1:EUzikiKadqXWcD1AzJLagx0j/BeeWGtn++04Xniyg44=
 gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/yaml.v2 v2.2.8/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=
@@ -237,26 +296,41 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM=
 k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc=
+k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA=
 k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE=
+k8s.io/apiextensions-apiserver v0.30.0 h1:jcZFKMqnICJfRxTgnC4E+Hpcq8UEhT8B2lhBcQ+6uAs=
+k8s.io/apiextensions-apiserver v0.30.0/go.mod h1:N9ogQFGcrbWqAY9p2mUAL5mGxsLqwgtUce127VtRX5Y=
 k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A=
 k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8=
+k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA=
 k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
+k8s.io/apiserver v0.30.0 h1:QCec+U72tMQ+9tR6A0sMBB5Vh6ImCEkoKkTDRABWq6M=
+k8s.io/apiserver v0.30.0/go.mod h1:smOIBq8t0MbKZi7O7SyIpjPsiKJ8qa+llcFCluKyqiY=
 k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4=
 k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo=
+k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ=
 k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY=
+k8s.io/component-base v0.30.0 h1:cj6bp38g0ainlfYtaOQuRELh5KSYjhKxM+io7AUIk4o=
+k8s.io/component-base v0.30.0/go.mod h1:V9x/0ePFNaKeKYA3bOvIbrNoluTSG+fSJKjLdjOoeXQ=
 k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
 k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
+k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
 k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
 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/kube-openapi v0.0.0-20240430033511-f0e62f92d13f h1:0LQagt0gDpKqvIkAMPaRGcXawNMouPECM1+F9BVxEaM=
 k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f/go.mod h1:S9tOR0FxgyusSNR+MboCuiDpVWkAifZvaYI1Q2ubgro=
 k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
 k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak=
 k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+sigs.k8s.io/controller-runtime v0.18.2 h1:RqVW6Kpeaji67CY5nPEfRz6ZfFMk0lWQlNrLqlNpx+Q=
+sigs.k8s.io/controller-runtime v0.18.2/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw=
 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.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk=
 sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
+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/agent/run.go b/pkg/agent/run.go
index 799cb232..90021c97 100644
--- a/pkg/agent/run.go
+++ b/pkg/agent/run.go
@@ -22,6 +22,7 @@ import (
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
 	"github.com/spf13/cobra"
+	"sigs.k8s.io/controller-runtime/pkg/manager"
 
 	"github.com/jetstack/preflight/api"
 	"github.com/jetstack/preflight/pkg/client"
@@ -65,6 +66,24 @@ var StrictMode bool
 // APIToken is an authentication token used for the backend API as an alternative to oauth flows.
 var APIToken string
 
+// VenConnName is the name of the VenafiConnection resource to use. Using this
+// flag will enable Venafi Connection mode.
+var VenConnName string
+
+// VenConnNS is the namespace of the VenafiConnection resource to use. It is
+// only useful when the VenafiConnection isn't in the same namespace as the
+// agent.
+//
+// May be left empty to use the same namespace as the agent.
+var VenConnNS string
+
+// InstallNS is the namespace in which the agent is running in. Only needed when
+// running the agent outside of Kubernetes.
+//
+// May be left empty when running in Kubernetes. In this case, the namespace is
+// read from the file /var/run/secrets/kubernetes.io/serviceaccount/namespace.
+var InstallNS string
+
 // Profiling flag enabled pprof endpoints to run on the agent
 var Profiling bool
 
@@ -79,6 +98,8 @@ var Prometheus bool
 // raw resource data of unstructuredList
 const schemaVersion string = "v2.0.0"
 
+const inClusterNamespacePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
+
 // Run starts the agent process
 func Run(cmd *cobra.Command, args []string) {
 	ctx, cancel := context.WithCancel(context.Background())
@@ -107,6 +128,19 @@ func Run(cmd *cobra.Command, args []string) {
 		}()
 	}
 
+	_, isVenConn := preflightClient.(*client.VenConnClient)
+	if isVenConn {
+		go func() {
+			err := preflightClient.(manager.Runnable).Start(ctx)
+			if err != nil {
+				log.Fatalf("failed to start a controller-runtime component: %v", err)
+			}
+
+			// The agent must stop if the controller-runtime component stops.
+			cancel()
+		}()
+	}
+
 	dataGatherers := map[string]datagatherer.DataGatherer{}
 	var wg sync.WaitGroup
 
@@ -201,7 +235,7 @@ func getConfiguration() (Config, client.Client) {
 	}
 
 	// If the ClientID of the service account is specified, then assume we are in Venafi Cloud mode.
-	if ClientID != "" {
+	if ClientID != "" || VenConnName != "" {
 		VenafiCloudMode = true
 	}
 
@@ -258,6 +292,18 @@ func getConfiguration() (Config, client.Client) {
 		}
 	}
 
+	venConnMode := VenConnName != ""
+
+	if venConnMode && InstallNS == "" {
+		InstallNS, err = getInClusterNamespace()
+		if err != nil {
+			log.Fatalf("could not guess which namespace the agent is running in: %s", err)
+		}
+	}
+	if venConnMode && VenConnNS == "" {
+		VenConnNS = InstallNS
+	}
+
 	agentMetadata := &api.AgentMetadata{
 		Version:   version.PreflightVersion,
 		ClusterID: config.ClusterID,
@@ -267,6 +313,32 @@ func getConfiguration() (Config, client.Client) {
 	switch {
 	case credentials != nil:
 		preflightClient, err = createCredentialClient(credentials, config, agentMetadata, baseURL)
+	case VenConnName != "":
+		// Why wasn't this added to the createCredentialClient instead? Because
+		// the --venafi-connection mode of authentication doesn't need any
+		// secrets (or any other information for that matter) to be loaded from
+		// disk (using --credentials-path). Everything is passed as flags.
+		log.Println("Venafi Connection mode was specified, using Venafi Connection authentication.")
+
+		// The venafi-cloud.upload_path was initially meant to let users
+		// configure HTTP proxies, but it has never been used since HTTP proxies
+		// don't rewrite paths. Thus, we've disabled the ability to change this
+		// value with the new --venafi-connection flag, and this field is simply
+		// ignored.
+		if config.VenafiCloud.UploadPath != "" {
+			log.Printf(`ignoring venafi-cloud.upload_path. In Venafi Connection mode, this field is not needed.`)
+		}
+
+		// Regarding venafi-cloud.uploader_id, we found that it doesn't do
+		// anything in the backend. Since the backend requires it for historical
+		// reasons (but cannot be empty), we just ignore whatever the user has
+		// set in the config file, and set it to an arbitrary value in the
+		// client since it doesn't matter.
+		if config.VenafiCloud.UploaderID != "" {
+			log.Printf(`ignoring venafi-cloud.uploader_id. In Venafi Connection mode, this field is not needed.`)
+		}
+
+		preflightClient, err = client.NewVenConnClient(&http.Client{}, agentMetadata, baseURL, InstallNS, VenConnName, VenConnNS)
 	case APIToken != "":
 		logs.Log.Println("An API token was specified, using API token authentication.")
 		preflightClient, err = client.NewAPITokenClient(agentMetadata, APIToken, baseURL)
@@ -468,3 +540,22 @@ func postData(config Config, preflightClient client.Client, readings []*api.Data
 
 	return nil
 }
+
+// Inspired from the controller-runtime project.
+func getInClusterNamespace() (string, error) {
+	// Check whether the namespace file exists.
+	// If not, we are not running in cluster so can't guess the namespace.
+	_, err := os.Stat(inClusterNamespacePath)
+	if os.IsNotExist(err) {
+		return "", fmt.Errorf("not running in cluster, please use --install-namespace to specify the namespace in which the agent is running")
+	}
+	if err != nil {
+		return "", fmt.Errorf("error checking namespace file: %w", err)
+	}
+
+	namespace, err := os.ReadFile(inClusterNamespacePath)
+	if err != nil {
+		return "", fmt.Errorf("error reading namespace file: %w", err)
+	}
+	return string(namespace), nil
+}
diff --git a/pkg/client/client_venconn.go b/pkg/client/client_venconn.go
new file mode 100644
index 00000000..ae9593bb
--- /dev/null
+++ b/pkg/client/client_venconn.go
@@ -0,0 +1,190 @@
+package client
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"log"
+	"log/slog"
+	"net/http"
+	"path/filepath"
+	"time"
+
+	"github.com/go-logr/logr"
+	"github.com/jetstack/preflight/api"
+	"github.com/jetstack/preflight/pkg/version"
+	venapi "github.com/jetstack/venafi-connection-lib/api/v1alpha1"
+	"github.com/jetstack/venafi-connection-lib/venafi_client"
+	"github.com/jetstack/venafi-connection-lib/venafi_client/auth"
+	"github.com/pkg/errors"
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/client-go/rest"
+	"k8s.io/client-go/tools/clientcmd"
+	"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
+	ll "sigs.k8s.io/controller-runtime/pkg/log"
+)
+
+type VenConnClient struct {
+	baseURL       string
+	agentMetadata *api.AgentMetadata
+	connHandler   venafi_client.ConnectionHandler
+	installNS     string       // Namespace in which the agent is running in.
+	venConnName   string       // Name of the VenafiConnection resource to use.
+	venConnNS     string       // Namespace of the VenafiConnection resource to use.
+	client        *http.Client // Used to make HTTP requests to Venafi Cloud.
+}
+
+// NewVenConnClient lets you make requests to the Venafi Cloud backend using the
+// given VenafiConnection resource. You need to call Start to start watching the
+// VenafiConnection resource. If you don't, the client will be unable to find
+// the VenafiConnection that you are referring to as its client-go cache will
+// remain empty.
+func NewVenConnClient(c *http.Client, agentMetadata *api.AgentMetadata, baseURL, installNS, venConnName, venConnNS string) (*VenConnClient, error) {
+	if installNS == "" {
+		return nil, errors.New("programmer mistake: installNS must be provided")
+	}
+	if venConnName == "" {
+		return nil, errors.New("programmer mistake: venConnName must be provided")
+	}
+	if venConnNS == "" {
+		return nil, errors.New("programmer mistake: venConnNS must be provided")
+	}
+
+	cfg, err := loadRESTConfig("")
+	if err != nil {
+		return nil, errors.WithStack(err)
+	}
+	cfg.Impersonate = rest.ImpersonationConfig{
+		UserName: fmt.Sprintf("system:serviceaccount:%s:venafi-connection", installNS),
+	}
+	restMapper, err := apiutil.NewDynamicRESTMapper(cfg, &http.Client{})
+	if err != nil {
+		return nil, errors.WithStack(err)
+	}
+
+	ll.SetLogger(logr.FromSlogHandler(slog.Default().Handler()))
+
+	// This Kubernetes client only needs to be able to read and write the
+	// VenafiConnection resources and read Secret resources.
+	scheme := runtime.NewScheme()
+	_ = venapi.AddToScheme(scheme)
+	_ = corev1.AddToScheme(scheme)
+
+	handler, err := venafi_client.NewConnectionHandler(
+		"venafi-kubernetes-agent/"+version.PreflightVersion,
+		"venafi-kubernetes-agent.jetstack.io",
+		"VenafiKubernetesAgent",
+		cfg,
+		scheme,
+		restMapper,
+		nil,
+	)
+	if err != nil {
+		return nil, err
+	}
+
+	return &VenConnClient{
+		baseURL:       baseURL,
+		agentMetadata: agentMetadata,
+		connHandler:   handler,
+		installNS:     installNS,
+		venConnName:   venConnName,
+		venConnNS:     venConnNS,
+		client:        c,
+	}, nil
+}
+
+// Start starts watching VenafiConnections. This function will return soon after
+// the context is closed, or if an error occurs.
+func (c *VenConnClient) Start(ctx context.Context) error {
+	return c.connHandler.CacheRunnable().Start(ctx)
+}
+
+func (c *VenConnClient) PostDataReadingsWithOptions(readings []*api.DataReading, opts Options) error {
+	return c.PostDataReadings(opts.OrgID, opts.ClusterID, readings)
+}
+
+// PostDataReadings uploads the slice of api.DataReading to the Jetstack Secure backend to be processed for later
+// viewing in the user-interface.
+func (c *VenConnClient) PostDataReadings(orgID, clusterID string, readings []*api.DataReading) error {
+	payload := api.DataReadingsPost{
+		AgentMetadata:  c.agentMetadata,
+		DataGatherTime: time.Now().UTC(),
+		DataReadings:   readings,
+	}
+	data, err := json.Marshal(payload)
+	if err != nil {
+		return err
+	}
+
+	res, err := c.Post(filepath.Join("/api/v1/org", orgID, "datareadings", clusterID), bytes.NewBuffer(data))
+	if err != nil {
+		return err
+	}
+	defer res.Body.Close()
+
+	if code := res.StatusCode; code < 200 || code >= 300 {
+		errorContent := ""
+		body, err := ioutil.ReadAll(res.Body)
+		if err == nil {
+			errorContent = string(body)
+		}
+
+		return fmt.Errorf("received response with status code %d. Body: [%s]", code, errorContent)
+	}
+
+	return nil
+}
+
+// Post performs an HTTP POST request.
+func (c *VenConnClient) Post(path string, body io.Reader) (*http.Response, error) {
+	// The VenafiConnection must be in the same namespace as the agent. It can't
+	log.Printf("Getting Venafi connection details from %s/%s", c.venConnNS, c.venConnName)
+	_, token, err := c.connHandler.Get(context.Background(), c.installNS, auth.Scope{}, types.NamespacedName{Name: c.venConnName, Namespace: c.venConnNS})
+	if err != nil {
+		return nil, err
+	}
+
+	req, err := http.NewRequest(http.MethodPost, fullURL(c.baseURL, path), body)
+	if err != nil {
+		return nil, err
+	}
+
+	req.Header.Set("Content-Type", "application/json")
+
+	if len(token.BearerToken) > 0 {
+		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.BearerToken))
+	}
+
+	return c.client.Do(req)
+}
+
+func loadRESTConfig(path string) (*rest.Config, error) {
+	switch path {
+	// If the kubeconfig path is not provided, use the default loading rules
+	// so we read the regular KUBECONFIG variable or create a non-interactive
+	// client for agents running in cluster
+	case "":
+		loadingrules := clientcmd.NewDefaultClientConfigLoadingRules()
+		cfg, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
+			loadingrules, &clientcmd.ConfigOverrides{}).ClientConfig()
+		if err != nil {
+			return nil, errors.WithStack(err)
+		}
+		return cfg, nil
+	// Otherwise use the explicitly named kubeconfig file.
+	default:
+		cfg, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
+			&clientcmd.ClientConfigLoadingRules{ExplicitPath: path},
+			&clientcmd.ConfigOverrides{}).ClientConfig()
+		if err != nil {
+			return nil, errors.WithStack(err)
+		}
+		return cfg, nil
+	}
+}

From 2e5a0f85ffe299a306ad5bacf475e3d8e0d2b079 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20Valais?= <mael@vls.dev>
Date: Thu, 25 Jul 2024 20:28:00 +0200
Subject: [PATCH 03/16] venconn: ignore config's venafi-cloud.upload_path and
 uploader_id

---
 pkg/agent/run.go             | 10 +++---
 pkg/client/client_venconn.go | 60 +++++++++++++++++++++++++++---------
 2 files changed, 52 insertions(+), 18 deletions(-)

diff --git a/pkg/agent/run.go b/pkg/agent/run.go
index 90021c97..3458a64c 100644
--- a/pkg/agent/run.go
+++ b/pkg/agent/run.go
@@ -98,7 +98,9 @@ var Prometheus bool
 // raw resource data of unstructuredList
 const schemaVersion string = "v2.0.0"
 
-const inClusterNamespacePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
+const (
+	inClusterNamespacePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
+)
 
 // Run starts the agent process
 func Run(cmd *cobra.Command, args []string) {
@@ -325,7 +327,7 @@ func getConfiguration() (Config, client.Client) {
 		// don't rewrite paths. Thus, we've disabled the ability to change this
 		// value with the new --venafi-connection flag, and this field is simply
 		// ignored.
-		if config.VenafiCloud.UploadPath != "" {
+		if config.VenafiCloud != nil && config.VenafiCloud.UploadPath != "" {
 			log.Printf(`ignoring venafi-cloud.upload_path. In Venafi Connection mode, this field is not needed.`)
 		}
 
@@ -334,11 +336,11 @@ func getConfiguration() (Config, client.Client) {
 		// reasons (but cannot be empty), we just ignore whatever the user has
 		// set in the config file, and set it to an arbitrary value in the
 		// client since it doesn't matter.
-		if config.VenafiCloud.UploaderID != "" {
+		if config.VenafiCloud != nil && config.VenafiCloud.UploaderID != "" {
 			log.Printf(`ignoring venafi-cloud.uploader_id. In Venafi Connection mode, this field is not needed.`)
 		}
 
-		preflightClient, err = client.NewVenConnClient(&http.Client{}, agentMetadata, baseURL, InstallNS, VenConnName, VenConnNS)
+		preflightClient, err = client.NewVenConnClient(&http.Client{Timeout: time.Minute}, agentMetadata, baseURL, InstallNS, VenConnName, VenConnNS)
 	case APIToken != "":
 		logs.Log.Println("An API token was specified, using API token authentication.")
 		preflightClient, err = client.NewAPITokenClient(agentMetadata, APIToken, baseURL)
diff --git a/pkg/client/client_venconn.go b/pkg/client/client_venconn.go
index ae9593bb..b85f4b36 100644
--- a/pkg/client/client_venconn.go
+++ b/pkg/client/client_venconn.go
@@ -3,14 +3,12 @@ package client
 import (
 	"bytes"
 	"context"
+	"encoding/base64"
 	"encoding/json"
 	"fmt"
 	"io"
-	"io/ioutil"
-	"log"
 	"log/slog"
 	"net/http"
-	"path/filepath"
 	"time"
 
 	"github.com/go-logr/logr"
@@ -30,7 +28,7 @@ import (
 )
 
 type VenConnClient struct {
-	baseURL       string
+	baseURL       string // E.g., "https://api.venafi.cloud" (trailing slash will be removed)
 	agentMetadata *api.AgentMetadata
 	connHandler   venafi_client.ConnectionHandler
 	installNS     string       // Namespace in which the agent is running in.
@@ -105,13 +103,24 @@ func (c *VenConnClient) Start(ctx context.Context) error {
 	return c.connHandler.CacheRunnable().Start(ctx)
 }
 
+// `opts.ClusterName` and `opts.ClusterDescription` are the only values used
+// from the Options struct. OrgID and ClusterID are not used in Venafi Cloud.
 func (c *VenConnClient) PostDataReadingsWithOptions(readings []*api.DataReading, opts Options) error {
-	return c.PostDataReadings(opts.OrgID, opts.ClusterID, readings)
-}
+	if opts.ClusterName == "" {
+		return fmt.Errorf("programmer mistake: the cluster name (aka `cluster_id` in the config file) cannot be left empty")
+	}
+
+	_, token, err := c.connHandler.Get(context.Background(), c.installNS, auth.Scope{}, types.NamespacedName{Name: c.venConnName, Namespace: c.venConnNS})
+	if err != nil {
+		return fmt.Errorf("while loading the VenafiConnection %s/%s: %w", c.venConnNS, c.venConnName, err)
+	}
+	if token.TPPAccessToken != "" {
+		return fmt.Errorf(`VenafiConnection %s/%s: the agent cannot be used with TPP`, c.venConnNS, c.venConnName)
+	}
+	if token.VCPAPIKey == "" && token.TPPAccessToken == "" {
+		return fmt.Errorf(`programmer mistake: VenafiConnection %s/%s: no VCP API key or VCP access token was returned by connHandler.Get`, c.venConnNS, c.venConnName)
+	}
 
-// PostDataReadings uploads the slice of api.DataReading to the Jetstack Secure backend to be processed for later
-// viewing in the user-interface.
-func (c *VenConnClient) PostDataReadings(orgID, clusterID string, readings []*api.DataReading) error {
 	payload := api.DataReadingsPost{
 		AgentMetadata:  c.agentMetadata,
 		DataGatherTime: time.Now().UTC(),
@@ -122,7 +131,32 @@ func (c *VenConnClient) PostDataReadings(orgID, clusterID string, readings []*ap
 		return err
 	}
 
-	res, err := c.Post(filepath.Join("/api/v1/org", orgID, "datareadings", clusterID), bytes.NewBuffer(data))
+	// The path parameter "no" is a dummy parameter to make the Venafi Cloud
+	// backend happy. This parameter, named `uploaderID` in the backend, is not
+	// actually used by the backend.
+	req, err := http.NewRequest(http.MethodPost, fullURL(c.baseURL, "/v1/tlspk/upload/clusterdata/no"), bytes.NewBuffer(data))
+	if err != nil {
+		return err
+	}
+
+	req.Header.Set("Content-Type", "application/json")
+	req.Header.Set("User-Agent", fmt.Sprintf("venafi-kubernetes-agent/%s", version.PreflightVersion))
+
+	if token.VCPAccessToken != "" {
+		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.VCPAccessToken))
+	}
+	if token.VCPAPIKey != "" {
+		req.Header.Set("tppl-api-key", token.VCPAPIKey)
+	}
+
+	q := req.URL.Query()
+	q.Set("name", opts.ClusterName)
+	if opts.ClusterDescription != "" {
+		q.Set("description", base64.RawURLEncoding.EncodeToString([]byte(opts.ClusterDescription)))
+	}
+	req.URL.RawQuery = q.Encode()
+
+	res, err := c.client.Do(req)
 	if err != nil {
 		return err
 	}
@@ -130,7 +164,7 @@ func (c *VenConnClient) PostDataReadings(orgID, clusterID string, readings []*ap
 
 	if code := res.StatusCode; code < 200 || code >= 300 {
 		errorContent := ""
-		body, err := ioutil.ReadAll(res.Body)
+		body, err := io.ReadAll(res.Body)
 		if err == nil {
 			errorContent = string(body)
 		}
@@ -143,11 +177,9 @@ func (c *VenConnClient) PostDataReadings(orgID, clusterID string, readings []*ap
 
 // Post performs an HTTP POST request.
 func (c *VenConnClient) Post(path string, body io.Reader) (*http.Response, error) {
-	// The VenafiConnection must be in the same namespace as the agent. It can't
-	log.Printf("Getting Venafi connection details from %s/%s", c.venConnNS, c.venConnName)
 	_, token, err := c.connHandler.Get(context.Background(), c.installNS, auth.Scope{}, types.NamespacedName{Name: c.venConnName, Namespace: c.venConnNS})
 	if err != nil {
-		return nil, err
+		return nil, fmt.Errorf("while loading the VenafiConnection %s/%s: %w", c.venConnNS, c.venConnName, err)
 	}
 
 	req, err := http.NewRequest(http.MethodPost, fullURL(c.baseURL, path), body)

From 01641b1344926c83fef15c99e1ef6d5139a81faa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20Valais?= <mael@vls.dev>
Date: Fri, 26 Jul 2024 17:46:35 +0200
Subject: [PATCH 04/16] misc: explain what is the difference between "cluster
 id" and "cluster name"

---
 pkg/client/client.go | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/pkg/client/client.go b/pkg/client/client.go
index 20b84818..6ddcb34d 100644
--- a/pkg/client/client.go
+++ b/pkg/client/client.go
@@ -13,9 +13,17 @@ type (
 	// Options is the struct describing additional information pertinent to an agent that isn't a data reading
 	// These fields will then be uploaded together with data readings.
 	Options struct {
-		OrgID              string
-		ClusterID          string
-		ClusterName        string
+		// Only used with Jetstack Secure.
+		OrgID string
+
+		// Only used with Jetstack Secure.
+		ClusterID string
+
+		// Only used with Venafi Cloud. The convention is to use the agent
+		// config's `cluster_id` as ClusterName.
+		ClusterName string
+
+		// Only used with Venafi Cloud.
 		ClusterDescription string
 	}
 

From baa1def55f0b52b9517100a228fbaf574931409e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20Valais?= <mael@vls.dev>
Date: Fri, 26 Jul 2024 17:51:24 +0200
Subject: [PATCH 05/16] venconn: remove unused Post and PostDataReadings in
 Venafi Connection mode

---
 pkg/client/client_venconn.go | 30 ++++++++++++------------------
 1 file changed, 12 insertions(+), 18 deletions(-)

diff --git a/pkg/client/client_venconn.go b/pkg/client/client_venconn.go
index b85f4b36..a7005827 100644
--- a/pkg/client/client_venconn.go
+++ b/pkg/client/client_venconn.go
@@ -175,25 +175,19 @@ func (c *VenConnClient) PostDataReadingsWithOptions(readings []*api.DataReading,
 	return nil
 }
 
-// Post performs an HTTP POST request.
-func (c *VenConnClient) Post(path string, body io.Reader) (*http.Response, error) {
-	_, token, err := c.connHandler.Get(context.Background(), c.installNS, auth.Scope{}, types.NamespacedName{Name: c.venConnName, Namespace: c.venConnNS})
-	if err != nil {
-		return nil, fmt.Errorf("while loading the VenafiConnection %s/%s: %w", c.venConnNS, c.venConnName, err)
-	}
-
-	req, err := http.NewRequest(http.MethodPost, fullURL(c.baseURL, path), body)
-	if err != nil {
-		return nil, err
-	}
-
-	req.Header.Set("Content-Type", "application/json")
-
-	if len(token.BearerToken) > 0 {
-		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.BearerToken))
-	}
+// PostDataReadings isn't implemented for Venafi Cloud. This is because Venafi
+// Cloud needs a `clusterName` and `clusterDescription`, but this function can
+// only pass `orgID` and `clusterID` which are both useless in Venafi Cloud. Use
+// PostDataReadingsWithOptions instead.
+func (c *VenConnClient) PostDataReadings(_orgID, _clusterID string, readings []*api.DataReading) error {
+	return fmt.Errorf("programmer mistake: PostDataReadings is not implemented for Venafi Cloud")
+}
 
-	return c.client.Do(req)
+// Post isn't implemented for Venafi Cloud because /v1/tlspk/upload/clusterdata
+// requires using the query parameters `name` and `description` which can't be
+// set using Post. Use PostDataReadingsWithOptions instead.
+func (c *VenConnClient) Post(path string, body io.Reader) (*http.Response, error) {
+	return nil, fmt.Errorf("programmer mistake: Post is not implemented for Venafi Cloud")
 }
 
 func loadRESTConfig(path string) (*rest.Config, error) {

From 39e17657d2015f88a46878db570205cc5e689685 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20Valais?= <mael@vls.dev>
Date: Mon, 12 Aug 2024 16:20:55 +0200
Subject: [PATCH 06/16] venconn: test NewVenConnClient and
 PostDataReadingsWithOptions

---
 cmd/agent.go                      |   2 +-
 go.mod                            |  29 ++-
 go.sum                            |  42 ++++
 pkg/agent/run.go                  |  38 ++-
 pkg/client/client_venconn.go      |  97 ++++----
 pkg/client/client_venconn_test.go | 378 ++++++++++++++++++++++++++++++
 6 files changed, 522 insertions(+), 64 deletions(-)
 create mode 100644 pkg/client/client_venconn_test.go

diff --git a/cmd/agent.go b/cmd/agent.go
index acef4c73..7d30e328 100644
--- a/cmd/agent.go
+++ b/cmd/agent.go
@@ -154,7 +154,7 @@ func init() {
 		&agent.InstallNS,
 		"install-namespace",
 		"",
-		"Namespace in which the agent is running. Only needed when running the agent outside of Kubernetes.",
+		"Namespace in which the agent is running. Only needed when running the agent outside of Kubernetes. Used for testing purposes.",
 	)
 	agentCmd.PersistentFlags().BoolVarP(
 		&agent.Profiling,
diff --git a/go.mod b/go.mod
index 047af32d..cb18c685 100644
--- a/go.mod
+++ b/go.mod
@@ -9,6 +9,7 @@ require (
 	github.com/fatih/color v1.16.0
 	github.com/google/uuid v1.6.0
 	github.com/hashicorp/go-multierror v1.1.1
+	github.com/jetstack/venafi-connection-lib v0.1.0
 	github.com/json-iterator/go v1.1.12
 	github.com/juju/errors v1.0.0
 	github.com/kylelemons/godebug v1.1.0
@@ -36,24 +37,29 @@ require (
 	github.com/blang/semver/v4 v4.0.0 // indirect
 	github.com/evanphx/json-patch/v5 v5.9.0 // indirect
 	github.com/fsnotify/fsnotify v1.7.0 // indirect
+	github.com/fxamacker/cbor/v2 v2.7.0 // indirect
 	github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/google/cel-go v0.17.8 // indirect
 	github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
 	github.com/gorilla/css v1.0.0 // indirect
 	github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
+	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
 	github.com/sosodev/duration v1.2.0 // indirect
 	github.com/stoewer/go-strcase v1.3.0 // indirect
+	github.com/stretchr/testify v1.9.0 // indirect
+	github.com/x448/float16 v0.8.4 // indirect
 	github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	go.uber.org/zap v1.26.0 // indirect
-	golang.org/x/crypto v0.22.0 // indirect
+	golang.org/x/crypto v0.24.0 // indirect
 	golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect
-	golang.org/x/net v0.24.0 // indirect
+	golang.org/x/net v0.26.0 // indirect
 	golang.org/x/sync v0.7.0 // indirect
 	gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
 	google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe // indirect
+	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	k8s.io/apiextensions-apiserver v0.30.0 // indirect
 	k8s.io/apiserver v0.30.0 // indirect
@@ -66,7 +72,7 @@ require (
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 	github.com/emicklei/go-restful/v3 v3.11.2 // indirect
 	github.com/evanphx/json-patch v5.9.0+incompatible // indirect
-	github.com/go-logr/logr v1.4.1 // indirect
+	github.com/go-logr/logr v1.4.2
 	github.com/go-openapi/jsonpointer v0.20.2 // indirect
 	github.com/go-openapi/jsonreference v0.20.4 // indirect
 	github.com/go-openapi/swag v0.22.9 // indirect
@@ -88,18 +94,19 @@ require (
 	github.com/prometheus/client_model v0.5.0 // indirect
 	github.com/prometheus/common v0.48.0 // indirect
 	github.com/prometheus/procfs v0.12.0 // indirect
-	golang.org/x/oauth2 v0.16.0 // indirect
-	golang.org/x/sys v0.19.0 // indirect
-	golang.org/x/term v0.19.0 // indirect
-	golang.org/x/text v0.14.0 // indirect
+	golang.org/x/oauth2 v0.21.0 // indirect
+	golang.org/x/sys v0.21.0 // indirect
+	golang.org/x/term v0.21.0 // indirect
+	golang.org/x/text v0.16.0 // indirect
 	golang.org/x/time v0.5.0 // indirect
 	google.golang.org/appengine v1.6.8 // indirect
-	google.golang.org/protobuf v1.34.0 // indirect
+	google.golang.org/protobuf v1.34.2 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1
-	k8s.io/klog/v2 v2.120.1 // indirect
+	k8s.io/klog/v2 v2.130.1 // indirect
 	k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f // indirect
-	k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 // indirect
+	k8s.io/kubernetes v1.30.3
+	k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
 	sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
-	sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
+	sigs.k8s.io/structured-merge-diff/v4 v4.4.1
 )
diff --git a/go.sum b/go.sum
index 289bcb6a..7c765f5e 100644
--- a/go.sum
+++ b/go.sum
@@ -39,6 +39,8 @@ github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
 github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
 github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
 github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
+github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
+github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
 github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a h1:v6zMvHuY9yue4+QkG/HQ/W67wvtQmWJ4SDo9aK/GIno=
 github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a/go.mod h1:I79BieaU4fxrw4LMXby6q5OS9XnoR9UIKLOzDFjUmuw=
 github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -46,6 +48,8 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
 github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
 github.com/go-logr/logr v1.4.1/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-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=
@@ -148,6 +152,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmylund/go-cache v2.1.0+incompatible h1:n+7K51jLz6a3sCvff3BppuCAkixuDHuJ/C57Vw/XjTE=
 github.com/pmylund/go-cache v2.1.0+incompatible/go.mod h1:hmz95dGvINpbRZGsqPcd7B5xXY5+EKb5PpGhQY3NTHk=
 github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
@@ -182,6 +188,10 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
 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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
+github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
 github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
 github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -198,6 +208,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
 golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
+golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
+golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
 golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
 golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -213,10 +225,14 @@ golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
 golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
 golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
 golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
+golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
+golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
 golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY=
 golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0=
 golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
 golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
+golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
+golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
 golang.org/x/sync v0.0.0-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=
@@ -236,12 +252,16 @@ golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
 golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
 golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
+golang.org/x/sys v0.21.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.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
 golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
 golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
 golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
+golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
+golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
@@ -250,6 +270,8 @@ golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
 golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 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/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
 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=
@@ -279,11 +301,15 @@ google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs
 google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4=
 google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
+google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/d4l3k/messagediff.v1 v1.2.1 h1:70AthpjunwzUiarMHyED52mj9UwtAnE89l1Gmrt3EU0=
 gopkg.in/d4l3k/messagediff.v1 v1.2.1/go.mod h1:EUzikiKadqXWcD1AzJLagx0j/BeeWGtn++04Xniyg44=
+gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
+gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
 gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
@@ -298,32 +324,48 @@ k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM=
 k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc=
 k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA=
 k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE=
+k8s.io/api v0.30.3/go.mod h1:GPc8jlzoe5JG3pb0KJCSLX5oAFIW3/qNJITlDj8BH04=
+k8s.io/api v0.31.0-rc.0 h1:R+jdJGdcV5EQ9hpMR4BhrOmVrCk+fcU1b8tnwSS/DwE=
+k8s.io/api v0.31.0-rc.0/go.mod h1:wb5Wz7B/Mz9Ri/aK+v7XFuePDh6OiZ0aCX6Yb9f/EAE=
 k8s.io/apiextensions-apiserver v0.30.0 h1:jcZFKMqnICJfRxTgnC4E+Hpcq8UEhT8B2lhBcQ+6uAs=
 k8s.io/apiextensions-apiserver v0.30.0/go.mod h1:N9ogQFGcrbWqAY9p2mUAL5mGxsLqwgtUce127VtRX5Y=
 k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A=
 k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8=
 k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA=
 k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
+k8s.io/apimachinery v0.30.3/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
+k8s.io/apimachinery v0.31.0-rc.0 h1:8guKYtBUM/ZEvvQoWfm3hl/NEj+1eVhuSnSpDHheJmw=
+k8s.io/apimachinery v0.31.0-rc.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
 k8s.io/apiserver v0.30.0 h1:QCec+U72tMQ+9tR6A0sMBB5Vh6ImCEkoKkTDRABWq6M=
 k8s.io/apiserver v0.30.0/go.mod h1:smOIBq8t0MbKZi7O7SyIpjPsiKJ8qa+llcFCluKyqiY=
 k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4=
 k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo=
 k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ=
 k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY=
+k8s.io/client-go v0.30.3 h1:bHrJu3xQZNXIi8/MoxYtZBBWQQXwy16zqJwloXXfD3k=
+k8s.io/client-go v0.30.3/go.mod h1:8d4pf8vYu665/kUbsxWAQ/JDBNWqfFeZnvFiVdmx89U=
+k8s.io/client-go v0.31.0-rc.0 h1:9ShcEasfMftGPmxfB8lo2QE63SWT+IEVMrFR7IuWkuI=
+k8s.io/client-go v0.31.0-rc.0/go.mod h1:dsCs0l4s2KrHxbbXcHviMhkMBd81uhRPZExRpN6ykgI=
 k8s.io/component-base v0.30.0 h1:cj6bp38g0ainlfYtaOQuRELh5KSYjhKxM+io7AUIk4o=
 k8s.io/component-base v0.30.0/go.mod h1:V9x/0ePFNaKeKYA3bOvIbrNoluTSG+fSJKjLdjOoeXQ=
 k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
 k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
 k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
 k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
+k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
+k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
 k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
 k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
 k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f h1:0LQagt0gDpKqvIkAMPaRGcXawNMouPECM1+F9BVxEaM=
 k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f/go.mod h1:S9tOR0FxgyusSNR+MboCuiDpVWkAifZvaYI1Q2ubgro=
+k8s.io/kubernetes v1.30.3 h1:A0qoXI1YQNzrQZiff33y5zWxYHFT/HeZRK98/sRDJI0=
+k8s.io/kubernetes v1.30.3/go.mod h1:yPbIk3MhmhGigX62FLJm+CphNtjxqCvAIFQXup6RKS0=
 k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
 k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
 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/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.18.2 h1:RqVW6Kpeaji67CY5nPEfRz6ZfFMk0lWQlNrLqlNpx+Q=
 sigs.k8s.io/controller-runtime v0.18.2/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw=
 sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
diff --git a/pkg/agent/run.go b/pkg/agent/run.go
index 3458a64c..3bea4307 100644
--- a/pkg/agent/run.go
+++ b/pkg/agent/run.go
@@ -22,6 +22,8 @@ import (
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
 	"github.com/spf13/cobra"
+	"k8s.io/client-go/rest"
+	"k8s.io/client-go/tools/clientcmd"
 	"sigs.k8s.io/controller-runtime/pkg/manager"
 
 	"github.com/jetstack/preflight/api"
@@ -336,11 +338,16 @@ func getConfiguration() (Config, client.Client) {
 		// reasons (but cannot be empty), we just ignore whatever the user has
 		// set in the config file, and set it to an arbitrary value in the
 		// client since it doesn't matter.
-		if config.VenafiCloud != nil && config.VenafiCloud.UploaderID != "" {
+		if config.VenafiCloud.UploaderID != "" {
 			log.Printf(`ignoring venafi-cloud.uploader_id. In Venafi Connection mode, this field is not needed.`)
 		}
 
-		preflightClient, err = client.NewVenConnClient(&http.Client{Timeout: time.Minute}, agentMetadata, baseURL, InstallNS, VenConnName, VenConnNS)
+		cfg, err := loadRESTConfig("")
+		if err != nil {
+			log.Fatalf("failed to load kubeconfig: %v", err)
+		}
+
+		preflightClient, err = client.NewVenConnClient(cfg, agentMetadata, InstallNS, VenConnName, VenConnNS, nil)
 	case APIToken != "":
 		logs.Log.Println("An API token was specified, using API token authentication.")
 		preflightClient, err = client.NewAPITokenClient(agentMetadata, APIToken, baseURL)
@@ -543,7 +550,7 @@ func postData(config Config, preflightClient client.Client, readings []*api.Data
 	return nil
 }
 
-// Inspired from the controller-runtime project.
+// Inspired by the controller-runtime project.
 func getInClusterNamespace() (string, error) {
 	// Check whether the namespace file exists.
 	// If not, we are not running in cluster so can't guess the namespace.
@@ -561,3 +568,28 @@ func getInClusterNamespace() (string, error) {
 	}
 	return string(namespace), nil
 }
+
+func loadRESTConfig(path string) (*rest.Config, error) {
+	switch path {
+	// If the kubeconfig path is not provided, use the default loading rules
+	// so we read the regular KUBECONFIG variable or create a non-interactive
+	// client for agents running in cluster
+	case "":
+		loadingrules := clientcmd.NewDefaultClientConfigLoadingRules()
+		cfg, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
+			loadingrules, &clientcmd.ConfigOverrides{}).ClientConfig()
+		if err != nil {
+			return nil, fmt.Errorf("failed to load kubeconfig: %w", err)
+		}
+		return cfg, nil
+	// Otherwise use the explicitly named kubeconfig file.
+	default:
+		cfg, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
+			&clientcmd.ClientConfigLoadingRules{ExplicitPath: path},
+			&clientcmd.ConfigOverrides{}).ClientConfig()
+		if err != nil {
+			return nil, fmt.Errorf("failed to load kubeconfig from %s: %w", path, err)
+		}
+		return cfg, nil
+	}
+}
diff --git a/pkg/client/client_venconn.go b/pkg/client/client_venconn.go
index a7005827..eb403cd7 100644
--- a/pkg/client/client_venconn.go
+++ b/pkg/client/client_venconn.go
@@ -3,11 +3,11 @@ package client
 import (
 	"bytes"
 	"context"
+	"crypto/x509"
 	"encoding/base64"
 	"encoding/json"
 	"fmt"
 	"io"
-	"log/slog"
 	"net/http"
 	"time"
 
@@ -22,13 +22,11 @@ import (
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/client-go/rest"
-	"k8s.io/client-go/tools/clientcmd"
 	"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
-	ll "sigs.k8s.io/controller-runtime/pkg/log"
+	ctrlruntimelog "sigs.k8s.io/controller-runtime/pkg/log"
 )
 
 type VenConnClient struct {
-	baseURL       string // E.g., "https://api.venafi.cloud" (trailing slash will be removed)
 	agentMetadata *api.AgentMetadata
 	connHandler   venafi_client.ConnectionHandler
 	installNS     string       // Namespace in which the agent is running in.
@@ -38,11 +36,22 @@ type VenConnClient struct {
 }
 
 // NewVenConnClient lets you make requests to the Venafi Cloud backend using the
-// given VenafiConnection resource. You need to call Start to start watching the
-// VenafiConnection resource. If you don't, the client will be unable to find
-// the VenafiConnection that you are referring to as its client-go cache will
-// remain empty.
-func NewVenConnClient(c *http.Client, agentMetadata *api.AgentMetadata, baseURL, installNS, venConnName, venConnNS string) (*VenConnClient, error) {
+// given VenafiConnection resource.
+//
+// You need to call Start to start watching the VenafiConnection resource. If
+// you don't, the client will be unable to find the VenafiConnection that you
+// are referring to as its client-go cache will remain empty.
+//
+// The http.Client is used for Venafi and Vault, not for Kubernetes. The
+// `installNS` is the namespace in which the agent is running in. The passed
+// `restcfg` is not mutated. `trustedCAs` is only used for connecting to Venafi
+// Cloud and Vault and can be left nil.
+func NewVenConnClient(restcfg *rest.Config, agentMetadata *api.AgentMetadata, installNS, venConnName, venConnNS string, trustedCAs *x509.CertPool) (*VenConnClient, error) {
+	// TODO(mael): The rest of the codebase uses the standard "log" package,
+	// venafi-connection-lib uses "go-logr/logr", and client-go uses "klog". We
+	// should standardize on one of them, probably "slog".
+	ctrlruntimelog.SetLogger(logr.Logger{})
+
 	if installNS == "" {
 		return nil, errors.New("programmer mistake: installNS must be provided")
 	}
@@ -53,19 +62,22 @@ func NewVenConnClient(c *http.Client, agentMetadata *api.AgentMetadata, baseURL,
 		return nil, errors.New("programmer mistake: venConnNS must be provided")
 	}
 
-	cfg, err := loadRESTConfig("")
-	if err != nil {
-		return nil, errors.WithStack(err)
-	}
-	cfg.Impersonate = rest.ImpersonationConfig{
+	restcfg = rest.CopyConfig(restcfg)
+	restcfg.Impersonate = rest.ImpersonationConfig{
 		UserName: fmt.Sprintf("system:serviceaccount:%s:venafi-connection", installNS),
 	}
-	restMapper, err := apiutil.NewDynamicRESTMapper(cfg, &http.Client{})
+
+	// TLS-related configuration such as root CAs and client certs are contained
+	// in the restcfg; let's create an http.Client that uses them.
+	httpCl, err := rest.HTTPClientFor(restcfg)
 	if err != nil {
-		return nil, errors.WithStack(err)
+		return nil, fmt.Errorf("while turning the REST config into an HTTP client: %w", err)
 	}
 
-	ll.SetLogger(logr.FromSlogHandler(slog.Default().Handler()))
+	restMapper, err := apiutil.NewDynamicRESTMapper(restcfg, httpCl)
+	if err != nil {
+		return nil, fmt.Errorf("while creating the REST mapper: %w", err)
+	}
 
 	// This Kubernetes client only needs to be able to read and write the
 	// VenafiConnection resources and read Secret resources.
@@ -77,23 +89,29 @@ func NewVenConnClient(c *http.Client, agentMetadata *api.AgentMetadata, baseURL,
 		"venafi-kubernetes-agent/"+version.PreflightVersion,
 		"venafi-kubernetes-agent.jetstack.io",
 		"VenafiKubernetesAgent",
-		cfg,
+		restcfg,
 		scheme,
 		restMapper,
-		nil,
+		trustedCAs,
 	)
 	if err != nil {
 		return nil, err
 	}
 
+	vcpClient := &http.Client{}
+	if trustedCAs != nil {
+		tr := http.DefaultTransport.(*http.Transport).Clone()
+		tr.TLSClientConfig.RootCAs = trustedCAs
+		vcpClient.Transport = tr
+	}
+
 	return &VenConnClient{
-		baseURL:       baseURL,
 		agentMetadata: agentMetadata,
 		connHandler:   handler,
 		installNS:     installNS,
 		venConnName:   venConnName,
 		venConnNS:     venConnNS,
-		client:        c,
+		client:        vcpClient,
 	}, nil
 }
 
@@ -117,8 +135,14 @@ func (c *VenConnClient) PostDataReadingsWithOptions(readings []*api.DataReading,
 	if token.TPPAccessToken != "" {
 		return fmt.Errorf(`VenafiConnection %s/%s: the agent cannot be used with TPP`, c.venConnNS, c.venConnName)
 	}
-	if token.VCPAPIKey == "" && token.TPPAccessToken == "" {
-		return fmt.Errorf(`programmer mistake: VenafiConnection %s/%s: no VCP API key or VCP access token was returned by connHandler.Get`, c.venConnNS, c.venConnName)
+	if token.VCPAPIKey != "" {
+		// Although it is technically possible to use an API key, we have
+		// decided to not allow it as it isn't recommended and will eventually
+		// be phased out.
+		return fmt.Errorf(`VenafiConnection %s/%s: the agent cannot be used with an API key`, c.venConnNS, c.venConnName)
+	}
+	if token.VCPAccessToken == "" {
+		return fmt.Errorf(`programmer mistake: VenafiConnection %s/%s: TPPAccessToken is empty in the token returned by connHandler.Get: %v`, c.venConnNS, c.venConnName, token)
 	}
 
 	payload := api.DataReadingsPost{
@@ -134,7 +158,7 @@ func (c *VenConnClient) PostDataReadingsWithOptions(readings []*api.DataReading,
 	// The path parameter "no" is a dummy parameter to make the Venafi Cloud
 	// backend happy. This parameter, named `uploaderID` in the backend, is not
 	// actually used by the backend.
-	req, err := http.NewRequest(http.MethodPost, fullURL(c.baseURL, "/v1/tlspk/upload/clusterdata/no"), bytes.NewBuffer(data))
+	req, err := http.NewRequest(http.MethodPost, fullURL(token.BaseURL, "/v1/tlspk/upload/clusterdata/no"), bytes.NewBuffer(data))
 	if err != nil {
 		return err
 	}
@@ -189,28 +213,3 @@ func (c *VenConnClient) PostDataReadings(_orgID, _clusterID string, readings []*
 func (c *VenConnClient) Post(path string, body io.Reader) (*http.Response, error) {
 	return nil, fmt.Errorf("programmer mistake: Post is not implemented for Venafi Cloud")
 }
-
-func loadRESTConfig(path string) (*rest.Config, error) {
-	switch path {
-	// If the kubeconfig path is not provided, use the default loading rules
-	// so we read the regular KUBECONFIG variable or create a non-interactive
-	// client for agents running in cluster
-	case "":
-		loadingrules := clientcmd.NewDefaultClientConfigLoadingRules()
-		cfg, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
-			loadingrules, &clientcmd.ConfigOverrides{}).ClientConfig()
-		if err != nil {
-			return nil, errors.WithStack(err)
-		}
-		return cfg, nil
-	// Otherwise use the explicitly named kubeconfig file.
-	default:
-		cfg, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
-			&clientcmd.ClientConfigLoadingRules{ExplicitPath: path},
-			&clientcmd.ConfigOverrides{}).ClientConfig()
-		if err != nil {
-			return nil, errors.WithStack(err)
-		}
-		return cfg, nil
-	}
-}
diff --git a/pkg/client/client_venconn_test.go b/pkg/client/client_venconn_test.go
new file mode 100644
index 00000000..8fca50a7
--- /dev/null
+++ b/pkg/client/client_venconn_test.go
@@ -0,0 +1,378 @@
+package client_test
+
+import (
+	"context"
+	"crypto/x509"
+	"io"
+	"net/http"
+	"net/http/httptest"
+	"strings"
+	"testing"
+
+	"github.com/jetstack/preflight/api"
+	"github.com/jetstack/preflight/pkg/client"
+
+	"github.com/jetstack/venafi-connection-lib/api/v1alpha1"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	corev1 "k8s.io/api/core/v1"
+	rbacv1 "k8s.io/api/rbac/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/types"
+	"k8s.io/apimachinery/pkg/util/yaml"
+	"k8s.io/client-go/rest"
+	ctrlruntime "sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/envtest"
+)
+
+// These are using envtest (slow) rather than a fake clientset (fast) because
+// controller-runtime's fake clientset doesn't support server-side apply [1] and
+// also because we want to create serviceaccount tokens, which isn't supported
+// by the fake clientset either.
+//
+// The goal is to test the following behaviors:
+//
+//   - VenafiConnection's `accessToken` works as expected with a fake Venafi
+//     Cloud server.
+//   - VenafiConnection's `apiKey` and `tpp` can't be used by the user.
+//   - NewVenConnClient's `trustedCAs` works as expected.
+//
+// [1] https://github.com/kubernetes-sigs/controller-runtime/issues/2341
+func TestVenConnClient_PostDataReadingsWithOptions(t *testing.T) {
+	t.Parallel()
+
+	t.Run("valid accessToken", run(testcase{
+		given: undent(`
+			apiVersion: jetstack.io/v1alpha1
+			kind: VenafiConnection
+			metadata:
+			  name: venafi-components
+			  namespace: venafi
+			spec:
+			  vcp:
+			    url: FAKE_VENAFI_CLOUD_URL
+			    accessToken:
+			      - secret:
+			          name: accesstoken
+			          fields: [accesstoken]`),
+		expectReadyCondMsg: "ea744d098c2c1c6044e4c4e9d3bf7c2a68ef30553db00f1714886cedf73230f1",
+	}))
+	t.Run("error when the apiKey field is used", run(testcase{
+		// Why isn't it possible to use the 'apiKey' field? Although the
+		// Kubernetes Discovery endpoint works with an API key, we have decided
+		// to not support it because it isn't recommended.
+		given: undent(`
+			apiVersion: jetstack.io/v1alpha1
+			kind: VenafiConnection
+			metadata:
+			  name: venafi-components
+			  namespace: venafi
+			spec:
+			  vcp:
+			    url: FAKE_VENAFI_CLOUD_URL
+			    apiKey:
+			      - secret:
+			          name: apikey
+			          fields: [apikey]`),
+		expectReadyCondMsg: "b099d634ccec56556da28028743475dab67f79d079b668bedc3ef544f7eed2f3",
+		expectErr:          "VenafiConnection venafi/venafi-components: the agent cannot be used with an API key",
+	}))
+	t.Run("error when the tpp field is used", run(testcase{
+		// IMPORTANT: The user may think they can use 'tpp', spend time
+		// debugging and making the venafi connection work, and then find out
+		// that it doesn't work. The reason is because as of now, we don't first
+		// check if the user has used the 'tpp' field before running Get.
+		given: undent(`
+			apiVersion: jetstack.io/v1alpha1
+			kind: VenafiConnection
+			metadata:
+			  name: venafi-components
+			  namespace: venafi
+			spec:
+			  tpp:
+			    url: FAKE_TPP_URL
+			    accessToken:
+			      - secret:
+			          name: accesstoken
+			          fields: [accesstoken]`),
+		expectErr:          ``,
+		expectReadyCondMsg: `ea744d098c2c1c6044e4c4e9d3bf7c2a68ef30553db00f1714886cedf73230f1`,
+	}))
+}
+
+type testcase struct {
+	given              string
+	expectErr          string
+	expectReadyCondMsg string
+}
+
+func run(test testcase) func(t *testing.T) {
+	return func(t *testing.T) {
+		fakeVenafiCloud, certCloud := fakeVenafiCloud(t)
+		fakeTPP, certTPP := fakeTPP(t)
+		_, restconf, kclient := startEnvtest(t)
+
+		certPool := x509.NewCertPool()
+		certPool.AddCert(certCloud)
+		certPool.AddCert(certTPP)
+
+		cl, err := client.NewVenConnClient(
+			restconf,
+			&api.AgentMetadata{ClusterID: "no"},
+			"venafi",            // Namespace in which the Agent is running.
+			"venafi-components", // Name of the VenafiConnection.
+			"venafi",            // Namespace of the VenafiConnection.
+			certPool,
+		)
+		require.NoError(t, err)
+
+		// This `cancel` is important because the below func `Start(ctx)` needs
+		// to be stopped before the apiserver is stopped. Otherwise, the test
+		// fail with the message "timeout waiting for process kube-apiserver to
+		// stop". See:
+		// https://github.com/jetstack/venafi-connection-lib/pull/158#issuecomment-1949002322
+		// https://github.com/kubernetes-sigs/controller-runtime/issues/1571#issuecomment-945535598
+		ctx, cancel := context.WithCancel(context.Background())
+		go func() {
+			err = cl.Start(ctx)
+			require.NoError(t, err)
+		}()
+		t.Cleanup(cancel)
+
+		// Apply the same RBAC as what you would get from the Venafi
+		// Connection Helm chart, for example after running this:
+		//  helm template venafi-connection oci://registry.venafi.cloud/charts/venafi-connection --version v0.1.0 -n venafi --show-only templates/venafi-connection-rbac.yaml
+		require.NoError(t, kclient.Create(context.Background(), &corev1.Namespace{
+			ObjectMeta: metav1.ObjectMeta{Name: "venafi"},
+		}))
+		require.NoError(t, kclient.Create(context.Background(), &corev1.ServiceAccount{
+			ObjectMeta: metav1.ObjectMeta{Name: "venafi-connection", Namespace: "venafi"},
+		}))
+		require.NoError(t, kclient.Create(context.Background(), &rbacv1.ClusterRole{
+			ObjectMeta: metav1.ObjectMeta{Name: "venafi-connection-role"},
+			Rules: []rbacv1.PolicyRule{
+				{APIGroups: []string{""}, Resources: []string{"namespaces"}, Verbs: []string{"get", "list", "watch"}},
+				{APIGroups: []string{"jetstack.io"}, Resources: []string{"venaficonnections"}, Verbs: []string{"get", "list", "watch"}},
+				{APIGroups: []string{"jetstack.io"}, Resources: []string{"venaficonnections/status"}, Verbs: []string{"get", "patch"}},
+			},
+		}))
+		require.NoError(t, kclient.Create(context.Background(), &rbacv1.ClusterRoleBinding{
+			ObjectMeta: metav1.ObjectMeta{Name: "venafi-connection-rolebinding"},
+			RoleRef:    rbacv1.RoleRef{APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole", Name: "venafi-connection-role"},
+			Subjects:   []rbacv1.Subject{{Kind: "ServiceAccount", Name: "venafi-connection", Namespace: "venafi"}},
+		}))
+		require.NoError(t, kclient.Create(context.Background(), &corev1.Secret{
+			ObjectMeta: metav1.ObjectMeta{Name: "accesstoken", Namespace: "venafi"},
+			StringData: map[string]string{"accesstoken": "VALID_ACCESS_TOKEN"},
+		}))
+		require.NoError(t, kclient.Create(context.Background(), &corev1.Secret{
+			ObjectMeta: metav1.ObjectMeta{Name: "apikey", Namespace: "venafi"},
+			StringData: map[string]string{"apikey": "VALID_API_KEY"},
+		}))
+		require.NoError(t, kclient.Create(context.Background(), &rbacv1.Role{
+			ObjectMeta: metav1.ObjectMeta{Name: "venafi-connection-secret-reader", Namespace: "venafi"},
+			Rules: []rbacv1.PolicyRule{
+				{APIGroups: []string{""}, Resources: []string{"secrets"}, Verbs: []string{"get"}, ResourceNames: []string{"accesstoken", "apikey"}},
+			},
+		}))
+		require.NoError(t, kclient.Create(context.Background(), &rbacv1.RoleBinding{
+			ObjectMeta: metav1.ObjectMeta{Name: "venafi-connection-secret-reader", Namespace: "venafi"},
+			RoleRef:    rbacv1.RoleRef{APIGroup: "rbac.authorization.k8s.io", Kind: "Role", Name: "venafi-connection-secret-reader"},
+			Subjects:   []rbacv1.Subject{{Kind: "ServiceAccount", Name: "venafi-connection", Namespace: "venafi"}},
+		}))
+
+		test.given = strings.ReplaceAll(test.given, "FAKE_VENAFI_CLOUD_URL", fakeVenafiCloud.URL)
+		test.given = strings.ReplaceAll(test.given, "FAKE_TPP_URL", fakeTPP.URL)
+		for _, obj := range parse(test.given) {
+			require.NoError(t, kclient.Create(context.Background(), obj))
+		}
+
+		err = cl.PostDataReadingsWithOptions([]*api.DataReading{}, client.Options{ClusterName: "test cluster name"})
+		if test.expectErr != "" {
+			assert.EqualError(t, err, test.expectErr)
+		}
+
+		got := v1alpha1.VenafiConnection{}
+		kclient.Get(context.Background(), types.NamespacedName{Name: "venafi-components", Namespace: "venafi"}, &got)
+		require.Len(t, got.Status.Conditions, 1)
+		assert.Equal(t, test.expectReadyCondMsg, got.Status.Conditions[0].Message)
+	}
+}
+
+func fakeVenafiCloud(t *testing.T) (*httptest.Server, *x509.Certificate) {
+	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		t.Logf("fake api.venafi.cloud received request: %s %s", r.Method, r.URL.Path)
+		accessToken := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
+		apiKey := r.Header.Get("tppl-api-key")
+		if accessToken != "VALID_ACCESS_TOKEN" && apiKey != "VALID_API_KEY" {
+			w.WriteHeader(http.StatusUnauthorized)
+			return
+		}
+		if r.URL.Path == "/v1/tlspk/upload/clusterdata/no" {
+			if r.URL.Query().Get("name") != "test cluster name" {
+				w.WriteHeader(http.StatusBadRequest)
+				return
+			}
+			_, _ = w.Write([]byte(`{"status":"ok","organization":"756db001-280e-11ee-84fb-991f3177e2d0"}`))
+		} else if r.URL.Path == "/v1/useraccounts" {
+			w.WriteHeader(http.StatusOK)
+			_, _ = w.Write([]byte(`{"user": {"username": "user","id": "76a126f0-280e-11ee-84fb-991f3177e2d0"}}`))
+
+		} else if r.URL.Path == "/v1/oauth2/v2.0/756db001-280e-11ee-84fb-991f3177e2d0/token" {
+			_, _ = w.Write([]byte(`{"access_token":"VALID_ACCESS_TOKEN","expires_in":900,"token_type":"bearer"}`))
+		} else {
+			w.WriteHeader(http.StatusInternalServerError)
+			_, _ = w.Write([]byte(`{"error":"unexpected path in the test server","path":"` + r.URL.Path + `"}`))
+		}
+	}))
+	t.Cleanup(server.Close)
+
+	cert, err := x509.ParseCertificate(server.TLS.Certificates[0].Certificate[0])
+	require.NoError(t, err)
+
+	return server, cert
+}
+
+func fakeTPP(t testing.TB) (*httptest.Server, *x509.Certificate) {
+	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		t.Logf("fake tpp.example.com received request: %s %s", r.Method, r.URL.Path)
+
+		accessToken := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
+
+		if r.URL.Path == "/vedsdk/Identity/Self" {
+			if accessToken != "VALID_ACCESS_TOKEN" {
+				w.WriteHeader(http.StatusUnauthorized)
+				return
+			}
+			_, _ = w.Write([]byte(`{"Identities":[{"Name":"TEST"}]}`))
+		} else if r.URL.Path == "/vedsdk/certificates/checkpolicy" {
+			_, _ = w.Write([]byte(`{"Policy":{"Subject":{"Organization":{"Value": "test-org"}}}}`))
+		} else {
+			w.WriteHeader(http.StatusInternalServerError)
+			_, _ = w.Write([]byte(`{"error":"unexpected path in the test server","path":"` + r.URL.Path + `"}`))
+		}
+	}))
+	t.Cleanup(server.Close)
+
+	cert, err := x509.ParseCertificate(server.TLS.Certificates[0].Certificate[0])
+	require.NoError(t, err)
+
+	return server, cert
+}
+
+// To see the API server logs, set:
+//
+//	export KUBEBUILDER_ATTACH_CONTROL_PLANE_OUTPUT=true
+func startEnvtest(t testing.TB) (_ *envtest.Environment, _ *rest.Config, kclient ctrlruntime.WithWatch) {
+	envtest := &envtest.Environment{
+		ErrorIfCRDPathMissing: true,
+		CRDDirectoryPaths:     []string{"/tmp/venafi-connection.yaml"},
+	}
+	restconf, err := envtest.Start()
+	require.NoError(t, err)
+
+	t.Cleanup(func() {
+		t.Log("Waiting for envtest to exit")
+		err = envtest.Stop()
+		require.NoError(t, err)
+	})
+
+	sch := runtime.NewScheme()
+	_ = v1alpha1.AddToScheme(sch)
+	_ = corev1.AddToScheme(sch)
+	_ = rbacv1.AddToScheme(sch)
+
+	kclient, err = ctrlruntime.NewWithWatch(restconf, ctrlruntime.Options{Scheme: sch})
+	require.NoError(t, err)
+
+	return envtest, restconf, kclient
+}
+
+// Undent removes leading indentation/white-space from given string and returns
+// it as a string. Useful for inlining YAML manifests in Go code. Inline YAML
+// manifests in the Go test files makes it easier to read the test case as
+// opposed to reading verbose-y Go structs.
+//
+// This was copied from https://github.com/jimeh/undent/blob/main/undent.go, all
+// credit goes to the author, Jim Myhrberg.
+func undent(s string) string {
+	const (
+		tab = 9
+		lf  = 10
+		spc = 32
+	)
+
+	if len(s) == 0 {
+		return ""
+	}
+
+	// find smallest indent relative to each line-feed
+	min := 99999999999
+	count := 0
+
+	lfs := make([]int, 0, strings.Count(s, "\n"))
+	if s[0] != lf {
+		lfs = append(lfs, -1)
+	}
+
+	indent := 0
+	for i := 0; i < len(s); i++ {
+		if s[i] == lf {
+			lfs = append(lfs, i)
+			indent = 0
+		} else if indent < min {
+			switch s[i] {
+			case spc, tab:
+				indent++
+			default:
+				if indent > 0 {
+					count++
+				}
+				if indent < min {
+					min = indent
+				}
+			}
+		}
+	}
+
+	// extract each line without indentation
+	out := make([]byte, 0, len(s)-(min*count))
+
+	for i := 0; i < len(lfs); i++ {
+		offset := lfs[i] + 1
+		end := len(s)
+		if i+1 < len(lfs) {
+			end = lfs[i+1] + 1
+		}
+
+		if offset+min < end {
+			out = append(out, s[offset+min:end]...)
+		} else if offset < end {
+			out = append(out, s[offset:end]...)
+		}
+	}
+
+	return string(out)
+}
+
+// Parses the YAML manifest. Useful for inlining YAML manifests in Go test
+// files, to be used in conjunction with `undent`.
+func parse(yamlmanifest string) []ctrlruntime.Object {
+	dec := yaml.NewYAMLOrJSONDecoder(strings.NewReader(yamlmanifest), 4096)
+	var objs []ctrlruntime.Object
+	for {
+		obj := &unstructured.Unstructured{}
+		err := dec.Decode(obj)
+		if err == io.EOF {
+			break
+		}
+		if err != nil {
+			panic(err)
+		}
+
+		objs = append(objs, obj)
+	}
+	return objs
+}

From 7c91430fc052a16ba4999a855db97d5638aa4abb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20Valais?= <mael@vls.dev>
Date: Tue, 13 Aug 2024 20:17:26 +0200
Subject: [PATCH 07/16] venconn: switch to venafi-connection-lib@main instead
 of v0.1.0 for now

Let's pin venafi-connection-lib to the latest commit on main until we
decide to release a new version of venafi-connection-lib (if we actually
do).
---
 go.mod |  37 +++++----
 go.sum | 236 ++++++++++++++++++++++-----------------------------------
 2 files changed, 108 insertions(+), 165 deletions(-)

diff --git a/go.mod b/go.mod
index cb18c685..274747ba 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,8 @@
 module github.com/jetstack/preflight
 
-go 1.22
+go 1.22.0
+
+toolchain go1.22.6
 
 require (
 	github.com/Jeffail/gabs/v2 v2.7.0
@@ -9,7 +11,7 @@ require (
 	github.com/fatih/color v1.16.0
 	github.com/google/uuid v1.6.0
 	github.com/hashicorp/go-multierror v1.1.1
-	github.com/jetstack/venafi-connection-lib v0.1.0
+	github.com/jetstack/venafi-connection-lib v0.1.1-0.20240814101135-a72cec54dc1a
 	github.com/json-iterator/go v1.1.12
 	github.com/juju/errors v1.0.0
 	github.com/kylelemons/godebug v1.1.0
@@ -20,50 +22,49 @@ require (
 	github.com/prometheus/client_golang v1.19.1
 	github.com/spf13/cobra v1.8.0
 	github.com/spf13/pflag v1.0.5
+	github.com/stretchr/testify v1.9.0
 	gopkg.in/d4l3k/messagediff.v1 v1.2.1
 	gopkg.in/yaml.v2 v2.4.0
-	k8s.io/api v0.30.0
-	k8s.io/apimachinery v0.30.0
-	k8s.io/client-go v0.30.0
-	sigs.k8s.io/controller-runtime v0.18.2
+	k8s.io/api v0.30.3
+	k8s.io/apimachinery v0.30.3
+	k8s.io/client-go v0.30.3
+	sigs.k8s.io/controller-runtime v0.18.5
 	sigs.k8s.io/yaml v1.4.0
 )
 
 require (
-	github.com/Venafi/vcert/v5 v5.6.5-0.20240430163638-86272e17ec45 // indirect
+	github.com/Khan/genqlient v0.7.0 // indirect
+	github.com/Venafi/vcert/v5 v5.7.1 // indirect
 	github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect
 	github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
 	github.com/aymerick/douceur v0.2.0 // indirect
 	github.com/blang/semver/v4 v4.0.0 // indirect
 	github.com/evanphx/json-patch/v5 v5.9.0 // indirect
 	github.com/fsnotify/fsnotify v1.7.0 // indirect
-	github.com/fxamacker/cbor/v2 v2.7.0 // indirect
 	github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/google/cel-go v0.17.8 // indirect
 	github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
 	github.com/gorilla/css v1.0.0 // indirect
-	github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
+	github.com/gorilla/websocket v1.5.1 // indirect
 	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
 	github.com/sosodev/duration v1.2.0 // indirect
 	github.com/stoewer/go-strcase v1.3.0 // indirect
-	github.com/stretchr/testify v1.9.0 // indirect
-	github.com/x448/float16 v0.8.4 // indirect
+	github.com/vektah/gqlparser/v2 v2.5.15 // indirect
 	github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	go.uber.org/zap v1.26.0 // indirect
 	golang.org/x/crypto v0.24.0 // indirect
 	golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect
 	golang.org/x/net v0.26.0 // indirect
-	golang.org/x/sync v0.7.0 // indirect
+	golang.org/x/sync v0.8.0 // indirect
 	gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
 	google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe // indirect
-	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
-	k8s.io/apiextensions-apiserver v0.30.0 // indirect
-	k8s.io/apiserver v0.30.0 // indirect
-	k8s.io/component-base v0.30.0 // indirect
+	k8s.io/apiextensions-apiserver v0.30.3 // indirect
+	k8s.io/apiserver v0.30.3 // indirect
+	k8s.io/component-base v0.30.3 // indirect
 )
 
 require (
@@ -99,14 +100,12 @@ require (
 	golang.org/x/term v0.21.0 // indirect
 	golang.org/x/text v0.16.0 // indirect
 	golang.org/x/time v0.5.0 // indirect
-	google.golang.org/appengine v1.6.8 // indirect
 	google.golang.org/protobuf v1.34.2 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1
 	k8s.io/klog/v2 v2.130.1 // indirect
 	k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f // indirect
-	k8s.io/kubernetes v1.30.3
 	k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
 	sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
-	sigs.k8s.io/structured-merge-diff/v4 v4.4.1
+	sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
 )
diff --git a/go.sum b/go.sum
index 7c765f5e..776c9978 100644
--- a/go.sum
+++ b/go.sum
@@ -1,7 +1,11 @@
 github.com/Jeffail/gabs/v2 v2.7.0 h1:Y2edYaTcE8ZpRsR2AtmPu5xQdFDIthFG0jYhu5PY8kg=
 github.com/Jeffail/gabs/v2 v2.7.0/go.mod h1:dp5ocw1FvBBQYssgHsG7I1WYsiLRtkUaB1FEtSwvNUw=
-github.com/Venafi/vcert/v5 v5.6.5-0.20240430163638-86272e17ec45 h1:vGAu/uGLC0KupzIxEF28gmry0SaS2uVjlxOFSeR0Feg=
-github.com/Venafi/vcert/v5 v5.6.5-0.20240430163638-86272e17ec45/go.mod h1:6NgXvi7m0MJzma4vNDmoMt0Pj12pGPKLPr293kcdyEA=
+github.com/Khan/genqlient v0.7.0 h1:GZ1meyRnzcDTK48EjqB8t3bcfYvHArCUUvgOwpz1D4w=
+github.com/Khan/genqlient v0.7.0/go.mod h1:HNyy3wZvuYwmW3Y7mkoQLZsa/R5n5yIRajS1kPBvSFM=
+github.com/Venafi/vcert/v5 v5.7.1 h1:gUDbSuP6NE4yAslWp+D+ZoJlYOSRWhQora48oExuEN4=
+github.com/Venafi/vcert/v5 v5.7.1/go.mod h1:UGI1A6IdZ7Sc4E3DQU70Qzaanot6fiY0ObIupcU2O94=
+github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
+github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
 github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18=
 github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
 github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
@@ -14,87 +18,83 @@ 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/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
 github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
+github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
+github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
 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/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
+github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
+github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
+github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U=
 github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
-github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
 github.com/emicklei/go-restful/v3 v3.11.2 h1:1onLa9DcsMYO9P+CXaL0dStDqQ2EHHXLiz+BtnqkLAU=
 github.com/emicklei/go-restful/v3 v3.11.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
-github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
-github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
 github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
 github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
 github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
 github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
+github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
+github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 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/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
-github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
 github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a h1:v6zMvHuY9yue4+QkG/HQ/W67wvtQmWJ4SDo9aK/GIno=
 github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a/go.mod h1:I79BieaU4fxrw4LMXby6q5OS9XnoR9UIKLOzDFjUmuw=
-github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
-github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
-github.com/go-logr/logr v1.4.1/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-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-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+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.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
 github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
-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/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU=
 github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4=
-github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
-github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
-github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
 github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE=
 github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE=
 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/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
 github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
+github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
 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/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.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/google/cel-go v0.17.8 h1:j9m730pMZt1Fc4oKhCLUHfjj6527LuhYcYw0Rl8gqto=
 github.com/google/cel-go v0.17.8/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY=
 github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU=
 github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M=
-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-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
+github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
 github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
+github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
+github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
 github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
 github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -104,8 +104,8 @@ 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/jetstack/venafi-connection-lib v0.1.0 h1:lNPI3gdmXUONGyVlRHDU51T2D61FWid0D7zvTiiDN9w=
-github.com/jetstack/venafi-connection-lib v0.1.0/go.mod h1:lKfibKF2wyMzcaFXg8GIuLILK9zcQRk5RuxfsQYLVLI=
+github.com/jetstack/venafi-connection-lib v0.1.1-0.20240814101135-a72cec54dc1a h1:hZZEgTTtiHZlp++rDqutTBtgsZ2fz4M+UkV65Ru2vrw=
+github.com/jetstack/venafi-connection-lib v0.1.1-0.20240814101135-a72cec54dc1a/go.mod h1:gn2domftAFcrEfrH+OhuTh85L3q0p+rw+j8hFS31Qd4=
 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/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -114,11 +114,8 @@ github.com/juju/errors v1.0.0 h1:yiq7kjCLll1BiaRuNY53MGI0+EQ3rF6GB+wvboZDefM=
 github.com/juju/errors v1.0.0/go.mod h1:B5x9thDqx0wIMH3+aLIMP9HjItInYWObRovoCFM5Qe8=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
@@ -130,8 +127,6 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-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/maxatome/go-testdeep v1.14.0 h1:rRlLv1+kI8eOI3OaBXZwb3O7xY3exRzdW5QyX48g9wI=
 github.com/maxatome/go-testdeep v1.14.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM=
 github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
@@ -143,34 +138,32 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE=
-github.com/onsi/ginkgo/v2 v2.9.4/go.mod h1:gCQYp2Q+kSoIj7ykSVb9nskRSsR6PUj4AiLywzIhbKM=
 github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g=
-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.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
+github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
+github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmylund/go-cache v2.1.0+incompatible h1:n+7K51jLz6a3sCvff3BppuCAkixuDHuJ/C57Vw/XjTE=
 github.com/pmylund/go-cache v2.1.0+incompatible/go.mod h1:hmz95dGvINpbRZGsqPcd7B5xXY5+EKb5PpGhQY3NTHk=
-github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
-github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
 github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
 github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
 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/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
 github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
 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.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
-github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
+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=
+github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
+github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/sosodev/duration v1.2.0 h1:pqK/FLSjsAADWY74SyWDCjOcd5l7H8GSnnOGEB9A1Us=
 github.com/sosodev/duration v1.2.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
 github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
@@ -186,17 +179,40 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
-github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
 github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
-github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
-github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
+github.com/vektah/gqlparser/v2 v2.5.15 h1:fYdnU8roQniJziV5TDiFPm/Ff7pE8xbVSOJqbsdl88A=
+github.com/vektah/gqlparser/v2 v2.5.15/go.mod h1:WQQjFc+I1YIzoPvZBhUQX7waZgg3pMLi0r8KymvAE2w=
 github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
 github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
 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.etcd.io/etcd/api/v3 v3.5.10 h1:szRajuUUbLyppkhs9K6BRtjY37l66XQQmw7oZRANE4k=
+go.etcd.io/etcd/api/v3 v3.5.10/go.mod h1:TidfmT4Uycad3NM/o25fG3J07odo4GBB9hoxaodFCtI=
+go.etcd.io/etcd/client/pkg/v3 v3.5.10 h1:kfYIdQftBnbAq8pUWFXfpuuxFSKzlmM5cSn76JByiT0=
+go.etcd.io/etcd/client/pkg/v3 v3.5.10/go.mod h1:DYivfIviIuQ8+/lCq4vcxuseg2P2XbHygkKwFo9fc8U=
+go.etcd.io/etcd/client/v3 v3.5.10 h1:W9TXNZ+oB3MCd/8UjxHTWK5J9Nquw9fQBLJd5ne5/Ao=
+go.etcd.io/etcd/client/v3 v3.5.10/go.mod h1:RVeBnDz2PUEZqTpgqwAtUd8nAPf5kjyFyND7P1VkOKc=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 h1:ZOLJc06r4CB42laIXg/7udr0pbZyuAihN10A/XuiQRY=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0/go.mod h1:5z+/ZWJQKXa9YT34fQNx5K8Hd1EoIhvtUygUQPqEOgQ=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0 h1:KfYpVmrjI7JuToy5k8XV3nkapjWx48k4E4JOtVstzQI=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0/go.mod h1:SeQhzAEccGVZVEy7aH87Nh0km+utSpo1pTv6eMMop48=
+go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs=
+go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I=
+go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE=
+go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8=
+go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
+go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
+go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
+go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
+go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
+go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
+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=
@@ -205,102 +221,60 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 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/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
-golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
 golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
 golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
 golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
 golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
 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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.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.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
-golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
-golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
-golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
 golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
 golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
-golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY=
-golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0=
-golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
-golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
 golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
 golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
 golang.org/x/sync v0.0.0-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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
-golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
+golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
-golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
-golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
 golang.org/x/sys v0.21.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.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
-golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
-golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
-golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
 golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
 golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
-golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
-golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-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/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
 golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
-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-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.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
-golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 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.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
-google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
+google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac h1:ZL/Teoy/ZGnzyrqK/Optxxp2pmVh+fmJ97slxSRyzUg=
+google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k=
 google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe h1:0poefMBYvYbs7g5UkjS6HcxBPaTRAmznle9jnxYoAI8=
 google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe h1:bQnxqljG/wqi4NTXu2+DJ3n7APcEA882QZ1JvhQAq9o=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
-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.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
-google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4=
-google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
+google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
 google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
 google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -308,8 +282,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/d4l3k/messagediff.v1 v1.2.1 h1:70AthpjunwzUiarMHyED52mj9UwtAnE89l1Gmrt3EU0=
 gopkg.in/d4l3k/messagediff.v1 v1.2.1/go.mod h1:EUzikiKadqXWcD1AzJLagx0j/BeeWGtn++04Xniyg44=
-gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
-gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
 gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
@@ -320,58 +292,30 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM=
-k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc=
-k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA=
-k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE=
+k8s.io/api v0.30.3 h1:ImHwK9DCsPA9uoU3rVh4QHAHHK5dTSv1nxJUapx8hoQ=
 k8s.io/api v0.30.3/go.mod h1:GPc8jlzoe5JG3pb0KJCSLX5oAFIW3/qNJITlDj8BH04=
-k8s.io/api v0.31.0-rc.0 h1:R+jdJGdcV5EQ9hpMR4BhrOmVrCk+fcU1b8tnwSS/DwE=
-k8s.io/api v0.31.0-rc.0/go.mod h1:wb5Wz7B/Mz9Ri/aK+v7XFuePDh6OiZ0aCX6Yb9f/EAE=
-k8s.io/apiextensions-apiserver v0.30.0 h1:jcZFKMqnICJfRxTgnC4E+Hpcq8UEhT8B2lhBcQ+6uAs=
-k8s.io/apiextensions-apiserver v0.30.0/go.mod h1:N9ogQFGcrbWqAY9p2mUAL5mGxsLqwgtUce127VtRX5Y=
-k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A=
-k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8=
-k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA=
-k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
+k8s.io/apiextensions-apiserver v0.30.3 h1:oChu5li2vsZHx2IvnGP3ah8Nj3KyqG3kRSaKmijhB9U=
+k8s.io/apiextensions-apiserver v0.30.3/go.mod h1:uhXxYDkMAvl6CJw4lrDN4CPbONkF3+XL9cacCT44kV4=
+k8s.io/apimachinery v0.30.3 h1:q1laaWCmrszyQuSQCfNB8cFgCuDAoPszKY4ucAjDwHc=
 k8s.io/apimachinery v0.30.3/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
-k8s.io/apimachinery v0.31.0-rc.0 h1:8guKYtBUM/ZEvvQoWfm3hl/NEj+1eVhuSnSpDHheJmw=
-k8s.io/apimachinery v0.31.0-rc.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
-k8s.io/apiserver v0.30.0 h1:QCec+U72tMQ+9tR6A0sMBB5Vh6ImCEkoKkTDRABWq6M=
-k8s.io/apiserver v0.30.0/go.mod h1:smOIBq8t0MbKZi7O7SyIpjPsiKJ8qa+llcFCluKyqiY=
-k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4=
-k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo=
-k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ=
-k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY=
+k8s.io/apiserver v0.30.3 h1:QZJndA9k2MjFqpnyYv/PH+9PE0SHhx3hBho4X0vE65g=
+k8s.io/apiserver v0.30.3/go.mod h1:6Oa88y1CZqnzetd2JdepO0UXzQX4ZnOekx2/PtEjrOg=
 k8s.io/client-go v0.30.3 h1:bHrJu3xQZNXIi8/MoxYtZBBWQQXwy16zqJwloXXfD3k=
 k8s.io/client-go v0.30.3/go.mod h1:8d4pf8vYu665/kUbsxWAQ/JDBNWqfFeZnvFiVdmx89U=
-k8s.io/client-go v0.31.0-rc.0 h1:9ShcEasfMftGPmxfB8lo2QE63SWT+IEVMrFR7IuWkuI=
-k8s.io/client-go v0.31.0-rc.0/go.mod h1:dsCs0l4s2KrHxbbXcHviMhkMBd81uhRPZExRpN6ykgI=
-k8s.io/component-base v0.30.0 h1:cj6bp38g0ainlfYtaOQuRELh5KSYjhKxM+io7AUIk4o=
-k8s.io/component-base v0.30.0/go.mod h1:V9x/0ePFNaKeKYA3bOvIbrNoluTSG+fSJKjLdjOoeXQ=
-k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
-k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
-k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
-k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
+k8s.io/component-base v0.30.3 h1:Ci0UqKWf4oiwy8hr1+E3dsnliKnkMLZMVbWzeorlk7s=
+k8s.io/component-base v0.30.3/go.mod h1:C1SshT3rGPCuNtBs14RmVD2xW0EhRSeLvBh7AGk1quA=
 k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
 k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
-k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
-k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
 k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f h1:0LQagt0gDpKqvIkAMPaRGcXawNMouPECM1+F9BVxEaM=
 k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f/go.mod h1:S9tOR0FxgyusSNR+MboCuiDpVWkAifZvaYI1Q2ubgro=
-k8s.io/kubernetes v1.30.3 h1:A0qoXI1YQNzrQZiff33y5zWxYHFT/HeZRK98/sRDJI0=
-k8s.io/kubernetes v1.30.3/go.mod h1:yPbIk3MhmhGigX62FLJm+CphNtjxqCvAIFQXup6RKS0=
-k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
-k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
-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/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.18.2 h1:RqVW6Kpeaji67CY5nPEfRz6ZfFMk0lWQlNrLqlNpx+Q=
-sigs.k8s.io/controller-runtime v0.18.2/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0 h1:/U5vjBbQn3RChhv7P11uhYvCSm5G2GaIi5AIGBS6r4c=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0/go.mod h1:z7+wmGM2dfIiLRfrC6jb5kV2Mq/sK1ZP303cxzkV5Y4=
+sigs.k8s.io/controller-runtime v0.18.5 h1:nTHio/W+Q4aBlQMgbnC5hZb4IjIidyrizMai9P6n4Rk=
+sigs.k8s.io/controller-runtime v0.18.5/go.mod h1:TVoGrfdpbA9VRFaRnKgk9P5/atA0pMwq+f+msb9M8Sg=
 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.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk=
-sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
 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=

From 571a3a085781fc7244ebd19a58fbd3a49339744b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20Valais?= <mael@vls.dev>
Date: Tue, 20 Aug 2024 11:01:21 +0200
Subject: [PATCH 08/16] venconn: add integration tests using envtest

Note that I should probably have gone with a fake of the
ConnectionHandler instead of an envtest. We will move to the fake later
on.

I added the venaficonnection CRDs manually for now. I have a PR to
automate pulling these CRDs from the venafi-connection-lib project:
https://github.com/jetstack/jetstack-secure/pull/556

For now, I added these manifests manually with the following commands:

  gh pr checkout 556
  git checkout -
  git checkout step1-makefile-modules -- deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd{,.without-validations}.yaml
---
 .gitignore                                    |    1 +
 Makefile                                      |   53 +
 ...fi-connection-crd.without-validations.yaml | 1116 ++++++++++++++++
 .../templates/venafi-connection-crd.yaml      | 1140 +++++++++++++++++
 make/util/checkhash.sh                        |   20 +
 make/util/hash.sh                             |    8 +
 pkg/client/client_venconn_test.go             |    2 +-
 7 files changed, 2339 insertions(+), 1 deletion(-)
 create mode 100644 deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.without-validations.yaml
 create mode 100644 deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.yaml
 create mode 100755 make/util/checkhash.sh
 create mode 100755 make/util/hash.sh

diff --git a/.gitignore b/.gitignore
index 834d55b0..17966351 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,4 @@ predicate.json
 *.pub
 *.tgz
 
+_bin
diff --git a/Makefile b/Makefile
index 280ed9c2..0076d25a 100644
--- a/Makefile
+++ b/Makefile
@@ -48,6 +48,8 @@ build:
 install:
 	cd $(ROOT_DIR) && $(GO_INSTALL)
 
+export KUBEBUILDER_ASSETS=$(ROOT_DIR)/_bin/tools
+test: _bin/tools/etcd _bin/tools/kube-apiserver
 test:
 	cd $(ROOT_DIR) && go test ./...
 
@@ -142,3 +144,54 @@ ci-build: ci-test build build-docker-image build-all-platforms bundle-all-platfo
 
 ci-publish: ci-build push-docker-image
 	echo "ci-publish is going to be disabled. We are adopting Github actions"
+
+# NOTE(mael): The download targets for yq, etcd, and kube-apiserver are a lesser
+# and suboptimal version of what's in venafi-enhanced-issuer. We will migrate to
+# makefile-modules and klone soon, so I didn't want to work too hard on this.
+
+YQ_linux_amd64_SHA256SUM=bd695a6513f1196aeda17b174a15e9c351843fb1cef5f9be0af170f2dd744f08
+YQ_darwin_amd64_SHA256SUM=b2ff70e295d02695b284755b2a41bd889cfb37454e1fa71abc3a6ec13b2676cf
+YQ_darwin_arm64_SHA256SUM=e9fc15db977875de982e0174ba5dc2cf5ae4a644e18432a4262c96d4439b1686
+YQ_VERSION=v4.35.1
+
+_bin/downloaded/tools/yq@$(YQ_VERSION)_%:
+	mkdir -p _bin/downloaded/tools
+	curl -L https://github.com/mikefarah/yq/releases/download/$(YQ_VERSION)/yq_$* -o $@
+	./make/util/checkhash.sh $@ $(YQ_$*_SHA256SUM)
+	chmod +x $@
+
+HOST_OS=$(shell uname | tr '[:upper:]' '[:lower:]')
+HOST_ARCH=$(shell uname -m | sed 's/x86_64/amd64/')
+
+_bin/tools/yq: _bin/downloaded/tools/yq@$(YQ_VERSION)_$(HOST_OS)_$(HOST_ARCH)
+	@mkdir -p _bin/tools
+	@cd $(dir $@) && ln -sf $(patsubst _bin/%,../%,$<) $(notdir $@)
+
+KUBEBUILDER_TOOLS_linux_amd64_SHA256SUM=f9699df7b021f71a1ab55329b36b48a798e6ae3a44d2132255fc7e46c6790d4d
+KUBEBUILDER_TOOLS_darwin_amd64_SHA256SUM=e1913674bacaa70c067e15649237e1f67d891ba53f367c0a50786b4a274ee047
+KUBEBUILDER_TOOLS_darwin_arm64_SHA256SUM=0422632a2bbb0d4d14d7d8b0f05497a4d041c11d770a07b7a55c44bcc5e8ce66
+KUBEBUILDER_ASSETS_VERSION=1.27.1
+
+_bin/downloaded/tools/etcd@$(KUBEBUILDER_ASSETS_VERSION)_%: _bin/downloaded/tools/kubebuilder_tools_$(KUBEBUILDER_ASSETS_VERSION)_%.tar.gz | _bin/downloaded/tools
+	./make/util/checkhash.sh $< $(KUBEBUILDER_TOOLS_$*_SHA256SUM)
+	@# O writes the specified file to stdout
+	tar xfO $< kubebuilder/bin/etcd > $@ && chmod 775 $@
+
+_bin/downloaded/tools/kube-apiserver@$(KUBEBUILDER_ASSETS_VERSION)_%: _bin/downloaded/tools/kubebuilder_tools_$(KUBEBUILDER_ASSETS_VERSION)_%.tar.gz | _bin/downloaded/tools
+	./make/util/checkhash.sh $< $(KUBEBUILDER_TOOLS_$*_SHA256SUM)
+	@# O writes the specified file to stdout
+	tar xfO $< kubebuilder/bin/kube-apiserver > $@ && chmod 775 $@
+
+_bin/downloaded/tools/kubebuilder_tools_$(KUBEBUILDER_ASSETS_VERSION)_$(HOST_OS)_$(HOST_ARCH).tar.gz: | _bin/downloaded/tools
+	curl -L https://storage.googleapis.com/kubebuilder-tools/kubebuilder-tools-$(KUBEBUILDER_ASSETS_VERSION)-$(HOST_OS)-$(HOST_ARCH).tar.gz -o $@
+
+_bin/downloaded/tools:
+	@mkdir -p $@
+
+_bin/tools/etcd: _bin/downloaded/tools/etcd@$(KUBEBUILDER_ASSETS_VERSION)_$(HOST_OS)_$(HOST_ARCH)
+	@mkdir -p _bin/tools
+	@cd $(dir $@) && ln -sf $(patsubst _bin/%,../%,$<) $(notdir $@)
+
+_bin/tools/kube-apiserver: _bin/downloaded/tools/kube-apiserver@$(KUBEBUILDER_ASSETS_VERSION)_$(HOST_OS)_$(HOST_ARCH)
+	@mkdir -p _bin/tools
+	@cd $(dir $@) && ln -sf $(patsubst _bin/%,../%,$<) $(notdir $@)
diff --git a/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.without-validations.yaml b/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.without-validations.yaml
new file mode 100644
index 00000000..6d92a2fa
--- /dev/null
+++ b/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.without-validations.yaml
@@ -0,0 +1,1116 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.14.0
+  name: venaficonnections.jetstack.io
+spec:
+  group: jetstack.io
+  names:
+    kind: VenafiConnection
+    listKind: VenafiConnectionList
+    plural: venaficonnections
+    shortNames:
+      - vc
+    singular: venaficonnection
+  scope: Namespaced
+  versions:
+    - name: v1alpha1
+      schema:
+        openAPIV3Schema:
+          description: VenafiConnection is the Schema for the VenafiConnection API
+          properties:
+            apiVersion:
+              description: |-
+                APIVersion defines the versioned schema of this representation of an object.
+                Servers should convert recognized schemas to the latest internal value, and
+                may reject unrecognized values.
+                More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+              type: string
+            kind:
+              description: |-
+                Kind is a string value representing the REST resource this object represents.
+                Servers may infer this from the endpoint the client submits requests to.
+                Cannot be updated.
+                In CamelCase.
+                More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+              type: string
+            metadata:
+              type: object
+            spec:
+              properties:
+                allowReferencesFrom:
+                  description: |-
+                    A namespace selector that specifies what namespaces this VenafiConnection
+                    is allowed to be used from.
+                    If not set/ null, the VenafiConnection can only be used within its namespace.
+                    An empty selector ({}) matches all namespaces.
+                    If set to a non-empty selector, the VenafiConnection can only be used from
+                    namespaces that match the selector. This possibly excludes the namespace
+                    the VenafiConnection is in.
+                  properties:
+                    matchExpressions:
+                      description: matchExpressions is a list of label selector requirements. The requirements are ANDed.
+                      items:
+                        description: |-
+                          A label selector requirement is a selector that contains values, a key, and an operator that
+                          relates the key and values.
+                        properties:
+                          key:
+                            description: key is the label key that the selector applies to.
+                            type: string
+                          operator:
+                            description: |-
+                              operator represents a key's relationship to a set of values.
+                              Valid operators are In, NotIn, Exists and DoesNotExist.
+                            type: string
+                          values:
+                            description: |-
+                              values is an array of string values. If the operator is In or NotIn,
+                              the values array must be non-empty. If the operator is Exists or DoesNotExist,
+                              the values array must be empty. This array is replaced during a strategic
+                              merge patch.
+                            items:
+                              type: string
+                            type: array
+                            x-kubernetes-list-type: atomic
+                        required:
+                          - key
+                          - operator
+                        type: object
+                      type: array
+                      x-kubernetes-list-type: atomic
+                    matchLabels:
+                      additionalProperties:
+                        type: string
+                      description: |-
+                        matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
+                        map is equivalent to an element of matchExpressions, whose key field is "key", the
+                        operator is "In", and the values array contains only "value". The requirements are ANDed.
+                      type: object
+                  type: object
+                  x-kubernetes-map-type: atomic
+                tpp:
+                  properties:
+                    accessToken:
+                      description: The list of steps to retrieve a TPP access token.
+                      items:
+                        properties:
+                          hashicorpVaultLDAP:
+                            description: |-
+                              HashicorpVaultLDAP is a SecretSource step that requires a Vault token in
+                              the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                              then fetches the requested secrets from Vault for use in the next step.
+                            properties:
+                              ldapPath:
+                                description: |-
+                                  The full HTTP path to the secret in Vault. Example:
+                                  /v1/ldap/static-cred/:role_name
+                                  or
+                                  /v1/ldap/creds/:role_name
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - ldapPath
+                            type: object
+                          hashicorpVaultOAuth:
+                            description: |-
+                              HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource
+                              step to provide an OAuth token, which this step uses to authenticate to
+                              Vault. The output of this step is a Vault token. This step allows you to use
+                              the step `HashicorpVaultSecret` afterwards.
+                            properties:
+                              authInputType:
+                                description: |-
+                                  AuthInputType is the authentication method to be used to authenticate
+                                  with HashiCorp Vault. The only supported value is "OIDC".
+                                enum:
+                                  - OIDC
+                                type: string
+                              authPath:
+                                description: |-
+                                  The login URL used for obtaining the Vault token. Example:
+                                  /v1/auth/oidc/login
+                                type: string
+                              clientId:
+                                description: "DEPRECATED: This field does nothing and will be removed in the future."
+                                type: string
+                              role:
+                                description: |-
+                                  The role defined in Vault that we want to use when authenticating to
+                                  Vault.
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - authInputType
+                              - authPath
+                              - role
+                            type: object
+                          hashicorpVaultSecret:
+                            description: |-
+                              HashicorpVaultSecret is a SecretSource step that requires a Vault token in
+                              the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                              then fetches the requested secrets from Vault for use in the next step.
+                            properties:
+                              fields:
+                                description: |-
+                                  The fields are Vault keys pointing to the secrets passed to the next
+                                  SecretSource step.
+
+
+                                  Example 1 (TPP, username and password): imagining that you have stored
+                                  the username and password for TPP under the keys "username" and
+                                  "password", you will want to set this field to `["username",
+                                  "password"]`. The username is expected to be given first, the password
+                                  second.
+                                items:
+                                  type: string
+                                type: array
+                              secretPath:
+                                description: |-
+                                  The full HTTP path to the secret in Vault. Example:
+                                  /v1/secret/data/application-team-a/tpp-username-password
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - fields
+                              - secretPath
+                            type: object
+                          secret:
+                            description: |-
+                              Secret is a SecretSource step meant to be the first step. It retrieves secret
+                              values from a Kubernetes Secret, and passes them to the next step.
+                            properties:
+                              fields:
+                                description: |-
+                                  The names of the fields we want to extract from the Kubernetes secret.
+                                  These fields are passed to the next step in the chain.
+                                items:
+                                  type: string
+                                type: array
+                              name:
+                                description: The name of the Kubernetes secret.
+                                type: string
+                            required:
+                              - fields
+                              - name
+                            type: object
+                          serviceAccountToken:
+                            description: |-
+                              ServiceAccountToken is a SecretSource step meant to be the first step. It
+                              uses the Kubernetes TokenRequest API to retrieve a token for a given service
+                              account, and passes it to the next step.
+                            properties:
+                              audiences:
+                                description: |-
+                                  Audiences are the intendend audiences of the token. A recipient of a
+                                  token must identify themself with an identifier in the list of
+                                  audiences of the token, and otherwise should reject the token. A
+                                  token issued for multiple audiences may be used to authenticate
+                                  against any of the audiences listed but implies a high degree of
+                                  trust between the target audiences.
+                                items:
+                                  type: string
+                                type: array
+                              expirationSeconds:
+                                description: |-
+                                  ExpirationSeconds is the requested duration of validity of the request. The
+                                  token issuer may return a token with a different validity duration so a
+                                  client needs to check the 'expiration' field in a response.
+                                format: int64
+                                type: integer
+                              name:
+                                description: The name of the Kubernetes service account.
+                                type: string
+                            required:
+                              - audiences
+                              - name
+                            type: object
+                          tppOAuth:
+                            description: |-
+                              TPPOAuth is a SecretSource step that authenticates to a TPP server. This
+                              step is meant to be the last step and requires a prior step that depends
+                              on the `authInputType`.
+                            properties:
+                              authInputType:
+                                description: |-
+                                  AuthInputType is the authentication method to be used to authenticate
+                                  with TPP. The supported values are "UsernamePassword" and "JWT".
+                                enum:
+                                  - UsernamePassword
+                                  - JWT
+                                type: string
+                              clientId:
+                                default: cert-manager.io
+                                description: ClientID is the clientId used to authenticate with TPP.
+                                type: string
+                              url:
+                                description: |-
+                                  The URL to connect to the Venafi TPP instance. The two URLs
+                                  https://tpp.example.com and https://tpp.example.com/vedsdk are
+                                  equivalent. The ending `/vedsdk` is optional and is stripped out
+                                  by our client.
+                                  If not set, defaults to the URL defined at the top-level of the
+                                  TPP configuration.
+                                type: string
+                            required:
+                              - authInputType
+                            type: object
+                          vcpOAuth:
+                            description: |-
+                              VCPOAuth is a SecretSource step that authenticates to the Venafi Control
+                              Plane. This step is meant to be the last step and requires a prior step
+                              that outputs a JWT token.
+                            properties:
+                              tenantID:
+                                description: TenantID is the tenant ID used to authenticate with VCP.
+                                type: string
+                            type: object
+                        type: object
+                      maxItems: 50
+                      type: array
+                      x-kubernetes-list-type: atomic
+                    url:
+                      description: |-
+                        The URL to connect to the Venafi TPP instance. The two URLs
+                        https://tpp.example.com and https://tpp.example.com/vedsdk are
+                        equivalent. The ending `/vedsdk` is optional and is stripped out by
+                        venafi-connection-lib.
+                      type: string
+                  required:
+                    - url
+                  type: object
+                vaas:
+                  description: "Deprecated: The 'vaas' field is deprecated use the field called 'vcp' instead."
+                  properties:
+                    accessToken:
+                      description: |-
+                        The list of steps to retrieve the Access Token that will be used to connect
+                        to VCP.
+                      items:
+                        properties:
+                          hashicorpVaultLDAP:
+                            description: |-
+                              HashicorpVaultLDAP is a SecretSource step that requires a Vault token in
+                              the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                              then fetches the requested secrets from Vault for use in the next step.
+                            properties:
+                              ldapPath:
+                                description: |-
+                                  The full HTTP path to the secret in Vault. Example:
+                                  /v1/ldap/static-cred/:role_name
+                                  or
+                                  /v1/ldap/creds/:role_name
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - ldapPath
+                            type: object
+                          hashicorpVaultOAuth:
+                            description: |-
+                              HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource
+                              step to provide an OAuth token, which this step uses to authenticate to
+                              Vault. The output of this step is a Vault token. This step allows you to use
+                              the step `HashicorpVaultSecret` afterwards.
+                            properties:
+                              authInputType:
+                                description: |-
+                                  AuthInputType is the authentication method to be used to authenticate
+                                  with HashiCorp Vault. The only supported value is "OIDC".
+                                enum:
+                                  - OIDC
+                                type: string
+                              authPath:
+                                description: |-
+                                  The login URL used for obtaining the Vault token. Example:
+                                  /v1/auth/oidc/login
+                                type: string
+                              clientId:
+                                description: "DEPRECATED: This field does nothing and will be removed in the future."
+                                type: string
+                              role:
+                                description: |-
+                                  The role defined in Vault that we want to use when authenticating to
+                                  Vault.
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - authInputType
+                              - authPath
+                              - role
+                            type: object
+                          hashicorpVaultSecret:
+                            description: |-
+                              HashicorpVaultSecret is a SecretSource step that requires a Vault token in
+                              the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                              then fetches the requested secrets from Vault for use in the next step.
+                            properties:
+                              fields:
+                                description: |-
+                                  The fields are Vault keys pointing to the secrets passed to the next
+                                  SecretSource step.
+
+
+                                  Example 1 (TPP, username and password): imagining that you have stored
+                                  the username and password for TPP under the keys "username" and
+                                  "password", you will want to set this field to `["username",
+                                  "password"]`. The username is expected to be given first, the password
+                                  second.
+                                items:
+                                  type: string
+                                type: array
+                              secretPath:
+                                description: |-
+                                  The full HTTP path to the secret in Vault. Example:
+                                  /v1/secret/data/application-team-a/tpp-username-password
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - fields
+                              - secretPath
+                            type: object
+                          secret:
+                            description: |-
+                              Secret is a SecretSource step meant to be the first step. It retrieves secret
+                              values from a Kubernetes Secret, and passes them to the next step.
+                            properties:
+                              fields:
+                                description: |-
+                                  The names of the fields we want to extract from the Kubernetes secret.
+                                  These fields are passed to the next step in the chain.
+                                items:
+                                  type: string
+                                type: array
+                              name:
+                                description: The name of the Kubernetes secret.
+                                type: string
+                            required:
+                              - fields
+                              - name
+                            type: object
+                          serviceAccountToken:
+                            description: |-
+                              ServiceAccountToken is a SecretSource step meant to be the first step. It
+                              uses the Kubernetes TokenRequest API to retrieve a token for a given service
+                              account, and passes it to the next step.
+                            properties:
+                              audiences:
+                                description: |-
+                                  Audiences are the intendend audiences of the token. A recipient of a
+                                  token must identify themself with an identifier in the list of
+                                  audiences of the token, and otherwise should reject the token. A
+                                  token issued for multiple audiences may be used to authenticate
+                                  against any of the audiences listed but implies a high degree of
+                                  trust between the target audiences.
+                                items:
+                                  type: string
+                                type: array
+                              expirationSeconds:
+                                description: |-
+                                  ExpirationSeconds is the requested duration of validity of the request. The
+                                  token issuer may return a token with a different validity duration so a
+                                  client needs to check the 'expiration' field in a response.
+                                format: int64
+                                type: integer
+                              name:
+                                description: The name of the Kubernetes service account.
+                                type: string
+                            required:
+                              - audiences
+                              - name
+                            type: object
+                          tppOAuth:
+                            description: |-
+                              TPPOAuth is a SecretSource step that authenticates to a TPP server. This
+                              step is meant to be the last step and requires a prior step that depends
+                              on the `authInputType`.
+                            properties:
+                              authInputType:
+                                description: |-
+                                  AuthInputType is the authentication method to be used to authenticate
+                                  with TPP. The supported values are "UsernamePassword" and "JWT".
+                                enum:
+                                  - UsernamePassword
+                                  - JWT
+                                type: string
+                              clientId:
+                                default: cert-manager.io
+                                description: ClientID is the clientId used to authenticate with TPP.
+                                type: string
+                              url:
+                                description: |-
+                                  The URL to connect to the Venafi TPP instance. The two URLs
+                                  https://tpp.example.com and https://tpp.example.com/vedsdk are
+                                  equivalent. The ending `/vedsdk` is optional and is stripped out
+                                  by our client.
+                                  If not set, defaults to the URL defined at the top-level of the
+                                  TPP configuration.
+                                type: string
+                            required:
+                              - authInputType
+                            type: object
+                          vcpOAuth:
+                            description: |-
+                              VCPOAuth is a SecretSource step that authenticates to the Venafi Control
+                              Plane. This step is meant to be the last step and requires a prior step
+                              that outputs a JWT token.
+                            properties:
+                              tenantID:
+                                description: TenantID is the tenant ID used to authenticate with VCP.
+                                type: string
+                            type: object
+                        type: object
+                      maxItems: 50
+                      type: array
+                      x-kubernetes-list-type: atomic
+                    apiKey:
+                      description: |-
+                        The list of steps to retrieve the API key that will be used to connect to
+                        VCP.
+                      items:
+                        properties:
+                          hashicorpVaultLDAP:
+                            description: |-
+                              HashicorpVaultLDAP is a SecretSource step that requires a Vault token in
+                              the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                              then fetches the requested secrets from Vault for use in the next step.
+                            properties:
+                              ldapPath:
+                                description: |-
+                                  The full HTTP path to the secret in Vault. Example:
+                                  /v1/ldap/static-cred/:role_name
+                                  or
+                                  /v1/ldap/creds/:role_name
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - ldapPath
+                            type: object
+                          hashicorpVaultOAuth:
+                            description: |-
+                              HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource
+                              step to provide an OAuth token, which this step uses to authenticate to
+                              Vault. The output of this step is a Vault token. This step allows you to use
+                              the step `HashicorpVaultSecret` afterwards.
+                            properties:
+                              authInputType:
+                                description: |-
+                                  AuthInputType is the authentication method to be used to authenticate
+                                  with HashiCorp Vault. The only supported value is "OIDC".
+                                enum:
+                                  - OIDC
+                                type: string
+                              authPath:
+                                description: |-
+                                  The login URL used for obtaining the Vault token. Example:
+                                  /v1/auth/oidc/login
+                                type: string
+                              clientId:
+                                description: "DEPRECATED: This field does nothing and will be removed in the future."
+                                type: string
+                              role:
+                                description: |-
+                                  The role defined in Vault that we want to use when authenticating to
+                                  Vault.
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - authInputType
+                              - authPath
+                              - role
+                            type: object
+                          hashicorpVaultSecret:
+                            description: |-
+                              HashicorpVaultSecret is a SecretSource step that requires a Vault token in
+                              the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                              then fetches the requested secrets from Vault for use in the next step.
+                            properties:
+                              fields:
+                                description: |-
+                                  The fields are Vault keys pointing to the secrets passed to the next
+                                  SecretSource step.
+
+
+                                  Example 1 (TPP, username and password): imagining that you have stored
+                                  the username and password for TPP under the keys "username" and
+                                  "password", you will want to set this field to `["username",
+                                  "password"]`. The username is expected to be given first, the password
+                                  second.
+                                items:
+                                  type: string
+                                type: array
+                              secretPath:
+                                description: |-
+                                  The full HTTP path to the secret in Vault. Example:
+                                  /v1/secret/data/application-team-a/tpp-username-password
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - fields
+                              - secretPath
+                            type: object
+                          secret:
+                            description: |-
+                              Secret is a SecretSource step meant to be the first step. It retrieves secret
+                              values from a Kubernetes Secret, and passes them to the next step.
+                            properties:
+                              fields:
+                                description: |-
+                                  The names of the fields we want to extract from the Kubernetes secret.
+                                  These fields are passed to the next step in the chain.
+                                items:
+                                  type: string
+                                type: array
+                              name:
+                                description: The name of the Kubernetes secret.
+                                type: string
+                            required:
+                              - fields
+                              - name
+                            type: object
+                          serviceAccountToken:
+                            description: |-
+                              ServiceAccountToken is a SecretSource step meant to be the first step. It
+                              uses the Kubernetes TokenRequest API to retrieve a token for a given service
+                              account, and passes it to the next step.
+                            properties:
+                              audiences:
+                                description: |-
+                                  Audiences are the intendend audiences of the token. A recipient of a
+                                  token must identify themself with an identifier in the list of
+                                  audiences of the token, and otherwise should reject the token. A
+                                  token issued for multiple audiences may be used to authenticate
+                                  against any of the audiences listed but implies a high degree of
+                                  trust between the target audiences.
+                                items:
+                                  type: string
+                                type: array
+                              expirationSeconds:
+                                description: |-
+                                  ExpirationSeconds is the requested duration of validity of the request. The
+                                  token issuer may return a token with a different validity duration so a
+                                  client needs to check the 'expiration' field in a response.
+                                format: int64
+                                type: integer
+                              name:
+                                description: The name of the Kubernetes service account.
+                                type: string
+                            required:
+                              - audiences
+                              - name
+                            type: object
+                          tppOAuth:
+                            description: |-
+                              TPPOAuth is a SecretSource step that authenticates to a TPP server. This
+                              step is meant to be the last step and requires a prior step that depends
+                              on the `authInputType`.
+                            properties:
+                              authInputType:
+                                description: |-
+                                  AuthInputType is the authentication method to be used to authenticate
+                                  with TPP. The supported values are "UsernamePassword" and "JWT".
+                                enum:
+                                  - UsernamePassword
+                                  - JWT
+                                type: string
+                              clientId:
+                                default: cert-manager.io
+                                description: ClientID is the clientId used to authenticate with TPP.
+                                type: string
+                              url:
+                                description: |-
+                                  The URL to connect to the Venafi TPP instance. The two URLs
+                                  https://tpp.example.com and https://tpp.example.com/vedsdk are
+                                  equivalent. The ending `/vedsdk` is optional and is stripped out
+                                  by our client.
+                                  If not set, defaults to the URL defined at the top-level of the
+                                  TPP configuration.
+                                type: string
+                            required:
+                              - authInputType
+                            type: object
+                          vcpOAuth:
+                            description: |-
+                              VCPOAuth is a SecretSource step that authenticates to the Venafi Control
+                              Plane. This step is meant to be the last step and requires a prior step
+                              that outputs a JWT token.
+                            properties:
+                              tenantID:
+                                description: TenantID is the tenant ID used to authenticate with VCP.
+                                type: string
+                            type: object
+                        type: object
+                      maxItems: 50
+                      type: array
+                      x-kubernetes-list-type: atomic
+                    url:
+                      description: |-
+                        The URL to connect to the Venafi VCP instance. If not set, the default
+                        value https://api.venafi.cloud is used.
+                      type: string
+                  type: object
+                vcp:
+                  properties:
+                    accessToken:
+                      description: |-
+                        The list of steps to retrieve the Access Token that will be used to connect
+                        to VCP.
+                      items:
+                        properties:
+                          hashicorpVaultLDAP:
+                            description: |-
+                              HashicorpVaultLDAP is a SecretSource step that requires a Vault token in
+                              the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                              then fetches the requested secrets from Vault for use in the next step.
+                            properties:
+                              ldapPath:
+                                description: |-
+                                  The full HTTP path to the secret in Vault. Example:
+                                  /v1/ldap/static-cred/:role_name
+                                  or
+                                  /v1/ldap/creds/:role_name
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - ldapPath
+                            type: object
+                          hashicorpVaultOAuth:
+                            description: |-
+                              HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource
+                              step to provide an OAuth token, which this step uses to authenticate to
+                              Vault. The output of this step is a Vault token. This step allows you to use
+                              the step `HashicorpVaultSecret` afterwards.
+                            properties:
+                              authInputType:
+                                description: |-
+                                  AuthInputType is the authentication method to be used to authenticate
+                                  with HashiCorp Vault. The only supported value is "OIDC".
+                                enum:
+                                  - OIDC
+                                type: string
+                              authPath:
+                                description: |-
+                                  The login URL used for obtaining the Vault token. Example:
+                                  /v1/auth/oidc/login
+                                type: string
+                              clientId:
+                                description: "DEPRECATED: This field does nothing and will be removed in the future."
+                                type: string
+                              role:
+                                description: |-
+                                  The role defined in Vault that we want to use when authenticating to
+                                  Vault.
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - authInputType
+                              - authPath
+                              - role
+                            type: object
+                          hashicorpVaultSecret:
+                            description: |-
+                              HashicorpVaultSecret is a SecretSource step that requires a Vault token in
+                              the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                              then fetches the requested secrets from Vault for use in the next step.
+                            properties:
+                              fields:
+                                description: |-
+                                  The fields are Vault keys pointing to the secrets passed to the next
+                                  SecretSource step.
+
+
+                                  Example 1 (TPP, username and password): imagining that you have stored
+                                  the username and password for TPP under the keys "username" and
+                                  "password", you will want to set this field to `["username",
+                                  "password"]`. The username is expected to be given first, the password
+                                  second.
+                                items:
+                                  type: string
+                                type: array
+                              secretPath:
+                                description: |-
+                                  The full HTTP path to the secret in Vault. Example:
+                                  /v1/secret/data/application-team-a/tpp-username-password
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - fields
+                              - secretPath
+                            type: object
+                          secret:
+                            description: |-
+                              Secret is a SecretSource step meant to be the first step. It retrieves secret
+                              values from a Kubernetes Secret, and passes them to the next step.
+                            properties:
+                              fields:
+                                description: |-
+                                  The names of the fields we want to extract from the Kubernetes secret.
+                                  These fields are passed to the next step in the chain.
+                                items:
+                                  type: string
+                                type: array
+                              name:
+                                description: The name of the Kubernetes secret.
+                                type: string
+                            required:
+                              - fields
+                              - name
+                            type: object
+                          serviceAccountToken:
+                            description: |-
+                              ServiceAccountToken is a SecretSource step meant to be the first step. It
+                              uses the Kubernetes TokenRequest API to retrieve a token for a given service
+                              account, and passes it to the next step.
+                            properties:
+                              audiences:
+                                description: |-
+                                  Audiences are the intendend audiences of the token. A recipient of a
+                                  token must identify themself with an identifier in the list of
+                                  audiences of the token, and otherwise should reject the token. A
+                                  token issued for multiple audiences may be used to authenticate
+                                  against any of the audiences listed but implies a high degree of
+                                  trust between the target audiences.
+                                items:
+                                  type: string
+                                type: array
+                              expirationSeconds:
+                                description: |-
+                                  ExpirationSeconds is the requested duration of validity of the request. The
+                                  token issuer may return a token with a different validity duration so a
+                                  client needs to check the 'expiration' field in a response.
+                                format: int64
+                                type: integer
+                              name:
+                                description: The name of the Kubernetes service account.
+                                type: string
+                            required:
+                              - audiences
+                              - name
+                            type: object
+                          tppOAuth:
+                            description: |-
+                              TPPOAuth is a SecretSource step that authenticates to a TPP server. This
+                              step is meant to be the last step and requires a prior step that depends
+                              on the `authInputType`.
+                            properties:
+                              authInputType:
+                                description: |-
+                                  AuthInputType is the authentication method to be used to authenticate
+                                  with TPP. The supported values are "UsernamePassword" and "JWT".
+                                enum:
+                                  - UsernamePassword
+                                  - JWT
+                                type: string
+                              clientId:
+                                default: cert-manager.io
+                                description: ClientID is the clientId used to authenticate with TPP.
+                                type: string
+                              url:
+                                description: |-
+                                  The URL to connect to the Venafi TPP instance. The two URLs
+                                  https://tpp.example.com and https://tpp.example.com/vedsdk are
+                                  equivalent. The ending `/vedsdk` is optional and is stripped out
+                                  by our client.
+                                  If not set, defaults to the URL defined at the top-level of the
+                                  TPP configuration.
+                                type: string
+                            required:
+                              - authInputType
+                            type: object
+                          vcpOAuth:
+                            description: |-
+                              VCPOAuth is a SecretSource step that authenticates to the Venafi Control
+                              Plane. This step is meant to be the last step and requires a prior step
+                              that outputs a JWT token.
+                            properties:
+                              tenantID:
+                                description: TenantID is the tenant ID used to authenticate with VCP.
+                                type: string
+                            type: object
+                        type: object
+                      maxItems: 50
+                      type: array
+                      x-kubernetes-list-type: atomic
+                    apiKey:
+                      description: |-
+                        The list of steps to retrieve the API key that will be used to connect to
+                        VCP.
+                      items:
+                        properties:
+                          hashicorpVaultLDAP:
+                            description: |-
+                              HashicorpVaultLDAP is a SecretSource step that requires a Vault token in
+                              the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                              then fetches the requested secrets from Vault for use in the next step.
+                            properties:
+                              ldapPath:
+                                description: |-
+                                  The full HTTP path to the secret in Vault. Example:
+                                  /v1/ldap/static-cred/:role_name
+                                  or
+                                  /v1/ldap/creds/:role_name
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - ldapPath
+                            type: object
+                          hashicorpVaultOAuth:
+                            description: |-
+                              HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource
+                              step to provide an OAuth token, which this step uses to authenticate to
+                              Vault. The output of this step is a Vault token. This step allows you to use
+                              the step `HashicorpVaultSecret` afterwards.
+                            properties:
+                              authInputType:
+                                description: |-
+                                  AuthInputType is the authentication method to be used to authenticate
+                                  with HashiCorp Vault. The only supported value is "OIDC".
+                                enum:
+                                  - OIDC
+                                type: string
+                              authPath:
+                                description: |-
+                                  The login URL used for obtaining the Vault token. Example:
+                                  /v1/auth/oidc/login
+                                type: string
+                              clientId:
+                                description: "DEPRECATED: This field does nothing and will be removed in the future."
+                                type: string
+                              role:
+                                description: |-
+                                  The role defined in Vault that we want to use when authenticating to
+                                  Vault.
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - authInputType
+                              - authPath
+                              - role
+                            type: object
+                          hashicorpVaultSecret:
+                            description: |-
+                              HashicorpVaultSecret is a SecretSource step that requires a Vault token in
+                              the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                              then fetches the requested secrets from Vault for use in the next step.
+                            properties:
+                              fields:
+                                description: |-
+                                  The fields are Vault keys pointing to the secrets passed to the next
+                                  SecretSource step.
+
+
+                                  Example 1 (TPP, username and password): imagining that you have stored
+                                  the username and password for TPP under the keys "username" and
+                                  "password", you will want to set this field to `["username",
+                                  "password"]`. The username is expected to be given first, the password
+                                  second.
+                                items:
+                                  type: string
+                                type: array
+                              secretPath:
+                                description: |-
+                                  The full HTTP path to the secret in Vault. Example:
+                                  /v1/secret/data/application-team-a/tpp-username-password
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - fields
+                              - secretPath
+                            type: object
+                          secret:
+                            description: |-
+                              Secret is a SecretSource step meant to be the first step. It retrieves secret
+                              values from a Kubernetes Secret, and passes them to the next step.
+                            properties:
+                              fields:
+                                description: |-
+                                  The names of the fields we want to extract from the Kubernetes secret.
+                                  These fields are passed to the next step in the chain.
+                                items:
+                                  type: string
+                                type: array
+                              name:
+                                description: The name of the Kubernetes secret.
+                                type: string
+                            required:
+                              - fields
+                              - name
+                            type: object
+                          serviceAccountToken:
+                            description: |-
+                              ServiceAccountToken is a SecretSource step meant to be the first step. It
+                              uses the Kubernetes TokenRequest API to retrieve a token for a given service
+                              account, and passes it to the next step.
+                            properties:
+                              audiences:
+                                description: |-
+                                  Audiences are the intendend audiences of the token. A recipient of a
+                                  token must identify themself with an identifier in the list of
+                                  audiences of the token, and otherwise should reject the token. A
+                                  token issued for multiple audiences may be used to authenticate
+                                  against any of the audiences listed but implies a high degree of
+                                  trust between the target audiences.
+                                items:
+                                  type: string
+                                type: array
+                              expirationSeconds:
+                                description: |-
+                                  ExpirationSeconds is the requested duration of validity of the request. The
+                                  token issuer may return a token with a different validity duration so a
+                                  client needs to check the 'expiration' field in a response.
+                                format: int64
+                                type: integer
+                              name:
+                                description: The name of the Kubernetes service account.
+                                type: string
+                            required:
+                              - audiences
+                              - name
+                            type: object
+                          tppOAuth:
+                            description: |-
+                              TPPOAuth is a SecretSource step that authenticates to a TPP server. This
+                              step is meant to be the last step and requires a prior step that depends
+                              on the `authInputType`.
+                            properties:
+                              authInputType:
+                                description: |-
+                                  AuthInputType is the authentication method to be used to authenticate
+                                  with TPP. The supported values are "UsernamePassword" and "JWT".
+                                enum:
+                                  - UsernamePassword
+                                  - JWT
+                                type: string
+                              clientId:
+                                default: cert-manager.io
+                                description: ClientID is the clientId used to authenticate with TPP.
+                                type: string
+                              url:
+                                description: |-
+                                  The URL to connect to the Venafi TPP instance. The two URLs
+                                  https://tpp.example.com and https://tpp.example.com/vedsdk are
+                                  equivalent. The ending `/vedsdk` is optional and is stripped out
+                                  by our client.
+                                  If not set, defaults to the URL defined at the top-level of the
+                                  TPP configuration.
+                                type: string
+                            required:
+                              - authInputType
+                            type: object
+                          vcpOAuth:
+                            description: |-
+                              VCPOAuth is a SecretSource step that authenticates to the Venafi Control
+                              Plane. This step is meant to be the last step and requires a prior step
+                              that outputs a JWT token.
+                            properties:
+                              tenantID:
+                                description: TenantID is the tenant ID used to authenticate with VCP.
+                                type: string
+                            type: object
+                        type: object
+                      maxItems: 50
+                      type: array
+                      x-kubernetes-list-type: atomic
+                    url:
+                      description: |-
+                        The URL to connect to the Venafi VCP instance. If not set, the default
+                        value https://api.venafi.cloud is used.
+                      type: string
+                  type: object
+              type: object
+            status:
+              properties:
+                conditions:
+                  description: List of status conditions to indicate the status of a VenafiConnection.
+                  items:
+                    description: ConnectionCondition contains condition information for a VenafiConnection.
+                    properties:
+                      lastTransitionTime:
+                        description: |-
+                          LastTransitionTime is the timestamp corresponding to the last status
+                          change of this condition.
+                        format: date-time
+                        type: string
+                      lastUpdateTime:
+                        description: lastUpdateTime is the time of the last update to this condition
+                        format: date-time
+                        type: string
+                      message:
+                        description: |-
+                          Message is a human readable description of the details of the last
+                          transition, complementing reason.
+                        type: string
+                      observedGeneration:
+                        description: |-
+                          If set, this represents the .metadata.generation that the condition was
+                          set based upon.
+                          For instance, if .metadata.generation is currently 12, but the
+                          .status.condition[x].observedGeneration is 9, the condition is out of date
+                          with respect to the current state of the Issuer.
+                        format: int64
+                        type: integer
+                      reason:
+                        description: |-
+                          Reason is a brief machine readable explanation for the condition's last
+                          transition.
+                        type: string
+                      status:
+                        description: Status of the condition, one of (`True`, `False`, `Unknown`).
+                        type: string
+                      tokenValidUntil:
+                        description: |-
+                          The ValidUntil time of the token used to authenticate with the Venafi
+                          Control Plane server.
+                        format: date-time
+                        type: string
+                      type:
+                        description: |-
+                          Type of the condition, should be a combination of the unique name of the
+                          operator and the type of condition.
+                          eg. `VenafiEnhancedIssuerReady`
+                        type: string
+                    required:
+                      - status
+                      - type
+                    type: object
+                  type: array
+                  x-kubernetes-list-map-keys:
+                    - type
+                  x-kubernetes-list-type: map
+              type: object
+          type: object
+      served: true
+      storage: true
+      subresources:
+        status: {}
diff --git a/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.yaml b/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.yaml
new file mode 100644
index 00000000..97a6aa71
--- /dev/null
+++ b/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.yaml
@@ -0,0 +1,1140 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.14.0
+  name: venaficonnections.jetstack.io
+spec:
+  group: jetstack.io
+  names:
+    kind: VenafiConnection
+    listKind: VenafiConnectionList
+    plural: venaficonnections
+    shortNames:
+      - vc
+    singular: venaficonnection
+  scope: Namespaced
+  versions:
+    - name: v1alpha1
+      schema:
+        openAPIV3Schema:
+          description: VenafiConnection is the Schema for the VenafiConnection API
+          properties:
+            apiVersion:
+              description: |-
+                APIVersion defines the versioned schema of this representation of an object.
+                Servers should convert recognized schemas to the latest internal value, and
+                may reject unrecognized values.
+                More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+              type: string
+            kind:
+              description: |-
+                Kind is a string value representing the REST resource this object represents.
+                Servers may infer this from the endpoint the client submits requests to.
+                Cannot be updated.
+                In CamelCase.
+                More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+              type: string
+            metadata:
+              type: object
+            spec:
+              properties:
+                allowReferencesFrom:
+                  description: |-
+                    A namespace selector that specifies what namespaces this VenafiConnection
+                    is allowed to be used from.
+                    If not set/ null, the VenafiConnection can only be used within its namespace.
+                    An empty selector ({}) matches all namespaces.
+                    If set to a non-empty selector, the VenafiConnection can only be used from
+                    namespaces that match the selector. This possibly excludes the namespace
+                    the VenafiConnection is in.
+                  properties:
+                    matchExpressions:
+                      description: matchExpressions is a list of label selector requirements. The requirements are ANDed.
+                      items:
+                        description: |-
+                          A label selector requirement is a selector that contains values, a key, and an operator that
+                          relates the key and values.
+                        properties:
+                          key:
+                            description: key is the label key that the selector applies to.
+                            type: string
+                          operator:
+                            description: |-
+                              operator represents a key's relationship to a set of values.
+                              Valid operators are In, NotIn, Exists and DoesNotExist.
+                            type: string
+                          values:
+                            description: |-
+                              values is an array of string values. If the operator is In or NotIn,
+                              the values array must be non-empty. If the operator is Exists or DoesNotExist,
+                              the values array must be empty. This array is replaced during a strategic
+                              merge patch.
+                            items:
+                              type: string
+                            type: array
+                            x-kubernetes-list-type: atomic
+                        required:
+                          - key
+                          - operator
+                        type: object
+                      type: array
+                      x-kubernetes-list-type: atomic
+                    matchLabels:
+                      additionalProperties:
+                        type: string
+                      description: |-
+                        matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
+                        map is equivalent to an element of matchExpressions, whose key field is "key", the
+                        operator is "In", and the values array contains only "value". The requirements are ANDed.
+                      type: object
+                  type: object
+                  x-kubernetes-map-type: atomic
+                tpp:
+                  properties:
+                    accessToken:
+                      description: The list of steps to retrieve a TPP access token.
+                      items:
+                        properties:
+                          hashicorpVaultLDAP:
+                            description: |-
+                              HashicorpVaultLDAP is a SecretSource step that requires a Vault token in
+                              the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                              then fetches the requested secrets from Vault for use in the next step.
+                            properties:
+                              ldapPath:
+                                description: |-
+                                  The full HTTP path to the secret in Vault. Example:
+                                  /v1/ldap/static-cred/:role_name
+                                  or
+                                  /v1/ldap/creds/:role_name
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - ldapPath
+                            type: object
+                          hashicorpVaultOAuth:
+                            description: |-
+                              HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource
+                              step to provide an OAuth token, which this step uses to authenticate to
+                              Vault. The output of this step is a Vault token. This step allows you to use
+                              the step `HashicorpVaultSecret` afterwards.
+                            properties:
+                              authInputType:
+                                description: |-
+                                  AuthInputType is the authentication method to be used to authenticate
+                                  with HashiCorp Vault. The only supported value is "OIDC".
+                                enum:
+                                  - OIDC
+                                type: string
+                              authPath:
+                                description: |-
+                                  The login URL used for obtaining the Vault token. Example:
+                                  /v1/auth/oidc/login
+                                type: string
+                              clientId:
+                                description: "DEPRECATED: This field does nothing and will be removed in the future."
+                                type: string
+                              role:
+                                description: |-
+                                  The role defined in Vault that we want to use when authenticating to
+                                  Vault.
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - authInputType
+                              - authPath
+                              - role
+                            type: object
+                          hashicorpVaultSecret:
+                            description: |-
+                              HashicorpVaultSecret is a SecretSource step that requires a Vault token in
+                              the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                              then fetches the requested secrets from Vault for use in the next step.
+                            properties:
+                              fields:
+                                description: |-
+                                  The fields are Vault keys pointing to the secrets passed to the next
+                                  SecretSource step.
+
+
+                                  Example 1 (TPP, username and password): imagining that you have stored
+                                  the username and password for TPP under the keys "username" and
+                                  "password", you will want to set this field to `["username",
+                                  "password"]`. The username is expected to be given first, the password
+                                  second.
+                                items:
+                                  type: string
+                                type: array
+                              secretPath:
+                                description: |-
+                                  The full HTTP path to the secret in Vault. Example:
+                                  /v1/secret/data/application-team-a/tpp-username-password
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - fields
+                              - secretPath
+                            type: object
+                          secret:
+                            description: |-
+                              Secret is a SecretSource step meant to be the first step. It retrieves secret
+                              values from a Kubernetes Secret, and passes them to the next step.
+                            properties:
+                              fields:
+                                description: |-
+                                  The names of the fields we want to extract from the Kubernetes secret.
+                                  These fields are passed to the next step in the chain.
+                                items:
+                                  type: string
+                                type: array
+                              name:
+                                description: The name of the Kubernetes secret.
+                                type: string
+                            required:
+                              - fields
+                              - name
+                            type: object
+                          serviceAccountToken:
+                            description: |-
+                              ServiceAccountToken is a SecretSource step meant to be the first step. It
+                              uses the Kubernetes TokenRequest API to retrieve a token for a given service
+                              account, and passes it to the next step.
+                            properties:
+                              audiences:
+                                description: |-
+                                  Audiences are the intendend audiences of the token. A recipient of a
+                                  token must identify themself with an identifier in the list of
+                                  audiences of the token, and otherwise should reject the token. A
+                                  token issued for multiple audiences may be used to authenticate
+                                  against any of the audiences listed but implies a high degree of
+                                  trust between the target audiences.
+                                items:
+                                  type: string
+                                type: array
+                              expirationSeconds:
+                                description: |-
+                                  ExpirationSeconds is the requested duration of validity of the request. The
+                                  token issuer may return a token with a different validity duration so a
+                                  client needs to check the 'expiration' field in a response.
+                                format: int64
+                                type: integer
+                              name:
+                                description: The name of the Kubernetes service account.
+                                type: string
+                            required:
+                              - audiences
+                              - name
+                            type: object
+                          tppOAuth:
+                            description: |-
+                              TPPOAuth is a SecretSource step that authenticates to a TPP server. This
+                              step is meant to be the last step and requires a prior step that depends
+                              on the `authInputType`.
+                            properties:
+                              authInputType:
+                                description: |-
+                                  AuthInputType is the authentication method to be used to authenticate
+                                  with TPP. The supported values are "UsernamePassword" and "JWT".
+                                enum:
+                                  - UsernamePassword
+                                  - JWT
+                                type: string
+                              clientId:
+                                default: cert-manager.io
+                                description: ClientID is the clientId used to authenticate with TPP.
+                                type: string
+                              url:
+                                description: |-
+                                  The URL to connect to the Venafi TPP instance. The two URLs
+                                  https://tpp.example.com and https://tpp.example.com/vedsdk are
+                                  equivalent. The ending `/vedsdk` is optional and is stripped out
+                                  by our client.
+                                  If not set, defaults to the URL defined at the top-level of the
+                                  TPP configuration.
+                                type: string
+                            required:
+                              - authInputType
+                            type: object
+                          vcpOAuth:
+                            description: |-
+                              VCPOAuth is a SecretSource step that authenticates to the Venafi Control
+                              Plane. This step is meant to be the last step and requires a prior step
+                              that outputs a JWT token.
+                            properties:
+                              tenantID:
+                                description: TenantID is the tenant ID used to authenticate with VCP.
+                                type: string
+                            type: object
+                        type: object
+                        x-kubernetes-validations:
+                          - message: must have exactly one field set
+                            rule: "(has(self.secret) ? 1 : 0) + (has(self.serviceAccountToken) ? 1 : 0) + (has(self.hashicorpVaultOAuth) ? 1 : 0) + (has(self.hashicorpVaultSecret) ? 1 : 0) + (has(self.hashicorpVaultLDAP) ? 1 : 0) + (has(self.tppOAuth) ? 1 : 0) + (has(self.vcpOAuth) ? 1 : 0) == 1"
+                      maxItems: 50
+                      type: array
+                      x-kubernetes-list-type: atomic
+                    url:
+                      description: |-
+                        The URL to connect to the Venafi TPP instance. The two URLs
+                        https://tpp.example.com and https://tpp.example.com/vedsdk are
+                        equivalent. The ending `/vedsdk` is optional and is stripped out by
+                        venafi-connection-lib.
+                      type: string
+                  required:
+                    - url
+                  type: object
+                vaas:
+                  description: "Deprecated: The 'vaas' field is deprecated use the field called 'vcp' instead."
+                  properties:
+                    accessToken:
+                      description: |-
+                        The list of steps to retrieve the Access Token that will be used to connect
+                        to VCP.
+                      items:
+                        properties:
+                          hashicorpVaultLDAP:
+                            description: |-
+                              HashicorpVaultLDAP is a SecretSource step that requires a Vault token in
+                              the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                              then fetches the requested secrets from Vault for use in the next step.
+                            properties:
+                              ldapPath:
+                                description: |-
+                                  The full HTTP path to the secret in Vault. Example:
+                                  /v1/ldap/static-cred/:role_name
+                                  or
+                                  /v1/ldap/creds/:role_name
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - ldapPath
+                            type: object
+                          hashicorpVaultOAuth:
+                            description: |-
+                              HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource
+                              step to provide an OAuth token, which this step uses to authenticate to
+                              Vault. The output of this step is a Vault token. This step allows you to use
+                              the step `HashicorpVaultSecret` afterwards.
+                            properties:
+                              authInputType:
+                                description: |-
+                                  AuthInputType is the authentication method to be used to authenticate
+                                  with HashiCorp Vault. The only supported value is "OIDC".
+                                enum:
+                                  - OIDC
+                                type: string
+                              authPath:
+                                description: |-
+                                  The login URL used for obtaining the Vault token. Example:
+                                  /v1/auth/oidc/login
+                                type: string
+                              clientId:
+                                description: "DEPRECATED: This field does nothing and will be removed in the future."
+                                type: string
+                              role:
+                                description: |-
+                                  The role defined in Vault that we want to use when authenticating to
+                                  Vault.
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - authInputType
+                              - authPath
+                              - role
+                            type: object
+                          hashicorpVaultSecret:
+                            description: |-
+                              HashicorpVaultSecret is a SecretSource step that requires a Vault token in
+                              the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                              then fetches the requested secrets from Vault for use in the next step.
+                            properties:
+                              fields:
+                                description: |-
+                                  The fields are Vault keys pointing to the secrets passed to the next
+                                  SecretSource step.
+
+
+                                  Example 1 (TPP, username and password): imagining that you have stored
+                                  the username and password for TPP under the keys "username" and
+                                  "password", you will want to set this field to `["username",
+                                  "password"]`. The username is expected to be given first, the password
+                                  second.
+                                items:
+                                  type: string
+                                type: array
+                              secretPath:
+                                description: |-
+                                  The full HTTP path to the secret in Vault. Example:
+                                  /v1/secret/data/application-team-a/tpp-username-password
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - fields
+                              - secretPath
+                            type: object
+                          secret:
+                            description: |-
+                              Secret is a SecretSource step meant to be the first step. It retrieves secret
+                              values from a Kubernetes Secret, and passes them to the next step.
+                            properties:
+                              fields:
+                                description: |-
+                                  The names of the fields we want to extract from the Kubernetes secret.
+                                  These fields are passed to the next step in the chain.
+                                items:
+                                  type: string
+                                type: array
+                              name:
+                                description: The name of the Kubernetes secret.
+                                type: string
+                            required:
+                              - fields
+                              - name
+                            type: object
+                          serviceAccountToken:
+                            description: |-
+                              ServiceAccountToken is a SecretSource step meant to be the first step. It
+                              uses the Kubernetes TokenRequest API to retrieve a token for a given service
+                              account, and passes it to the next step.
+                            properties:
+                              audiences:
+                                description: |-
+                                  Audiences are the intendend audiences of the token. A recipient of a
+                                  token must identify themself with an identifier in the list of
+                                  audiences of the token, and otherwise should reject the token. A
+                                  token issued for multiple audiences may be used to authenticate
+                                  against any of the audiences listed but implies a high degree of
+                                  trust between the target audiences.
+                                items:
+                                  type: string
+                                type: array
+                              expirationSeconds:
+                                description: |-
+                                  ExpirationSeconds is the requested duration of validity of the request. The
+                                  token issuer may return a token with a different validity duration so a
+                                  client needs to check the 'expiration' field in a response.
+                                format: int64
+                                type: integer
+                              name:
+                                description: The name of the Kubernetes service account.
+                                type: string
+                            required:
+                              - audiences
+                              - name
+                            type: object
+                          tppOAuth:
+                            description: |-
+                              TPPOAuth is a SecretSource step that authenticates to a TPP server. This
+                              step is meant to be the last step and requires a prior step that depends
+                              on the `authInputType`.
+                            properties:
+                              authInputType:
+                                description: |-
+                                  AuthInputType is the authentication method to be used to authenticate
+                                  with TPP. The supported values are "UsernamePassword" and "JWT".
+                                enum:
+                                  - UsernamePassword
+                                  - JWT
+                                type: string
+                              clientId:
+                                default: cert-manager.io
+                                description: ClientID is the clientId used to authenticate with TPP.
+                                type: string
+                              url:
+                                description: |-
+                                  The URL to connect to the Venafi TPP instance. The two URLs
+                                  https://tpp.example.com and https://tpp.example.com/vedsdk are
+                                  equivalent. The ending `/vedsdk` is optional and is stripped out
+                                  by our client.
+                                  If not set, defaults to the URL defined at the top-level of the
+                                  TPP configuration.
+                                type: string
+                            required:
+                              - authInputType
+                            type: object
+                          vcpOAuth:
+                            description: |-
+                              VCPOAuth is a SecretSource step that authenticates to the Venafi Control
+                              Plane. This step is meant to be the last step and requires a prior step
+                              that outputs a JWT token.
+                            properties:
+                              tenantID:
+                                description: TenantID is the tenant ID used to authenticate with VCP.
+                                type: string
+                            type: object
+                        type: object
+                        x-kubernetes-validations:
+                          - message: must have exactly one field set
+                            rule: "(has(self.secret) ? 1 : 0) + (has(self.serviceAccountToken) ? 1 : 0) + (has(self.hashicorpVaultOAuth) ? 1 : 0) + (has(self.hashicorpVaultSecret) ? 1 : 0) + (has(self.hashicorpVaultLDAP) ? 1 : 0) + (has(self.tppOAuth) ? 1 : 0) + (has(self.vcpOAuth) ? 1 : 0) == 1"
+                      maxItems: 50
+                      type: array
+                      x-kubernetes-list-type: atomic
+                    apiKey:
+                      description: |-
+                        The list of steps to retrieve the API key that will be used to connect to
+                        VCP.
+                      items:
+                        properties:
+                          hashicorpVaultLDAP:
+                            description: |-
+                              HashicorpVaultLDAP is a SecretSource step that requires a Vault token in
+                              the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                              then fetches the requested secrets from Vault for use in the next step.
+                            properties:
+                              ldapPath:
+                                description: |-
+                                  The full HTTP path to the secret in Vault. Example:
+                                  /v1/ldap/static-cred/:role_name
+                                  or
+                                  /v1/ldap/creds/:role_name
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - ldapPath
+                            type: object
+                          hashicorpVaultOAuth:
+                            description: |-
+                              HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource
+                              step to provide an OAuth token, which this step uses to authenticate to
+                              Vault. The output of this step is a Vault token. This step allows you to use
+                              the step `HashicorpVaultSecret` afterwards.
+                            properties:
+                              authInputType:
+                                description: |-
+                                  AuthInputType is the authentication method to be used to authenticate
+                                  with HashiCorp Vault. The only supported value is "OIDC".
+                                enum:
+                                  - OIDC
+                                type: string
+                              authPath:
+                                description: |-
+                                  The login URL used for obtaining the Vault token. Example:
+                                  /v1/auth/oidc/login
+                                type: string
+                              clientId:
+                                description: "DEPRECATED: This field does nothing and will be removed in the future."
+                                type: string
+                              role:
+                                description: |-
+                                  The role defined in Vault that we want to use when authenticating to
+                                  Vault.
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - authInputType
+                              - authPath
+                              - role
+                            type: object
+                          hashicorpVaultSecret:
+                            description: |-
+                              HashicorpVaultSecret is a SecretSource step that requires a Vault token in
+                              the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                              then fetches the requested secrets from Vault for use in the next step.
+                            properties:
+                              fields:
+                                description: |-
+                                  The fields are Vault keys pointing to the secrets passed to the next
+                                  SecretSource step.
+
+
+                                  Example 1 (TPP, username and password): imagining that you have stored
+                                  the username and password for TPP under the keys "username" and
+                                  "password", you will want to set this field to `["username",
+                                  "password"]`. The username is expected to be given first, the password
+                                  second.
+                                items:
+                                  type: string
+                                type: array
+                              secretPath:
+                                description: |-
+                                  The full HTTP path to the secret in Vault. Example:
+                                  /v1/secret/data/application-team-a/tpp-username-password
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - fields
+                              - secretPath
+                            type: object
+                          secret:
+                            description: |-
+                              Secret is a SecretSource step meant to be the first step. It retrieves secret
+                              values from a Kubernetes Secret, and passes them to the next step.
+                            properties:
+                              fields:
+                                description: |-
+                                  The names of the fields we want to extract from the Kubernetes secret.
+                                  These fields are passed to the next step in the chain.
+                                items:
+                                  type: string
+                                type: array
+                              name:
+                                description: The name of the Kubernetes secret.
+                                type: string
+                            required:
+                              - fields
+                              - name
+                            type: object
+                          serviceAccountToken:
+                            description: |-
+                              ServiceAccountToken is a SecretSource step meant to be the first step. It
+                              uses the Kubernetes TokenRequest API to retrieve a token for a given service
+                              account, and passes it to the next step.
+                            properties:
+                              audiences:
+                                description: |-
+                                  Audiences are the intendend audiences of the token. A recipient of a
+                                  token must identify themself with an identifier in the list of
+                                  audiences of the token, and otherwise should reject the token. A
+                                  token issued for multiple audiences may be used to authenticate
+                                  against any of the audiences listed but implies a high degree of
+                                  trust between the target audiences.
+                                items:
+                                  type: string
+                                type: array
+                              expirationSeconds:
+                                description: |-
+                                  ExpirationSeconds is the requested duration of validity of the request. The
+                                  token issuer may return a token with a different validity duration so a
+                                  client needs to check the 'expiration' field in a response.
+                                format: int64
+                                type: integer
+                              name:
+                                description: The name of the Kubernetes service account.
+                                type: string
+                            required:
+                              - audiences
+                              - name
+                            type: object
+                          tppOAuth:
+                            description: |-
+                              TPPOAuth is a SecretSource step that authenticates to a TPP server. This
+                              step is meant to be the last step and requires a prior step that depends
+                              on the `authInputType`.
+                            properties:
+                              authInputType:
+                                description: |-
+                                  AuthInputType is the authentication method to be used to authenticate
+                                  with TPP. The supported values are "UsernamePassword" and "JWT".
+                                enum:
+                                  - UsernamePassword
+                                  - JWT
+                                type: string
+                              clientId:
+                                default: cert-manager.io
+                                description: ClientID is the clientId used to authenticate with TPP.
+                                type: string
+                              url:
+                                description: |-
+                                  The URL to connect to the Venafi TPP instance. The two URLs
+                                  https://tpp.example.com and https://tpp.example.com/vedsdk are
+                                  equivalent. The ending `/vedsdk` is optional and is stripped out
+                                  by our client.
+                                  If not set, defaults to the URL defined at the top-level of the
+                                  TPP configuration.
+                                type: string
+                            required:
+                              - authInputType
+                            type: object
+                          vcpOAuth:
+                            description: |-
+                              VCPOAuth is a SecretSource step that authenticates to the Venafi Control
+                              Plane. This step is meant to be the last step and requires a prior step
+                              that outputs a JWT token.
+                            properties:
+                              tenantID:
+                                description: TenantID is the tenant ID used to authenticate with VCP.
+                                type: string
+                            type: object
+                        type: object
+                        x-kubernetes-validations:
+                          - message: must have exactly one field set
+                            rule: "(has(self.secret) ? 1 : 0) + (has(self.serviceAccountToken) ? 1 : 0) + (has(self.hashicorpVaultOAuth) ? 1 : 0) + (has(self.hashicorpVaultSecret) ? 1 : 0) + (has(self.hashicorpVaultLDAP) ? 1 : 0) + (has(self.tppOAuth) ? 1 : 0) + (has(self.vcpOAuth) ? 1 : 0) == 1"
+                      maxItems: 50
+                      type: array
+                      x-kubernetes-list-type: atomic
+                    url:
+                      description: |-
+                        The URL to connect to the Venafi VCP instance. If not set, the default
+                        value https://api.venafi.cloud is used.
+                      type: string
+                  type: object
+                  x-kubernetes-validations:
+                    - message: "must have exactly ONE of the following fields set: apiKey or accessToken"
+                      rule: "(has(self.apiKey) ? 1 : 0) + (has(self.accessToken) ? 1 : 0) == 1"
+                vcp:
+                  properties:
+                    accessToken:
+                      description: |-
+                        The list of steps to retrieve the Access Token that will be used to connect
+                        to VCP.
+                      items:
+                        properties:
+                          hashicorpVaultLDAP:
+                            description: |-
+                              HashicorpVaultLDAP is a SecretSource step that requires a Vault token in
+                              the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                              then fetches the requested secrets from Vault for use in the next step.
+                            properties:
+                              ldapPath:
+                                description: |-
+                                  The full HTTP path to the secret in Vault. Example:
+                                  /v1/ldap/static-cred/:role_name
+                                  or
+                                  /v1/ldap/creds/:role_name
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - ldapPath
+                            type: object
+                          hashicorpVaultOAuth:
+                            description: |-
+                              HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource
+                              step to provide an OAuth token, which this step uses to authenticate to
+                              Vault. The output of this step is a Vault token. This step allows you to use
+                              the step `HashicorpVaultSecret` afterwards.
+                            properties:
+                              authInputType:
+                                description: |-
+                                  AuthInputType is the authentication method to be used to authenticate
+                                  with HashiCorp Vault. The only supported value is "OIDC".
+                                enum:
+                                  - OIDC
+                                type: string
+                              authPath:
+                                description: |-
+                                  The login URL used for obtaining the Vault token. Example:
+                                  /v1/auth/oidc/login
+                                type: string
+                              clientId:
+                                description: "DEPRECATED: This field does nothing and will be removed in the future."
+                                type: string
+                              role:
+                                description: |-
+                                  The role defined in Vault that we want to use when authenticating to
+                                  Vault.
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - authInputType
+                              - authPath
+                              - role
+                            type: object
+                          hashicorpVaultSecret:
+                            description: |-
+                              HashicorpVaultSecret is a SecretSource step that requires a Vault token in
+                              the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                              then fetches the requested secrets from Vault for use in the next step.
+                            properties:
+                              fields:
+                                description: |-
+                                  The fields are Vault keys pointing to the secrets passed to the next
+                                  SecretSource step.
+
+
+                                  Example 1 (TPP, username and password): imagining that you have stored
+                                  the username and password for TPP under the keys "username" and
+                                  "password", you will want to set this field to `["username",
+                                  "password"]`. The username is expected to be given first, the password
+                                  second.
+                                items:
+                                  type: string
+                                type: array
+                              secretPath:
+                                description: |-
+                                  The full HTTP path to the secret in Vault. Example:
+                                  /v1/secret/data/application-team-a/tpp-username-password
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - fields
+                              - secretPath
+                            type: object
+                          secret:
+                            description: |-
+                              Secret is a SecretSource step meant to be the first step. It retrieves secret
+                              values from a Kubernetes Secret, and passes them to the next step.
+                            properties:
+                              fields:
+                                description: |-
+                                  The names of the fields we want to extract from the Kubernetes secret.
+                                  These fields are passed to the next step in the chain.
+                                items:
+                                  type: string
+                                type: array
+                              name:
+                                description: The name of the Kubernetes secret.
+                                type: string
+                            required:
+                              - fields
+                              - name
+                            type: object
+                          serviceAccountToken:
+                            description: |-
+                              ServiceAccountToken is a SecretSource step meant to be the first step. It
+                              uses the Kubernetes TokenRequest API to retrieve a token for a given service
+                              account, and passes it to the next step.
+                            properties:
+                              audiences:
+                                description: |-
+                                  Audiences are the intendend audiences of the token. A recipient of a
+                                  token must identify themself with an identifier in the list of
+                                  audiences of the token, and otherwise should reject the token. A
+                                  token issued for multiple audiences may be used to authenticate
+                                  against any of the audiences listed but implies a high degree of
+                                  trust between the target audiences.
+                                items:
+                                  type: string
+                                type: array
+                              expirationSeconds:
+                                description: |-
+                                  ExpirationSeconds is the requested duration of validity of the request. The
+                                  token issuer may return a token with a different validity duration so a
+                                  client needs to check the 'expiration' field in a response.
+                                format: int64
+                                type: integer
+                              name:
+                                description: The name of the Kubernetes service account.
+                                type: string
+                            required:
+                              - audiences
+                              - name
+                            type: object
+                          tppOAuth:
+                            description: |-
+                              TPPOAuth is a SecretSource step that authenticates to a TPP server. This
+                              step is meant to be the last step and requires a prior step that depends
+                              on the `authInputType`.
+                            properties:
+                              authInputType:
+                                description: |-
+                                  AuthInputType is the authentication method to be used to authenticate
+                                  with TPP. The supported values are "UsernamePassword" and "JWT".
+                                enum:
+                                  - UsernamePassword
+                                  - JWT
+                                type: string
+                              clientId:
+                                default: cert-manager.io
+                                description: ClientID is the clientId used to authenticate with TPP.
+                                type: string
+                              url:
+                                description: |-
+                                  The URL to connect to the Venafi TPP instance. The two URLs
+                                  https://tpp.example.com and https://tpp.example.com/vedsdk are
+                                  equivalent. The ending `/vedsdk` is optional and is stripped out
+                                  by our client.
+                                  If not set, defaults to the URL defined at the top-level of the
+                                  TPP configuration.
+                                type: string
+                            required:
+                              - authInputType
+                            type: object
+                          vcpOAuth:
+                            description: |-
+                              VCPOAuth is a SecretSource step that authenticates to the Venafi Control
+                              Plane. This step is meant to be the last step and requires a prior step
+                              that outputs a JWT token.
+                            properties:
+                              tenantID:
+                                description: TenantID is the tenant ID used to authenticate with VCP.
+                                type: string
+                            type: object
+                        type: object
+                        x-kubernetes-validations:
+                          - message: must have exactly one field set
+                            rule: "(has(self.secret) ? 1 : 0) + (has(self.serviceAccountToken) ? 1 : 0) + (has(self.hashicorpVaultOAuth) ? 1 : 0) + (has(self.hashicorpVaultSecret) ? 1 : 0) + (has(self.hashicorpVaultLDAP) ? 1 : 0) + (has(self.tppOAuth) ? 1 : 0) + (has(self.vcpOAuth) ? 1 : 0) == 1"
+                      maxItems: 50
+                      type: array
+                      x-kubernetes-list-type: atomic
+                    apiKey:
+                      description: |-
+                        The list of steps to retrieve the API key that will be used to connect to
+                        VCP.
+                      items:
+                        properties:
+                          hashicorpVaultLDAP:
+                            description: |-
+                              HashicorpVaultLDAP is a SecretSource step that requires a Vault token in
+                              the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                              then fetches the requested secrets from Vault for use in the next step.
+                            properties:
+                              ldapPath:
+                                description: |-
+                                  The full HTTP path to the secret in Vault. Example:
+                                  /v1/ldap/static-cred/:role_name
+                                  or
+                                  /v1/ldap/creds/:role_name
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - ldapPath
+                            type: object
+                          hashicorpVaultOAuth:
+                            description: |-
+                              HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource
+                              step to provide an OAuth token, which this step uses to authenticate to
+                              Vault. The output of this step is a Vault token. This step allows you to use
+                              the step `HashicorpVaultSecret` afterwards.
+                            properties:
+                              authInputType:
+                                description: |-
+                                  AuthInputType is the authentication method to be used to authenticate
+                                  with HashiCorp Vault. The only supported value is "OIDC".
+                                enum:
+                                  - OIDC
+                                type: string
+                              authPath:
+                                description: |-
+                                  The login URL used for obtaining the Vault token. Example:
+                                  /v1/auth/oidc/login
+                                type: string
+                              clientId:
+                                description: "DEPRECATED: This field does nothing and will be removed in the future."
+                                type: string
+                              role:
+                                description: |-
+                                  The role defined in Vault that we want to use when authenticating to
+                                  Vault.
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - authInputType
+                              - authPath
+                              - role
+                            type: object
+                          hashicorpVaultSecret:
+                            description: |-
+                              HashicorpVaultSecret is a SecretSource step that requires a Vault token in
+                              the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                              then fetches the requested secrets from Vault for use in the next step.
+                            properties:
+                              fields:
+                                description: |-
+                                  The fields are Vault keys pointing to the secrets passed to the next
+                                  SecretSource step.
+
+
+                                  Example 1 (TPP, username and password): imagining that you have stored
+                                  the username and password for TPP under the keys "username" and
+                                  "password", you will want to set this field to `["username",
+                                  "password"]`. The username is expected to be given first, the password
+                                  second.
+                                items:
+                                  type: string
+                                type: array
+                              secretPath:
+                                description: |-
+                                  The full HTTP path to the secret in Vault. Example:
+                                  /v1/secret/data/application-team-a/tpp-username-password
+                                type: string
+                              url:
+                                description: The URL to connect to your HashiCorp Vault instance.
+                                type: string
+                            required:
+                              - fields
+                              - secretPath
+                            type: object
+                          secret:
+                            description: |-
+                              Secret is a SecretSource step meant to be the first step. It retrieves secret
+                              values from a Kubernetes Secret, and passes them to the next step.
+                            properties:
+                              fields:
+                                description: |-
+                                  The names of the fields we want to extract from the Kubernetes secret.
+                                  These fields are passed to the next step in the chain.
+                                items:
+                                  type: string
+                                type: array
+                              name:
+                                description: The name of the Kubernetes secret.
+                                type: string
+                            required:
+                              - fields
+                              - name
+                            type: object
+                          serviceAccountToken:
+                            description: |-
+                              ServiceAccountToken is a SecretSource step meant to be the first step. It
+                              uses the Kubernetes TokenRequest API to retrieve a token for a given service
+                              account, and passes it to the next step.
+                            properties:
+                              audiences:
+                                description: |-
+                                  Audiences are the intendend audiences of the token. A recipient of a
+                                  token must identify themself with an identifier in the list of
+                                  audiences of the token, and otherwise should reject the token. A
+                                  token issued for multiple audiences may be used to authenticate
+                                  against any of the audiences listed but implies a high degree of
+                                  trust between the target audiences.
+                                items:
+                                  type: string
+                                type: array
+                              expirationSeconds:
+                                description: |-
+                                  ExpirationSeconds is the requested duration of validity of the request. The
+                                  token issuer may return a token with a different validity duration so a
+                                  client needs to check the 'expiration' field in a response.
+                                format: int64
+                                type: integer
+                              name:
+                                description: The name of the Kubernetes service account.
+                                type: string
+                            required:
+                              - audiences
+                              - name
+                            type: object
+                          tppOAuth:
+                            description: |-
+                              TPPOAuth is a SecretSource step that authenticates to a TPP server. This
+                              step is meant to be the last step and requires a prior step that depends
+                              on the `authInputType`.
+                            properties:
+                              authInputType:
+                                description: |-
+                                  AuthInputType is the authentication method to be used to authenticate
+                                  with TPP. The supported values are "UsernamePassword" and "JWT".
+                                enum:
+                                  - UsernamePassword
+                                  - JWT
+                                type: string
+                              clientId:
+                                default: cert-manager.io
+                                description: ClientID is the clientId used to authenticate with TPP.
+                                type: string
+                              url:
+                                description: |-
+                                  The URL to connect to the Venafi TPP instance. The two URLs
+                                  https://tpp.example.com and https://tpp.example.com/vedsdk are
+                                  equivalent. The ending `/vedsdk` is optional and is stripped out
+                                  by our client.
+                                  If not set, defaults to the URL defined at the top-level of the
+                                  TPP configuration.
+                                type: string
+                            required:
+                              - authInputType
+                            type: object
+                          vcpOAuth:
+                            description: |-
+                              VCPOAuth is a SecretSource step that authenticates to the Venafi Control
+                              Plane. This step is meant to be the last step and requires a prior step
+                              that outputs a JWT token.
+                            properties:
+                              tenantID:
+                                description: TenantID is the tenant ID used to authenticate with VCP.
+                                type: string
+                            type: object
+                        type: object
+                        x-kubernetes-validations:
+                          - message: must have exactly one field set
+                            rule: "(has(self.secret) ? 1 : 0) + (has(self.serviceAccountToken) ? 1 : 0) + (has(self.hashicorpVaultOAuth) ? 1 : 0) + (has(self.hashicorpVaultSecret) ? 1 : 0) + (has(self.hashicorpVaultLDAP) ? 1 : 0) + (has(self.tppOAuth) ? 1 : 0) + (has(self.vcpOAuth) ? 1 : 0) == 1"
+                      maxItems: 50
+                      type: array
+                      x-kubernetes-list-type: atomic
+                    url:
+                      description: |-
+                        The URL to connect to the Venafi VCP instance. If not set, the default
+                        value https://api.venafi.cloud is used.
+                      type: string
+                  type: object
+                  x-kubernetes-validations:
+                    - message: "must have exactly ONE of the following fields set: apiKey or accessToken"
+                      rule: "(has(self.apiKey) ? 1 : 0) + (has(self.accessToken) ? 1 : 0) == 1"
+              type: object
+              x-kubernetes-validations:
+                - message: "must have exactly ONE of the following fields set: tpp or vcp"
+                  rule: "(has(self.tpp) ? 1 : 0) + (has(self.vaas) ? 1 : 0) + (has(self.vcp) ? 1 : 0) == 1"
+            status:
+              properties:
+                conditions:
+                  description: List of status conditions to indicate the status of a VenafiConnection.
+                  items:
+                    description: ConnectionCondition contains condition information for a VenafiConnection.
+                    properties:
+                      lastTransitionTime:
+                        description: |-
+                          LastTransitionTime is the timestamp corresponding to the last status
+                          change of this condition.
+                        format: date-time
+                        type: string
+                      lastUpdateTime:
+                        description: lastUpdateTime is the time of the last update to this condition
+                        format: date-time
+                        type: string
+                      message:
+                        description: |-
+                          Message is a human readable description of the details of the last
+                          transition, complementing reason.
+                        type: string
+                      observedGeneration:
+                        description: |-
+                          If set, this represents the .metadata.generation that the condition was
+                          set based upon.
+                          For instance, if .metadata.generation is currently 12, but the
+                          .status.condition[x].observedGeneration is 9, the condition is out of date
+                          with respect to the current state of the Issuer.
+                        format: int64
+                        type: integer
+                      reason:
+                        description: |-
+                          Reason is a brief machine readable explanation for the condition's last
+                          transition.
+                        type: string
+                      status:
+                        description: Status of the condition, one of (`True`, `False`, `Unknown`).
+                        type: string
+                      tokenValidUntil:
+                        description: |-
+                          The ValidUntil time of the token used to authenticate with the Venafi
+                          Control Plane server.
+                        format: date-time
+                        type: string
+                      type:
+                        description: |-
+                          Type of the condition, should be a combination of the unique name of the
+                          operator and the type of condition.
+                          eg. `VenafiEnhancedIssuerReady`
+                        type: string
+                    required:
+                      - status
+                      - type
+                    type: object
+                  type: array
+                  x-kubernetes-list-map-keys:
+                    - type
+                  x-kubernetes-list-type: map
+              type: object
+          type: object
+      served: true
+      storage: true
+      subresources:
+        status: {}
diff --git a/make/util/checkhash.sh b/make/util/checkhash.sh
new file mode 100755
index 00000000..3f1907b8
--- /dev/null
+++ b/make/util/checkhash.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+
+set -eu -o pipefail
+
+# This script takes the hash of its first argument and verifies it against the
+# hex hash given in its second argument
+
+SHASUM=$(./make/util/hash.sh "$1")
+
+# When running 'make learn-sha-tools', we don't want this script to fail.
+# Instead we log what sha values are wrong, so the make.mk file can be updated.
+if [ "$SHASUM" != "$2" ] && [ "${LEARN_FILE:-}" != "" ]; then
+	echo "s/$2/$SHASUM/g" >> "${LEARN_FILE:-}"
+	exit 0
+fi
+
+if [ "$SHASUM" != "$2"  ]; then
+	echo "invalid checksum for \"$1\": wanted \"$2\" but got \"$SHASUM\""
+	exit 1
+fi
diff --git a/make/util/hash.sh b/make/util/hash.sh
new file mode 100755
index 00000000..50364102
--- /dev/null
+++ b/make/util/hash.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+set -eu -o pipefail
+
+# This script is a wrapper for outputting purely the sha256 hash of the input file,
+# ideally in a portable way.
+
+sha256sum "$1" | cut -d" " -f1
diff --git a/pkg/client/client_venconn_test.go b/pkg/client/client_venconn_test.go
index 8fca50a7..3f03065a 100644
--- a/pkg/client/client_venconn_test.go
+++ b/pkg/client/client_venconn_test.go
@@ -268,7 +268,7 @@ func fakeTPP(t testing.TB) (*httptest.Server, *x509.Certificate) {
 func startEnvtest(t testing.TB) (_ *envtest.Environment, _ *rest.Config, kclient ctrlruntime.WithWatch) {
 	envtest := &envtest.Environment{
 		ErrorIfCRDPathMissing: true,
-		CRDDirectoryPaths:     []string{"/tmp/venafi-connection.yaml"},
+		CRDDirectoryPaths:     []string{"../../deploy/charts/venafi-kubernetes-agent/crd_bases/jetstack.io_venaficonnections.yaml"},
 	}
 	restconf, err := envtest.Start()
 	require.NoError(t, err)

From aa41af27c878f2cd3467e00773ab70845b0b1efb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20Valais?= <mael@vls.dev>
Date: Tue, 13 Aug 2024 20:47:00 +0200
Subject: [PATCH 09/16] github actions, go.mod: use Go 1.22 instead of the
 unsupported 1.19

---
 .github/workflows/release-master.yml | 4 ++--
 .github/workflows/test.yml           | 4 ++--
 go.mod                               | 2 --
 3 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/.github/workflows/release-master.yml b/.github/workflows/release-master.yml
index 3bdb3840..43e96029 100644
--- a/.github/workflows/release-master.yml
+++ b/.github/workflows/release-master.yml
@@ -11,7 +11,7 @@ jobs:
   vet:
     name: vet
     runs-on: ubuntu-22.04
-    container: golang:1.19
+    container: golang:1.22
     steps:
     - uses: actions/checkout@v4
     - run: make vet
@@ -19,7 +19,7 @@ jobs:
   test:
     name: go test
     runs-on: ubuntu-22.04
-    container: golang:1.19
+    container: golang:1.22
     steps:
     - uses: actions/checkout@v4
     - run: make test
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 354abfec..2d1c62f9 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -13,7 +13,7 @@ jobs:
   vet:
     name: vet
     runs-on: ubuntu-22.04
-    container: golang:1.19
+    container: golang:1.22
     steps:
     - uses: actions/checkout@v4
     - run: make vet
@@ -21,7 +21,7 @@ jobs:
   test:
     name: go test
     runs-on: ubuntu-22.04
-    container: golang:1.19
+    container: golang:1.22
     steps:
     - uses: actions/checkout@v4
     - run: make test
diff --git a/go.mod b/go.mod
index 274747ba..1504559f 100644
--- a/go.mod
+++ b/go.mod
@@ -2,8 +2,6 @@ module github.com/jetstack/preflight
 
 go 1.22.0
 
-toolchain go1.22.6
-
 require (
 	github.com/Jeffail/gabs/v2 v2.7.0
 	github.com/cenkalti/backoff v2.2.1+incompatible

From d9939b793f025c222e7e9709be6ed08f0e7307b4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20Valais?= <mael@vls.dev>
Date: Mon, 19 Aug 2024 17:42:56 +0200
Subject: [PATCH 10/16] github actions: configure
 DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB

---
 .github/workflows/release-master.yml | 57 ++++++++++++++++++++++++++
 .github/workflows/test.yml           | 60 ++++++++++++++++++++++++++++
 builder.dockerfile                   |  4 +-
 3 files changed, 120 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/release-master.yml b/.github/workflows/release-master.yml
index 43e96029..86718dfc 100644
--- a/.github/workflows/release-master.yml
+++ b/.github/workflows/release-master.yml
@@ -13,6 +13,25 @@ jobs:
     runs-on: ubuntu-22.04
     container: golang:1.22
     steps:
+    - name: Configure jetstack/venafi-connection-lib repo pull access
+      run: |
+        mkdir ~/.ssh
+        chmod 700 ~/.ssh
+
+        echo "${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}" > ~/.ssh/venafi_connection_lib_id
+        chmod 600 ~/.ssh/venafi_connection_lib_id
+
+        cat <<EOT >> ~/.ssh/config
+        Host venafi-connection-lib.github.com
+        HostName github.com
+        IdentityFile ~/.ssh/venafi_connection_lib_id
+        IdentitiesOnly yes
+        EOT
+
+        cat <<EOT >> ~/.gitconfig
+        [url "git@venafi-connection-lib.github.com:jetstack/venafi-connection-lib"]
+          insteadOf = https://github.com/jetstack/venafi-connection-lib
+        EOT
     - uses: actions/checkout@v4
     - run: make vet
       shell: bash
@@ -21,6 +40,25 @@ jobs:
     runs-on: ubuntu-22.04
     container: golang:1.22
     steps:
+    - name: Configure jetstack/venafi-connection-lib repo pull access
+      run: |
+        mkdir ~/.ssh
+        chmod 700 ~/.ssh
+
+        echo "${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}" > ~/.ssh/venafi_connection_lib_id
+        chmod 600 ~/.ssh/venafi_connection_lib_id
+
+        cat <<EOT >> ~/.ssh/config
+        Host venafi-connection-lib.github.com
+        HostName github.com
+        IdentityFile ~/.ssh/venafi_connection_lib_id
+        IdentitiesOnly yes
+        EOT
+
+        cat <<EOT >> ~/.gitconfig
+        [url "git@venafi-connection-lib.github.com:jetstack/venafi-connection-lib"]
+          insteadOf = https://github.com/jetstack/venafi-connection-lib
+        EOT
     - uses: actions/checkout@v4
     - run: make test
   docker_build:
@@ -41,6 +79,25 @@ jobs:
       packages: write
       id-token: write
     steps:
+    - name: Configure jetstack/venafi-connection-lib repo pull access
+      run: |
+        mkdir ~/.ssh
+        chmod 700 ~/.ssh
+
+        echo "${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}" > ~/.ssh/venafi_connection_lib_id
+        chmod 600 ~/.ssh/venafi_connection_lib_id
+
+        cat <<EOT >> ~/.ssh/config
+        Host venafi-connection-lib.github.com
+        HostName github.com
+        IdentityFile ~/.ssh/venafi_connection_lib_id
+        IdentitiesOnly yes
+        EOT
+
+        cat <<EOT >> ~/.gitconfig
+        [url "git@venafi-connection-lib.github.com:jetstack/venafi-connection-lib"]
+          insteadOf = https://github.com/jetstack/venafi-connection-lib
+        EOT
     - name: Install Tools
       # Installing 'bash' because it's required by the 'cosign-installer' action
       # and 'coreutils' because the 'slsa-provenance-action' requires a version
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 2d1c62f9..0fe98dca 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -9,12 +9,34 @@ on:
     branches:
       - "*"
 
+env:
+  GOPRIVATE: github.com/jetstack/venafi-connection-lib
+
 jobs:
   vet:
     name: vet
     runs-on: ubuntu-22.04
     container: golang:1.22
     steps:
+    - name: Configure jetstack/venafi-connection-lib repo pull access
+      run: |
+        mkdir ~/.ssh
+        chmod 700 ~/.ssh
+
+        echo "${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}" > ~/.ssh/venafi_connection_lib_id
+        chmod 600 ~/.ssh/venafi_connection_lib_id
+
+        cat <<EOT >> ~/.ssh/config
+        Host venafi-connection-lib.github.com
+        HostName github.com
+        IdentityFile ~/.ssh/venafi_connection_lib_id
+        IdentitiesOnly yes
+        EOT
+
+        cat <<EOT >> ~/.gitconfig
+        [url "git@venafi-connection-lib.github.com:jetstack/venafi-connection-lib"]
+          insteadOf = https://github.com/jetstack/venafi-connection-lib
+        EOT
     - uses: actions/checkout@v4
     - run: make vet
       shell: bash
@@ -23,6 +45,25 @@ jobs:
     runs-on: ubuntu-22.04
     container: golang:1.22
     steps:
+    - name: Configure jetstack/venafi-connection-lib repo pull access
+      run: |
+        mkdir ~/.ssh
+        chmod 700 ~/.ssh
+
+        echo "${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}" > ~/.ssh/venafi_connection_lib_id
+        chmod 600 ~/.ssh/venafi_connection_lib_id
+
+        cat <<EOT >> ~/.ssh/config
+        Host venafi-connection-lib.github.com
+        HostName github.com
+        IdentityFile ~/.ssh/venafi_connection_lib_id
+        IdentitiesOnly yes
+        EOT
+
+        cat <<EOT >> ~/.gitconfig
+        [url "git@venafi-connection-lib.github.com:jetstack/venafi-connection-lib"]
+          insteadOf = https://github.com/jetstack/venafi-connection-lib
+        EOT
     - uses: actions/checkout@v4
     - run: make test
   docker_build:
@@ -39,6 +80,25 @@ jobs:
           DOCKER_DRIVER: overlay
           DOCKER_HOST: tcp://localhost:2375
     steps:
+    - name: Configure jetstack/venafi-connection-lib repo pull access
+      run: |
+        mkdir ~/.ssh
+        chmod 700 ~/.ssh
+
+        echo "${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}" > ~/.ssh/venafi_connection_lib_id
+        chmod 600 ~/.ssh/venafi_connection_lib_id
+
+        cat <<EOT >> ~/.ssh/config
+        Host venafi-connection-lib.github.com
+        HostName github.com
+        IdentityFile ~/.ssh/venafi_connection_lib_id
+        IdentitiesOnly yes
+        EOT
+
+        cat <<EOT >> ~/.gitconfig
+        [url "git@venafi-connection-lib.github.com:jetstack/venafi-connection-lib"]
+          insteadOf = https://github.com/jetstack/venafi-connection-lib
+        EOT
     - name: Install Tools
       run: apk add --update make git jq rsync curl
     - name: Adding github workspace as safe directory
diff --git a/builder.dockerfile b/builder.dockerfile
index 32c6ed70..4496a10d 100644
--- a/builder.dockerfile
+++ b/builder.dockerfile
@@ -7,6 +7,9 @@ WORKDIR /go/github.com/jetstack/preflight
 COPY ./go.mod .
 COPY ./go.sum .
 
+# Due to https://github.com/jetstack/venafi-connection-lib being private.
+COPY ~/.ssh/ ~/.gitconfig /root/
+
 RUN go mod download
 
 ## Bring in everything else
@@ -22,7 +25,6 @@ RUN make build-all-platforms \
     OAUTH_CLIENT_SECRET=${oauth_client_secret} \
     OAUTH_AUTH_SERVER_DOMAIN=${oauth_auth_server_domain}
 
-
 RUN go install github.com/google/go-licenses@v1.6.0
 
 # We need this '|| true' because go-licenses could fail to find a license so

From 4d4dbc9ba0e53baa4124bc25146338883a022f60 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20Valais?= <mael@vls.dev>
Date: Mon, 19 Aug 2024 21:10:15 +0200
Subject: [PATCH 11/16] venconn: builder.dockerfile: go mod download needs
 access to venafi-connection-lib

---
 .github/workflows/release-master.yml | 90 +++++++++++---------------
 .github/workflows/test.yml           | 96 ++++++++++++----------------
 Makefile                             |  6 +-
 builder.dockerfile                   | 23 ++++---
 4 files changed, 98 insertions(+), 117 deletions(-)

diff --git a/.github/workflows/release-master.yml b/.github/workflows/release-master.yml
index 86718dfc..a205be16 100644
--- a/.github/workflows/release-master.yml
+++ b/.github/workflows/release-master.yml
@@ -13,25 +13,19 @@ jobs:
     runs-on: ubuntu-22.04
     container: golang:1.22
     steps:
-    - name: Configure jetstack/venafi-connection-lib repo pull access
+    - run: git config --global url.git@github.com:jetstack/venafi-connection-lib.insteadOf https://github.com/jetstack/venafi-connection-lib
+    - name: "Add GitHub to the SSH known hosts file"
       run: |
-        mkdir ~/.ssh
-        chmod 700 ~/.ssh
-
-        echo "${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}" > ~/.ssh/venafi_connection_lib_id
-        chmod 600 ~/.ssh/venafi_connection_lib_id
-
-        cat <<EOT >> ~/.ssh/config
-        Host venafi-connection-lib.github.com
-        HostName github.com
-        IdentityFile ~/.ssh/venafi_connection_lib_id
-        IdentitiesOnly yes
-        EOT
-
-        cat <<EOT >> ~/.gitconfig
-        [url "git@venafi-connection-lib.github.com:jetstack/venafi-connection-lib"]
-          insteadOf = https://github.com/jetstack/venafi-connection-lib
-        EOT
+        mkdir -p -m 0700 ~/.ssh
+        cat <<EOF >~/.ssh/known_hosts
+        github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
+        github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
+        github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
+        EOF
+        chmod 600 ~/.ssh/known_hosts
+    - uses: webfactory/ssh-agent@v0.9.0
+      with:
+        ssh-private-key: ${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}
     - uses: actions/checkout@v4
     - run: make vet
       shell: bash
@@ -40,25 +34,19 @@ jobs:
     runs-on: ubuntu-22.04
     container: golang:1.22
     steps:
-    - name: Configure jetstack/venafi-connection-lib repo pull access
+    - run: git config --global url.git@github.com:jetstack/venafi-connection-lib.insteadOf https://github.com/jetstack/venafi-connection-lib
+    - name: "Add GitHub to the SSH known hosts file"
       run: |
-        mkdir ~/.ssh
-        chmod 700 ~/.ssh
-
-        echo "${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}" > ~/.ssh/venafi_connection_lib_id
-        chmod 600 ~/.ssh/venafi_connection_lib_id
-
-        cat <<EOT >> ~/.ssh/config
-        Host venafi-connection-lib.github.com
-        HostName github.com
-        IdentityFile ~/.ssh/venafi_connection_lib_id
-        IdentitiesOnly yes
-        EOT
-
-        cat <<EOT >> ~/.gitconfig
-        [url "git@venafi-connection-lib.github.com:jetstack/venafi-connection-lib"]
-          insteadOf = https://github.com/jetstack/venafi-connection-lib
-        EOT
+        mkdir -p -m 0700 ~/.ssh
+        cat <<EOF >~/.ssh/known_hosts
+        github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
+        github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
+        github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
+        EOF
+        chmod 600 ~/.ssh/known_hosts
+    - uses: webfactory/ssh-agent@v0.9.0
+      with:
+        ssh-private-key: ${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}
     - uses: actions/checkout@v4
     - run: make test
   docker_build:
@@ -79,25 +67,19 @@ jobs:
       packages: write
       id-token: write
     steps:
-    - name: Configure jetstack/venafi-connection-lib repo pull access
+    - run: git config --global url.git@github.com:jetstack/venafi-connection-lib.insteadOf https://github.com/jetstack/venafi-connection-lib
+    - name: "Add GitHub to the SSH known hosts file"
       run: |
-        mkdir ~/.ssh
-        chmod 700 ~/.ssh
-
-        echo "${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}" > ~/.ssh/venafi_connection_lib_id
-        chmod 600 ~/.ssh/venafi_connection_lib_id
-
-        cat <<EOT >> ~/.ssh/config
-        Host venafi-connection-lib.github.com
-        HostName github.com
-        IdentityFile ~/.ssh/venafi_connection_lib_id
-        IdentitiesOnly yes
-        EOT
-
-        cat <<EOT >> ~/.gitconfig
-        [url "git@venafi-connection-lib.github.com:jetstack/venafi-connection-lib"]
-          insteadOf = https://github.com/jetstack/venafi-connection-lib
-        EOT
+        mkdir -p -m 0700 ~/.ssh
+        cat <<EOF >~/.ssh/known_hosts
+        github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
+        github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
+        github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
+        EOF
+        chmod 600 ~/.ssh/known_hosts
+    - uses: webfactory/ssh-agent@v0.9.0
+      with:
+        ssh-private-key: ${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}
     - name: Install Tools
       # Installing 'bash' because it's required by the 'cosign-installer' action
       # and 'coreutils' because the 'slsa-provenance-action' requires a version
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 0fe98dca..8d73df62 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -18,25 +18,24 @@ jobs:
     runs-on: ubuntu-22.04
     container: golang:1.22
     steps:
-    - name: Configure jetstack/venafi-connection-lib repo pull access
+    - run: git config --global url.git@github.com:jetstack/venafi-connection-lib.insteadOf https://github.com/jetstack/venafi-connection-lib
+    # The only reason we need to configure ~/.ssh/known_hosts is because we are
+    # using a container-based runner. Non-container runners already have the
+    # github.com fingerprints in their known_hosts file. We could use `curl
+    # --silent https://api.github.com/meta` to fetch it but golang:1.22 does not
+    # have jq installed.
+    - name: "Add GitHub to the SSH known hosts file"
       run: |
-        mkdir ~/.ssh
-        chmod 700 ~/.ssh
-
-        echo "${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}" > ~/.ssh/venafi_connection_lib_id
-        chmod 600 ~/.ssh/venafi_connection_lib_id
-
-        cat <<EOT >> ~/.ssh/config
-        Host venafi-connection-lib.github.com
-        HostName github.com
-        IdentityFile ~/.ssh/venafi_connection_lib_id
-        IdentitiesOnly yes
-        EOT
-
-        cat <<EOT >> ~/.gitconfig
-        [url "git@venafi-connection-lib.github.com:jetstack/venafi-connection-lib"]
-          insteadOf = https://github.com/jetstack/venafi-connection-lib
-        EOT
+        mkdir -p -m 0700 ~/.ssh
+        cat <<EOF >~/.ssh/known_hosts
+        github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
+        github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
+        github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
+        EOF
+        chmod 600 ~/.ssh/known_hosts
+    - uses: webfactory/ssh-agent@v0.9.0
+      with:
+        ssh-private-key: ${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}
     - uses: actions/checkout@v4
     - run: make vet
       shell: bash
@@ -45,25 +44,20 @@ jobs:
     runs-on: ubuntu-22.04
     container: golang:1.22
     steps:
-    - name: Configure jetstack/venafi-connection-lib repo pull access
+    - run: git config --global url.git@github.com:jetstack/venafi-connection-lib.insteadOf https://github.com/jetstack/venafi-connection-lib
+    - name: "Add GitHub to the SSH known hosts file"
       run: |
-        mkdir ~/.ssh
-        chmod 700 ~/.ssh
-
-        echo "${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}" > ~/.ssh/venafi_connection_lib_id
-        chmod 600 ~/.ssh/venafi_connection_lib_id
-
-        cat <<EOT >> ~/.ssh/config
-        Host venafi-connection-lib.github.com
-        HostName github.com
-        IdentityFile ~/.ssh/venafi_connection_lib_id
-        IdentitiesOnly yes
-        EOT
-
-        cat <<EOT >> ~/.gitconfig
-        [url "git@venafi-connection-lib.github.com:jetstack/venafi-connection-lib"]
-          insteadOf = https://github.com/jetstack/venafi-connection-lib
-        EOT
+        mkdir -p -m 0700 ~/.ssh
+        cat <<EOF >~/.ssh/known_hosts
+        github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
+        github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
+        github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
+        EOF
+        chmod 600 ~/.ssh/known_hosts
+    - run: curl --silent https://api.github.com/meta | jq --raw-output '"github.com "+.ssh_keys[]' >> ~/.ssh/known_hosts
+    - uses: webfactory/ssh-agent@v0.9.0
+      with:
+        ssh-private-key: ${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}
     - uses: actions/checkout@v4
     - run: make test
   docker_build:
@@ -80,25 +74,19 @@ jobs:
           DOCKER_DRIVER: overlay
           DOCKER_HOST: tcp://localhost:2375
     steps:
-    - name: Configure jetstack/venafi-connection-lib repo pull access
+    - run: git config --global url.git@github.com:jetstack/venafi-connection-lib.insteadOf https://github.com/jetstack/venafi-connection-lib
+    - name: "Add GitHub to the SSH known hosts file"
       run: |
-        mkdir ~/.ssh
-        chmod 700 ~/.ssh
-
-        echo "${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}" > ~/.ssh/venafi_connection_lib_id
-        chmod 600 ~/.ssh/venafi_connection_lib_id
-
-        cat <<EOT >> ~/.ssh/config
-        Host venafi-connection-lib.github.com
-        HostName github.com
-        IdentityFile ~/.ssh/venafi_connection_lib_id
-        IdentitiesOnly yes
-        EOT
-
-        cat <<EOT >> ~/.gitconfig
-        [url "git@venafi-connection-lib.github.com:jetstack/venafi-connection-lib"]
-          insteadOf = https://github.com/jetstack/venafi-connection-lib
-        EOT
+        mkdir -p -m 0700 ~/.ssh
+        cat <<EOF >~/.ssh/known_hosts
+        github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
+        github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
+        github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
+        EOF
+        chmod 600 ~/.ssh/known_hosts
+    - uses: webfactory/ssh-agent@v0.9.0
+      with:
+        ssh-private-key: ${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}
     - name: Install Tools
       run: apk add --update make git jq rsync curl
     - name: Adding github workspace as safe directory
diff --git a/Makefile b/Makefile
index 0076d25a..0f48df1f 100644
--- a/Makefile
+++ b/Makefile
@@ -7,6 +7,8 @@ GOVERSION:=$(shell go version | awk '{print $$3 " " $$4}')
 GOOS:=$(shell go env GOOS)
 GOARCH:=$(shell go env GOARCH)
 
+export GOPRIVATE=github.com/jetstack/venafi-connection-lib
+
 BIN_NAME:=preflight
 
 DOCKER_IMAGE?=quay.io/jetstack/preflight
@@ -75,11 +77,13 @@ build-all-platforms-in-host:
 
 build-all-platforms-in-docker:
 	rm -rf ./builds
-	docker build --rm -t preflight-bin -f ./builder.dockerfile \
+	docker buildx build --load --rm -t preflight-bin -f ./builder.dockerfile \
 		--build-arg oauth_client_id=$(OAUTH_CLIENT_ID) \
 		--build-arg oauth_client_secret=$(OAUTH_CLIENT_SECRET) \
 		--build-arg oauth_auth_server_domain=$(OAUTH_AUTH_SERVER_DOMAIN) \
+		--ssh default \
 		.
+	docker rm -f preflight-bin-container 2>/dev/null || true
 	docker create --rm --name=preflight-bin-container preflight-bin
 	docker cp preflight-bin-container:/go/github.com/jetstack/preflight/builds ./builds
 	docker rm preflight-bin-container
diff --git a/builder.dockerfile b/builder.dockerfile
index 4496a10d..f69dd000 100644
--- a/builder.dockerfile
+++ b/builder.dockerfile
@@ -1,16 +1,23 @@
-FROM golang:1.21.6 as builder
+FROM golang:1.22.5 as builder
 
 WORKDIR /go/github.com/jetstack/preflight
 
 # Run a dependency resolve with just the go mod files present for
 # better caching
-COPY ./go.mod .
-COPY ./go.sum .
-
-# Due to https://github.com/jetstack/venafi-connection-lib being private.
-COPY ~/.ssh/ ~/.gitconfig /root/
-
-RUN go mod download
+COPY go.mod go.sum .
+
+COPY <<EOF /root/.gitconfig
+[url "git@github.com:jetstack/venafi-connection-lib"] \
+insteadOf = https://github.com/jetstack/venafi-connection-lib
+EOF
+COPY <<EOF /root/.ssh/known_hosts
+github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
+github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
+github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
+EOF
+ENV GOPRIVATE=github.com/jetstack/venafi-connection-lib
+
+RUN --mount=type=ssh go mod download
 
 ## Bring in everything else
 COPY . .

From 5190b5987d8c1c90ed15287c482c0dd7d1a46e75 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20Valais?= <mael@vls.dev>
Date: Tue, 20 Aug 2024 16:36:52 +0200
Subject: [PATCH 12/16] misc: fix log line missed in #558

---
 pkg/agent/run.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/pkg/agent/run.go b/pkg/agent/run.go
index 3bea4307..bf32868a 100644
--- a/pkg/agent/run.go
+++ b/pkg/agent/run.go
@@ -7,6 +7,7 @@ import (
 	"fmt"
 	"io"
 	"io/ioutil"
+	"log"
 	"net/http"
 	_ "net/http/pprof"
 	"net/url"
@@ -137,7 +138,7 @@ func Run(cmd *cobra.Command, args []string) {
 		go func() {
 			err := preflightClient.(manager.Runnable).Start(ctx)
 			if err != nil {
-				log.Fatalf("failed to start a controller-runtime component: %v", err)
+				logs.Log.Fatalf("failed to start a controller-runtime component: %v", err)
 			}
 
 			// The agent must stop if the controller-runtime component stops.

From 10126b494a868daef26a131889ab3b60ef2f6bbf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20Valais?= <mael@vls.dev>
Date: Tue, 20 Aug 2024 14:18:46 +0200
Subject: [PATCH 13/16] github actions: use the ssh-agent action to handle the
 deploy key

---
 .github/workflows/release-master.yml | 29 ++++++++------------
 .github/workflows/test.yml           | 41 +++++++++++++---------------
 2 files changed, 30 insertions(+), 40 deletions(-)

diff --git a/.github/workflows/release-master.yml b/.github/workflows/release-master.yml
index a205be16..a4551a8d 100644
--- a/.github/workflows/release-master.yml
+++ b/.github/workflows/release-master.yml
@@ -13,16 +13,16 @@ jobs:
     runs-on: ubuntu-22.04
     container: golang:1.22
     steps:
-    - run: git config --global url.git@github.com:jetstack/venafi-connection-lib.insteadOf https://github.com/jetstack/venafi-connection-lib
     - name: "Add GitHub to the SSH known hosts file"
       run: |
-        mkdir -p -m 0700 ~/.ssh
-        cat <<EOF >~/.ssh/known_hosts
+        mkdir -p -m 0700 /root/.ssh
+        cat <<EOF >/root/.ssh/known_hosts
         github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
         github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
         github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
         EOF
-        chmod 600 ~/.ssh/known_hosts
+        chmod 600 /root/.ssh/known_hosts
+        touch /root/.ssh/config
     - uses: webfactory/ssh-agent@v0.9.0
       with:
         ssh-private-key: ${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}
@@ -34,20 +34,23 @@ jobs:
     runs-on: ubuntu-22.04
     container: golang:1.22
     steps:
-    - run: git config --global url.git@github.com:jetstack/venafi-connection-lib.insteadOf https://github.com/jetstack/venafi-connection-lib
     - name: "Add GitHub to the SSH known hosts file"
       run: |
-        mkdir -p -m 0700 ~/.ssh
-        cat <<EOF >~/.ssh/known_hosts
+        mkdir -p -m 0700 /root/.ssh
+        cat <<EOF >/root/.ssh/known_hosts
         github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
         github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
         github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
         EOF
-        chmod 600 ~/.ssh/known_hosts
+        chmod 600 /root/.ssh/known_hosts
+        touch /root/.ssh/config
     - uses: webfactory/ssh-agent@v0.9.0
       with:
         ssh-private-key: ${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}
     - uses: actions/checkout@v4
+    - name: Adding github workspace as safe directory
+      # See issue https://github.com/actions/checkout/issues/760
+      run: git config --global --add safe.directory $GITHUB_WORKSPACE
     - run: make test
   docker_build:
     name: docker_build
@@ -67,16 +70,6 @@ jobs:
       packages: write
       id-token: write
     steps:
-    - run: git config --global url.git@github.com:jetstack/venafi-connection-lib.insteadOf https://github.com/jetstack/venafi-connection-lib
-    - name: "Add GitHub to the SSH known hosts file"
-      run: |
-        mkdir -p -m 0700 ~/.ssh
-        cat <<EOF >~/.ssh/known_hosts
-        github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
-        github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
-        github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
-        EOF
-        chmod 600 ~/.ssh/known_hosts
     - uses: webfactory/ssh-agent@v0.9.0
       with:
         ssh-private-key: ${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 8d73df62..85d67f31 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -18,25 +18,30 @@ jobs:
     runs-on: ubuntu-22.04
     container: golang:1.22
     steps:
-    - run: git config --global url.git@github.com:jetstack/venafi-connection-lib.insteadOf https://github.com/jetstack/venafi-connection-lib
+    - uses: actions/checkout@v4
     # The only reason we need to configure ~/.ssh/known_hosts is because we are
     # using a container-based runner. Non-container runners already have the
     # github.com fingerprints in their known_hosts file. We could use `curl
     # --silent https://api.github.com/meta` to fetch it but golang:1.22 does not
     # have jq installed.
+    #
+    # Remember that the container "golang:1.22.0" has two "homes": /root is the
+    # home returned by getent(), which is what the GitHub Action and SSH will
+    # use to load .ssh/config and keys under .ssh/, and $HOME is /github/home,
+    # which is where Git loads ~/.gitconfig from.
     - name: "Add GitHub to the SSH known hosts file"
       run: |
-        mkdir -p -m 0700 ~/.ssh
-        cat <<EOF >~/.ssh/known_hosts
+        mkdir -p -m 0700 /root/.ssh
+        cat <<EOF >/root/.ssh/known_hosts
         github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
         github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
         github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
         EOF
-        chmod 600 ~/.ssh/known_hosts
+        chmod 600 /root/.ssh/known_hosts
+        touch /root/.ssh/config
     - uses: webfactory/ssh-agent@v0.9.0
       with:
         ssh-private-key: ${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}
-    - uses: actions/checkout@v4
     - run: make vet
       shell: bash
   test:
@@ -44,21 +49,23 @@ jobs:
     runs-on: ubuntu-22.04
     container: golang:1.22
     steps:
-    - run: git config --global url.git@github.com:jetstack/venafi-connection-lib.insteadOf https://github.com/jetstack/venafi-connection-lib
     - name: "Add GitHub to the SSH known hosts file"
       run: |
-        mkdir -p -m 0700 ~/.ssh
-        cat <<EOF >~/.ssh/known_hosts
+        mkdir -p -m 0700 /root/.ssh
+        cat <<EOF >/root/.ssh/known_hosts
         github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
         github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
         github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
         EOF
-        chmod 600 ~/.ssh/known_hosts
-    - run: curl --silent https://api.github.com/meta | jq --raw-output '"github.com "+.ssh_keys[]' >> ~/.ssh/known_hosts
+        chmod 600 /root/.ssh/known_hosts
+        touch /root/.ssh/config
     - uses: webfactory/ssh-agent@v0.9.0
       with:
         ssh-private-key: ${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}
     - uses: actions/checkout@v4
+    - name: Adding github workspace as safe directory
+      # See issue https://github.com/actions/checkout/issues/760
+      run: git config --global --add safe.directory $GITHUB_WORKSPACE
     - run: make test
   docker_build:
     name: docker_build
@@ -74,21 +81,11 @@ jobs:
           DOCKER_DRIVER: overlay
           DOCKER_HOST: tcp://localhost:2375
     steps:
-    - run: git config --global url.git@github.com:jetstack/venafi-connection-lib.insteadOf https://github.com/jetstack/venafi-connection-lib
-    - name: "Add GitHub to the SSH known hosts file"
-      run: |
-        mkdir -p -m 0700 ~/.ssh
-        cat <<EOF >~/.ssh/known_hosts
-        github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
-        github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
-        github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
-        EOF
-        chmod 600 ~/.ssh/known_hosts
+    - name: Install Tools
+      run: apk add --update make git jq rsync curl
     - uses: webfactory/ssh-agent@v0.9.0
       with:
         ssh-private-key: ${{ secrets.DEPLOY_KEY_READ_VENAFI_CONNECTION_LIB }}
-    - name: Install Tools
-      run: apk add --update make git jq rsync curl
     - name: Adding github workspace as safe directory
       # See issue https://github.com/actions/checkout/issues/760
       run: git config --global --add safe.directory $GITHUB_WORKSPACE

From f1f175cc27f78ae8b74f3eaa9e77a02540fc0c46 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20Valais?= <mael@vls.dev>
Date: Tue, 20 Aug 2024 18:17:40 +0200
Subject: [PATCH 14/16] venconn test: use the template CRD temporarily

---
 pkg/client/client_venconn_test.go | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/pkg/client/client_venconn_test.go b/pkg/client/client_venconn_test.go
index 3f03065a..5a52f8bb 100644
--- a/pkg/client/client_venconn_test.go
+++ b/pkg/client/client_venconn_test.go
@@ -268,7 +268,10 @@ func fakeTPP(t testing.TB) (*httptest.Server, *x509.Certificate) {
 func startEnvtest(t testing.TB) (_ *envtest.Environment, _ *rest.Config, kclient ctrlruntime.WithWatch) {
 	envtest := &envtest.Environment{
 		ErrorIfCRDPathMissing: true,
-		CRDDirectoryPaths:     []string{"../../deploy/charts/venafi-kubernetes-agent/crd_bases/jetstack.io_venaficonnections.yaml"},
+		// TODO(mael): Once the automation around generating the CRD is in
+		// place, remember to update this path to
+		// deploy/charts/venafi-kubernetes-agent/crd_bases/jetstack.io_venaficonnections.yaml
+		CRDDirectoryPaths: []string{"../../deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.yaml"},
 	}
 	restconf, err := envtest.Start()
 	require.NoError(t, err)

From 804d81d7899ef61d6b680d0c92a96606483ae4d5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20Valais?= <mael@vls.dev>
Date: Thu, 22 Aug 2024 10:52:55 +0200
Subject: [PATCH 15/16] address review feedback: error unchecked in
 client_venconn_test.go

Co-authored-by: Richard Wall <978965+wallrj@users.noreply.github.com>
---
 pkg/client/client_venconn_test.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/pkg/client/client_venconn_test.go b/pkg/client/client_venconn_test.go
index 5a52f8bb..2427ec29 100644
--- a/pkg/client/client_venconn_test.go
+++ b/pkg/client/client_venconn_test.go
@@ -195,7 +195,8 @@ func run(test testcase) func(t *testing.T) {
 		}
 
 		got := v1alpha1.VenafiConnection{}
-		kclient.Get(context.Background(), types.NamespacedName{Name: "venafi-components", Namespace: "venafi"}, &got)
+		err = kclient.Get(context.Background(), types.NamespacedName{Name: "venafi-components", Namespace: "venafi"}, &got)
+		require.NoError(t, err)
 		require.Len(t, got.Status.Conditions, 1)
 		assert.Equal(t, test.expectReadyCondMsg, got.Status.Conditions[0].Message)
 	}

From 9b0e6197fdf224220694d79dc0b71d8e915d4bc3 Mon Sep 17 00:00:00 2001
From: Richard Wall <richard.wall@venafi.com>
Date: Fri, 16 Aug 2024 17:22:14 +0100
Subject: [PATCH 16/16] Add missing VenafiConnection templates and values to
 the Helm chart

Signed-off-by: Richard Wall <richard.wall@venafi.com>
---
 .../charts/venafi-kubernetes-agent/README.md  |  141 +-
 .../venafi-kubernetes-agent/README.md.gotmpl  |  129 +-
 .../jetstack.io_venaficonnections.yaml        | 1199 +++++++++++++++++
 .../templates/NOTES.txt                       |   12 +-
 .../templates/_venafi-connection.tpl          |   26 +
 .../templates/deployment.yaml                 |   15 +-
 .../templates/rbac.yaml                       |   27 -
 ...fi-connection-crd.without-validations.yaml |    9 +-
 .../templates/venafi-connection-crd.yaml      |    9 +-
 .../templates/venafi-connection-rbac.yaml     |   46 +
 .../templates/venafi-rbac.yaml                |   30 +
 .../venafi-kubernetes-agent/values.yaml       |   38 +-
 hack/e2e/test.sh                              |  142 ++
 hack/e2e/values.venafi-kubernetes-agent.yaml  |   12 +
 hack/e2e/venafi-components.yaml               |   40 +
 pkg/client/client_venconn_test.go             |    5 +-
 16 files changed, 1583 insertions(+), 297 deletions(-)
 create mode 100644 deploy/charts/venafi-kubernetes-agent/crd_bases/jetstack.io_venaficonnections.yaml
 create mode 100644 deploy/charts/venafi-kubernetes-agent/templates/_venafi-connection.tpl
 create mode 100644 deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-rbac.yaml
 create mode 100644 deploy/charts/venafi-kubernetes-agent/templates/venafi-rbac.yaml
 create mode 100755 hack/e2e/test.sh
 create mode 100644 hack/e2e/values.venafi-kubernetes-agent.yaml
 create mode 100644 hack/e2e/venafi-components.yaml

diff --git a/deploy/charts/venafi-kubernetes-agent/README.md b/deploy/charts/venafi-kubernetes-agent/README.md
index a59d18f6..6ed704ba 100644
--- a/deploy/charts/venafi-kubernetes-agent/README.md
+++ b/deploy/charts/venafi-kubernetes-agent/README.md
@@ -4,147 +4,26 @@ The Venafi Kubernetes Agent connects your Kubernetes or Openshift cluster to the
 
 ![Version: 0.1.49](https://img.shields.io/badge/Version-0.1.49-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v0.1.49](https://img.shields.io/badge/AppVersion-v0.1.49-informational?style=flat-square)
 
-## Additional Information
-
 The Venafi Kubernetes Agent connects your Kubernetes or OpenShift cluster to the Venafi Control Plane.
 You will require a Venafi Control Plane account to connect your cluster.
 If you do not have one, you can sign up for a free trial now at:
 - https://venafi.com/try-venafi/tls-protect/
 
-Note that there are EU and US Venafi Control Plane options.
-Upon signing up you will be redirected to one of either of the following login URLs:
-- https://ui.venafi.cloud/ (US)
-- https://ui.venafi.eu/ (EU)
-
-> 📖 Learn more about [Venafi Kubernetes Agent network requirements](https://docs.venafi.cloud/vaas/k8s-components/c-vcp-network-requirements/),
-> in the two regions.
-
-## Installation
-
-The Helm chart is available from the following Venafi OCI registries:
-
-- `oci://registry.venafi.cloud/charts/venafi-kubernetes-agent` (public)
-- `oci://private-registry.venafi.cloud/charts/venafi-kubernetes-agent` (private, US)
-- `oci://private-registry.venafi.eu/charts/venafi-kubernetes-agent` (private, EU)
-
-> ℹ️ In the following steps it is assumed that you are using the **public** registry.
->
-> 📖 Learn [how to access the private Venafi OCI registries](https://docs.venafi.cloud/vaas/k8s-components/th-guide-confg-access-to-tlspk-enterprise-components/).
-
-Familiarise yourself with the Helm chart:
-
-```sh
-helm show readme oci://registry.venafi.cloud/charts/venafi-kubernetes-agent
-helm show values oci://registry.venafi.cloud/charts/venafi-kubernetes-agent
-helm template oci://registry.venafi.cloud/charts/venafi-kubernetes-agent
-```
-
-### 1) Create a Venafi service account
-
-Create a new service account in the Venafi TLS Protect Cloud web UI.
-The service account is used by the Venafi Kubernetes Agent to authenticate to the Venafi Control Plane.
-Every Venafi Kubernetes Agent should use a unique service account.
-You must create the service account **before** installing the Helm chart.
-
-First create an RSA key pair:
-
-```shell
-export VENAFI_SERVICE_ACCOUNT="example-cluster"
-openssl genrsa -out ${VENAFI_SERVICE_ACCOUNT}.pem
-openssl rsa -in ${VENAFI_SERVICE_ACCOUNT}.pem -pubout --out ${VENAFI_SERVICE_ACCOUNT}.pub
-```
-
-Next create a service account in the Venafi Control Plane:
-
-- Click **Settings > Service Accounts**.
-- Click **New**.
-- Type a name for your new service account.
-  Must match the ${VENAFI_SERVICE_ACCOUNT} variable that you used above.
-- Select an **Owning Team**, which is the team who owns the machine you want to create the service account for.
-- The scope should be "Kubernetes Discovery" only.
-- Set the validity period of your pubic key up to a maximum of 365 days.
-- Paste in the **public key** from the pair you generated.
-- Click **Save** to finish and return to the Service Account list view.
-- Find the row matching the name you entered and copy the "Client ID" value,
-  by clicking "Copy Client ID" in the row actions menu.
-  You will need this when you install the Helm chart.
-
-### 2) Deploy the chart
-
-Create a Namespace and then create a Secret containing the private key of the service account:
-
-```shell
-export VENAFI_NAMESPACE="venafi"
-kubectl create namespace ${VENAFI_NAMESPACE}
-kubectl create secret generic agent-credentials \
-  --namespace ${VENAFI_NAMESPACE} \
-  --from-file=privatekey.pem=${VENAFI_SERVICE_ACCOUNT}.pem
-```
-
-Install the chart:
-
-```shell
-export VENAFI_CLIENT_ID="<your-client-id>"
-helm upgrade venafi-kubernetes-agent oci://registry.venafi.cloud/charts/venafi-kubernetes-agent \
-  --install \
-  --namespace ${VENAFI_NAMESPACE} \
-  --set config.clientId="${VENAFI_CLIENT_ID}"
-```
-
-> ℹ️ To use the [EU Venafi Control Plane](https://docs.venafi.cloud/vaas/k8s-components/c-vcp-network-requirements/),
-> add: `--set config.server=https://api.venafi.eu/`.
-
-### 3) Connect the cluster in Venafi Control Plane
-
-- Click **Installations > Kubernetes Clusters**.
-- Click **Connect**.
-- On step 1, click **Continue**.
-- On step 2, select **Advanced Connection**.
-- On step 3, click **Continue** to skip.
-- On step 4, fill in the details as follows:
-  - Name: use the name of the service account that you created earlier. E.g. "example-cluster".
-  - Service Account: select the service account that you created earlier.
-  - Check "The connection command has completed." box and click **continue**.
-- On step 5, either wait for validation or click **Finish** to go back to the cluster list.
-
-### 4) Verify the deployment
-
-Check the agent logs:
-
-```shell
-kubectl logs -n ${VENAFI_NAMESPACE} -l app.kubernetes.io/instance=venafi-kubernetes-agent --tail -1 | grep -A 5 "Running Agent"
-```
-
-You should see:
-
-```console
-2023/10/24 12:10:03 Running Agent...
-2023/10/24 12:10:03 Posting data to: https://api.venafi.cloud/
-2023/10/24 12:10:04 Data sent successfully.
-```
-
-Check the cluster status by visiting the Clusters page in the Venafi Control Plane:
-- Click  **Installations > Kubernetes Clusters**
-
-You should see:
-- Status: Active
-- Last Check In: ...seconds ago
-
-Check the Event Log page:
-- Click **Settings > Event Log**
-
-You should see the following events for your service account:
-- Service Account Access Token Granted
-- Login Succeeded
+> 📖 Read the [Venafi Kubernetes Agent documentation](https://docs.venafi.cloud/vaas/k8s-components/c-tlspk-agent-overview/),
+> to learn how install and configure this Helm chart.
 
 ## Values
 
 | Key | Type | Default | Description |
 |-----|------|---------|-------------|
 | affinity | object | `{}` | Embed YAML for Node affinity settings, see https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/. |
-| authentication | object | `{"secretKey":"privatekey.pem","secretName":"agent-credentials"}` | Authentication details for the Venafi Kubernetes Agent |
+| authentication | object | `{"secretKey":"privatekey.pem","secretName":"agent-credentials","venafiConnection":{"enabled":false,"name":"venafi-components","namespace":"venafi"}}` | Authentication details for the Venafi Kubernetes Agent |
 | authentication.secretKey | string | `"privatekey.pem"` | Key name in the referenced secret |
 | authentication.secretName | string | `"agent-credentials"` | Name of the secret containing the private key |
+| authentication.venafiConnection | object | `{"enabled":false,"name":"venafi-components","namespace":"venafi"}` | Configure VenafiConnection authentication |
+| authentication.venafiConnection.enabled | bool | `false` | When set to true, the Venafi Kubernetes Agent will authenticate to Venafi using the configuration in a VenafiConnection resource. Use `venafiConnection.enabled=true` for [secretless authentication](https://docs.venafi.cloud/vaas/k8s-components/t-install-tlspk-agent/). When set to true, the `authentication.secret` values will be ignored and the Secret with `authentication.secretName` will _not_ be mounted into the Venafi Kubernetes Agent Pod. |
+| authentication.venafiConnection.name | string | `"venafi-components"` | The name of a VenafiConnection resource which contains the configuration for authenticating to Venafi. |
+| authentication.venafiConnection.namespace | string | `"venafi"` | The namespace of a VenafiConnection resource which contains the configuration for authenticating to Venafi. |
 | command | list | `[]` | Specify the command to run overriding default binary. |
 | config | object | `{"clientId":"","clusterDescription":"","clusterName":"","configmap":{"key":null,"name":null},"period":"0h1m0s","server":"https://api.venafi.cloud/"}` | Configuration section for the Venafi Kubernetes Agent itself |
 | config.clientId | string | `""` | The client-id returned from the Venafi Control Plane |
@@ -153,11 +32,13 @@ You should see the following events for your service account:
 | config.configmap | object | `{"key":null,"name":null}` | Specify ConfigMap details to load config from an existing resource. This should be blank by default unless you have you own config. |
 | config.period | string | `"0h1m0s"` | Send data back to the platform every minute unless changed |
 | config.server | string | `"https://api.venafi.cloud/"` | Overrides the server if using a proxy in your environment For the EU variant use: https://api.venafi.eu/ |
+| crds.forceRemoveValidationAnnotations | bool | `false` | The 'x-kubernetes-validations' annotation is not supported in Kubernetes 1.22 and below. This annotation is used by CEL, which is a feature introduced in Kubernetes 1.25 that improves how validation is performed. This option allows to force the 'x-kubernetes-validations' annotation to be excluded, even on Kubernetes 1.25+ clusters. |
+| crds.venafiConnection | object | `{"include":false}` | Optionally include the VenafiConnection CRDs |
+| crds.venafiConnection.include | bool | `false` | When set to false, the rendered output does not contain the VenafiConnection CRDs and RBAC. This is useful for when the Venafi Connection resources are already installed separately. |
 | extraArgs | list | `[]` | Specify additional arguments to pass to the agent binary. For example `["--strict", "--oneshot"]` |
 | fullnameOverride | string | `""` | Helm default setting, use this to shorten the full install name. |
 | image.pullPolicy | string | `"IfNotPresent"` | Defaults to only pull if not already present |
 | image.repository | string | `"registry.venafi.cloud/venafi-agent/venafi-agent"` | Default to Open Source image repository |
-| image.tag | string | `"v0.1.49"` | Overrides the image tag whose default is the chart appVersion |
 | imagePullSecrets | list | `[]` | Specify image pull credentials if using a private registry example: - name: my-pull-secret |
 | metrics.enabled | bool | `true` | Enable the metrics server. If false, the metrics server will be disabled and the other metrics fields below will be ignored. |
 | metrics.podmonitor.annotations | object | `{}` | Additional annotations to add to the PodMonitor. |
@@ -176,7 +57,7 @@ You should see the following events for your service account:
 | podSecurityContext | object | `{}` | Optional Pod (all containers) `SecurityContext` options, see https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod. |
 | replicaCount | int | `1` | default replicas, do not scale up |
 | resources | object | `{"limits":{"memory":"500Mi"},"requests":{"cpu":"200m","memory":"200Mi"}}` | Set resource requests and limits for the pod.  Read [Venafi Kubernetes components deployment best practices](https://docs.venafi.cloud/vaas/k8s-components/c-k8s-components-best-practice/#scaling) to learn how to choose suitable CPU and memory resource requests and limits. |
-| securityContext | object | `{"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true,"runAsUser":1000}` | Add Container specific SecurityContext settings to the container. Takes precedence over `podSecurityContext` when set. See https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-capabilities-for-a-container |
+| securityContext | object | `{"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true}` | Add Container specific SecurityContext settings to the container. Takes precedence over `podSecurityContext` when set. See https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-capabilities-for-a-container |
 | serviceAccount.annotations | object | `{}` | Annotations YAML to add to the service account |
 | serviceAccount.create | bool | `true` | Specifies whether a service account should be created |
 | serviceAccount.name | string | `""` | The name of the service account to use. If blank and `serviceAccount.create` is true, a name is generated using the fullname template of the release. |
diff --git a/deploy/charts/venafi-kubernetes-agent/README.md.gotmpl b/deploy/charts/venafi-kubernetes-agent/README.md.gotmpl
index ed5e7d00..9a6edc26 100644
--- a/deploy/charts/venafi-kubernetes-agent/README.md.gotmpl
+++ b/deploy/charts/venafi-kubernetes-agent/README.md.gotmpl
@@ -3,138 +3,13 @@
 
 {{ template "chart.versionBadge" . }}{{ template "chart.typeBadge" . }}{{ template "chart.appVersionBadge" . }}
 
-## Additional Information
-
 The Venafi Kubernetes Agent connects your Kubernetes or OpenShift cluster to the Venafi Control Plane.
 You will require a Venafi Control Plane account to connect your cluster.
 If you do not have one, you can sign up for a free trial now at:
 - https://venafi.com/try-venafi/tls-protect/
 
-Note that there are EU and US Venafi Control Plane options.
-Upon signing up you will be redirected to one of either of the following login URLs:
-- https://ui.venafi.cloud/ (US)
-- https://ui.venafi.eu/ (EU)
-
-> 📖 Learn more about [Venafi Kubernetes Agent network requirements](https://docs.venafi.cloud/vaas/k8s-components/c-vcp-network-requirements/),
-> in the two regions.
-
-## Installation
-
-The Helm chart is available from the following Venafi OCI registries:
-
-- `oci://registry.venafi.cloud/charts/venafi-kubernetes-agent` (public)
-- `oci://private-registry.venafi.cloud/charts/venafi-kubernetes-agent` (private, US)
-- `oci://private-registry.venafi.eu/charts/venafi-kubernetes-agent` (private, EU)
-
-> ℹ️ In the following steps it is assumed that you are using the **public** registry.
->
-> 📖 Learn [how to access the private Venafi OCI registries](https://docs.venafi.cloud/vaas/k8s-components/th-guide-confg-access-to-tlspk-enterprise-components/).
-
-Familiarise yourself with the Helm chart:
-
-```sh
-helm show readme oci://registry.venafi.cloud/charts/venafi-kubernetes-agent
-helm show values oci://registry.venafi.cloud/charts/venafi-kubernetes-agent
-helm template oci://registry.venafi.cloud/charts/venafi-kubernetes-agent
-```
-
-### 1) Create a Venafi service account
-
-Create a new service account in the Venafi TLS Protect Cloud web UI.
-The service account is used by the Venafi Kubernetes Agent to authenticate to the Venafi Control Plane.
-Every Venafi Kubernetes Agent should use a unique service account.
-You must create the service account **before** installing the Helm chart.
-
-First create an RSA key pair:
-
-```shell
-export VENAFI_SERVICE_ACCOUNT="example-cluster"
-openssl genrsa -out ${VENAFI_SERVICE_ACCOUNT}.pem
-openssl rsa -in ${VENAFI_SERVICE_ACCOUNT}.pem -pubout --out ${VENAFI_SERVICE_ACCOUNT}.pub
-```
-
-Next create a service account in the Venafi Control Plane:
-
-- Click **Settings > Service Accounts**.
-- Click **New**.
-- Type a name for your new service account.
-  Must match the ${VENAFI_SERVICE_ACCOUNT} variable that you used above.
-- Select an **Owning Team**, which is the team who owns the machine you want to create the service account for.
-- The scope should be "Kubernetes Discovery" only.
-- Set the validity period of your pubic key up to a maximum of 365 days.
-- Paste in the **public key** from the pair you generated.
-- Click **Save** to finish and return to the Service Account list view.
-- Find the row matching the name you entered and copy the "Client ID" value,
-  by clicking "Copy Client ID" in the row actions menu.
-  You will need this when you install the Helm chart.
-
-### 2) Deploy the chart
-
-Create a Namespace and then create a Secret containing the private key of the service account:
-
-```shell
-export VENAFI_NAMESPACE="venafi"
-kubectl create namespace ${VENAFI_NAMESPACE}
-kubectl create secret generic agent-credentials \
-  --namespace ${VENAFI_NAMESPACE} \
-  --from-file=privatekey.pem=${VENAFI_SERVICE_ACCOUNT}.pem
-```
-
-Install the chart:
-
-```shell
-export VENAFI_CLIENT_ID="<your-client-id>"
-helm upgrade venafi-kubernetes-agent oci://registry.venafi.cloud/charts/venafi-kubernetes-agent \
-  --install \
-  --namespace ${VENAFI_NAMESPACE} \
-  --set config.clientId="${VENAFI_CLIENT_ID}"
-```
-
-> ℹ️ To use the [EU Venafi Control Plane](https://docs.venafi.cloud/vaas/k8s-components/c-vcp-network-requirements/),
-> add: `--set config.server=https://api.venafi.eu/`.
-
-### 3) Connect the cluster in Venafi Control Plane
-
-- Click **Installations > Kubernetes Clusters**.
-- Click **Connect**.
-- On step 1, click **Continue**.
-- On step 2, select **Advanced Connection**.
-- On step 3, click **Continue** to skip.
-- On step 4, fill in the details as follows:
-  - Name: use the name of the service account that you created earlier. E.g. "example-cluster".
-  - Service Account: select the service account that you created earlier.
-  - Check "The connection command has completed." box and click **continue**.
-- On step 5, either wait for validation or click **Finish** to go back to the cluster list.
-
-### 4) Verify the deployment
-
-Check the agent logs:
-
-```shell
-kubectl logs -n ${VENAFI_NAMESPACE} -l app.kubernetes.io/instance=venafi-kubernetes-agent --tail -1 | grep -A 5 "Running Agent"
-```
-
-You should see:
-
-```console
-2023/10/24 12:10:03 Running Agent...
-2023/10/24 12:10:03 Posting data to: https://api.venafi.cloud/
-2023/10/24 12:10:04 Data sent successfully.
-```
-
-Check the cluster status by visiting the Clusters page in the Venafi Control Plane:
-- Click  **Installations > Kubernetes Clusters**
-
-You should see:
-- Status: Active
-- Last Check In: ...seconds ago
-
-Check the Event Log page:
-- Click **Settings > Event Log**
-
-You should see the following events for your service account:
-- Service Account Access Token Granted
-- Login Succeeded
+> 📖 Read the [Venafi Kubernetes Agent documentation](https://docs.venafi.cloud/vaas/k8s-components/c-tlspk-agent-overview/),
+> to learn how install and configure this Helm chart.
 
 {{ template "chart.requirementsSection" . }}
 
diff --git a/deploy/charts/venafi-kubernetes-agent/crd_bases/jetstack.io_venaficonnections.yaml b/deploy/charts/venafi-kubernetes-agent/crd_bases/jetstack.io_venaficonnections.yaml
new file mode 100644
index 00000000..c4f3e814
--- /dev/null
+++ b/deploy/charts/venafi-kubernetes-agent/crd_bases/jetstack.io_venaficonnections.yaml
@@ -0,0 +1,1199 @@
+# DO NOT EDIT. Use 'make generate-crds-venconn' to regenerate.
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.14.0
+  name: venaficonnections.jetstack.io
+spec:
+  group: jetstack.io
+  names:
+    kind: VenafiConnection
+    listKind: VenafiConnectionList
+    plural: venaficonnections
+    shortNames:
+    - vc
+    singular: venaficonnection
+  scope: Namespaced
+  versions:
+  - name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: VenafiConnection is the Schema for the VenafiConnection API
+        properties:
+          apiVersion:
+            description: |-
+              APIVersion defines the versioned schema of this representation of an object.
+              Servers should convert recognized schemas to the latest internal value, and
+              may reject unrecognized values.
+              More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+            type: string
+          kind:
+            description: |-
+              Kind is a string value representing the REST resource this object represents.
+              Servers may infer this from the endpoint the client submits requests to.
+              Cannot be updated.
+              In CamelCase.
+              More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+            type: string
+          metadata:
+            type: object
+          spec:
+            properties:
+              allowReferencesFrom:
+                description: |-
+                  A namespace selector that specifies what namespaces this VenafiConnection
+                  is allowed to be used from.
+                  If not set/ null, the VenafiConnection can only be used within its namespace.
+                  An empty selector ({}) matches all namespaces.
+                  If set to a non-empty selector, the VenafiConnection can only be used from
+                  namespaces that match the selector. This possibly excludes the namespace
+                  the VenafiConnection is in.
+                properties:
+                  matchExpressions:
+                    description: matchExpressions is a list of label selector requirements.
+                      The requirements are ANDed.
+                    items:
+                      description: |-
+                        A label selector requirement is a selector that contains values, a key, and an operator that
+                        relates the key and values.
+                      properties:
+                        key:
+                          description: key is the label key that the selector applies
+                            to.
+                          type: string
+                        operator:
+                          description: |-
+                            operator represents a key's relationship to a set of values.
+                            Valid operators are In, NotIn, Exists and DoesNotExist.
+                          type: string
+                        values:
+                          description: |-
+                            values is an array of string values. If the operator is In or NotIn,
+                            the values array must be non-empty. If the operator is Exists or DoesNotExist,
+                            the values array must be empty. This array is replaced during a strategic
+                            merge patch.
+                          items:
+                            type: string
+                          type: array
+                          x-kubernetes-list-type: atomic
+                      required:
+                      - key
+                      - operator
+                      type: object
+                    type: array
+                    x-kubernetes-list-type: atomic
+                  matchLabels:
+                    additionalProperties:
+                      type: string
+                    description: |-
+                      matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
+                      map is equivalent to an element of matchExpressions, whose key field is "key", the
+                      operator is "In", and the values array contains only "value". The requirements are ANDed.
+                    type: object
+                type: object
+                x-kubernetes-map-type: atomic
+              tpp:
+                properties:
+                  accessToken:
+                    description: The list of steps to retrieve a TPP access token.
+                    items:
+                      properties:
+                        hashicorpVaultLDAP:
+                          description: |-
+                            HashicorpVaultLDAP is a SecretSource step that requires a Vault token in
+                            the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                            then fetches the requested secrets from Vault for use in the next step.
+                          properties:
+                            ldapPath:
+                              description: |-
+                                The full HTTP path to the secret in Vault. Example:
+                                /v1/ldap/static-cred/:role_name
+                                or
+                                /v1/ldap/creds/:role_name
+                              type: string
+                            url:
+                              description: The URL to connect to your HashiCorp Vault
+                                instance.
+                              type: string
+                          required:
+                          - ldapPath
+                          type: object
+                        hashicorpVaultOAuth:
+                          description: |-
+                            HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource
+                            step to provide an OAuth token, which this step uses to authenticate to
+                            Vault. The output of this step is a Vault token. This step allows you to use
+                            the step `HashicorpVaultSecret` afterwards.
+                          properties:
+                            authInputType:
+                              description: |-
+                                AuthInputType is the authentication method to be used to authenticate
+                                with HashiCorp Vault. The only supported value is "OIDC".
+                              enum:
+                              - OIDC
+                              type: string
+                            authPath:
+                              description: |-
+                                The login URL used for obtaining the Vault token. Example:
+                                /v1/auth/oidc/login
+                              type: string
+                            clientId:
+                              description: 'DEPRECATED: This field does nothing and
+                                will be removed in the future.'
+                              type: string
+                            role:
+                              description: |-
+                                The role defined in Vault that we want to use when authenticating to
+                                Vault.
+                              type: string
+                            url:
+                              description: The URL to connect to your HashiCorp Vault
+                                instance.
+                              type: string
+                          required:
+                          - authInputType
+                          - authPath
+                          - role
+                          type: object
+                        hashicorpVaultSecret:
+                          description: |-
+                            HashicorpVaultSecret is a SecretSource step that requires a Vault token in
+                            the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                            then fetches the requested secrets from Vault for use in the next step.
+                          properties:
+                            fields:
+                              description: |-
+                                The fields are Vault keys pointing to the secrets passed to the next
+                                SecretSource step.
+
+
+                                Example 1 (TPP, username and password): imagining that you have stored
+                                the username and password for TPP under the keys "username" and
+                                "password", you will want to set this field to `["username",
+                                "password"]`. The username is expected to be given first, the password
+                                second.
+                              items:
+                                type: string
+                              type: array
+                            secretPath:
+                              description: |-
+                                The full HTTP path to the secret in Vault. Example:
+                                /v1/secret/data/application-team-a/tpp-username-password
+                              type: string
+                            url:
+                              description: The URL to connect to your HashiCorp Vault
+                                instance.
+                              type: string
+                          required:
+                          - fields
+                          - secretPath
+                          type: object
+                        secret:
+                          description: |-
+                            Secret is a SecretSource step meant to be the first step. It retrieves secret
+                            values from a Kubernetes Secret, and passes them to the next step.
+                          properties:
+                            fields:
+                              description: |-
+                                The names of the fields we want to extract from the Kubernetes secret.
+                                These fields are passed to the next step in the chain.
+                              items:
+                                type: string
+                              type: array
+                            name:
+                              description: The name of the Kubernetes secret.
+                              type: string
+                          required:
+                          - fields
+                          - name
+                          type: object
+                        serviceAccountToken:
+                          description: |-
+                            ServiceAccountToken is a SecretSource step meant to be the first step. It
+                            uses the Kubernetes TokenRequest API to retrieve a token for a given service
+                            account, and passes it to the next step.
+                          properties:
+                            audiences:
+                              description: |-
+                                Audiences are the intendend audiences of the token. A recipient of a
+                                token must identify themself with an identifier in the list of
+                                audiences of the token, and otherwise should reject the token. A
+                                token issued for multiple audiences may be used to authenticate
+                                against any of the audiences listed but implies a high degree of
+                                trust between the target audiences.
+                              items:
+                                type: string
+                              type: array
+                            expirationSeconds:
+                              description: |-
+                                ExpirationSeconds is the requested duration of validity of the request. The
+                                token issuer may return a token with a different validity duration so a
+                                client needs to check the 'expiration' field in a response.
+                              format: int64
+                              type: integer
+                            name:
+                              description: The name of the Kubernetes service account.
+                              type: string
+                          required:
+                          - audiences
+                          - name
+                          type: object
+                        tppOAuth:
+                          description: |-
+                            TPPOAuth is a SecretSource step that authenticates to a TPP server. This
+                            step is meant to be the last step and requires a prior step that depends
+                            on the `authInputType`.
+                          properties:
+                            authInputType:
+                              description: |-
+                                AuthInputType is the authentication method to be used to authenticate
+                                with TPP. The supported values are "UsernamePassword" and "JWT".
+                              enum:
+                              - UsernamePassword
+                              - JWT
+                              type: string
+                            clientId:
+                              default: cert-manager.io
+                              description: ClientID is the clientId used to authenticate
+                                with TPP.
+                              type: string
+                            url:
+                              description: |-
+                                The URL to connect to the Venafi TPP instance. The two URLs
+                                https://tpp.example.com and https://tpp.example.com/vedsdk are
+                                equivalent. The ending `/vedsdk` is optional and is stripped out
+                                by our client.
+                                If not set, defaults to the URL defined at the top-level of the
+                                TPP configuration.
+                              type: string
+                          required:
+                          - authInputType
+                          type: object
+                        vcpOAuth:
+                          description: |-
+                            VCPOAuth is a SecretSource step that authenticates to the Venafi Control
+                            Plane. This step is meant to be the last step and requires a prior step
+                            that outputs a JWT token.
+                          properties:
+                            tenantID:
+                              description: TenantID is the tenant ID used to authenticate
+                                with VCP.
+                              type: string
+                          type: object
+                      type: object
+                      x-kubernetes-validations:
+                      - message: must have exactly one field set
+                        rule: '(has(self.secret) ? 1 : 0) + (has(self.serviceAccountToken)
+                          ? 1 : 0) + (has(self.hashicorpVaultOAuth) ? 1 : 0) + (has(self.hashicorpVaultSecret)
+                          ? 1 : 0) + (has(self.hashicorpVaultLDAP) ? 1 : 0) + (has(self.tppOAuth)
+                          ? 1 : 0) + (has(self.vcpOAuth) ? 1 : 0) == 1'
+                    maxItems: 50
+                    type: array
+                    x-kubernetes-list-type: atomic
+                  url:
+                    description: |-
+                      The URL to connect to the Venafi TPP instance. The two URLs
+                      https://tpp.example.com and https://tpp.example.com/vedsdk are
+                      equivalent. The ending `/vedsdk` is optional and is stripped out by
+                      venafi-connection-lib.
+                    type: string
+                required:
+                - url
+                type: object
+              vaas:
+                description: 'Deprecated: The ''vaas'' field is deprecated use the
+                  field called ''vcp'' instead.'
+                properties:
+                  accessToken:
+                    description: |-
+                      The list of steps to retrieve the Access Token that will be used to connect
+                      to VCP.
+                    items:
+                      properties:
+                        hashicorpVaultLDAP:
+                          description: |-
+                            HashicorpVaultLDAP is a SecretSource step that requires a Vault token in
+                            the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                            then fetches the requested secrets from Vault for use in the next step.
+                          properties:
+                            ldapPath:
+                              description: |-
+                                The full HTTP path to the secret in Vault. Example:
+                                /v1/ldap/static-cred/:role_name
+                                or
+                                /v1/ldap/creds/:role_name
+                              type: string
+                            url:
+                              description: The URL to connect to your HashiCorp Vault
+                                instance.
+                              type: string
+                          required:
+                          - ldapPath
+                          type: object
+                        hashicorpVaultOAuth:
+                          description: |-
+                            HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource
+                            step to provide an OAuth token, which this step uses to authenticate to
+                            Vault. The output of this step is a Vault token. This step allows you to use
+                            the step `HashicorpVaultSecret` afterwards.
+                          properties:
+                            authInputType:
+                              description: |-
+                                AuthInputType is the authentication method to be used to authenticate
+                                with HashiCorp Vault. The only supported value is "OIDC".
+                              enum:
+                              - OIDC
+                              type: string
+                            authPath:
+                              description: |-
+                                The login URL used for obtaining the Vault token. Example:
+                                /v1/auth/oidc/login
+                              type: string
+                            clientId:
+                              description: 'DEPRECATED: This field does nothing and
+                                will be removed in the future.'
+                              type: string
+                            role:
+                              description: |-
+                                The role defined in Vault that we want to use when authenticating to
+                                Vault.
+                              type: string
+                            url:
+                              description: The URL to connect to your HashiCorp Vault
+                                instance.
+                              type: string
+                          required:
+                          - authInputType
+                          - authPath
+                          - role
+                          type: object
+                        hashicorpVaultSecret:
+                          description: |-
+                            HashicorpVaultSecret is a SecretSource step that requires a Vault token in
+                            the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                            then fetches the requested secrets from Vault for use in the next step.
+                          properties:
+                            fields:
+                              description: |-
+                                The fields are Vault keys pointing to the secrets passed to the next
+                                SecretSource step.
+
+
+                                Example 1 (TPP, username and password): imagining that you have stored
+                                the username and password for TPP under the keys "username" and
+                                "password", you will want to set this field to `["username",
+                                "password"]`. The username is expected to be given first, the password
+                                second.
+                              items:
+                                type: string
+                              type: array
+                            secretPath:
+                              description: |-
+                                The full HTTP path to the secret in Vault. Example:
+                                /v1/secret/data/application-team-a/tpp-username-password
+                              type: string
+                            url:
+                              description: The URL to connect to your HashiCorp Vault
+                                instance.
+                              type: string
+                          required:
+                          - fields
+                          - secretPath
+                          type: object
+                        secret:
+                          description: |-
+                            Secret is a SecretSource step meant to be the first step. It retrieves secret
+                            values from a Kubernetes Secret, and passes them to the next step.
+                          properties:
+                            fields:
+                              description: |-
+                                The names of the fields we want to extract from the Kubernetes secret.
+                                These fields are passed to the next step in the chain.
+                              items:
+                                type: string
+                              type: array
+                            name:
+                              description: The name of the Kubernetes secret.
+                              type: string
+                          required:
+                          - fields
+                          - name
+                          type: object
+                        serviceAccountToken:
+                          description: |-
+                            ServiceAccountToken is a SecretSource step meant to be the first step. It
+                            uses the Kubernetes TokenRequest API to retrieve a token for a given service
+                            account, and passes it to the next step.
+                          properties:
+                            audiences:
+                              description: |-
+                                Audiences are the intendend audiences of the token. A recipient of a
+                                token must identify themself with an identifier in the list of
+                                audiences of the token, and otherwise should reject the token. A
+                                token issued for multiple audiences may be used to authenticate
+                                against any of the audiences listed but implies a high degree of
+                                trust between the target audiences.
+                              items:
+                                type: string
+                              type: array
+                            expirationSeconds:
+                              description: |-
+                                ExpirationSeconds is the requested duration of validity of the request. The
+                                token issuer may return a token with a different validity duration so a
+                                client needs to check the 'expiration' field in a response.
+                              format: int64
+                              type: integer
+                            name:
+                              description: The name of the Kubernetes service account.
+                              type: string
+                          required:
+                          - audiences
+                          - name
+                          type: object
+                        tppOAuth:
+                          description: |-
+                            TPPOAuth is a SecretSource step that authenticates to a TPP server. This
+                            step is meant to be the last step and requires a prior step that depends
+                            on the `authInputType`.
+                          properties:
+                            authInputType:
+                              description: |-
+                                AuthInputType is the authentication method to be used to authenticate
+                                with TPP. The supported values are "UsernamePassword" and "JWT".
+                              enum:
+                              - UsernamePassword
+                              - JWT
+                              type: string
+                            clientId:
+                              default: cert-manager.io
+                              description: ClientID is the clientId used to authenticate
+                                with TPP.
+                              type: string
+                            url:
+                              description: |-
+                                The URL to connect to the Venafi TPP instance. The two URLs
+                                https://tpp.example.com and https://tpp.example.com/vedsdk are
+                                equivalent. The ending `/vedsdk` is optional and is stripped out
+                                by our client.
+                                If not set, defaults to the URL defined at the top-level of the
+                                TPP configuration.
+                              type: string
+                          required:
+                          - authInputType
+                          type: object
+                        vcpOAuth:
+                          description: |-
+                            VCPOAuth is a SecretSource step that authenticates to the Venafi Control
+                            Plane. This step is meant to be the last step and requires a prior step
+                            that outputs a JWT token.
+                          properties:
+                            tenantID:
+                              description: TenantID is the tenant ID used to authenticate
+                                with VCP.
+                              type: string
+                          type: object
+                      type: object
+                      x-kubernetes-validations:
+                      - message: must have exactly one field set
+                        rule: '(has(self.secret) ? 1 : 0) + (has(self.serviceAccountToken)
+                          ? 1 : 0) + (has(self.hashicorpVaultOAuth) ? 1 : 0) + (has(self.hashicorpVaultSecret)
+                          ? 1 : 0) + (has(self.hashicorpVaultLDAP) ? 1 : 0) + (has(self.tppOAuth)
+                          ? 1 : 0) + (has(self.vcpOAuth) ? 1 : 0) == 1'
+                    maxItems: 50
+                    type: array
+                    x-kubernetes-list-type: atomic
+                  apiKey:
+                    description: |-
+                      The list of steps to retrieve the API key that will be used to connect to
+                      VCP.
+                    items:
+                      properties:
+                        hashicorpVaultLDAP:
+                          description: |-
+                            HashicorpVaultLDAP is a SecretSource step that requires a Vault token in
+                            the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                            then fetches the requested secrets from Vault for use in the next step.
+                          properties:
+                            ldapPath:
+                              description: |-
+                                The full HTTP path to the secret in Vault. Example:
+                                /v1/ldap/static-cred/:role_name
+                                or
+                                /v1/ldap/creds/:role_name
+                              type: string
+                            url:
+                              description: The URL to connect to your HashiCorp Vault
+                                instance.
+                              type: string
+                          required:
+                          - ldapPath
+                          type: object
+                        hashicorpVaultOAuth:
+                          description: |-
+                            HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource
+                            step to provide an OAuth token, which this step uses to authenticate to
+                            Vault. The output of this step is a Vault token. This step allows you to use
+                            the step `HashicorpVaultSecret` afterwards.
+                          properties:
+                            authInputType:
+                              description: |-
+                                AuthInputType is the authentication method to be used to authenticate
+                                with HashiCorp Vault. The only supported value is "OIDC".
+                              enum:
+                              - OIDC
+                              type: string
+                            authPath:
+                              description: |-
+                                The login URL used for obtaining the Vault token. Example:
+                                /v1/auth/oidc/login
+                              type: string
+                            clientId:
+                              description: 'DEPRECATED: This field does nothing and
+                                will be removed in the future.'
+                              type: string
+                            role:
+                              description: |-
+                                The role defined in Vault that we want to use when authenticating to
+                                Vault.
+                              type: string
+                            url:
+                              description: The URL to connect to your HashiCorp Vault
+                                instance.
+                              type: string
+                          required:
+                          - authInputType
+                          - authPath
+                          - role
+                          type: object
+                        hashicorpVaultSecret:
+                          description: |-
+                            HashicorpVaultSecret is a SecretSource step that requires a Vault token in
+                            the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                            then fetches the requested secrets from Vault for use in the next step.
+                          properties:
+                            fields:
+                              description: |-
+                                The fields are Vault keys pointing to the secrets passed to the next
+                                SecretSource step.
+
+
+                                Example 1 (TPP, username and password): imagining that you have stored
+                                the username and password for TPP under the keys "username" and
+                                "password", you will want to set this field to `["username",
+                                "password"]`. The username is expected to be given first, the password
+                                second.
+                              items:
+                                type: string
+                              type: array
+                            secretPath:
+                              description: |-
+                                The full HTTP path to the secret in Vault. Example:
+                                /v1/secret/data/application-team-a/tpp-username-password
+                              type: string
+                            url:
+                              description: The URL to connect to your HashiCorp Vault
+                                instance.
+                              type: string
+                          required:
+                          - fields
+                          - secretPath
+                          type: object
+                        secret:
+                          description: |-
+                            Secret is a SecretSource step meant to be the first step. It retrieves secret
+                            values from a Kubernetes Secret, and passes them to the next step.
+                          properties:
+                            fields:
+                              description: |-
+                                The names of the fields we want to extract from the Kubernetes secret.
+                                These fields are passed to the next step in the chain.
+                              items:
+                                type: string
+                              type: array
+                            name:
+                              description: The name of the Kubernetes secret.
+                              type: string
+                          required:
+                          - fields
+                          - name
+                          type: object
+                        serviceAccountToken:
+                          description: |-
+                            ServiceAccountToken is a SecretSource step meant to be the first step. It
+                            uses the Kubernetes TokenRequest API to retrieve a token for a given service
+                            account, and passes it to the next step.
+                          properties:
+                            audiences:
+                              description: |-
+                                Audiences are the intendend audiences of the token. A recipient of a
+                                token must identify themself with an identifier in the list of
+                                audiences of the token, and otherwise should reject the token. A
+                                token issued for multiple audiences may be used to authenticate
+                                against any of the audiences listed but implies a high degree of
+                                trust between the target audiences.
+                              items:
+                                type: string
+                              type: array
+                            expirationSeconds:
+                              description: |-
+                                ExpirationSeconds is the requested duration of validity of the request. The
+                                token issuer may return a token with a different validity duration so a
+                                client needs to check the 'expiration' field in a response.
+                              format: int64
+                              type: integer
+                            name:
+                              description: The name of the Kubernetes service account.
+                              type: string
+                          required:
+                          - audiences
+                          - name
+                          type: object
+                        tppOAuth:
+                          description: |-
+                            TPPOAuth is a SecretSource step that authenticates to a TPP server. This
+                            step is meant to be the last step and requires a prior step that depends
+                            on the `authInputType`.
+                          properties:
+                            authInputType:
+                              description: |-
+                                AuthInputType is the authentication method to be used to authenticate
+                                with TPP. The supported values are "UsernamePassword" and "JWT".
+                              enum:
+                              - UsernamePassword
+                              - JWT
+                              type: string
+                            clientId:
+                              default: cert-manager.io
+                              description: ClientID is the clientId used to authenticate
+                                with TPP.
+                              type: string
+                            url:
+                              description: |-
+                                The URL to connect to the Venafi TPP instance. The two URLs
+                                https://tpp.example.com and https://tpp.example.com/vedsdk are
+                                equivalent. The ending `/vedsdk` is optional and is stripped out
+                                by our client.
+                                If not set, defaults to the URL defined at the top-level of the
+                                TPP configuration.
+                              type: string
+                          required:
+                          - authInputType
+                          type: object
+                        vcpOAuth:
+                          description: |-
+                            VCPOAuth is a SecretSource step that authenticates to the Venafi Control
+                            Plane. This step is meant to be the last step and requires a prior step
+                            that outputs a JWT token.
+                          properties:
+                            tenantID:
+                              description: TenantID is the tenant ID used to authenticate
+                                with VCP.
+                              type: string
+                          type: object
+                      type: object
+                      x-kubernetes-validations:
+                      - message: must have exactly one field set
+                        rule: '(has(self.secret) ? 1 : 0) + (has(self.serviceAccountToken)
+                          ? 1 : 0) + (has(self.hashicorpVaultOAuth) ? 1 : 0) + (has(self.hashicorpVaultSecret)
+                          ? 1 : 0) + (has(self.hashicorpVaultLDAP) ? 1 : 0) + (has(self.tppOAuth)
+                          ? 1 : 0) + (has(self.vcpOAuth) ? 1 : 0) == 1'
+                    maxItems: 50
+                    type: array
+                    x-kubernetes-list-type: atomic
+                  url:
+                    description: |-
+                      The URL to connect to the Venafi VCP instance. If not set, the default
+                      value https://api.venafi.cloud is used.
+                    type: string
+                type: object
+                x-kubernetes-validations:
+                - message: 'must have exactly ONE of the following fields set: apiKey
+                    or accessToken'
+                  rule: '(has(self.apiKey) ? 1 : 0) + (has(self.accessToken) ? 1 :
+                    0) == 1'
+              vcp:
+                properties:
+                  accessToken:
+                    description: |-
+                      The list of steps to retrieve the Access Token that will be used to connect
+                      to VCP.
+                    items:
+                      properties:
+                        hashicorpVaultLDAP:
+                          description: |-
+                            HashicorpVaultLDAP is a SecretSource step that requires a Vault token in
+                            the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                            then fetches the requested secrets from Vault for use in the next step.
+                          properties:
+                            ldapPath:
+                              description: |-
+                                The full HTTP path to the secret in Vault. Example:
+                                /v1/ldap/static-cred/:role_name
+                                or
+                                /v1/ldap/creds/:role_name
+                              type: string
+                            url:
+                              description: The URL to connect to your HashiCorp Vault
+                                instance.
+                              type: string
+                          required:
+                          - ldapPath
+                          type: object
+                        hashicorpVaultOAuth:
+                          description: |-
+                            HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource
+                            step to provide an OAuth token, which this step uses to authenticate to
+                            Vault. The output of this step is a Vault token. This step allows you to use
+                            the step `HashicorpVaultSecret` afterwards.
+                          properties:
+                            authInputType:
+                              description: |-
+                                AuthInputType is the authentication method to be used to authenticate
+                                with HashiCorp Vault. The only supported value is "OIDC".
+                              enum:
+                              - OIDC
+                              type: string
+                            authPath:
+                              description: |-
+                                The login URL used for obtaining the Vault token. Example:
+                                /v1/auth/oidc/login
+                              type: string
+                            clientId:
+                              description: 'DEPRECATED: This field does nothing and
+                                will be removed in the future.'
+                              type: string
+                            role:
+                              description: |-
+                                The role defined in Vault that we want to use when authenticating to
+                                Vault.
+                              type: string
+                            url:
+                              description: The URL to connect to your HashiCorp Vault
+                                instance.
+                              type: string
+                          required:
+                          - authInputType
+                          - authPath
+                          - role
+                          type: object
+                        hashicorpVaultSecret:
+                          description: |-
+                            HashicorpVaultSecret is a SecretSource step that requires a Vault token in
+                            the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                            then fetches the requested secrets from Vault for use in the next step.
+                          properties:
+                            fields:
+                              description: |-
+                                The fields are Vault keys pointing to the secrets passed to the next
+                                SecretSource step.
+
+
+                                Example 1 (TPP, username and password): imagining that you have stored
+                                the username and password for TPP under the keys "username" and
+                                "password", you will want to set this field to `["username",
+                                "password"]`. The username is expected to be given first, the password
+                                second.
+                              items:
+                                type: string
+                              type: array
+                            secretPath:
+                              description: |-
+                                The full HTTP path to the secret in Vault. Example:
+                                /v1/secret/data/application-team-a/tpp-username-password
+                              type: string
+                            url:
+                              description: The URL to connect to your HashiCorp Vault
+                                instance.
+                              type: string
+                          required:
+                          - fields
+                          - secretPath
+                          type: object
+                        secret:
+                          description: |-
+                            Secret is a SecretSource step meant to be the first step. It retrieves secret
+                            values from a Kubernetes Secret, and passes them to the next step.
+                          properties:
+                            fields:
+                              description: |-
+                                The names of the fields we want to extract from the Kubernetes secret.
+                                These fields are passed to the next step in the chain.
+                              items:
+                                type: string
+                              type: array
+                            name:
+                              description: The name of the Kubernetes secret.
+                              type: string
+                          required:
+                          - fields
+                          - name
+                          type: object
+                        serviceAccountToken:
+                          description: |-
+                            ServiceAccountToken is a SecretSource step meant to be the first step. It
+                            uses the Kubernetes TokenRequest API to retrieve a token for a given service
+                            account, and passes it to the next step.
+                          properties:
+                            audiences:
+                              description: |-
+                                Audiences are the intendend audiences of the token. A recipient of a
+                                token must identify themself with an identifier in the list of
+                                audiences of the token, and otherwise should reject the token. A
+                                token issued for multiple audiences may be used to authenticate
+                                against any of the audiences listed but implies a high degree of
+                                trust between the target audiences.
+                              items:
+                                type: string
+                              type: array
+                            expirationSeconds:
+                              description: |-
+                                ExpirationSeconds is the requested duration of validity of the request. The
+                                token issuer may return a token with a different validity duration so a
+                                client needs to check the 'expiration' field in a response.
+                              format: int64
+                              type: integer
+                            name:
+                              description: The name of the Kubernetes service account.
+                              type: string
+                          required:
+                          - audiences
+                          - name
+                          type: object
+                        tppOAuth:
+                          description: |-
+                            TPPOAuth is a SecretSource step that authenticates to a TPP server. This
+                            step is meant to be the last step and requires a prior step that depends
+                            on the `authInputType`.
+                          properties:
+                            authInputType:
+                              description: |-
+                                AuthInputType is the authentication method to be used to authenticate
+                                with TPP. The supported values are "UsernamePassword" and "JWT".
+                              enum:
+                              - UsernamePassword
+                              - JWT
+                              type: string
+                            clientId:
+                              default: cert-manager.io
+                              description: ClientID is the clientId used to authenticate
+                                with TPP.
+                              type: string
+                            url:
+                              description: |-
+                                The URL to connect to the Venafi TPP instance. The two URLs
+                                https://tpp.example.com and https://tpp.example.com/vedsdk are
+                                equivalent. The ending `/vedsdk` is optional and is stripped out
+                                by our client.
+                                If not set, defaults to the URL defined at the top-level of the
+                                TPP configuration.
+                              type: string
+                          required:
+                          - authInputType
+                          type: object
+                        vcpOAuth:
+                          description: |-
+                            VCPOAuth is a SecretSource step that authenticates to the Venafi Control
+                            Plane. This step is meant to be the last step and requires a prior step
+                            that outputs a JWT token.
+                          properties:
+                            tenantID:
+                              description: TenantID is the tenant ID used to authenticate
+                                with VCP.
+                              type: string
+                          type: object
+                      type: object
+                      x-kubernetes-validations:
+                      - message: must have exactly one field set
+                        rule: '(has(self.secret) ? 1 : 0) + (has(self.serviceAccountToken)
+                          ? 1 : 0) + (has(self.hashicorpVaultOAuth) ? 1 : 0) + (has(self.hashicorpVaultSecret)
+                          ? 1 : 0) + (has(self.hashicorpVaultLDAP) ? 1 : 0) + (has(self.tppOAuth)
+                          ? 1 : 0) + (has(self.vcpOAuth) ? 1 : 0) == 1'
+                    maxItems: 50
+                    type: array
+                    x-kubernetes-list-type: atomic
+                  apiKey:
+                    description: |-
+                      The list of steps to retrieve the API key that will be used to connect to
+                      VCP.
+                    items:
+                      properties:
+                        hashicorpVaultLDAP:
+                          description: |-
+                            HashicorpVaultLDAP is a SecretSource step that requires a Vault token in
+                            the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                            then fetches the requested secrets from Vault for use in the next step.
+                          properties:
+                            ldapPath:
+                              description: |-
+                                The full HTTP path to the secret in Vault. Example:
+                                /v1/ldap/static-cred/:role_name
+                                or
+                                /v1/ldap/creds/:role_name
+                              type: string
+                            url:
+                              description: The URL to connect to your HashiCorp Vault
+                                instance.
+                              type: string
+                          required:
+                          - ldapPath
+                          type: object
+                        hashicorpVaultOAuth:
+                          description: |-
+                            HashicorpVaultOAuth is a SecretSource that relies on a prior SecretSource
+                            step to provide an OAuth token, which this step uses to authenticate to
+                            Vault. The output of this step is a Vault token. This step allows you to use
+                            the step `HashicorpVaultSecret` afterwards.
+                          properties:
+                            authInputType:
+                              description: |-
+                                AuthInputType is the authentication method to be used to authenticate
+                                with HashiCorp Vault. The only supported value is "OIDC".
+                              enum:
+                              - OIDC
+                              type: string
+                            authPath:
+                              description: |-
+                                The login URL used for obtaining the Vault token. Example:
+                                /v1/auth/oidc/login
+                              type: string
+                            clientId:
+                              description: 'DEPRECATED: This field does nothing and
+                                will be removed in the future.'
+                              type: string
+                            role:
+                              description: |-
+                                The role defined in Vault that we want to use when authenticating to
+                                Vault.
+                              type: string
+                            url:
+                              description: The URL to connect to your HashiCorp Vault
+                                instance.
+                              type: string
+                          required:
+                          - authInputType
+                          - authPath
+                          - role
+                          type: object
+                        hashicorpVaultSecret:
+                          description: |-
+                            HashicorpVaultSecret is a SecretSource step that requires a Vault token in
+                            the previous step, either using a step `HashicorpVaultOAuth` or `Secret`. It
+                            then fetches the requested secrets from Vault for use in the next step.
+                          properties:
+                            fields:
+                              description: |-
+                                The fields are Vault keys pointing to the secrets passed to the next
+                                SecretSource step.
+
+
+                                Example 1 (TPP, username and password): imagining that you have stored
+                                the username and password for TPP under the keys "username" and
+                                "password", you will want to set this field to `["username",
+                                "password"]`. The username is expected to be given first, the password
+                                second.
+                              items:
+                                type: string
+                              type: array
+                            secretPath:
+                              description: |-
+                                The full HTTP path to the secret in Vault. Example:
+                                /v1/secret/data/application-team-a/tpp-username-password
+                              type: string
+                            url:
+                              description: The URL to connect to your HashiCorp Vault
+                                instance.
+                              type: string
+                          required:
+                          - fields
+                          - secretPath
+                          type: object
+                        secret:
+                          description: |-
+                            Secret is a SecretSource step meant to be the first step. It retrieves secret
+                            values from a Kubernetes Secret, and passes them to the next step.
+                          properties:
+                            fields:
+                              description: |-
+                                The names of the fields we want to extract from the Kubernetes secret.
+                                These fields are passed to the next step in the chain.
+                              items:
+                                type: string
+                              type: array
+                            name:
+                              description: The name of the Kubernetes secret.
+                              type: string
+                          required:
+                          - fields
+                          - name
+                          type: object
+                        serviceAccountToken:
+                          description: |-
+                            ServiceAccountToken is a SecretSource step meant to be the first step. It
+                            uses the Kubernetes TokenRequest API to retrieve a token for a given service
+                            account, and passes it to the next step.
+                          properties:
+                            audiences:
+                              description: |-
+                                Audiences are the intendend audiences of the token. A recipient of a
+                                token must identify themself with an identifier in the list of
+                                audiences of the token, and otherwise should reject the token. A
+                                token issued for multiple audiences may be used to authenticate
+                                against any of the audiences listed but implies a high degree of
+                                trust between the target audiences.
+                              items:
+                                type: string
+                              type: array
+                            expirationSeconds:
+                              description: |-
+                                ExpirationSeconds is the requested duration of validity of the request. The
+                                token issuer may return a token with a different validity duration so a
+                                client needs to check the 'expiration' field in a response.
+                              format: int64
+                              type: integer
+                            name:
+                              description: The name of the Kubernetes service account.
+                              type: string
+                          required:
+                          - audiences
+                          - name
+                          type: object
+                        tppOAuth:
+                          description: |-
+                            TPPOAuth is a SecretSource step that authenticates to a TPP server. This
+                            step is meant to be the last step and requires a prior step that depends
+                            on the `authInputType`.
+                          properties:
+                            authInputType:
+                              description: |-
+                                AuthInputType is the authentication method to be used to authenticate
+                                with TPP. The supported values are "UsernamePassword" and "JWT".
+                              enum:
+                              - UsernamePassword
+                              - JWT
+                              type: string
+                            clientId:
+                              default: cert-manager.io
+                              description: ClientID is the clientId used to authenticate
+                                with TPP.
+                              type: string
+                            url:
+                              description: |-
+                                The URL to connect to the Venafi TPP instance. The two URLs
+                                https://tpp.example.com and https://tpp.example.com/vedsdk are
+                                equivalent. The ending `/vedsdk` is optional and is stripped out
+                                by our client.
+                                If not set, defaults to the URL defined at the top-level of the
+                                TPP configuration.
+                              type: string
+                          required:
+                          - authInputType
+                          type: object
+                        vcpOAuth:
+                          description: |-
+                            VCPOAuth is a SecretSource step that authenticates to the Venafi Control
+                            Plane. This step is meant to be the last step and requires a prior step
+                            that outputs a JWT token.
+                          properties:
+                            tenantID:
+                              description: TenantID is the tenant ID used to authenticate
+                                with VCP.
+                              type: string
+                          type: object
+                      type: object
+                      x-kubernetes-validations:
+                      - message: must have exactly one field set
+                        rule: '(has(self.secret) ? 1 : 0) + (has(self.serviceAccountToken)
+                          ? 1 : 0) + (has(self.hashicorpVaultOAuth) ? 1 : 0) + (has(self.hashicorpVaultSecret)
+                          ? 1 : 0) + (has(self.hashicorpVaultLDAP) ? 1 : 0) + (has(self.tppOAuth)
+                          ? 1 : 0) + (has(self.vcpOAuth) ? 1 : 0) == 1'
+                    maxItems: 50
+                    type: array
+                    x-kubernetes-list-type: atomic
+                  url:
+                    description: |-
+                      The URL to connect to the Venafi VCP instance. If not set, the default
+                      value https://api.venafi.cloud is used.
+                    type: string
+                type: object
+                x-kubernetes-validations:
+                - message: 'must have exactly ONE of the following fields set: apiKey
+                    or accessToken'
+                  rule: '(has(self.apiKey) ? 1 : 0) + (has(self.accessToken) ? 1 :
+                    0) == 1'
+            type: object
+            x-kubernetes-validations:
+            - message: 'must have exactly ONE of the following fields set: tpp or
+                vcp'
+              rule: '(has(self.tpp) ? 1 : 0) + (has(self.vaas) ? 1 : 0) + (has(self.vcp)
+                ? 1 : 0) == 1'
+          status:
+            properties:
+              conditions:
+                description: List of status conditions to indicate the status of a
+                  VenafiConnection.
+                items:
+                  description: ConnectionCondition contains condition information
+                    for a VenafiConnection.
+                  properties:
+                    lastTransitionTime:
+                      description: |-
+                        LastTransitionTime is the timestamp corresponding to the last status
+                        change of this condition.
+                      format: date-time
+                      type: string
+                    lastUpdateTime:
+                      description: lastUpdateTime is the time of the last update to
+                        this condition
+                      format: date-time
+                      type: string
+                    message:
+                      description: |-
+                        Message is a human readable description of the details of the last
+                        transition, complementing reason.
+                      type: string
+                    observedGeneration:
+                      description: |-
+                        If set, this represents the .metadata.generation that the condition was
+                        set based upon.
+                        For instance, if .metadata.generation is currently 12, but the
+                        .status.condition[x].observedGeneration is 9, the condition is out of date
+                        with respect to the current state of the Issuer.
+                      format: int64
+                      type: integer
+                    reason:
+                      description: |-
+                        Reason is a brief machine readable explanation for the condition's last
+                        transition.
+                      type: string
+                    status:
+                      description: Status of the condition, one of (`True`, `False`,
+                        `Unknown`).
+                      type: string
+                    tokenValidUntil:
+                      description: |-
+                        The ValidUntil time of the token used to authenticate with the Venafi
+                        Control Plane server.
+                      format: date-time
+                      type: string
+                    type:
+                      description: |-
+                        Type of the condition, should be a combination of the unique name of the
+                        operator and the type of condition.
+                        eg. `VenafiEnhancedIssuerReady`
+                      type: string
+                  required:
+                  - status
+                  - type
+                  type: object
+                type: array
+                x-kubernetes-list-map-keys:
+                - type
+                x-kubernetes-list-type: map
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
diff --git a/deploy/charts/venafi-kubernetes-agent/templates/NOTES.txt b/deploy/charts/venafi-kubernetes-agent/templates/NOTES.txt
index 5e7b015d..f74caec3 100644
--- a/deploy/charts/venafi-kubernetes-agent/templates/NOTES.txt
+++ b/deploy/charts/venafi-kubernetes-agent/templates/NOTES.txt
@@ -1,8 +1,12 @@
-1. Check the credentials Secret exists: "{{ .Values.authentication.secretName }}"
+{{- if .Values.authentication.venafiConnection.enabled }}
+- Check the VenafiConnection exists: "{{ .Values.authentication.venafiConnection.namespace }}/{{ .Values.authentication.venafiConnection.name }}"
+> kubectl get VenafiConnection -n {{ .Values.authentication.venafiConnection.namespace }} {{ .Values.authentication.venafiConnection.name }}
+{{- else }}
+- Check the credentials Secret exists: "{{ .Values.authentication.secretName }}"
 > kubectl get secret -n {{ .Release.Namespace }} {{ .Values.authentication.secretName }}
-
-2. Check the application is running:
+{{- end }}
+- Check the application is running:
 > kubectl get pods -n {{ .Release.Namespace }} -l app.kubernetes.io/instance={{ .Release.Name }}
 
-3. Check the application logs for successful connection to the platform:
+- Check the application logs for successful connection to the platform:
 > kubectl logs -n {{ .Release.Namespace }} -l app.kubernetes.io/instance={{ .Release.Name }}
diff --git a/deploy/charts/venafi-kubernetes-agent/templates/_venafi-connection.tpl b/deploy/charts/venafi-kubernetes-agent/templates/_venafi-connection.tpl
new file mode 100644
index 00000000..07e7fe1c
--- /dev/null
+++ b/deploy/charts/venafi-kubernetes-agent/templates/_venafi-connection.tpl
@@ -0,0 +1,26 @@
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "venafi-connection.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "venafi-connection.labels" -}}
+helm.sh/chart: {{ include "venafi-connection.chart" . }}
+{{ include "venafi-connection.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "venafi-connection.selectorLabels" -}}
+app.kubernetes.io/name: "venafi-connection"
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
diff --git a/deploy/charts/venafi-kubernetes-agent/templates/deployment.yaml b/deploy/charts/venafi-kubernetes-agent/templates/deployment.yaml
index 68a1b9bb..ca3ef98a 100644
--- a/deploy/charts/venafi-kubernetes-agent/templates/deployment.yaml
+++ b/deploy/charts/venafi-kubernetes-agent/templates/deployment.yaml
@@ -57,8 +57,15 @@ spec:
             - "agent"
             - "-c"
             - "/etc/venafi/agent/config/{{ default "config.yaml" .Values.config.configmap.key }}"
+            {{- if .Values.authentication.venafiConnection.enabled }}
+            - --venafi-connection
+            - {{ .Values.authentication.venafiConnection.name | quote }}
+            - --venafi-connection-namespace
+            - {{ .Values.authentication.venafiConnection.namespace | quote }}
+            {{- else }}
             - "--client-id"
-            - "{{ .Values.config.clientId }}"
+            - {{ .Values.config.clientId | quote }}
+            {{- end }}
             - "-p"
             - "0h1m0s"
             - --venafi-cloud
@@ -74,9 +81,11 @@ spec:
             - name: config
               mountPath: "/etc/venafi/agent/config"
               readOnly: true
+            {{- if not .Values.authentication.venafiConnection.enabled }}
             - name: credentials
               mountPath: "/etc/venafi/agent/key"
               readOnly: true
+            {{- end }}
             {{- with .Values.volumeMounts }}
             {{- toYaml . | nindent 12 }}
             {{- end }}
@@ -102,10 +111,12 @@ spec:
           configMap:
             name: {{ default "agent-config" .Values.config.configmap.name }}
             optional: false
+        {{- if not .Values.authentication.venafiConnection.enabled }}
         - name: credentials
           secret:
-            secretName: {{ default "agent-credentials" .Values.authentication.secretName }}
+            secretName: {{ .Values.authentication.secretName }}
             optional: false
+        {{- end }}
         {{- with .Values.volumes }}
         {{- toYaml . | nindent 8 }}
         {{- end }}
diff --git a/deploy/charts/venafi-kubernetes-agent/templates/rbac.yaml b/deploy/charts/venafi-kubernetes-agent/templates/rbac.yaml
index 693623d6..c3c2fcc8 100644
--- a/deploy/charts/venafi-kubernetes-agent/templates/rbac.yaml
+++ b/deploy/charts/venafi-kubernetes-agent/templates/rbac.yaml
@@ -1,32 +1,5 @@
 ---
 apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  name: {{ include "venafi-kubernetes-agent.fullname" . }}-venaficonnection
-  labels:
-    {{- include "venafi-kubernetes-agent.labels" . | nindent 4 }}
-rules:
-  - apiGroups: ["jetstack.io"]
-    resources:
-      - venaficonnections
-    verbs: ["get", "list", "watch", "update"]
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
-  name: {{ include "venafi-kubernetes-agent.fullname" . }}-venaficonnection
-  labels:
-    {{- include "venafi-kubernetes-agent.labels" . | nindent 4 }}
-roleRef:
-  kind: ClusterRole
-  name: {{ include "venafi-kubernetes-agent.fullname" . }}-venaficonnection
-  apiGroup: rbac.authorization.k8s.io
-subjects:
-  - kind: ServiceAccount
-    name: {{ include "venafi-kubernetes-agent.serviceAccountName" . }}
-    namespace: {{ .Release.Namespace }}
----
-apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRoleBinding
 metadata:
   name: {{ include "venafi-kubernetes-agent.fullname" . }}-cluster-viewer
diff --git a/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.without-validations.yaml b/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.without-validations.yaml
index 6d92a2fa..e969b224 100644
--- a/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.without-validations.yaml
+++ b/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.without-validations.yaml
@@ -1,10 +1,15 @@
----
+{{- if .Values.crds.venafiConnection.include }}
+{{- if (or (semverCompare "<1.25" .Capabilities.KubeVersion.GitVersion) .Values.crds.forceRemoveValidationAnnotations) }}
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
     controller-gen.kubebuilder.io/version: v0.14.0
+    # This annotation prevents the CRD from being pruned by Helm when this chart is deleted.
+    "helm.sh/resource-policy": keep
   name: venaficonnections.jetstack.io
+  labels:
+  {{- include "venafi-connection.labels" $ | nindent 4 }}
 spec:
   group: jetstack.io
   names:
@@ -1114,3 +1119,5 @@ spec:
       storage: true
       subresources:
         status: {}
+{{- end }}
+{{- end }}
diff --git a/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.yaml b/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.yaml
index 97a6aa71..ff03bf68 100644
--- a/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.yaml
+++ b/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.yaml
@@ -1,10 +1,15 @@
----
+{{- if .Values.crds.venafiConnection.include }}
+{{- if not (or (semverCompare "<1.25" .Capabilities.KubeVersion.GitVersion) .Values.crds.forceRemoveValidationAnnotations) }}
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
     controller-gen.kubebuilder.io/version: v0.14.0
+    # This annotation prevents the CRD from being pruned by Helm when this chart is deleted.
+    "helm.sh/resource-policy": keep
   name: venaficonnections.jetstack.io
+  labels:
+  {{- include "venafi-connection.labels" $ | nindent 4 }}
 spec:
   group: jetstack.io
   names:
@@ -1138,3 +1143,5 @@ spec:
       storage: true
       subresources:
         status: {}
+{{- end }}
+{{- end }}
diff --git a/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-rbac.yaml b/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-rbac.yaml
new file mode 100644
index 00000000..620ad162
--- /dev/null
+++ b/deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-rbac.yaml
@@ -0,0 +1,46 @@
+{{- if .Values.crds.venafiConnection.include }}
+# The 'venafi-connection' service account is used by multiple
+# controllers. When configuring which resources a VenafiConnection
+# can access, the RBAC rules you create manually must point to this SA.
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: venafi-connection
+  namespace: {{ $.Release.Namespace | quote }}
+  labels:
+  {{- include "venafi-connection.labels" $ | nindent 4 }}
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: venafi-connection-role
+  labels:
+  {{- include "venafi-connection.labels" $ | nindent 4 }}
+rules:
+- apiGroups: [ "" ]
+  resources: [ "namespaces" ]
+  verbs: [ "get", "list", "watch" ]
+
+- apiGroups: [ "jetstack.io" ]
+  resources: [ "venaficonnections" ]
+  verbs: [ "get", "list", "watch" ]
+
+- apiGroups: [ "jetstack.io" ]
+  resources: [ "venaficonnections/status" ]
+  verbs: [ "get", "patch" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: venafi-connection-rolebinding
+  labels:
+  {{- include "venafi-connection.labels" $ | nindent 4 }}
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: venafi-connection-role
+subjects:
+- kind: ServiceAccount
+  name: venafi-connection
+  namespace: {{ $.Release.Namespace | quote }}
+{{- end }}
diff --git a/deploy/charts/venafi-kubernetes-agent/templates/venafi-rbac.yaml b/deploy/charts/venafi-kubernetes-agent/templates/venafi-rbac.yaml
new file mode 100644
index 00000000..21d19f13
--- /dev/null
+++ b/deploy/charts/venafi-kubernetes-agent/templates/venafi-rbac.yaml
@@ -0,0 +1,30 @@
+{{- if .Values.authentication.venafiConnection.enabled }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: venafi-kubernetes-agent-impersonate-role
+  namespace: {{ $.Release.Namespace | quote }}
+  labels:
+  {{- include "venafi-kubernetes-agent.labels" . | nindent 4 }}
+rules:
+- apiGroups: [ "" ]
+  resources: [ "serviceaccounts" ]
+  verbs: [ "impersonate" ]
+  resourceNames: [ "venafi-connection" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: venafi-kubernetes-agent-impersonate-rolebinding
+  namespace: {{ $.Release.Namespace | quote }}
+  labels:
+  {{- include "venafi-kubernetes-agent.labels" . | nindent 4 }}
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: venafi-kubernetes-agent-impersonate-role
+subjects:
+- kind: ServiceAccount
+  name: {{ include "venafi-kubernetes-agent.serviceAccountName" . }}
+  namespace: {{ $.Release.Namespace | quote }}
+{{- end }}
diff --git a/deploy/charts/venafi-kubernetes-agent/values.yaml b/deploy/charts/venafi-kubernetes-agent/values.yaml
index 8502a92d..7cdc22aa 100644
--- a/deploy/charts/venafi-kubernetes-agent/values.yaml
+++ b/deploy/charts/venafi-kubernetes-agent/values.yaml
@@ -57,7 +57,7 @@ image:
   # -- Defaults to only pull if not already present
   pullPolicy: IfNotPresent
   # -- Overrides the image tag whose default is the chart appVersion
-  tag: "v0.1.49"
+  # tag: "v0.0.0"
 
 # -- Specify image pull credentials if using a private registry
 # example: - name: my-pull-secret
@@ -172,6 +172,23 @@ authentication:
   # -- Key name in the referenced secret
   secretKey: "privatekey.pem"
 
+  # +docs:section=Venafi Connection
+  # -- Configure VenafiConnection authentication
+  venafiConnection:
+    # -- When set to true, the Venafi Kubernetes Agent will authenticate to
+    # Venafi using the configuration in a VenafiConnection resource.
+    # Use `venafiConnection.enabled=true` for [secretless authentication](https://docs.venafi.cloud/vaas/k8s-components/t-install-tlspk-agent/).
+    # When set to true, the `authentication.secret` values will be ignored and the
+    # Secret with `authentication.secretName` will _not_ be mounted into the
+    # Venafi Kubernetes Agent Pod.
+    enabled: false
+    # -- The name of a VenafiConnection resource which contains the configuration
+    # for authenticating to Venafi.
+    name: venafi-components
+    # -- The namespace of a VenafiConnection resource which contains the
+    # configuration for authenticating to Venafi.
+    namespace: venafi
+
 # -- Configuration section for the Venafi Kubernetes Agent itself
 config:
   # -- Overrides the server if using a proxy in your environment
@@ -208,3 +225,22 @@ podDisruptionBudget:
   # an integer (e.g. 1) or a percentage value (e.g. 25%).
   # Cannot be used if `minAvailable` is set.
   # maxUnavailable: 1
+
+# +docs:section=CRDs
+# The CRDs installed by this chart are annotated with "helm.sh/resource-policy: keep", this
+# prevents them from being accidentally removed by Helm when this chart is deleted. After
+# deleting the installed chart, the user still has to manually remove the remaining CRDs.
+crds:
+  # -- The 'x-kubernetes-validations' annotation is not supported in Kubernetes 1.22 and below.
+  # This annotation is used by CEL, which is a feature introduced in Kubernetes 1.25 that
+  # improves how validation is performed.
+  # This option allows to force the 'x-kubernetes-validations' annotation to be excluded,
+  # even on Kubernetes 1.25+ clusters.
+  forceRemoveValidationAnnotations: false
+
+  # -- Optionally include the VenafiConnection CRDs
+  venafiConnection:
+    # -- When set to false, the rendered output does not contain the
+    # VenafiConnection CRDs and RBAC. This is useful for when the
+    # Venafi Connection resources are already installed separately.
+    include: false
diff --git a/hack/e2e/test.sh b/hack/e2e/test.sh
new file mode 100755
index 00000000..b56e4990
--- /dev/null
+++ b/hack/e2e/test.sh
@@ -0,0 +1,142 @@
+#!/usr/bin/env bash
+#
+# Build and install venafi-kubernetes-agent for VenafiConnection based authentication.
+# Wait for it to log a message indicating successful data upload.
+#
+# venafi-kubernetes-agent is packaged using ko and Helm and installed in a Kind cluster.
+# A VenafiConnection resource is created which directly loads a bearer token
+# from a Kubernetes Secret.
+# This is the simplest way of testing the VenafiConnection integration,
+# but it does not fully test "secretless" (workload identity federation) authentication.
+#
+# Prerequisites:
+# * ko: https://github.com/ko-build/ko/releases/tag/v0.16.0
+# * helm: https://helm.sh/docs/intro/install/
+# * kind: https://kubernetes.io/docs/tasks/tools/#kind
+# * kubectl: https://kubernetes.io/docs/tasks/tools/#kubectl
+# * venctl: https://docs.venafi.cloud/vaas/venctl/t-venctl-install/
+# * jq: https://jqlang.github.io/jq/download/
+# * step: https://smallstep.com/docs/step-cli/installation/
+# * curl: https://www.man7.org/linux/man-pages/man1/curl.1.html
+# * envsubst: https://www.man7.org/linux/man-pages/man1/envsubst.1.html
+
+set -o nounset
+set -o errexit
+set -o pipefail
+
+# Your Venafi Cloud API key.
+: ${VEN_API_KEY?}
+
+# The Venafi Cloud team which will be the owner of the generated Venafi service
+# accounts.
+: ${VEN_OWNING_TEAM?}
+
+# The base URL of the OCI registry used for Docker images and Helm charts
+# E.g. ttl.sh/63773370-0bcf-4ac0-bd42-5515616089ff
+: ${OCI_BASE?}
+
+export VERSION=$(git describe --tags --always --match='v*' --abbrev=14 --dirty)
+export KO_DOCKER_REPO=$OCI_BASE/images/venafi-agent
+export TERM=dumb
+
+script_dir=$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd)
+root_dir=$(cd "${script_dir}/../.." && pwd)
+
+cd "${script_dir}"
+
+pushd "${root_dir}"
+ko build  --bare --tags "${VERSION}"
+helm package deploy/charts/venafi-kubernetes-agent --version "${VERSION}" --app-version "${VERSION}"
+helm push venafi-kubernetes-agent-${VERSION}.tgz "oci://${OCI_BASE}/charts"
+popd
+
+kind create cluster || true
+
+kubectl create ns venafi || true
+
+# Pull secret for Venafi OCI registry
+if ! kubectl get secret venafi-image-pull-secret -n venafi; then
+    venctl iam service-accounts registry create \
+           --no-prompts \
+           --owning-team "${VEN_OWNING_TEAM}" \
+           --name "venafi-kubernetes-agent-e2e-registry-${RANDOM}" \
+           --scopes enterprise-cert-manager,enterprise-venafi-issuer,enterprise-approver-policy \
+    | jq '{
+            "apiVersion": "v1",
+            "kind": "Secret",
+            "metadata": {
+              "name": "venafi-image-pull-secret"
+            },
+            "type": "kubernetes.io/dockerconfigjson",
+            "stringData": {
+              ".dockerconfigjson": {
+                "auths": {
+                  "\(.oci_registry)": {
+                    "username": .username,
+                    "password": .password
+                  }
+                }
+              } | tostring
+            }
+          }' \
+    | kubectl create -n venafi -f -
+fi
+
+# Cache the Service account credentials for venafi-kubernetes-agent in the cluster
+# but this Secret will not be mounted by the agent.
+kubectl create ns venafi-kubernetes-agent-e2e || true
+if ! kubectl get secret cached-venafi-agent-service-account -n venafi-kubernetes-agent-e2e; then
+    venctl iam service-account agent create \
+           --no-prompts \
+           --owning-team "${VEN_OWNING_TEAM}" \
+           --name "venafi-kubernetes-agent-e2e-agent-${RANDOM}" \
+    | jq '{
+            "apiVersion": "v1",
+            "kind": "Secret",
+            "metadata": {
+              "name": "cached-venafi-agent-service-account"
+            },
+            "stringData": {
+              "privatekey.pem": .private_key,
+              "client-id": .client_id
+            }
+          }' \
+    | kubectl create -n venafi-kubernetes-agent-e2e -f -
+fi
+
+export VENAFI_KUBERNETES_AGENT_CLIENT_ID="not-used-but-required-by-venctl"
+venctl components kubernetes apply \
+       --venafi-kubernetes-agent \
+       --venafi-kubernetes-agent-version "$VERSION" \
+       --venafi-kubernetes-agent-values-files "${script_dir}/values.venafi-kubernetes-agent.yaml" \
+       --venafi-kubernetes-agent-custom-image-registry "${OCI_BASE}/images" \
+       --venafi-kubernetes-agent-custom-chart-repository "oci://${OCI_BASE}/charts"
+
+privatekey=$(kubectl get secret cached-venafi-agent-service-account \
+                     --namespace venafi-kubernetes-agent-e2e \
+                     --template="{{index .data \"privatekey.pem\" | base64decode}}")
+clientid=$(kubectl get secret cached-venafi-agent-service-account \
+                   --namespace venafi-kubernetes-agent-e2e \
+                   --template="{{index .data \"client-id\" | base64decode}}")
+jwt=$(step crypto jwt sign \
+                   --key <(sed 's/ PRIVATE KEY/ EC PRIVATE KEY/g' <<<"$privatekey") \
+                   --aud api.venafi.cloud/v1/oauth/token/serviceaccount \
+                   --exp "$([ "$(uname)" = "Darwin" ] && date -v +30M +'%s' || date -d '+30 minutes' +'%s')" \
+                   --sub "$clientid" \
+                   --iss "$clientid" \
+                  | tee >(step crypto jwt inspect --insecure >/dev/stderr))
+accesstoken=$(curl https://api.venafi.cloud/v1/oauth/token/serviceaccount \
+             -sS --fail-with-body \
+             --data-urlencode assertion="$jwt" \
+             --data-urlencode grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer | tee /dev/stderr | jq '.access_token' -r)
+export accesstoken
+envsubst < venafi-components.yaml | kubectl apply -n venafi -f -
+
+# Wait for log message indicating success.
+# Filter out distracting data gatherer errors and warnings.
+# Show other useful log messages on stderr.
+kubectl logs deployments/venafi-kubernetes-agent \
+        --follow \
+        --namespace venafi \
+    | tee >(grep -v -e "reflector\.go" -e "datagatherer" -e "data gatherer" > /dev/stderr) \
+    | grep -q "Data sent successfully"
diff --git a/hack/e2e/values.venafi-kubernetes-agent.yaml b/hack/e2e/values.venafi-kubernetes-agent.yaml
new file mode 100644
index 00000000..a2545eca
--- /dev/null
+++ b/hack/e2e/values.venafi-kubernetes-agent.yaml
@@ -0,0 +1,12 @@
+config:
+  clusterName: venafi-kubernetes-agent-e2e
+  clusterDescription: |
+      A kind cluster used for testing the venafi-kubernetes-agent.
+
+authentication:
+  venafiConnection:
+    enabled: true
+
+crds:
+  venafiConnection:
+    include: true
diff --git a/hack/e2e/venafi-components.yaml b/hack/e2e/venafi-components.yaml
new file mode 100644
index 00000000..5e48a08b
--- /dev/null
+++ b/hack/e2e/venafi-components.yaml
@@ -0,0 +1,40 @@
+apiVersion: jetstack.io/v1alpha1
+kind: VenafiConnection
+metadata:
+  name: venafi-components
+spec:
+  vcp:
+    accessToken:
+    - secret:
+        name: venafi-credentials
+        fields: ["access-token"]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+      name: get-venafi-credentials
+rules:
+    - apiGroups: [ "" ]
+      resources: [ "secrets" ]
+      verbs: [ "get" ]
+      resourceNames: [ "venafi-credentials" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: application-team-1-secret-rolebinding
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: get-venafi-credentials
+subjects:
+- kind: ServiceAccount
+  name: venafi-connection
+  namespace: venafi
+---
+apiVersion: v1
+kind: Secret
+metadata:
+  name: venafi-credentials
+stringData:
+  access-token: ${accesstoken}
diff --git a/pkg/client/client_venconn_test.go b/pkg/client/client_venconn_test.go
index 2427ec29..0396b5e8 100644
--- a/pkg/client/client_venconn_test.go
+++ b/pkg/client/client_venconn_test.go
@@ -269,10 +269,7 @@ func fakeTPP(t testing.TB) (*httptest.Server, *x509.Certificate) {
 func startEnvtest(t testing.TB) (_ *envtest.Environment, _ *rest.Config, kclient ctrlruntime.WithWatch) {
 	envtest := &envtest.Environment{
 		ErrorIfCRDPathMissing: true,
-		// TODO(mael): Once the automation around generating the CRD is in
-		// place, remember to update this path to
-		// deploy/charts/venafi-kubernetes-agent/crd_bases/jetstack.io_venaficonnections.yaml
-		CRDDirectoryPaths: []string{"../../deploy/charts/venafi-kubernetes-agent/templates/venafi-connection-crd.yaml"},
+		CRDDirectoryPaths:     []string{"../../deploy/charts/venafi-kubernetes-agent/crd_bases/jetstack.io_venaficonnections.yaml"},
 	}
 	restconf, err := envtest.Start()
 	require.NoError(t, err)
