PV & PVC Client implementation
This commit is contained in:
parent
c048e6fcbf
commit
95bd170ca2
@ -65,6 +65,11 @@ func validateObject(obj runtime.Object) (errors []error) {
|
|||||||
for i := range t.Items {
|
for i := range t.Items {
|
||||||
errors = append(errors, validateObject(&t.Items[i])...)
|
errors = append(errors, validateObject(&t.Items[i])...)
|
||||||
}
|
}
|
||||||
|
case *api.PersistentVolume:
|
||||||
|
errors = validation.ValidatePersistentVolume(t)
|
||||||
|
case *api.PersistentVolumeClaim:
|
||||||
|
api.ValidNamespace(ctx, &t.ObjectMeta)
|
||||||
|
errors = validation.ValidatePersistentVolumeClaim(t)
|
||||||
default:
|
default:
|
||||||
return []error{fmt.Errorf("no validation defined for %#v", obj)}
|
return []error{fmt.Errorf("no validation defined for %#v", obj)}
|
||||||
}
|
}
|
||||||
@ -160,6 +165,16 @@ func TestExampleObjectSchemas(t *testing.T) {
|
|||||||
"kitten-rc": &api.ReplicationController{},
|
"kitten-rc": &api.ReplicationController{},
|
||||||
"nautilus-rc": &api.ReplicationController{},
|
"nautilus-rc": &api.ReplicationController{},
|
||||||
},
|
},
|
||||||
|
"../examples/persistent-volumes/volumes": {
|
||||||
|
"local-01": &api.PersistentVolume{},
|
||||||
|
"local-02": &api.PersistentVolume{},
|
||||||
|
"gce": &api.PersistentVolume{},
|
||||||
|
},
|
||||||
|
"../examples/persistent-volumes/claims": {
|
||||||
|
"claim-01": &api.PersistentVolumeClaim{},
|
||||||
|
"claim-02": &api.PersistentVolumeClaim{},
|
||||||
|
"claim-03": &api.PersistentVolumeClaim{},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for path, expected := range cases {
|
for path, expected := range cases {
|
||||||
|
10
examples/persistent-volumes/claims/claim-01.yaml
Normal file
10
examples/persistent-volumes/claims/claim-01.yaml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
kind: PersistentVolumeClaim
|
||||||
|
apiVersion: v1beta3
|
||||||
|
metadata:
|
||||||
|
name: myclaim-1
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 3
|
10
examples/persistent-volumes/claims/claim-02.yaml
Normal file
10
examples/persistent-volumes/claims/claim-02.yaml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
kind: PersistentVolumeClaim
|
||||||
|
apiVersion: v1beta3
|
||||||
|
metadata:
|
||||||
|
name: myclaim-2
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 8
|
17
examples/persistent-volumes/claims/claim-03.json
Normal file
17
examples/persistent-volumes/claims/claim-03.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"kind": "PersistentVolumeClaim",
|
||||||
|
"apiVersion": "v1beta3",
|
||||||
|
"metadata": {
|
||||||
|
"name": "myclaim-3"
|
||||||
|
}, "spec": {
|
||||||
|
"accessModes": [
|
||||||
|
"ReadWriteOnce",
|
||||||
|
"ReadOnlyMany"
|
||||||
|
],
|
||||||
|
"resources": {
|
||||||
|
"requests": {
|
||||||
|
"storage": "10G"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
examples/persistent-volumes/simpletest/README.md
Normal file
69
examples/persistent-volumes/simpletest/README.md
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# How To Use Persistent Volumes
|
||||||
|
|
||||||
|
This guide assumes knowledge of Kubernetes fundamentals and that a user has a cluster up and running.
|
||||||
|
|
||||||
|
## Create volumes
|
||||||
|
|
||||||
|
Persistent Volumes are intended for "network volumes", such as GCE Persistent Disks, NFS shares, and AWS EBS volumes.
|
||||||
|
|
||||||
|
The `HostPath` VolumeSource was included in the Persistent Volumes implementation for ease of testing.
|
||||||
|
|
||||||
|
Create persistent volumes by posting them to the API server:
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
cluster/kubectl.sh create -f examples/persistent-volumes/volumes/local-01.yaml
|
||||||
|
cluster/kubectl.sh create -f examples/persistent-volumes/volumes/local-02.yaml
|
||||||
|
|
||||||
|
cluster/kubectl.sh get pv
|
||||||
|
|
||||||
|
NAME LABELS CAPACITY ACCESSMODES STATUS CLAIM
|
||||||
|
pv0001 map[] 10737418240 RWO
|
||||||
|
pv0002 map[] 5368709120 RWO
|
||||||
|
|
||||||
|
|
||||||
|
In the log:
|
||||||
|
|
||||||
|
I0302 10:20:45.663225 1920 persistent_volume_manager.go:115] Managing PersistentVolume[UID=b16e91d6-c0ef-11e4-8be4-80e6500a981e]
|
||||||
|
I0302 10:20:55.667945 1920 persistent_volume_manager.go:115] Managing PersistentVolume[UID=b41f4f0e-c0ef-11e4-8be4-80e6500a981e]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create claims
|
||||||
|
|
||||||
|
You must be in a namespace to create claims.
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
cluster/kubectl.sh create -f examples/persistent-volumes/claims/claim-01.yaml
|
||||||
|
cluster/kubectl.sh create -f examples/persistent-volumes/claims/claim-02.yaml
|
||||||
|
|
||||||
|
NAME LABELS STATUS VOLUME
|
||||||
|
myclaim-1 map[]
|
||||||
|
myclaim-2 map[]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Matching and binding
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
PersistentVolumeClaim[UID=f4b3d283-c0ef-11e4-8be4-80e6500a981e] bound to PersistentVolume[UID=b16e91d6-c0ef-11e4-8be4-80e6500a981e]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cluster/kubectl.sh get pv
|
||||||
|
|
||||||
|
NAME LABELS CAPACITY ACCESSMODES STATUS CLAIM
|
||||||
|
pv0001 map[] 10737418240 RWO myclaim-1 / f4b3d283-c0ef-11e4-8be4-80e6500a981e
|
||||||
|
pv0002 map[] 5368709120 RWO myclaim-2 / f70da891-c0ef-11e4-8be4-80e6500a981e
|
||||||
|
|
||||||
|
|
||||||
|
cluster/kubectl.sh get pvc
|
||||||
|
|
||||||
|
NAME LABELS STATUS VOLUME
|
||||||
|
myclaim-1 map[] b16e91d6-c0ef-11e4-8be4-80e6500a981e
|
||||||
|
myclaim-2 map[] b41f4f0e-c0ef-11e4-8be4-80e6500a981e
|
||||||
|
|
||||||
|
```
|
10
examples/persistent-volumes/simpletest/namespace.json
Normal file
10
examples/persistent-volumes/simpletest/namespace.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"kind": "Namespace",
|
||||||
|
"apiVersion":"v1beta3",
|
||||||
|
"metadata": {
|
||||||
|
"name": "myns",
|
||||||
|
"labels": {
|
||||||
|
"name": "development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
examples/persistent-volumes/simpletest/pod.yaml
Normal file
18
examples/persistent-volumes/simpletest/pod.yaml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
kind: Pod
|
||||||
|
apiVersion: v1beta3
|
||||||
|
metadata:
|
||||||
|
name: mypod
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: dockerfile/nginx
|
||||||
|
name: myfrontend
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: "/var/www/html"
|
||||||
|
name: mypd
|
||||||
|
volumes:
|
||||||
|
- name: mypd
|
||||||
|
source:
|
||||||
|
persistentVolumeClaim:
|
||||||
|
accessMode: ReadWriteOnce
|
||||||
|
claimRef:
|
||||||
|
name: myclaim-1
|
10
examples/persistent-volumes/volumes/gce.yaml
Normal file
10
examples/persistent-volumes/volumes/gce.yaml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
kind: PersistentVolume
|
||||||
|
apiVersion: v1beta3
|
||||||
|
metadata:
|
||||||
|
name: pv0003
|
||||||
|
spec:
|
||||||
|
capacity:
|
||||||
|
storage: 10
|
||||||
|
gcePersistentDisk:
|
||||||
|
pdName: "abc123"
|
||||||
|
fsType: "ext4"
|
11
examples/persistent-volumes/volumes/local-01.yaml
Normal file
11
examples/persistent-volumes/volumes/local-01.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
kind: PersistentVolume
|
||||||
|
apiVersion: v1beta3
|
||||||
|
metadata:
|
||||||
|
name: pv0001
|
||||||
|
labels:
|
||||||
|
type: local
|
||||||
|
spec:
|
||||||
|
capacity:
|
||||||
|
storage: 10Gi
|
||||||
|
hostPath:
|
||||||
|
path: "/tmp/data01"
|
11
examples/persistent-volumes/volumes/local-02.yaml
Normal file
11
examples/persistent-volumes/volumes/local-02.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
kind: PersistentVolume
|
||||||
|
apiVersion: v1beta3
|
||||||
|
metadata:
|
||||||
|
name: pv0002
|
||||||
|
labels:
|
||||||
|
type: local
|
||||||
|
spec:
|
||||||
|
capacity:
|
||||||
|
storage: 5Gi
|
||||||
|
hostPath:
|
||||||
|
path: "/tmp/data02"
|
@ -529,6 +529,49 @@ __EOF__
|
|||||||
# Post-condition: no replication controller is running
|
# Post-condition: no replication controller is running
|
||||||
kube::test::get_object_assert rc "{{range.items}}{{$id_field}}:{{end}}" ''
|
kube::test::get_object_assert rc "{{range.items}}{{$id_field}}:{{end}}" ''
|
||||||
|
|
||||||
|
######################
|
||||||
|
# Persistent Volumes #
|
||||||
|
######################
|
||||||
|
|
||||||
|
### Create and delete persistent volume examples
|
||||||
|
# Pre-condition: no persistent volumes currently exist
|
||||||
|
kube::test::get_object_assert pv "{{range.items}}{{.$id_field}}:{{end}}" ''
|
||||||
|
# Command
|
||||||
|
kubectl create -f examples/persistent-volumes/volumes/local-01.yaml "${kube_flags[@]}"
|
||||||
|
kube::test::get_object_assert pv "{{range.items}}{{.$id_field}}:{{end}}" 'pv0001:'
|
||||||
|
kubectl delete pv pv0001 "${kube_flags[@]}"
|
||||||
|
kubectl create -f examples/persistent-volumes/volumes/local-02.yaml "${kube_flags[@]}"
|
||||||
|
kube::test::get_object_assert pv "{{range.items}}{{.$id_field}}:{{end}}" 'pv0002:'
|
||||||
|
kubectl delete pv pv0002 "${kube_flags[@]}"
|
||||||
|
kubectl create -f examples/persistent-volumes/volumes/gce.yaml "${kube_flags[@]}"
|
||||||
|
kube::test::get_object_assert pv "{{range.items}}{{.$id_field}}:{{end}}" 'pv0003:'
|
||||||
|
kubectl delete pv pv0003 "${kube_flags[@]}"
|
||||||
|
# Post-condition: no PVs
|
||||||
|
kube::test::get_object_assert pv "{{range.items}}{{.$id_field}}:{{end}}" ''
|
||||||
|
|
||||||
|
############################
|
||||||
|
# Persistent Volume Claims #
|
||||||
|
############################
|
||||||
|
|
||||||
|
### Create and delete persistent volume claim examples
|
||||||
|
# Pre-condition: no persistent volume claims currently exist
|
||||||
|
kube::test::get_object_assert pvc "{{range.items}}{{.$id_field}}:{{end}}" ''
|
||||||
|
# Command
|
||||||
|
kubectl create -f examples/persistent-volumes/claims/claim-01.yaml "${kube_flags[@]}"
|
||||||
|
kube::test::get_object_assert pvc "{{range.items}}{{.$id_field}}:{{end}}" 'myclaim-1:'
|
||||||
|
kubectl delete pvc myclaim-1 "${kube_flags[@]}"
|
||||||
|
|
||||||
|
kubectl create -f examples/persistent-volumes/claims/claim-02.yaml "${kube_flags[@]}"
|
||||||
|
kube::test::get_object_assert pvc "{{range.items}}{{.$id_field}}:{{end}}" 'myclaim-2:'
|
||||||
|
kubectl delete pvc myclaim-2 "${kube_flags[@]}"
|
||||||
|
|
||||||
|
kubectl create -f examples/persistent-volumes/claims/claim-03.json "${kube_flags[@]}"
|
||||||
|
kube::test::get_object_assert pvc "{{range.items}}{{.$id_field}}:{{end}}" 'myclaim-3:'
|
||||||
|
kubectl delete pvc myclaim-3 "${kube_flags[@]}"
|
||||||
|
# Post-condition: no PVCs
|
||||||
|
kube::test::get_object_assert pvc "{{range.items}}{{.$id_field}}:{{end}}" ''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#########
|
#########
|
||||||
# Nodes #
|
# Nodes #
|
||||||
|
@ -224,8 +224,9 @@ type PersistentVolumeSpec struct {
|
|||||||
// Resources represents the actual resources of the volume
|
// Resources represents the actual resources of the volume
|
||||||
Capacity ResourceList `json:"capacity`
|
Capacity ResourceList `json:"capacity`
|
||||||
// Source represents the location and type of a volume to mount.
|
// Source represents the location and type of a volume to mount.
|
||||||
// AccessModeTypes are inferred from the Source.
|
|
||||||
PersistentVolumeSource `json:",inline"`
|
PersistentVolumeSource `json:",inline"`
|
||||||
|
// AccessModes contains all ways the volume can be mounted
|
||||||
|
AccessModes []AccessModeType `json:"accessModes,omitempty"`
|
||||||
// holds the binding reference to a PersistentVolumeClaim
|
// holds the binding reference to a PersistentVolumeClaim
|
||||||
ClaimRef *ObjectReference `json:"claimRef,omitempty"`
|
ClaimRef *ObjectReference `json:"claimRef,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -139,8 +139,9 @@ type PersistentVolumeSpec struct {
|
|||||||
// Resources represents the actual resources of the volume
|
// Resources represents the actual resources of the volume
|
||||||
Capacity ResourceList `json:"capacity,omitempty" description:"a description of the persistent volume's resources and capacity"`
|
Capacity ResourceList `json:"capacity,omitempty" description:"a description of the persistent volume's resources and capacity"`
|
||||||
// Source represents the location and type of a volume to mount.
|
// Source represents the location and type of a volume to mount.
|
||||||
// AccessModeTypes are inferred from the Source.
|
|
||||||
PersistentVolumeSource `json:",inline" description:"the actual volume backing the persistent volume"`
|
PersistentVolumeSource `json:",inline" description:"the actual volume backing the persistent volume"`
|
||||||
|
// AccessModes contains all ways the volume can be mounted
|
||||||
|
AccessModes []AccessModeType `json:"accessModes,omitempty" description:"all ways the volume can be mounted"`
|
||||||
// holds the binding reference to a PersistentVolumeClaim
|
// holds the binding reference to a PersistentVolumeClaim
|
||||||
ClaimRef *ObjectReference `json:"claimRef,omitempty" description:"the binding reference to a persistent volume claim"`
|
ClaimRef *ObjectReference `json:"claimRef,omitempty" description:"the binding reference to a persistent volume claim"`
|
||||||
}
|
}
|
||||||
|
@ -108,8 +108,9 @@ type PersistentVolumeSpec struct {
|
|||||||
// Resources represents the actual resources of the volume
|
// Resources represents the actual resources of the volume
|
||||||
Capacity ResourceList `json:"capacity,omitempty" description:"a description of the persistent volume's resources and capacity"`
|
Capacity ResourceList `json:"capacity,omitempty" description:"a description of the persistent volume's resources and capacity"`
|
||||||
// Source represents the location and type of a volume to mount.
|
// Source represents the location and type of a volume to mount.
|
||||||
// AccessModeTypes are inferred from the Source.
|
|
||||||
PersistentVolumeSource `json:",inline" description:"the actual volume backing the persistent volume"`
|
PersistentVolumeSource `json:",inline" description:"the actual volume backing the persistent volume"`
|
||||||
|
// AccessModes contains all ways the volume can be mounted
|
||||||
|
AccessModes []AccessModeType `json:"accessModes,omitempty" description:"all ways the volume can be mounted"`
|
||||||
// holds the binding reference to a PersistentVolumeClaim
|
// holds the binding reference to a PersistentVolumeClaim
|
||||||
ClaimRef *ObjectReference `json:"claimRef,omitempty" description:"the binding reference to a persistent volume claim"`
|
ClaimRef *ObjectReference `json:"claimRef,omitempty" description:"the binding reference to a persistent volume claim"`
|
||||||
}
|
}
|
||||||
|
@ -241,8 +241,9 @@ type PersistentVolumeSpec struct {
|
|||||||
// Resources represents the actual resources of the volume
|
// Resources represents the actual resources of the volume
|
||||||
Capacity ResourceList `json:"capacity,omitempty" description:"a description of the persistent volume's resources and capacity"`
|
Capacity ResourceList `json:"capacity,omitempty" description:"a description of the persistent volume's resources and capacity"`
|
||||||
// Source represents the location and type of a volume to mount.
|
// Source represents the location and type of a volume to mount.
|
||||||
// AccessModeTypes are inferred from the Source.
|
|
||||||
PersistentVolumeSource `json:",inline" description:"the actual volume backing the persistent volume"`
|
PersistentVolumeSource `json:",inline" description:"the actual volume backing the persistent volume"`
|
||||||
|
// AccessModes contains all ways the volume can be mounted
|
||||||
|
AccessModes []AccessModeType `json:"accessModes,omitempty" description:"all ways the volume can be mounted"`
|
||||||
// holds the binding reference to a PersistentVolumeClaim
|
// holds the binding reference to a PersistentVolumeClaim
|
||||||
ClaimRef *ObjectReference `json:"claimRef,omitempty" description:"the binding reference to a persistent volume claim"`
|
ClaimRef *ObjectReference `json:"claimRef,omitempty" description:"the binding reference to a persistent volume claim"`
|
||||||
}
|
}
|
||||||
|
@ -364,11 +364,12 @@ func validateNFS(nfs *api.NFSVolumeSource) errs.ValidationErrorList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ValidatePersistentVolumeName(name string, prefix bool) (bool, string) {
|
func ValidatePersistentVolumeName(name string, prefix bool) (bool, string) {
|
||||||
return util.IsDNS1123Label(name), name
|
return nameIsDNSSubdomain(name, prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ValidatePersistentVolume(pv *api.PersistentVolume) errs.ValidationErrorList {
|
func ValidatePersistentVolume(pv *api.PersistentVolume) errs.ValidationErrorList {
|
||||||
allErrs := ValidateObjectMeta(&pv.ObjectMeta, false, ValidatePersistentVolumeName)
|
allErrs := errs.ValidationErrorList{}
|
||||||
|
allErrs = append(allErrs, ValidateObjectMeta(&pv.ObjectMeta, false, ValidatePersistentVolumeName).Prefix("metadata")...)
|
||||||
|
|
||||||
if len(pv.Spec.Capacity) == 0 {
|
if len(pv.Spec.Capacity) == 0 {
|
||||||
allErrs = append(allErrs, errs.NewFieldRequired("persistentVolume.Capacity"))
|
allErrs = append(allErrs, errs.NewFieldRequired("persistentVolume.Capacity"))
|
||||||
@ -393,6 +394,27 @@ func ValidatePersistentVolume(pv *api.PersistentVolume) errs.ValidationErrorList
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidatePersistentVolumeUpdate tests to see if the update is legal for an end user to make.
|
||||||
|
// newPv is updated with fields that cannot be changed.
|
||||||
|
func ValidatePersistentVolumeUpdate(newPv, oldPv *api.PersistentVolume) errs.ValidationErrorList {
|
||||||
|
allErrs := errs.ValidationErrorList{}
|
||||||
|
allErrs = ValidatePersistentVolume(newPv)
|
||||||
|
newPv.Status = oldPv.Status
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidatePersistentVolumeStatusUpdate tests to see if the status update is legal for an end user to make.
|
||||||
|
// newPv is updated with fields that cannot be changed.
|
||||||
|
func ValidatePersistentVolumeStatusUpdate(newPv, oldPv *api.PersistentVolume) errs.ValidationErrorList {
|
||||||
|
allErrs := errs.ValidationErrorList{}
|
||||||
|
allErrs = append(allErrs, ValidateObjectMetaUpdate(&oldPv.ObjectMeta, &newPv.ObjectMeta).Prefix("metadata")...)
|
||||||
|
if newPv.ResourceVersion == "" {
|
||||||
|
allErrs = append(allErrs, fmt.Errorf("ResourceVersion must be specified"))
|
||||||
|
}
|
||||||
|
newPv.Spec = oldPv.Spec
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
func ValidatePersistentVolumeClaim(pvc *api.PersistentVolumeClaim) errs.ValidationErrorList {
|
func ValidatePersistentVolumeClaim(pvc *api.PersistentVolumeClaim) errs.ValidationErrorList {
|
||||||
allErrs := ValidateObjectMeta(&pvc.ObjectMeta, true, ValidatePersistentVolumeName)
|
allErrs := ValidateObjectMeta(&pvc.ObjectMeta, true, ValidatePersistentVolumeName)
|
||||||
if len(pvc.Spec.AccessModes) == 0 {
|
if len(pvc.Spec.AccessModes) == 0 {
|
||||||
@ -404,6 +426,23 @@ func ValidatePersistentVolumeClaim(pvc *api.PersistentVolumeClaim) errs.Validati
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *api.PersistentVolumeClaim) errs.ValidationErrorList {
|
||||||
|
allErrs := errs.ValidationErrorList{}
|
||||||
|
allErrs = ValidatePersistentVolumeClaim(newPvc)
|
||||||
|
newPvc.Status = oldPvc.Status
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidatePersistentVolumeClaimStatusUpdate(newPvc, oldPvc *api.PersistentVolumeClaim) errs.ValidationErrorList {
|
||||||
|
allErrs := errs.ValidationErrorList{}
|
||||||
|
allErrs = append(allErrs, ValidateObjectMetaUpdate(&oldPvc.ObjectMeta, &newPvc.ObjectMeta).Prefix("metadata")...)
|
||||||
|
if newPvc.ResourceVersion == "" {
|
||||||
|
allErrs = append(allErrs, fmt.Errorf("ResourceVersion must be specified"))
|
||||||
|
}
|
||||||
|
newPvc.Spec = oldPvc.Spec
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
var supportedPortProtocols = util.NewStringSet(string(api.ProtocolTCP), string(api.ProtocolUDP))
|
var supportedPortProtocols = util.NewStringSet(string(api.ProtocolTCP), string(api.ProtocolUDP))
|
||||||
|
|
||||||
func validatePorts(ports []api.ContainerPort) errs.ValidationErrorList {
|
func validatePorts(ports []api.ContainerPort) errs.ValidationErrorList {
|
||||||
|
@ -41,6 +41,8 @@ type Interface interface {
|
|||||||
ResourceQuotasNamespacer
|
ResourceQuotasNamespacer
|
||||||
SecretsNamespacer
|
SecretsNamespacer
|
||||||
NamespacesInterface
|
NamespacesInterface
|
||||||
|
PersistentVolumesInterface
|
||||||
|
PersistentVolumeClaimsNamespacer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) ReplicationControllers(namespace string) ReplicationControllerInterface {
|
func (c *Client) ReplicationControllers(namespace string) ReplicationControllerInterface {
|
||||||
@ -66,7 +68,6 @@ func (c *Client) Pods(namespace string) PodInterface {
|
|||||||
func (c *Client) Services(namespace string) ServiceInterface {
|
func (c *Client) Services(namespace string) ServiceInterface {
|
||||||
return newServices(c, namespace)
|
return newServices(c, namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) LimitRanges(namespace string) LimitRangeInterface {
|
func (c *Client) LimitRanges(namespace string) LimitRangeInterface {
|
||||||
return newLimitRanges(c, namespace)
|
return newLimitRanges(c, namespace)
|
||||||
}
|
}
|
||||||
@ -83,6 +84,14 @@ func (c *Client) Namespaces() NamespaceInterface {
|
|||||||
return newNamespaces(c)
|
return newNamespaces(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) PersistentVolumes() PersistentVolumeInterface {
|
||||||
|
return newPersistentVolumes(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) PersistentVolumeClaims(namespace string) PersistentVolumeClaimInterface {
|
||||||
|
return newPersistentVolumeClaims(c, namespace)
|
||||||
|
}
|
||||||
|
|
||||||
// VersionInterface has a method to retrieve the server version.
|
// VersionInterface has a method to retrieve the server version.
|
||||||
type VersionInterface interface {
|
type VersionInterface interface {
|
||||||
ServerVersion() (*version.Info, error)
|
ServerVersion() (*version.Info, error)
|
||||||
|
@ -35,22 +35,26 @@ type FakeAction struct {
|
|||||||
// Fake implements Interface. Meant to be embedded into a struct to get a default
|
// Fake implements Interface. Meant to be embedded into a struct to get a default
|
||||||
// implementation. This makes faking out just the method you want to test easier.
|
// implementation. This makes faking out just the method you want to test easier.
|
||||||
type Fake struct {
|
type Fake struct {
|
||||||
Actions []FakeAction
|
Actions []FakeAction
|
||||||
PodsList api.PodList
|
PodsList api.PodList
|
||||||
CtrlList api.ReplicationControllerList
|
CtrlList api.ReplicationControllerList
|
||||||
Ctrl api.ReplicationController
|
Ctrl api.ReplicationController
|
||||||
ServiceList api.ServiceList
|
ServiceList api.ServiceList
|
||||||
EndpointsList api.EndpointsList
|
EndpointsList api.EndpointsList
|
||||||
MinionsList api.NodeList
|
MinionsList api.NodeList
|
||||||
EventsList api.EventList
|
EventsList api.EventList
|
||||||
LimitRangesList api.LimitRangeList
|
LimitRangesList api.LimitRangeList
|
||||||
ResourceQuotaStatus api.ResourceQuota
|
ResourceQuotaStatus api.ResourceQuota
|
||||||
ResourceQuotasList api.ResourceQuotaList
|
ResourceQuotasList api.ResourceQuotaList
|
||||||
NamespacesList api.NamespaceList
|
NamespacesList api.NamespaceList
|
||||||
SecretList api.SecretList
|
SecretList api.SecretList
|
||||||
Secret api.Secret
|
Secret api.Secret
|
||||||
Err error
|
Err error
|
||||||
Watch watch.Interface
|
Watch watch.Interface
|
||||||
|
PersistentVolume api.PersistentVolume
|
||||||
|
PersistentVolumesList api.PersistentVolumeList
|
||||||
|
PersistentVolumeClaim api.PersistentVolumeClaim
|
||||||
|
PersistentVolumeClaimList api.PersistentVolumeClaimList
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Fake) LimitRanges(namespace string) LimitRangeInterface {
|
func (c *Fake) LimitRanges(namespace string) LimitRangeInterface {
|
||||||
@ -81,6 +85,14 @@ func (c *Fake) Pods(namespace string) PodInterface {
|
|||||||
return &FakePods{Fake: c, Namespace: namespace}
|
return &FakePods{Fake: c, Namespace: namespace}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Fake) PersistentVolumes() PersistentVolumeInterface {
|
||||||
|
return &FakePersistentVolumes{Fake: c}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Fake) PersistentVolumeClaims(namespace string) PersistentVolumeClaimInterface {
|
||||||
|
return &FakePersistentVolumeClaims{Fake: c, Namespace: namespace}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Fake) Services(namespace string) ServiceInterface {
|
func (c *Fake) Services(namespace string) ServiceInterface {
|
||||||
return &FakeServices{Fake: c, Namespace: namespace}
|
return &FakeServices{Fake: c, Namespace: namespace}
|
||||||
}
|
}
|
||||||
|
58
pkg/client/fake_persistent_volume_claims.go
Normal file
58
pkg/client/fake_persistent_volume_claims.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FakePersistentVolumeClaims struct {
|
||||||
|
Fake *Fake
|
||||||
|
Namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakePersistentVolumeClaims) List(labels labels.Selector, field fields.Selector) (*api.PersistentVolumeClaimList, error) {
|
||||||
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "list-persistentVolumeClaims"})
|
||||||
|
return api.Scheme.CopyOrDie(&c.Fake.PersistentVolumeClaimList).(*api.PersistentVolumeClaimList), c.Fake.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakePersistentVolumeClaims) Get(name string) (*api.PersistentVolumeClaim, error) {
|
||||||
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "get-persistentVolumeClaim", Value: name})
|
||||||
|
return api.Scheme.CopyOrDie(&c.Fake.PersistentVolumeClaim).(*api.PersistentVolumeClaim), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakePersistentVolumeClaims) Delete(name string) error {
|
||||||
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "delete-persistentVolumeClaim", Value: name})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakePersistentVolumeClaims) Create(persistentvolumeclaim *api.PersistentVolumeClaim) (*api.PersistentVolumeClaim, error) {
|
||||||
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "create-persistentVolumeClaim"})
|
||||||
|
return &api.PersistentVolumeClaim{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakePersistentVolumeClaims) Update(persistentvolumeclaim *api.PersistentVolumeClaim) (*api.PersistentVolumeClaim, error) {
|
||||||
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "update-persistentVolumeClaim", Value: persistentvolumeclaim.Name})
|
||||||
|
return &api.PersistentVolumeClaim{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakePersistentVolumeClaims) Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) {
|
||||||
|
return c.Fake.Watch, c.Fake.Err
|
||||||
|
}
|
58
pkg/client/fake_persistent_volumes.go
Normal file
58
pkg/client/fake_persistent_volumes.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FakePersistentVolumes struct {
|
||||||
|
Fake *Fake
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakePersistentVolumes) List(labels labels.Selector, field fields.Selector) (*api.PersistentVolumeList, error) {
|
||||||
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "list-persistentVolumes"})
|
||||||
|
return api.Scheme.CopyOrDie(&c.Fake.PersistentVolumesList).(*api.PersistentVolumeList), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakePersistentVolumes) Get(name string) (*api.PersistentVolume, error) {
|
||||||
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "get-persistentVolume", Value: name})
|
||||||
|
return api.Scheme.CopyOrDie(&c.Fake.PersistentVolume).(*api.PersistentVolume), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakePersistentVolumes) Delete(name string) error {
|
||||||
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "delete-persistentVolume", Value: name})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakePersistentVolumes) Create(persistentvolume *api.PersistentVolume) (*api.PersistentVolume, error) {
|
||||||
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "create-persistentVolume"})
|
||||||
|
return &api.PersistentVolume{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakePersistentVolumes) Update(persistentvolume *api.PersistentVolume) (*api.PersistentVolume, error) {
|
||||||
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "update-persistentVolume", Value: persistentvolume.Name})
|
||||||
|
return &api.PersistentVolume{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakePersistentVolumes) Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) {
|
||||||
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "watch-persistentVolumes", Value: resourceVersion})
|
||||||
|
return c.Fake.Watch, c.Fake.Err
|
||||||
|
}
|
103
pkg/client/persistentvolumeclaim.go
Normal file
103
pkg/client/persistentvolumeclaim.go
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PersistentVolumeClaimsNamespacer has methods to work with PersistentVolumeClaim resources in a namespace
|
||||||
|
type PersistentVolumeClaimsNamespacer interface {
|
||||||
|
PersistentVolumeClaims(namespace string) PersistentVolumeClaimInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
// PersistentVolumeClaimInterface has methods to work with PersistentVolumeClaim resources.
|
||||||
|
type PersistentVolumeClaimInterface interface {
|
||||||
|
List(label labels.Selector, field fields.Selector) (*api.PersistentVolumeClaimList, error)
|
||||||
|
Get(name string) (*api.PersistentVolumeClaim, error)
|
||||||
|
Create(claim *api.PersistentVolumeClaim) (*api.PersistentVolumeClaim, error)
|
||||||
|
Update(claim *api.PersistentVolumeClaim) (*api.PersistentVolumeClaim, error)
|
||||||
|
Delete(name string) error
|
||||||
|
Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// persistentVolumeClaims implements PersistentVolumeClaimsNamespacer interface
|
||||||
|
type persistentVolumeClaims struct {
|
||||||
|
client *Client
|
||||||
|
namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
// newPersistentVolumeClaims returns a PodsClient
|
||||||
|
func newPersistentVolumeClaims(c *Client, namespace string) *persistentVolumeClaims {
|
||||||
|
return &persistentVolumeClaims{c, namespace}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *persistentVolumeClaims) List(label labels.Selector, field fields.Selector) (result *api.PersistentVolumeClaimList, err error) {
|
||||||
|
result = &api.PersistentVolumeClaimList{}
|
||||||
|
|
||||||
|
err = c.client.Get().
|
||||||
|
Namespace(c.namespace).
|
||||||
|
Resource("persistentVolumeClaims").
|
||||||
|
LabelsSelectorParam(api.LabelSelectorQueryParam(c.client.APIVersion()), label).
|
||||||
|
FieldsSelectorParam(api.FieldSelectorQueryParam(c.client.APIVersion()), field).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *persistentVolumeClaims) Get(name string) (result *api.PersistentVolumeClaim, err error) {
|
||||||
|
result = &api.PersistentVolumeClaim{}
|
||||||
|
err = c.client.Get().Namespace(c.namespace).Resource("persistentVolumeClaims").Name(name).Do().Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *persistentVolumeClaims) Create(claim *api.PersistentVolumeClaim) (result *api.PersistentVolumeClaim, err error) {
|
||||||
|
result = &api.PersistentVolumeClaim{}
|
||||||
|
err = c.client.Post().Namespace(c.namespace).Resource("persistentVolumeClaims").Body(claim).Do().Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *persistentVolumeClaims) Update(claim *api.PersistentVolumeClaim) (result *api.PersistentVolumeClaim, err error) {
|
||||||
|
result = &api.PersistentVolumeClaim{}
|
||||||
|
if len(claim.ResourceVersion) == 0 {
|
||||||
|
err = fmt.Errorf("invalid update object, missing resource version: %v", claim)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = c.client.Put().Namespace(c.namespace).Resource("persistentVolumeClaims").Name(claim.Name).Body(claim).Do().Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *persistentVolumeClaims) Delete(name string) error {
|
||||||
|
return c.client.Delete().Namespace(c.namespace).Resource("persistentVolumeClaims").Name(name).Do().Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *persistentVolumeClaims) Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) {
|
||||||
|
return c.client.Get().
|
||||||
|
Prefix("watch").
|
||||||
|
Namespace(c.namespace).
|
||||||
|
Resource("persistentVolumeClaims").
|
||||||
|
Param("resourceVersion", resourceVersion).
|
||||||
|
LabelsSelectorParam(api.LabelSelectorQueryParam(c.client.APIVersion()), label).
|
||||||
|
FieldsSelectorParam(api.FieldSelectorQueryParam(c.client.APIVersion()), field).
|
||||||
|
Watch()
|
||||||
|
}
|
97
pkg/client/persistentvolumes.go
Normal file
97
pkg/client/persistentvolumes.go
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PersistentVolumesInterface interface {
|
||||||
|
PersistentVolumes() PersistentVolumeInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
// PersistentVolumeInterface has methods to work with PersistentVolume resources.
|
||||||
|
type PersistentVolumeInterface interface {
|
||||||
|
List(label labels.Selector, field fields.Selector) (*api.PersistentVolumeList, error)
|
||||||
|
Get(name string) (*api.PersistentVolume, error)
|
||||||
|
Create(volume *api.PersistentVolume) (*api.PersistentVolume, error)
|
||||||
|
Update(volume *api.PersistentVolume) (*api.PersistentVolume, error)
|
||||||
|
Delete(name string) error
|
||||||
|
Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// persistentVolumes implements PersistentVolumesInterface
|
||||||
|
type persistentVolumes struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPersistentVolumes(c *Client) *persistentVolumes {
|
||||||
|
return &persistentVolumes{c}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *persistentVolumes) List(label labels.Selector, field fields.Selector) (result *api.PersistentVolumeList, err error) {
|
||||||
|
result = &api.PersistentVolumeList{}
|
||||||
|
err = c.client.Get().
|
||||||
|
Resource("persistentVolumes").
|
||||||
|
LabelsSelectorParam(api.LabelSelectorQueryParam(c.client.APIVersion()), label).
|
||||||
|
FieldsSelectorParam(api.FieldSelectorQueryParam(c.client.APIVersion()), field).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *persistentVolumes) Get(name string) (result *api.PersistentVolume, err error) {
|
||||||
|
result = &api.PersistentVolume{}
|
||||||
|
err = c.client.Get().Resource("persistentVolumes").Name(name).Do().Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *persistentVolumes) Create(volume *api.PersistentVolume) (result *api.PersistentVolume, err error) {
|
||||||
|
result = &api.PersistentVolume{}
|
||||||
|
err = c.client.Post().Resource("persistentVolumes").Body(volume).Do().Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *persistentVolumes) Update(volume *api.PersistentVolume) (result *api.PersistentVolume, err error) {
|
||||||
|
result = &api.PersistentVolume{}
|
||||||
|
if len(volume.ResourceVersion) == 0 {
|
||||||
|
err = fmt.Errorf("invalid update object, missing resource version: %v", volume)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = c.client.Put().Resource("persistentVolumes").Name(volume.Name).Body(volume).Do().Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *persistentVolumes) Delete(name string) error {
|
||||||
|
return c.client.Delete().Resource("persistentVolumes").Name(name).Do().Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *persistentVolumes) Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) {
|
||||||
|
return c.client.Get().
|
||||||
|
Prefix("watch").
|
||||||
|
Resource("persistentVolumes").
|
||||||
|
Param("resourceVersion", resourceVersion).
|
||||||
|
LabelsSelectorParam(api.LabelSelectorQueryParam(c.client.APIVersion()), label).
|
||||||
|
FieldsSelectorParam(api.FieldSelectorQueryParam(c.client.APIVersion()), field).
|
||||||
|
Watch()
|
||||||
|
}
|
@ -68,6 +68,10 @@ func DescriberFor(kind string, c *client.Client) (Describer, bool) {
|
|||||||
return &ReplicationControllerDescriber{c}, true
|
return &ReplicationControllerDescriber{c}, true
|
||||||
case "Service":
|
case "Service":
|
||||||
return &ServiceDescriber{c}, true
|
return &ServiceDescriber{c}, true
|
||||||
|
case "PersistentVolume":
|
||||||
|
return &PersistentVolumeDescriber{c}, true
|
||||||
|
case "PersistentVolumeClaim":
|
||||||
|
return &PersistentVolumeClaimDescriber{c}, true
|
||||||
case "Minion", "Node":
|
case "Minion", "Node":
|
||||||
return &NodeDescriber{c}, true
|
return &NodeDescriber{c}, true
|
||||||
case "LimitRange":
|
case "LimitRange":
|
||||||
@ -269,6 +273,49 @@ func describePod(pod *api.Pod, rcs []api.ReplicationController, events *api.Even
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PersistentVolumeDescriber struct {
|
||||||
|
client.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *PersistentVolumeDescriber) Describe(namespace, name string) (string, error) {
|
||||||
|
c := d.PersistentVolumes()
|
||||||
|
|
||||||
|
pv, err := c.Get(name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tabbedString(func(out io.Writer) error {
|
||||||
|
fmt.Fprintf(out, "Name:\t%s\n", pv.Name)
|
||||||
|
fmt.Fprintf(out, "Labels:\t%s\n", formatLabels(pv.Labels))
|
||||||
|
fmt.Fprintf(out, "Status:\t%d\n", pv.Status.Phase)
|
||||||
|
fmt.Fprintf(out, "Claim:\t%d\n", pv.Spec.ClaimRef.UID)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type PersistentVolumeClaimDescriber struct {
|
||||||
|
client.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *PersistentVolumeClaimDescriber) Describe(namespace, name string) (string, error) {
|
||||||
|
c := d.PersistentVolumeClaims(namespace)
|
||||||
|
|
||||||
|
psd, err := c.Get(name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tabbedString(func(out io.Writer) error {
|
||||||
|
fmt.Fprintf(out, "Name:\t%s\n", psd.Name)
|
||||||
|
fmt.Fprintf(out, "Status:\t%d\n", psd.Status.Phase)
|
||||||
|
fmt.Fprintf(out, "Volume:\t%d\n", psd.Status.VolumeRef.UID)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func describeContainers(containers []api.ContainerStatus, out io.Writer) {
|
func describeContainers(containers []api.ContainerStatus, out io.Writer) {
|
||||||
for _, container := range containers {
|
for _, container := range containers {
|
||||||
fmt.Fprintf(out, " %v:\n", container.Name)
|
fmt.Fprintf(out, " %v:\n", container.Name)
|
||||||
|
@ -107,6 +107,8 @@ func expandResourceShortcut(resource string) string {
|
|||||||
"ev": "events",
|
"ev": "events",
|
||||||
"limits": "limitRanges",
|
"limits": "limitRanges",
|
||||||
"quota": "resourceQuotas",
|
"quota": "resourceQuotas",
|
||||||
|
"pv": "persistentVolumes",
|
||||||
|
"pvc": "persistentVolumeClaims",
|
||||||
}
|
}
|
||||||
if expanded, ok := shortForms[resource]; ok {
|
if expanded, ok := shortForms[resource]; ok {
|
||||||
return expanded
|
return expanded
|
||||||
|
@ -32,6 +32,7 @@ import (
|
|||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume"
|
||||||
"github.com/docker/docker/pkg/units"
|
"github.com/docker/docker/pkg/units"
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
@ -237,6 +238,8 @@ var limitRangeColumns = []string{"NAME"}
|
|||||||
var resourceQuotaColumns = []string{"NAME"}
|
var resourceQuotaColumns = []string{"NAME"}
|
||||||
var namespaceColumns = []string{"NAME", "LABELS", "STATUS"}
|
var namespaceColumns = []string{"NAME", "LABELS", "STATUS"}
|
||||||
var secretColumns = []string{"NAME", "DATA"}
|
var secretColumns = []string{"NAME", "DATA"}
|
||||||
|
var persistentVolumeColumns = []string{"NAME", "LABELS", "CAPACITY", "ACCESSMODES", "STATUS", "CLAIM"}
|
||||||
|
var persistentVolumeClaimColumns = []string{"NAME", "LABELS", "STATUS", "VOLUME"}
|
||||||
|
|
||||||
// addDefaultHandlers adds print handlers for default Kubernetes types.
|
// addDefaultHandlers adds print handlers for default Kubernetes types.
|
||||||
func (h *HumanReadablePrinter) addDefaultHandlers() {
|
func (h *HumanReadablePrinter) addDefaultHandlers() {
|
||||||
@ -261,6 +264,10 @@ func (h *HumanReadablePrinter) addDefaultHandlers() {
|
|||||||
h.Handler(namespaceColumns, printNamespaceList)
|
h.Handler(namespaceColumns, printNamespaceList)
|
||||||
h.Handler(secretColumns, printSecret)
|
h.Handler(secretColumns, printSecret)
|
||||||
h.Handler(secretColumns, printSecretList)
|
h.Handler(secretColumns, printSecretList)
|
||||||
|
h.Handler(persistentVolumeClaimColumns, printPersistentVolumeClaim)
|
||||||
|
h.Handler(persistentVolumeClaimColumns, printPersistentVolumeClaimList)
|
||||||
|
h.Handler(persistentVolumeColumns, printPersistentVolume)
|
||||||
|
h.Handler(persistentVolumeColumns, printPersistentVolumeList)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HumanReadablePrinter) unknown(data []byte, w io.Writer) error {
|
func (h *HumanReadablePrinter) unknown(data []byte, w io.Writer) error {
|
||||||
@ -506,6 +513,50 @@ func printNodeList(list *api.NodeList, w io.Writer) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func printPersistentVolume(pv *api.PersistentVolume, w io.Writer) error {
|
||||||
|
claimRefUID := ""
|
||||||
|
if pv.Spec.ClaimRef != nil {
|
||||||
|
claimRefUID += pv.Spec.ClaimRef.Name
|
||||||
|
claimRefUID += " / "
|
||||||
|
claimRefUID += string(pv.Spec.ClaimRef.UID)
|
||||||
|
}
|
||||||
|
|
||||||
|
modesStr := volume.GetAccessModesAsString(pv.Spec.AccessModes)
|
||||||
|
|
||||||
|
aQty := pv.Spec.Capacity[api.ResourceStorage]
|
||||||
|
aSize := aQty.Value()
|
||||||
|
|
||||||
|
_, err := fmt.Fprintf(w, "%s\t%s\t%d\t%s\t%s\t%s\n", pv.Name, pv.Labels, aSize, modesStr, pv.Status.Phase, claimRefUID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func printPersistentVolumeList(list *api.PersistentVolumeList, w io.Writer) error {
|
||||||
|
for _, pv := range list.Items {
|
||||||
|
if err := printPersistentVolume(&pv, w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func printPersistentVolumeClaim(pvc *api.PersistentVolumeClaim, w io.Writer) error {
|
||||||
|
volumeRefUID := ""
|
||||||
|
if pvc.Status.VolumeRef != nil {
|
||||||
|
volumeRefUID = string(pvc.Status.VolumeRef.UID)
|
||||||
|
}
|
||||||
|
_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", pvc.Name, pvc.Labels, pvc.Status.Phase, volumeRefUID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func printPersistentVolumeClaimList(list *api.PersistentVolumeClaimList, w io.Writer) error {
|
||||||
|
for _, psd := range list.Items {
|
||||||
|
if err := printPersistentVolumeClaim(&psd, w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func printStatus(status *api.Status, w io.Writer) error {
|
func printStatus(status *api.Status, w io.Writer) error {
|
||||||
_, err := fmt.Fprintf(w, "%v\n", status.Status)
|
_, err := fmt.Fprintf(w, "%v\n", status.Status)
|
||||||
return err
|
return err
|
||||||
|
@ -52,6 +52,8 @@ import (
|
|||||||
nodeetcd "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/minion/etcd"
|
nodeetcd "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/minion/etcd"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/namespace"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/namespace"
|
||||||
namespaceetcd "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/namespace/etcd"
|
namespaceetcd "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/namespace/etcd"
|
||||||
|
pvetcd "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/persistentvolume/etcd"
|
||||||
|
pvcetcd "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/persistentvolumeclaim/etcd"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/pod"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/pod"
|
||||||
podetcd "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/pod/etcd"
|
podetcd "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/pod/etcd"
|
||||||
resourcequotaetcd "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/resourcequota/etcd"
|
resourcequotaetcd "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/resourcequota/etcd"
|
||||||
@ -363,6 +365,8 @@ func (m *Master) init(c *Config) {
|
|||||||
|
|
||||||
resourceQuotaStorage, resourceQuotaStatusStorage := resourcequotaetcd.NewStorage(c.EtcdHelper)
|
resourceQuotaStorage, resourceQuotaStatusStorage := resourcequotaetcd.NewStorage(c.EtcdHelper)
|
||||||
secretRegistry := secret.NewEtcdRegistry(c.EtcdHelper)
|
secretRegistry := secret.NewEtcdRegistry(c.EtcdHelper)
|
||||||
|
persistentVolumeStorage, persistentVolumeStatusStorage := pvetcd.NewStorage(c.EtcdHelper)
|
||||||
|
persistentVolumeClaimStorage := pvcetcd.NewStorage(c.EtcdHelper)
|
||||||
|
|
||||||
namespaceStorage, namespaceStatusStorage, namespaceFinalizeStorage := namespaceetcd.NewStorage(c.EtcdHelper)
|
namespaceStorage, namespaceStatusStorage, namespaceFinalizeStorage := namespaceetcd.NewStorage(c.EtcdHelper)
|
||||||
m.namespaceRegistry = namespace.NewRegistry(namespaceStorage)
|
m.namespaceRegistry = namespace.NewRegistry(namespaceStorage)
|
||||||
@ -393,13 +397,16 @@ func (m *Master) init(c *Config) {
|
|||||||
"nodes": nodeStorage,
|
"nodes": nodeStorage,
|
||||||
"events": event.NewStorage(eventRegistry),
|
"events": event.NewStorage(eventRegistry),
|
||||||
|
|
||||||
"limitRanges": limitrange.NewStorage(limitRangeRegistry),
|
"limitRanges": limitrange.NewStorage(limitRangeRegistry),
|
||||||
"resourceQuotas": resourceQuotaStorage,
|
"resourceQuotas": resourceQuotaStorage,
|
||||||
"resourceQuotas/status": resourceQuotaStatusStorage,
|
"resourceQuotas/status": resourceQuotaStatusStorage,
|
||||||
"namespaces": namespaceStorage,
|
"namespaces": namespaceStorage,
|
||||||
"namespaces/status": namespaceStatusStorage,
|
"namespaces/status": namespaceStatusStorage,
|
||||||
"namespaces/finalize": namespaceFinalizeStorage,
|
"namespaces/finalize": namespaceFinalizeStorage,
|
||||||
"secrets": secret.NewStorage(secretRegistry),
|
"secrets": secret.NewStorage(secretRegistry),
|
||||||
|
"persistentVolumes": persistentVolumeStorage,
|
||||||
|
"persistentVolumes/status": persistentVolumeStatusStorage,
|
||||||
|
"persistentVolumeClaims": persistentVolumeClaimStorage,
|
||||||
}
|
}
|
||||||
|
|
||||||
apiVersions := []string{"v1beta1", "v1beta2"}
|
apiVersions := []string{"v1beta1", "v1beta2"}
|
||||||
|
@ -133,6 +133,10 @@ func deleteAllContent(kubeClient client.Interface, namespace string) (err error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = deletePersistentVolumeClaims(kubeClient, namespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
err = deleteLimitRanges(kubeClient, namespace)
|
err = deleteLimitRanges(kubeClient, namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -292,3 +296,17 @@ func deleteSecrets(kubeClient client.Interface, ns string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func deletePersistentVolumeClaims(kubeClient client.Interface, ns string) error {
|
||||||
|
items, err := kubeClient.PersistentVolumeClaims(ns).List(labels.Everything(), fields.Everything())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := range items.Items {
|
||||||
|
err := kubeClient.PersistentVolumeClaims(ns).Delete(items.Items[i].Name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
17
pkg/registry/persistentvolume/doc.go
Normal file
17
pkg/registry/persistentvolume/doc.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 persistentvolume
|
80
pkg/registry/persistentvolume/etcd/etcd.go
Normal file
80
pkg/registry/persistentvolume/etcd/etcd.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 etcd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic"
|
||||||
|
etcdgeneric "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic/etcd"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/persistentvolume"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
||||||
|
)
|
||||||
|
|
||||||
|
// rest implements a RESTStorage for persistentvolumes against etcd
|
||||||
|
type REST struct {
|
||||||
|
*etcdgeneric.Etcd
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewREST returns a RESTStorage object that will work against PersistentVolume objects.
|
||||||
|
func NewStorage(h tools.EtcdHelper) (*REST, *StatusREST) {
|
||||||
|
prefix := "/registry/persistentvolumes"
|
||||||
|
store := &etcdgeneric.Etcd{
|
||||||
|
NewFunc: func() runtime.Object { return &api.PersistentVolume{} },
|
||||||
|
NewListFunc: func() runtime.Object { return &api.PersistentVolumeList{} },
|
||||||
|
KeyRootFunc: func(ctx api.Context) string {
|
||||||
|
return prefix
|
||||||
|
},
|
||||||
|
KeyFunc: func(ctx api.Context, name string) (string, error) {
|
||||||
|
return prefix + "/" + name, nil
|
||||||
|
},
|
||||||
|
ObjectNameFunc: func(obj runtime.Object) (string, error) {
|
||||||
|
return obj.(*api.PersistentVolume).Name, nil
|
||||||
|
},
|
||||||
|
PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher {
|
||||||
|
return persistentvolume.MatchPersistentVolumes(label, field)
|
||||||
|
},
|
||||||
|
EndpointName: "persistentvolume",
|
||||||
|
|
||||||
|
Helper: h,
|
||||||
|
}
|
||||||
|
|
||||||
|
store.CreateStrategy = persistentvolume.Strategy
|
||||||
|
store.UpdateStrategy = persistentvolume.Strategy
|
||||||
|
store.ReturnDeletedObject = true
|
||||||
|
|
||||||
|
statusStore := *store
|
||||||
|
statusStore.UpdateStrategy = persistentvolume.StatusStrategy
|
||||||
|
|
||||||
|
return &REST{store}, &StatusREST{store: &statusStore}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatusREST implements the REST endpoint for changing the status of a persistentvolume.
|
||||||
|
type StatusREST struct {
|
||||||
|
store *etcdgeneric.Etcd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *StatusREST) New() runtime.Object {
|
||||||
|
return &api.PersistentVolume{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update alters the status subset of an object.
|
||||||
|
func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) {
|
||||||
|
return r.store.Update(ctx, obj)
|
||||||
|
}
|
342
pkg/registry/persistentvolume/etcd/etcd_test.go
Normal file
342
pkg/registry/persistentvolume/etcd/etcd_test.go
Normal file
@ -0,0 +1,342 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 etcd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest/resttest"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
|
|
||||||
|
"github.com/coreos/go-etcd/etcd"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testRegistry struct {
|
||||||
|
*registrytest.GenericRegistry
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStorage(t *testing.T) (*REST, *StatusREST, *tools.FakeEtcdClient, tools.EtcdHelper) {
|
||||||
|
fakeEtcdClient := tools.NewFakeEtcdClient(t)
|
||||||
|
fakeEtcdClient.TestIndex = true
|
||||||
|
helper := tools.NewEtcdHelper(fakeEtcdClient, latest.Codec)
|
||||||
|
storage, statusStorage := NewStorage(helper)
|
||||||
|
return storage, statusStorage, fakeEtcdClient, helper
|
||||||
|
}
|
||||||
|
|
||||||
|
func validNewPersistentVolume(name string) *api.PersistentVolume {
|
||||||
|
pv := &api.PersistentVolume{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Spec: api.PersistentVolumeSpec{
|
||||||
|
Capacity: api.ResourceList{
|
||||||
|
api.ResourceName(api.ResourceStorage): resource.MustParse("10G"),
|
||||||
|
},
|
||||||
|
PersistentVolumeSource: api.PersistentVolumeSource{
|
||||||
|
HostPath: &api.HostPathVolumeSource{Path: "/foo"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return pv
|
||||||
|
}
|
||||||
|
|
||||||
|
func validChangedPersistentVolume() *api.PersistentVolume {
|
||||||
|
pv := validNewPersistentVolume("foo")
|
||||||
|
pv.ResourceVersion = "1"
|
||||||
|
return pv
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreate(t *testing.T) {
|
||||||
|
storage, _, fakeEtcdClient, _ := newStorage(t)
|
||||||
|
test := resttest.New(t, storage, fakeEtcdClient.SetError)
|
||||||
|
pv := validNewPersistentVolume("foo")
|
||||||
|
pv.ObjectMeta = api.ObjectMeta{}
|
||||||
|
test.TestCreate(
|
||||||
|
// valid
|
||||||
|
pv,
|
||||||
|
// invalid
|
||||||
|
&api.PersistentVolume{
|
||||||
|
ObjectMeta: api.ObjectMeta{Name: "*BadName!"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDelete(t *testing.T) {
|
||||||
|
ctx := api.NewDefaultContext()
|
||||||
|
storage, _, fakeEtcdClient, _ := newStorage(t)
|
||||||
|
test := resttest.New(t, storage, fakeEtcdClient.SetError)
|
||||||
|
|
||||||
|
pv := validChangedPersistentVolume()
|
||||||
|
key, _ := storage.KeyFunc(ctx, pv.Name)
|
||||||
|
createFn := func() runtime.Object {
|
||||||
|
fakeEtcdClient.Data[key] = tools.EtcdResponseWithError{
|
||||||
|
R: &etcd.Response{
|
||||||
|
Node: &etcd.Node{
|
||||||
|
Value: runtime.EncodeOrDie(latest.Codec, pv),
|
||||||
|
ModifiedIndex: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return pv
|
||||||
|
}
|
||||||
|
gracefulSetFn := func() bool {
|
||||||
|
if fakeEtcdClient.Data[key].R.Node == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return fakeEtcdClient.Data[key].R.Node.TTL == 30
|
||||||
|
}
|
||||||
|
test.TestDeleteNoGraceful(createFn, gracefulSetFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEtcdListPersistentVolumes(t *testing.T) {
|
||||||
|
ctx := api.NewDefaultContext()
|
||||||
|
storage, _, fakeClient, _ := newStorage(t)
|
||||||
|
key := storage.KeyRootFunc(ctx)
|
||||||
|
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
||||||
|
R: &etcd.Response{
|
||||||
|
Node: &etcd.Node{
|
||||||
|
Nodes: []*etcd.Node{
|
||||||
|
{
|
||||||
|
Value: runtime.EncodeOrDie(latest.Codec, validNewPersistentVolume("foo")),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: runtime.EncodeOrDie(latest.Codec, validNewPersistentVolume("bar")),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
E: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
pvObj, err := storage.List(ctx, labels.Everything(), fields.Everything())
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
pvs := pvObj.(*api.PersistentVolumeList)
|
||||||
|
|
||||||
|
if len(pvs.Items) != 2 || pvs.Items[0].Name != "foo" || pvs.Items[1].Name != "bar" {
|
||||||
|
t.Errorf("Unexpected persistentVolume list: %#v", pvs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEtcdGetPersistentVolumes(t *testing.T) {
|
||||||
|
ctx := api.NewDefaultContext()
|
||||||
|
storage, _, fakeClient, _ := newStorage(t)
|
||||||
|
persistentVolume := validNewPersistentVolume("foo")
|
||||||
|
name := persistentVolume.Name
|
||||||
|
key, _ := storage.KeyFunc(ctx, name)
|
||||||
|
fakeClient.Set(key, runtime.EncodeOrDie(latest.Codec, persistentVolume), 0)
|
||||||
|
|
||||||
|
response, err := fakeClient.Get(key, false, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error %v", err)
|
||||||
|
}
|
||||||
|
var persistentVolumeOut api.PersistentVolume
|
||||||
|
err = latest.Codec.DecodeInto([]byte(response.Node.Value), &persistentVolumeOut)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj, err := storage.Get(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
got := obj.(*api.PersistentVolume)
|
||||||
|
|
||||||
|
persistentVolume.ObjectMeta.ResourceVersion = got.ObjectMeta.ResourceVersion
|
||||||
|
if e, a := persistentVolume, got; !api.Semantic.DeepEqual(*e, *a) {
|
||||||
|
t.Errorf("Unexpected persistentVolume: %#v, expected %#v", e, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListEmptyPersistentVolumesList(t *testing.T) {
|
||||||
|
ctx := api.NewDefaultContext()
|
||||||
|
storage, _, fakeClient, _ := newStorage(t)
|
||||||
|
fakeClient.ChangeIndex = 1
|
||||||
|
key := storage.KeyRootFunc(ctx)
|
||||||
|
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
||||||
|
R: &etcd.Response{},
|
||||||
|
E: fakeClient.NewError(tools.EtcdErrorCodeNotFound),
|
||||||
|
}
|
||||||
|
|
||||||
|
persistentVolume, err := storage.List(ctx, labels.Everything(), fields.Everything())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(persistentVolume.(*api.PersistentVolumeList).Items) != 0 {
|
||||||
|
t.Errorf("Unexpected non-zero pod list: %#v", persistentVolume)
|
||||||
|
}
|
||||||
|
if persistentVolume.(*api.PersistentVolumeList).ResourceVersion != "1" {
|
||||||
|
t.Errorf("Unexpected resource version: %#v", persistentVolume)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListPersistentVolumesList(t *testing.T) {
|
||||||
|
ctx := api.NewDefaultContext()
|
||||||
|
storage, _, fakeClient, _ := newStorage(t)
|
||||||
|
fakeClient.ChangeIndex = 1
|
||||||
|
key := storage.KeyRootFunc(ctx)
|
||||||
|
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
||||||
|
R: &etcd.Response{
|
||||||
|
Node: &etcd.Node{
|
||||||
|
Nodes: []*etcd.Node{
|
||||||
|
{
|
||||||
|
Value: runtime.EncodeOrDie(latest.Codec, &api.PersistentVolume{
|
||||||
|
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: runtime.EncodeOrDie(latest.Codec, &api.PersistentVolume{
|
||||||
|
ObjectMeta: api.ObjectMeta{Name: "bar"},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
persistentVolumeObj, err := storage.List(ctx, labels.Everything(), fields.Everything())
|
||||||
|
persistentVolumeList := persistentVolumeObj.(*api.PersistentVolumeList)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(persistentVolumeList.Items) != 2 {
|
||||||
|
t.Errorf("Unexpected persistentVolume list: %#v", persistentVolumeList)
|
||||||
|
}
|
||||||
|
if persistentVolumeList.Items[0].Name != "foo" {
|
||||||
|
t.Errorf("Unexpected persistentVolume: %#v", persistentVolumeList.Items[0])
|
||||||
|
}
|
||||||
|
if persistentVolumeList.Items[1].Name != "bar" {
|
||||||
|
t.Errorf("Unexpected persistentVolume: %#v", persistentVolumeList.Items[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPersistentVolumesDecode(t *testing.T) {
|
||||||
|
storage, _, _, _ := newStorage(t)
|
||||||
|
expected := validNewPersistentVolume("foo")
|
||||||
|
body, err := latest.Codec.Encode(expected)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := storage.New()
|
||||||
|
if err := latest.Codec.DecodeInto(body, actual); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !api.Semantic.DeepEqual(expected, actual) {
|
||||||
|
t.Errorf("mismatch: %s", util.ObjectDiff(expected, actual))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEtcdUpdatePersistentVolumes(t *testing.T) {
|
||||||
|
ctx := api.NewDefaultContext()
|
||||||
|
storage, _, fakeClient, _ := newStorage(t)
|
||||||
|
persistentVolume := validChangedPersistentVolume()
|
||||||
|
|
||||||
|
key, _ := storage.KeyFunc(ctx, "foo")
|
||||||
|
fakeClient.Set(key, runtime.EncodeOrDie(latest.Codec, validNewPersistentVolume("foo")), 0)
|
||||||
|
|
||||||
|
_, _, err := storage.Update(ctx, persistentVolume)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := fakeClient.Get(key, false, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error %v", err)
|
||||||
|
}
|
||||||
|
var persistentVolumeOut api.PersistentVolume
|
||||||
|
err = latest.Codec.DecodeInto([]byte(response.Node.Value), &persistentVolumeOut)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
persistentVolume.ObjectMeta.ResourceVersion = persistentVolumeOut.ObjectMeta.ResourceVersion
|
||||||
|
if !api.Semantic.DeepEqual(persistentVolume, &persistentVolumeOut) {
|
||||||
|
t.Errorf("Unexpected persistentVolume: %#v, expected %#v", &persistentVolumeOut, persistentVolume)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeletePersistentVolumes(t *testing.T) {
|
||||||
|
ctx := api.NewDefaultContext()
|
||||||
|
storage, _, fakeClient, _ := newStorage(t)
|
||||||
|
persistentVolume := validNewPersistentVolume("foo")
|
||||||
|
name := persistentVolume.Name
|
||||||
|
key, _ := storage.KeyFunc(ctx, name)
|
||||||
|
fakeClient.ChangeIndex = 1
|
||||||
|
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
||||||
|
R: &etcd.Response{
|
||||||
|
Node: &etcd.Node{
|
||||||
|
Value: runtime.EncodeOrDie(latest.Codec, persistentVolume),
|
||||||
|
ModifiedIndex: 1,
|
||||||
|
CreatedIndex: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := storage.Delete(ctx, name, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEtcdUpdateStatus(t *testing.T) {
|
||||||
|
storage, statusStorage, fakeClient, helper := newStorage(t)
|
||||||
|
ctx := api.NewDefaultContext()
|
||||||
|
fakeClient.TestIndex = true
|
||||||
|
|
||||||
|
key, _ := storage.KeyFunc(ctx, "foo")
|
||||||
|
pvStart := validNewPersistentVolume("foo")
|
||||||
|
fakeClient.Set(key, runtime.EncodeOrDie(latest.Codec, pvStart), 1)
|
||||||
|
|
||||||
|
pvIn := &api.PersistentVolume{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
ResourceVersion: "1",
|
||||||
|
},
|
||||||
|
Status: api.PersistentVolumeStatus{
|
||||||
|
Phase: api.VolumeBound,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := *pvStart
|
||||||
|
expected.ResourceVersion = "2"
|
||||||
|
expected.Labels = pvIn.Labels
|
||||||
|
expected.Status = pvIn.Status
|
||||||
|
|
||||||
|
_, _, err := statusStorage.Update(ctx, pvIn)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
var pvOut api.PersistentVolume
|
||||||
|
if err := helper.ExtractObj(key, &pvOut, false); err != nil {
|
||||||
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if !api.Semantic.DeepEqual(expected, pvOut) {
|
||||||
|
t.Errorf("unexpected object: %s", util.ObjectDiff(expected, pvOut))
|
||||||
|
}
|
||||||
|
}
|
87
pkg/registry/persistentvolume/registry.go
Normal file
87
pkg/registry/persistentvolume/registry.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 persistentvolume
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Registry is an interface implemented by things that know how to store PersistentVolume objects.
|
||||||
|
type Registry interface {
|
||||||
|
// ListPersistentVolumes obtains a list of persistentVolumes having labels which match selector.
|
||||||
|
ListPersistentVolumes(ctx api.Context, selector labels.Selector) (*api.PersistentVolumeList, error)
|
||||||
|
// Watch for new/changed/deleted persistentVolumes
|
||||||
|
WatchPersistentVolumes(ctx api.Context, label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error)
|
||||||
|
// Get a specific persistentVolume
|
||||||
|
GetPersistentVolume(ctx api.Context, persistentVolumeID string) (*api.PersistentVolume, error)
|
||||||
|
// Create a persistentVolume based on a specification.
|
||||||
|
CreatePersistentVolume(ctx api.Context, persistentVolume *api.PersistentVolume) error
|
||||||
|
// Update an existing persistentVolume
|
||||||
|
UpdatePersistentVolume(ctx api.Context, persistentVolume *api.PersistentVolume) error
|
||||||
|
// Delete an existing persistentVolume
|
||||||
|
DeletePersistentVolume(ctx api.Context, persistentVolumeID string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// storage puts strong typing around storage calls
|
||||||
|
type storage struct {
|
||||||
|
rest.StandardStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
|
||||||
|
// types will panic.
|
||||||
|
func NewRegistry(s rest.StandardStorage) Registry {
|
||||||
|
return &storage{s}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *storage) ListPersistentVolumes(ctx api.Context, label labels.Selector) (*api.PersistentVolumeList, error) {
|
||||||
|
obj, err := s.List(ctx, label, fields.Everything())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*api.PersistentVolumeList), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *storage) WatchPersistentVolumes(ctx api.Context, label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) {
|
||||||
|
return s.Watch(ctx, label, field, resourceVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *storage) GetPersistentVolume(ctx api.Context, persistentVolumeID string) (*api.PersistentVolume, error) {
|
||||||
|
obj, err := s.Get(ctx, persistentVolumeID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*api.PersistentVolume), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *storage) CreatePersistentVolume(ctx api.Context, persistentVolume *api.PersistentVolume) error {
|
||||||
|
_, err := s.Create(ctx, persistentVolume)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *storage) UpdatePersistentVolume(ctx api.Context, persistentVolume *api.PersistentVolume) error {
|
||||||
|
_, _, err := s.Update(ctx, persistentVolume)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *storage) DeletePersistentVolume(ctx api.Context, persistentVolumeID string) error {
|
||||||
|
_, err := s.Delete(ctx, persistentVolumeID, nil)
|
||||||
|
return err
|
||||||
|
}
|
109
pkg/registry/persistentvolume/rest.go
Normal file
109
pkg/registry/persistentvolume/rest.go
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 persistentvolume
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/fielderrors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// persistentvolumeStrategy implements behavior for PersistentVolume objects
|
||||||
|
type persistentvolumeStrategy struct {
|
||||||
|
runtime.ObjectTyper
|
||||||
|
api.NameGenerator
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strategy is the default logic that applies when creating and updating PersistentVolume
|
||||||
|
// objects via the REST API.
|
||||||
|
var Strategy = persistentvolumeStrategy{api.Scheme, api.SimpleNameGenerator}
|
||||||
|
|
||||||
|
// NamespaceScoped is false for persistentvolumes.
|
||||||
|
func (persistentvolumeStrategy) NamespaceScoped() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetBeforeCreate clears fields that are not allowed to be set by end users on creation.
|
||||||
|
func (persistentvolumeStrategy) PrepareForCreate(obj runtime.Object) {
|
||||||
|
pv := obj.(*api.PersistentVolume)
|
||||||
|
pv.Status = api.PersistentVolumeStatus{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
|
||||||
|
func (persistentvolumeStrategy) PrepareForUpdate(obj, old runtime.Object) {
|
||||||
|
newPv := obj.(*api.PersistentVolume)
|
||||||
|
oldPv := obj.(*api.PersistentVolume)
|
||||||
|
newPv.Status = oldPv.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates a new persistentvolume.
|
||||||
|
func (persistentvolumeStrategy) Validate(obj runtime.Object) fielderrors.ValidationErrorList {
|
||||||
|
persistentvolume := obj.(*api.PersistentVolume)
|
||||||
|
return validation.ValidatePersistentVolume(persistentvolume)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllowCreateOnUpdate is false for persistentvolumes.
|
||||||
|
func (persistentvolumeStrategy) AllowCreateOnUpdate() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateUpdate is the default update validation for an end user.
|
||||||
|
func (persistentvolumeStrategy) ValidateUpdate(obj, old runtime.Object) fielderrors.ValidationErrorList {
|
||||||
|
return validation.ValidatePersistentVolumeUpdate(obj.(*api.PersistentVolume), old.(*api.PersistentVolume))
|
||||||
|
}
|
||||||
|
|
||||||
|
type persistentvolumeStatusStrategy struct {
|
||||||
|
persistentvolumeStrategy
|
||||||
|
}
|
||||||
|
|
||||||
|
var StatusStrategy = persistentvolumeStatusStrategy{Strategy}
|
||||||
|
|
||||||
|
func (persistentvolumeStatusStrategy) PrepareForUpdate(obj, old runtime.Object) {
|
||||||
|
newPv := obj.(*api.PersistentVolume)
|
||||||
|
oldPv := obj.(*api.PersistentVolume)
|
||||||
|
newPv.Spec = oldPv.Spec
|
||||||
|
}
|
||||||
|
|
||||||
|
func (persistentvolumeStatusStrategy) ValidateUpdate(obj, old runtime.Object) fielderrors.ValidationErrorList {
|
||||||
|
return validation.ValidatePersistentVolumeStatusUpdate(obj.(*api.PersistentVolume), old.(*api.PersistentVolume))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchPersistentVolume returns a generic matcher for a given label and field selector.
|
||||||
|
func MatchPersistentVolumes(label labels.Selector, field fields.Selector) generic.Matcher {
|
||||||
|
return generic.MatcherFunc(func(obj runtime.Object) (bool, error) {
|
||||||
|
persistentvolumeObj, ok := obj.(*api.PersistentVolume)
|
||||||
|
if !ok {
|
||||||
|
return false, fmt.Errorf("not a persistentvolume")
|
||||||
|
}
|
||||||
|
fields := PersistentVolumeToSelectableFields(persistentvolumeObj)
|
||||||
|
return label.Matches(labels.Set(persistentvolumeObj.Labels)) && field.Matches(fields), nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// PersistentVolumeToSelectableFields returns a label set that represents the object
|
||||||
|
// TODO: fields are not labels, and the validation rules for them do not apply.
|
||||||
|
func PersistentVolumeToSelectableFields(persistentvolume *api.PersistentVolume) labels.Set {
|
||||||
|
return labels.Set{
|
||||||
|
"name": persistentvolume.Name,
|
||||||
|
}
|
||||||
|
}
|
17
pkg/registry/persistentvolumeclaim/doc.go
Normal file
17
pkg/registry/persistentvolumeclaim/doc.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 persistentvolumeclaim
|
63
pkg/registry/persistentvolumeclaim/etcd/etcd.go
Normal file
63
pkg/registry/persistentvolumeclaim/etcd/etcd.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 etcd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic"
|
||||||
|
etcdgeneric "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic/etcd"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/persistentvolumeclaim"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
||||||
|
)
|
||||||
|
|
||||||
|
// rest implements a RESTStorage for persistentvolumeclaims against etcd
|
||||||
|
type REST struct {
|
||||||
|
*etcdgeneric.Etcd
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewREST returns a RESTStorage object that will work against PersistentVolumeClaim objects.
|
||||||
|
func NewStorage(h tools.EtcdHelper) *REST {
|
||||||
|
prefix := "/registry/persistentvolumeclaims"
|
||||||
|
store := &etcdgeneric.Etcd{
|
||||||
|
NewFunc: func() runtime.Object { return &api.PersistentVolumeClaim{} },
|
||||||
|
NewListFunc: func() runtime.Object { return &api.PersistentVolumeClaimList{} },
|
||||||
|
KeyRootFunc: func(ctx api.Context) string {
|
||||||
|
return etcdgeneric.NamespaceKeyRootFunc(ctx, prefix)
|
||||||
|
},
|
||||||
|
KeyFunc: func(ctx api.Context, name string) (string, error) {
|
||||||
|
return etcdgeneric.NamespaceKeyFunc(ctx, prefix, name)
|
||||||
|
},
|
||||||
|
ObjectNameFunc: func(obj runtime.Object) (string, error) {
|
||||||
|
return obj.(*api.PersistentVolumeClaim).Name, nil
|
||||||
|
},
|
||||||
|
PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher {
|
||||||
|
return persistentvolumeclaim.MatchPersistentVolumeClaim(label, field)
|
||||||
|
},
|
||||||
|
EndpointName: "persistentvolumeclaims",
|
||||||
|
|
||||||
|
Helper: h,
|
||||||
|
}
|
||||||
|
|
||||||
|
store.CreateStrategy = persistentvolumeclaim.Strategy
|
||||||
|
store.UpdateStrategy = persistentvolumeclaim.Strategy
|
||||||
|
store.ReturnDeletedObject = true
|
||||||
|
|
||||||
|
return &REST{store}
|
||||||
|
}
|
334
pkg/registry/persistentvolumeclaim/etcd/etcd_test.go
Normal file
334
pkg/registry/persistentvolumeclaim/etcd/etcd_test.go
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 etcd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest/resttest"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
|
|
||||||
|
"github.com/coreos/go-etcd/etcd"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testRegistry struct {
|
||||||
|
*registrytest.GenericRegistry
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient, tools.EtcdHelper) {
|
||||||
|
fakeEtcdClient := tools.NewFakeEtcdClient(t)
|
||||||
|
fakeEtcdClient.TestIndex = true
|
||||||
|
helper := tools.NewEtcdHelper(fakeEtcdClient, latest.Codec)
|
||||||
|
storage := NewStorage(helper)
|
||||||
|
return storage, fakeEtcdClient, helper
|
||||||
|
}
|
||||||
|
|
||||||
|
func validNewPersistentVolumeClaim(name, ns string) *api.PersistentVolumeClaim {
|
||||||
|
pv := &api.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
Namespace: ns,
|
||||||
|
},
|
||||||
|
Spec: api.PersistentVolumeClaimSpec{
|
||||||
|
AccessModes: []api.AccessModeType{api.ReadWriteOnce},
|
||||||
|
Resources: api.ResourceRequirements{
|
||||||
|
Requests: api.ResourceList{
|
||||||
|
api.ResourceName(api.ResourceStorage): resource.MustParse("10G"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return pv
|
||||||
|
}
|
||||||
|
|
||||||
|
func validChangedPersistentVolumeClaim() *api.PersistentVolumeClaim {
|
||||||
|
pv := validNewPersistentVolumeClaim("foo", api.NamespaceDefault)
|
||||||
|
pv.ResourceVersion = "1"
|
||||||
|
return pv
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreate(t *testing.T) {
|
||||||
|
|
||||||
|
registry, fakeEtcdClient, _ := newStorage(t)
|
||||||
|
test := resttest.New(t, registry, fakeEtcdClient.SetError)
|
||||||
|
pv := validNewPersistentVolumeClaim("foo", api.NamespaceDefault)
|
||||||
|
pv.ObjectMeta = api.ObjectMeta{}
|
||||||
|
test.TestCreate(
|
||||||
|
// valid
|
||||||
|
pv,
|
||||||
|
// invalid
|
||||||
|
&api.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: api.ObjectMeta{Name: "*BadName!"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDelete2(t *testing.T) {
|
||||||
|
ctx := api.NewDefaultContext()
|
||||||
|
storage, fakeEtcdClient, _ := newStorage(t)
|
||||||
|
test := resttest.New(t, storage, fakeEtcdClient.SetError)
|
||||||
|
|
||||||
|
pv := validChangedPersistentVolumeClaim()
|
||||||
|
key, _ := storage.KeyFunc(ctx, pv.Name)
|
||||||
|
createFn := func() runtime.Object {
|
||||||
|
fakeEtcdClient.Data[key] = tools.EtcdResponseWithError{
|
||||||
|
R: &etcd.Response{
|
||||||
|
Node: &etcd.Node{
|
||||||
|
Value: runtime.EncodeOrDie(latest.Codec, pv),
|
||||||
|
ModifiedIndex: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return pv
|
||||||
|
}
|
||||||
|
gracefulSetFn := func() bool {
|
||||||
|
if fakeEtcdClient.Data[key].R.Node == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return fakeEtcdClient.Data[key].R.Node.TTL == 30
|
||||||
|
}
|
||||||
|
test.TestDeleteNoGraceful(createFn, gracefulSetFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDelete(t *testing.T) {
|
||||||
|
ctx := api.NewDefaultContext()
|
||||||
|
registry, fakeEtcdClient, _ := newStorage(t)
|
||||||
|
test := resttest.New(t, registry, fakeEtcdClient.SetError)
|
||||||
|
|
||||||
|
pvc := validChangedPersistentVolumeClaim()
|
||||||
|
key, _ := registry.KeyFunc(ctx, pvc.Name)
|
||||||
|
createFn := func() runtime.Object {
|
||||||
|
fakeEtcdClient.Data[key] = tools.EtcdResponseWithError{
|
||||||
|
R: &etcd.Response{
|
||||||
|
Node: &etcd.Node{
|
||||||
|
Value: runtime.EncodeOrDie(latest.Codec, pvc),
|
||||||
|
ModifiedIndex: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return pvc
|
||||||
|
}
|
||||||
|
gracefulSetFn := func() bool {
|
||||||
|
if fakeEtcdClient.Data[key].R.Node == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return fakeEtcdClient.Data[key].R.Node.TTL == 30
|
||||||
|
}
|
||||||
|
test.TestDeleteNoGraceful(createFn, gracefulSetFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEtcdListPersistentVolumeClaims(t *testing.T) {
|
||||||
|
ctx := api.NewDefaultContext()
|
||||||
|
registry, fakeClient, _ := newStorage(t)
|
||||||
|
key := registry.KeyRootFunc(ctx)
|
||||||
|
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
||||||
|
R: &etcd.Response{
|
||||||
|
Node: &etcd.Node{
|
||||||
|
Nodes: []*etcd.Node{
|
||||||
|
{
|
||||||
|
Value: runtime.EncodeOrDie(latest.Codec, validNewPersistentVolumeClaim("foo", api.NamespaceDefault)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: runtime.EncodeOrDie(latest.Codec, validNewPersistentVolumeClaim("bar", api.NamespaceDefault)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
E: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
pvObj, err := registry.List(ctx, labels.Everything(), fields.Everything())
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
pvs := pvObj.(*api.PersistentVolumeClaimList)
|
||||||
|
|
||||||
|
if len(pvs.Items) != 2 || pvs.Items[0].Name != "foo" || pvs.Items[1].Name != "bar" {
|
||||||
|
t.Errorf("Unexpected persistentVolume list: %#v", pvs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEtcdGetPersistentVolumeClaims(t *testing.T) {
|
||||||
|
ctx := api.NewDefaultContext()
|
||||||
|
registry, fakeClient, _ := newStorage(t)
|
||||||
|
persistentVolume := validNewPersistentVolumeClaim("foo", api.NamespaceDefault)
|
||||||
|
name := persistentVolume.Name
|
||||||
|
key, _ := registry.KeyFunc(ctx, name)
|
||||||
|
fakeClient.Set(key, runtime.EncodeOrDie(latest.Codec, persistentVolume), 0)
|
||||||
|
|
||||||
|
response, err := fakeClient.Get(key, false, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error %v", err)
|
||||||
|
}
|
||||||
|
var persistentVolumeOut api.PersistentVolumeClaim
|
||||||
|
err = latest.Codec.DecodeInto([]byte(response.Node.Value), &persistentVolumeOut)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj, err := registry.Get(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
got := obj.(*api.PersistentVolumeClaim)
|
||||||
|
|
||||||
|
persistentVolume.ObjectMeta.ResourceVersion = got.ObjectMeta.ResourceVersion
|
||||||
|
if e, a := persistentVolume, got; !api.Semantic.DeepEqual(*e, *a) {
|
||||||
|
t.Errorf("Unexpected persistentVolume: %#v, expected %#v", e, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListEmptyPersistentVolumeClaimsList(t *testing.T) {
|
||||||
|
ctx := api.NewDefaultContext()
|
||||||
|
registry, fakeClient, _ := newStorage(t)
|
||||||
|
fakeClient.ChangeIndex = 1
|
||||||
|
key := registry.KeyRootFunc(ctx)
|
||||||
|
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
||||||
|
R: &etcd.Response{},
|
||||||
|
E: fakeClient.NewError(tools.EtcdErrorCodeNotFound),
|
||||||
|
}
|
||||||
|
|
||||||
|
persistentVolume, err := registry.List(ctx, labels.Everything(), fields.Everything())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(persistentVolume.(*api.PersistentVolumeClaimList).Items) != 0 {
|
||||||
|
t.Errorf("Unexpected non-zero pod list: %#v", persistentVolume)
|
||||||
|
}
|
||||||
|
if persistentVolume.(*api.PersistentVolumeClaimList).ResourceVersion != "1" {
|
||||||
|
t.Errorf("Unexpected resource version: %#v", persistentVolume)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListPersistentVolumeClaimsList(t *testing.T) {
|
||||||
|
ctx := api.NewDefaultContext()
|
||||||
|
registry, fakeClient, _ := newStorage(t)
|
||||||
|
fakeClient.ChangeIndex = 1
|
||||||
|
key := registry.KeyRootFunc(ctx)
|
||||||
|
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
||||||
|
R: &etcd.Response{
|
||||||
|
Node: &etcd.Node{
|
||||||
|
Nodes: []*etcd.Node{
|
||||||
|
{
|
||||||
|
Value: runtime.EncodeOrDie(latest.Codec, &api.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: runtime.EncodeOrDie(latest.Codec, &api.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: api.ObjectMeta{Name: "bar"},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
persistentVolumeObj, err := registry.List(ctx, labels.Everything(), fields.Everything())
|
||||||
|
persistentVolumeList := persistentVolumeObj.(*api.PersistentVolumeClaimList)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(persistentVolumeList.Items) != 2 {
|
||||||
|
t.Errorf("Unexpected persistentVolume list: %#v", persistentVolumeList)
|
||||||
|
}
|
||||||
|
if persistentVolumeList.Items[0].Name != "foo" {
|
||||||
|
t.Errorf("Unexpected persistentVolume: %#v", persistentVolumeList.Items[0])
|
||||||
|
}
|
||||||
|
if persistentVolumeList.Items[1].Name != "bar" {
|
||||||
|
t.Errorf("Unexpected persistentVolume: %#v", persistentVolumeList.Items[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPersistentVolumeClaimsDecode(t *testing.T) {
|
||||||
|
registry, _, _ := newStorage(t)
|
||||||
|
expected := validNewPersistentVolumeClaim("foo", api.NamespaceDefault)
|
||||||
|
body, err := latest.Codec.Encode(expected)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := registry.New()
|
||||||
|
if err := latest.Codec.DecodeInto(body, actual); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !api.Semantic.DeepEqual(expected, actual) {
|
||||||
|
t.Errorf("mismatch: %s", util.ObjectDiff(expected, actual))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEtcdUpdatePersistentVolumeClaims(t *testing.T) {
|
||||||
|
ctx := api.NewDefaultContext()
|
||||||
|
registry, fakeClient, _ := newStorage(t)
|
||||||
|
persistentVolume := validChangedPersistentVolumeClaim()
|
||||||
|
|
||||||
|
key, _ := registry.KeyFunc(ctx, "foo")
|
||||||
|
fakeClient.Set(key, runtime.EncodeOrDie(latest.Codec, validNewPersistentVolumeClaim("foo", api.NamespaceDefault)), 0)
|
||||||
|
|
||||||
|
_, _, err := registry.Update(ctx, persistentVolume)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := fakeClient.Get(key, false, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error %v", err)
|
||||||
|
}
|
||||||
|
var persistentVolumeOut api.PersistentVolumeClaim
|
||||||
|
err = latest.Codec.DecodeInto([]byte(response.Node.Value), &persistentVolumeOut)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
persistentVolume.ObjectMeta.ResourceVersion = persistentVolumeOut.ObjectMeta.ResourceVersion
|
||||||
|
if !api.Semantic.DeepEqual(persistentVolume, &persistentVolumeOut) {
|
||||||
|
t.Errorf("Unexpected persistentVolume: %#v, expected %#v", &persistentVolumeOut, persistentVolume)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeletePersistentVolumeClaims(t *testing.T) {
|
||||||
|
ctx := api.NewDefaultContext()
|
||||||
|
registry, fakeClient, _ := newStorage(t)
|
||||||
|
persistentVolume := validNewPersistentVolumeClaim("foo", api.NamespaceDefault)
|
||||||
|
name := persistentVolume.Name
|
||||||
|
key, _ := registry.KeyFunc(ctx, name)
|
||||||
|
fakeClient.ChangeIndex = 1
|
||||||
|
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
||||||
|
R: &etcd.Response{
|
||||||
|
Node: &etcd.Node{
|
||||||
|
Value: runtime.EncodeOrDie(latest.Codec, persistentVolume),
|
||||||
|
ModifiedIndex: 1,
|
||||||
|
CreatedIndex: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := registry.Delete(ctx, name, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
87
pkg/registry/persistentvolumeclaim/registry.go
Normal file
87
pkg/registry/persistentvolumeclaim/registry.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 persistentvolumeclaim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Registry is an interface implemented by things that know how to store PersistentVolumeClaim objects.
|
||||||
|
type Registry interface {
|
||||||
|
// ListPersistentVolumeClaims obtains a list of PVCs having labels which match selector.
|
||||||
|
ListPersistentVolumeClaims(ctx api.Context, selector labels.Selector) (*api.PersistentVolumeClaimList, error)
|
||||||
|
// Watch for new/changed/deleted PVCs
|
||||||
|
WatchPersistentVolumeClaims(ctx api.Context, label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error)
|
||||||
|
// Get a specific PVC
|
||||||
|
GetPersistentVolumeClaim(ctx api.Context, pvcID string) (*api.PersistentVolumeClaim, error)
|
||||||
|
// Create a PVC based on a specification.
|
||||||
|
CreatePersistentVolumeClaim(ctx api.Context, pvc *api.PersistentVolumeClaim) error
|
||||||
|
// Update an existing PVC
|
||||||
|
UpdatePersistentVolumeClaim(ctx api.Context, pvc *api.PersistentVolumeClaim) error
|
||||||
|
// Delete an existing PVC
|
||||||
|
DeletePersistentVolumeClaim(ctx api.Context, pvcID string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// storage puts strong typing around storage calls
|
||||||
|
type storage struct {
|
||||||
|
rest.StandardStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
|
||||||
|
// types will panic.
|
||||||
|
func NewRegistry(s rest.StandardStorage) Registry {
|
||||||
|
return &storage{s}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *storage) ListPersistentVolumeClaims(ctx api.Context, label labels.Selector) (*api.PersistentVolumeClaimList, error) {
|
||||||
|
obj, err := s.List(ctx, label, fields.Everything())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*api.PersistentVolumeClaimList), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *storage) WatchPersistentVolumeClaims(ctx api.Context, label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) {
|
||||||
|
return s.Watch(ctx, label, field, resourceVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *storage) GetPersistentVolumeClaim(ctx api.Context, podID string) (*api.PersistentVolumeClaim, error) {
|
||||||
|
obj, err := s.Get(ctx, podID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*api.PersistentVolumeClaim), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *storage) CreatePersistentVolumeClaim(ctx api.Context, pod *api.PersistentVolumeClaim) error {
|
||||||
|
_, err := s.Create(ctx, pod)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *storage) UpdatePersistentVolumeClaim(ctx api.Context, pod *api.PersistentVolumeClaim) error {
|
||||||
|
_, _, err := s.Update(ctx, pod)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *storage) DeletePersistentVolumeClaim(ctx api.Context, podID string) error {
|
||||||
|
_, err := s.Delete(ctx, podID, nil)
|
||||||
|
return err
|
||||||
|
}
|
109
pkg/registry/persistentvolumeclaim/rest.go
Normal file
109
pkg/registry/persistentvolumeclaim/rest.go
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 persistentvolumeclaim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/fielderrors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// persistentvolumeclaimStrategy implements behavior for PersistentVolumeClaim objects
|
||||||
|
type persistentvolumeclaimStrategy struct {
|
||||||
|
runtime.ObjectTyper
|
||||||
|
api.NameGenerator
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strategy is the default logic that applies when creating and updating PersistentVolumeClaim
|
||||||
|
// objects via the REST API.
|
||||||
|
var Strategy = persistentvolumeclaimStrategy{api.Scheme, api.SimpleNameGenerator}
|
||||||
|
|
||||||
|
// NamespaceScoped is true for persistentvolumeclaims.
|
||||||
|
func (persistentvolumeclaimStrategy) NamespaceScoped() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetBeforeCreate clears fields that are not allowed to be set by end users on creation.
|
||||||
|
func (persistentvolumeclaimStrategy) PrepareForCreate(obj runtime.Object) {
|
||||||
|
pv := obj.(*api.PersistentVolumeClaim)
|
||||||
|
pv.Status = api.PersistentVolumeClaimStatus{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
|
||||||
|
func (persistentvolumeclaimStrategy) PrepareForUpdate(obj, old runtime.Object) {
|
||||||
|
newPvc := obj.(*api.PersistentVolumeClaim)
|
||||||
|
oldPvc := obj.(*api.PersistentVolumeClaim)
|
||||||
|
newPvc.Status = oldPvc.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates a new persistentvolumeclaim.
|
||||||
|
func (persistentvolumeclaimStrategy) Validate(obj runtime.Object) fielderrors.ValidationErrorList {
|
||||||
|
pvc := obj.(*api.PersistentVolumeClaim)
|
||||||
|
return validation.ValidatePersistentVolumeClaim(pvc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllowCreateOnUpdate is false for persistentvolumeclaims.
|
||||||
|
func (persistentvolumeclaimStrategy) AllowCreateOnUpdate() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateUpdate is the default update validation for an end user.
|
||||||
|
func (persistentvolumeclaimStrategy) ValidateUpdate(obj, old runtime.Object) fielderrors.ValidationErrorList {
|
||||||
|
return validation.ValidatePersistentVolumeClaimUpdate(obj.(*api.PersistentVolumeClaim), old.(*api.PersistentVolumeClaim))
|
||||||
|
}
|
||||||
|
|
||||||
|
type persistentvolumeclaimStatusStrategy struct {
|
||||||
|
persistentvolumeclaimStrategy
|
||||||
|
}
|
||||||
|
|
||||||
|
var StatusStrategy = persistentvolumeclaimStatusStrategy{Strategy}
|
||||||
|
|
||||||
|
func (persistentvolumeclaimStatusStrategy) PrepareForUpdate(obj, old runtime.Object) {
|
||||||
|
newPvc := obj.(*api.PersistentVolumeClaim)
|
||||||
|
oldPvc := obj.(*api.PersistentVolumeClaim)
|
||||||
|
newPvc.Spec = oldPvc.Spec
|
||||||
|
}
|
||||||
|
|
||||||
|
func (persistentvolumeclaimStatusStrategy) ValidateUpdate(obj, old runtime.Object) fielderrors.ValidationErrorList {
|
||||||
|
return validation.ValidatePersistentVolumeClaimStatusUpdate(obj.(*api.PersistentVolumeClaim), old.(*api.PersistentVolumeClaim))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchPersistentVolumeClaim returns a generic matcher for a given label and field selector.
|
||||||
|
func MatchPersistentVolumeClaim(label labels.Selector, field fields.Selector) generic.Matcher {
|
||||||
|
return generic.MatcherFunc(func(obj runtime.Object) (bool, error) {
|
||||||
|
persistentvolumeclaimObj, ok := obj.(*api.PersistentVolumeClaim)
|
||||||
|
if !ok {
|
||||||
|
return false, fmt.Errorf("not a persistentvolumeclaim")
|
||||||
|
}
|
||||||
|
fields := PersistentVolumeClaimToSelectableFields(persistentvolumeclaimObj)
|
||||||
|
return label.Matches(labels.Set(persistentvolumeclaimObj.Labels)) && field.Matches(fields), nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// PersistentVolumeClaimToSelectableFields returns a label set that represents the object
|
||||||
|
// TODO: fields are not labels, and the validation rules for them do not apply.
|
||||||
|
func PersistentVolumeClaimToSelectableFields(persistentvolumeclaim *api.PersistentVolumeClaim) labels.Set {
|
||||||
|
return labels.Set{
|
||||||
|
"name": persistentvolumeclaim.Name,
|
||||||
|
}
|
||||||
|
}
|
54
pkg/volume/util.go
Normal file
54
pkg/volume/util.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 volume
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetAccessModesAsString(modes []api.AccessModeType) string {
|
||||||
|
modesAsString := ""
|
||||||
|
|
||||||
|
if contains(modes, api.ReadWriteOnce) {
|
||||||
|
appendAccessMode(modesAsString, "RWO")
|
||||||
|
}
|
||||||
|
if contains(modes, api.ReadOnlyMany) {
|
||||||
|
appendAccessMode(modesAsString, "ROX")
|
||||||
|
}
|
||||||
|
if contains(modes, api.ReadWriteMany) {
|
||||||
|
appendAccessMode(modesAsString, "RWX")
|
||||||
|
}
|
||||||
|
|
||||||
|
return modesAsString
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendAccessMode(modes, mode string) string {
|
||||||
|
if modes != "" {
|
||||||
|
modes += ","
|
||||||
|
}
|
||||||
|
modes += mode
|
||||||
|
return modes
|
||||||
|
}
|
||||||
|
|
||||||
|
func contains(modes []api.AccessModeType, mode api.AccessModeType) bool {
|
||||||
|
for _, m := range modes {
|
||||||
|
if m == mode {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user