-
Notifications
You must be signed in to change notification settings - Fork 40k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PersistentVolume Provisioner Controller #14537
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,12 +21,18 @@ import ( | |
// This should probably be part of some configuration fed into the build for a | ||
// given binary target. | ||
|
||
"fmt" | ||
|
||
//Cloud providers | ||
_ "k8s.io/kubernetes/pkg/cloudprovider/providers" | ||
|
||
// Volume plugins | ||
"k8s.io/kubernetes/pkg/cloudprovider" | ||
"k8s.io/kubernetes/pkg/util/io" | ||
"k8s.io/kubernetes/pkg/volume" | ||
"k8s.io/kubernetes/pkg/volume/aws_ebs" | ||
"k8s.io/kubernetes/pkg/volume/cinder" | ||
"k8s.io/kubernetes/pkg/volume/gce_pd" | ||
"k8s.io/kubernetes/pkg/volume/host_path" | ||
"k8s.io/kubernetes/pkg/volume/nfs" | ||
|
||
|
@@ -51,7 +57,7 @@ func ProbeRecyclableVolumePlugins(flags VolumeConfigFlags) []volume.VolumePlugin | |
RecyclerTimeoutIncrement: flags.PersistentVolumeRecyclerIncrementTimeoutHostPath, | ||
RecyclerPodTemplate: volume.NewPersistentVolumeRecyclerPodTemplate(), | ||
} | ||
if err := attemptToLoadRecycler(flags.PersistentVolumeRecyclerPodTemplateFilePathHostPath, &hostPathConfig); err != nil { | ||
if err := AttemptToLoadRecycler(flags.PersistentVolumeRecyclerPodTemplateFilePathHostPath, &hostPathConfig); err != nil { | ||
glog.Fatalf("Could not create hostpath recycler pod from file %s: %+v", flags.PersistentVolumeRecyclerPodTemplateFilePathHostPath, err) | ||
} | ||
allPlugins = append(allPlugins, host_path.ProbeVolumePlugins(hostPathConfig)...) | ||
|
@@ -61,18 +67,49 @@ func ProbeRecyclableVolumePlugins(flags VolumeConfigFlags) []volume.VolumePlugin | |
RecyclerTimeoutIncrement: flags.PersistentVolumeRecyclerIncrementTimeoutNFS, | ||
RecyclerPodTemplate: volume.NewPersistentVolumeRecyclerPodTemplate(), | ||
} | ||
if err := attemptToLoadRecycler(flags.PersistentVolumeRecyclerPodTemplateFilePathNFS, &nfsConfig); err != nil { | ||
if err := AttemptToLoadRecycler(flags.PersistentVolumeRecyclerPodTemplateFilePathNFS, &nfsConfig); err != nil { | ||
glog.Fatalf("Could not create NFS recycler pod from file %s: %+v", flags.PersistentVolumeRecyclerPodTemplateFilePathNFS, err) | ||
} | ||
allPlugins = append(allPlugins, nfs.ProbeVolumePlugins(nfsConfig)...) | ||
|
||
allPlugins = append(allPlugins, aws_ebs.ProbeVolumePlugins()...) | ||
allPlugins = append(allPlugins, gce_pd.ProbeVolumePlugins()...) | ||
allPlugins = append(allPlugins, cinder.ProbeVolumePlugins()...) | ||
|
||
return allPlugins | ||
} | ||
|
||
// attemptToLoadRecycler tries decoding a pod from a filepath for use as a recycler for a volume. | ||
// NewVolumeProvisioner returns a volume provisioner to use when running in a cloud or development environment. | ||
// The beta implementation of provisioning allows 1 implied provisioner per cloud, until we allow configuration of many. | ||
// We explicitly map clouds to volume plugins here which allows us to configure many later without backwards compatibility issues. | ||
// Not all cloudproviders have provisioning capability, which is the reason for the bool in the return to tell the caller to expect one or not. | ||
func NewVolumeProvisioner(cloud cloudprovider.Interface, flags VolumeConfigFlags) (volume.ProvisionableVolumePlugin, error) { | ||
switch { | ||
case cloud == nil && flags.EnableHostPathProvisioning: | ||
return getProvisionablePluginFromVolumePlugins(host_path.ProbeVolumePlugins(volume.VolumeConfig{})) | ||
// case cloud != nil && aws.ProviderName == cloud.ProviderName(): | ||
// return getProvisionablePluginFromVolumePlugins(aws_ebs.ProbeVolumePlugins()) | ||
// case cloud != nil && gce.ProviderName == cloud.ProviderName(): | ||
// return getProvisionablePluginFromVolumePlugins(gce_pd.ProbeVolumePlugins()) | ||
// case cloud != nil && openstack.ProviderName == cloud.ProviderName(): | ||
// return getProvisionablePluginFromVolumePlugins(cinder.ProbeVolumePlugins()) | ||
} | ||
return nil, nil | ||
} | ||
|
||
func getProvisionablePluginFromVolumePlugins(plugins []volume.VolumePlugin) (volume.ProvisionableVolumePlugin, error) { | ||
for _, plugin := range plugins { | ||
if provisonablePlugin, ok := plugin.(volume.ProvisionableVolumePlugin); ok { | ||
return provisonablePlugin, nil | ||
} | ||
} | ||
return nil, fmt.Errorf("ProvisionablePlugin expected but not found in %#v: ", plugins) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. kube-controller-manager with just this patchset (without provisioners in separate PRs) will always fail with "ProvisionablePlugin expected but not found in []volume.VolumePlugin{(*gce_pd.gcePersistentDiskPlugin)(0xc208564e70)}", as the returned volume plugin does not implement ProvisionableVolumePlugin yet. I expect e2e test will always fail. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Per our IRC conversation, I downgraded the error in controllermanager from Fatal to Error. |
||
} | ||
|
||
// AttemptToLoadRecycler tries decoding a pod from a filepath for use as a recycler for a volume. | ||
// If successful, this method will set the recycler on the config. | ||
// If unsucessful, an error is returned. | ||
func attemptToLoadRecycler(path string, config *volume.VolumeConfig) error { | ||
// If unsuccessful, an error is returned. Function is exported for reuse downstream. | ||
func AttemptToLoadRecycler(path string, config *volume.VolumeConfig) error { | ||
if path != "" { | ||
recyclerPod, err := io.LoadPodFromFile(path) | ||
if err != nil { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
<!-- BEGIN MUNGE: UNVERSIONED_WARNING --> | ||
|
||
<!-- BEGIN STRIP_FOR_RELEASE --> | ||
|
||
<img src="http://kubernetes.io/img/warning.png" alt="WARNING" | ||
width="25" height="25"> | ||
<img src="http://kubernetes.io/img/warning.png" alt="WARNING" | ||
width="25" height="25"> | ||
<img src="http://kubernetes.io/img/warning.png" alt="WARNING" | ||
width="25" height="25"> | ||
<img src="http://kubernetes.io/img/warning.png" alt="WARNING" | ||
width="25" height="25"> | ||
<img src="http://kubernetes.io/img/warning.png" alt="WARNING" | ||
width="25" height="25"> | ||
|
||
<h2>PLEASE NOTE: This document applies to the HEAD of the source tree</h2> | ||
|
||
If you are using a released version of Kubernetes, you should | ||
refer to the docs that go with that version. | ||
|
||
<strong> | ||
The latest release of this document can be found | ||
[here](http://releases.k8s.io/release-1.1/examples/experimental/persistent-volume-provisioning/README.md). | ||
|
||
Documentation for other releases can be found at | ||
[releases.k8s.io](http://releases.k8s.io). | ||
</strong> | ||
-- | ||
|
||
<!-- END STRIP_FOR_RELEASE --> | ||
|
||
<!-- END MUNGE: UNVERSIONED_WARNING --> | ||
|
||
## Persistent Volume Provisioning | ||
|
||
This example shows how to use experimental persistent volume provisioning. | ||
|
||
### Pre-requisites | ||
|
||
This example assumes that you have an understanding of Kubernetes administration and can modify the | ||
scripts that launch kube-controller-manager. | ||
|
||
### Admin Configuration | ||
|
||
No configuration is required by the admin! 3 cloud providers will be provided in the alpha version | ||
of this feature: EBS, GCE, and Cinder. | ||
|
||
When Kubernetes is running in one of those clouds, there will be an implied provisioner. | ||
There is no provisioner when running outside of any of those 3 cloud providers. | ||
|
||
A fourth provisioner is included for testing and development only. It creates HostPath volumes, | ||
which will never work outside of a single node cluster. It is not supported in any way except for | ||
local for testing and development. | ||
|
||
|
||
### User provisioning requests | ||
|
||
Users request dynamically provisioned storage by including a storage class in their `PersistentVolumeClaim`. | ||
The annotation `volume.alpha.kubernetes.io/storage-class` is used to access this experimental feature. | ||
In the future, admins will be able to define many storage classes. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a note here saying that the value of the annotation doesn't matter. |
||
The storage class may remain in an annotation or become a field on the claim itself. | ||
|
||
> The value of the storage-class annotation does not matter in the alpha version of this feature. There is | ||
a single implied provisioner per cloud (which creates 1 kind of volume in the provider). The full version of the feature | ||
will require that this value matches what is configured by the administrator. | ||
|
||
``` | ||
{ | ||
"kind": "PersistentVolumeClaim", | ||
"apiVersion": "v1", | ||
"metadata": { | ||
"name": "claim1", | ||
"annotations": { | ||
"volume.alpha.kubernetes.io/storage-class": "foo" | ||
} | ||
}, | ||
"spec": { | ||
"accessModes": [ | ||
"ReadWriteOnce" | ||
], | ||
"resources": { | ||
"requests": { | ||
"storage": "3Gi" | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
### Sample output | ||
|
||
This example uses HostPath but any provisioner would follow the same flow. | ||
|
||
First we note there are no Persistent Volumes in the cluster. After creating a claim, we see a new PV is created | ||
and automatically bound to the claim requesting storage. | ||
|
||
|
||
``` | ||
$ kubectl get pv | ||
|
||
$ kubectl create -f examples/experimental/persistent-volume-provisioning/claim1.json | ||
I1012 13:07:57.666759 22875 decoder.go:141] decoding stream as JSON | ||
persistentvolumeclaim "claim1" created | ||
|
||
$ kubectl get pv | ||
NAME LABELS CAPACITY ACCESSMODES STATUS CLAIM REASON AGE | ||
pv-hostpath-r6z5o createdby=hostpath-dynamic-provisioner 3Gi RWO Bound default/claim1 2s | ||
|
||
$ kubectl get pvc | ||
NAME LABELS STATUS VOLUME CAPACITY ACCESSMODES AGE | ||
claim1 <none> Bound pv-hostpath-r6z5o 3Gi RWO 7s | ||
|
||
# delete the claim to release the volume | ||
$ kubectl delete pvc claim1 | ||
persistentvolumeclaim "claim1" deleted | ||
|
||
# the volume is deleted in response to being release of its claim | ||
$ kubectl get pv | ||
|
||
``` | ||
|
||
<!-- BEGIN MUNGE: GENERATED_ANALYTICS --> | ||
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/examples/experimental/persistent-volume-provisioning/README.md?pixel)]() | ||
<!-- END MUNGE: GENERATED_ANALYTICS --> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"kind": "PersistentVolumeClaim", | ||
"apiVersion": "v1", | ||
"metadata": { | ||
"name": "claim1", | ||
"annotations": { | ||
"volume.alpha.kubernetes.io/storage-class": "foo" | ||
} | ||
}, | ||
"spec": { | ||
"accessModes": [ | ||
"ReadWriteOnce" | ||
], | ||
"resources": { | ||
"requests": { | ||
"storage": "3Gi" | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"kind": "PersistentVolumeClaim", | ||
"apiVersion": "v1", | ||
"metadata": { | ||
"name": "claim2", | ||
"annotations": { | ||
"volume.alpha.kubernetes.io/storage-class": "bar" | ||
} | ||
}, | ||
"spec": { | ||
"accessModes": [ | ||
"ReadWriteOnce" | ||
], | ||
"resources": { | ||
"requests": { | ||
"storage": "3Gi" | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are no recyclers defined for these plugins yet. Will that break anything?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. All is well.