diff --git a/pkg/kubelet/podcertificate/podcertificatemanager.go b/pkg/kubelet/podcertificate/podcertificatemanager.go index c0a2c669462..ec8c5127e85 100644 --- a/pkg/kubelet/podcertificate/podcertificatemanager.go +++ b/pkg/kubelet/podcertificate/podcertificatemanager.go @@ -481,7 +481,7 @@ func (m *IssuingManager) handleProjection(ctx context.Context, key projectionKey // remember creating the PCR, then we must be in case 2. Return to // credStateInitial so we create a new PCR. rec.curState = &credStateInitial{} - return fmt.Errorf("PodCertificateRequest %q appears to have been deleted", pcr.ObjectMeta.Namespace+"/"+pcr.ObjectMeta.Name) + return fmt.Errorf("PodCertificateRequest %q appears to have been deleted", key.Namespace+"/"+state.pcrName) } else if err != nil { return fmt.Errorf("while getting PodCertificateRequest %q: %w", key.Namespace+"/"+state.pcrName, err) } diff --git a/pkg/kubelet/podcertificate/podcertificatemanager_test.go b/pkg/kubelet/podcertificate/podcertificatemanager_test.go index 72feb85b74a..331a2c4e8e7 100644 --- a/pkg/kubelet/podcertificate/podcertificatemanager_test.go +++ b/pkg/kubelet/podcertificate/podcertificatemanager_test.go @@ -178,6 +178,117 @@ func TestTransitionInitialToWait(t *testing.T) { } } +func TestPCRDeletedWhileWaiting(t *testing.T) { + ctx, cancel := context.WithCancel(ktesting.Init(t)) + defer cancel() + + kc := fake.NewClientset() + clock := testclock.NewFakeClock(mustRFC3339(t, "2010-01-01T00:00:00Z")) + + signerName := "foo.com/signer" + + pcrStore := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}) + pcrLister := certlistersv1beta1.NewPodCertificateRequestLister(pcrStore) + + nodeStore := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}) + nodeLister := corelistersv1.NewNodeLister(nodeStore) + node1 := &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "node1", + UID: "node1-uid", + }, + } + if err := nodeStore.Add(node1); err != nil { + t.Fatalf("Unexpected error adding node: %v", err) + } + + workloadSA, err := kc.CoreV1().ServiceAccounts("ns1").Create(ctx, &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns1", + Name: "workload", + }, + }, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Unexpected error creating workload serviceaccount: %v", err) + } + + node1PodManager := &FakeSynchronousPodManager{ + pods: []*corev1.Pod{}, + } + + workloadPod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns1", + Name: "workload", + }, + Spec: corev1.PodSpec{ + ServiceAccountName: workloadSA.ObjectMeta.Name, + NodeName: "node1", + Containers: []corev1.Container{ + { + Name: "main", + Image: "notarealimage", + VolumeMounts: []corev1.VolumeMount{ + { + Name: "certificate", + MountPath: "/run/foo-cert", + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "certificate", + VolumeSource: corev1.VolumeSource{ + Projected: &corev1.ProjectedVolumeSource{ + Sources: []corev1.VolumeProjection{ + { + PodCertificate: &corev1.PodCertificateProjection{ + SignerName: signerName, + KeyType: "ED25519", + CredentialBundlePath: "creds.pem", + MaxExpirationSeconds: ptr.To[int32](86400), // Defaulting doesn't work with a fake client. + UserAnnotations: map[string]string{"test.domain/foo": "bar"}, + }, + }, + }, + }, + }, + }, + }, + }, + } + + node1PodManager.pods = append(node1PodManager.pods, workloadPod) + + node1PodCertificateManager := &IssuingManager{ + kc: kc, + podManager: node1PodManager, + pcrLister: pcrLister, + nodeLister: nodeLister, + nodeName: types.NodeName("node1"), + clock: clock, + credStore: map[projectionKey]*projectionRecord{}, + } + + // Step the handling state machine by one step. We should now be in wait state. + if err := node1PodCertificateManager.handleProjection(ctx, projectionKey{workloadPod.ObjectMeta.Namespace, workloadPod.ObjectMeta.Name, string(workloadPod.ObjectMeta.UID), "certificate", 0}); err != nil { + t.Fatalf("Unexpected error while running handleProjection: %v", err) + } + + // Clear all PCRs and advance time past assumeDeletedThreshold. + if err := kc.CertificatesV1beta1().PodCertificateRequests("ns1").DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{}); err != nil { + t.Fatalf("Unexpected error while deleting all PCRs in ns1: %v", err) + } + clock.Step(assumeDeletedThreshold + 1*time.Minute) + + // Calling handleProjection again should return an error, *not* nil panic. + err = node1PodCertificateManager.handleProjection(ctx, projectionKey{workloadPod.ObjectMeta.Namespace, workloadPod.ObjectMeta.Name, string(workloadPod.ObjectMeta.UID), "certificate", 0}) + if err == nil { // EQUALS nil + t.Fatalf("Got no error from handleProjection, but wanted an error") + } +} + func TestFullFlow(t *testing.T) { ctx, cancel := context.WithCancel(ktesting.Init(t)) defer cancel()