Skip to content

Using the esoctl tool

The tool can be found under cmd/esoctl.

Debugging templates

The template command can be used to test templates for PushSecret and ExternalSecret.

To run render simply execute make build in the cmd/esoctl folder. This will result in a binary under cmd/esoctl/bin.

Once the build succeeds, the command can be used as such:

bin/esoctl template --source-templated-object template-test/push-secret.yaml --source-secret-data-file template-test/secret.yaml

Where template-test looks like this:

❯ tree template-test/                                                                                                                                                                                                                   (base)
template-test/
├── push-secret.yaml
└── secret.yaml

1 directory, 2 files

PushSecret is simply the following:

apiVersion: external-secrets.io/v1alpha1
kind: PushSecret
metadata:
  name: example-push-secret-with-template
spec:
  refreshInterval: 10s
  secretStoreRefs:
    - name: secret-store-name
      kind: SecretStore
  selector:
    secret:
      name: git-sync-secret
  template:
    engineVersion: v2
    data:
      token: "{{ .token | toString | upper }} was templated"
  data:
    - match:
        secretKey: token
        remoteRef:
          remoteKey: git-sync-secret-copy-templated
          property: token

And secret data is:

token: dG9rZW4=

Therefore if there is a PushSecret or an ExternalSecret object that the user would like to test the template for, simply put it into a file along with the data it's using, and run this command.

The output will be something like this:

bin/esoctl template --source-templated-object template-test/push-secret.yaml --source-secret-data-file template-test/secret.yaml
data:
  token: VE9LRU4gd2FzIHRlbXBsYXRlZA==
metadata:
  creationTimestamp: null

echo -n "VE9LRU4gd2FzIHRlbXBsYXRlZA==" | base64 -d
TOKEN was templated⏎

Further options can be used to provide templates from a ConfigMap or a Secret:

bin/esoctl template --source-templated-object template-test/push-secret.yaml \
  --source-secret-data-file template-test/secret.yaml \
  --template-from-config-map template-test/template-config-map.yaml \
  --template-from-secret template-test/template-secret.yaml

Bootstrapping generator code

The bootstrap generator command can be used to create a new generator.

When running it, it will automatically:

  • Bootstrap a new generator CRD
  • Bootstrap a new generator implementation
  • Update the register file with the new generator
  • Update Cluster Generators to include the new generator
  • Update needed dependencies (go.mod, resolver file, etc)

To run, simply execute:

bin/esoctl bootstrap generator --name GeneratorName --description "A description of this generator" --package generatorname

Example

bin/esoctl bootstrap generator --name MyAwesomeGenerator --description "An awesome generator I want to add to ESO :)"

✓ Created CRD: /home/gusfcarvalho/Documents/repos/external-secrets/apis/generators/v1alpha1/types_myawesomegenerator.go
✓ Created implementation: /home/gusfcarvalho/Documents/repos/external-secrets/generators/v1/myawesomegenerator/myawesomegenerator.go
✓ Created test file: /home/gusfcarvalho/Documents/repos/external-secrets/generators/v1/myawesomegenerator/myawesomegenerator_test.go
✓ Created go.mod: /home/gusfcarvalho/Documents/repos/external-secrets/generators/v1/myawesomegenerator/go.mod
✓ Created go.sum: /home/gusfcarvalho/Documents/repos/external-secrets/generators/v1/myawesomegenerator/go.sum
✓ Updated register file: /home/gusfcarvalho/Documents/repos/external-secrets/pkg/register/generators.go
✓ Updated types_cluster.go
✓ Updated main go.mod
✓ Updated resolver file: /home/gusfcarvalho/Documents/repos/external-secrets/runtime/esutils/resolvers/generator.go
✓ Updated register.go
✓ Successfully bootstrapped generator: MyAwesomeGenerator

Next steps:
1. Review and customize: apis/generators/v1alpha1/types_myawesomegenerator.go
2. Implement the generator logic in: generators/v1/myawesomegenerator/myawesomegenerator.go
3. Run: go mod tidy
4. Run: make generate
5. Run: make manifests
6. Add tests for your generator

You should also expect the following git diff with specific changes:

