fix(helm): correct installGatewayAPI helm value defaulting (#13759)

The Helm function `default` will treat a boolean false value as unset and use the default instead of the false, even when it is set.  When rendering CRDs during install or upgrade, this can cause Linkerd to fall back to using the `installGatewayAPI` value even when `enableHttpRoutes` is explicitly set to false.

We replace the `default` function with a ternary which checks if the key is present.  We also add tests for both CLI and Helm.

Signed-off-by: Alex Leong <alex@buoyant.io>
This commit is contained in:
Alex Leong 2025-03-10 16:03:21 -07:00 committed by GitHub
parent e055c32f31
commit f09def4895
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 54217 additions and 21 deletions

View File

@ -1,4 +1,4 @@
{{- if .Values.enableHttpRoutes | default .Values.installGatewayAPI }}
{{- if (ternary .Values.enableHttpRoutes .Values.installGatewayAPI (hasKey .Values "enableHttpRoutes")) }}
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition

View File

@ -1,4 +1,4 @@
{{- if .Values.enableHttpRoutes | default .Values.installGatewayAPI }}
{{- if (ternary .Values.enableHttpRoutes .Values.installGatewayAPI (hasKey .Values "enableHttpRoutes")) }}
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition

View File

@ -1,4 +1,4 @@
{{- if .Values.enableTcpRoutes | default .Values.installGatewayAPI }}
{{- if (ternary .Values.enableTcpRoutes .Values.installGatewayAPI (hasKey .Values "enableTcpRoutes")) }}
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition

View File

@ -1,4 +1,4 @@
{{- if .Values.enableTlsRoutes | default .Values.installGatewayAPI }}
{{- if (ternary .Values.enableTlsRoutes .Values.installGatewayAPI (hasKey .Values "enableTlsRoutes")) }}
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition

View File

@ -4,10 +4,10 @@
installGatewayAPI: true
# -- Controls if Linkerd should install the Gateway API HTTPRoutes and
# GRPCRoutes CRDs. If unspecified, the value of `installGatewayAPI` is used.
enableHttpRoutes: false
# enableHttpRoutes: false
# -- Controls if Linkerd should install the Gateway API TLSRoutes CRD. If
# unspecified, the value of `installGatewayAPI` is used.
enableTlsRoutes: false
# enableTlsRoutes: false
# -- Controls if Linkerd should install the Gateway API TCPRoutes CRD. If
# unspecified, the value of `installGatewayAPI` is used.
enableTcpRoutes: false
# enableTcpRoutes: false

View File

@ -24,15 +24,31 @@ func TestRenderHelm(t *testing.T) {
t.Run("Non-HA mode", func(t *testing.T) {
chartCrds := chartCrds(t)
chartControlPlane := chartControlPlane(t, false, "", "111", "222")
testRenderHelm(t, chartCrds, "install_helm_crds_output.golden")
testRenderHelm(t, chartControlPlane, "install_helm_control_plane_output.golden")
testRenderHelm(t, chartCrds, nil, "install_helm_crds_output.golden")
testRenderHelm(t, chartControlPlane, nil, "install_helm_control_plane_output.golden")
})
t.Run("CRDs without Gateway API", func(t *testing.T) {
chartCrds := chartCrds(t)
testRenderHelm(t, chartCrds, map[string]interface{}{
"installGatewayAPI": false,
}, "install_helm_crds_without_gateway_output.golden")
})
t.Run("CRDs without Gateway API routes", func(t *testing.T) {
chartCrds := chartCrds(t)
testRenderHelm(t, chartCrds, map[string]interface{}{
"enableHttpRoutes": false,
"enableTcpRoutes": false,
"enableTlsRoutes": false,
}, "install_helm_crds_without_gateway_output.golden")
})
t.Run("HA mode", func(t *testing.T) {
chartCrds := chartCrds(t)
chartControlPlane := chartControlPlane(t, true, "", "111", "222")
testRenderHelm(t, chartCrds, "install_helm_crds_output_ha.golden")
testRenderHelm(t, chartControlPlane, "install_helm_control_plane_output_ha.golden")
testRenderHelm(t, chartCrds, nil, "install_helm_crds_output_ha.golden")
testRenderHelm(t, chartControlPlane, nil, "install_helm_control_plane_output_ha.golden")
})
t.Run("HA mode with GID", func(t *testing.T) {
@ -42,7 +58,7 @@ proxy:
gid: 4231
`
chartControlPlane := chartControlPlane(t, true, additionalConfig, "111", "222")
testRenderHelm(t, chartControlPlane, "install_helm_control_plane_output_ha_with_gid.golden")
testRenderHelm(t, chartControlPlane, nil, "install_helm_control_plane_output_ha_with_gid.golden")
})
t.Run("HA mode with podLabels and podAnnotations", func(t *testing.T) {
@ -55,7 +71,7 @@ podAnnotations:
asda: fasda
`
chartControlPlane := chartControlPlane(t, true, additionalConfig, "333", "444")
testRenderHelm(t, chartControlPlane, "install_helm_output_ha_labels.golden")
testRenderHelm(t, chartControlPlane, nil, "install_helm_output_ha_labels.golden")
})
t.Run("HA mode with custom namespaceSelector", func(t *testing.T) {
@ -76,11 +92,11 @@ profileValidator:
- enabled
`
chartControlPlane := chartControlPlane(t, true, additionalConfig, "111", "222")
testRenderHelm(t, chartControlPlane, "install_helm_output_ha_namespace_selector.golden")
testRenderHelm(t, chartControlPlane, nil, "install_helm_output_ha_namespace_selector.golden")
})
}
func testRenderHelm(t *testing.T, linkerd2Chart *chart.Chart, goldenFileName string) {
func testRenderHelm(t *testing.T, linkerd2Chart *chart.Chart, additionalValues map[string]interface{}, goldenFileName string) {
var (
chartName = "linkerd2"
namespace = "linkerd-dev"
@ -139,6 +155,9 @@ func testRenderHelm(t *testing.T, linkerd2Chart *chart.Chart, goldenFileName str
if err != nil {
t.Fatal("Unexpected error", err)
}
for k, v := range additionalValues {
overrideConfig[k] = v
}
releaseOptions := chartutil.ReleaseOptions{
Name: chartName,

View File

@ -289,12 +289,6 @@ func TestIgnoreCluster(t *testing.T) {
}
func TestRenderCRDs(t *testing.T) {
defaultValues, err := testInstallOptions()
if err != nil {
t.Fatal(err)
}
addFakeTLSSecrets(defaultValues)
var buf bytes.Buffer
if err := renderCRDs(context.Background(), nil, &buf, values.Options{}, "yaml"); err != nil {
t.Fatalf("Failed to render templates: %v", err)
@ -304,6 +298,19 @@ func TestRenderCRDs(t *testing.T) {
}
}
func TestRenderCRDsWithGatewayAPI(t *testing.T) {
options := values.Options{
Values: []string{"installGatewayAPI=true"},
}
var buf bytes.Buffer
if err := renderCRDs(context.Background(), nil, &buf, options, "yaml"); err != nil {
t.Fatalf("Failed to render templates: %v", err)
}
if err := testDataDiffer.DiffTestYAML("install_crds_with_gateway_api.golden", buf.String()); err != nil {
t.Error(err)
}
}
func TestValidateAndBuild_Errors(t *testing.T) {
t.Run("Fails validation for invalid ignoreInboundPorts", func(t *testing.T) {
values, err := testInstallOptions()

19978
cli/cmd/testdata/install_crds_with_gateway_api.golden generated vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

19978
cli/cmd/testdata/upgrade_crds_with_gateway_api.golden generated vendored Normal file

File diff suppressed because it is too large Load Diff

7072
cli/cmd/testdata/upgrade_crds_without_gateway_api.golden generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -345,6 +345,48 @@ func TestUpgradeWebhookCrtsNameChange(t *testing.T) {
}
}
func TestUpgradeCRDsWithGatewayAPI(t *testing.T) {
installOpts := valuespkg.Options{
Values: []string{"installGatewayAPI=true"},
}
var installBuf bytes.Buffer
if err := renderCRDs(context.Background(), nil, &installBuf, installOpts, "yaml"); err != nil {
t.Fatalf("could not render install manifests: %s", err)
}
installManifest := installBuf.String()
k, err := k8s.NewFakeAPIFromManifests([]io.Reader{strings.NewReader(installManifest)})
if err != nil {
t.Fatalf("failed to initialize fake API: %s\n\n%s", err, installManifest)
}
upgradeBuf := upgradeCRDs(context.Background(), k, valuespkg.Options{}, "yaml")
upgradeManifest := upgradeBuf.String()
if err := testDataDiffer.DiffTestYAML("upgrade_crds_with_gateway_api.golden", upgradeManifest); err != nil {
t.Error(err)
}
}
func TestUpgradeCRDsWithoutGatewayAPI(t *testing.T) {
installOpts := valuespkg.Options{
Values: []string{"installGatewayAPI=false"},
}
var installBuf bytes.Buffer
if err := renderCRDs(context.Background(), nil, &installBuf, installOpts, "yaml"); err != nil {
t.Fatalf("could not render install manifests: %s", err)
}
installManifest := installBuf.String()
k, err := k8s.NewFakeAPIFromManifests([]io.Reader{strings.NewReader(installManifest)})
if err != nil {
t.Fatalf("failed to initialize fake API: %s\n\n%s", err, installManifest)
}
upgradeBuf := upgradeCRDs(context.Background(), k, valuespkg.Options{}, "yaml")
upgradeManifest := upgradeBuf.String()
if err := testDataDiffer.DiffTestYAML("upgrade_crds_without_gateway_api.golden", upgradeManifest); err != nil {
t.Error(err)
}
}
func replaceK8sSecrets(input string) string {
manifest := strings.ReplaceAll(input, "kubernetes.io/tls", "Opaque")
manifest = strings.ReplaceAll(manifest, "tls.key", "key.pem")