Skip to content
This repository has been archived by the owner on Mar 17, 2023. It is now read-only.

Commit

Permalink
Enhance template data + documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
adracus committed Sep 9, 2021
1 parent 9a86a71 commit 5401a41
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 29 deletions.
80 changes: 62 additions & 18 deletions api/v1alpha1/template_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,59 +21,103 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.

// TemplateSpec defines the desired state of Template
type TemplateSpec struct {
// CommonLabels are common labels that should be applied for all resulting resources of this template.
CommonLabels map[string]string `json:"commonLabels,omitempty"`

GroupKinds []metav1.GroupKind `json:"groupKinds,omitempty"`
Selector *metav1.LabelSelector `json:"selector,omitempty"`
// GroupKinds are metav1.GroupKinds that are produced by this template.
GroupKinds []metav1.GroupKind `json:"groupKinds,omitempty"`
// Selector is a metav1.LabelSelector to select resources produced by this template.
Selector *metav1.LabelSelector `json:"selector,omitempty"`

// Sources is a list of TemplateSource to draw template values from
Sources []TemplateSource `json:"sources,omitempty"`

// TemplateData holds the definition of the template.
Data TemplateData `json:"data"`

// Prune indicates whether to prune unused resources of a template.
Prune bool `json:"prune,omitempty"`
}

// TemplateSource is a source for the values of a template.
type TemplateSource struct {
Name string `json:"name"`
// Name is the name the source shall be registered with in the values.
Name string `json:"name"`
// ObjectReference is a reference to an object to serve as source.
Object *ObjectReference `json:"object,omitempty"`
}

// ObjectReference references an object in a specific api version.
type ObjectReference struct {
// APIVersion is the api version of the target object to use.
APIVersion string `json:"apiVersion"`
Kind string `json:"kind"`
Namespace string `json:"namespace,omitempty"`
Name string `json:"name"`
// Kind is the kind of the target object.
Kind string `json:"kind"`
// Namespace is the namespace the target object lives in.
Namespace string `json:"namespace,omitempty"`
// Name is the name of the target object.
Name string `json:"name"`
}

// ConfigMapKeySelector is a reference to a specific 'key' within a ConfigMap resource.
// In some instances, `key` is a required field.
type ConfigMapKeySelector struct {
// The name of the ConfigMap resource being referred to.
corev1.LocalObjectReference `json:",inline"`
// The key of the entry in the ConfigMap resource's `data` field to be used.
// Some instances of this field may be defaulted, in others it may be
// required.
// +optional
Key string `json:"key,omitempty"`
}

// DefaultConfigMapTemplateKey is the default key of the template definition in a config map.
const DefaultConfigMapTemplateKey = "template.yaml"

// TemplateData contains where the template definition should be drawn from.
type TemplateData struct {
// Inline is an inline template definition.
Inline string `json:"inline,omitempty"`
// ConfigMapRef is the reference to a config map containing the template.
// If key is not specified, it defaults to DefaultConfigMapTemplateKey.
ConfigMapRef *ConfigMapKeySelector `json:"configMapRef,omitempty"`
}

// TemplateStatus defines the observed state of Template
type TemplateStatus struct {
Conditions []TemplateCondition `json:"conditions,omitempty"`
ManagedResources []ObjectReference `json:"managedResources,omitempty"`
// Conditions is a list of TemplateCondition referring to individual state
// information of a Template.
Conditions []TemplateCondition `json:"conditions,omitempty"`
// ManagedResources are resources that are managed by this template.
ManagedResources []ObjectReference `json:"managedResources,omitempty"`
}

// TemplateConditionType is a type of a TemplateCondition.
type TemplateConditionType string

const (
// TemplateApplied indicates whether a template could be successfully applied.
TemplateApplied TemplateConditionType = "Applied"
)

