Merge branch 'helm:main' into feature/rollback-revision-history

pull/31859/head
MrJack 4 weeks ago committed by GitHub
commit 16c6662566
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -55,7 +55,7 @@ jobs:
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: SARIF file
path: results.sarif

@ -23,7 +23,7 @@ require (
github.com/gofrs/flock v0.13.0
github.com/gosuri/uitable v0.0.4
github.com/jmoiron/sqlx v1.4.0
github.com/lib/pq v1.12.2
github.com/lib/pq v1.12.3
github.com/mattn/go-shellwords v1.0.12
github.com/moby/term v0.5.2
github.com/opencontainers/go-digest v1.0.0
@ -35,7 +35,7 @@ require (
github.com/stretchr/testify v1.11.1
github.com/tetratelabs/wazero v1.11.0
go.yaml.in/yaml/v3 v3.0.4
golang.org/x/crypto v0.49.0
golang.org/x/crypto v0.50.0
golang.org/x/term v0.42.0
golang.org/x/text v0.36.0
gopkg.in/yaml.v3 v3.0.1 // indirect

@ -194,8 +194,8 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtB
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.12.2 h1:ajJNv84limnK3aPbDIhLtcjrUbqAw/5XNdkuI6KNe/Q=
github.com/lib/pq v1.12.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ=
github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
@ -385,8 +385,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=

@ -27,6 +27,7 @@ import (
"path"
"path/filepath"
"slices"
"sort"
"strings"
"sync"
"text/template"
@ -144,39 +145,6 @@ const (
filenameAnnotation = "postrenderer.helm.sh/postrender-filename"
)
// fixDocSeparators ensures YAML document separators ("---") are always
// followed by a newline in rendered template content. Go template whitespace
// trimming ({{-) can remove the newline after "---", producing e.g.
// "---apiVersion: v1" which is not a valid YAML document separator.
// This function inserts a newline after any "---" at the start of a line
// that is immediately followed by non-whitespace content.
func fixDocSeparators(content string) string {
var b strings.Builder
remaining := content
for {
// Find "---" at the start of a line (or start of content).
idx := strings.Index(remaining, "---")
if idx == -1 {
b.WriteString(remaining)
break
}
// "---" must be at the start of a line: either idx==0 or preceded by '\n'.
if idx > 0 && remaining[idx-1] != '\n' {
b.WriteString(remaining[:idx+3])
remaining = remaining[idx+3:]
continue
}
b.WriteString(remaining[:idx+3])
remaining = remaining[idx+3:]
// If "---" is followed by non-whitespace (e.g. "---apiVersion"),
// insert a newline to make it a proper document separator.
if len(remaining) > 0 && remaining[0] != '\n' && remaining[0] != '\r' && remaining[0] != ' ' && remaining[0] != '\t' {
b.WriteByte('\n')
}
}
return b.String()
}
// annotateAndMerge combines multiple YAML files into a single stream of documents,
// adding filename annotations to each document for later reconstruction.
func annotateAndMerge(files map[string]string) (string, error) {
@ -192,22 +160,32 @@ func annotateAndMerge(files map[string]string) (string, error) {
continue
}
// Fix document separators where Go template whitespace trimming
// ({{-) has removed the newline after "---", producing e.g.
// "---apiVersion: v1" which is not a valid YAML document
// separator. Insert the missing newline so kio.ParseAll can
// parse the content correctly.
content = fixDocSeparators(content)
manifests, err := kio.ParseAll(content)
if err != nil {
return "", fmt.Errorf("parsing %s: %w", fname, err)
// For consistency with the non-post-renderers code path, we need
// to use releaseutil.SplitManifests here to split the file into
// individual documents before feeding them to kio.ParseAll. In
// Chart API before v3 this function had leniency for badly-written
// Go templates, so this must be preserved for older charts.
splitDocs := releaseutil.SplitManifests(content)
keys := make([]string, 0, len(splitDocs))
for k := range splitDocs {
keys = append(keys, k)
}
for _, manifest := range manifests {
if err := manifest.PipeE(kyaml.SetAnnotation(filenameAnnotation, fname)); err != nil {
return "", fmt.Errorf("annotating %s: %w", fname, err)
sort.Sort(releaseutil.BySplitManifestsOrder(keys))
for _, key := range keys {
doc := splitDocs[key]
if strings.TrimSpace(doc) == "" {
continue
}
manifests, err := kio.ParseAll(doc)
if err != nil {
return "", fmt.Errorf("parsing %s: %w", fname, err)
}
for _, manifest := range manifests {
if err := manifest.PipeE(kyaml.SetAnnotation(filenameAnnotation, fname)); err != nil {
return "", fmt.Errorf("annotating %s: %w", fname, err)
}
combinedManifests = append(combinedManifests, manifest)
}
combinedManifests = append(combinedManifests, manifest)
}
}

@ -403,96 +403,6 @@ func (m *mockPostRenderer) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer,
return bytes.NewBufferString(content), nil
}
func TestFixDocSeparators(t *testing.T) {
tests := []struct {
name string
input string
expected string
}{
{
name: "no separator",
input: "apiVersion: v1\nkind: Service\n",
expected: "apiVersion: v1\nkind: Service\n",
},
{
name: "separator on its own line",
input: "---\napiVersion: v1\nkind: Service\n",
expected: "---\napiVersion: v1\nkind: Service\n",
},
{
name: "leading separator glued to content",
input: "---apiVersion: v1\nkind: Service\n",
expected: "---\napiVersion: v1\nkind: Service\n",
},
{
name: "mid-content separator glued to content",
input: "apiVersion: v1\nkind: ConfigMap\n---apiVersion: v1\nkind: Service\n",
expected: "apiVersion: v1\nkind: ConfigMap\n---\napiVersion: v1\nkind: Service\n",
},
{
name: "multiple separators all proper",
input: "---\napiVersion: v1\n---\napiVersion: v1\n",
expected: "---\napiVersion: v1\n---\napiVersion: v1\n",
},
{
name: "multiple separators some glued",
input: "---apiVersion: v1\nkind: ConfigMap\n---apiVersion: v1\nkind: Service\n",
expected: "---\napiVersion: v1\nkind: ConfigMap\n---\napiVersion: v1\nkind: Service\n",
},
{
name: "empty string",
input: "",
expected: "",
},
{
name: "only separator",
input: "---\n",
expected: "---\n",
},
{
name: "triple dash in a value is not a separator",
input: "data:\n key: ---value\n",
expected: "data:\n key: ---value\n",
},
{
name: "realistic multi-doc template output",
input: "apiVersion: v1\nkind: Deployment\n---\napiVersion: v1\nkind: Ingress\n---apiVersion: v1\nkind: Service\n",
expected: "apiVersion: v1\nkind: Deployment\n---\napiVersion: v1\nkind: Ingress\n---\napiVersion: v1\nkind: Service\n",
},
{
name: "separator followed by carriage return",
input: "---\r\napiVersion: v1\n",
expected: "---\r\napiVersion: v1\n",
},
{
name: "separator followed by space",
input: "--- \napiVersion: v1\n",
expected: "--- \napiVersion: v1\n",
},
{
name: "separator followed by tab",
input: "---\t\napiVersion: v1\n",
expected: "---\t\napiVersion: v1\n",
},
{
name: "four dashes on its own line",
input: "----\napiVersion: v1\n",
expected: "---\n-\napiVersion: v1\n",
},
{
name: "four dashes followed by text",
input: "----more\napiVersion: v1\n",
expected: "---\n-more\napiVersion: v1\n",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.expected, fixDocSeparators(tt.input))
})
}
}
func TestAnnotateAndMerge(t *testing.T) {
tests := []struct {
name string
@ -690,6 +600,339 @@ metadata:
name: test-svc
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/all.yaml'
`,
},
{
name: "ConfigMap with embedded CA certificate",
files: map[string]string{
"templates/configmap.yaml": `
apiVersion: v1
kind: ConfigMap
metadata:
name: ca-bundle
data:
ca.crt: |
------BEGIN CERTIFICATE------
MIICEzCCAXygAwIBAgIQMIMChMLGrR+QvmQvpwAU6zAKBggqhkjOPQQDAzASMRAw
DgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYwMDAw
WjASMRAwDgYDVQQKEwdBY21lIENvMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE7Rmm
------END CERTIFICATE------
------BEGIN CERTIFICATE------
MIICEzCCAXygAwIBAgIQMIMChMLGrR+QvmQvpwAU6zAKBggqhkjOPQQDAzASMRAw
DgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYwMDAw
WjASMRAwDgYDVQQKEwdBY21lIENvMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE7Rmm
------END CERTIFICATE------
`,
},
expected: `apiVersion: v1
kind: ConfigMap
metadata:
name: ca-bundle
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/configmap.yaml'
data:
ca.crt: |-
------BEGIN CERTIFICATE------
MIICEzCCAXygAwIBAgIQMIMChMLGrR+QvmQvpwAU6zAKBggqhkjOPQQDAzASMRAw
DgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYwMDAw
WjASMRAwDgYDVQQKEwdBY21lIENvMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE7Rmm
------END CERTIFICATE------
------BEGIN CERTIFICATE------
MIICEzCCAXygAwIBAgIQMIMChMLGrR+QvmQvpwAU6zAKBggqhkjOPQQDAzASMRAw
DgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYwMDAw
WjASMRAwDgYDVQQKEwdBY21lIENvMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE7Rmm
------END CERTIFICATE------
`,
},
{
name: "consecutive dashes in YAML value are not treated as document separators",
files: map[string]string{
"templates/configmap.yaml": `
apiVersion: v1
kind: ConfigMap
metadata:
name: test-cm
data:
config: |
# ---------------------------------------------------------------------------
[section]
key = value
# ---------------------------------------------------------------------------
`,
},
expected: `apiVersion: v1
kind: ConfigMap
metadata:
name: test-cm
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/configmap.yaml'
data:
config: |-
# ---------------------------------------------------------------------------
[section]
key = value
# ---------------------------------------------------------------------------
`,
},
{
name: "JSON with dashes in values is not corrupted",
files: map[string]string{
"templates/dashboard.yaml": `
apiVersion: v1
kind: ConfigMap
metadata:
name: dashboard
data:
dashboard.json: |
{"options":{"---------":{"color":"#292929","text":"N/A"}}}
`,
},
expected: `apiVersion: v1
kind: ConfigMap
metadata:
name: dashboard
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/dashboard.yaml'
data:
dashboard.json: |-
{"options":{"---------":{"color":"#292929","text":"N/A"}}}
`,
},
// **Note for Chart API v3**: This input should return an _ERROR_ in Chart API v3.
// See the comment on the releaseutil.SplitManifests function for more details.
{
name: "multiple glued separators in same file",
files: map[string]string{
"templates/multi.yaml": `
---apiVersion: v1
kind: ConfigMap
metadata:
name: cm1
---apiVersion: v1
kind: ConfigMap
metadata:
name: cm2
---apiVersion: v1
kind: ConfigMap
metadata:
name: cm3
`,
},
expected: `apiVersion: v1
kind: ConfigMap
metadata:
name: cm1
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/multi.yaml'
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm2
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/multi.yaml'
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm3
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/multi.yaml'
`,
},
// **Note for Chart API v3**: This input should return an _ERROR_ in Chart API v3.
// See the comment on the releaseutil.SplitManifests function for more details.
{
name: "mixed glued and proper separators",
files: map[string]string{
"templates/mixed.yaml": `
apiVersion: v1
kind: ConfigMap
metadata:
name: cm1
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm2
---apiVersion: v1
kind: ConfigMap
metadata:
name: cm3
`,
},
expected: `apiVersion: v1
kind: ConfigMap
metadata:
name: cm1
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/mixed.yaml'
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm2
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/mixed.yaml'
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm3
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/mixed.yaml'
`,
},
{
name: "12 documents preserve in-file order",
files: map[string]string{
"templates/many.yaml": `
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-01
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-02
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-03
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-04
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-05
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-06
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-07
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-08
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-09
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-10
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-11
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-12
`,
},
expected: `apiVersion: v1
kind: ConfigMap
metadata:
name: cm-01
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/many.yaml'
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-02
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/many.yaml'
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-03
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/many.yaml'
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-04
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/many.yaml'
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-05
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/many.yaml'
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-06
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/many.yaml'
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-07
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/many.yaml'
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-08
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/many.yaml'
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-09
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/many.yaml'
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-10
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/many.yaml'
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-11
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/many.yaml'
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-12
annotations:
postrenderer.helm.sh/postrender-filename: 'templates/many.yaml'
`,
},
}

@ -1680,8 +1680,6 @@ func TestMethodContextOverridesGeneralContext(t *testing.T) {
t.Run("method-specific context overrides general context for WaitForDelete", func(t *testing.T) {
t.Parallel()
c := newTestClient(t)
timeout := time.Second
timeUntilPodDelete := time.Millisecond * 500
fakeClient := dynamicfake.NewSimpleDynamicClient(scheme.Scheme)
fakeMapper := testutil.NewFakeRESTMapper(
v1.SchemeGroupVersion.WithKind("Pod"),
@ -1698,27 +1696,14 @@ func TestMethodContextOverridesGeneralContext(t *testing.T) {
waitForDeleteCtx: context.Background(), // Not cancelled - should be used
}
// Use a non-existent resource: WaitForDelete should return immediately since
// the pod is already in the desired "deleted" state.
// This also validates context selection: if generalCtx (cancelled) were
// incorrectly used instead of waitForDeleteCtx, the watch context would be
// immediately cancelled and the call would return a context error.
objs := getRuntimeObjFromManifests(t, []string{podCurrentManifest})
for _, obj := range objs {
u := obj.(*unstructured.Unstructured)
gvr := getGVR(t, fakeMapper, u)
err := fakeClient.Tracker().Create(gvr, u, u.GetNamespace())
require.NoError(t, err)
}
// Schedule deletion
for _, obj := range objs {
u := obj.(*unstructured.Unstructured)
gvr := getGVR(t, fakeMapper, u)
go func(gvr schema.GroupVersionResource, u *unstructured.Unstructured) {
time.Sleep(timeUntilPodDelete)
err := fakeClient.Tracker().Delete(gvr, u.GetNamespace(), u.GetName())
assert.NoError(t, err)
}(gvr, u)
}
resourceList := getResourceListFromRuntimeObjs(t, c, objs)
err := sw.WaitForDelete(resourceList, timeout)
err := sw.WaitForDelete(resourceList, time.Second)
// Should succeed because method context is used and it's not cancelled
assert.NoError(t, err)
})

@ -36,6 +36,15 @@ type SimpleHead struct {
var sep = regexp.MustCompile("(?:^|\\s*\n)---\\s*")
// SplitManifests takes a string of manifest and returns a map contains individual manifests
//
// **Note for Chart API v3**: This function (due to the regex above) has allowed _WRONG_
// Go templates to be defined inside charts across the years. The generated text from Go
// templates may contain `---apiVersion: v1`, and this function magically splits this back
// to `---\napiVersion: v1`. This has caused issues recently after Helm 4 introduced
// kio.ParseAll to inject annotations when post-renderers are used. In Chart API v3,
// we should kill this regex with fire (or change it) and expose charts doing the wrong
// thing Go template-wise. Helm should say a big _NO_ to charts doing the wrong thing,
// with or without post-renderers.
func SplitManifests(bigFile string) map[string]string {
// Basically, we're quickly splitting a stream of YAML documents into an
// array of YAML docs. The file name is just a place holder, but should be

Loading…
Cancel
Save