diff --git a/cmd/helm/template.go b/cmd/helm/template.go index 36c029e69..5c1ba33d1 100644 --- a/cmd/helm/template.go +++ b/cmd/helm/template.go @@ -62,19 +62,13 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { client.Replace = true // Skip the name check client.ClientOnly = !validate client.APIVersions = chartutil.VersionSet(extraAPIs) + client.IncludeCRDs = includeCrds rel, err := runInstall(args, client, valueOpts, out) if err != nil { return err } var manifests bytes.Buffer - - if includeCrds { - for _, f := range rel.Chart.CRDs() { - fmt.Fprintf(&manifests, "---\n# Source: %s\n%s\n", f.Name, f.Data) - } - } - fmt.Fprintln(&manifests, strings.TrimSpace(rel.Manifest)) if !client.DisableHooks { diff --git a/pkg/action/install.go b/pkg/action/install.go index c4ee50857..78021dc8c 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -85,6 +85,7 @@ type Install struct { SkipCRDs bool SubNotes bool DisableOpenAPIValidation bool + IncludeCRDs bool // APIVersions allows a manual set of supported API Versions to be passed // (for things like templating). These are ignored if ClientOnly is false APIVersions chartutil.VersionSet @@ -115,12 +116,12 @@ func NewInstall(cfg *Configuration) *Install { } } -func (i *Install) installCRDs(crds []*chart.File) error { +func (i *Install) installCRDs(crds []chart.CRD) error { // We do these one file at a time in the order they were read. totalItems := []*resource.Info{} for _, obj := range crds { // Read in the resources - res, err := i.cfg.KubeClient.Build(bytes.NewBuffer(obj.Data), false) + res, err := i.cfg.KubeClient.Build(bytes.NewBuffer(obj.File.Data), false) if err != nil { return errors.Wrapf(err, "failed to install CRD %s", obj.Name) } @@ -171,7 +172,7 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release. // Pre-install anything in the crd/ directory. We do this before Helm // contacts the upstream server and builds the capabilities object. - if crds := chrt.CRDs(); !i.ClientOnly && !i.SkipCRDs && len(crds) > 0 { + if crds := chrt.CRDObjects(); !i.ClientOnly && !i.SkipCRDs && len(crds) > 0 { // On dry run, bail here if i.DryRun { i.cfg.Log("WARNING: This chart or one of its subcharts contains CRDs. Rendering may fail or contain inaccuracies.") @@ -221,7 +222,7 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release. rel := i.createRelease(chrt, vals) var manifestDoc *bytes.Buffer - rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName) + rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs) // Even for errors, attach this if available if manifestDoc != nil { rel.Manifest = manifestDoc.String() @@ -425,7 +426,7 @@ func (i *Install) replaceRelease(rel *release.Release) error { } // renderResources renders the templates in a chart -func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName string, outputDir string, subNotes, useReleaseName bool) ([]*release.Hook, *bytes.Buffer, string, error) { +func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName string, outputDir string, subNotes, useReleaseName bool, includeCrds bool) ([]*release.Hook, *bytes.Buffer, string, error) { hs := []*release.Hook{} b := bytes.NewBuffer(nil) @@ -498,6 +499,21 @@ func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values // Aggregate all valid manifests into one big doc. fileWritten := make(map[string]bool) + + if includeCrds { + for _, crd := range ch.CRDObjects() { + if outputDir == "" { + fmt.Fprintf(b, "---\n# Source: %s\n%s\n", crd.Name, string(crd.File.Data[:])) + } else { + err = writeToFile(outputDir, crd.Filename, string(crd.File.Data[:]), fileWritten[crd.Name]) + if err != nil { + return hs, b, "", err + } + fileWritten[crd.Name] = true + } + } + } + for _, m := range manifests { if outputDir == "" { fmt.Fprintf(b, "---\n# Source: %s\n%s\n", m.Name, m.Content) diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index ad3a235e6..3e8a04a56 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -161,7 +161,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin return nil, nil, err } - hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false) + hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false) if err != nil { return nil, nil, err } diff --git a/pkg/chart/chart.go b/pkg/chart/chart.go index 5eb4d4d94..1a85373b9 100644 --- a/pkg/chart/chart.go +++ b/pkg/chart/chart.go @@ -15,7 +15,10 @@ limitations under the License. package chart -import "strings" +import ( + "path/filepath" + "strings" +) // APIVersionV1 is the API version number for version 1. const APIVersionV1 = "v1" @@ -49,6 +52,15 @@ type Chart struct { dependencies []*Chart } +type CRD struct { + // Name is the File.Name for the crd file + Name string + // Filename is the File obj Name including (sub-)chart.ChartFullPath + Filename string + // File is the File obj for the crd + File *File +} + // SetDependencies replaces the chart dependencies. func (ch *Chart) SetDependencies(charts ...*Chart) { ch.dependencies = nil @@ -118,6 +130,7 @@ func (ch *Chart) AppVersion() string { } // CRDs returns a list of File objects in the 'crds/' directory of a Helm chart. +// Deprecated: use CRDObjects() func (ch *Chart) CRDs() []*File { files := []*File{} // Find all resources in the crds/ directory @@ -132,3 +145,20 @@ func (ch *Chart) CRDs() []*File { } return files } + +// CRDObjects returns a list of CRD objects in the 'crds/' directory of a Helm chart & subcharts +func (ch *Chart) CRDObjects() []CRD { + crds := []CRD{} + // Find all resources in the crds/ directory + for _, f := range ch.Files { + if strings.HasPrefix(f.Name, "crds/") { + mycrd := CRD{Name: f.Name, Filename: filepath.Join(ch.ChartFullPath(), f.Name), File: f} + crds = append(crds, mycrd) + } + } + // Get CRDs from dependencies, too. + for _, dep := range ch.Dependencies() { + crds = append(crds, dep.CRDObjects()...) + } + return crds +}