// TemplateCondition is a status information of an aspect of a Template.
type TemplateCondition struct {
Type TemplateConditionType `json:"type"`
Status corev1.ConditionStatus `json:"status"`
Reason string `json:"reason"`
Message string `json:"message"`
LastUpdateTime metav1.Time `json:"lastUpdateTime"`
LastTransitionTime metav1.Time `json:"lastTransitionTime"`
ObservedGeneration int64 `json:"observedGeneration"`
// Type is the TemplateConditionType of this condition.
Type TemplateConditionType `json:"type"`
// Status reports the status of the condition.
Status corev1.ConditionStatus `json:"status"`
// Reason is a machine- and human-readable short explanation of the condition.
Reason string `json:"reason"`
// Message is a human-readable detailed explanation of the condition reason.
Message string `json:"message"`
// LastUpdateTime is the last time a condition has been updated.
LastUpdateTime metav1.Time `json:"lastUpdateTime"`
// LastTransitionTime is the last time a condition transitioned between two statuses.
LastTransitionTime metav1.Time `json:"lastTransitionTime"`
// ObservedGeneration is the observed generation for which a condition is reported.
ObservedGeneration int64 `json:"observedGeneration"`
}

//+kubebuilder:object:root=true
Expand Down
23 changes: 22 additions & 1 deletion api/v1alpha1/zz_generated.deepcopy.go

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

8 changes: 4 additions & 4 deletions charts/template-operator/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ apiVersion: v2
name: template-operator
description: A Helm chart for the template operator
type: application
version: 0.1.0
version: 0.1.1
appVersion: 0.1.0
engine: gotpl
home: https://github.com/onmetal/template-operator
sources:
- https://github.com/onmetal/template-operator
- https://github.com/onmetal/template-operator
maintainers:
- name: adracus
- name: afritzler
- name: adracus
- name: afritzler
67 changes: 63 additions & 4 deletions config/crd/bases/template.onmetal.de_templates.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,33 @@ spec:
commonLabels:
additionalProperties:
type: string
description: CommonLabels are common labels that should be applied
for all resulting resources of this template.
type: object
data:
description: TemplateData holds the definition of the template.
properties:
configMapRef:
description: ConfigMapRef is the reference to a config map containing
the template. If key is not specified, it defaults to DefaultConfigMapTemplateKey.
properties:
key:
description: The key of the entry in the ConfigMap resource's
`data` field to be used. Some instances of this field may
be defaulted, in others it may be required.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
type: object
inline:
description: Inline is an inline template definition.
type: string
type: object
groupKinds:
description: GroupKinds are metav1.GroupKinds that are produced by
this template.
items:
description: GroupKind specifies a Group and a Kind, but does not
force a version. This is useful for identifying concepts during
Expand All @@ -66,12 +86,12 @@ spec:
type: object
type: array
prune:
description: Prune indicates whether to prune unused resources of
a template.
type: boolean
selector:
description: A label selector is a label query over a set of resources.
The result of matchLabels and matchExpressions are ANDed. An empty
label selector matches all objects. A null label selector matches
no objects.
description: Selector is a metav1.LabelSelector to select resources
produced by this template.
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements.
Expand Down Expand Up @@ -115,19 +135,32 @@ spec:
type: object
type: object
sources:
description: Sources is a list of TemplateSource to draw template
values from
items:
description: TemplateSource is a source for the values of a template.
properties:
name:
description: Name is the name the source shall be registered
with in the values.
type: string
object:
description: ObjectReference is a reference to an object to
serve as source.
properties:
apiVersion:
description: APIVersion is the api version of the target
object to use.
type: string
kind:
description: Kind is the kind of the target object.
type: string
name:
description: Name is the name of the target object.
type: string
namespace:
description: Namespace is the namespace the target object
lives in.
type: string
required:
- apiVersion
Expand All @@ -145,24 +178,40 @@ spec:
description: TemplateStatus defines the observed state of Template
properties:
conditions:
description: Conditions is a list of TemplateCondition referring to
individual state information of a Template.
items:
description: TemplateCondition is a status information of an aspect
of a Template.
properties:
lastTransitionTime:
description: LastTransitionTime is the last time a condition
transitioned between two statuses.
format: date-time
type: string
lastUpdateTime:
description: LastUpdateTime is the last time a condition has
been updated.
format: date-time
type: string
message:
description: Message is a human-readable detailed explanation
of the condition reason.
type: string
observedGeneration:
description: ObservedGeneration is the observed generation for
which a condition is reported.
format: int64
type: integer
reason:
description: Reason is a machine- and human-readable short explanation
of the condition.
type: string
status:
description: Status reports the status of the condition.
type: string
type:
description: Type is the TemplateConditionType of this condition.
type: string
required:
- lastTransitionTime
Expand All @@ -175,15 +224,25 @@ spec:
type: object
type: array
managedResources:
description: ManagedResources are resources that are managed by this
template.
items:
description: ObjectReference references an object in a specific
api version.
properties:
apiVersion:
description: APIVersion is the api version of the target object
to use.
type: string
kind:
description: Kind is the kind of the target object.
type: string
name:
description: Name is the name of the target object.
type: string
namespace:
description: Namespace is the namespace the target object lives
in.
type: string
required:
- apiVersion
Expand Down
46 changes: 44 additions & 2 deletions controllers/template_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,35 @@ func (r *TemplateReconciler) resolveSources(ctx context.Context, logger logr.Log
return values, nil
}

