...

Source file src/github.com/linkerd/linkerd2/cli/cmd/upgrade_test.go

Documentation: github.com/linkerd/linkerd2/cli/cmd

     1  package cmd
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/base64"
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/linkerd/linkerd2/cli/flag"
    14  	charts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
    15  	flagspkg "github.com/linkerd/linkerd2/pkg/flags"
    16  	"github.com/linkerd/linkerd2/pkg/k8s"
    17  	"github.com/linkerd/linkerd2/pkg/tls"
    18  	"github.com/spf13/pflag"
    19  	valuespkg "helm.sh/helm/v3/pkg/cli/values"
    20  	corev1 "k8s.io/api/core/v1"
    21  )
    22  
    23  const (
    24  	upgradeProxyVersion        = "UPGRADE-PROXY-VERSION"
    25  	upgradeControlPlaneVersion = "UPGRADE-CONTROL-PLANE-VERSION"
    26  	upgradeDebugVersion        = "UPGRADE-DEBUG-VERSION"
    27  	overridesSecret            = "Secret/linkerd-config-overrides"
    28  	linkerdConfigMap           = "ConfigMap/linkerd-config"
    29  )
    30  
    31  type (
    32  	issuerCerts struct {
    33  		caFile  string
    34  		ca      string
    35  		crtFile string
    36  		crt     string
    37  		keyFile string
    38  		key     string
    39  	}
    40  
    41  	flagOptions struct {
    42  		flags           []flag.Flag
    43  		flagSet         *pflag.FlagSet
    44  		templateOptions *valuespkg.Options
    45  	}
    46  )
    47  
    48  /* Test cases */
    49  
    50  /* Most test cases in this file work by first rendering an install manifest
    51     list, creating a fake k8s client initialized with those manifests, rendering
    52     an upgrade manifest list, and comparing the install manifests to the upgrade
    53     manifests. In some cases we expect these manifests to be identical and in
    54     others there are certain expected differences */
    55  
    56  func TestUpgradeDefault(t *testing.T) {
    57  	installOpts, upgradeOpts := testOptions(t)
    58  	install, upgrade, err := renderInstallAndUpgrade(t, installOpts, upgradeOpts.flags, *upgradeOpts.templateOptions)
    59  	if err != nil {
    60  		t.Fatal(err)
    61  	}
    62  	// Install and upgrade manifests should be identical except for the version.
    63  	expected := replaceVersions(install.String())
    64  	expectedManifests := parseManifestList(expected)
    65  	upgradeManifests := parseManifestList(upgrade.String())
    66  	for id, diffs := range diffManifestLists(expectedManifests, upgradeManifests) {
    67  		for _, diff := range diffs {
    68  			if ignorableDiff(id, diff) {
    69  				continue
    70  			}
    71  			t.Errorf("Unexpected diff in %s:\n%s", id, diff.String())
    72  		}
    73  	}
    74  }
    75  
    76  func TestUpgradeHA(t *testing.T) {
    77  	installOpts, upgradeOpts := testOptions(t)
    78  	installOpts.HighAvailability = true
    79  	install, upgrade, err := renderInstallAndUpgrade(t, installOpts, upgradeOpts.flags, *upgradeOpts.templateOptions)
    80  	if err != nil {
    81  		t.Fatal(err)
    82  	}
    83  	// Install and upgrade manifests should be identical except for the version.
    84  	expected := replaceVersions(install.String())
    85  	expectedManifests := parseManifestList(expected)
    86  	upgradeManifests := parseManifestList(upgrade.String())
    87  	for id, diffs := range diffManifestLists(expectedManifests, upgradeManifests) {
    88  		for _, diff := range diffs {
    89  			if ignorableDiff(id, diff) {
    90  				continue
    91  			}
    92  			t.Errorf("Unexpected diff in %s:\n%s", id, diff.String())
    93  		}
    94  	}
    95  }
    96  
    97  func TestUpgradeExternalIssuer(t *testing.T) {
    98  	installOpts, err := testInstallOptionsNoCerts(false)
    99  	if err != nil {
   100  		t.Fatalf("failed to create install options: %s", err)
   101  	}
   102  
   103  	upgradeOpts, err := testUpgradeOptions()
   104  	if err != nil {
   105  		t.Fatalf("failed to create upgrade options: %s", err)
   106  	}
   107  
   108  	issuer := generateIssuerCerts(t, true)
   109  	defer issuer.cleanup()
   110  
   111  	installOpts.Identity.Issuer.Scheme = string(corev1.SecretTypeTLS)
   112  	ca, err := base64.StdEncoding.DecodeString(issuer.ca)
   113  	if err != nil {
   114  		t.Fatal(err)
   115  	}
   116  	installOpts.IdentityTrustAnchorsPEM = string(ca)
   117  	install := renderInstall(t, installOpts)
   118  	upgrade, err := renderUpgrade(install.String()+externalIssuerSecret(issuer), upgradeOpts.flags, *upgradeOpts.templateOptions)
   119  
   120  	if err != nil {
   121  		t.Fatal(err)
   122  	}
   123  	// Install and upgrade manifests should be identical except for the version.
   124  	expected := replaceVersions(install.String())
   125  	expectedManifests := parseManifestList(expected)
   126  	upgradeManifests := parseManifestList(upgrade.String())
   127  	for id, diffs := range diffManifestLists(expectedManifests, upgradeManifests) {
   128  		for _, diff := range diffs {
   129  			if ignorableDiff(id, diff) {
   130  				continue
   131  			}
   132  			t.Errorf("Unexpected diff in %s:\n%s", id, diff.String())
   133  		}
   134  	}
   135  }
   136  
   137  func TestUpgradeIssuerWithExternalIssuerFails(t *testing.T) {
   138  	installOpts, upgradeOpts := testOptions(t)
   139  
   140  	issuer := generateIssuerCerts(t, true)
   141  	defer issuer.cleanup()
   142  
   143  	installOpts.IdentityTrustDomain = "cluster.local"
   144  	installOpts.IdentityTrustDomain = issuer.ca
   145  	installOpts.Identity.Issuer.Scheme = string(corev1.SecretTypeTLS)
   146  	installOpts.Identity.Issuer.TLS.CrtPEM = issuer.crt
   147  	installOpts.Identity.Issuer.TLS.KeyPEM = issuer.key
   148  	install := renderInstall(t, installOpts)
   149  
   150  	upgradedIssuer := generateIssuerCerts(t, true)
   151  	defer upgradedIssuer.cleanup()
   152  
   153  	upgradeOpts.flagSet.Set("identity-trust-anchors-file", upgradedIssuer.caFile)
   154  	upgradeOpts.flagSet.Set("identity-issuer-certificate-file", upgradedIssuer.crtFile)
   155  	upgradeOpts.flagSet.Set("identity-issuer-key-file", upgradedIssuer.keyFile)
   156  
   157  	_, err := renderUpgrade(install.String()+externalIssuerSecret(issuer), upgradeOpts.flags, *upgradeOpts.templateOptions)
   158  
   159  	expectedErr := "cannot update issuer certificates if you are using external cert management solution"
   160  
   161  	if err == nil || err.Error() != expectedErr {
   162  		t.Errorf("Expected error: %s but got %s", expectedErr, err)
   163  	}
   164  }
   165  
   166  func TestUpgradeOverwriteIssuer(t *testing.T) {
   167  	installOpts, upgradeOpts := testOptions(t)
   168  
   169  	issuerCerts := generateIssuerCerts(t, true)
   170  	defer issuerCerts.cleanup()
   171  
   172  	upgradeOpts.flagSet.Set("identity-trust-anchors-file", issuerCerts.caFile)
   173  	upgradeOpts.flagSet.Set("identity-issuer-certificate-file", issuerCerts.crtFile)
   174  	upgradeOpts.flagSet.Set("identity-issuer-key-file", issuerCerts.keyFile)
   175  
   176  	install, upgrade, err := renderInstallAndUpgrade(t, installOpts, upgradeOpts.flags, *upgradeOpts.templateOptions)
   177  	if err != nil {
   178  		t.Fatal(err)
   179  	}
   180  	// When upgrading the trust root, we expect to see the new trust root passed
   181  	// to each proxy, the trust root updated in the linkerd-config, and the
   182  	// updated credentials in the linkerd-identity-issuer secret.
   183  	expected := replaceVersions(install.String())
   184  	expectedManifests := parseManifestList(expected)
   185  	upgradeManifests := parseManifestList(upgrade.String())
   186  	for id, diffs := range diffManifestLists(expectedManifests, upgradeManifests) {
   187  		for _, diff := range diffs {
   188  			if ignorableDiff(id, diff) {
   189  				continue
   190  			}
   191  			if isProxyEnvDiff(diff.path) {
   192  				// Trust root has changed.
   193  				continue
   194  			}
   195  
   196  			if id == "Deployment/linkerd-identity" || id == "Deployment/linkerd-proxy-injector" {
   197  				if pathMatch(diff.path, []string{"spec", "template", "spec", "containers", "*", "env", "*", "value"}) && diff.b.(string) == issuerCerts.ca {
   198  					continue
   199  				}
   200  				if pathMatch(diff.path, []string{"spec", "template", "metadata", "annotations", "linkerd.io/trust-root-sha256"}) {
   201  					continue
   202  				}
   203  				t.Errorf("Unexpected diff in %s:\n%s", id, diff.String())
   204  			}
   205  			if id == "Deployment/linkerd-destination" {
   206  				if pathMatch(diff.path, []string{"spec", "template", "metadata", "annotations", "linkerd.io/trust-root-sha256"}) {
   207  					continue
   208  				}
   209  			}
   210  
   211  			if id == "Secret/linkerd-identity-issuer" {
   212  				if pathMatch(diff.path, []string{"data", "crt.pem"}) {
   213  					if diff.b.(string) != issuerCerts.crt {
   214  						diff.a = issuerCerts.crt
   215  						t.Errorf("Unexpected diff in %s:\n%s", id, diff.String())
   216  					}
   217  				} else if pathMatch(diff.path, []string{"data", "key.pem"}) {
   218  					if diff.b.(string) != issuerCerts.key {
   219  						diff.a = issuerCerts.key
   220  						t.Errorf("Unexpected diff in %s:\n%s", id, diff.String())
   221  					}
   222  				} else {
   223  					t.Errorf("Unexpected diff in %s:\n%s", id, diff.String())
   224  				}
   225  				continue
   226  			}
   227  
   228  			if id == "ConfigMap/linkerd-identity-trust-roots" {
   229  				if pathMatch(diff.path, []string{"data", "ca-bundle.crt"}) {
   230  					ca, err := base64.StdEncoding.DecodeString(issuerCerts.ca)
   231  					if err != nil {
   232  						t.Fatal(err)
   233  					}
   234  					if diff.b.(string) != string(ca) {
   235  						diff.a = string(ca)
   236  						t.Errorf("Unexpected diff in %s:\n%s", id, diff.String())
   237  					}
   238  				} else {
   239  					t.Errorf("Unexpected diff in %s:\n%s", id, diff.String())
   240  				}
   241  				continue
   242  			}
   243  
   244  			t.Errorf("Unexpected diff in %s:\n%s", id, diff.String())
   245  		}
   246  	}
   247  }
   248  
   249  func TestUpgradeFailsWithOnlyIssuerCert(t *testing.T) {
   250  	installOpts, upgradeOpts := testOptions(t)
   251  
   252  	issuerCerts := generateIssuerCerts(t, true)
   253  	defer issuerCerts.cleanup()
   254  
   255  	upgradeOpts.flagSet.Set("identity-trust-anchors-file", issuerCerts.caFile)
   256  	upgradeOpts.flagSet.Set("identity-issuer-certificate-file", issuerCerts.crtFile)
   257  
   258  	_, _, err := renderInstallAndUpgrade(t, installOpts, upgradeOpts.flags, *upgradeOpts.templateOptions)
   259  
   260  	expectedErr := "failed to validate issuer credentials: failed to read CA: tls: Public and private key do not match"
   261  
   262  	if err == nil || err.Error() != expectedErr {
   263  		t.Errorf("Expected error: %s but got %s", expectedErr, err)
   264  	}
   265  }
   266  
   267  func TestUpgradeFailsWithOnlyIssuerKey(t *testing.T) {
   268  	installOpts, upgradeOpts := testOptions(t)
   269  
   270  	issuerCerts := generateIssuerCerts(t, false)
   271  	defer issuerCerts.cleanup()
   272  
   273  	upgradeOpts.flagSet.Set("identity-trust-anchors-file", issuerCerts.caFile)
   274  	upgradeOpts.flagSet.Set("identity-issuer-certificate-file", issuerCerts.crtFile)
   275  
   276  	_, _, err := renderInstallAndUpgrade(t, installOpts, upgradeOpts.flags, *upgradeOpts.templateOptions)
   277  
   278  	expectedErr := "failed to validate issuer credentials: failed to read CA: tls: Public and private key do not match"
   279  
   280  	if err == nil || err.Error() != expectedErr {
   281  		t.Errorf("Expected error: %s but got %s", expectedErr, err)
   282  	}
   283  }
   284  
   285  func TestUpgradeRootFailsWithOldPods(t *testing.T) {
   286  	installOpts, upgradeOpts := testOptions(t)
   287  
   288  	oldIssuer := generateIssuerCerts(t, false)
   289  	defer oldIssuer.cleanup()
   290  
   291  	install := renderInstall(t, installOpts)
   292  
   293  	issuerCerts := generateIssuerCerts(t, true)
   294  	defer issuerCerts.cleanup()
   295  
   296  	upgradeOpts.flagSet.Set("identity-trust-anchors-file", issuerCerts.caFile)
   297  	upgradeOpts.flagSet.Set("identity-issuer-certificate-file", issuerCerts.crtFile)
   298  	upgradeOpts.flagSet.Set("identity-issuer-key-file", issuerCerts.keyFile)
   299  
   300  	_, err := renderUpgrade(install.String()+podWithSidecar(oldIssuer), upgradeOpts.flags, *upgradeOpts.templateOptions)
   301  
   302  	expectedErr := "You are attempting to use an issuer certificate which does not validate against the trust anchors of the following pods"
   303  	if err == nil || !strings.HasPrefix(err.Error(), expectedErr) {
   304  		t.Errorf("Expected error: %s but got %s", expectedErr, err)
   305  	}
   306  }
   307  
   308  // this test constructs a set of secrets resources
   309  func TestUpgradeWebhookCrtsNameChange(t *testing.T) {
   310  	installOpts, upgradeOpts := testOptions(t)
   311  
   312  	injectorCerts := generateCerts(t, "linkerd-proxy-injector.linkerd.svc", false)
   313  	defer injectorCerts.cleanup()
   314  	installOpts.ProxyInjector.TLS = &charts.TLS{
   315  		ExternalSecret: true,
   316  		CaBundle:       injectorCerts.ca,
   317  	}
   318  
   319  	validatorCerts := generateCerts(t, "linkerd-sp-validator.linkerd.svc", false)
   320  	defer validatorCerts.cleanup()
   321  	installOpts.ProfileValidator.TLS = &charts.TLS{
   322  		ExternalSecret: true,
   323  		CaBundle:       validatorCerts.ca,
   324  	}
   325  
   326  	rendered := renderInstall(t, installOpts)
   327  	expected := replaceVersions(rendered.String())
   328  
   329  	// switch back to old tls secret names.
   330  	install := replaceK8sSecrets(expected)
   331  
   332  	upgrade, err := renderUpgrade(install, upgradeOpts.flags, *upgradeOpts.templateOptions)
   333  	if err != nil {
   334  		t.Fatal(err)
   335  	}
   336  	expectedManifests := parseManifestList(expected)
   337  	upgradeManifests := parseManifestList(upgrade.String())
   338  	for id, diffs := range diffManifestLists(expectedManifests, upgradeManifests) {
   339  		for _, diff := range diffs {
   340  			if ignorableDiff(id, diff) {
   341  				continue
   342  			}
   343  			t.Errorf("Unexpected diff in %s:\n%s", id, diff.String())
   344  		}
   345  	}
   346  }
   347  
   348  func replaceK8sSecrets(input string) string {
   349  	manifest := strings.ReplaceAll(input, "kubernetes.io/tls", "Opaque")
   350  	manifest = strings.ReplaceAll(manifest, "tls.key", "key.pem")
   351  	manifest = strings.ReplaceAll(manifest, "tls.crt", "crt.pem")
   352  	manifest = strings.ReplaceAll(manifest, "linkerd-proxy-injector-k8s-tls", "linkerd-proxy-injector-tls")
   353  	manifest = strings.ReplaceAll(manifest, "linkerd-tap-k8s-tls", "linkerd-tap-tls")
   354  	manifest = strings.ReplaceAll(manifest, "linkerd-sp-validator-k8s-tls", "linkerd-sp-validator-tls")
   355  	manifest = strings.ReplaceAll(manifest, "linkerd-policy-validator-k8s-tls", "linkerd-policy-validator-tls")
   356  	return manifest
   357  }
   358  
   359  func TestUpgradeTwoLevelWebhookCrts(t *testing.T) {
   360  	installOpts, upgradeOpts := testOptions(t)
   361  
   362  	// This tests the case where the webhook certs are not self-signed.
   363  	injectorCerts := generateCerts(t, "linkerd-proxy-injector.linkerd.svc", false)
   364  	defer injectorCerts.cleanup()
   365  	installOpts.ProxyInjector.TLS = &charts.TLS{
   366  		ExternalSecret: true,
   367  		CaBundle:       injectorCerts.ca,
   368  	}
   369  
   370  	validatorCerts := generateCerts(t, "linkerd-sp-validator.linkerd.svc", false)
   371  	defer validatorCerts.cleanup()
   372  	installOpts.ProfileValidator.TLS = &charts.TLS{
   373  		ExternalSecret: true,
   374  		CaBundle:       validatorCerts.ca,
   375  	}
   376  
   377  	install := renderInstall(t, installOpts)
   378  	upgrade, err := renderUpgrade(install.String(), upgradeOpts.flags, *upgradeOpts.templateOptions)
   379  	if err != nil {
   380  		t.Fatal(err)
   381  	}
   382  	expected := replaceVersions(install.String())
   383  	expectedManifests := parseManifestList(expected)
   384  	upgradeManifests := parseManifestList(upgrade.String())
   385  	for id, diffs := range diffManifestLists(expectedManifests, upgradeManifests) {
   386  		for _, diff := range diffs {
   387  			if ignorableDiff(id, diff) {
   388  				continue
   389  			}
   390  			t.Errorf("Unexpected diff in %s:\n%s", id, diff.String())
   391  		}
   392  	}
   393  }
   394  
   395  /* Helpers */
   396  
   397  func testUpgradeOptions() (flagOptions, error) {
   398  	defaults, err := charts.NewValues()
   399  	if err != nil {
   400  		return flagOptions{}, err
   401  	}
   402  
   403  	installUpgradeFlags, installUpgradeFlagSet, err := makeInstallUpgradeFlags(defaults)
   404  	if err != nil {
   405  		return flagOptions{}, err
   406  	}
   407  	proxyFlags, proxyFlagSet := makeProxyFlags(defaults)
   408  	upgradeFlagSet := makeUpgradeFlags()
   409  
   410  	flags := flattenFlags(installUpgradeFlags, proxyFlags)
   411  	flagSet := pflag.NewFlagSet("upgrade", pflag.ExitOnError)
   412  	flagSet.AddFlagSet(installUpgradeFlagSet)
   413  	flagSet.AddFlagSet(proxyFlagSet)
   414  	flagSet.AddFlagSet(upgradeFlagSet)
   415  
   416  	// Explicitly set policy controller override to upgrade control plane version
   417  	templateOpts := &valuespkg.Options{}
   418  	flagspkg.AddValueOptionsFlags(flagSet, templateOpts)
   419  	flagSet.Set("set", fmt.Sprintf("policyController.image.version=%[1]s,linkerdVersion=%[1]s", upgradeControlPlaneVersion))
   420  
   421  	flagSet.Set("set", fmt.Sprintf("proxy.image.version=%s", upgradeProxyVersion))
   422  
   423  	return flagOptions{flags, flagSet, templateOpts}, nil
   424  }
   425  
   426  func testOptions(t *testing.T) (*charts.Values, flagOptions) {
   427  	t.Helper()
   428  	installValues, err := testInstallOptions()
   429  	if err != nil {
   430  		t.Fatalf("failed to create install options: %s", err)
   431  	}
   432  	flagOpts, err := testUpgradeOptions()
   433  	if err != nil {
   434  		t.Fatalf("failed to create upgrade options: %s", err)
   435  	}
   436  
   437  	return installValues, flagOpts
   438  }
   439  
   440  func replaceVersions(manifest string) string {
   441  	manifest = strings.ReplaceAll(manifest, installProxyVersion, upgradeProxyVersion)
   442  	manifest = strings.ReplaceAll(manifest, installControlPlaneVersion, upgradeControlPlaneVersion)
   443  	manifest = strings.ReplaceAll(manifest, installDebugVersion, upgradeDebugVersion)
   444  	return manifest
   445  }
   446  
   447  func generateIssuerCerts(t *testing.T, b64encode bool) issuerCerts {
   448  	t.Helper()
   449  	return generateCerts(t, "identity.linkerd.cluster.local", b64encode)
   450  }
   451  
   452  func generateCerts(t *testing.T, name string, b64encode bool) issuerCerts {
   453  	t.Helper()
   454  	ca, err := tls.GenerateRootCAWithDefaults("test")
   455  	if err != nil {
   456  		t.Fatal(err)
   457  	}
   458  	issuer, err := ca.GenerateCA(name, -1)
   459  	if err != nil {
   460  		t.Fatal(err)
   461  	}
   462  	caPem := strings.TrimSpace(issuer.Cred.EncodePEM())
   463  	keyPem := strings.TrimSpace(issuer.Cred.EncodePrivateKeyPEM())
   464  	crtPem := strings.TrimSpace(issuer.Cred.EncodeCertificatePEM())
   465  
   466  	caFile, err := os.CreateTemp("", "ca.*.pem")
   467  	if err != nil {
   468  		t.Fatal(err)
   469  	}
   470  	crtFile, err := os.CreateTemp("", "crt.*.pem")
   471  	if err != nil {
   472  		t.Fatal(err)
   473  	}
   474  	keyFile, err := os.CreateTemp("", "key.*.pem")
   475  	if err != nil {
   476  		t.Fatal(err)
   477  	}
   478  
   479  	_, err = caFile.Write([]byte(caPem))
   480  	if err != nil {
   481  		t.Fatal(err)
   482  	}
   483  	_, err = crtFile.Write([]byte(crtPem))
   484  	if err != nil {
   485  		t.Fatal(err)
   486  	}
   487  	_, err = keyFile.Write([]byte(keyPem))
   488  	if err != nil {
   489  		t.Fatal(err)
   490  	}
   491  
   492  	if b64encode {
   493  		caPem = base64.StdEncoding.EncodeToString([]byte(caPem))
   494  		crtPem = base64.StdEncoding.EncodeToString([]byte(crtPem))
   495  		keyPem = base64.StdEncoding.EncodeToString([]byte(keyPem))
   496  	}
   497  
   498  	return issuerCerts{
   499  		caFile:  caFile.Name(),
   500  		ca:      caPem,
   501  		crtFile: crtFile.Name(),
   502  		crt:     crtPem,
   503  		keyFile: keyFile.Name(),
   504  		key:     keyPem,
   505  	}
   506  }
   507  
   508  func (ic issuerCerts) cleanup() {
   509  	os.Remove(ic.caFile)
   510  	os.Remove(ic.crtFile)
   511  	os.Remove(ic.keyFile)
   512  }
   513  
   514  func externalIssuerSecret(certs issuerCerts) string {
   515  	return fmt.Sprintf(`---
   516  apiVersion: v1
   517  kind: Secret
   518  metadata:
   519    name: linkerd-identity-issuer
   520    namespace: linkerd
   521  data:
   522    tls.crt: %s
   523    tls.key: %s
   524    ca.crt: %s
   525  type: kubernetes.io/tls
   526  `, certs.crt, certs.key, certs.ca)
   527  }
   528  
   529  func indentLines(s string, prefix string) string {
   530  	lines := strings.Split(s, "\n")
   531  	for i, line := range lines {
   532  		lines[i] = prefix + line
   533  	}
   534  	return strings.Join(lines, "\n")
   535  }
   536  
   537  func podWithSidecar(certs issuerCerts) string {
   538  	return fmt.Sprintf(`---
   539  apiVersion: v1
   540  kind: Pod
   541  metadata:
   542    annotations:
   543      linkerd.io/created-by: linkerd/cli some-version
   544      linkerd.io/proxy-version: some-version
   545    labels:
   546      linkerd.io/control-plane-ns: linkerd
   547    name: backend-wrong-anchors
   548    namespace: some-namespace
   549  spec:
   550    containers:
   551    - env:
   552      - name: LINKERD2_PROXY_IDENTITY_TRUST_ANCHORS
   553        value: |
   554  %s
   555      image: cr.l5d.io/linkerd/proxy:some-version
   556      name: linkerd-proxy
   557  `, indentLines(certs.ca, "        "))
   558  }
   559  
   560  func isProxyEnvDiff(path []string) bool {
   561  	template := []string{"spec", "template", "spec", "containers", "*", "env", "*", "value"}
   562  	return pathMatch(path, template)
   563  }
   564  
   565  func pathMatch(path []string, template []string) bool {
   566  	if len(path) != len(template) {
   567  		return false
   568  	}
   569  	for i, elem := range template {
   570  		if elem != "*" && elem != path[i] {
   571  			return false
   572  		}
   573  	}
   574  	return true
   575  }
   576  
   577  func renderInstall(t *testing.T, values *charts.Values) *bytes.Buffer {
   578  	t.Helper()
   579  	var buf bytes.Buffer
   580  	if err := renderCRDs(&buf, valuespkg.Options{}); err != nil {
   581  		t.Fatalf("could not render install manifests: %s", err)
   582  	}
   583  	buf.WriteString("---\n")
   584  	if err := renderControlPlane(&buf, values, nil); err != nil {
   585  		t.Fatalf("could not render install manifests: %s", err)
   586  	}
   587  	return &buf
   588  }
   589  
   590  func renderUpgrade(installManifest string, upgradeOpts []flag.Flag, templateOpts valuespkg.Options) (*bytes.Buffer, error) {
   591  	k, err := k8s.NewFakeAPIFromManifests([]io.Reader{strings.NewReader(installManifest)})
   592  	if err != nil {
   593  		return nil, fmt.Errorf("failed to initialize fake API: %w\n\n%s", err, installManifest)
   594  	}
   595  	buf := upgradeCRDs(valuespkg.Options{})
   596  	buf.WriteString("---\n")
   597  	cpbuf, err := upgradeControlPlane(context.Background(), k, upgradeOpts, templateOpts)
   598  	if err != nil {
   599  		return nil, err
   600  	}
   601  	buf.Write(cpbuf.Bytes())
   602  	return buf, nil
   603  }
   604  
   605  func renderInstallAndUpgrade(t *testing.T, installOpts *charts.Values, upgradeOpts []flag.Flag, templateOpts valuespkg.Options) (*bytes.Buffer, *bytes.Buffer, error) {
   606  	t.Helper()
   607  	err := validateValues(context.Background(), nil, installOpts)
   608  	if err != nil {
   609  		return nil, nil, fmt.Errorf("failed to validate values: %w", err)
   610  	}
   611  	installBuf := renderInstall(t, installOpts)
   612  	upgradeBuf, err := renderUpgrade(installBuf.String(), upgradeOpts, templateOpts)
   613  	return installBuf, upgradeBuf, err
   614  }
   615  
   616  // Certain resources are expected to change during an upgrade. We can safely
   617  // ignore these diffs in every test.
   618  func ignorableDiff(id string, diff diff) bool {
   619  	if id == overridesSecret {
   620  		// The config overrides will always change because at least the control
   621  		// plane and proxy versions will change.
   622  		return true
   623  	}
   624  	if id == linkerdConfigMap {
   625  		// The linkerd-config values will always change because at least the control
   626  		// plane and proxy versions will change.
   627  		return true
   628  	}
   629  	if (strings.HasPrefix(id, "MutatingWebhookConfiguration") || strings.HasPrefix(id, "ValidatingWebhookConfiguration")) &&
   630  		pathMatch(diff.path, []string{"webhooks", "*", "clientConfig", "caBundle"}) {
   631  		// Webhook TLS chains are regenerated upon upgrade so we expect the
   632  		// caBundle to change.
   633  		return true
   634  	}
   635  	if strings.HasPrefix(id, "APIService") &&
   636  		pathMatch(diff.path, []string{"spec", "caBundle"}) {
   637  		// APIService TLS chains are regenerated upon upgrade so we expect the
   638  		// caBundle to change.
   639  		return true
   640  	}
   641  
   642  	if (id == "Deployment/linkerd-sp-validator" || id == "Deployment/linkerd-proxy-injector" || id == "Deployment/linkerd-tap" || id == "Deployment/linkerd-destination") &&
   643  		pathMatch(diff.path, []string{"spec", "template", "metadata", "annotations", "checksum/config"}) {
   644  		// APIService TLS chains are regenerated upon upgrade so we expect the
   645  		// caBundle to change.
   646  		return true
   647  	}
   648  
   649  	if id == "Secret/linkerd-proxy-injector-tls" || id == "Secret/linkerd-sp-validator-tls" ||
   650  		id == "Secret/linkerd-tap-tls" || id == "Secret/linkerd-sp-validator-k8s-tls" ||
   651  		id == "Secret/linkerd-proxy-injector-k8s-tls" || id == "Secret/linkerd-tap-k8s-tls" ||
   652  		id == "Secret/linkerd-policy-validator-tls" || id == "Secret/linkerd-policy-validator-k8s-tls" {
   653  		// Webhook and APIService TLS chains are regenerated upon upgrade.
   654  		return true
   655  	}
   656  	return false
   657  }
   658  

View as plain text