containerized-data-importer/pkg/controller/clone-controller_test.go
Michael Henriksen 66a1c8c996 Unique client cert per clone source pod (#987)
* unique cert per clone source pod

Signed-off-by: Michael Henriksen <mhenriks@redhat.com>

* upload controller shouldn't cache certs

Signed-off-by: Michael Henriksen <mhenriks@redhat.com>

* fix test variable names

Signed-off-by: Michael Henriksen <mhenriks@redhat.com>
2019-10-09 15:10:55 -04:00

260 lines
7.7 KiB
Go

package controller
import (
"crypto/rand"
"crypto/rsa"
"sync"
"testing"
corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"kubevirt.io/containerized-data-importer/pkg/util/cert/triple"
)
type CloneFixture struct {
ControllerFixture
}
var (
apiServerKey *rsa.PrivateKey
apiServerKeyOnce sync.Once
)
func testCreateClientKeyAndCert(ca *triple.KeyPair, commonName string, organizations []string) ([]byte, []byte, error) {
return []byte("foo"), []byte("bar"), nil
}
func getAPIServerKey() *rsa.PrivateKey {
apiServerKeyOnce.Do(func() {
apiServerKey, _ = rsa.GenerateKey(rand.Reader, 2048)
})
return apiServerKey
}
func newCloneFixture(t *testing.T) *CloneFixture {
f := &CloneFixture{
ControllerFixture: *newControllerFixture(t),
}
return f
}
func (f *CloneFixture) newCloneController() *CloneController {
v := newCloneTokenValidator(&getAPIServerKey().PublicKey)
return &CloneController{
Controller: *f.newController("test/mycloneimage", "Always", "5"),
recorder: &record.FakeRecorder{},
tokenValidator: v,
}
}
func (f *CloneFixture) run(pvcName string) {
f.runController(pvcName, true, false, false)
}
func (f *CloneFixture) runWithExpectation(pvcName string) {
f.runController(pvcName, true, false, true)
}
func (f *CloneFixture) runExpectError(pvcName string) {
f.runController(pvcName, true, true, false)
}
func (f *CloneFixture) runController(pvcName string,
startInformers bool,
expectError bool,
withCreateExpectation bool) {
c := f.newCloneController()
if startInformers {
stopCh := make(chan struct{})
defer close(stopCh)
go c.pvcInformer.Run(stopCh)
go c.podInformer.Run(stopCh)
cache.WaitForCacheSync(stopCh, c.pvcInformer.HasSynced)
cache.WaitForCacheSync(stopCh, c.pvcInformer.HasSynced)
}
if withCreateExpectation {
c.raisePodCreate(pvcName)
}
err := c.syncPvc(pvcName)
if !expectError && err != nil {
f.t.Errorf("error syncing pvc: %s: %v", pvcName, err)
} else if expectError && err == nil {
f.t.Error("expected error syncing pvc, got nil")
}
k8sActions := filterActions(f.kubeclient.Actions())
for i, action := range k8sActions {
if len(f.kubeactions) < i+1 {
f.t.Errorf("%d unexpected actions: %+v", len(k8sActions)-len(f.kubeactions), k8sActions[i:])
break
}
expectedAction := f.kubeactions[i]
checkAction(expectedAction, action, f.t)
}
if len(f.kubeactions) > len(k8sActions) {
f.t.Errorf("%d additional expected actions:%+v", len(f.kubeactions)-len(k8sActions), f.kubeactions[len(k8sActions):])
}
}
func TestWaitsTargetRunning(t *testing.T) {
f := newCloneFixture(t)
pvc := createClonePvc("source-ns", "golden-pvc", "target-ns", "target-pvc", nil, nil)
pvc.Annotations[AnnPodReady] = "false"
f.pvcLister = append(f.pvcLister, pvc)
f.kubeobjects = append(f.kubeobjects, pvc)
f.run(getPvcKey(pvc, t))
}
func TestWaitsTargetRunningNoAnnotation(t *testing.T) {
f := newCloneFixture(t)
pvc := createClonePvc("source-ns", "golden-pvc", "target-ns", "target-pvc", nil, nil)
f.pvcLister = append(f.pvcLister, pvc)
f.kubeobjects = append(f.kubeobjects, pvc)
f.run(getPvcKey(pvc, t))
}
func TestCreatesSourcePod(t *testing.T) {
createClientKeyAndCertFunc = testCreateClientKeyAndCert
defer func() {
createClientKeyAndCertFunc = createClientKeyAndCert
}()
f := newCloneFixture(t)
sourcePvc := createPvc("golden-pvc", "source-ns", nil, nil)
pvc := createClonePvc("source-ns", "golden-pvc", "target-ns", "target-pvc", nil, nil)
pvc.Annotations[AnnPodReady] = "true"
f.pvcLister = append(f.pvcLister, sourcePvc, pvc)
f.kubeobjects = append(f.kubeobjects, getUploadServerCASecret(), getUploadServerClientCASecret(), sourcePvc, pvc)
id := string(pvc.GetUID())
expSourcePod := createSourcePod(pvc, id)
pvcUpdate := pvc.DeepCopy()
pvcUpdate.Finalizers = []string{cloneSourcePodFinalizer}
f.expectUpdatePvcAction(pvcUpdate)
f.expectSecretGetAction(getUploadServerCASecret())
f.expectSecretGetAction(getUploadServerClientCASecret())
f.expectCreatePodAction(expSourcePod)
f.run(getPvcKey(pvc, t))
}
func TestAddsCloneOfAnnotation(t *testing.T) {
f := newCloneFixture(t)
pvc := createClonePvc("source-ns", "golden-pvc", "target-ns", "target-pvc", nil, nil)
pvc.Annotations[AnnPodReady] = "true"
pvc.Annotations[AnnPodPhase] = string(corev1.PodSucceeded)
id := string(pvc.GetUID())
pod := createSourcePod(pvc, id)
pod.Namespace = "source-ns"
f.pvcLister = append(f.pvcLister, pvc)
f.podLister = append(f.podLister, pod)
f.kubeobjects = append(f.kubeobjects, pvc, pod)
updatedPVC := pvc.DeepCopy()
updatedPVC.Annotations[AnnCloneOf] = "true"
f.expectUpdatePvcAction(updatedPVC)
f.run(getPvcKey(pvc, t))
}
func TestDeletesSourcePodAndFinalizer(t *testing.T) {
f := newCloneFixture(t)
pvc := createClonePvc("source-ns", "golden-pvc", "target-ns", "target-pvc", nil, nil)
pvc.Annotations[AnnCloneOf] = "true"
pvc.Finalizers = []string{cloneSourcePodFinalizer}
id := string(pvc.GetUID())
pod := createSourcePod(pvc, id)
pod.Name = pod.GenerateName + "random"
pod.Namespace = "source-ns"
f.pvcLister = append(f.pvcLister, pvc)
f.podLister = append(f.podLister, pod)
f.kubeobjects = append(f.kubeobjects, pvc, pod)
pvcUpdate := pvc.DeepCopy()
pvcUpdate.Finalizers = nil
f.expectDeletePodAction(pod)
f.expectUpdatePvcAction(pvcUpdate)
f.run(getPvcKey(pvc, t))
}
func TestSourceDoesNotExist(t *testing.T) {
f := newCloneFixture(t)
pvc := createClonePvc("source-ns", "golden-pvc", "target-ns", "target-pvc", nil, nil)
pvc.Annotations[AnnPodReady] = "true"
f.pvcLister = append(f.pvcLister, pvc)
f.kubeobjects = append(f.kubeobjects, pvc)
f.runExpectError(getPvcKey(pvc, t))
}
func TestExpectationsNotMet(t *testing.T) {
f := newCloneFixture(t)
pvc := createClonePvc("source-ns", "golden-pvc", "target-ns", "target-pvc", nil, nil)
pvc.Annotations[AnnPodReady] = "true"
f.pvcLister = append(f.pvcLister, pvc)
f.kubeobjects = append(f.kubeobjects, pvc)
f.runWithExpectation(getPvcKey(pvc, t))
}
// Verifies that one cannot clone a fs pvc to a block pvc
func TestCannotCloneFSToBlockPvc(t *testing.T) {
f := newCloneFixture(t)
sourcePvc := createPvc("golden-pvc", "source-ns", nil, nil)
pvc := createCloneBlockPvc("source-ns", "golden-pvc", "target-ns", "target-pvc", nil, nil)
pvc.Annotations[AnnPodReady] = "true"
f.pvcLister = append(f.pvcLister, sourcePvc, pvc)
f.kubeobjects = append(f.kubeobjects, sourcePvc, pvc)
f.runExpectError(getPvcKey(pvc, t))
}
// Verifies that one cannot clone a fs pvc to a block pvc
func TestCannotCloneBlockToFSPvc(t *testing.T) {
f := newCloneFixture(t)
sourcePvc := createBlockPvc("golden-pvc", "source-ns", nil, nil)
pvc := createClonePvc("source-ns", "golden-pvc", "target-ns", "target-pvc", nil, nil)
pvc.Annotations[AnnPodReady] = "true"
f.pvcLister = append(f.pvcLister, sourcePvc)
f.pvcLister = append(f.pvcLister, pvc)
f.kubeobjects = append(f.kubeobjects, sourcePvc)
f.kubeobjects = append(f.kubeobjects, pvc)
f.runExpectError(getPvcKey(pvc, t))
}
// Verifies that one cannot clone a fs pvc to a block pvc
func TestCannotCloneIfTargetIsSmaller(t *testing.T) {
f := newCloneFixture(t)
sourcePvc := createPvc("golden-pvc", "source-ns", nil, nil)
pvc := createClonePvcWithSize("source-ns", "golden-pvc", "target-ns", "target-pvc", nil, nil, "500M")
pvc.Annotations[AnnPodReady] = "true"
f.pvcLister = append(f.pvcLister, sourcePvc)
f.pvcLister = append(f.pvcLister, pvc)
f.kubeobjects = append(f.kubeobjects, sourcePvc)
f.kubeobjects = append(f.kubeobjects, pvc)
f.runExpectError(getPvcKey(pvc, t))
}
// verifies no work is done on pvcs without our annotations
func TestCloneIgnorePVC(t *testing.T) {
f := newCloneFixture(t)
pvc := createPvc("target-pvc", "target-ns", nil, nil)
f.pvcLister = append(f.pvcLister, pvc)
f.kubeobjects = append(f.kubeobjects, pvc)
f.run(getPvcKey(pvc, t))
}