templates

package
v5.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 5, 2024 License: Apache-2.0 Imports: 28 Imported by: 0

Documentation

Index

Examples

Constants

View Source
const (
	IVSize = 16 // Size in bytes

)

Variables

View Source
var (
	ErrAESKeyNotSet          = errors.New("AESKey must be set to use this encryption mode")
	ErrInvalidAESKey         = errors.New("the AES key is invalid")
	ErrInvalidB64OfEncrypted = errors.New("the encrypted string is invalid base64")
	ErrIVNotSet              = errors.New("initialization vector must be set to use this encryption mode")
	ErrInvalidIV             = errors.New("initialization vector must be 128 bits")
	ErrInvalidPKCS7Padding   = errors.New("invalid PCKS7 padding")
	ErrMissingAPIResource    = errors.New("one or more API resources are not installed on the API server")
	ErrProtectNotEnabled     = errors.New("the protect template function is not enabled in this mode")
	ErrNewLinesNotAllowed    = errors.New("new lines are not allowed in the string passed to the toLiteral function")
	ErrInvalidContextType    = errors.New(
		"the input context must be a struct, with either string fields or map[string]string fields",
	)
	ErrMissingNamespace = errors.New(
		"the lookup of a single namespaced resource must have a namespace specified",
	)
	ErrRestrictedNamespace      = errors.New("the namespace argument is restricted")
	ErrInvalidInput             = errors.New("the input is invalid")
	ErrCacheDisabled            = client.ErrCacheDisabled
	ErrNoCacheEntry             = client.ErrNoCacheEntry
	ErrContextTransformerFailed = errors.New("the context transformer failed")
)

Functions

func HasTemplate

func HasTemplate(template []byte, startDelim string, checkForEncrypted bool) bool

HasTemplate performs a simple check for the template start delimiter or the "$ocm_encrypted" prefix (checkForEncrypted must be set to true) to indicate if the input byte slice has a template. If the startDelim argument is an empty string, the default start delimiter of "{{" will be used.

func JSONToYAML

func JSONToYAML(j []byte) ([]byte, error)

JSONToYAML converts JSON to YAML using yaml.v3. This is important since line wrapping is disabled in v3.

func UsesEncryption

func UsesEncryption(template []byte, startDelim string, stopDelim string) bool

UsesEncryption searches for templates that would generate encrypted values and returns a boolean whether one was found.

Types

type CacheCleanUpFunc

type CacheCleanUpFunc func() error

type CachingQueryAPI

type CachingQueryAPI interface {
	// Get will add an additional watch and return the watched object.
	Get(
		gvk schema.GroupVersionKind, namespace string, name string,
	) (*unstructured.Unstructured, error)
	// List will add an additional list watch and return the watched objects.
	List(
		gvk schema.GroupVersionKind, namespace string, selector labels.Selector,
	) ([]unstructured.Unstructured, error)
}

CachingQueryAPI is a limited query API that will cache results. This is used with ContextTransformers.

type ClusterScopedLookupRestrictedError

type ClusterScopedLookupRestrictedError struct {
	// contains filtered or unexported fields
}

func (ClusterScopedLookupRestrictedError) Error

type ClusterScopedObjectIdentifier

type ClusterScopedObjectIdentifier struct {
	Group string
	Kind  string
	Name  string
}

type Config

type Config struct {
	AdditionalIndentation uint
	DisabledFunctions     []string
	StartDelim            string
	StopDelim             string

	MissingAPIResourceCacheTTL time.Duration
}

Config is a struct containing configuration for the API.

- AdditionalIndentation sets the number of additional spaces to be added to the input number to the indent method. This is useful in situations when the indentation should be relative to a logical starting point in a YAML file.

- DisabledFunctions is a slice of default template function names that should be disabled.

- StartDelim customizes the start delimiter used to distinguish a template action. This defaults to "{{". If StopDelim is set, this must also be set.

- StopDelim customizes the stop delimiter used to distinguish a template action. This defaults to "}}". If StartDelim is set, this must also be set.

- MissingAPIResourceCacheTTL can be set if you want to temporarily cache an API resource is missing to avoid duplicate API queries when a CRD is missing. By default, this will not be cached. Note that this only affects when caching is enabled.

type EncryptionConfig

type EncryptionConfig struct {
	AESKey                []byte
	AESKeyFallback        []byte
	DecryptionConcurrency uint8
	DecryptionEnabled     bool
	EncryptionEnabled     bool
	InitializationVector  []byte
}

EncryptionConfig is a struct containing configuration for template encryption/decryption functionality.

- AESKey is an AES key (e.g. AES-256) to use for the "protect" template function and decrypting such values.

- AESKeyFallback is an AES key to try if the decryption fails using AESKey.

