[templates] extract some rendering code into a package

pull/4358/head
Mike Lundy 7 years ago
parent 67de9f2be4
commit 4139a00e17

@ -37,8 +37,8 @@ import (
"k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/getter"
"k8s.io/helm/pkg/helm" "k8s.io/helm/pkg/helm"
"k8s.io/helm/pkg/kube" "k8s.io/helm/pkg/kube"
"k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/proto/hapi/release" "k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/renderutil"
"k8s.io/helm/pkg/repo" "k8s.io/helm/pkg/repo"
"k8s.io/helm/pkg/strvals" "k8s.io/helm/pkg/strvals"
) )
@ -254,7 +254,7 @@ func (i *installCmd) run() error {
// If checkDependencies returns an error, we have unfulfilled dependencies. // If checkDependencies returns an error, we have unfulfilled dependencies.
// As of Helm 2.4.0, this is treated as a stopping condition: // As of Helm 2.4.0, this is treated as a stopping condition:
// https://github.com/kubernetes/helm/issues/2209 // https://github.com/kubernetes/helm/issues/2209
if err := checkDependencies(chartRequested, req); err != nil { if err := renderutil.CheckDependencies(chartRequested, req); err != nil {
if i.depUp { if i.depUp {
man := &downloader.Manager{ man := &downloader.Manager{
Out: i.out, Out: i.out,
@ -515,29 +515,6 @@ func defaultNamespace() string {
return "default" return "default"
} }
func checkDependencies(ch *chart.Chart, reqs *chartutil.Requirements) error {
missing := []string{}
deps := ch.GetDependencies()
for _, r := range reqs.Dependencies {
found := false
for _, d := range deps {
if d.Metadata.Name == r.Name {
found = true
break
}
}
if !found {
missing = append(missing, r.Name)
}
}
if len(missing) > 0 {
return fmt.Errorf("found in requirements.yaml, but missing in charts/ directory: %s", strings.Join(missing, ", "))
}
return nil
}
//readFile load a file from the local directory or a remote file with a url. //readFile load a file from the local directory or a remote file with a url.
func readFile(filePath, CertFile, KeyFile, CAFile string) ([]byte, error) { func readFile(filePath, CertFile, KeyFile, CAFile string) ([]byte, error) {
u, _ := url.Parse(filePath) u, _ := url.Parse(filePath)

@ -35,6 +35,7 @@ import (
"k8s.io/helm/pkg/helm/helmpath" "k8s.io/helm/pkg/helm/helmpath"
"k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/provenance" "k8s.io/helm/pkg/provenance"
"k8s.io/helm/pkg/renderutil"
"k8s.io/helm/pkg/repo" "k8s.io/helm/pkg/repo"
) )
@ -151,7 +152,7 @@ func (p *packageCmd) run() error {
} }
if reqs, err := chartutil.LoadRequirements(ch); err == nil { if reqs, err := chartutil.LoadRequirements(ch); err == nil {
if err := checkDependencies(ch, reqs); err != nil { if err := renderutil.CheckDependencies(ch, reqs); err != nil {
return err return err
} }
} else { } else {

@ -27,17 +27,15 @@ import (
"strings" "strings"
"time" "time"
"github.com/Masterminds/semver"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/engine" "k8s.io/helm/pkg/manifest"
"k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/proto/hapi/release" "k8s.io/helm/pkg/proto/hapi/release"
util "k8s.io/helm/pkg/releaseutil" "k8s.io/helm/pkg/renderutil"
"k8s.io/helm/pkg/tiller" "k8s.io/helm/pkg/tiller"
"k8s.io/helm/pkg/timeconv" "k8s.io/helm/pkg/timeconv"
tversion "k8s.io/helm/pkg/version"
) )
const defaultDirectoryPermission = 0755 const defaultDirectoryPermission = 0755
@ -154,69 +152,21 @@ func (t *templateCmd) run(cmd *cobra.Command, args []string) error {
return prettyError(err) return prettyError(err)
} }
if req, err := chartutil.LoadRequirements(c); err == nil { renderOpts := renderutil.Options{
if err := checkDependencies(c, req); err != nil { ReleaseOptions: chartutil.ReleaseOptions{
return prettyError(err) Name: t.releaseName,
} IsInstall: !t.releaseIsUpgrade,
} else if err != chartutil.ErrRequirementsNotFound { IsUpgrade: t.releaseIsUpgrade,
return fmt.Errorf("cannot load requirements: %v", err) Time: timeconv.Now(),
} Namespace: t.namespace,
options := chartutil.ReleaseOptions{ },
Name: t.releaseName, KubeVersion: t.kubeVersion,
IsInstall: !t.releaseIsUpgrade,
IsUpgrade: t.releaseIsUpgrade,
Time: timeconv.Now(),
Namespace: t.namespace,
}
err = chartutil.ProcessRequirementsEnabled(c, config)
if err != nil {
return err
}
err = chartutil.ProcessRequirementsImportValues(c)
if err != nil {
return err
}
// Set up engine.
renderer := engine.New()
caps := &chartutil.Capabilities{
APIVersions: chartutil.DefaultVersionSet,
KubeVersion: chartutil.DefaultKubeVersion,
TillerVersion: tversion.GetVersionProto(),
}
// kubernetes version
kv, err := semver.NewVersion(t.kubeVersion)
if err != nil {
return fmt.Errorf("could not parse a kubernetes version: %v", err)
}
caps.KubeVersion.Major = fmt.Sprint(kv.Major())
caps.KubeVersion.Minor = fmt.Sprint(kv.Minor())
caps.KubeVersion.GitVersion = fmt.Sprintf("v%d.%d.0", kv.Major(), kv.Minor())
vals, err := chartutil.ToRenderValuesCaps(c, config, options, caps)
if err != nil {
return err
} }
out, err := renderer.Render(c, vals) renderedTemplates, err := renderutil.Render(c, config, renderOpts)
listManifests := []tiller.Manifest{}
if err != nil { if err != nil {
return err return err
} }
// extract kind and name
re := regexp.MustCompile("kind:(.*)\n")
for k, v := range out {
match := re.FindStringSubmatch(v)
h := "Unknown"
if len(match) == 2 {
h = strings.TrimSpace(match[1])
}
m := tiller.Manifest{Name: k, Content: v, Head: &util.SimpleHead{Kind: h}}
listManifests = append(listManifests, m)
}
if settings.Debug { if settings.Debug {
rel := &release.Release{ rel := &release.Release{
@ -230,7 +180,8 @@ func (t *templateCmd) run(cmd *cobra.Command, args []string) error {
printRelease(os.Stdout, rel) printRelease(os.Stdout, rel)
} }
var manifestsToRender []tiller.Manifest listManifests := manifest.SplitManifests(renderedTemplates)
var manifestsToRender []manifest.Manifest
// if we have a list of files to render, then check that each of the // if we have a list of files to render, then check that each of the
// provided files exists in the chart. // provided files exists in the chart.

@ -25,6 +25,7 @@ import (
"k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/helm" "k8s.io/helm/pkg/helm"
"k8s.io/helm/pkg/renderutil"
"k8s.io/helm/pkg/storage/driver" "k8s.io/helm/pkg/storage/driver"
) )
@ -211,7 +212,7 @@ func (u *upgradeCmd) run() error {
// Check chart requirements to make sure all dependencies are present in /charts // Check chart requirements to make sure all dependencies are present in /charts
if ch, err := chartutil.Load(chartPath); err == nil { if ch, err := chartutil.Load(chartPath); err == nil {
if req, err := chartutil.LoadRequirements(ch); err == nil { if req, err := chartutil.LoadRequirements(ch); err == nil {
if err := checkDependencies(ch, req); err != nil { if err := renderutil.CheckDependencies(ch, req); err != nil {
return err return err
} }
} else if err != chartutil.ErrRequirementsNotFound { } else if err != chartutil.ErrRequirementsNotFound {

@ -0,0 +1,43 @@
/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package manifest
import (
"regexp"
"strings"
"k8s.io/helm/pkg/releaseutil"
)
// SplitManifests takes a map of rendered templates and splits them into the
// detected manifests.
func SplitManifests(templates map[string]string) []Manifest {
var listManifests []Manifest
// extract kind and name
re := regexp.MustCompile("kind:(.*)\n")
for k, v := range templates {
match := re.FindStringSubmatch(v)
h := "Unknown"
if len(match) == 2 {
h = strings.TrimSpace(match[1])
}
m := Manifest{Name: k, Content: v, Head: &releaseutil.SimpleHead{Kind: h}}
listManifests = append(listManifests, m)
}
return listManifests
}

@ -0,0 +1,50 @@
/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package renderutil
import (
"fmt"
"strings"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/proto/hapi/chart"
)
// CheckDependencies will do a simple dependency check on the chart for local
// rendering
func CheckDependencies(ch *chart.Chart, reqs *chartutil.Requirements) error {
missing := []string{}
deps := ch.GetDependencies()
for _, r := range reqs.Dependencies {
found := false
for _, d := range deps {
if d.Metadata.Name == r.Name {
found = true
break
}
}
if !found {
missing = append(missing, r.Name)
}
}
if len(missing) > 0 {
return fmt.Errorf("found in requirements.yaml, but missing in charts/ directory: %s", strings.Join(missing, ", "))
}
return nil
}

@ -0,0 +1,24 @@
/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*Package renderutil contains tools related to the local rendering of charts.
Local rendering means rendering without the tiller; this is generally used for
local debugging and testing (see the `helm template` command for examples of
use). This package will not render charts exactly the same way as the tiller
will, but will be generally close enough for local debug purposes.
*/
package renderutil // import "k8s.io/helm/pkg/renderutil"

@ -0,0 +1,88 @@
/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package renderutil
import (
"fmt"
"github.com/Masterminds/semver"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/engine"
"k8s.io/helm/pkg/proto/hapi/chart"
tversion "k8s.io/helm/pkg/version"
)
// Options are options for this simple local render
type Options struct {
ReleaseOptions chartutil.ReleaseOptions
KubeVersion string
}
// Render chart templates locally and display the output.
// This does not require the Tiller. Any values that would normally be
// looked up or retrieved in-cluster will be faked locally. Additionally, none
// of the server-side testing of chart validity (e.g. whether an API is supported)
// is done.
//
// Note: a `nil` config passed here means "ignore the chart's default values";
// if you want the normal behavior of merging the defaults with the new config,
// you should pass `&chart.Config{Raw: "{}"},
func Render(c *chart.Chart, config *chart.Config, opts Options) (map[string]string, error) {
if req, err := chartutil.LoadRequirements(c); err == nil {
if err := CheckDependencies(c, req); err != nil {
return nil, err
}
} else if err != chartutil.ErrRequirementsNotFound {
return nil, fmt.Errorf("cannot load requirements: %v", err)
}
err := chartutil.ProcessRequirementsEnabled(c, config)
if err != nil {
return nil, err
}
err = chartutil.ProcessRequirementsImportValues(c)
if err != nil {
return nil, err
}
// Set up engine.
renderer := engine.New()
caps := &chartutil.Capabilities{
APIVersions: chartutil.DefaultVersionSet,
KubeVersion: chartutil.DefaultKubeVersion,
TillerVersion: tversion.GetVersionProto(),
}
if opts.KubeVersion != "" {
kv, verErr := semver.NewVersion(opts.KubeVersion)
if verErr != nil {
return nil, fmt.Errorf("could not parse a kubernetes version: %v", verErr)
}
caps.KubeVersion.Major = fmt.Sprint(kv.Major())
caps.KubeVersion.Minor = fmt.Sprint(kv.Minor())
caps.KubeVersion.GitVersion = fmt.Sprintf("v%d.%d.0", kv.Major(), kv.Minor())
}
vals, err := chartutil.ToRenderValuesCaps(c, config, opts.ReleaseOptions, caps)
if err != nil {
return nil, err
}
return renderer.Render(c, vals)
}

@ -0,0 +1,153 @@
/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package renderutil
import (
"testing"
"github.com/stretchr/testify/require"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/proto/hapi/chart"
)
const cmTemplate = `kind: ConfigMap
apiVersion: v1
metadata:
name: example
data:
Chart:
{{.Chart | toYaml | indent 4}}
Release:
{{.Release | toYaml | indent 4}}
Values:
{{.Values | toYaml | indent 4}}
`
func TestRender(t *testing.T) {
testChart := &chart.Chart{
Metadata: &chart.Metadata{Name: "hello"},
Templates: []*chart.Template{
{Name: "templates/cm.yaml", Data: []byte(cmTemplate)},
},
Values: &chart.Config{Raw: "meow: defaultmeow"},
}
newConfig := &chart.Config{Raw: "meow: newmeow"}
defaultConfig := &chart.Config{Raw: "{}"}
tests := map[string]struct {
chart *chart.Chart
config *chart.Config
opts Options
want map[string]string
}{
"BasicWithValues": {
chart: testChart,
config: newConfig,
opts: Options{},
want: map[string]string{
"hello/templates/cm.yaml": `kind: ConfigMap
apiVersion: v1
metadata:
name: example
data:
Chart:
name: hello
Release:
IsInstall: false
IsUpgrade: false
Name: ""
Namespace: ""
Revision: 0
Service: Tiller
Time: null
Values:
meow: newmeow
`,
},
},
"BasicNoValues": {
chart: testChart,
config: defaultConfig,
opts: Options{},
want: map[string]string{
"hello/templates/cm.yaml": `kind: ConfigMap
apiVersion: v1
metadata:
name: example
data:
Chart:
name: hello
Release:
IsInstall: false
IsUpgrade: false
Name: ""
Namespace: ""
Revision: 0
Service: Tiller
Time: null
Values:
meow: defaultmeow
`,
},
},
"SetSomeReleaseValues": {
chart: testChart,
config: defaultConfig,
opts: Options{ReleaseOptions: chartutil.ReleaseOptions{Name: "meow"}},
want: map[string]string{
"hello/templates/cm.yaml": `kind: ConfigMap
apiVersion: v1
metadata:
name: example
data:
Chart:
name: hello
Release:
IsInstall: false
IsUpgrade: false
Name: meow
Namespace: ""
Revision: 0
Service: Tiller
Time: null
Values:
meow: defaultmeow
`,
},
},
}
for testName, tt := range tests {
t.Run(testName, func(t *testing.T) {
got, err := Render(tt.chart, tt.config, tt.opts)
require.NoError(t, err)
require.Equal(t, tt.want, got)
})
}
}
Loading…
Cancel
Save