Fetch .Capabilities.APIVersions lazily

Signed-off-by: Eric Latham <ericoliverlatham@gmail.com>
pull/13146/head
Eric Latham 4 days ago
parent ff03c66d44
commit d3373da8d7

@ -97,6 +97,18 @@ type Configuration struct {
Log func(string, ...interface{})
}
// chartNeedsAPIVersions returns true if any template in the given Chart references .Capabilities.APIVersions.
func chartNeedsAPIVersions(chart *chart.Chart) bool {
if chart != nil {
for _, template := range chart.Templates {
if bytes.Contains(template.Data, []byte(".Capabilities.APIVersions")) {
return true
}
}
}
return false
}
// renderResources renders the templates in a chart
//
// TODO: This function is badly in need of a refactor.
@ -107,7 +119,7 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Valu
hs := []*release.Hook{}
b := bytes.NewBuffer(nil)
caps, err := cfg.getCapabilities()
caps, err := cfg.getCapabilities(ch)
if err != nil {
return hs, b, "", err
}
@ -243,7 +255,7 @@ type RESTClientGetter interface {
type DebugLog func(format string, v ...interface{})
// capabilities builds a Capabilities from discovery information.
func (cfg *Configuration) getCapabilities() (*chartutil.Capabilities, error) {
func (cfg *Configuration) getCapabilities(ch *chart.Chart) (*chartutil.Capabilities, error) {
if cfg.Capabilities != nil {
return cfg.Capabilities, nil
}
@ -257,18 +269,23 @@ func (cfg *Configuration) getCapabilities() (*chartutil.Capabilities, error) {
if err != nil {
return nil, errors.Wrap(err, "could not get server version from Kubernetes")
}
// Issue #6361:
// Client-Go emits an error when an API service is registered but unimplemented.
// We trap that error here and print a warning. But since the discovery client continues
// building the API object, it is correctly populated with all valid APIs.
// See https://github.com/kubernetes/kubernetes/issues/72051#issuecomment-521157642
apiVersions, err := GetVersionSet(dc)
if err != nil {
if discovery.IsGroupDiscoveryFailedError(err) {
cfg.Log("WARNING: The Kubernetes server has an orphaned API service. Server reports: %s", err)
cfg.Log("WARNING: To fix this, kubectl delete apiservice <service-name>")
} else {
return nil, errors.Wrap(err, "could not get apiVersions from Kubernetes")
// Only fetch APIVersions if the chart needs it
var apiVersions chartutil.VersionSet
if chartNeedsAPIVersions(ch) {
// Issue #6361:
// Client-Go emits an error when an API service is registered but unimplemented.
// We trap that error here and print a warning. But since the discovery client continues
// building the API object, it is correctly populated with all valid APIs.
// See https://github.com/kubernetes/kubernetes/issues/72051#issuecomment-521157642
apiVersions, err = GetVersionSet(dc)
if err != nil {
if discovery.IsGroupDiscoveryFailedError(err) {
cfg.Log("WARNING: The Kubernetes server has an orphaned API service. Server reports: %s", err)
cfg.Log("WARNING: To fix this, kubectl delete apiservice <service-name>")
} else {
return nil, errors.Wrap(err, "could not get apiVersions from Kubernetes")
}
}
}

@ -20,6 +20,7 @@ import (
"io"
"testing"
"github.com/stretchr/testify/assert"
fakeclientset "k8s.io/client-go/kubernetes/fake"
"helm.sh/helm/v3/pkg/chart"
@ -273,6 +274,19 @@ func namedReleaseStub(name string, status release.Status) *release.Release {
}
}
func Test_chartNeedsAPIVersions(t *testing.T) {
is := assert.New(t)
is.Equal(chartNeedsAPIVersions(nil), false)
ch := &chart.Chart{Metadata: &chart.Metadata{APIVersion: "v1", Name: "test", Version: "0.1.0"}}
is.Equal(chartNeedsAPIVersions(ch), false)
ch.Templates = append(ch.Templates, &chart.File{Name: "templates/hello", Data: []byte("hello: world")})
is.Equal(chartNeedsAPIVersions(ch), false)
ch.Templates = append(ch.Templates, &chart.File{Name: "templates/kube-version", Data: []byte("{{ .Capabilities.KubeVersion }}")})
is.Equal(chartNeedsAPIVersions(ch), false)
ch.Templates = append(ch.Templates, &chart.File{Name: "templates/api-versions", Data: []byte("{{ .Capabilities.APIVersions }}")})
is.Equal(chartNeedsAPIVersions(ch), true)
}
func TestGetVersionSet(t *testing.T) {
client := fakeclientset.NewSimpleClientset()

@ -284,7 +284,7 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
// the user doesn't have to specify both
i.Wait = i.Wait || i.Atomic
caps, err := i.cfg.getCapabilities()
caps, err := i.cfg.getCapabilities(chrt)
if err != nil {
return nil, err
}

@ -196,7 +196,7 @@ func joinErrors(errs []error) string {
// deleteRelease deletes the release and returns list of delete resources and manifests that were kept in the deletion process
func (u *Uninstall) deleteRelease(rel *release.Release) (kube.ResourceList, string, []error) {
var errs []error
caps, err := u.cfg.getCapabilities()
caps, err := u.cfg.getCapabilities(rel.Chart)
if err != nil {
return nil, rel.Manifest, []error{errors.Wrap(err, "could not get apiVersions from Kubernetes")}
}

@ -254,7 +254,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin
IsUpgrade: true,
}
caps, err := u.cfg.getCapabilities()
caps, err := u.cfg.getCapabilities(chart)
if err != nil {
return nil, nil, err
}

Loading…
Cancel
Save