...

Source file src/edge-infra.dev/test/e2e/linkerd/trustanchor_test.go

Documentation: edge-infra.dev/test/e2e/linkerd

     1  package linkerd
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  
     8  	"cuelang.org/go/pkg/strings"
     9  	goVersion "github.com/hashicorp/go-version"
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  	"gotest.tools/v3/assert/cmp"
    13  	"gotest.tools/v3/poll"
    14  	corev1 "k8s.io/api/core/v1"
    15  	"k8s.io/apimachinery/pkg/types"
    16  	"sigs.k8s.io/controller-runtime/pkg/client"
    17  
    18  	"edge-infra.dev/pkg/edge/linkerd"
    19  	"edge-infra.dev/pkg/edge/linkerd/certs/trustanchor"
    20  	l5dv1alpha1 "edge-infra.dev/pkg/edge/linkerd/k8s/apis/linkerd/v1alpha1"
    21  	"edge-infra.dev/test/f2"
    22  	"edge-infra.dev/test/f2/x/ktest"
    23  	"edge-infra.dev/test/f2/x/ktest/kustomization"
    24  	l5dhelm "edge-infra.dev/third_party/k8s/linkerd/helm"
    25  )
    26  
    27  func TestManualTrustAnchorRotation(t *testing.T) {
    28  	var (
    29  		k                        *ktest.K8s
    30  		l5d                      *l5dv1alpha1.Linkerd
    31  		initialTrustAnchorSecret *corev1.Secret
    32  		injected                 []string
    33  	)
    34  	manualRotation := f2.NewFeature("manual trust anchor rotation").
    35  		Setup("setup", func(ctx f2.Context, t *testing.T) f2.Context {
    36  			k = ktest.FromContextT(ctx, t)
    37  
    38  			nodes := &corev1.NodeList{}
    39  			require.NoError(t, k.Client.List(ctx, nodes))
    40  
    41  			initialTrustAnchorSecret = &corev1.Secret{}
    42  			require.NoError(t, k.Client.Get(ctx, linkerd.TrustAnchorKey(), initialTrustAnchorSecret))
    43  			return ctx
    44  		}).
    45  		Test("manual rotation", func(ctx f2.Context, t *testing.T) f2.Context {
    46  			l5d = &l5dv1alpha1.Linkerd{}
    47  			assert.NoError(t, k.Client.Get(ctx, types.NamespacedName{Name: "linkerd.edge.ncr.com"}, l5d))
    48  			patch := client.RawPatch(types.MergePatchType, []byte(fmt.Sprintf(
    49  				`{"metadata":{"annotations":{"%s":"%s"}}}`,
    50  				trustanchor.ManualTrustAnchorRotation, "true"),
    51  			))
    52  			assert.NoError(t, k.Client.Patch(ctx, l5d, patch))
    53  			assert.Eventually(t, func() bool { return trustanchor.IsRotated(ctx, k.Client) }, ktest.Timeout*2, ktest.Tick, "Wait for linkerd trust anchor secret to be rotated")
    54  			return ctx
    55  		}).
    56  		Test("check manual rotation annotation", func(ctx f2.Context, t *testing.T) f2.Context {
    57  			l5d = &l5dv1alpha1.Linkerd{}
    58  			assert.NoError(t, k.Client.Get(ctx, types.NamespacedName{Name: "linkerd.edge.ncr.com"}, l5d))
    59  			assert.Eventually(t, func() bool { return !trustanchor.HasManualRotationAnnotation(l5d) }, ktest.Timeout*2, ktest.Tick, "wait for manual rotation annotation to be removed")
    60  			return ctx
    61  		}).
    62  		Test("trust anchor check", func(ctx f2.Context, t *testing.T) f2.Context {
    63  			currentTrustAnchorSecret := &corev1.Secret{}
    64  			assert.NoError(t, k.Client.Get(ctx, linkerd.TrustAnchorKey(), currentTrustAnchorSecret))
    65  			assert.NotEqual(t, currentTrustAnchorSecret.Data["tls.crt"], initialTrustAnchorSecret.Data["tls.crt"])
    66  			caBundleOK, err := trustanchor.CheckCaBundle(ctx, k.Client)
    67  			assert.NoError(t, err)
    68  			assert.True(t, caBundleOK)
    69  			return ctx
    70  		}).
    71  		Test("verify linkerd CR status", func(ctx f2.Context, t *testing.T) f2.Context {
    72  			l5d := &l5dv1alpha1.Linkerd{}
    73  			assert.NoError(t, k.Client.Get(ctx, types.NamespacedName{Name: "linkerd.edge.ncr.com"}, l5d))
    74  			assert.True(t, isReady(l5d.Status.Conditions))
    75  			assert.NotNil(t, l5d.Status.Version)
    76  			injected = l5d.Status.InjectedNamespaces
    77  			return ctx
    78  		}).
    79  		Test("verify linkerdworkloadinjection CR status", func(ctx f2.Context, t *testing.T) f2.Context {
    80  			workloadinjection := &l5dv1alpha1.LinkerdWorkloadInjection{}
    81  			assert.NoError(t, k.Client.Get(ctx, types.NamespacedName{Name: linkerd.Namespace, Namespace: linkerd.Namespace}, workloadinjection))
    82  			assert.True(t, isReady(workloadinjection.Status.Conditions))
    83  			return ctx
    84  		}).
    85  		Test("verify injected workloads are meshed successfully", func(ctx f2.Context, t *testing.T) f2.Context {
    86  			podList := &corev1.PodList{}
    87  			require.NoError(t, k.Client.List(ctx, podList))
    88  			for _, pod := range podList.Items {
    89  				if pod.Status.Phase == corev1.PodRunning && isInjected(injected, pod.Namespace) && !pod.Spec.HostNetwork && !isLinkerdDisabled(pod) {
    90  					assert.True(t, isMeshed(pod) && proxyReady(pod), pod.Name)
    91  				}
    92  			}
    93  			return ctx
    94  		}).
    95  		Feature()
    96  	f.Test(t, manualRotation)
    97  }
    98  
    99  // TestAutomaticTrustAnchorRotation is an e2e test that'll need to be run against a cluster at an older
   100  // version of linkerd to what is latest for us. If it isn't, this test will be skipped.
   101  func TestAutomaticTrustAnchorRotation(t *testing.T) {
   102  	const (
   103  		l5dObjName = l5dv1alpha1.Name
   104  	)
   105  	var (
   106  		k                        *ktest.K8s
   107  		l5d                      *l5dv1alpha1.Linkerd
   108  		initialTrustAnchorSecret *corev1.Secret
   109  		injected                 []string
   110  		latestL5dVersion         = l5dhelm.Version
   111  	)
   112  	automaticRotation := f2.NewFeature("automatic trust anchor rotation").
   113  		Setup("setup", func(ctx f2.Context, t *testing.T) f2.Context {
   114  			k = ktest.FromContextT(ctx, t)
   115  
   116  			nodes := &corev1.NodeList{}
   117  			require.NoError(t, k.Client.List(ctx, nodes))
   118  
   119  			initialTrustAnchorSecret = &corev1.Secret{}
   120  			require.NoError(t, k.Client.Get(ctx, linkerd.TrustAnchorKey(), initialTrustAnchorSecret))
   121  			return ctx
   122  		}).
   123  		Setup("linkerd version check", func(ctx f2.Context, t *testing.T) f2.Context {
   124  			l5d = &l5dv1alpha1.Linkerd{}
   125  			require.NoError(t, k.Client.Get(ctx, types.NamespacedName{Name: l5dObjName}, l5d))
   126  
   127  			versionCheckSuccess, err := isLinkerdUpgradable(l5d, latestL5dVersion)
   128  			require.NoError(t, err)
   129  			if !versionCheckSuccess {
   130  				t.SkipNow()
   131  			}
   132  			return ctx
   133  		}).
   134  		Setup("linkerd upgrade", func(ctx f2.Context, t *testing.T) f2.Context {
   135  			crdManifests, err := kustomization.ProcessManifests(ctx.RunID, linkerdCRDManifests, linkerd.Namespace)
   136  			require.NoError(t, err)
   137  			for _, manifest := range crdManifests {
   138  				require.NoError(t, k.Client.Create(ctx, manifest))
   139  			}
   140  			controllerManifests, err := kustomization.ProcessManifests(ctx.RunID, linkerdControllerManifests, linkerd.Namespace)
   141  			require.NoError(t, err)
   142  			for _, manifest := range controllerManifests {
   143  				require.NoError(t, k.Client.Create(ctx, manifest))
   144  			}
   145  			return ctx
   146  		}).
   147  		Setup("wait for linkerd upgrade", func(ctx f2.Context, t *testing.T) f2.Context {
   148  			l5d = &l5dv1alpha1.Linkerd{}
   149  			require.NoError(t, k.Client.Get(ctx, types.NamespacedName{Name: l5dObjName}, l5d))
   150  
   151  			k.WaitOn(t, k.Check(l5d, func(_ client.Object) cmp.Result {
   152  				require.NoError(t, k.Client.Get(ctx, types.NamespacedName{Name: l5dObjName}, l5d))
   153  				if l5d.Status.Version == latestL5dVersion {
   154  					return cmp.ResultSuccess
   155  				}
   156  				return cmp.ResultFailure("linkerd not upgraded")
   157  			}), poll.WithTimeout(time.Minute*2))
   158  			return ctx
   159  		}).
   160  		Test("verify linkerd CR status", func(ctx f2.Context, t *testing.T) f2.Context {
   161  			l5d := &l5dv1alpha1.Linkerd{}
   162  			assert.NoError(t, k.Client.Get(ctx, types.NamespacedName{Name: "linkerd.edge.ncr.com"}, l5d))
   163  			assert.True(t, isReady(l5d.Status.Conditions))
   164  			assert.NotNil(t, l5d.Status.Version)
   165  			injected = l5d.Status.InjectedNamespaces
   166  			return ctx
   167  		}).
   168  		Test("verify linkerdworkloadinjection CR status", func(ctx f2.Context, t *testing.T) f2.Context {
   169  			workloadinjection := &l5dv1alpha1.LinkerdWorkloadInjection{}
   170  			assert.NoError(t, k.Client.Get(ctx, types.NamespacedName{Name: linkerd.Namespace, Namespace: linkerd.Namespace}, workloadinjection))
   171  			assert.True(t, isReady(workloadinjection.Status.Conditions))
   172  			return ctx
   173  		}).
   174  		Test("verify injected workloads are meshed successfully", func(ctx f2.Context, t *testing.T) f2.Context {
   175  			podList := &corev1.PodList{}
   176  			require.NoError(t, k.Client.List(ctx, podList))
   177  			for _, pod := range podList.Items {
   178  				if pod.Status.Phase == corev1.PodRunning && isInjected(injected, pod.Namespace) && !pod.Spec.HostNetwork && !isLinkerdDisabled(pod) {
   179  					assert.True(t, isMeshed(pod) && proxyReady(pod), pod.Name)
   180  				}
   181  			}
   182  			return ctx
   183  		}).Feature()
   184  	f.Test(t, automaticRotation)
   185  }
   186  
   187  func isLinkerdUpgradable(l5d *l5dv1alpha1.Linkerd, latestL5dVersion string) (bool, error) {
   188  	currentVersion, err := goVersion.NewVersion(strings.TrimPrefix(l5d.Status.Version, "edge-"))
   189  	if err != nil {
   190  		return false, err
   191  	}
   192  	latestVersion, err := goVersion.NewVersion(strings.TrimPrefix(latestL5dVersion, "edge-"))
   193  	if err != nil {
   194  		return false, err
   195  	}
   196  	return currentVersion.LessThan(latestVersion), nil
   197  }
   198  

View as plain text