containerized-data-importer/pkg/controller/clone-controller_test.go
Alexander Wels 17db2920b1
Update clone controller to use runtime library. (#1075)
Signed-off-by: Alexander Wels <awels@redhat.com>

Co-authored-by: Michael Henriksen <mhenriks@redhat.com>
2020-02-14 23:04:46 +01:00

595 lines
25 KiB
Go

package controller
import (
"context"
"crypto/rand"
"crypto/rsa"
"fmt"
"sync"
"time"
"kubevirt.io/containerized-data-importer/pkg/util/cert/fetcher"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
k8sfake "k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/record"
cdiv1 "kubevirt.io/containerized-data-importer/pkg/apis/core/v1alpha1"
"kubevirt.io/containerized-data-importer/pkg/common"
"kubevirt.io/containerized-data-importer/pkg/token"
"kubevirt.io/containerized-data-importer/pkg/util/cert/triple"
)
var (
apiServerKey *rsa.PrivateKey
apiServerKeyOnce sync.Once
)
type fakeCertGenerator struct {
}
func (cg *fakeCertGenerator) MakeClientCert(name string, groups []string, duration time.Duration) ([]byte, []byte, error) {
return []byte("foo"), []byte("bar"), nil
}
func (cg *fakeCertGenerator) MakeServerCert(namespace, service string, duration time.Duration) ([]byte, []byte, error) {
return []byte("foo"), []byte("bar"), nil
}
type FakeValidator struct {
match string
Operation token.Operation
Name string
Namespace string
Resource metav1.GroupVersionResource
Params map[string]string
}
var _ = Describe("Clone controller reconcile loop", func() {
var (
reconciler *CloneReconciler
)
AfterEach(func() {
if reconciler != nil {
close(reconciler.recorder.(*record.FakeRecorder).Events)
reconciler = nil
}
})
It("Should return success if a PVC with no annotations is passed, due to it being ignored", func() {
reconciler = createCloneReconciler(createPvc("testPvc1", "default", map[string]string{}, nil))
_, err := reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: "testPvc1", Namespace: "default"}})
Expect(err).ToNot(HaveOccurred())
})
It("Should return success if no PVC can be found, due to it not existing", func() {
reconciler = createCloneReconciler()
_, err := reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: "testPvc1", Namespace: "default"}})
Expect(err).ToNot(HaveOccurred())
})
It("Should return success if no PVC can be found due to not existing in passed namespace", func() {
reconciler = createCloneReconciler(createPvc("testPvc1", "default", map[string]string{AnnEndpoint: testEndPoint}, nil))
_, err := reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: "testPvc1", Namespace: "invalid"}})
Expect(err).ToNot(HaveOccurred())
})
It("Should return success if a PVC with clone request annotation and cloneof is passed, due to it being ignored", func() {
reconciler = createCloneReconciler(createPvc("testPvc1", "default", map[string]string{AnnCloneRequest: "cloneme", AnnCloneOf: "something"}, nil))
_, err := reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: "testPvc1", Namespace: "default"}})
Expect(err).ToNot(HaveOccurred())
})
It("Should return success if target pod is not ready", func() {
reconciler = createCloneReconciler(createPvc("testPvc1", "default", map[string]string{AnnCloneRequest: "cloneme"}, nil))
_, err := reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: "testPvc1", Namespace: "default"}})
Expect(err).ToNot(HaveOccurred())
})
It("Should return error if target pod annotation is invalid", func() {
reconciler = createCloneReconciler(createPvc("testPvc1", "default", map[string]string{AnnCloneRequest: "cloneme", AnnPodReady: "invalid"}, nil))
_, err := reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: "testPvc1", Namespace: "default"}})
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(fmt.Sprintf("error parsing %s annotation", AnnPodReady)))
})
It("Should create new source pod if none exists, and target pod is marked ready", func() {
testPvc := createPvc("testPvc1", "default", map[string]string{
AnnCloneRequest: "default/source", AnnPodReady: "true", AnnCloneToken: "foobaz", AnnUploadClientName: "uploadclient"}, nil)
reconciler = createCloneReconciler(testPvc, createPvc("source", "default", map[string]string{}, nil))
By("Setting up the match token")
reconciler.tokenValidator.(*FakeValidator).match = "foobaz"
reconciler.tokenValidator.(*FakeValidator).Name = "source"
reconciler.tokenValidator.(*FakeValidator).Namespace = "default"
reconciler.tokenValidator.(*FakeValidator).Params["targetNamespace"] = "default"
reconciler.tokenValidator.(*FakeValidator).Params["targetName"] = "testPvc1"
By("Verifying no source pod exists")
sourcePod, err := reconciler.findCloneSourcePod(testPvc)
Expect(sourcePod).To(BeNil())
_, err = reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: "testPvc1", Namespace: "default"}})
Expect(err).ToNot(HaveOccurred())
By("Verifying source pod exists")
sourcePod, err = reconciler.findCloneSourcePod(testPvc)
Expect(err).ToNot(HaveOccurred())
Expect(sourcePod.GetLabels()[CloneUniqueID]).To(Equal("default-testPvc1-source-pod"))
By("Verifying the PVC now has a finalizer")
err = reconciler.Client.Get(context.TODO(), types.NamespacedName{Name: "testPvc1", Namespace: "default"}, testPvc)
Expect(err).ToNot(HaveOccurred())
Expect(reconciler.hasFinalizer(testPvc, cloneSourcePodFinalizer)).To(BeTrue())
})
It("Should error with missing upload client name annotation if none provided", func() {
testPvc := createPvc("testPvc1", "default", map[string]string{
AnnCloneRequest: "default/source", AnnPodReady: "true", AnnCloneToken: "foobaz"}, nil)
reconciler = createCloneReconciler(testPvc, createPvc("source", "default", map[string]string{}, nil))
By("Setting up the match token")
reconciler.tokenValidator.(*FakeValidator).match = "foobaz"
reconciler.tokenValidator.(*FakeValidator).Name = "source"
reconciler.tokenValidator.(*FakeValidator).Namespace = "default"
reconciler.tokenValidator.(*FakeValidator).Params["targetNamespace"] = "default"
reconciler.tokenValidator.(*FakeValidator).Params["targetName"] = "testPvc1"
By("Verifying no source pod exists")
sourcePod, err := reconciler.findCloneSourcePod(testPvc)
Expect(sourcePod).To(BeNil())
_, err = reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: "testPvc1", Namespace: "default"}})
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("missing required " + AnnUploadClientName + " annotation"))
})
It("Should update the PVC from the pod status", func() {
testPvc := createPvc("testPvc1", "default", map[string]string{
AnnCloneRequest: "default/source", AnnPodReady: "true", AnnCloneToken: "foobaz", AnnUploadClientName: "uploadclient"}, nil)
reconciler = createCloneReconciler(testPvc, createPvc("source", "default", map[string]string{}, nil))
By("Setting up the match token")
reconciler.tokenValidator.(*FakeValidator).match = "foobaz"
reconciler.tokenValidator.(*FakeValidator).Name = "source"
reconciler.tokenValidator.(*FakeValidator).Namespace = "default"
reconciler.tokenValidator.(*FakeValidator).Params["targetNamespace"] = "default"
reconciler.tokenValidator.(*FakeValidator).Params["targetName"] = "testPvc1"
By("Verifying no source pod exists")
sourcePod, err := reconciler.findCloneSourcePod(testPvc)
Expect(sourcePod).To(BeNil())
_, err = reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: "testPvc1", Namespace: "default"}})
Expect(err).ToNot(HaveOccurred())
By("Verifying source pod exists")
sourcePod, err = reconciler.findCloneSourcePod(testPvc)
Expect(err).ToNot(HaveOccurred())
Expect(sourcePod.GetLabels()[CloneUniqueID]).To(Equal("default-testPvc1-source-pod"))
By("Verifying the PVC now has a finalizer")
err = reconciler.Client.Get(context.TODO(), types.NamespacedName{Name: "testPvc1", Namespace: "default"}, testPvc)
Expect(err).ToNot(HaveOccurred())
Expect(reconciler.hasFinalizer(testPvc, cloneSourcePodFinalizer)).To(BeTrue())
})
It("Should update the cloneof when complete", func() {
testPvc := createPvc("testPvc1", "default", map[string]string{
AnnCloneRequest: "default/source", AnnPodReady: "true", AnnCloneToken: "foobaz", AnnUploadClientName: "uploadclient"}, nil)
reconciler = createCloneReconciler(testPvc, createPvc("source", "default", map[string]string{}, nil))
By("Setting up the match token")
reconciler.tokenValidator.(*FakeValidator).match = "foobaz"
reconciler.tokenValidator.(*FakeValidator).Name = "source"
reconciler.tokenValidator.(*FakeValidator).Namespace = "default"
reconciler.tokenValidator.(*FakeValidator).Params["targetNamespace"] = "default"
reconciler.tokenValidator.(*FakeValidator).Params["targetName"] = "testPvc1"
By("Verifying no source pod exists")
sourcePod, err := reconciler.findCloneSourcePod(testPvc)
Expect(sourcePod).To(BeNil())
_, err = reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: "testPvc1", Namespace: "default"}})
Expect(err).ToNot(HaveOccurred())
By("Verifying source pod exists")
sourcePod, err = reconciler.findCloneSourcePod(testPvc)
Expect(err).ToNot(HaveOccurred())
Expect(sourcePod.GetLabels()[CloneUniqueID]).To(Equal("default-testPvc1-source-pod"))
By("Verifying the PVC now has a finalizer")
err = reconciler.Client.Get(context.TODO(), types.NamespacedName{Name: "testPvc1", Namespace: "default"}, testPvc)
Expect(err).ToNot(HaveOccurred())
Expect(reconciler.hasFinalizer(testPvc, cloneSourcePodFinalizer)).To(BeTrue())
By("Updating the PVC to completed")
testPvc = createPvc("testPvc1", "default", map[string]string{
AnnCloneRequest: "default/source", AnnPodReady: "true", AnnCloneToken: "foobaz", AnnUploadClientName: "uploadclient", AnnPodPhase: string(corev1.PodSucceeded)}, nil)
reconciler.Client.Update(context.TODO(), testPvc)
_, err = reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: "testPvc1", Namespace: "default"}})
Expect(err).ToNot(HaveOccurred())
err = reconciler.Client.Get(context.TODO(), types.NamespacedName{Name: "testPvc1", Namespace: "default"}, testPvc)
Expect(err).ToNot(HaveOccurred())
Expect(testPvc.GetAnnotations()[AnnCloneOf]).To(Equal("true"))
sourcePod, err = reconciler.findCloneSourcePod(testPvc)
Expect(err).ToNot(HaveOccurred())
Expect(sourcePod).ToNot(BeNil())
_, err = reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: "testPvc1", Namespace: "default"}})
Expect(err).ToNot(HaveOccurred())
By("Checking error event recorded")
event := <-reconciler.recorder.(*record.FakeRecorder).Events
Expect(event).To(ContainSubstring("Clone Successful"))
sourcePod, err = reconciler.findCloneSourcePod(testPvc)
Expect(err).ToNot(HaveOccurred())
Expect(sourcePod).To(BeNil())
})
It("Should update the cloneof when complete, block mode", func() {
testPvc := createBlockPvc("testPvc1", "default", map[string]string{
AnnCloneRequest: "default/source", AnnPodReady: "true", AnnCloneToken: "foobaz", AnnUploadClientName: "uploadclient"}, nil)
reconciler = createCloneReconciler(testPvc, createBlockPvc("source", "default", map[string]string{}, nil))
By("Setting up the match token")
reconciler.tokenValidator.(*FakeValidator).match = "foobaz"
reconciler.tokenValidator.(*FakeValidator).Name = "source"
reconciler.tokenValidator.(*FakeValidator).Namespace = "default"
reconciler.tokenValidator.(*FakeValidator).Params["targetNamespace"] = "default"
reconciler.tokenValidator.(*FakeValidator).Params["targetName"] = "testPvc1"
By("Verifying no source pod exists")
sourcePod, err := reconciler.findCloneSourcePod(testPvc)
Expect(sourcePod).To(BeNil())
_, err = reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: "testPvc1", Namespace: "default"}})
Expect(err).ToNot(HaveOccurred())
By("Verifying source pod exists")
sourcePod, err = reconciler.findCloneSourcePod(testPvc)
Expect(err).ToNot(HaveOccurred())
Expect(sourcePod.GetLabels()[CloneUniqueID]).To(Equal("default-testPvc1-source-pod"))
By("Verifying the PVC now has a finalizer")
err = reconciler.Client.Get(context.TODO(), types.NamespacedName{Name: "testPvc1", Namespace: "default"}, testPvc)
Expect(err).ToNot(HaveOccurred())
Expect(reconciler.hasFinalizer(testPvc, cloneSourcePodFinalizer)).To(BeTrue())
By("Updating the PVC to completed")
testPvc = createPvc("testPvc1", "default", map[string]string{
AnnCloneRequest: "default/source", AnnPodReady: "true", AnnCloneToken: "foobaz", AnnUploadClientName: "uploadclient", AnnPodPhase: string(corev1.PodSucceeded)}, nil)
reconciler.Client.Update(context.TODO(), testPvc)
_, err = reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: "testPvc1", Namespace: "default"}})
Expect(err).ToNot(HaveOccurred())
err = reconciler.Client.Get(context.TODO(), types.NamespacedName{Name: "testPvc1", Namespace: "default"}, testPvc)
Expect(err).ToNot(HaveOccurred())
Expect(testPvc.GetAnnotations()[AnnCloneOf]).To(Equal("true"))
sourcePod, err = reconciler.findCloneSourcePod(testPvc)
Expect(err).ToNot(HaveOccurred())
Expect(sourcePod).ToNot(BeNil())
_, err = reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: "testPvc1", Namespace: "default"}})
Expect(err).ToNot(HaveOccurred())
By("Checking error event recorded")
event := <-reconciler.recorder.(*record.FakeRecorder).Events
Expect(event).To(ContainSubstring("Clone Successful"))
sourcePod, err = reconciler.findCloneSourcePod(testPvc)
Expect(err).ToNot(HaveOccurred())
Expect(sourcePod).To(BeNil())
})
It("Should error when source and target volume modes do not match (fs->block)", func() {
testPvc := createBlockPvc("testPvc1", "default", map[string]string{
AnnCloneRequest: "default/source", AnnPodReady: "true", AnnCloneToken: "foobaz", AnnUploadClientName: "uploadclient"}, nil)
reconciler = createCloneReconciler(testPvc, createPvc("source", "default", map[string]string{}, nil))
By("Setting up the match token")
reconciler.tokenValidator.(*FakeValidator).match = "foobaz"
reconciler.tokenValidator.(*FakeValidator).Name = "source"
reconciler.tokenValidator.(*FakeValidator).Namespace = "default"
reconciler.tokenValidator.(*FakeValidator).Params["targetNamespace"] = "default"
reconciler.tokenValidator.(*FakeValidator).Params["targetName"] = "testPvc1"
By("Verifying no source pod exists")
sourcePod, err := reconciler.findCloneSourcePod(testPvc)
Expect(sourcePod).To(BeNil())
_, err = reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: "testPvc1", Namespace: "default"}})
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("source volumeMode (Filesystem) and target volumeMode (Block) do not match"))
})
It("Should error when source and target volume modes do not match (fs->block)", func() {
testPvc := createPvc("testPvc1", "default", map[string]string{
AnnCloneRequest: "default/source", AnnPodReady: "true", AnnCloneToken: "foobaz", AnnUploadClientName: "uploadclient"}, nil)
reconciler = createCloneReconciler(testPvc, createBlockPvc("source", "default", map[string]string{}, nil))
By("Setting up the match token")
reconciler.tokenValidator.(*FakeValidator).match = "foobaz"
reconciler.tokenValidator.(*FakeValidator).Name = "source"
reconciler.tokenValidator.(*FakeValidator).Namespace = "default"
reconciler.tokenValidator.(*FakeValidator).Params["targetNamespace"] = "default"
reconciler.tokenValidator.(*FakeValidator).Params["targetName"] = "testPvc1"
By("Verifying no source pod exists")
sourcePod, err := reconciler.findCloneSourcePod(testPvc)
Expect(sourcePod).To(BeNil())
_, err = reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: "testPvc1", Namespace: "default"}})
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("source volumeMode (Block) and target volumeMode (Filesystem) do not match"))
})
})
var _ = Describe("ParseCloneRequestAnnotation", func() {
It("should return false/empty/empty if no annotation exists", func() {
pvc := createPvc("testPvc1", "default", map[string]string{}, nil)
exists, ns, name := ParseCloneRequestAnnotation(pvc)
Expect(exists).To(BeFalse())
Expect(ns).To(BeEmpty())
Expect(name).To(BeEmpty())
})
It("should return false/empty/empty if annotation is invalid", func() {
pvc := createPvc("testPvc1", "default", map[string]string{AnnCloneRequest: "default"}, nil)
exists, ns, name := ParseCloneRequestAnnotation(pvc)
Expect(exists).To(BeFalse())
Expect(ns).To(BeEmpty())
Expect(name).To(BeEmpty())
pvc = createPvc("testPvc1", "default", map[string]string{AnnCloneRequest: "default/test/something"}, nil)
exists, ns, name = ParseCloneRequestAnnotation(pvc)
Expect(exists).To(BeFalse())
Expect(ns).To(BeEmpty())
Expect(name).To(BeEmpty())
})
It("should return true/default/test if annotation is valid", func() {
pvc := createPvc("testPvc1", "default", map[string]string{AnnCloneRequest: "default/test"}, nil)
exists, ns, name := ParseCloneRequestAnnotation(pvc)
Expect(exists).To(BeTrue())
Expect(ns).To(Equal("default"))
Expect(name).To(Equal("test"))
})
})
var _ = Describe("CloneSourcePodName", func() {
It("Should be unique and deterministic", func() {
pvc1d := createPvc("testPvc1", "default", map[string]string{AnnCloneRequest: "default/test"}, nil)
pvc1d2 := createPvc("testPvc1", "default2", map[string]string{AnnCloneRequest: "default/test"}, nil)
pvc2d1 := createPvc("testPvc2", "default", map[string]string{AnnCloneRequest: "default/test"}, nil)
pvcSimilar := createPvc("testP", "vc1default", map[string]string{AnnCloneRequest: "default/test"}, nil)
podName1d := getCloneSourcePodName(pvc1d)
podName1dagain := getCloneSourcePodName(pvc1d)
By("Verifying rerunning getloneSourcePodName on same PVC I get same name")
Expect(podName1d).To(Equal(podName1dagain))
By("Verifying different namespace but same name I get different pod name")
podName1d2 := getCloneSourcePodName(pvc1d2)
Expect(podName1d).NotTo(Equal(podName1d2))
By("Verifying same namespace but different name I get different pod name")
podName2d1 := getCloneSourcePodName(pvc2d1)
Expect(podName1d).NotTo(Equal(podName2d1))
By("Verifying concatenated ns/name of same characters I get different pod name")
podNameSimilar := getCloneSourcePodName(pvcSimilar)
Expect(podName1d).NotTo(Equal(podNameSimilar))
})
})
func createCloneReconciler(objects ...runtime.Object) *CloneReconciler {
objs := []runtime.Object{}
objs = append(objs, objects...)
cdiConfig := MakeEmptyCDIConfigSpec(common.ConfigName)
cdiConfig.Status = cdiv1.CDIConfigStatus{
DefaultPodResourceRequirements: createDefaultPodResourceRequirements(int64(0), int64(0), int64(0), int64(0)),
}
objs = append(objs, cdiConfig)
// Register operator types with the runtime scheme.
s := scheme.Scheme
cdiv1.AddToScheme(s)
rec := record.NewFakeRecorder(1)
// Create a fake client to mock API calls.
cl := fake.NewFakeClientWithScheme(s, objs...)
k8sfakeclientset := k8sfake.NewSimpleClientset(createStorageClass(testStorageClass, nil))
// Create a ReconcileMemcached object with the scheme and fake client.
return &CloneReconciler{
Client: cl,
Scheme: s,
Log: log,
recorder: rec,
tokenValidator: &FakeValidator{
Params: make(map[string]string, 0),
},
K8sClient: k8sfakeclientset,
Image: testImage,
clientCertGenerator: &fakeCertGenerator{},
serverCAFetcher: &fetcher.MemCertBundleFetcher{Bundle: []byte("baz")},
}
}
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 (v *FakeValidator) Validate(value string) (*token.Payload, error) {
if value != v.match {
return nil, fmt.Errorf("Token does not match expected")
}
resource := metav1.GroupVersionResource{
Resource: "persistentvolumeclaims",
}
return &token.Payload{
Name: v.Name,
Namespace: v.Namespace,
Operation: token.OperationClone,
Resource: resource,
Params: v.Params,
}, nil
}
func createClonePvc(sourceNamespace, sourceName, targetNamespace, targetName string, annotations, labels map[string]string) *corev1.PersistentVolumeClaim {
return createClonePvcWithSize(sourceNamespace, sourceName, targetNamespace, targetName, annotations, labels, "1G")
}
func createClonePvcWithSize(sourceNamespace, sourceName, targetNamespace, targetName string, annotations, labels map[string]string, size string) *corev1.PersistentVolumeClaim {
tokenData := &token.Payload{
Operation: token.OperationClone,
Name: sourceName,
Namespace: sourceNamespace,
Resource: metav1.GroupVersionResource{
Group: "",
Version: "v1",
Resource: "persistentvolumeclaims",
},
Params: map[string]string{
"targetNamespace": targetNamespace,
"targetName": targetName,
},
}
g := token.NewGenerator(common.CloneTokenIssuer, getAPIServerKey(), 5*time.Minute)
tokenString, err := g.Generate(tokenData)
if err != nil {
panic("error generating token")
}
if annotations == nil {
annotations = make(map[string]string)
}
annotations[AnnCloneRequest] = fmt.Sprintf("%s/%s", sourceNamespace, sourceName)
annotations[AnnCloneToken] = tokenString
annotations[AnnUploadClientName] = "FOOBAR"
return &corev1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: targetName,
Namespace: targetNamespace,
Annotations: annotations,
Labels: labels,
UID: "pvc-uid",
},
Spec: corev1.PersistentVolumeClaimSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadOnlyMany, corev1.ReadWriteOnce},
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceName(corev1.ResourceStorage): resource.MustParse(size),
},
},
},
}
}
func createCloneBlockPvc(sourceNamespace, sourceName, targetNamespace, targetName string, annotations, labels map[string]string) *corev1.PersistentVolumeClaim {
pvc := createClonePvc(sourceNamespace, sourceName, targetNamespace, targetName, annotations, labels)
VolumeMode := corev1.PersistentVolumeBlock
pvc.Spec.VolumeMode = &VolumeMode
return pvc
}
func createSourcePod(pvc *corev1.PersistentVolumeClaim, pvcUID string) *corev1.Pod {
_, _, sourcePvcName := ParseCloneRequestAnnotation(pvc)
podName := fmt.Sprintf("%s-%s-", common.ClonerSourcePodName, sourcePvcName)
pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
GenerateName: podName,
Annotations: map[string]string{
AnnCreatedBy: "yes",
AnnOwnerRef: fmt.Sprintf("%s/%s", pvc.Namespace, pvc.Name),
},
Labels: map[string]string{
common.CDILabelKey: common.CDILabelValue, //filtered by the podInformer
common.CDIComponentLabel: common.ClonerSourcePodName,
// this label is used when searching for a pvc's cloner source pod.
CloneUniqueID: pvcUID + "-source-pod",
common.PrometheusLabel: "",
},
},
Spec: corev1.PodSpec{
SecurityContext: &corev1.PodSecurityContext{
RunAsUser: &[]int64{0}[0],
},
Containers: []corev1.Container{
{
Name: common.ClonerSourcePodName,
Image: "test/mycloneimage",
ImagePullPolicy: corev1.PullAlways,
Env: []corev1.EnvVar{
{
Name: "CLIENT_KEY",
Value: "bar",
},
{
Name: "CLIENT_CERT",
Value: "foo",
},
{
Name: "SERVER_CA_CERT",
Value: string("baz"),
},
{
Name: "UPLOAD_URL",
Value: GetUploadServerURL(pvc.Namespace, pvc.Name, common.UploadPathSync),
},
{
Name: common.OwnerUID,
Value: "",
},
},
Ports: []corev1.ContainerPort{
{
Name: "metrics",
ContainerPort: 8443,
Protocol: corev1.ProtocolTCP,
},
},
},
},
RestartPolicy: corev1.RestartPolicyOnFailure,
Volumes: []corev1.Volume{
{
Name: DataVolName,
VolumeSource: corev1.VolumeSource{
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
ClaimName: sourcePvcName,
ReadOnly: false,
},
},
},
},
},
}
var volumeMode corev1.PersistentVolumeMode
var addVars []corev1.EnvVar
if pvc.Spec.VolumeMode != nil {
volumeMode = *pvc.Spec.VolumeMode
} else {
volumeMode = corev1.PersistentVolumeFilesystem
}
if volumeMode == corev1.PersistentVolumeBlock {
pod.Spec.Containers[0].VolumeDevices = addVolumeDevices()
addVars = []corev1.EnvVar{
{
Name: "VOLUME_MODE",
Value: "block",
},
{
Name: "MOUNT_POINT",
Value: common.WriteBlockPath,
},
}
pod.Spec.SecurityContext = &corev1.PodSecurityContext{
RunAsUser: &[]int64{0}[0],
}
} else {
pod.Spec.Containers[0].VolumeMounts = []corev1.VolumeMount{
{
Name: DataVolName,
MountPath: common.ClonerMountPath,
},
}
addVars = []corev1.EnvVar{
{
Name: "VOLUME_MODE",
Value: "filesystem",
},
{
Name: "MOUNT_POINT",
Value: common.ClonerMountPath,
},
}
}
pod.Spec.Containers[0].Env = append(pod.Spec.Containers[0].Env, addVars...)
return pod
}