containerized-data-importer/tests/utils/pvc.go
tavni e9b6b4f501 Adding support to upload disk image to a Raw Block PV
Signed-off-by: tavni <tavni@redhat.com>
2019-04-17 10:27:24 +03:00

214 lines
8.0 KiB
Go

package utils
import (
"time"
"fmt"
"github.com/onsi/ginkgo"
corev1 "k8s.io/api/core/v1"
k8sv1 "k8s.io/api/core/v1"
apierrs "k8s.io/apimachinery/pkg/api/errors"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
)
const (
defaultPollInterval = 2 * time.Second
defaultPollPeriod = 90 * time.Second
// DefaultPvcMountPath is the default mount path used
DefaultPvcMountPath = "/pvc"
// DefaultImagePath is the default destination for images created by CDI
DefaultImagePath = DefaultPvcMountPath + "/disk.img"
pvcPollInterval = defaultPollInterval
pvcCreateTime = defaultPollPeriod
pvcDeleteTime = defaultPollPeriod
pvcPhaseTime = defaultPollPeriod
)
// CreatePVCFromDefinition creates a PVC in the passed in namespace from the passed in PersistentVolumeClaim definition.
// An example of creating a PVC without annotations looks like this:
// CreatePVCFromDefinition(client, namespace, NewPVCDefinition(name, size, nil, nil))
func CreatePVCFromDefinition(clientSet *kubernetes.Clientset, namespace string, def *k8sv1.PersistentVolumeClaim) (*k8sv1.PersistentVolumeClaim, error) {
var pvc *k8sv1.PersistentVolumeClaim
err := wait.PollImmediate(pvcPollInterval, pvcCreateTime, func() (bool, error) {
var err error
pvc, err = clientSet.CoreV1().PersistentVolumeClaims(namespace).Create(def)
if err == nil || apierrs.IsAlreadyExists(err) {
return true, nil
}
return false, err
})
if err != nil {
return nil, err
}
return pvc, nil
}
// DeletePVC deletes the passed in PVC
func DeletePVC(clientSet *kubernetes.Clientset, namespace string, pvc *k8sv1.PersistentVolumeClaim) error {
return wait.PollImmediate(pvcPollInterval, pvcDeleteTime, func() (bool, error) {
err := clientSet.CoreV1().PersistentVolumeClaims(namespace).Delete(pvc.GetName(), nil)
if err == nil || apierrs.IsNotFound(err) {
return true, nil
}
return false, err
})
}
// FindPVC Finds the passed in PVC
func FindPVC(clientSet *kubernetes.Clientset, namespace, pvcName string) (*k8sv1.PersistentVolumeClaim, error) {
return clientSet.CoreV1().PersistentVolumeClaims(namespace).Get(pvcName, metav1.GetOptions{})
}
// WaitForPVCAnnotation waits for an anotation to appear on a PVC
func WaitForPVCAnnotation(clientSet *kubernetes.Clientset, namespace string, pvc *k8sv1.PersistentVolumeClaim, annotation string) (string, bool, error) {
var result string
var called bool
err := pollPVCAnnotation(clientSet, namespace, pvc, annotation, func(value string) bool {
called = true
result = value
return true
})
return result, called, err
}
// WaitForPVCAnnotationWithValue waits for an annotation with a specific value on a PVC
func WaitForPVCAnnotationWithValue(clientSet *kubernetes.Clientset, namespace string, pvc *k8sv1.PersistentVolumeClaim, annotation, expected string) (bool, error) {
var result bool
err := pollPVCAnnotation(clientSet, namespace, pvc, annotation, func(value string) bool {
result = (expected == value)
return result
})
return result, err
}
// WaitPVCPodStatusRunning waits for the pod status annotation to be Running
func WaitPVCPodStatusRunning(clientSet *kubernetes.Clientset, pvc *k8sv1.PersistentVolumeClaim) (bool, error) {
return WaitForPVCAnnotationWithValue(clientSet, pvc.Namespace, pvc, uploadStatusAnnotation, string(k8sv1.PodRunning))
}
// WaitPVCPodStatusSucceeded waits for the pod status annotation to be Succeeded
func WaitPVCPodStatusSucceeded(clientSet *kubernetes.Clientset, pvc *k8sv1.PersistentVolumeClaim) (bool, error) {
return WaitForPVCAnnotationWithValue(clientSet, pvc.Namespace, pvc, uploadStatusAnnotation, string(k8sv1.PodSucceeded))
}
// WaitPVCPodStatusFailed waits for the pod status annotation to be Failed
func WaitPVCPodStatusFailed(clientSet *kubernetes.Clientset, pvc *k8sv1.PersistentVolumeClaim) (bool, error) {
return WaitForPVCAnnotationWithValue(clientSet, pvc.Namespace, pvc, uploadStatusAnnotation, string(k8sv1.PodFailed))
}
type pollPVCAnnotationFunc = func(string) bool
func pollPVCAnnotation(clientSet *kubernetes.Clientset, namespace string, pvc *k8sv1.PersistentVolumeClaim, annotation string, f pollPVCAnnotationFunc) error {
err := wait.PollImmediate(pvcPollInterval, pvcCreateTime, func() (bool, error) {
pvc, err := FindPVC(clientSet, namespace, pvc.Name)
if err != nil {
return false, err
}
fmt.Fprintf(ginkgo.GinkgoWriter, "INFO: PVC annotations: %v\n", pvc.ObjectMeta.Annotations)
value, ok := pvc.ObjectMeta.Annotations[annotation]
if ok {
return f(value), nil
}
return false, err
})
return err
}
// NewPVCDefinitionWithSelector creates a PVC definition.
func NewPVCDefinitionWithSelector(pvcName, size string, selector map[string]string, annotations, labels map[string]string,
storageClassName string) *k8sv1.PersistentVolumeClaim {
return &k8sv1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: pvcName,
Annotations: annotations,
Labels: labels,
},
Spec: k8sv1.PersistentVolumeClaimSpec{
AccessModes: []k8sv1.PersistentVolumeAccessMode{k8sv1.ReadWriteOnce},
Resources: k8sv1.ResourceRequirements{
Requests: k8sv1.ResourceList{
k8sv1.ResourceName(k8sv1.ResourceStorage): resource.MustParse(size),
},
},
StorageClassName: &storageClassName,
Selector: &metav1.LabelSelector{
MatchLabels: selector,
},
},
}
}
// NewPVCDefinition creates a PVC definition using the passed in name and requested size.
// You can use the following annotation keys to request an import or clone. The values are defined in the controller package
// AnnEndpoint
// AnnSecret
// AnnCloneRequest
// You can also pass in any label you want.
func NewPVCDefinition(pvcName string, size string, annotations, labels map[string]string) *k8sv1.PersistentVolumeClaim {
return &k8sv1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: pvcName,
Annotations: annotations,
Labels: labels,
},
Spec: k8sv1.PersistentVolumeClaimSpec{
AccessModes: []k8sv1.PersistentVolumeAccessMode{k8sv1.ReadWriteOnce},
Resources: k8sv1.ResourceRequirements{
Requests: k8sv1.ResourceList{
k8sv1.ResourceName(k8sv1.ResourceStorage): resource.MustParse(size),
},
},
},
}
}
// NewBlockPVCDefinition creates a PVC definition with volumeMode 'Block'
func NewBlockPVCDefinition(pvcName string, size string, annotations, labels map[string]string, storageClassName string) *k8sv1.PersistentVolumeClaim {
pvcDef := NewPVCDefinition(pvcName, size, annotations, labels)
pvcDef.Spec.StorageClassName = &storageClassName
volumeMode := corev1.PersistentVolumeBlock
pvcDef.Spec.VolumeMode = &volumeMode
return pvcDef
}
// WaitForPersistentVolumeClaimPhase waits for the PVC to be in a particular phase (Pending, Bound, or Lost)
func WaitForPersistentVolumeClaimPhase(clientSet *kubernetes.Clientset, namespace string, phase k8sv1.PersistentVolumeClaimPhase, pvcName string) error {
err := wait.PollImmediate(pvcPollInterval, pvcPhaseTime, func() (bool, error) {
pvc, err := clientSet.CoreV1().PersistentVolumeClaims(namespace).Get(pvcName, metav1.GetOptions{})
fmt.Fprintf(ginkgo.GinkgoWriter, "INFO: Checking PVC phase: %s\n", string(pvc.Status.Phase))
if err != nil || pvc.Status.Phase != phase {
return false, err
}
return true, nil
})
if err != nil {
return fmt.Errorf("PersistentVolumeClaim %s not in phase %s within %v", pvcName, phase, pvcPhaseTime)
}
return nil
}
// WaitPVCDeleted polls the specified PVC until timeout or it's not found, returns true if deleted in the specified timeout period, and any errors
func WaitPVCDeleted(clientSet *kubernetes.Clientset, pvcName, namespace string, timeout time.Duration) (bool, error) {
var result bool
err := wait.PollImmediate(2*time.Second, timeout, func() (bool, error) {
_, err := clientSet.CoreV1().PersistentVolumeClaims(namespace).Get(pvcName, metav1.GetOptions{})
if err != nil {
if k8serrors.IsNotFound(err) {
result = true
return true, nil
}
return false, err
}
return false, nil
})
return result, err
}