To add support for External Secret Stores in a provider, we need the following changes at a high level:
PublishConnectionDetails
API.ConnectionPublisher
if
feature enabled.In this document, we will go through each step in details. You can check this PR as a complete example.
If your provider is a Terrajet based provider, then please check this PR instead.
1. Bump Crossplane Runtime and Crossplane Tools to latest and generate
existing resources to include PublishConnectionDetails
API.
We need a workaround for code generation since latest runtime both adds new API but also adds a new interface to managed.resourceSpec. Without this workaround, expect errors similar to below:
16:40:56 [ .. ] go generate darwin_amd64
angryjet: error: error loading packages using pattern ./...: /Users/hasanturken/ Workspace/crossplane/provider-gcp/apis/cache/v1beta1/zz_ generated.managedlist.go:27:14: cannot use &l.Items[i] (value of type * CloudMemorystoreInstance) as "github.com/crossplane/crossplane-runtime/pkg/ resource".Managed value in assignment: missing method GetPublishConnectionDetailsTo
exit status 1
apis/generate.go:30: running "go": exit status 1
16:41:04 [FAIL]
make[1]: *** [go.generate] Error 1
make: *** [generate] Error 2
First, we need to consume a temporary runtime version together with the latest Crossplane Tools:
go mod edit -replace=github.com/crossplane/crossplane-runtime=github.com/turkenh/crossplane-runtime@v0.0.0-20220314141040-6f74175d3c1f
go get github.com/crossplane/crossplane-tools@master
go mod tidy
Then, remove trivialVersions=true
in the file api/generate.go
:
-//go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen object:headerFile=../hack/boilerplate.go.txt paths=./... crd:trivialVersions=true,crdVersions=v1 output:artifacts:config=../package/crds
+//go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen object:headerFile=../hack/boilerplate.go.txt paths=./... crd:crdVersions=v1 output:artifacts:config=../package/crds
Now, we can generate CRDs with PublishConnectionDetailsTo
API:
make generate
Finally, we can revert our workaround by consuming the latest Crossplane Runtime:
go mod edit -dropreplace=github.com/crossplane/crossplane-runtime
go get github.com/crossplane/crossplane-runtime@master
go mod tidy
make generate
2. Add a new Type and CRD for Secret StoreConfig.
See this commit as an example on how to add the type. It is expected to be
almost same for all providers except groupName which includes the name short
name of the provider (e.g. gcp.crossplane.io
)
Generate the CRD with:
make generate
3. Add feature flag for enabling External Secret Store support.
We will add a feature flag to enable the feature which would be off by default.
As part of this step, we will also create a default
StoreConfig
during
provider start up, which stores connection secrets into the same Kubernetes
cluster.
To be consistent across all providers, please define
--enable-external-secret-stores
as a boolean which is false by default.
See this commit as an example for adding the feature flag.
4. Add Secret Store Connection Details Manager as a ConnectionPublisher
if
feature enabled.
Add the following to the Setup function controller. Unfortunately this step requires some dirty work as we need to this for all types:
func SetupServiceAccountKey(mgr ctrl.Manager, o controller.Options) error {
name := managed.ControllerName(v1alpha1.ServiceAccountKeyGroupKind)
+ cps := []managed.ConnectionPublisher{managed.NewAPISecretPublisher(mgr.GetClient(), mgr.GetScheme())}
+ if o.Features.Enabled(features.EnableAlphaExternalSecretStores) {
+ cps = append(cps, connection.NewDetailsManager(mgr.GetClient(), scv1alpha1.StoreConfigGroupVersionKind))
+ }
+
r := managed.NewReconciler(mgr,
resource.ManagedKind(v1alpha1.ServiceAccountKeyGroupVersionKind),
managed.WithInitializers(),
managed.WithExternalConnecter(&serviceAccountKeyServiceConnector{client: mgr.GetClient()}),
managed.WithPollInterval(o.PollInterval),
managed.WithLogger(o.Logger.WithValues("controller", name)),
- managed.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name))))
+ managed.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name))),
+ managed.WithConnectionPublishers(cps...))
return ctrl.NewControllerManagedBy(mgr).
Named(name).
You can check this commit as an example for changes in Setup functions as an example.