diff --git a/apis/generators/v1alpha1/register.go b/apis/generators/v1alpha1/register.go
index 16c05154b..9538bcc57 100644
--- a/apis/generators/v1alpha1/register.go
+++ b/apis/generators/v1alpha1/register.go
@@ -73,6 +73,9 @@ var (
        ClusterGeneratorKind = reflect.TypeOf(ClusterGenerator{}).Name()
        // CloudsmithAccessTokenKind is the kind name for CloudsmithAccessToken resource.
        CloudsmithAccessTokenKind = reflect.TypeOf(CloudsmithAccessToken{}).Name()
+
+       // MyAwesomeGeneratorKind is the kind name for MyAwesomeGenerator resource.
+       MyAwesomeGeneratorKind = reflect.TypeOf(MyAwesomeGenerator{}).Name()
 )

 func init() {
@@ -109,4 +112,5 @@ func init() {
        SchemeBuilder.Register(&Webhook{}, &WebhookList{})
        SchemeBuilder.Register(&Grafana{}, &GrafanaList{})
        SchemeBuilder.Register(&MFA{}, &MFAList{})
+       SchemeBuilder.Register(&MyAwesomeGenerator{}, &MyAwesomeGeneratorList{})
 }
diff --git a/apis/generators/v1alpha1/types_cluster.go b/apis/generators/v1alpha1/types_cluster.go
index e212dab76..0245e8f1c 100644
--- a/apis/generators/v1alpha1/types_cluster.go
+++ b/apis/generators/v1alpha1/types_cluster.go
@@ -30,7 +30,7 @@ type ClusterGeneratorSpec struct {
 }

 // GeneratorKind represents a kind of generator.
-// +kubebuilder:validation:Enum=ACRAccessToken;CloudsmithAccessToken;ECRAuthorizationToken;Fake;GCRAccessToken;GithubAccessToken;QuayAccessToken;Password;SSHKey;STSSessionToken;UUID;VaultDynamicSecret;Webhook;Grafana
+// +kubebuilder:validation:Enum=ACRAccessToken;CloudsmithAccessToken;ECRAuthorizationToken;Fake;GCRAccessToken;GithubAccessToken;QuayAccessToken;Password;SSHKey;STSSessionToken;UUID;VaultDynamicSecret;Webhook;Grafana;MyAwesomeGenerator
 type GeneratorKind string

 const (
@@ -64,6 +64,8 @@ const (
        GeneratorKindMFA GeneratorKind = "MFA"
        // GeneratorKindCloudsmithAccessToken represents a Cloudsmith access token generator.
        GeneratorKindCloudsmithAccessToken GeneratorKind = "CloudsmithAccessToken"
+       // GeneratorKindMyAwesomeGenerator represents a myawesomegenerator generator.
+       GeneratorKindMyAwesomeGenerator GeneratorKind = "MyAwesomeGenerator"
 )

 // GeneratorSpec defines the configuration for various supported generator types.
@@ -85,6 +87,7 @@ type GeneratorSpec struct {
        WebhookSpec               *WebhookSpec               `json:"webhookSpec,omitempty"`
        GrafanaSpec               *GrafanaSpec               `json:"grafanaSpec,omitempty"`
        MFASpec                   *MFASpec                   `json:"mfaSpec,omitempty"`
+       MyAwesomeGeneratorSpec             *MyAwesomeGeneratorSpec             `json:"myawesomegeneratorSpec,omitempty"`
 }

 // ClusterGenerator represents a cluster-wide generator which can be referenced as part of `generatorRef` fields.
diff --git a/go.mod b/go.mod
index ff95a9558..c73ecb0c7 100644
--- a/go.mod
+++ b/go.mod
@@ -14,6 +14,7 @@ replace (
        github.com/external-secrets/external-secrets/generators/v1/github => ./generators/v1/github
        github.com/external-secrets/external-secrets/generators/v1/grafana => ./generators/v1/grafana
        github.com/external-secrets/external-secrets/generators/v1/mfa => ./generators/v1/mfa
+       github.com/external-secrets/external-secrets/generators/v1/myawesomegenerator => ./generators/v1/myawesomegenerator
        github.com/external-secrets/external-secrets/generators/v1/password => ./generators/v1/password
        github.com/external-secrets/external-secrets/generators/v1/quay => ./generators/v1/quay
        github.com/external-secrets/external-secrets/generators/v1/sshkey => ./generators/v1/sshkey
diff --git a/pkg/register/generators.go b/pkg/register/generators.go
index dd9ad55fb..6aafd4089 100644
--- a/pkg/register/generators.go
+++ b/pkg/register/generators.go
@@ -34,6 +34,7 @@ import (
        uuid "github.com/external-secrets/external-secrets/generators/v1/uuid"
        vaultgen "github.com/external-secrets/external-secrets/generators/v1/vault"
        webhookgen "github.com/external-secrets/external-secrets/generators/v1/webhook"
+       myawesomegenerator "github.com/external-secrets/external-secrets/generators/v1/myawesomegenerator"
 )

 func init() {
@@ -53,4 +54,5 @@ func init() {
        genv1alpha1.Register(uuid.Kind(), uuid.NewGenerator())
        genv1alpha1.Register(vaultgen.Kind(), vaultgen.NewGenerator())
        genv1alpha1.Register(webhookgen.Kind(), webhookgen.NewGenerator())
+       genv1alpha1.Register(myawesomegenerator.Kind(), myawesomegenerator.NewGenerator())
 }
diff --git a/runtime/esutils/resolvers/generator.go b/runtime/esutils/resolvers/generator.go
index 66f4b4037..938ccd6cd 100644
--- a/runtime/esutils/resolvers/generator.go
+++ b/runtime/esutils/resolvers/generator.go
@@ -302,6 +302,17 @@ func clusterGeneratorToVirtual(gen *genv1alpha1.ClusterGenerator) (client.Object
                        },
                        Spec: *gen.Spec.Generator.MFASpec,
                }, nil
+       case genv1alpha1.GeneratorKindMyAwesomeGenerator:
+               if gen.Spec.Generator.MyAwesomeGeneratorSpec == nil {
+                       return nil, fmt.Errorf("when kind is %s, MyAwesomeGeneratorSpec must be set", gen.Spec.Kind)
+               }
+               return &genv1alpha1.MyAwesomeGenerator{
+                       TypeMeta: metav1.TypeMeta{
+                               APIVersion: genv1alpha1.SchemeGroupVersion.String(),
+                               Kind:       genv1alpha1.MyAwesomeGeneratorKind,
+                       },
+                       Spec: *gen.Spec.Generator.MyAwesomeGeneratorSpec,
+               }, nil
        default:
                return nil, fmt.Errorf("unknown kind %s", gen.Spec.Kind)
        }

flags

name

Defines the generator name. Must be PascalCase.

description

Defines the generator description (added as a golang comment)

package (optional)

Defines the package name for the generator. Must be snake_case. defaults to lowercase of name