refactor certificate controller
This commit is contained in:
@@ -17,209 +17,70 @@ limitations under the License.
|
||||
package certificates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/util/cert"
|
||||
"k8s.io/client-go/util/cert/triple"
|
||||
certificates "k8s.io/kubernetes/pkg/apis/certificates/v1beta1"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake"
|
||||
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/externalversions"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
)
|
||||
|
||||
type testController struct {
|
||||
*CertificateController
|
||||
certFile string
|
||||
keyFile string
|
||||
csrStore cache.Store
|
||||
informerFactory informers.SharedInformerFactory
|
||||
approver *fakeAutoApprover
|
||||
}
|
||||
|
||||
func alwaysReady() bool { return true }
|
||||
|
||||
func newController(csrs ...runtime.Object) (*testController, error) {
|
||||
client := fake.NewSimpleClientset(csrs...)
|
||||
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
|
||||
|
||||
certFile, keyFile, err := createTestCertFiles()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signer, err := NewCFSSLSigner(certFile, keyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
approver := &fakeAutoApprover{make(chan *certificates.CertificateSigningRequest, 1)}
|
||||
controller, err := NewCertificateController(
|
||||
client,
|
||||
informerFactory.Certificates().V1beta1().CertificateSigningRequests(),
|
||||
signer,
|
||||
approver,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
controller.csrsSynced = alwaysReady
|
||||
|
||||
return &testController{
|
||||
controller,
|
||||
certFile,
|
||||
keyFile,
|
||||
informerFactory.Certificates().V1beta1().CertificateSigningRequests().Informer().GetStore(),
|
||||
informerFactory,
|
||||
approver,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *testController) cleanup() {
|
||||
os.Remove(c.certFile)
|
||||
os.Remove(c.keyFile)
|
||||
}
|
||||
|
||||
func createTestCertFiles() (string, string, error) {
|
||||
keyPair, err := triple.NewCA("test-ca")
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Generate cert
|
||||
certBuffer := bytes.Buffer{}
|
||||
if err := pem.Encode(&certBuffer, &pem.Block{Type: "CERTIFICATE", Bytes: keyPair.Cert.Raw}); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Generate key
|
||||
keyBuffer := bytes.Buffer{}
|
||||
if err := pem.Encode(&keyBuffer, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(keyPair.Key)}); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
certFile, err := ioutil.TempFile(dir, "cert")
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
keyFile, err := ioutil.TempFile(dir, "key")
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
_, err = certFile.Write(certBuffer.Bytes())
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
certFile.Close()
|
||||
|
||||
_, err = keyFile.Write(keyBuffer.Bytes())
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
keyFile.Close()
|
||||
|
||||
return certFile.Name(), keyFile.Name(), nil
|
||||
}
|
||||
|
||||
type fakeAutoApprover struct {
|
||||
csr chan *certificates.CertificateSigningRequest
|
||||
}
|
||||
|
||||
func (f *fakeAutoApprover) AutoApprove(csr *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, error) {
|
||||
csr.Status.Conditions = append(csr.Status.Conditions, certificates.CertificateSigningRequestCondition{
|
||||
Type: certificates.CertificateApproved,
|
||||
Reason: "test reason",
|
||||
Message: "test message",
|
||||
})
|
||||
f.csr <- csr
|
||||
return csr, nil
|
||||
}
|
||||
|
||||
// TODO flesh this out to cover things like not being able to find the csr in the cache, not
|
||||
// auto-approving, etc.
|
||||
func TestCertificateController(t *testing.T) {
|
||||
csrKey, err := cert.NewPrivateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("error creating private key for csr: %v", err)
|
||||
}
|
||||
|
||||
subject := &pkix.Name{
|
||||
Organization: []string{"test org"},
|
||||
CommonName: "test cn",
|
||||
}
|
||||
csrBytes, err := cert.MakeCSR(csrKey, subject, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating csr: %v", err)
|
||||
}
|
||||
|
||||
csr := &certificates.CertificateSigningRequest{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-csr",
|
||||
},
|
||||
Spec: certificates.CertificateSigningRequestSpec{
|
||||
Request: csrBytes,
|
||||
Usages: []certificates.KeyUsage{
|
||||
certificates.UsageDigitalSignature,
|
||||
certificates.UsageKeyEncipherment,
|
||||
certificates.UsageClientAuth,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
controller, err := newController(csr)
|
||||
client := fake.NewSimpleClientset(csr)
|
||||
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
|
||||
|
||||
handler := func(csr *certificates.CertificateSigningRequest) error {
|
||||
csr.Status.Conditions = append(csr.Status.Conditions, certificates.CertificateSigningRequestCondition{
|
||||
Type: certificates.CertificateApproved,
|
||||
Reason: "test reason",
|
||||
Message: "test message",
|
||||
})
|
||||
_, err := client.Certificates().CertificateSigningRequests().UpdateApproval(csr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
controller, err := NewCertificateController(
|
||||
client,
|
||||
informerFactory.Certificates().V1beta1().CertificateSigningRequests(),
|
||||
handler,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating controller: %v", err)
|
||||
}
|
||||
defer controller.cleanup()
|
||||
|
||||
received := make(chan struct{})
|
||||
|
||||
controllerSyncHandler := controller.syncHandler
|
||||
controller.syncHandler = func(key string) error {
|
||||
defer close(received)
|
||||
return controllerSyncHandler(key)
|
||||
}
|
||||
controller.csrsSynced = func() bool { return true }
|
||||
|
||||
stopCh := make(chan struct{})
|
||||
defer close(stopCh)
|
||||
go controller.Run(1, stopCh)
|
||||
go controller.informerFactory.Start(stopCh)
|
||||
go informerFactory.Start(stopCh)
|
||||
|
||||
select {
|
||||
case <-received:
|
||||
case <-time.After(wait.ForeverTestTimeout):
|
||||
t.Errorf("timed out")
|
||||
controller.processNextWorkItem()
|
||||
|
||||
actions := client.Actions()
|
||||
if len(actions) != 3 {
|
||||
t.Errorf("expected 3 actions")
|
||||
}
|
||||
if a := actions[0]; !a.Matches("list", "certificatesigningrequests") {
|
||||
t.Errorf("unexpected action: %#v", a)
|
||||
}
|
||||
if a := actions[1]; !a.Matches("watch", "certificatesigningrequests") {
|
||||
t.Errorf("unexpected action: %#v", a)
|
||||
}
|
||||
if a := actions[2]; !a.Matches("update", "certificatesigningrequests") ||
|
||||
a.GetSubresource() != "approval" {
|
||||
t.Errorf("unexpected action: %#v", a)
|
||||
}
|
||||
|
||||
csr = <-controller.approver.csr
|
||||
|
||||
if e, a := 1, len(csr.Status.Conditions); e != a {
|
||||
t.Fatalf("expected %d status condition, got %d", e, a)
|
||||
}
|
||||
if e, a := certificates.CertificateApproved, csr.Status.Conditions[0].Type; e != a {
|
||||
t.Errorf("type: expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := "test reason", csr.Status.Conditions[0].Reason; e != a {
|
||||
t.Errorf("reason: expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := "test message", csr.Status.Conditions[0].Message; e != a {
|
||||
t.Errorf("message: expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user