/* Copyright 2017 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package cleaner import ( "testing" "time" capi "k8s.io/api/certificates/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" ) const ( expiredCert = `-----BEGIN CERTIFICATE----- MIICIzCCAc2gAwIBAgIJAOApTlMFDOUnMA0GCSqGSIb3DQEBCwUAMG0xCzAJBgNV BAYTAkdCMQ8wDQYDVQQIDAZMb25kb24xDzANBgNVBAcMBkxvbmRvbjEYMBYGA1UE CgwPR2xvYmFsIFNlY3VyaXR5MRYwFAYDVQQLDA1JVCBEZXBhcnRtZW50MQowCAYD VQQDDAEqMB4XDTE3MTAwNDIwNDgzOFoXDTE3MTAwMzIwNDgzOFowbTELMAkGA1UE BhMCR0IxDzANBgNVBAgMBkxvbmRvbjEPMA0GA1UEBwwGTG9uZG9uMRgwFgYDVQQK DA9HbG9iYWwgU2VjdXJpdHkxFjAUBgNVBAsMDUlUIERlcGFydG1lbnQxCjAIBgNV BAMMASowXDANBgkqhkiG9w0BAQEFAANLADBIAkEA3Gt0KmuRXDxvqZUiX/xqAn1t nZZX98guZvPPyxnQtV3YpA274W0sX3jL+U71Ya+3kaUstXQa4YrWBUHiXoqJnwID AQABo1AwTjAdBgNVHQ4EFgQUtDsIpzHoUiLsO88f9fm+G0tYSPowHwYDVR0jBBgw FoAUtDsIpzHoUiLsO88f9fm+G0tYSPowDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B AQsFAANBADfrlKof5CUkxGlX9Rifxv/mWOk8ZuTLWfMYQH2nycBHnmOxy6sR+87W /Mb/uRz0TXVnGVcbu5E8Bz7e/Far1ZI= -----END CERTIFICATE-----` unexpiredCert = `-----BEGIN CERTIFICATE----- MIICJTCCAc+gAwIBAgIJAIRjMToP+pPEMA0GCSqGSIb3DQEBCwUAMG0xCzAJBgNV BAYTAkdCMQ8wDQYDVQQIDAZMb25kb24xDzANBgNVBAcMBkxvbmRvbjEYMBYGA1UE CgwPR2xvYmFsIFNlY3VyaXR5MRYwFAYDVQQLDA1JVCBEZXBhcnRtZW50MQowCAYD VQQDDAEqMCAXDTE3MTAwNDIwNDUyNFoYDzIxMTcwOTEwMjA0NTI0WjBtMQswCQYD VQQGEwJHQjEPMA0GA1UECAwGTG9uZG9uMQ8wDQYDVQQHDAZMb25kb24xGDAWBgNV BAoMD0dsb2JhbCBTZWN1cml0eTEWMBQGA1UECwwNSVQgRGVwYXJ0bWVudDEKMAgG A1UEAwwBKjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC7j9BAV5HqIJGi6r4G4YeI ioHxH2loVu8IOKSK7xVs3v/EjR/eXbQzM+jZU7duyZqn6YjySZNLl0K0MfHCHBgX AgMBAAGjUDBOMB0GA1UdDgQWBBTwxV40NFSNW7lpQ3eUWX7Mxs03yzAfBgNVHSME GDAWgBTwxV40NFSNW7lpQ3eUWX7Mxs03yzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3 DQEBCwUAA0EALDi9OidANHflx8q+w3p0rJo9gpA6cJcFpEtP2Lv4kvOtB1f6L0jY MLd7MVm4cS/MNcx4L7l23UC3Hx4+nAxvIg== -----END CERTIFICATE-----` ) func TestCleanerWithApprovedExpiredCSR(t *testing.T) { testCases := []struct { name string created metav1.Time certificate []byte conditions []capi.CertificateSigningRequestCondition expectedActions []string }{ { "no delete approved not passed deadline", metav1.NewTime(time.Now().Add(-1 * time.Minute)), []byte(unexpiredCert), []capi.CertificateSigningRequestCondition{ { Type: capi.CertificateApproved, LastUpdateTime: metav1.NewTime(time.Now().Add(-50 * time.Minute)), }, }, []string{}, }, { "no delete approved passed deadline not issued", metav1.NewTime(time.Now().Add(-1 * time.Minute)), nil, []capi.CertificateSigningRequestCondition{ { Type: capi.CertificateApproved, LastUpdateTime: metav1.NewTime(time.Now().Add(-50 * time.Minute)), }, }, []string{}, }, { "delete approved passed deadline", metav1.NewTime(time.Now().Add(-1 * time.Minute)), []byte(unexpiredCert), []capi.CertificateSigningRequestCondition{ { Type: capi.CertificateApproved, LastUpdateTime: metav1.NewTime(time.Now().Add(-2 * time.Hour)), }, }, []string{"delete"}, }, { "no delete denied not passed deadline", metav1.NewTime(time.Now().Add(-1 * time.Minute)), nil, []capi.CertificateSigningRequestCondition{ { Type: capi.CertificateDenied, LastUpdateTime: metav1.NewTime(time.Now().Add(-50 * time.Minute)), }, }, []string{}, }, { "delete denied passed deadline", metav1.NewTime(time.Now().Add(-1 * time.Minute)), nil, []capi.CertificateSigningRequestCondition{ { Type: capi.CertificateDenied, LastUpdateTime: metav1.NewTime(time.Now().Add(-2 * time.Hour)), }, }, []string{"delete"}, }, { "no delete pending not passed deadline", metav1.NewTime(time.Now().Add(-5 * time.Hour)), nil, []capi.CertificateSigningRequestCondition{}, []string{}, }, { "delete pending passed deadline", metav1.NewTime(time.Now().Add(-25 * time.Hour)), nil, []capi.CertificateSigningRequestCondition{}, []string{"delete"}, }, { "no delete approved not passed deadline unexpired", metav1.NewTime(time.Now().Add(-1 * time.Minute)), []byte(unexpiredCert), []capi.CertificateSigningRequestCondition{ { Type: capi.CertificateApproved, LastUpdateTime: metav1.NewTime(time.Now().Add(-50 * time.Minute)), }, }, []string{}, }, { "delete approved not passed deadline expired", metav1.NewTime(time.Now().Add(-1 * time.Minute)), []byte(expiredCert), []capi.CertificateSigningRequestCondition{ { Type: capi.CertificateApproved, LastUpdateTime: metav1.NewTime(time.Now().Add(-50 * time.Minute)), }, }, []string{"delete"}, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { csr := &capi.CertificateSigningRequest{ ObjectMeta: metav1.ObjectMeta{ Name: "fake-csr", CreationTimestamp: tc.created, }, Status: capi.CertificateSigningRequestStatus{ Certificate: tc.certificate, Conditions: tc.conditions, }, } client := fake.NewSimpleClientset(csr) s := &CSRCleanerController{ csrClient: client.CertificatesV1beta1().CertificateSigningRequests(), } err := s.handle(csr) if err != nil { t.Fatalf("failed to clean CSR: %v", err) } actions := client.Actions() if len(actions) != len(tc.expectedActions) { t.Fatalf("got %d actions, wanted %d actions", len(actions), len(tc.expectedActions)) } for i := 0; i < len(actions); i++ { if a := actions[i]; !a.Matches(tc.expectedActions[i], "certificatesigningrequests") { t.Errorf("got action %#v, wanted %v", a, tc.expectedActions[i]) } } }) } }