k8s.io/kubernetes@v1.29.3/test/e2e/upgrades/auth/serviceaccount_admission_controller_migration.go (about)

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package auth
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"time"
    23  
    24  	"github.com/onsi/ginkgo/v2"
    25  	v1 "k8s.io/api/core/v1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/util/wait"
    28  	e2eauth "k8s.io/kubernetes/test/e2e/auth"
    29  	"k8s.io/kubernetes/test/e2e/framework"
    30  	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
    31  	"k8s.io/kubernetes/test/e2e/upgrades"
    32  	imageutils "k8s.io/kubernetes/test/utils/image"
    33  )
    34  
    35  const (
    36  	podBeforeMigrationName = "pod-before-migration"
    37  	podAfterMigrationName  = "pod-after-migration"
    38  )
    39  
    40  // ServiceAccountAdmissionControllerMigrationTest test that a pod is functioning before and after
    41  // a cluster upgrade.
    42  type ServiceAccountAdmissionControllerMigrationTest struct {
    43  	pod *v1.Pod
    44  }
    45  
    46  // Name returns the tracking name of the test.
    47  func (ServiceAccountAdmissionControllerMigrationTest) Name() string {
    48  	return "[sig-auth] serviceaccount-admission-controller-migration"
    49  }
    50  
    51  // Setup creates pod-before-migration which has legacy service account token.
    52  func (t *ServiceAccountAdmissionControllerMigrationTest) Setup(ctx context.Context, f *framework.Framework) {
    53  	t.pod = createPod(ctx, f, podBeforeMigrationName)
    54  	inClusterClientMustWork(ctx, f, t.pod)
    55  }
    56  
    57  // Test waits for the upgrade to complete, and then verifies pod-before-migration
    58  // and pod-after-migration are able to make requests using in cluster config.
    59  func (t *ServiceAccountAdmissionControllerMigrationTest) Test(ctx context.Context, f *framework.Framework, done <-chan struct{}, upgrade upgrades.UpgradeType) {
    60  	ginkgo.By("Waiting for upgrade to finish")
    61  	<-done
    62  
    63  	ginkgo.By("Starting post-upgrade check")
    64  	ginkgo.By("Checking pod-before-migration makes successful requests using in cluster config")
    65  	podBeforeMigration, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Get(ctx, podBeforeMigrationName, metav1.GetOptions{})
    66  	framework.ExpectNoError(err)
    67  	if podBeforeMigration.GetUID() != t.pod.GetUID() {
    68  		framework.Failf("Pod %q GetUID() = %q, want %q.", podBeforeMigration.Name, podBeforeMigration.GetUID(), t.pod.GetUID())
    69  	}
    70  	if podBeforeMigration.Status.ContainerStatuses[0].RestartCount != 0 {
    71  		framework.Failf("Pod %q RestartCount = %d, want 0.", podBeforeMigration.Name, podBeforeMigration.Status.ContainerStatuses[0].RestartCount)
    72  	}
    73  	inClusterClientMustWork(ctx, f, podBeforeMigration)
    74  
    75  	ginkgo.By("Checking pod-after-migration makes successful requests using in cluster config")
    76  	podAfterMigration := createPod(ctx, f, podAfterMigrationName)
    77  	if len(podAfterMigration.Spec.Volumes) != 1 || podAfterMigration.Spec.Volumes[0].Projected == nil {
    78  		framework.Failf("Pod %q Volumes[0].Projected.Sources = nil, want non-nil.", podAfterMigration.Name)
    79  	}
    80  	inClusterClientMustWork(ctx, f, podAfterMigration)
    81  
    82  	ginkgo.By("Finishing post-upgrade check")
    83  }
    84  
    85  // Teardown cleans up any remaining resources.
    86  func (t *ServiceAccountAdmissionControllerMigrationTest) Teardown(ctx context.Context, f *framework.Framework) {
    87  	// rely on the namespace deletion to clean up everything
    88  }
    89  
    90  func inClusterClientMustWork(ctx context.Context, f *framework.Framework, pod *v1.Pod) {
    91  	var logs string
    92  	since := time.Now()
    93  	if err := wait.PollImmediate(15*time.Second, 5*time.Minute, func() (done bool, err error) {
    94  		framework.Logf("Polling logs")
    95  		logs, err = e2epod.GetPodLogsSince(ctx, f.ClientSet, pod.Namespace, pod.Name, "inclusterclient", since)
    96  		if err != nil {
    97  			framework.Logf("Error pulling logs: %v", err)
    98  			return false, nil
    99  		}
   100  		numTokens, err := e2eauth.ParseInClusterClientLogs(logs)
   101  		if err != nil {
   102  			framework.Logf("Error parsing inclusterclient logs: %v", err)
   103  			return false, fmt.Errorf("inclusterclient reported an error: %w", err)
   104  		}
   105  		if numTokens == 0 {
   106  			framework.Logf("No authenticated API calls found")
   107  			return false, nil
   108  		}
   109  		return true, nil
   110  	}); err != nil {
   111  		framework.Failf("Unexpected error: %v\n%s", err, logs)
   112  	}
   113  }
   114  
   115  // createPod creates a pod.
   116  func createPod(ctx context.Context, f *framework.Framework, podName string) *v1.Pod {
   117  	pod := &v1.Pod{
   118  		ObjectMeta: metav1.ObjectMeta{
   119  			Name:      podName,
   120  			Namespace: f.Namespace.Name,
   121  		},
   122  		Spec: v1.PodSpec{
   123  			Containers: []v1.Container{{
   124  				Name:  "inclusterclient",
   125  				Image: imageutils.GetE2EImage(imageutils.Agnhost),
   126  				Args:  []string{"inclusterclient", "--poll-interval=5"},
   127  			}},
   128  			RestartPolicy: v1.RestartPolicyNever,
   129  		},
   130  	}
   131  
   132  	createdPod, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(ctx, pod, metav1.CreateOptions{})
   133  	framework.ExpectNoError(err)
   134  	framework.Logf("Created pod %s", podName)
   135  
   136  	if !e2epod.CheckPodsRunningReady(ctx, f.ClientSet, f.Namespace.Name, []string{pod.Name}, time.Minute) {
   137  		framework.Failf("Pod %q/%q never became ready", createdPod.Namespace, createdPod.Name)
   138  	}
   139  
   140  	return createdPod
   141  }