- DecryptionConcurrency is the concurrency (i.e. number of Goroutines) limit when decrypting encrypted strings. Not setting this value is the equivalent of setting this to 1, which means no concurrency.

- DecryptionEnabled enables automatic decrypting of encrypted strings. AESKey and InitializationVector must also be set if this is enabled.

- EncryptionEnabled enables the "protect" template function and "fromSecret" returns encrypted content. AESKey and InitializationVector must also be set if this is enabled.

- InitializationVector is the initialization vector (IV) used in the AES-CBC encryption/decryption. Note that it must be equal to the AES block size which is always 128 bits (16 bytes). This value must be random but does not need to be private. Its purpose is to make the same plaintext value, when encrypted with the same AES key, appear unique. When performing decryption, the IV must be the same as it was for the encryption of the data. Note that all values encrypted in the template will use this same IV, which means that duplicate plaintext values that are encrypted will yield the same encrypted value in the template.

type ResolveOptions

type ResolveOptions struct {
	ContextTransformers []func(
		queryAPI CachingQueryAPI, context interface{},
	) (transformedContext interface{}, err error)
	ClusterScopedAllowList []ClusterScopedObjectIdentifier
	EncryptionConfig
	DisableAutoCacheCleanUp bool
	InputIsYAML             bool
	LookupNamespace         string
	Watcher                 *client.ObjectIdentifier
}

ResolveOptions is a struct containing configuration for calling ResolveTemplate.

- ContextTransformers is a list of functions that can modify the input context to ResolveTemplate using the caching query API. This is useful if you want to add information about a Kubernetes object in the context and be notified when the object changes.

- ClusterScopedAllowList is a list of cluster-scoped object identifiers (group, kind, name) which are allowed to be used in "lookup" calls even when LookupNamespace is set. A wildcard value `*` may be used in any or all of the fields. The default behavior when LookupNamespace is set is to deny all cluster-scoped lookups.

- EncryptionConfig is the configuration for template encryption/decryption functionality.

- DisableAutoCacheCleanUp will not clean up stale API watches and cache entries after ResolveTemplate is called. The caller must call the CacheCleanUp function returned from ResolveTemplate when done. This is useful if you are splitting up calls to ResolveTemplate for a single template owner object.

- InputIsYAML can be set to true to indicate that the input to the template is already in YAML format and thus does not need to be converted from JSON to YAML before template processing occurs. This should be set to true when passing raw YAML directly to the template resolver.

- LookupNamespace is the namespace to restrict "lookup" template functions (e.g. fromConfigMap) to. If this is not set (i.e. an empty string), then all namespaces can be used.

- Watcher is the Kubernetes object that includes the templates. This is only used when caching is enabled.

type TemplateResolver

type TemplateResolver struct {
	// contains filtered or unexported fields
}

TemplateResolver is the API for processing templates. It's better to use the NewResolver function instead of instantiating this directly so that configuration defaults and validation are applied.

func NewResolver

func NewResolver(kubeConfig *rest.Config, config Config) (*TemplateResolver, error)

NewResolver creates a new TemplateResolver instance, which is the API for processing templates.

- kubeConfig is the rest.Config instance used to create Kubernetes clients for template processing.

- config is the Config instance for configuring optional values for template processing.

func NewResolverWithCaching

func NewResolverWithCaching(
	ctx context.Context, kubeConfig *rest.Config, config Config,
) (
	*TemplateResolver, *source.Channel, error,
)

NewResolverWithCaching creates a new caching TemplateResolver instance, which is the API for processing templates.

The caching works by adding watches to the objects and list queries used in the templates. A controller-runtime Channel is also returned to trigger reconciles on the watched object provided in ResolveTemplate when a watched object is added, updated, or removed.

  • ctx should be a cancelable context that should be canceled when you want the background goroutines involving caching to be stopped.

  • kubeConfig is the rest.Config instance used to create Kubernetes clients for template processing.

  • config is the Config instance for configuring optional values for template processing.

func NewResolverWithDynamicWatcher

func NewResolverWithDynamicWatcher(dynWatcher client.DynamicWatcher, config Config) (*TemplateResolver, error)

NewResolverWithDynamicWatcher creates a new caching TemplateResolver instance, using the provided dependency-watcher. The caller is responsible for managing the given DynamicWatcher, including starting and stopping it. The caller must start a query batch on the DynamicWatcher for the "watcher" object before calling ResolveTemplate.

- dynWatcher is an already running DynamicWatcher from kubernetes-dependency-watches.

- config is the Config instance for configuring optional values for template processing.

func (*TemplateResolver) GetFromCache

func (t *TemplateResolver) GetFromCache(
	gvk schema.GroupVersionKind, namespace string, name string,
) (*unstructured.Unstructured, error)

GetFromCache will return the object from the cache. The ErrNoCacheEntry error is returned if no template function has caused an entry to be cached.

