Skip to content

Commit

Permalink
[feat] Support key secret templating
Browse files Browse the repository at this point in the history
  • Loading branch information
bcm820 committed Aug 21, 2024
1 parent 40116a6 commit 8579ade
Show file tree
Hide file tree
Showing 7 changed files with 306 additions and 77 deletions.
8 changes: 8 additions & 0 deletions api/v1alpha2/linodeobjectstoragekey_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ type LinodeObjectStorageKeySpec struct {
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
// +optional
SecretType corev1.SecretType `json:"secretType,omitempty"`

// SecretDataFormat instructs the controller how to format the data stored in the secret containing access key details.
// It supports Go template syntax and interpolating the following values: .AccessKey, .SecretKey.
// If no format is supplied then a generic one is used containing the values specified.
// When SecretType is set to addons.cluster.x-k8s.io/resource-set, a .BucketEndpoint value is also available pointing to the location of the first bucket specified in BucketAccess.
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
// +optional
SecretDataFormat map[string]string `json:"secretDataFormat,omitempty"`
}

// LinodeObjectStorageKeyStatus defines the observed state of LinodeObjectStorageKey
Expand Down
7 changes: 7 additions & 0 deletions api/v1alpha2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 29 additions & 29 deletions cloud/scope/object_storage_key.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package scope

import (
"bytes"
"context"
"errors"
"fmt"
"text/template"

"github.com/go-logr/logr"
"github.com/linode/linodego"
Expand Down Expand Up @@ -97,21 +99,7 @@ func (s *ObjectStorageKeyScope) AddFinalizer(ctx context.Context) error {
return nil
}

const (
accessKeySecretNameTemplate = "%s-obj-key"

ClusterResourceSetSecretFilename = "etcd-backup.yaml"
BucketKeySecret = `kind: Secret
apiVersion: v1
metadata:
name: %s
stringData:
bucket_name: %s
bucket_region: %s
bucket_endpoint: %s
access_key: %s
secret_key: %s`
)
const accessKeySecretNameTemplate = "%s-obj-key"

// GenerateKeySecret returns a secret suitable for submission to the Kubernetes API.
// The secret is expected to contain keys for accessing the bucket, as well as owner and controller references.
Expand All @@ -120,13 +108,21 @@ func (s *ObjectStorageKeyScope) GenerateKeySecret(ctx context.Context, key *lino
return nil, errors.New("expected non-nil object storage key")
}

var secretStringData map[string]string

secretName := fmt.Sprintf(accessKeySecretNameTemplate, s.Key.Name)
secretStringData := make(map[string]string)

tmplData := map[string]string{
"AccessKey": key.AccessKey,
"SecretKey": key.SecretKey,
}

// If the desired secret is of ClusterResourceSet type, encapsulate the secret.
// Bucket details are retrieved from the first referenced LinodeObjectStorageBucket in the access key.
if s.Key.Spec.SecretType == clusteraddonsv1.ClusterResourceSetSecretType {
if len(s.Key.Spec.SecretDataFormat) == 0 {
return nil, fmt.Errorf("unable to generate %s; spec.secretDataFormat must specify resources", clusteraddonsv1.ClusterResourceSetSecretType)
}

// This should never run since the CRD has a validation marker to ensure bucketAccess has at least one item.
if len(s.Key.Spec.BucketAccess) == 0 {
return nil, fmt.Errorf("unable to generate %s; spec.bucketAccess must not be empty", clusteraddonsv1.ClusterResourceSetSecretType)
Expand All @@ -138,24 +134,28 @@ func (s *ObjectStorageKeyScope) GenerateKeySecret(ctx context.Context, key *lino
return nil, fmt.Errorf("unable to generate %s; failed to get bucket: %w", clusteraddonsv1.ClusterResourceSetSecretType, err)
}

secretStringData = map[string]string{
ClusterResourceSetSecretFilename: fmt.Sprintf(
BucketKeySecret,
secretName,
bucket.Label,
bucket.Region,
bucket.Hostname,
key.AccessKey,
key.SecretKey,
),
}
} else {
tmplData["BucketEndpoint"] = bucket.Hostname
} else if len(s.Key.Spec.SecretDataFormat) == 0 {
secretStringData = map[string]string{
"access_key": key.AccessKey,
"secret_key": key.SecretKey,
}
}

for key, tmpl := range s.Key.Spec.SecretDataFormat {
goTmpl, err := template.New(key).Parse(tmpl)
if err != nil {
return nil, fmt.Errorf("unable to generate secret; failed to parse template in secret data format for key %s: %w", key, err)
}

var output bytes.Buffer
if err := goTmpl.Execute(&output, tmplData); err != nil {
return nil, fmt.Errorf("unable to generate secret; failed to exec template in secret data format for key %s: %w", key, err)
}

secretStringData[key] = output.String()
}

secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Expand Down
Loading

0 comments on commit 8579ade

Please sign in to comment.