|
|
|
@ -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 <data> to <output-dir>/<name>
|
|
|
|
|
/*
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|