func (r *TemplateReconciler) executeTemplate(logger logr.Logger, template *templatev1alpha1.Template, data interface{}) (io.Reader, error) {
func (r *TemplateReconciler) resolveTemplateDefinition(ctx context.Context, logger logr.Logger, template *templatev1alpha1.Template) (string, error) {
dataSpec := template.Spec.Data
switch {
case dataSpec.Inline != "":
return dataSpec.Inline, nil
case dataSpec.ConfigMapRef != nil:
configMap := &corev1.ConfigMap{}
key := client.ObjectKey{Namespace: template.Namespace, Name: dataSpec.ConfigMapRef.Name}
if err := r.Get(ctx, key, configMap); err != nil {
return "", fmt.Errorf("error getting config map %s: %w", key, err)
}

dataKey := dataSpec.ConfigMapRef.Key
if dataKey == "" {
dataKey = templatev1alpha1.DefaultConfigMapTemplateKey
}

data, ok := configMap.Data[dataKey]
if !ok || data == "" {
return "", fmt.Errorf("config map %s has no data at %s", key, dataKey)
}

return data, nil
default:
return "", fmt.Errorf("invalid data spec: no valid source supplied")
}
}

func (r *TemplateReconciler) executeTemplate(logger logr.Logger, template *templatev1alpha1.Template, templateSpec string, data interface{}) (io.Reader, error) {
logger.V(1).Info("Creating / Parsing template")
tmpl := gotemplate.New(fmt.Sprintf("%s/%s", template.Namespace, template.Name))
fMap := funcMap(tmpl)
Expand Down Expand Up @@ -832,8 +860,22 @@ func (r *TemplateReconciler) reconcile(ctx context.Context, logger logr.Logger,
return ctrl.Result{}, fmt.Errorf("could not resolve template data: %w", err)
}

logger.V(1).Info("Resolving template definition.")
templateDefinition, err := r.resolveTemplateDefinition(ctx, logger, template)
if err != nil {
if err := r.updateStatus(ctx, template,
nil,
corev1.ConditionFalse,
"CannotResolveContent",
fmt.Sprintf("Resolving the template definition failed: %v", err),
); err != nil {
logger.Error(err, "Error updating status.")
}
return ctrl.Result{}, fmt.Errorf("error resolving template definition: %w", err)
}

logger.V(1).Info("Executing template.")
rd, err := r.executeTemplate(logger, template, data)
rd, err := r.executeTemplate(logger, template, templateDefinition, data)
if err != nil {
if err := r.updateStatus(ctx, template,
nil,
Expand Down

0 comments on commit 5401a41

Please sign in to comment.