Upgrade dependent version: helm.sh/helm/v3 v3.9.0 -> v3.10.3 (#5420)

Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io>

Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io>
This commit is contained in:
hongzhouzi
2022-12-20 11:24:52 +08:00
committed by GitHub
parent 146830dfcb
commit 188c7bc7d8
32 changed files with 349 additions and 92 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.9"
version = "v3.10"
// metadata is extra build time data
metadata = ""

View File

@@ -712,6 +712,10 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (
RegistryClient: c.registryClient,
}
if registry.IsOCI(name) {
dl.Options = append(dl.Options, getter.WithRegistryClient(c.registryClient))
}
if c.Verify {
dl.Verify = downloader.VerifyAlways
}
@@ -751,20 +755,13 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (
}
filename, _, err := dl.DownloadTo(name, version, settings.RepositoryCache)
if err == nil {
lname, err := filepath.Abs(filename)
if err != nil {
return filename, err
}
return lname, nil
} else if settings.Debug {
if err != nil {
return "", err
}
lname, err := filepath.Abs(filename)
if err != nil {
return filename, err
}
atVersion := ""
if version != "" {
atVersion = fmt.Sprintf(" at version %q", version)
}
return filename, errors.Errorf("failed to download %q%s", name, atVersion)
return lname, nil
}

View File

@@ -76,7 +76,7 @@ func (l *Lint) Run(paths []string, vals map[string]interface{}) *LintResult {
return result
}
// HasWaringsOrErrors checks is LintResult has any warnings or errors
// HasWarningsOrErrors checks is LintResult has any warnings or errors
func HasWarningsOrErrors(result *LintResult) bool {
for _, msg := range result.Messages {
if msg.Severity > support.InfoSev {

View File

@@ -125,6 +125,7 @@ type List struct {
// Filter is a filter that is applied to the results
Filter string
Short bool
NoHeaders bool
TimeFormat string
Uninstalled bool
Superseded bool

View File

@@ -113,6 +113,10 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
}
deletedResources, kept, errs := u.deleteRelease(rel)
if errs != nil {
u.cfg.Log("uninstall: Failed to delete release: %s", errs)
return nil, errors.Errorf("failed to delete release: %s", name)
}
if kept != "" {
kept = "These resources were kept due to the resource policy:\n" + kept

View File

@@ -391,6 +391,9 @@ func (u *Upgrade) releasingUpgrade(c chan<- resultMessage, upgradedRelease *rele
}
if u.Wait {
u.cfg.Log(
"waiting for release %s resources (created: %d updated: %d deleted: %d)",
upgradedRelease.Name, len(results.Created), len(results.Updated), len(results.Deleted))
if u.WaitForJobs {
if err := u.cfg.KubeClient.WaitWithJobs(target, u.Timeout); err != nil {
u.cfg.recordRelease(originalRelease)

View File

@@ -53,6 +53,9 @@ type Dependency struct {
// the chart. This check must be done at load time before the dependency's charts are
// loaded.
func (d *Dependency) Validate() error {
if d == nil {
return ValidationError("dependency cannot be an empty list")
}
d.Name = sanitizeString(d.Name)
d.Version = sanitizeString(d.Version)
d.Repository = sanitizeString(d.Repository)

View File

@@ -34,6 +34,9 @@ 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")
}
m.Name = sanitizeString(m.Name)
m.Email = sanitizeString(m.Email)
m.URL = sanitizeString(m.URL)

View File

@@ -312,7 +312,7 @@ spec:
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 80
containerPort: {{ .Values.service.port }}
protocol: TCP
livenessProbe:
httpGet:

View File

@@ -55,7 +55,13 @@ func ValidateAgainstSchema(chrt *chart.Chart, values map[string]interface{}) err
}
// ValidateAgainstSingleSchema checks that values does not violate the structure laid out in this schema
func ValidateAgainstSingleSchema(values Values, schemaJSON []byte) error {
func ValidateAgainstSingleSchema(values Values, schemaJSON []byte) (reterr error) {
defer func() {
if r := recover(); r != nil {
reterr = fmt.Errorf("unable to validate schema: %s", r)
}
}()
valuesData, err := yaml.Marshal(values)
if err != nil {
return err

View File

@@ -68,7 +68,7 @@ func (v Values) Table(name string) (Values, error) {
//
// It protects against nil map panics.
func (v Values) AsMap() map[string]interface{} {
if v == nil || len(v) == 0 {
if len(v) == 0 {
return map[string]interface{}{}
}
return v

View File

@@ -30,6 +30,7 @@ import (
"github.com/spf13/pflag"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/rest"
"helm.sh/helm/v3/pkg/helmpath"
)
@@ -37,6 +38,9 @@ import (
// defaultMaxHistory sets the maximum number of releases to 0: unlimited
const defaultMaxHistory = 10
// defaultBurstLimit sets the default client-side throttling limit
const defaultBurstLimit = 100
// EnvSettings describes all of the environment settings.
type EnvSettings struct {
namespace string
@@ -56,6 +60,12 @@ type EnvSettings struct {
KubeAPIServer string
// Custom certificate authority file.
KubeCaFile string
// KubeInsecureSkipTLSVerify indicates if server's certificate will not be checked for validity.
// This makes the HTTPS connections insecure
KubeInsecureSkipTLSVerify bool
// KubeTLSServerName overrides the name to use for server certificate validation.
// If it is not provided, the hostname used to contact the server is used
KubeTLSServerName string
// Debug indicates whether or not Helm is running in Debug mode.
Debug bool
// RegistryConfig is the path to the registry config file.
@@ -68,22 +78,27 @@ type EnvSettings struct {
PluginsDirectory string
// MaxHistory is the max release history maintained.
MaxHistory int
// BurstLimit is the default client-side throttling limit.
BurstLimit int
}
func New() *EnvSettings {
env := &EnvSettings{
namespace: os.Getenv("HELM_NAMESPACE"),
MaxHistory: envIntOr("HELM_MAX_HISTORY", defaultMaxHistory),
KubeContext: os.Getenv("HELM_KUBECONTEXT"),
KubeToken: os.Getenv("HELM_KUBETOKEN"),
KubeAsUser: os.Getenv("HELM_KUBEASUSER"),
KubeAsGroups: envCSV("HELM_KUBEASGROUPS"),
KubeAPIServer: os.Getenv("HELM_KUBEAPISERVER"),
KubeCaFile: os.Getenv("HELM_KUBECAFILE"),
PluginsDirectory: envOr("HELM_PLUGINS", helmpath.DataPath("plugins")),
RegistryConfig: envOr("HELM_REGISTRY_CONFIG", helmpath.ConfigPath("registry/config.json")),
RepositoryConfig: envOr("HELM_REPOSITORY_CONFIG", helmpath.ConfigPath("repositories.yaml")),
RepositoryCache: envOr("HELM_REPOSITORY_CACHE", helmpath.CachePath("repository")),
namespace: os.Getenv("HELM_NAMESPACE"),
MaxHistory: envIntOr("HELM_MAX_HISTORY", defaultMaxHistory),
KubeContext: os.Getenv("HELM_KUBECONTEXT"),
KubeToken: os.Getenv("HELM_KUBETOKEN"),
KubeAsUser: os.Getenv("HELM_KUBEASUSER"),
KubeAsGroups: envCSV("HELM_KUBEASGROUPS"),
KubeAPIServer: os.Getenv("HELM_KUBEAPISERVER"),
KubeCaFile: os.Getenv("HELM_KUBECAFILE"),
KubeTLSServerName: os.Getenv("HELM_KUBETLS_SERVER_NAME"),
KubeInsecureSkipTLSVerify: envBoolOr("HELM_KUBEINSECURE_SKIP_TLS_VERIFY", false),
PluginsDirectory: envOr("HELM_PLUGINS", helmpath.DataPath("plugins")),
RegistryConfig: envOr("HELM_REGISTRY_CONFIG", helmpath.ConfigPath("registry/config.json")),
RepositoryConfig: envOr("HELM_REPOSITORY_CONFIG", helmpath.ConfigPath("repositories.yaml")),
RepositoryCache: envOr("HELM_REPOSITORY_CACHE", helmpath.CachePath("repository")),
BurstLimit: envIntOr("HELM_BURST_LIMIT", defaultBurstLimit),
}
env.Debug, _ = strconv.ParseBool(os.Getenv("HELM_DEBUG"))
@@ -96,7 +111,13 @@ func New() *EnvSettings {
CAFile: &env.KubeCaFile,
KubeConfig: &env.KubeConfig,
Impersonate: &env.KubeAsUser,
Insecure: &env.KubeInsecureSkipTLSVerify,
TLSServerName: &env.KubeTLSServerName,
ImpersonateGroup: &env.KubeAsGroups,
WrapConfigFn: func(config *rest.Config) *rest.Config {
config.Burst = env.BurstLimit
return config
},
}
return env
}
@@ -111,10 +132,13 @@ func (s *EnvSettings) AddFlags(fs *pflag.FlagSet) {
fs.StringArrayVar(&s.KubeAsGroups, "kube-as-group", s.KubeAsGroups, "group to impersonate for the operation, this flag can be repeated to specify multiple groups.")
fs.StringVar(&s.KubeAPIServer, "kube-apiserver", s.KubeAPIServer, "the address and the port for the Kubernetes API server")
fs.StringVar(&s.KubeCaFile, "kube-ca-file", s.KubeCaFile, "the certificate authority file for the Kubernetes API server connection")
fs.StringVar(&s.KubeTLSServerName, "kube-tls-server-name", s.KubeTLSServerName, "server name to use for Kubernetes API server certificate validation. If it is not provided, the hostname used to contact the server is used")
fs.BoolVar(&s.KubeInsecureSkipTLSVerify, "kube-insecure-skip-tls-verify", s.KubeInsecureSkipTLSVerify, "if true, the Kubernetes API server's certificate will not be checked for validity. This will make your HTTPS connections insecure")
fs.BoolVar(&s.Debug, "debug", s.Debug, "enable verbose output")
fs.StringVar(&s.RegistryConfig, "registry-config", s.RegistryConfig, "path to the registry config file")
fs.StringVar(&s.RepositoryConfig, "repository-config", s.RepositoryConfig, "path to the file containing repository names and URLs")
fs.StringVar(&s.RepositoryCache, "repository-cache", s.RepositoryCache, "path to the file containing cached repository indexes")
fs.IntVar(&s.BurstLimit, "burst-limit", s.BurstLimit, "client-side default throttling limit")
}
func envOr(name, def string) string {
@@ -124,6 +148,18 @@ func envOr(name, def string) string {
return def
}
func envBoolOr(name string, def bool) bool {
if name == "" {
return def
}
envVal := envOr(name, strconv.FormatBool(def))
ret, err := strconv.ParseBool(envVal)
if err != nil {
return def
}
return ret
}
func envIntOr(name string, def int) int {
if name == "" {
return def
@@ -157,14 +193,17 @@ func (s *EnvSettings) EnvVars() map[string]string {
"HELM_REPOSITORY_CONFIG": s.RepositoryConfig,
"HELM_NAMESPACE": s.Namespace(),
"HELM_MAX_HISTORY": strconv.Itoa(s.MaxHistory),
"HELM_BURST_LIMIT": strconv.Itoa(s.BurstLimit),
// broken, these are populated from helm flags and not kubeconfig.
"HELM_KUBECONTEXT": s.KubeContext,
"HELM_KUBETOKEN": s.KubeToken,
"HELM_KUBEASUSER": s.KubeAsUser,
"HELM_KUBEASGROUPS": strings.Join(s.KubeAsGroups, ","),
"HELM_KUBEAPISERVER": s.KubeAPIServer,
"HELM_KUBECAFILE": s.KubeCaFile,
"HELM_KUBECONTEXT": s.KubeContext,
"HELM_KUBETOKEN": s.KubeToken,
"HELM_KUBEASUSER": s.KubeAsUser,
"HELM_KUBEASGROUPS": strings.Join(s.KubeAsGroups, ","),
"HELM_KUBEAPISERVER": s.KubeAPIServer,
"HELM_KUBECAFILE": s.KubeCaFile,
"HELM_KUBEINSECURE_SKIP_TLS_VERIFY": strconv.FormatBool(s.KubeInsecureSkipTLSVerify),
"HELM_KUBETLS_SERVER_NAME": s.KubeTLSServerName,
}
if s.KubeConfig != "" {
envvars["KUBECONFIG"] = s.KubeConfig

View File

@@ -307,6 +307,7 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er
}
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()

View File

@@ -77,7 +77,7 @@ func NewLookupFunction(config *rest.Config) lookupFunc {
}
}
// getDynamicClientOnUnstructured returns a dynamic client on an Unstructured type. This client can be further namespaced.
// getDynamicClientOnKind returns a dynamic client on an Unstructured type. This client can be further namespaced.
func getDynamicClientOnKind(apiversion string, kind string, config *rest.Config) (dynamic.NamespaceableResourceInterface, bool, error) {
gvk := schema.FromAPIVersionAndKind(apiversion, kind)
apiRes, err := getAPIResourceForGVK(gvk, config)

View File

@@ -63,7 +63,9 @@ func (g *OCIGetter) get(href string) (*bytes.Buffer, error) {
// NewOCIGetter constructs a valid http/https client as a Getter
func NewOCIGetter(ops ...Option) (Getter, error) {
registryClient, err := registry.NewClient()
registryClient, err := registry.NewClient(
registry.ClientOptEnableCache(true),
)
if err != nil {
return nil, err
}

View File

@@ -353,9 +353,16 @@ func (c *ReadyChecker) crdReady(crd apiextv1.CustomResourceDefinition) bool {
func (c *ReadyChecker) statefulSetReady(sts *appsv1.StatefulSet) bool {
// If the update strategy is not a rolling update, there will be nothing to wait for
if sts.Spec.UpdateStrategy.Type != appsv1.RollingUpdateStatefulSetStrategyType {
c.log("StatefulSet skipped ready check: %s/%s. updateStrategy is %v", sts.Namespace, sts.Name, sts.Spec.UpdateStrategy.Type)
return true
}
// Make sure the status is up-to-date with the StatefulSet changes
if sts.Status.ObservedGeneration < sts.Generation {
c.log("StatefulSet is not ready: %s/%s. update has not yet been observed", sts.Namespace, sts.Name)
return false
}
// Dereference all the pointers because StatefulSets like them
var partition int
// 1 is the default for replicas if not set
@@ -386,6 +393,13 @@ func (c *ReadyChecker) statefulSetReady(sts *appsv1.StatefulSet) bool {
c.log("StatefulSet is not ready: %s/%s. %d out of %d expected pods are ready", sts.Namespace, sts.Name, sts.Status.ReadyReplicas, replicas)
return false
}
if sts.Status.CurrentRevision != sts.Status.UpdateRevision {
c.log("StatefulSet is not ready: %s/%s. currentRevision %s does not yet match updateRevision %s", sts.Namespace, sts.Name, sts.Status.CurrentRevision, sts.Status.UpdateRevision)
return false
}
c.log("StatefulSet is ready: %s/%s. %d out of %d expected pods are ready", sts.Namespace, sts.Name, sts.Status.ReadyReplicas, replicas)
return true
}

View File

@@ -22,6 +22,9 @@ 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"
@@ -32,7 +35,6 @@ 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"
)
@@ -42,6 +44,22 @@ 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 {
@@ -54,6 +72,9 @@ 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
}
}
@@ -72,6 +93,9 @@ 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

@@ -85,7 +85,9 @@ func (pusher *OCIPusher) push(chartRef, href string) error {
// NewOCIPusher constructs a valid OCI client as a Pusher
func NewOCIPusher(ops ...Option) (Pusher, error) {
registryClient, err := registry.NewClient()
registryClient, err := registry.NewClient(
registry.ClientOptEnableCache(true),
)
if err != nil {
return nil, err
}

View File

@@ -53,7 +53,8 @@ a plus (+) when pulling from a registry.`
type (
// Client works with OCI-compliant registries
Client struct {
debug bool
debug bool
enableCache bool
// path to repository config file e.g. ~/.docker/config.json
credentialsFile string
out io.Writer
@@ -95,12 +96,18 @@ func NewClient(options ...ClientOption) (*Client, error) {
}
client.resolver = resolver
}
// allocate a cache if option is set
var cache registryauth.Cache
if client.enableCache {
cache = registryauth.DefaultCache
}
if client.registryAuthorizer == nil {
client.registryAuthorizer = &registryauth.Client{
Header: http.Header{
"User-Agent": {version.GetUserAgent()},
},
Cache: registryauth.DefaultCache,
Cache: cache,
Credential: func(ctx context.Context, reg string) (registryauth.Credential, error) {
dockerClient, ok := client.authorizer.(*dockerauth.Client)
if !ok {
@@ -138,6 +145,13 @@ func ClientOptDebug(debug bool) ClientOption {
}
}
// ClientOptEnableCache returns a function that sets the enableCache setting on a client options set
func ClientOptEnableCache(enableCache bool) ClientOption {
return func(client *Client) {
client.enableCache = enableCache
}
}
// ClientOptWriter returns a function that sets the writer setting on client options set
func ClientOptWriter(out io.Writer) ClientOption {
return func(client *Client) {

View File

@@ -253,6 +253,10 @@ func FindChartInAuthAndTLSAndPassRepoURL(repoURL, username, password, chartName,
if err != nil {
return "", errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", repoURL)
}
defer func() {
os.RemoveAll(filepath.Join(r.CachePath, helmpath.CacheChartsFile(r.Config.Name)))
os.RemoveAll(filepath.Join(r.CachePath, helmpath.CacheIndexFile(r.Config.Name)))
}()
// Read the index file for the repository to get chart information and return chart URL
repoIndex, err := LoadIndexFile(idx)

View File

@@ -118,6 +118,10 @@ func LoadIndexFile(path string) (*IndexFile, error) {
// MustAdd adds a file to the index
// This can leave the index in an unsorted state
func (i IndexFile) MustAdd(md *chart.Metadata, filename, baseURL, digest string) error {
if i.Entries == nil {
return errors.New("entries not initialized")
}
if md.APIVersion == "" {
md.APIVersion = chart.APIVersionV1
}
@@ -339,6 +343,10 @@ func loadIndex(data []byte, source string) (*IndexFile, error) {
for name, cvs := range i.Entries {
for idx := len(cvs) - 1; idx >= 0; idx-- {
if cvs[idx] == nil {
log.Printf("skipping loading invalid entry for chart %q from %s: empty entry", name, source)
continue
}
if cvs[idx].APIVersion == "" {
cvs[idx].APIVersion = chart.APIVersionV1
}

View File

@@ -100,6 +100,9 @@ func (r *File) Remove(name string) bool {
cp := []*Entry{}
found := false
for _, rf := range r.Repositories {
if rf == nil {
continue
}
if rf.Name == name {
found = true
continue

View File

@@ -63,7 +63,7 @@ func decodeRelease(data string) (*rspb.Release, error) {
// For backwards compatibility with releases that were stored before
// compression was introduced we skip decompression if the
// gzip magic header is not found
if bytes.Equal(b[0:3], magicGzip) {
if len(b) > 3 && bytes.Equal(b[0:3], magicGzip) {
r, err := gzip.NewReader(bytes.NewReader(b))
if err != nil {
return nil, err

View File

@@ -17,10 +17,13 @@ package strvals
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"strconv"
"strings"
"unicode"
"github.com/pkg/errors"
"sigs.k8s.io/yaml"
@@ -29,6 +32,14 @@ import (
// ErrNotList indicates that a non-list was treated as a list.
var ErrNotList = errors.New("not a list")
// MaxIndex is the maximum index that will be allowed by setIndex.
// The default value 65536 = 1024 * 64
var MaxIndex = 65536
// MaxNestedNameLevel is the maximum level of nesting for a value name that
// will be allowed.
var MaxNestedNameLevel = 30
// ToYAML takes a string of arguments and converts to a YAML document.
func ToYAML(s string) (string, error) {
m, err := Parse(s)
@@ -94,6 +105,18 @@ func ParseIntoString(s string, dest map[string]interface{}) error {
return t.parse()
}
// ParseJSON parses a string with format key1=val1, key2=val2, ...
// where values are json strings (null, or scalars, or arrays, or objects).
// An empty val is treated as null.
//
// If a key exists in dest, the new value overwrites the dest version.
//
func ParseJSON(s string, dest map[string]interface{}) error {
scanner := bytes.NewBufferString(s)
t := newJSONParser(scanner, dest)
return t.parse()
}
// ParseIntoFile parses a filevals line and merges the result into dest.
//
// This method always returns a string as the value.
@@ -113,9 +136,10 @@ type RunesValueReader func([]rune) (interface{}, error)
// where sc is the source of the original data being parsed
// where data is the final parsed data from the parses with correct types
type parser struct {
sc *bytes.Buffer
data map[string]interface{}
reader RunesValueReader
sc *bytes.Buffer
data map[string]interface{}
reader RunesValueReader
isjsonval bool
}
func newParser(sc *bytes.Buffer, data map[string]interface{}, stringBool bool) *parser {
@@ -125,13 +149,17 @@ func newParser(sc *bytes.Buffer, data map[string]interface{}, stringBool bool) *
return &parser{sc: sc, data: data, reader: stringConverter}
}
func newJSONParser(sc *bytes.Buffer, data map[string]interface{}) *parser {
return &parser{sc: sc, data: data, reader: nil, isjsonval: true}
}
func newFileParser(sc *bytes.Buffer, data map[string]interface{}, reader RunesValueReader) *parser {
return &parser{sc: sc, data: data, reader: reader}
}
func (t *parser) parse() error {
for {
err := t.key(t.data)
err := t.key(t.data, 0)
if err == nil {
continue
}
@@ -150,7 +178,7 @@ func runeSet(r []rune) map[rune]bool {
return s
}
func (t *parser) key(data map[string]interface{}) (reterr error) {
func (t *parser) key(data map[string]interface{}, nestedNameLevel int) (reterr error) {
defer func() {
if r := recover(); r != nil {
reterr = fmt.Errorf("unable to parse key: %s", r)
@@ -180,10 +208,37 @@ func (t *parser) key(data map[string]interface{}) (reterr error) {
}
// Now we need to get the value after the ].
list, err = t.listItem(list, i)
list, err = t.listItem(list, i, nestedNameLevel)
set(data, kk, list)
return err
case last == '=':
if t.isjsonval {
empval, err := t.emptyVal()
if err != nil {
return err
}
if empval {
set(data, string(k), nil)
return nil
}
// parse jsonvals by using Gos JSON standard library
// Decode is preferred to Unmarshal in order to parse just the json parts of the list key1=jsonval1,key2=jsonval2,...
// Since Decode has its own buffer that consumes more characters (from underlying t.sc) than the ones actually decoded,
// we invoke Decode on a separate reader built with a copy of what is left in t.sc. After Decode is executed, we
// discard in t.sc the chars of the decoded json value (the number of those characters is returned by InputOffset).
var jsonval interface{}
dec := json.NewDecoder(strings.NewReader(t.sc.String()))
if err = dec.Decode(&jsonval); err != nil {
return err
}
set(data, string(k), jsonval)
if _, err = io.CopyN(ioutil.Discard, t.sc, dec.InputOffset()); err != nil {
return err
}
// skip possible blanks and comma
_, err = t.emptyVal()
return err
}
//End of key. Consume =, Get value.
// FIXME: Get value list first
vl, e := t.valList()
@@ -205,12 +260,17 @@ func (t *parser) key(data map[string]interface{}) (reterr error) {
default:
return e
}
case last == ',':
// No value given. Set the value to empty string. Return error.
set(data, string(k), "")
return errors.Errorf("key %q has no value (cannot end with ,)", string(k))
case last == '.':
// Check value name is within the maximum nested name level
nestedNameLevel++
if nestedNameLevel > MaxNestedNameLevel {
return fmt.Errorf("value name nested level is greater than maximum supported nested level of %d", MaxNestedNameLevel)
}
// First, create or find the target map.
inner := map[string]interface{}{}
if _, ok := data[string(k)]; ok {
@@ -218,11 +278,13 @@ func (t *parser) key(data map[string]interface{}) (reterr error) {
}
// Recurse
e := t.key(inner)
if len(inner) == 0 {
e := t.key(inner, nestedNameLevel)
if e == nil && len(inner) == 0 {
return errors.Errorf("key map %q has no value", string(k))
}
set(data, string(k), inner)
if len(inner) != 0 {
set(data, string(k), inner)
}
return e
}
}
@@ -249,6 +311,9 @@ func setIndex(list []interface{}, index int, val interface{}) (l2 []interface{},
if index < 0 {
return list, fmt.Errorf("negative %d index not allowed", index)
}
if index > MaxIndex {
return list, fmt.Errorf("index of %d is greater than maximum supported index of %d", index, MaxIndex)
}
if len(list) <= index {
newlist := make([]interface{}, index+1)
copy(newlist, list)
@@ -269,7 +334,7 @@ func (t *parser) keyIndex() (int, error) {
return strconv.Atoi(string(v))
}
func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) {
func (t *parser) listItem(list []interface{}, i, nestedNameLevel int) ([]interface{}, error) {
if i < 0 {
return list, fmt.Errorf("negative %d index not allowed", i)
}
@@ -280,6 +345,34 @@ func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) {
case err != nil:
return list, err
case last == '=':
if t.isjsonval {
empval, err := t.emptyVal()
if err != nil {
return list, err
}
if empval {
return setIndex(list, i, nil)
}
// parse jsonvals by using Gos JSON standard library
// Decode is preferred to Unmarshal in order to parse just the json parts of the list key1=jsonval1,key2=jsonval2,...
// Since Decode has its own buffer that consumes more characters (from underlying t.sc) than the ones actually decoded,
// we invoke Decode on a separate reader built with a copy of what is left in t.sc. After Decode is executed, we
// discard in t.sc the chars of the decoded json value (the number of those characters is returned by InputOffset).
var jsonval interface{}
dec := json.NewDecoder(strings.NewReader(t.sc.String()))
if err = dec.Decode(&jsonval); err != nil {
return list, err
}
if list, err = setIndex(list, i, jsonval); err != nil {
return list, err
}
if _, err = io.CopyN(ioutil.Discard, t.sc, dec.InputOffset()); err != nil {
return list, err
}
// skip possible blanks and comma
_, err = t.emptyVal()
return list, err
}
vl, e := t.valList()
switch e {
case nil:
@@ -314,7 +407,7 @@ func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) {
}
}
// Now we need to get the value after the ].
list2, err := t.listItem(crtList, nextI)
list2, err := t.listItem(crtList, nextI, nestedNameLevel)
if err != nil {
return list, err
}
@@ -333,7 +426,7 @@ func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) {
}
// Recurse
e := t.key(inner)
e := t.key(inner, nestedNameLevel)
if e != nil {
return list, e
}
@@ -343,6 +436,28 @@ func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) {
}
}
// check for an empty value
// read and consume optional spaces until comma or EOF (empty val) or any other char (not empty val)
// comma and spaces are consumed, while any other char is not cosumed
func (t *parser) emptyVal() (bool, error) {
for {
r, _, e := t.sc.ReadRune()
if e == io.EOF {
return true, nil
}
if e != nil {
return false, e
}
if r == ',' {
return true, nil
}
if !unicode.IsSpace(r) {
t.sc.UnreadRune()
return false, nil
}
}
}
func (t *parser) val() ([]rune, error) {
stop := runeSet([]rune{','})
v, _, err := runesUntil(t.sc, stop)

18
vendor/modules.txt vendored
View File

@@ -5,7 +5,7 @@ code.cloudfoundry.org/bytefmt
## explicit
github.com/Azure/go-ansiterm
github.com/Azure/go-ansiterm/winterm
# github.com/BurntSushi/toml v1.0.0 => github.com/BurntSushi/toml v0.3.1
# github.com/BurntSushi/toml v1.1.0 => github.com/BurntSushi/toml v0.3.1
## explicit
github.com/BurntSushi/toml
# github.com/MakeNowJust/heredoc v1.0.0 => github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e
@@ -20,7 +20,7 @@ github.com/Masterminds/semver/v3
# github.com/Masterminds/sprig/v3 v3.2.2 => github.com/Masterminds/sprig/v3 v3.0.0
## explicit; go 1.13
github.com/Masterminds/sprig/v3
# github.com/Masterminds/squirrel v1.5.2 => github.com/Masterminds/squirrel v0.0.0-20161115235646-20f192218cf5
# github.com/Masterminds/squirrel v1.5.3 => github.com/Masterminds/squirrel v0.0.0-20161115235646-20f192218cf5
## explicit
github.com/Masterminds/squirrel
# github.com/Microsoft/go-winio v0.5.1 => github.com/Microsoft/go-winio v0.4.12
@@ -293,7 +293,7 @@ github.com/emirpasic/gods/lists/arraylist
github.com/emirpasic/gods/trees
github.com/emirpasic/gods/trees/binaryheap
github.com/emirpasic/gods/utils
# github.com/evanphx/json-patch v4.12.0+incompatible => github.com/evanphx/json-patch v4.12.0+incompatible
# github.com/evanphx/json-patch v5.6.0+incompatible => github.com/evanphx/json-patch v4.12.0+incompatible
## explicit
github.com/evanphx/json-patch
# github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d => github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d
@@ -545,7 +545,7 @@ github.com/jbenet/go-context/io
# github.com/jmespath/go-jmespath v0.4.0 => github.com/jmespath/go-jmespath v0.3.0
## explicit; go 1.14
github.com/jmespath/go-jmespath
# github.com/jmoiron/sqlx v1.3.4 => github.com/jmoiron/sqlx v1.2.0
# github.com/jmoiron/sqlx v1.3.5 => github.com/jmoiron/sqlx v1.2.0
## explicit
github.com/jmoiron/sqlx
github.com/jmoiron/sqlx/reflectx
@@ -598,7 +598,7 @@ github.com/lann/builder
# github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 => github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0
## explicit
github.com/lann/ps
# github.com/lib/pq v1.10.4 => github.com/lib/pq v1.2.0
# github.com/lib/pq v1.10.6 => github.com/lib/pq v1.2.0
## explicit
github.com/lib/pq
github.com/lib/pq/oid
@@ -971,7 +971,7 @@ github.com/rcrowley/go-metrics
# github.com/robfig/cron/v3 v3.0.1 => github.com/robfig/cron/v3 v3.0.1
## explicit; go 1.12
github.com/robfig/cron/v3
# github.com/rubenv/sql-migrate v1.1.1 => github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351
# github.com/rubenv/sql-migrate v1.1.2 => github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351
## explicit; go 1.11
github.com/rubenv/sql-migrate
github.com/rubenv/sql-migrate/sqlparse
@@ -1500,8 +1500,8 @@ gotest.tools/assert/cmp
gotest.tools/internal/difflib
gotest.tools/internal/format
gotest.tools/internal/source
# helm.sh/helm/v3 v3.9.0 => helm.sh/helm/v3 v3.9.0
## explicit; go 1.17
# helm.sh/helm/v3 v3.10.3 => helm.sh/helm/v3 v3.10.3
## explicit; go 1.18
helm.sh/helm/v3/internal/fileutil
helm.sh/helm/v3/internal/ignore
helm.sh/helm/v3/internal/resolver
@@ -3281,7 +3281,7 @@ sigs.k8s.io/yaml
# gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
# gotest.tools => gotest.tools v2.2.0+incompatible
# gotest.tools/v3 => gotest.tools/v3 v3.0.3
# helm.sh/helm/v3 => helm.sh/helm/v3 v3.9.0
# helm.sh/helm/v3 => helm.sh/helm/v3 v3.10.3
# honnef.co/go/tools => honnef.co/go/tools v0.0.1-2020.1.3
# howett.net/plist => howett.net/plist v0.0.0-20181124034731-591f970eefbb
# istio.io/api => istio.io/api v0.0.0-20201113182140-d4b7e3fc2b44