diff --git a/cmd/helm/template.go b/cmd/helm/template.go index 44285cb1a..36fbec939 100644 --- a/cmd/helm/template.go +++ b/cmd/helm/template.go @@ -19,30 +19,33 @@ package main import ( "fmt" "io" + "io/ioutil" "os" - "path" "path/filepath" - "regexp" - "strings" - "time" + + "k8s.io/apimachinery/pkg/version" + "k8s.io/client-go/discovery" "github.com/Masterminds/semver" "github.com/pkg/errors" "github.com/spf13/cobra" + fakedisc "k8s.io/client-go/discovery/fake" + "k8s.io/client-go/kubernetes/fake" "k8s.io/helm/cmd/helm/require" + "k8s.io/helm/pkg/action" "k8s.io/helm/pkg/chart/loader" "k8s.io/helm/pkg/chartutil" - "k8s.io/helm/pkg/engine" "k8s.io/helm/pkg/hapi/release" - util "k8s.io/helm/pkg/releaseutil" - "k8s.io/helm/pkg/tiller" + "k8s.io/helm/pkg/storage" + "k8s.io/helm/pkg/storage/driver" + "k8s.io/helm/pkg/tiller/environment" ) -const defaultDirectoryPermission = 0755 +//const defaultDirectoryPermission = 0755 var ( - whitespaceRegex = regexp.MustCompile(`^\s*$`) + //whitespaceRegex = regexp.MustCompile(`^\s*$`) // defaultKubeVersion is the default value of --kube-version flag defaultKubeVersion = fmt.Sprintf("%s.%s", chartutil.DefaultKubeVersion.Major, chartutil.DefaultKubeVersion.Minor) @@ -130,13 +133,6 @@ func (o *templateOptions) run(out io.Writer) error { } } - // verify that output-dir exists if provided - if o.outputDir != "" { - if _, err := os.Stat(o.outputDir); os.IsNotExist(err) { - return errors.Errorf("output-dir '%s' does not exist", o.outputDir) - } - } - // get combined values and create config config, err := o.mergedValues() if err != nil { @@ -162,100 +158,162 @@ func (o *templateOptions) run(out io.Writer) error { return err } } - options := chartutil.ReleaseOptions{ - Name: o.releaseName, - } if err := chartutil.ProcessDependencies(c, config); err != nil { return err } - // Set up engine. - renderer := engine.New() - - // kubernetes version - kv, err := semver.NewVersion(o.kubeVersion) + // MPB: It appears that we can now do everything we need with an install + // dry-run using the Kubernetes mock + disc, err := createFakeDiscovery(o.kubeVersion) if err != nil { - return errors.Wrap(err, "could not parse a kubernetes version") + return err } - - caps := chartutil.DefaultCapabilities - 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.ToRenderValues(c, config, options, caps) + customConfig := &action.Configuration{ + // Add mock objects in here so it doesn't use Kube API server + Releases: storage.Init(driver.NewMemory()), + KubeClient: &environment.PrintingKubeClient{Out: ioutil.Discard}, + Discovery: disc, + Log: func(format string, v ...interface{}) { + fmt.Fprintf(out, format, v...) + }, + } + inst := action.NewInstall(customConfig) + inst.DryRun = true + inst.Replace = true // Skip running the name check + inst.ReleaseName = o.releaseName + rel, err := inst.Run(c, config) if err != nil { return err } - rendered, err := renderer.Render(c, vals) - if err != nil { - return err + if o.showNotes { + fmt.Fprintln(out, rel.Info.Notes) } - listManifests := []tiller.Manifest{} - // extract kind and name - re := regexp.MustCompile("kind:(.*)\n") - for k, v := range rendered { - 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 o.outputDir != "" { + return o.writeAsFiles(rel) + } - in := func(needle string, haystack []string) bool { - // make needle path absolute - d := strings.Split(needle, string(os.PathSeparator)) - dd := d[1:] - an := filepath.Join(o.chartPath, strings.Join(dd, string(os.PathSeparator))) - - for _, h := range haystack { - if h == an { - return true + fmt.Fprintln(out, rel.Manifest) + return nil + + /* + + // Set up engine. + renderer := engine.New() + + // kubernetes version + kv, err := semver.NewVersion(o.kubeVersion) + if err != nil { + return errors.Wrap(err, "could not parse a kubernetes version") + } + + caps := chartutil.DefaultCapabilities + 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.ToRenderValues(c, config, options, caps) + if err != nil { + return err + } + + rendered, err := renderer.Render(c, vals) + if err != nil { + return err + } + + listManifests := []tiller.Manifest{} + // extract kind and name + re := regexp.MustCompile("kind:(.*)\n") + for k, v := range rendered { + 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) + } + in := func(needle string, haystack []string) bool { + // make needle path absolute + d := strings.Split(needle, string(os.PathSeparator)) + dd := d[1:] + an := filepath.Join(o.chartPath, strings.Join(dd, string(os.PathSeparator))) + + for _, h := range haystack { + if h == an { + return true + } + } + return false } - return false - } - if settings.Debug { - rel := &release.Release{ - Name: o.releaseName, - Chart: c, - Config: config, - Version: 1, - Info: &release.Info{LastDeployed: time.Now()}, + if settings.Debug { + rel := &release.Release{ + Name: o.releaseName, + Chart: c, + Config: config, + Version: 1, + Info: &release.Info{LastDeployed: time.Now()}, + } + printRelease(out, rel) } - printRelease(out, rel) - } - for _, m := range tiller.SortByKind(listManifests) { - b := filepath.Base(m.Name) - switch { - case len(o.renderFiles) > 0 && !in(m.Name, rf): - continue - case !o.showNotes && b == "NOTES.txt": - continue - case strings.HasPrefix(b, "_"): - continue - case whitespaceRegex.MatchString(m.Content): - // blank template after execution - continue - case o.outputDir != "": - if err := writeToFile(out, o.outputDir, m.Name, m.Content); err != nil { - return err + for _, m := range tiller.SortByKind(listManifests) { + b := filepath.Base(m.Name) + switch { + case len(o.renderFiles) > 0 && !in(m.Name, rf): + continue + case !o.showNotes && b == "NOTES.txt": + continue + case strings.HasPrefix(b, "_"): + continue + case whitespaceRegex.MatchString(m.Content): + // blank template after execution + continue + case o.outputDir != "": + if err := writeToFile(out, o.outputDir, m.Name, m.Content); err != nil { + return err + } + default: + fmt.Fprintf(out, "---\n# Source: %s\n", m.Name) + fmt.Fprintln(out, m.Content) } - default: - fmt.Fprintf(out, "---\n# Source: %s\n", m.Name) - fmt.Fprintln(out, m.Content) } + */ +} + +func (o *templateOptions) writeAsFiles(rel *release.Release) error { + if _, err := os.Stat(o.outputDir); os.IsNotExist(err) { + return errors.Errorf("output-dir '%s' does not exist", o.outputDir) } - return nil + // At one point we parsed out the returned manifest and created multiple files. + // I'm not totally sure what the use case was for that. + filename := filepath.Join(o.outputDir, rel.Name+".yaml") + return ioutil.WriteFile(filename, []byte(rel.Manifest), 0644) +} + +// createFakeDiscovery creates a discovery client and seeds it with mock data. +func createFakeDiscovery(verStr string) (discovery.DiscoveryInterface, error) { + disc := fake.NewSimpleClientset().Discovery() + if verStr != "" { + kv, err := semver.NewVersion(verStr) + if err != nil { + return disc, errors.Wrap(err, "could not parse a kubernetes version") + } + disc.(*fakedisc.FakeDiscovery).FakedServerVersion = &version.Info{ + Major: fmt.Sprintf("%d", kv.Major()), + Minor: fmt.Sprintf("%d", kv.Minor()), + GitVersion: fmt.Sprintf("v%d.%d.0", kv.Major(), kv.Minor()), + } + } + return disc, nil } // write the to / +/* func writeToFile(out io.Writer, outputDir, name, data string) error { outfileName := strings.Join([]string{outputDir, name}, string(filepath.Separator)) @@ -278,6 +336,7 @@ func writeToFile(out io.Writer, outputDir, name, data string) error { return nil } + // check if the directory exists to create file. creates if don't exists func ensureDirectoryForFile(file string) error { baseDir := path.Dir(file) @@ -287,3 +346,4 @@ func ensureDirectoryForFile(file string) error { return os.MkdirAll(baseDir, defaultDirectoryPermission) } +*/