linkerd2/cli/cmd/install_helm_test.go

345 lines
8.9 KiB
Go

package cmd
import (
"bytes"
"path/filepath"
"testing"
"github.com/linkerd/linkerd2/pkg/charts"
l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
"github.com/linkerd/linkerd2/pkg/charts/static"
"github.com/linkerd/linkerd2/pkg/k8s"
"github.com/linkerd/linkerd2/testutil"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chart/loader"
"helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/engine"
"sigs.k8s.io/yaml"
)
func TestRenderHelm(t *testing.T) {
// read the control plane chart and its defaults from the local folder.
// override certain defaults with pinned values.
// use the Helm lib to render the templates.
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")
})
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")
})
t.Run("HA mode with podLabels and podAnnotations", func(t *testing.T) {
additionalConfig := `
podLabels:
foo: bar
fiz: buz
podAnnotations:
bingo: bongo
asda: fasda
`
chartControlPlane := chartControlPlane(t, true, additionalConfig, "333", "444")
testRenderHelm(t, chartControlPlane, "install_helm_output_ha_labels.golden")
})
t.Run("HA mode with custom namespaceSelector", func(t *testing.T) {
additionalConfig := `
proxyInjector:
namespaceSelector:
matchExpressions:
- key: config.linkerd.io/admission-webhooks
operator: In
values:
- enabled
profileValidator:
namespaceSelector:
matchExpressions:
- key: config.linkerd.io/admission-webhooks
operator: In
values:
- enabled
`
chartControlPlane := chartControlPlane(t, true, additionalConfig, "111", "222")
testRenderHelm(t, chartControlPlane, "install_helm_output_ha_namespace_selector.golden")
})
}
func testRenderHelm(t *testing.T, linkerd2Chart *chart.Chart, goldenFileName string) {
var (
chartName = "linkerd2"
namespace = "linkerd-dev"
)
// pin values that are changed by Helm functions on each test run
overrideJSON := `{
"cliVersion":"",
"linkerdVersion":"linkerd-version",
"identityTrustAnchorsPEM":"test-trust-anchor",
"identityTrustDomain":"test.trust.domain",
"proxy":{
"image":{
"version":"test-proxy-version"
}
},
"proxyInit":{
"image":{
"version":"test-proxy-init-version"
}
},
"identity":{
"issuer":{
"tls":{
"keyPEM":"test-key-pem",
"crtPEM":"test-crt-pem"
}
}
},
"configs": null,
"debugContainer":{
"image":{
"version":"test-debug-version"
}
},
"proxyInjector":{
"externalSecret": true,
"caBundle":"test-proxy-injector-ca-bundle"
},
"profileValidator":{
"externalSecret": true,
"caBundle":"test-profile-validator-ca-bundle"
},
"policyValidator":{
"externalSecret": true,
"caBundle":"test-profile-validator-ca-bundle"
},
"tap":{
"externalSecret": true,
"caBundle":"test-tap-ca-bundle"
}
}`
var overrideConfig chartutil.Values
err := yaml.Unmarshal([]byte(overrideJSON), &overrideConfig)
if err != nil {
t.Fatal("Unexpected error", err)
}
releaseOptions := chartutil.ReleaseOptions{
Name: chartName,
Namespace: namespace,
IsUpgrade: false,
IsInstall: true,
}
valuesToRender, err := chartutil.ToRenderValues(linkerd2Chart, overrideConfig, releaseOptions, nil)
if err != nil {
t.Fatal("Unexpected error", err)
}
rendered, err := engine.Render(linkerd2Chart, valuesToRender)
if err != nil {
t.Fatal("Unexpected error", err)
}
var buf bytes.Buffer
for _, template := range linkerd2Chart.Templates {
source := linkerd2Chart.Metadata.Name + "/" + template.Name
v, exists := rendered[source]
if !exists {
// skip partial templates
continue
}
buf.WriteString("---\n# Source: " + source + "\n")
buf.WriteString(v)
}
for _, dep := range linkerd2Chart.Dependencies() {
for _, template := range dep.Templates {
source := linkerd2Chart.Metadata.Name + "/charts" + "/" + dep.Metadata.Name + "/" + template.Name
v, exists := rendered[source]
if !exists {
// skip partial templates
continue
}
buf.WriteString("---\n# Source: " + source + "\n")
buf.WriteString(v)
}
}
if err := testDataDiffer.DiffTestYAML(goldenFileName, buf.String()); err != nil {
t.Error(err)
}
}
func chartCrds(t *testing.T) *chart.Chart {
partialPaths := []string{
"templates/_proxy.tpl",
"templates/_proxy-init.tpl",
"templates/_volumes.tpl",
"templates/_resources.tpl",
"templates/_metadata.tpl",
"templates/_debug.tpl",
"templates/_trace.tpl",
"templates/_capabilities.tpl",
"templates/_affinity.tpl",
"templates/_nodeselector.tpl",
"templates/_tolerations.tpl",
"templates/_validate.tpl",
"templates/_pull-secrets.tpl",
}
chartPartials := chartPartials(partialPaths)
// Load defaults from values.yaml
valuesFile := &loader.BufferedFile{Name: l5dcharts.HelmChartDirCrds + "/values.yaml"}
if err := charts.ReadFile(static.Templates, "/", valuesFile); err != nil {
t.Fatal(err)
}
var defaultValues map[string]interface{}
err := yaml.Unmarshal(valuesFile.Data, &defaultValues)
if err != nil {
t.Fatal(err)
}
defaultValues["cliVersion"] = k8s.CreatedByAnnotationValue()
linkerd2Chart := &chart.Chart{
Metadata: &chart.Metadata{
Name: helmDefaultChartNameCrds,
Sources: []string{
filepath.Join("..", "..", "..", "charts", "linkerd-crds"),
},
},
Values: defaultValues,
}
linkerd2Chart.AddDependency(chartPartials)
for _, filepath := range templatesCrdFiles {
linkerd2Chart.Templates = append(linkerd2Chart.Templates, &chart.File{
Name: filepath,
})
}
for _, template := range linkerd2Chart.Templates {
filepath := filepath.Join(linkerd2Chart.Metadata.Sources[0], template.Name)
template.Data = []byte(testutil.ReadTestdata(filepath))
}
return linkerd2Chart
}
func chartControlPlane(t *testing.T, ha bool, additionalConfig string, ignoreOutboundPorts string, ignoreInboundPorts string) *chart.Chart {
values, err := readTestValues(ha, ignoreOutboundPorts, ignoreInboundPorts)
if err != nil {
t.Fatal("Unexpected error", err)
}
if additionalConfig != "" {
err := yaml.Unmarshal([]byte(additionalConfig), values)
if err != nil {
t.Fatal("Unexpected error", err)
}
}
partialPaths := []string{
"templates/_proxy.tpl",
"templates/_proxy-init.tpl",
"templates/_volumes.tpl",
"templates/_resources.tpl",
"templates/_metadata.tpl",
"templates/_debug.tpl",
"templates/_trace.tpl",
"templates/_capabilities.tpl",
"templates/_affinity.tpl",
"templates/_nodeselector.tpl",
"templates/_tolerations.tpl",
"templates/_validate.tpl",
"templates/_pull-secrets.tpl",
}
chartPartials := chartPartials(partialPaths)
rawValues, err := yaml.Marshal(values)
if err != nil {
t.Fatal("Unexpected error", err)
}
var mapValues chartutil.Values
err = yaml.Unmarshal(rawValues, &mapValues)
if err != nil {
t.Fatal("Unexpected error", err)
}
linkerd2Chart := &chart.Chart{
Metadata: &chart.Metadata{
Name: helmDefaultChartNameCP,
Sources: []string{
filepath.Join("..", "..", "..", "charts", "linkerd-control-plane"),
},
},
Values: mapValues,
}
linkerd2Chart.AddDependency(chartPartials)
for _, filepath := range templatesControlPlane {
linkerd2Chart.Templates = append(linkerd2Chart.Templates, &chart.File{
Name: filepath,
})
}
for _, template := range linkerd2Chart.Templates {
filepath := filepath.Join(linkerd2Chart.Metadata.Sources[0], template.Name)
template.Data = []byte(testutil.ReadTestdata(filepath))
}
return linkerd2Chart
}
func chartPartials(paths []string) *chart.Chart {
var partialTemplates []*chart.File
for _, path := range paths {
partialTemplates = append(partialTemplates, &chart.File{Name: path})
}
chart := &chart.Chart{
Metadata: &chart.Metadata{
Name: "partials",
Sources: []string{
filepath.Join("..", "..", "..", "charts", "partials"),
},
},
Templates: partialTemplates,
}
for _, template := range chart.Templates {
template := template
filepath := filepath.Join(chart.Metadata.Sources[0], template.Name)
template.Data = []byte(testutil.ReadTestdata(filepath))
}
return chart
}
func readTestValues(ha bool, ignoreOutboundPorts string, ignoreInboundPorts string) (*l5dcharts.Values, error) {
values, err := l5dcharts.NewValues()
if err != nil {
return nil, err
}
if ha {
if err = l5dcharts.MergeHAValues(values); err != nil {
return nil, err
}
}
values.ProxyInit.IgnoreOutboundPorts = ignoreOutboundPorts
values.ProxyInit.IgnoreInboundPorts = ignoreInboundPorts
values.HeartbeatSchedule = "1 2 3 4 5"
return values, nil
}