Bump helm.sh/helm/v3 from 3.10.3 to 3.11.1 (#5528)

This commit is contained in:
hongming
2023-02-16 11:56:23 +08:00
committed by GitHub
parent b143b41e3c
commit 785a994a5c
345 changed files with 26548 additions and 7334 deletions

View File

@@ -29,7 +29,7 @@ var (
//
// Increment major number for new feature additions and behavioral changes.
// Increment minor number for bug fixes and performance enhancements.
version = "v3.10"
version = "v3.11"
// metadata is extra build time data
metadata = ""

View File

@@ -101,8 +101,9 @@ type Configuration struct {
//
// TODO: This function is badly in need of a refactor.
// TODO: As part of the refactor the duplicate code in cmd/helm/template.go should be removed
// This code has to do with writing files to disk.
func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer, dryRun bool) ([]*release.Hook, *bytes.Buffer, string, error) {
//
// This code has to do with writing files to disk.
func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer, dryRun, enableDNS bool) ([]*release.Hook, *bytes.Buffer, string, error) {
hs := []*release.Hook{}
b := bytes.NewBuffer(nil)
@@ -130,9 +131,13 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Valu
if err != nil {
return hs, b, "", err
}
files, err2 = engine.RenderWithClient(ch, values, restConfig)
e := engine.New(restConfig)
e.EnableDNS = enableDNS
files, err2 = e.Render(ch, values)
} else {
files, err2 = engine.Render(ch, values)
var e engine.Engine
e.EnableDNS = enableDNS
files, err2 = e.Render(ch, values)
}
if err2 != nil {

View File

@@ -69,6 +69,7 @@ type Install struct {
ChartPathOptions
ClientOnly bool
Force bool
CreateNamespace bool
DryRun bool
DisableHooks bool
@@ -96,6 +97,8 @@ type Install struct {
APIVersions chartutil.VersionSet
// Used by helm template to render charts with .Release.IsUpgrade. Ignored if Dry-Run is false
IsUpgrade bool
// Enable DNS lookups when rendering templates
EnableDNS bool
// Used by helm template to add the release as part of OutputDir path
// OutputDir/<ReleaseName>
UseReleaseName bool
@@ -256,7 +259,7 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
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, i.IncludeCRDs, i.PostRenderer, i.DryRun)
rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.PostRenderer, i.DryRun, i.EnableDNS)
// Even for errors, attach this if available
if manifestDoc != nil {
rel.Manifest = manifestDoc.String()
@@ -372,7 +375,7 @@ func (i *Install) performInstall(c chan<- resultMessage, rel *release.Release, t
return
}
} else if len(resources) > 0 {
if _, err := i.cfg.KubeClient.Update(toBeAdopted, resources, false); err != nil {
if _, err := i.cfg.KubeClient.Update(toBeAdopted, resources, i.Force); err != nil {
i.reportToRun(c, rel, err)
return
}
@@ -456,10 +459,10 @@ func (i *Install) failRelease(rel *release.Release, err error) (*release.Release
//
// Roughly, this will return an error if name is
//
// - empty
// - too long
// - already in use, and not deleted
// - used by a deleted release, and i.Replace is false
// - empty
// - too long
// - already in use, and not deleted
// - used by a deleted release, and i.Replace is false
func (i *Install) availableName() error {
start := i.ReleaseName

View File

@@ -17,6 +17,10 @@ limitations under the License.
package action
import (
"bytes"
"errors"
"helm.sh/helm/v3/pkg/kube"
"helm.sh/helm/v3/pkg/release"
)
@@ -32,6 +36,14 @@ type Status struct {
// only affect print type table.
// TODO Helm 4: Remove this flag and output the description by default.
ShowDescription bool
// ShowResources sets if the resources should be retrieved with the status.
// TODO Helm 4: Remove this flag and output the resources by default.
ShowResources bool
// ShowResourcesTable is used with ShowResources. When true this will cause
// the resulting objects to be retrieved as a kind=table.
ShowResourcesTable bool
}
// NewStatus creates a new Status object with the given configuration.
@@ -47,5 +59,37 @@ func (s *Status) Run(name string) (*release.Release, error) {
return nil, err
}
return s.cfg.releaseContent(name, s.Version)
if !s.ShowResources {
return s.cfg.releaseContent(name, s.Version)
}
rel, err := s.cfg.releaseContent(name, s.Version)
if err != nil {
return nil, err
}
if kubeClient, ok := s.cfg.KubeClient.(kube.InterfaceResources); ok {
var resources kube.ResourceList
if s.ShowResourcesTable {
resources, err = kubeClient.BuildTable(bytes.NewBufferString(rel.Manifest), false)
if err != nil {
return nil, err
}
} else {
resources, err = s.cfg.KubeClient.Build(bytes.NewBufferString(rel.Manifest), false)
if err != nil {
return nil, err
}
}
resp, err := kubeClient.Get(resources, true)
if err != nil {
return nil, err
}
rel.Info.Resources = resp
return rel, nil
}
return nil, errors.New("unable to get kubeClient with interface InterfaceResources")
}

View File

@@ -103,6 +103,8 @@ type Upgrade struct {
DependencyUpdate bool
// Lock to control raceconditions when the process receives a SIGTERM
Lock sync.Mutex
// Enable DNS lookups when rendering templates
EnableDNS bool
}
type resultMessage struct {
@@ -231,7 +233,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, false, u.PostRenderer, u.DryRun)
hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, u.DryRun, u.EnableDNS)
if err != nil {
return nil, nil, err
}

View File

@@ -54,7 +54,7 @@ type Dependency struct {
// loaded.
func (d *Dependency) Validate() error {
if d == nil {
return ValidationError("dependency cannot be an empty list")
return ValidationError("dependencies must not contain empty or null nodes")
}
d.Name = sanitizeString(d.Name)
d.Version = sanitizeString(d.Version)

View File

@@ -35,7 +35,7 @@ type Maintainer struct {
// Validate checks valid data and sanitizes string characters.
func (m *Maintainer) Validate() error {
if m == nil {
return ValidationError("maintainer cannot be an empty list")
return ValidationError("maintainers must not contain empty or null nodes")
}
m.Name = sanitizeString(m.Name)
m.Email = sanitizeString(m.Email)

View File

@@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
/*Package cli describes the operating environment for the Helm CLI.
/*
Package cli describes the operating environment for the Helm CLI.
Helm's environment encapsulates all of the service dependencies Helm has.
These dependencies are expressed as interfaces so that alternate implementations
@@ -24,6 +25,7 @@ package cli
import (
"fmt"
"net/http"
"os"
"strconv"
"strings"
@@ -32,6 +34,7 @@ import (
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/rest"
"helm.sh/helm/v3/internal/version"
"helm.sh/helm/v3/pkg/helmpath"
)
@@ -116,6 +119,10 @@ func New() *EnvSettings {
ImpersonateGroup: &env.KubeAsGroups,
WrapConfigFn: func(config *rest.Config) *rest.Config {
config.Burst = env.BurstLimit
config.Wrap(func(rt http.RoundTripper) http.RoundTripper {
return &retryingRoundTripper{wrapped: rt}
})
config.UserAgent = version.GetUserAgent()
return config
},
}

View File

@@ -0,0 +1,80 @@
/*
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 cli
import (
"bytes"
"encoding/json"
"io"
"net/http"
"strings"
)
type retryingRoundTripper struct {
wrapped http.RoundTripper
}
func (rt *retryingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
return rt.roundTrip(req, 1, nil)
}
func (rt *retryingRoundTripper) roundTrip(req *http.Request, retry int, prevResp *http.Response) (*http.Response, error) {
if retry < 0 {
return prevResp, nil
}
resp, rtErr := rt.wrapped.RoundTrip(req)
if rtErr != nil {
return resp, rtErr
}
if resp.StatusCode < 500 {
return resp, rtErr
}
if resp.Header.Get("content-type") != "application/json" {
return resp, rtErr
}
b, err := io.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return resp, rtErr
}
var ke kubernetesError
r := bytes.NewReader(b)
err = json.NewDecoder(r).Decode(&ke)
r.Seek(0, io.SeekStart)
resp.Body = io.NopCloser(r)
if err != nil {
return resp, rtErr
}
if ke.Code < 500 {
return resp, rtErr
}
// Matches messages like "etcdserver: leader changed"
if strings.HasSuffix(ke.Message, "etcdserver: leader changed") {
return rt.roundTrip(req, retry-1, resp)
}
// Matches messages like "rpc error: code = Unknown desc = raft proposal dropped"
if strings.HasSuffix(ke.Message, "raft proposal dropped") {
return rt.roundTrip(req, retry-1, resp)
}
return resp, rtErr
}
type kubernetesError struct {
Message string `json:"message"`
Code int `json:"code"`
}

View File

@@ -294,32 +294,13 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er
}
// TODO: Seems that picking first URL is not fully correct
u, err = url.Parse(cv.URLs[0])
resolvedURL, err := repo.ResolveReferenceURL(rc.URL, cv.URLs[0])
if err != nil {
return u, errors.Errorf("invalid chart URL format: %s", ref)
}
// If the URL is relative (no scheme), prepend the chart repo's base URL
if !u.IsAbs() {
repoURL, err := url.Parse(rc.URL)
if err != nil {
return repoURL, err
}
q := repoURL.Query()
// We need a trailing slash for ResolveReference to work, but make sure there isn't already one
repoURL.RawPath = strings.TrimSuffix(repoURL.RawPath, "/") + "/"
repoURL.Path = strings.TrimSuffix(repoURL.Path, "/") + "/"
u = repoURL.ResolveReference(u)
u.RawQuery = q.Encode()
// TODO add user-agent
if _, err := getter.NewHTTPGetter(getter.WithURL(rc.URL)); err != nil {
return repoURL, err
}
return u, err
}
// TODO add user-agent
return u, nil
return url.Parse(resolvedURL)
}
// VerifyChart takes a path to a chart archive and a keyring, and verifies the chart.

View File

@@ -42,6 +42,15 @@ type Engine struct {
LintMode bool
// the rest config to connect to the kubernetes api
config *rest.Config
// EnableDNS tells the engine to allow DNS lookups when rendering templates
EnableDNS bool
}
// New creates a new instance of Engine using the passed in rest config.
func New(config *rest.Config) Engine {
return Engine{
config: config,
}
}
// Render takes a chart, optional values, and value overrides, and attempts to render the Go templates.
@@ -189,6 +198,14 @@ func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]render
funcMap["lookup"] = NewLookupFunction(e.config)
}
// When DNS lookups are not enabled override the sprig function and return
// an empty string.
if !e.EnableDNS {
funcMap["getHostByName"] = func(name string) string {
return ""
}
}
t.Funcs(funcMap)
}

View File

@@ -128,7 +128,6 @@ func (g *HTTPGetter) httpClient() (*http.Client, error) {
if err != nil {
return nil, errors.Wrap(err, "can't create TLS config for client")
}
tlsConf.BuildNameToCertificate()
sni, err := urlutil.ExtractHostname(g.opts.url)
if err != nil {

View File

@@ -17,12 +17,14 @@ limitations under the License.
package kube // import "helm.sh/helm/v3/pkg/kube"
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"reflect"
"strings"
"sync"
"time"
@@ -38,7 +40,9 @@ import (
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/strategicpatch"
@@ -47,6 +51,7 @@ import (
"k8s.io/cli-runtime/pkg/resource"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
cachetools "k8s.io/client-go/tools/cache"
watchtools "k8s.io/client-go/tools/watch"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
@@ -132,6 +137,141 @@ func (c *Client) Create(resources ResourceList) (*Result, error) {
return &Result{Created: resources}, nil
}
func transformRequests(req *rest.Request) {
tableParam := strings.Join([]string{
fmt.Sprintf("application/json;as=Table;v=%s;g=%s", metav1.SchemeGroupVersion.Version, metav1.GroupName),
fmt.Sprintf("application/json;as=Table;v=%s;g=%s", metav1beta1.SchemeGroupVersion.Version, metav1beta1.GroupName),
"application/json",
}, ",")
req.SetHeader("Accept", tableParam)
// if sorting, ensure we receive the full object in order to introspect its fields via jsonpath
req.Param("includeObject", "Object")
}
// Get retrieves the resource objects supplied. If related is set to true the
// related pods are fetched as well. If the passed in resources are a table kind
// the related resources will also be fetched as kind=table.
func (c *Client) Get(resources ResourceList, related bool) (map[string][]runtime.Object, error) {
buf := new(bytes.Buffer)
objs := make(map[string][]runtime.Object)
podSelectors := []map[string]string{}
err := resources.Visit(func(info *resource.Info, err error) error {
if err != nil {
return err
}
gvk := info.ResourceMapping().GroupVersionKind
vk := gvk.Version + "/" + gvk.Kind
obj, err := getResource(info)
if err != nil {
fmt.Fprintf(buf, "Get resource %s failed, err:%v\n", info.Name, err)
} else {
objs[vk] = append(objs[vk], obj)
// Only fetch related pods if they are requested
if related {
// Discover if the existing object is a table. If it is, request
// the pods as Tables. Otherwise request them normally.
objGVK := obj.GetObjectKind().GroupVersionKind()
var isTable bool
if objGVK.Kind == "Table" {
isTable = true
}
objs, err = c.getSelectRelationPod(info, objs, isTable, &podSelectors)
if err != nil {
c.Log("Warning: get the relation pod is failed, err:%s", err.Error())
}
}
}
return nil
})
if err != nil {
return nil, err
}
return objs, nil
}
func (c *Client) getSelectRelationPod(info *resource.Info, objs map[string][]runtime.Object, table bool, podSelectors *[]map[string]string) (map[string][]runtime.Object, error) {
if info == nil {
return objs, nil
}
c.Log("get relation pod of object: %s/%s/%s", info.Namespace, info.Mapping.GroupVersionKind.Kind, info.Name)
selector, ok, _ := getSelectorFromObject(info.Object)
if !ok {
return objs, nil
}
for index := range *podSelectors {
if reflect.DeepEqual((*podSelectors)[index], selector) {
// check if pods for selectors are already added. This avoids duplicate printing of pods
return objs, nil
}
}
*podSelectors = append(*podSelectors, selector)
var infos []*resource.Info
var err error
if table {
infos, err = c.Factory.NewBuilder().
Unstructured().
ContinueOnError().
NamespaceParam(info.Namespace).
DefaultNamespace().
ResourceTypes("pods").
LabelSelector(labels.Set(selector).AsSelector().String()).
TransformRequests(transformRequests).
Do().Infos()
if err != nil {
return objs, err
}
} else {
infos, err = c.Factory.NewBuilder().
Unstructured().
ContinueOnError().
NamespaceParam(info.Namespace).
DefaultNamespace().
ResourceTypes("pods").
LabelSelector(labels.Set(selector).AsSelector().String()).
Do().Infos()
if err != nil {
return objs, err
}
}
vk := "v1/Pod(related)"
for _, info := range infos {
objs[vk] = append(objs[vk], info.Object)
}
return objs, nil
}
func getSelectorFromObject(obj runtime.Object) (map[string]string, bool, error) {
typed := obj.(*unstructured.Unstructured)
kind := typed.Object["kind"]
switch kind {
case "ReplicaSet", "Deployment", "StatefulSet", "DaemonSet", "Job":
return unstructured.NestedStringMap(typed.Object, "spec", "selector", "matchLabels")
case "ReplicationController":
return unstructured.NestedStringMap(typed.Object, "spec", "selector")
default:
return nil, false, nil
}
}
func getResource(info *resource.Info) (runtime.Object, error) {
obj, err := resource.NewHelper(info.Client, info.Mapping).Get(info.Namespace, info.Name)
if err != nil {
return nil, err
}
return obj, nil
}
// Wait waits up to the given timeout for the specified resources to be ready.
func (c *Client) Wait(resources ResourceList, timeout time.Duration) error {
cs, err := c.getKubeClient()
@@ -215,6 +355,33 @@ func (c *Client) Build(reader io.Reader, validate bool) (ResourceList, error) {
return result, scrubValidationError(err)
}
// BuildTable validates for Kubernetes objects and returns unstructured infos.
// The returned kind is a Table.
func (c *Client) BuildTable(reader io.Reader, validate bool) (ResourceList, error) {
validationDirective := metav1.FieldValidationIgnore
if validate {
validationDirective = metav1.FieldValidationStrict
}
dynamicClient, err := c.Factory.DynamicClient()
if err != nil {
return nil, err
}
verifier := resource.NewQueryParamVerifier(dynamicClient, c.Factory.OpenAPIGetter(), resource.QueryParamFieldValidation)
schema, err := c.Factory.Validator(validationDirective, verifier)
if err != nil {
return nil, err
}
result, err := c.newBuilder().
Unstructured().
Schema(schema).
Stream(reader, "").
TransformRequests(transformRequests).
Do().Infos()
return result, scrubValidationError(err)
}
// Update takes the current list of objects and target list of objects and
// creates resources that don't already exist, updates resources that have been
// modified in the target configuration, and deletes resources from the current
@@ -308,16 +475,20 @@ func (c *Client) Delete(resources ResourceList) (*Result, []error) {
mtx := sync.Mutex{}
err := perform(resources, func(info *resource.Info) error {
c.Log("Starting delete for %q %s", info.Name, info.Mapping.GroupVersionKind.Kind)
if err := c.skipIfNotFound(deleteResource(info)); err != nil {
mtx.Lock()
defer mtx.Unlock()
// Collect the error and continue on
errs = append(errs, err)
} else {
err := deleteResource(info)
if err == nil || apierrors.IsNotFound(err) {
if err != nil {
c.Log("Ignoring delete failure for %q %s: %v", info.Name, info.Mapping.GroupVersionKind, err)
}
mtx.Lock()
defer mtx.Unlock()
res.Deleted = append(res.Deleted, info)
return nil
}
mtx.Lock()
defer mtx.Unlock()
// Collect the error and continue on
errs = append(errs, err)
return nil
})
if err != nil {
@@ -334,14 +505,6 @@ func (c *Client) Delete(resources ResourceList) (*Result, []error) {
return res, nil
}
func (c *Client) skipIfNotFound(err error) error {
if apierrors.IsNotFound(err) {
c.Log("%v", err)
return nil
}
return err
}
func (c *Client) watchTimeout(t time.Duration) func(*resource.Info) error {
return func(info *resource.Info) error {
return c.watchUntilReady(t, info)
@@ -356,10 +519,10 @@ func (c *Client) watchTimeout(t time.Duration) func(*resource.Info) error {
// For most kinds, it checks to see if the resource is marked as Added or Modified
// by the Kubernetes event stream. For some kinds, it does more:
//
// - Jobs: A job is marked "Ready" when it has successfully completed. This is
// ascertained by watching the Status fields in a job's output.
// - Pods: A pod is marked "Ready" when it has successfully completed. This is
// ascertained by watching the status.phase field in a pod's output.
// - Jobs: A job is marked "Ready" when it has successfully completed. This is
// ascertained by watching the Status fields in a job's output.
// - Pods: A pod is marked "Ready" when it has successfully completed. This is
// ascertained by watching the status.phase field in a pod's output.
//
// Handling for other kinds will be added as necessary.
func (c *Client) WatchUntilReady(resources ResourceList, timeout time.Duration) error {

View File

@@ -22,6 +22,7 @@ import (
"time"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/cli-runtime/pkg/resource"
"helm.sh/helm/v3/pkg/kube"
@@ -33,11 +34,13 @@ import (
type FailingKubeClient struct {
PrintingKubeClient
CreateError error
GetError error
WaitError error
DeleteError error
WatchUntilReadyError error
UpdateError error
BuildError error
BuildTableError error
BuildUnstructuredError error
WaitAndGetCompletedPodPhaseError error
WaitDuration time.Duration
@@ -51,6 +54,14 @@ func (f *FailingKubeClient) Create(resources kube.ResourceList) (*kube.Result, e
return f.PrintingKubeClient.Create(resources)
}
// Get returns the configured error if set or prints
func (f *FailingKubeClient) Get(resources kube.ResourceList, related bool) (map[string][]runtime.Object, error) {
if f.GetError != nil {
return nil, f.GetError
}
return f.PrintingKubeClient.Get(resources, related)
}
// Waits the amount of time defined on f.WaitDuration, then returns the configured error if set or prints.
func (f *FailingKubeClient) Wait(resources kube.ResourceList, d time.Duration) error {
time.Sleep(f.WaitDuration)
@@ -108,6 +119,14 @@ func (f *FailingKubeClient) Build(r io.Reader, _ bool) (kube.ResourceList, error
return f.PrintingKubeClient.Build(r, false)
}
// BuildTable returns the configured error if set or prints
func (f *FailingKubeClient) BuildTable(r io.Reader, _ bool) (kube.ResourceList, error) {
if f.BuildTableError != nil {
return []*resource.Info{}, f.BuildTableError
}
return f.PrintingKubeClient.BuildTable(r, false)
}
// WaitAndGetCompletedPodPhase returns the configured error if set or prints
func (f *FailingKubeClient) WaitAndGetCompletedPodPhase(s string, d time.Duration) (v1.PodPhase, error) {
if f.WaitAndGetCompletedPodPhaseError != nil {

View File

@@ -22,6 +22,7 @@ import (
"time"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/cli-runtime/pkg/resource"
"helm.sh/helm/v3/pkg/kube"
@@ -47,6 +48,14 @@ func (p *PrintingKubeClient) Create(resources kube.ResourceList) (*kube.Result,
return &kube.Result{Created: resources}, nil
}
func (p *PrintingKubeClient) Get(resources kube.ResourceList, related bool) (map[string][]runtime.Object, error) {
_, err := io.Copy(p.Out, bufferize(resources))
if err != nil {
return nil, err
}
return make(map[string][]runtime.Object), nil
}
func (p *PrintingKubeClient) Wait(resources kube.ResourceList, _ time.Duration) error {
_, err := io.Copy(p.Out, bufferize(resources))
return err
@@ -96,6 +105,11 @@ func (p *PrintingKubeClient) Build(_ io.Reader, _ bool) (kube.ResourceList, erro
return []*resource.Info{}, nil
}
// BuildTable implements KubeClient BuildTable.
func (p *PrintingKubeClient) BuildTable(_ io.Reader, _ bool) (kube.ResourceList, error) {
return []*resource.Info{}, nil
}
// WaitAndGetCompletedPodPhase implements KubeClient WaitAndGetCompletedPodPhase.
func (p *PrintingKubeClient) WaitAndGetCompletedPodPhase(_ string, _ time.Duration) (v1.PodPhase, error) {
return v1.PodSucceeded, nil

View File

@@ -21,6 +21,7 @@ import (
"time"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
)
// Interface represents a client capable of communicating with the Kubernetes API.
@@ -78,5 +79,28 @@ type InterfaceExt interface {
WaitForDelete(resources ResourceList, timeout time.Duration) error
}
// InterfaceResources is introduced to avoid breaking backwards compatibility for Interface implementers.
//
// TODO Helm 4: Remove InterfaceResources and integrate its method(s) into the Interface.
type InterfaceResources interface {
// Get details of deployed resources.
// The first argument is a list of resources to get. The second argument
// specifies if related pods should be fetched. For example, the pods being
// managed by a deployment.
Get(resources ResourceList, related bool) (map[string][]runtime.Object, error)
// BuildTable creates a resource list from a Reader. This differs from
// Interface.Build() in that a table kind is returned. A table is useful
// if you want to use a printer to display the information.
//
// Reader must contain a YAML stream (one or more YAML documents separated
// by "\n---\n")
//
// Validates against OpenAPI schema if validate is true.
// TODO Helm 4: Integrate into Build with an argument
BuildTable(reader io.Reader, validate bool) (ResourceList, error)
}
var _ Interface = (*Client)(nil)
var _ InterfaceExt = (*Client)(nil)
var _ InterfaceResources = (*Client)(nil)

View File

@@ -291,7 +291,7 @@ func (c *ReadyChecker) daemonSetReady(ds *appsv1.DaemonSet) bool {
c.log("DaemonSet is not ready: %s/%s. %d out of %d expected pods have been scheduled", ds.Namespace, ds.Name, ds.Status.UpdatedNumberScheduled, ds.Status.DesiredNumberScheduled)
return false
}
maxUnavailable, err := intstr.GetValueFromIntOrPercent(ds.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable, int(ds.Status.DesiredNumberScheduled), true)
maxUnavailable, err := intstr.GetScaledValueFromIntOrPercent(ds.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable, int(ds.Status.DesiredNumberScheduled), true)
if err != nil {
// If for some reason the value is invalid, set max unavailable to the
// number of desired replicas. This is the same behavior as the

View File

@@ -22,9 +22,6 @@ import (
"time"
"github.com/pkg/errors"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
"google.golang.org/grpc/codes"
appsv1 "k8s.io/api/apps/v1"
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
@@ -35,6 +32,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/wait"
)
@@ -44,22 +42,6 @@ type waiter struct {
log func(string, ...interface{})
}
// isServiceUnavailable helps figure out if the error is caused by etcd not being available
// see https://pkg.go.dev/go.etcd.io/etcd/api/v3/v3rpc/rpctypes for `codes.Unavailable`
// we use this to check if the etcdserver is not available we should retry in case
// this is a temporary situation
func isServiceUnavailable(err error) bool {
if err != nil {
err = rpctypes.Error(err)
if ev, ok := err.(rpctypes.EtcdError); ok {
if ev.Code() == codes.Unavailable {
return true
}
}
}
return false
}
// waitForResources polls to get the current status of all pods, PVCs, Services and
// Jobs(optional) until all are ready or a timeout is reached
func (w *waiter) waitForResources(created ResourceList) error {
@@ -72,9 +54,6 @@ func (w *waiter) waitForResources(created ResourceList) error {
for _, v := range created {
ready, err := w.c.IsReady(ctx, v)
if !ready || err != nil {
if isServiceUnavailable(err) {
return false, nil
}
return false, err
}
}
@@ -93,9 +72,6 @@ func (w *waiter) waitForDeletedResources(deleted ResourceList) error {
for _, v := range deleted {
err := v.Get()
if err == nil || !apierrors.IsNotFound(err) {
if isServiceUnavailable(err) {
return false, nil
}
return false, err
}
}

View File

@@ -16,6 +16,8 @@ limitations under the License.
package release
import (
"k8s.io/apimachinery/pkg/runtime"
"helm.sh/helm/v3/pkg/time"
)
@@ -33,4 +35,6 @@ type Info struct {
Status Status `json:"status,omitempty"`
// Contains the rendered templates/NOTES.txt if available
Notes string `json:"notes,omitempty"`
// Contains the deployed resources information
Resources map[string][]runtime.Object `json:"resources,omitempty"`
}

View File

@@ -25,7 +25,6 @@ import (
"log"
"net/url"
"os"
"path"
"path/filepath"
"strings"
@@ -116,15 +115,11 @@ func (r *ChartRepository) Load() error {
// DownloadIndexFile fetches the index from a repository.
func (r *ChartRepository) DownloadIndexFile() (string, error) {
parsedURL, err := url.Parse(r.Config.URL)
indexURL, err := ResolveReferenceURL(r.Config.URL, "index.yaml")
if err != nil {
return "", err
}
parsedURL.RawPath = path.Join(parsedURL.RawPath, "index.yaml")
parsedURL.Path = path.Join(parsedURL.Path, "index.yaml")
indexURL := parsedURL.String()
// TODO add user-agent
resp, err := r.Client.Get(indexURL,
getter.WithURL(r.Config.URL),
getter.WithInsecureSkipVerifyTLS(r.Config.InsecureSkipTLSverify),
@@ -219,7 +214,7 @@ func FindChartInAuthRepoURL(repoURL, username, password, chartName, chartVersion
// but it also receives credentials and TLS verify flag for the chart repository.
// TODO Helm 4, FindChartInAuthAndTLSRepoURL should be integrated into FindChartInAuthRepoURL.
func FindChartInAuthAndTLSRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, insecureSkipTLSverify bool, getters getter.Providers) (string, error) {
return FindChartInAuthAndTLSAndPassRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile, false, false, getters)
return FindChartInAuthAndTLSAndPassRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile, insecureSkipTLSverify, false, getters)
}
// FindChartInAuthAndTLSAndPassRepoURL finds chart in chart repository pointed by repoURL
@@ -290,18 +285,27 @@ func FindChartInAuthAndTLSAndPassRepoURL(repoURL, username, password, chartName,
// ResolveReferenceURL resolves refURL relative to baseURL.
// If refURL is absolute, it simply returns refURL.
func ResolveReferenceURL(baseURL, refURL string) (string, error) {
// We need a trailing slash for ResolveReference to work, but make sure there isn't already one
parsedBaseURL, err := url.Parse(strings.TrimSuffix(baseURL, "/") + "/")
if err != nil {
return "", errors.Wrapf(err, "failed to parse %s as URL", baseURL)
}
parsedRefURL, err := url.Parse(refURL)
if err != nil {
return "", errors.Wrapf(err, "failed to parse %s as URL", refURL)
}
return parsedBaseURL.ResolveReference(parsedRefURL).String(), nil
if parsedRefURL.IsAbs() {
return refURL, nil
}
parsedBaseURL, err := url.Parse(baseURL)
if err != nil {
return "", errors.Wrapf(err, "failed to parse %s as URL", baseURL)
}
// We need a trailing slash for ResolveReference to work, but make sure there isn't already one
parsedBaseURL.RawPath = strings.TrimSuffix(parsedBaseURL.RawPath, "/") + "/"
parsedBaseURL.Path = strings.TrimSuffix(parsedBaseURL.Path, "/") + "/"
resolvedURL := parsedBaseURL.ResolveReference(parsedRefURL)
resolvedURL.RawQuery = parsedBaseURL.RawQuery
return resolvedURL.String(), nil
}
func (e *Entry) String() string {