Browse Source

New Generator for UUIDs (#3296)

* feat(generator/uuid): initial version

Signed-off-by: Alexander Schaber <a.schaber@cuegee.com>

* fix(generator/uuid): rename symbols in compliance with lint

Signed-off-by: Alexander Schaber <a.schaber@cuegee.com>

* fix(generator/uuid): rename unused vars to `_` to fix lint

Signed-off-by: Alexander Schaber <a.schaber@cuegee.com>

* docs(generator/uuid): initial documentation for uuid generator

Signed-off-by: Alexander Schaber <a.schaber@cuegee.com>

---------

Signed-off-by: Alexander Schaber <a.schaber@cuegee.com>
Alexander Schaber 1 year ago
parent
commit
f73187dabb

+ 46 - 0
apis/generators/v1alpha1/generator_uuid.go

@@ -0,0 +1,46 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// UUIDSpec controls the behavior of the uuid generator.
+type UUIDSpec struct {
+}
+
+// Password generates a random password based on the
+// configuration parameters in spec.
+// You can specify the length, characterset and other attributes.
+// +kubebuilder:object:root=true
+// +kubebuilder:storageversion
+// +kubebuilder:subresource:status
+// +kubebuilder:resource:scope=Namespaced,categories={password},shortName=password
+type UUID struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	Spec UUIDSpec `json:"spec,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+
+// UUIDList contains a list of ExternalSecret resources.
+type UUIDList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []Password `json:"items"`
+}

+ 8 - 0
apis/generators/v1alpha1/register.go

@@ -100,6 +100,14 @@ var (
 	GithubAccessTokenGroupVersionKind = SchemeGroupVersion.WithKind(GithubAccessTokenKind)
 )
 
+// Uuid type metadata.
+var (
+	UUIDKind             = reflect.TypeOf(UUID{}).Name()
+	UUIDGroupKind        = schema.GroupKind{Group: Group, Kind: UUIDKind}.String()
+	UUIDKindAPIVersion   = UUIDKind + "." + SchemeGroupVersion.String()
+	UUIDGroupVersionKind = SchemeGroupVersion.WithKind(UUIDKind)
+)
+
 func init() {
 	SchemeBuilder.Register(&ECRAuthorizationToken{}, &ECRAuthorizationToken{})
 	SchemeBuilder.Register(&GCRAccessToken{}, &GCRAccessTokenList{})

+ 73 - 0
apis/generators/v1alpha1/zz_generated.deepcopy.go

@@ -776,6 +776,79 @@ func (in *SecretKeySelector) DeepCopy() *SecretKeySelector {
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *UUID) DeepCopyInto(out *UUID) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	out.Spec = in.Spec
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UUID.
+func (in *UUID) DeepCopy() *UUID {
+	if in == nil {
+		return nil
+	}
+	out := new(UUID)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *UUID) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *UUIDList) DeepCopyInto(out *UUIDList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]Password, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UUIDList.
+func (in *UUIDList) DeepCopy() *UUIDList {
+	if in == nil {
+		return nil
+	}
+	out := new(UUIDList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *UUIDList) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *UUIDSpec) DeepCopyInto(out *UUIDSpec) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UUIDSpec.
+func (in *UUIDSpec) DeepCopy() *UUIDSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(UUIDSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *VaultDynamicSecret) DeepCopyInto(out *VaultDynamicSecret) {
 	*out = *in
 	out.TypeMeta = in.TypeMeta

+ 52 - 0
config/crds/bases/generators.external-secrets.io_uuids.yaml

@@ -0,0 +1,52 @@
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.16.2
+  name: uuids.generators.external-secrets.io
+spec:
+  group: generators.external-secrets.io
+  names:
+    categories:
+    - password
+    kind: UUID
+    listKind: UUIDList
+    plural: uuids
+    shortNames:
+    - password
+    singular: uuid
+  scope: Namespaced
+  versions:
+  - name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: |-
+          Password generates a random password based on the
+          configuration parameters in spec.
+          You can specify the length, characterset and other attributes.
+        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:
+            description: UUIDSpec controls the behavior of the uuid generator.
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}

+ 1 - 0
config/crds/bases/kustomization.yaml

@@ -13,5 +13,6 @@ resources:
   - generators.external-secrets.io_gcraccesstokens.yaml
   - generators.external-secrets.io_githubaccesstokens.yaml
   - generators.external-secrets.io_passwords.yaml
+  - generators.external-secrets.io_uuids.yaml
   - generators.external-secrets.io_vaultdynamicsecrets.yaml
   - generators.external-secrets.io_webhooks.yaml

+ 63 - 0
deploy/crds/bundle.yaml

@@ -11802,6 +11802,69 @@ kind: CustomResourceDefinition
 metadata:
   annotations:
     controller-gen.kubebuilder.io/version: v0.16.2
+  name: uuids.generators.external-secrets.io
+spec:
+  group: generators.external-secrets.io
+  names:
+    categories:
+      - password
+    kind: UUID
+    listKind: UUIDList
+    plural: uuids
+    shortNames:
+      - password
+    singular: uuid
+  scope: Namespaced
+  versions:
+    - name: v1alpha1
+      schema:
+        openAPIV3Schema:
+          description: |-
+            Password generates a random password based on the
+            configuration parameters in spec.
+            You can specify the length, characterset and other attributes.
+          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:
+              description: UUIDSpec controls the behavior of the uuid generator.
+              type: object
+          type: object
+      served: true
+      storage: true
+      subresources:
+        status: {}
+  conversion:
+    strategy: Webhook
+    webhook:
+      conversionReviewVersions:
+        - v1
+      clientConfig:
+        service:
+          name: kubernetes
+          namespace: default
+          path: /convert
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.16.2
   labels:
     external-secrets.io/component: controller
   name: vaultdynamicsecrets.generators.external-secrets.io

+ 35 - 0
docs/api/generator/uuid.md

@@ -0,0 +1,35 @@
+The UUID generator provides random UUIDs that you can feed into your applications. A UUID (Universally Unique Identifier) is a 128-bit label used for information in computer systems. Please see below for the format in use.
+
+## Output Keys and Values
+
+| Key  | Description        |
+| ---- | ------------------ |
+| uuid | the generated UUID |
+
+## Parameters
+
+The UUID generator does not require any additional parameters.
+
+## Example Manifest
+
+```yaml
+{ % include 'generator-uuid.yaml' % }
+```
+
+Example `ExternalSecret` that references the UUID generator:
+
+```yaml
+{ % include 'generator-uuid-example.yaml' % }
+```
+
+Which will generate a `Kind=Secret` with a key called 'uuid' that may look like:
+
+```
+EA111697-E7D0-452C-A24C-8E396947E865
+```
+
+With default values you would get something like:
+
+```
+4BEE258F-64C9-4755-92DC-AFF76451471B
+```

+ 14 - 0
docs/snippets/generator-uuid-example.yaml

@@ -0,0 +1,14 @@
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+  name: "uuid"
+spec:
+  refreshInterval: "30m"
+  target:
+    name: uuid-secret
+  dataFrom:
+    - sourceRef:
+        generatorRef:
+          apiVersion: generators.external-secrets.io/v1alpha1
+          kind: Uuid
+          name: "my-uuid"

+ 5 - 0
docs/snippets/generator-uuid.yaml

@@ -0,0 +1,5 @@
+apiVersion: generators.external-secrets.io/v1alpha1
+kind: Uuid
+metadata:
+  name: my-uuid
+spec: {}

+ 1 - 0
hack/api-docs/mkdocs.yml

@@ -63,6 +63,7 @@ nav:
       - Fake: api/generator/fake.md
       - Webhook: api/generator/webhook.md
       - Github: api/generator/github.md
+      - UUID: api/generator/uuid.md
     - Reference Docs:
       - API specification: api/spec.md
       - Controller Options: api/controller-options.md

+ 56 - 0
pkg/generator/uuid/uuid.go

@@ -0,0 +1,56 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package uuid
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/google/uuid"
+	apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
+)
+
+type Generator struct{}
+
+type generateFunc func() (string, error)
+
+func (g *Generator) Generate(_ context.Context, jsonSpec *apiextensions.JSON, _ client.Client, _ string) (map[string][]byte, error) {
+	return g.generate(
+		jsonSpec,
+		generateUUID,
+	)
+}
+
+func (g *Generator) generate(_ *apiextensions.JSON, uuidGen generateFunc) (map[string][]byte, error) {
+	uuid, err := uuidGen()
+	if err != nil {
+		return nil, fmt.Errorf("unable to generate UUID: %w", err)
+	}
+	return map[string][]byte{
+		"uuid": []byte(uuid),
+	}, nil
+}
+
+func generateUUID() (string, error) {
+	uuid := uuid.New()
+	return uuid.String(), nil
+}
+
+func init() {
+	genv1alpha1.Register(genv1alpha1.UUIDKind, &Generator{})
+}

+ 63 - 0
pkg/generator/uuid/uuid_test.go

@@ -0,0 +1,63 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package uuid
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+)
+
+func TestGenerate(t *testing.T) {
+	type args struct {
+		jsonSpec *apiextensions.JSON
+	}
+	tests := []struct {
+		name    string
+		g       *Generator
+		args    args
+		wantErr bool
+	}{
+		{
+			name: "generate UUID successfully",
+			args: args{
+				jsonSpec: &apiextensions.JSON{Raw: []byte(`{}`)},
+			},
+			wantErr: false,
+		},
+		{
+			name: "no json spec should not result in error",
+			args: args{
+				jsonSpec: nil,
+			},
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			g := &Generator{}
+			got, err := g.generate(tt.args.jsonSpec, generateUUID)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("Generator.Generate() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if err == nil {
+				// Basic validation that the generated string looks like a UUID
+				assert.Regexp(t, `[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}`, string(got["uuid"]), "Generated string must be a valid UUID")
+			}
+		})
+	}
+}