Skip to content

Commit

Permalink
KEP31: Template Support for Namespace Manifest (#1535)
Browse files Browse the repository at this point in the history
Co-authored-by: Andreas Neumann <aneumann@mesosphere.com>
Signed-off-by: Ken Sipe <kensipe@gmail.com>
  • Loading branch information
kensipe and ANeumann82 committed May 26, 2020
1 parent cdbbade commit f3e6f9d
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 21 deletions.
75 changes: 75 additions & 0 deletions pkg/engine/renderer/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"k8s.io/apimachinery/pkg/types"

"github.com/kudobuilder/kudo/pkg/engine"
"github.com/kudobuilder/kudo/pkg/kudoctl/packages"
)

// Metadata contains Metadata along with specific fields associated with current plan
Expand All @@ -28,6 +29,9 @@ type Engine struct {
FuncMap template.FuncMap
}

// VariableMap is the map of variables which are used in templates like map["OperatorName"]
type VariableMap map[string]interface{}

// New creates an engine with a default function map, using a modified Sprig func map. Because these
// templates are rendered by the operator, we delete any functions that potentially access the environment
// the controller is running in.
Expand Down Expand Up @@ -72,3 +76,74 @@ func (e *Engine) Render(tplName string, tpl string, vals map[string]interface{})

return buf.String(), nil
}

// NewVariableMap creates variable map necessary for template rendering
// it uses a builder pattern to create the desired map of variables
// for a map of default values `renderer.NewVariableMap().WithDefaults()`
// as a builder pattern, when chaining latter methods in the chain take precedence
// `renderer.NewVariableMap().WithDefaults().WithResource(pkg.Resources)` will have default values overwritten with resource data
func NewVariableMap() VariableMap {
return make(map[string]interface{})
}

// WithDefaults defines variables which are potentially required by any operator. By defaulting to this map,
// all templates should pass, even if values are not expected.
func (m VariableMap) WithDefaults() VariableMap {
m.WithInstance("OperatorName", "Name", "Namespace", "AppVersion", "OperatorVersion")
m["PlanName"] = "PlanName"
m["PhaseName"] = "PhaseName"
m["StepName"] = "StepName"
return m
}

// WithInstance provides a convince for add instance information.
func (m VariableMap) WithInstance(operatorName, instanceName, namespace, appVersion, operatorVersion string) VariableMap {
m["OperatorName"] = operatorName
m["Name"] = instanceName
m["Namespace"] = namespace
m["AppVersion"] = appVersion
m["OperatorVersion"] = operatorVersion

return m

}

// WithMetadata overrides the map with metadata data
func (m VariableMap) WithMetadata(meta Metadata) VariableMap {
m["OperatorName"] = meta.OperatorName
m["Name"] = meta.InstanceName
m["Namespace"] = meta.InstanceNamespace
m["AppVersion"] = meta.AppVersion
m["OperatorVersion"] = meta.OperatorVersion
m["PlanName"] = meta.PlanName
m["PhaseName"] = meta.PhaseName
m["StepName"] = meta.StepName
m["AppVersion"] = meta.AppVersion
return m
}

// WithResource overrides map with resource data
func (m VariableMap) WithResource(resources *packages.Resources) VariableMap {
m["OperatorName"] = resources.Operator.Name
m["AppVersion"] = resources.OperatorVersion.Spec.AppVersion
m["OperatorVersion"] = resources.OperatorVersion.Spec.Version
return m
}

// WithParameters overrides the map with parameter map which uses `interface{}` values
func (m VariableMap) WithParameters(parameters map[string]interface{}) VariableMap {
m["Params"] = parameters
return m
}

// WithParameterStrings overrides the map with parameter map which uses `string values
func (m VariableMap) WithParameterStrings(parameters map[string]string) VariableMap {
m["Params"] = parameters
return m
}

