github.com/verrazzano/verrazzano@v1.7.1/application-operator/controllers/template/template.go (about)

     1  // Copyright (c) 2021, 2022, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package template
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"crypto/sha256"
    10  	"encoding/base64"
    11  	"errors"
    12  	"fmt"
    13  	k8score "k8s.io/api/core/v1"
    14  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    15  	"sigs.k8s.io/controller-runtime/pkg/client"
    16  	"text/template"
    17  )
    18  
    19  // Processor contains the references required to populate a golang template
    20  type Processor struct {
    21  	template string
    22  	client   client.Client
    23  }
    24  
    25  // NewProcessor creates a new template processor that can read values from kubernetes resources and provided structs
    26  func NewProcessor(client client.Client, template string) *Processor {
    27  	return &Processor{
    28  		client:   client,
    29  		template: template,
    30  	}
    31  }
    32  
    33  // get fetches a resources from the processor's client and returns the resulting map
    34  func (p *Processor) get(apiversion string, kind string, namespace string, name string) (map[string]interface{}, error) {
    35  	u := &unstructured.Unstructured{}
    36  	u.SetAPIVersion(apiversion)
    37  	u.SetKind(kind)
    38  	err := p.client.Get(context.TODO(), client.ObjectKey{Namespace: namespace, Name: name}, u)
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  	return u.Object, nil
    43  }
    44  
    45  // configmap populates template values from a kubernetes configmap
    46  func (p *Processor) configmap(namespace string, name string, key string) (string, error) {
    47  	cm := &k8score.ConfigMap{}
    48  	err := p.client.Get(context.TODO(), client.ObjectKey{Namespace: namespace, Name: name}, cm)
    49  	if err != nil {
    50  		return "", err
    51  	}
    52  	value, ok := cm.Data[key]
    53  	if !ok {
    54  		return "", errors.New("missing value for key " + key)
    55  	}
    56  
    57  	return value, nil
    58  }
    59  
    60  // secret populates template values from a kubernetes secret
    61  func (p *Processor) secret(namespace string, name string, key string) (string, error) {
    62  	secret := &k8score.Secret{}
    63  	err := p.client.Get(context.TODO(), client.ObjectKey{Namespace: namespace, Name: name}, secret)
    64  	if err != nil {
    65  		return "", err
    66  	}
    67  	value, ok := secret.Data[key]
    68  	if !ok {
    69  		return "", errors.New("missing value for key " + key)
    70  	}
    71  
    72  	return string(value), nil
    73  }
    74  
    75  // Process leverages kubernetes and the provided inputs to populate a template
    76  func (p *Processor) Process(inputs map[string]interface{}) (string, error) {
    77  	h := sha256.New()
    78  	h.Write([]byte(p.template))
    79  	n := base64.URLEncoding.EncodeToString(h.Sum(nil))
    80  	t, err := template.New(n).
    81  		Option("missingkey=error").
    82  		Funcs(template.FuncMap{
    83  			"get":       p.get,
    84  			"configmap": p.configmap,
    85  			"secret":    p.secret}).
    86  		Parse(p.template)
    87  	if err != nil {
    88  		return "", fmt.Errorf("error parsing template: %v", err)
    89  	}
    90  
    91  	var v bytes.Buffer
    92  	err = t.Execute(&v, inputs)
    93  	if err != nil {
    94  		return "", fmt.Errorf("error executing template: %v", err)
    95  	}
    96  
    97  	return v.String(), nil
    98  }