mirror of https://github.com/helm/helm
fix(template): regression - route registry messages to stderr in template and show (#32217)
* fix: route registry messages to stderr in template and show When pulling an OCI chart, the registry client prints "Pulled: ..." and "Digest: ..." status lines (and deprecation/underscore warnings) to its configured output writer. Since v4.2.1 (introduced by #32056), these messages leaked into the stdout output of helm template and helm show, breaking downstream consumers such as cdk8s and other YAML parsers. Fix by passing the command's stderr to the registry client in the template and show commands instead of stdout. This keeps stdout clean for machine-readable YAML while still surfacing registry warnings and status messages on stderr for troubleshooting, rather than discarding them. The pull/push commands continue to print these messages on their normal output writer. The show command's addRegistryClient writer parameter is renamed to registryOut and wired through to the registry client, so it is no longer a no-op. Fixes #32215 Signed-off-by: amarkdotdev <amarkdotdev@users.noreply.github.com> * test(cmd): cover registry client stderr routing via OCI pull Exercise helm template and helm show against an in-process OCI registry (repotest.NewOCIServer) and assert Pulled:/Digest: status lines appear on stderr only, keeping stdout free of registry noise. Signed-off-by: amarkdotdev <amarkdotdev@users.noreply.github.com> * test(cmd): drop weak addRegistryClient unit test OCI pull coverage already asserts Pulled/Digest land on stderr, not stdout. Signed-off-by: amarkdotdev <amarkdotdev@users.noreply.github.com> --------- Signed-off-by: amarkdotdev <amarkdotdev@users.noreply.github.com> Co-authored-by: amarkdotdev <amarkdotdev@users.noreply.github.com>pull/32596/head
parent
11e2010aeb
commit
9a3c040369
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
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 cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"helm.sh/helm/v4/pkg/action"
|
||||||
|
"helm.sh/helm/v4/pkg/chart/common"
|
||||||
|
kubefake "helm.sh/helm/v4/pkg/kube/fake"
|
||||||
|
"helm.sh/helm/v4/pkg/repo/v1/repotest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTemplateOCIRegistryMessagesNotOnStdout(t *testing.T) {
|
||||||
|
defer resetEnv()()
|
||||||
|
|
||||||
|
stdout, stderr := runOCIChartCommand(t, func(ref, registryConfig, contentCache string) []string {
|
||||||
|
return []string{
|
||||||
|
"template", "release-name", ref,
|
||||||
|
"--version", "0.1.0",
|
||||||
|
"--plain-http",
|
||||||
|
"--registry-config", registryConfig,
|
||||||
|
"--content-cache", contentCache,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
require.NotEmpty(t, stdout)
|
||||||
|
require.NotContains(t, stdout, "Pulled:")
|
||||||
|
require.NotContains(t, stdout, "Digest:")
|
||||||
|
require.Contains(t, stderr, "Pulled:")
|
||||||
|
require.Contains(t, stderr, "Digest:")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShowOCIRegistryMessagesNotOnStdout(t *testing.T) {
|
||||||
|
defer resetEnv()()
|
||||||
|
|
||||||
|
stdout, stderr := runOCIChartCommand(t, func(ref, registryConfig, contentCache string) []string {
|
||||||
|
return []string{
|
||||||
|
"show", "chart", ref,
|
||||||
|
"--version", "0.1.0",
|
||||||
|
"--plain-http",
|
||||||
|
"--registry-config", registryConfig,
|
||||||
|
"--content-cache", contentCache,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
require.NotEmpty(t, stdout)
|
||||||
|
require.Contains(t, stdout, "name: oci-dependent-chart")
|
||||||
|
require.NotContains(t, stdout, "Pulled:")
|
||||||
|
require.NotContains(t, stdout, "Digest:")
|
||||||
|
require.Contains(t, stderr, "Pulled:")
|
||||||
|
require.Contains(t, stderr, "Digest:")
|
||||||
|
}
|
||||||
|
|
||||||
|
func runOCIChartCommand(t *testing.T, argsFn func(ref, registryConfig, contentCache string) []string) (string, string) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
srv := repotest.NewTempServer(
|
||||||
|
t,
|
||||||
|
repotest.WithChartSourceGlob("testdata/testcharts/*.tgz*"),
|
||||||
|
)
|
||||||
|
t.Cleanup(func() { srv.Stop() })
|
||||||
|
|
||||||
|
ociSrv, err := repotest.NewOCIServer(t, srv.Root())
|
||||||
|
require.NoError(t, err)
|
||||||
|
ociSrv.Run(t)
|
||||||
|
|
||||||
|
ref := fmt.Sprintf("oci://%s/u/ocitestuser/oci-dependent-chart", ociSrv.RegistryURL)
|
||||||
|
registryConfig := filepath.Join(srv.Root(), "config.json")
|
||||||
|
contentCache := t.TempDir()
|
||||||
|
args := argsFn(ref, registryConfig, contentCache)
|
||||||
|
|
||||||
|
stdout := &bytes.Buffer{}
|
||||||
|
stderr := &bytes.Buffer{}
|
||||||
|
actionConfig := &action.Configuration{
|
||||||
|
Releases: storageFixture(),
|
||||||
|
KubeClient: &kubefake.PrintingKubeClient{Out: io.Discard},
|
||||||
|
Capabilities: common.DefaultCapabilities,
|
||||||
|
}
|
||||||
|
|
||||||
|
root, err := newRootCmdWithConfig(actionConfig, stdout, args, SetupLogging)
|
||||||
|
require.NoError(t, err)
|
||||||
|
root.SetOut(stdout)
|
||||||
|
root.SetErr(stderr)
|
||||||
|
root.SetArgs(args)
|
||||||
|
|
||||||
|
require.NoError(t, root.Execute(), "stdout:\n%s\nstderr:\n%s", stdout.String(), stderr.String())
|
||||||
|
return stdout.String(), stderr.String()
|
||||||
|
}
|
||||||
Loading…
Reference in new issue