// WithPipes overrides the map with a pipe map
func (m VariableMap) WithPipes(pipes map[string]string) VariableMap {
m["Pipes"] = pipes
return m
}
15 changes: 5 additions & 10 deletions pkg/engine/task/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,11 @@ import (

// render method takes resource names and Instance parameters and then renders passed templates using kudo engine.
func render(resourceNames []string, ctx Context) (map[string]string, error) {
configs := make(map[string]interface{})
configs["OperatorName"] = ctx.Meta.OperatorName
configs["Name"] = ctx.Meta.InstanceName
configs["Namespace"] = ctx.Meta.InstanceNamespace
configs["Params"] = ctx.Parameters
configs["Pipes"] = ctx.Pipes
configs["PlanName"] = ctx.Meta.PlanName
configs["PhaseName"] = ctx.Meta.PhaseName
configs["StepName"] = ctx.Meta.StepName
configs["AppVersion"] = ctx.Meta.AppVersion

configs := renderer.NewVariableMap().
WithMetadata(ctx.Meta).
WithParameters(ctx.Parameters).
WithPipes(ctx.Pipes)

resources := map[string]string{}
engine := renderer.New()
Expand Down
14 changes: 4 additions & 10 deletions pkg/kudoctl/packages/verifier/template/verify_render.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,10 @@ func templateCompilable(pf *packages.Files) verifier.Result {
return res
}

configs := make(map[string]interface{})
configs["OperatorName"] = "OperatorName"
configs["Name"] = "Name"
configs["Namespace"] = "Namespace"
configs["Params"] = params
configs["Pipes"] = pipes
configs["PlanName"] = "PlanName"
configs["PhaseName"] = "PhaseName"
configs["StepName"] = "StepName"
configs["AppVersion"] = "AppVersion"
configs := renderer.NewVariableMap().
WithDefaults().
WithParameters(params).
WithPipes(pipes)

engine := renderer.New()
for k, v := range pf.Templates {
Expand Down
21 changes: 20 additions & 1 deletion pkg/kudoctl/util/kudo/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
pollwait "k8s.io/apimachinery/pkg/util/wait"

"github.com/kudobuilder/kudo/pkg/apis/kudo/v1beta1"
"github.com/kudobuilder/kudo/pkg/engine/renderer"
"github.com/kudobuilder/kudo/pkg/kudoctl/clog"
"github.com/kudobuilder/kudo/pkg/kudoctl/packages"
)
Expand All @@ -27,7 +28,12 @@ func InstallPackage(kc *Client, resources *packages.Resources, skipInstance bool
var manifest string = ""
if resources.Operator.Spec.NamespaceManifest != "" {
clog.V(3).Printf("creating namespace with manifest named: %q", resources.Operator.Spec.NamespaceManifest)
manifest = resources.OperatorVersion.Spec.Templates[resources.Operator.Spec.NamespaceManifest]
template := resources.OperatorVersion.Spec.Templates[resources.Operator.Spec.NamespaceManifest]
var err error
manifest, err = render("namespace", template, resources, instanceName, namespace, parameters)
if err != nil {
return fmt.Errorf("failed to render namespace manifest %s: %w", resources.Operator.Spec.NamespaceManifest, err)
}
}
err := kc.CreateNamespace(namespace, manifest)
if err != nil {
Expand Down Expand Up @@ -145,3 +151,16 @@ func versionExists(version string, versions []string) bool {

return false
}

func render(name, manifest string, resources *packages.Resources, instanceName, namespace string, parameters map[string]string) (string, error) {
configs := renderer.NewVariableMap().
WithInstance("", instanceName, namespace, "", "").
WithResource(resources).
WithParameterStrings(parameters)
engine := renderer.New()
rendered, err := engine.Render(name, manifest, configs)
if err != nil {
return "", err
}
return rendered, nil
}
53 changes: 53 additions & 0 deletions pkg/kudoctl/util/kudo/install_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package kudo

import (
"flag"
"fmt"
"io/ioutil"
"path/filepath"
"testing"

tassert "github.com/stretchr/testify/assert"
"gotest.tools/assert"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -17,6 +21,8 @@ import (
"github.com/kudobuilder/kudo/pkg/util/convert"
)

var update = flag.Bool("update", false, "update .golden files")

func Test_InstallPackage(t *testing.T) {
resources := packages.Resources{
Operator: &v1beta1.Operator{
Expand Down Expand Up @@ -98,3 +104,50 @@ func Test_InstallPackage(t *testing.T) {
}
}
}

func TestNamespaceManifestRendering(t *testing.T) {

namespaceFile := "testdata/namespace.yaml"

ns, err := ioutil.ReadFile(namespaceFile)
tassert.NoError(t, err)

params := make(map[string]string)
params["foo"] = "bar-param"

rendered, err := render("namespace", string(ns), testResources(), "foo-bar", "namespace-name", params)
tassert.NoError(t, err)

file := "rendered-namespace.yaml"
gf := filepath.Join("testdata", file+".golden")

if *update {
t.Log("update golden file")
if err := ioutil.WriteFile(gf, []byte(rendered), 0644); err != nil {
t.Fatalf("failed to update golden file: %s", err)
}
}

golden, err := ioutil.ReadFile(gf)
if err != nil {
t.Fatalf("failed reading .golden: %s", err)
}

assert.Equal(t, string(golden), rendered, "for golden file: %s", gf)
}

func testResources() *packages.Resources {
result := &packages.Resources{
Operator: &v1beta1.Operator{
ObjectMeta: metav1.ObjectMeta{Name: "InstanceName"},
},
OperatorVersion: &v1beta1.OperatorVersion{
Spec: v1beta1.OperatorVersionSpec{
AppVersion: "1.0",
Version: "2.0",
},
},
Instance: nil,
}
return result
}
10 changes: 10 additions & 0 deletions pkg/kudoctl/util/kudo/testdata/namespace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: v1
kind: Namespace
metadata:
labels:
ca.istio.io/override: "true"
istio-injection: enabled
katib-metricscollector-injection: enabled
kudo.dev/instance-name: {{ .Name }}
kudo.dev/app-version: {{ .AppVersion }}
kudo.dev/param-foo: {{ .Params.foo }}
10 changes: 10 additions & 0 deletions pkg/kudoctl/util/kudo/testdata/rendered-namespace.yaml.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: v1
kind: Namespace
metadata:
labels:
ca.istio.io/override: "true"
istio-injection: enabled
katib-metricscollector-injection: enabled
kudo.dev/instance-name: foo-bar
kudo.dev/app-version: 1.0
kudo.dev/param-foo: bar-param

0 comments on commit f3e6f9d

Please sign in to comment.