func (*TemplateResolver) GetWatchCount

func (t *TemplateResolver) GetWatchCount() uint

GetWatchCount returns the total number of active API watch requests which can be used for metrics.

func (*TemplateResolver) ListWatchedFromCache

func (t *TemplateResolver) ListWatchedFromCache(watcher client.ObjectIdentifier) ([]unstructured.Unstructured, error)

ListWatchedFromCache will return all watched objects by the watcher in the cache. The ErrNoCacheEntry error is returned if no template function has caused an entry to be cached.

func (*TemplateResolver) ResolveTemplate

func (t *TemplateResolver) ResolveTemplate(
	tmplRaw []byte, context interface{}, options *ResolveOptions,
) (TemplateResult, error)

ResolveTemplate accepts a map marshaled as JSON or YAML. It also accepts a struct with string fields that will be made available when the template is processed. For example, if the argument is `struct{ClusterName string}{"cluster1"}`, the value `cluster1` would be available with `{{ .ClusterName }}`. This can also be `nil` if no fields should be made available.

ResolveTemplate will process any template strings in the map and return the processed map. The ErrMissingAPIResource is returned when one or more "lookup" calls referenced an API resource which isn't installed on the Kubernetes API server.

The input options contains options for template resolution. The options.Watcher field is an ObjectIdentifier that is used in caching mode and the controller-runtime integration. Set this to nil when not in caching mode. When in caching mode, watches are automatically garbage collected when a new call to ResolveTemplate no longer specifies an object or list query it used to.

This method is only concurrency safe when caching is enabled. When caching is disabled, a local cache of objects is stored just for the ResolveTemplate execution to avoid duplicate API queries. If running this method concurrently with caching disabled, you may get some items from the temporary cache while others will be from API queries.

Example
policyYAML := `
---
apiVersion: policy.open-cluster-management.io/v1
kind: ConfigurationPolicy
metadata:
  name: demo-sampleapp-config
  namespace: sampleapp
spec:
  remediationAction: enforce
  namespaceSelector:
    exclude:
    - kube-*
    include:
    - default
  object-templates:
  - complianceType: musthave
    objectDefinition:
      kind: ConfigMap
      apiVersion: v1
      metadata:
        name: demo-sampleapp-config
        namespace: test
      data:
        message: '{{ "VGVtcGxhdGVzIHJvY2sh" | base64dec }}'
        b64-cluster-name: '{{ .ClusterName | base64enc }}'
    severity: high
`

policyJSON, err := yamlToJSON([]byte(policyYAML))
if err != nil {
	fmt.Fprintf(os.Stderr, "Failed to convert the policy YAML to JSON: %v\n", err)
	panic(err)
}

resolver, err := NewResolver(k8sConfig, Config{})
if err != nil {
	fmt.Fprintf(os.Stderr, "Failed to instantiate the templatesResolver struct: %v\n", err)
	panic(err)
}

templateContext := struct{ ClusterName string }{ClusterName: "cluster0001"}

tmplResult, err := resolver.ResolveTemplate(policyJSON, templateContext, nil)
policyResolvedJSON := tmplResult.ResolvedJSON

if err != nil {
	fmt.Fprintf(os.Stderr, "Failed to process the policy YAML: %v\n", err)
	panic(err)
}

var policyResolved interface{}
err = yaml.Unmarshal(policyResolvedJSON, &policyResolved)

objTmpls := policyResolved.(map[string]interface{})["spec"].(map[string]interface{})["object-templates"]
objDef := objTmpls.([]interface{})[0].(map[string]interface{})["objectDefinition"]

data, ok := objDef.(map[string]interface{})["data"].(map[string]interface{})
if !ok {
	fmt.Fprintf(os.Stderr, "Failed to process the policy YAML: %v\n", err)
	panic(err)
}

message, ok := data["message"].(string)
if !ok {
	fmt.Fprintf(os.Stderr, "Failed to process the policy YAML: %v\n", err)
	panic(err)
}

b64ClusterName, ok := data["b64-cluster-name"].(string)
if !ok {
	fmt.Fprintf(os.Stderr, "Failed to process the policy YAML: %v\n", err)
	panic(err)
}

fmt.Println(message)
fmt.Println(b64ClusterName)
Output:

Templates rock!
Y2x1c3RlcjAwMDE=

func (*TemplateResolver) UncacheWatcher

func (t *TemplateResolver) UncacheWatcher(watcher client.ObjectIdentifier) error

UncacheWatcher will clear the watcher from the cache and remove all associated API watches.

type TemplateResult

type TemplateResult struct {
	ResolvedJSON []byte
	CacheCleanUp CacheCleanUpFunc
	// HasSensitiveData is true if a template references a secret or decrypts an encrypted value.
	HasSensitiveData bool
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL