Skip to content
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

Revert "Remove alpha provisioning" #41558

Merged
merged 1 commit into from
Feb 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/kube-controller-manager/app/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ go_library(
"//pkg/controller/ttl:go_default_library",
"//pkg/controller/volume/attachdetach:go_default_library",
"//pkg/controller/volume/persistentvolume:go_default_library",
"//pkg/features:go_default_library",
"//pkg/quota/install:go_default_library",
"//pkg/serviceaccount:go_default_library",
"//pkg/util/configz:go_default_library",
Expand Down Expand Up @@ -97,6 +98,7 @@ go_library(
"//vendor:k8s.io/apimachinery/pkg/util/sets",
"//vendor:k8s.io/apimachinery/pkg/util/wait",
"//vendor:k8s.io/apiserver/pkg/server/healthz",
"//vendor:k8s.io/apiserver/pkg/util/feature",
"//vendor:k8s.io/client-go/discovery",
"//vendor:k8s.io/client-go/dynamic",
"//vendor:k8s.io/client-go/kubernetes/typed/core/v1",
Expand Down
5 changes: 5 additions & 0 deletions cmd/kube-controller-manager/app/controllermanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -472,9 +472,14 @@ func StartControllers(controllers map[string]InitFunc, s *options.CMServer, root
glog.Infof("Will not configure cloud provider routes for allocate-node-cidrs: %v, configure-cloud-routes: %v.", s.AllocateNodeCIDRs, s.ConfigureCloudRoutes)
}

alphaProvisioner, err := NewAlphaVolumeProvisioner(cloud, s.VolumeConfiguration)
if err != nil {
return fmt.Errorf("an backward-compatible provisioner could not be created: %v, but one was expected. Provisioning will not work. This functionality is considered an early Alpha version.", err)
}
params := persistentvolumecontroller.ControllerParameters{
KubeClient: clientBuilder.ClientOrDie("persistent-volume-binder"),
SyncPeriod: s.PVClaimBinderSyncPeriod.Duration,
AlphaProvisioner: alphaProvisioner,
VolumePlugins: ProbeControllerVolumePlugins(cloud, s.VolumeConfiguration),
Cloud: cloud,
ClusterName: s.ClusterName,
Expand Down
32 changes: 32 additions & 0 deletions cmd/kube-controller-manager/app/plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ import (

// Volume plugins
"github.com/golang/glog"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/cloudprovider/providers/aws"
"k8s.io/kubernetes/pkg/cloudprovider/providers/azure"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce"
"k8s.io/kubernetes/pkg/cloudprovider/providers/openstack"
"k8s.io/kubernetes/pkg/cloudprovider/providers/photon"
"k8s.io/kubernetes/pkg/cloudprovider/providers/vsphere"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/util/io"
"k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/aws_ebs"
Expand Down Expand Up @@ -134,6 +136,36 @@ func ProbeControllerVolumePlugins(cloud cloudprovider.Interface, config componen
return allPlugins
}

// NewAlphaVolumeProvisioner returns a volume provisioner to use when running in
// a cloud or development environment. The alpha implementation of provisioning
// allows 1 implied provisioner per cloud and is here only for compatibility
// with Kubernetes 1.3
// TODO: remove in Kubernetes 1.5
func NewAlphaVolumeProvisioner(cloud cloudprovider.Interface, config componentconfig.VolumeConfiguration) (volume.ProvisionableVolumePlugin, error) {
switch {
case !utilfeature.DefaultFeatureGate.Enabled(features.DynamicVolumeProvisioning):
return nil, nil
case cloud == nil && config.EnableHostPathProvisioning:
return getProvisionablePluginFromVolumePlugins(host_path.ProbeVolumePlugins(
volume.VolumeConfig{
ProvisioningEnabled: true,
}))
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())
case cloud != nil && vsphere.ProviderName == cloud.ProviderName():
return getProvisionablePluginFromVolumePlugins(vsphere_volume.ProbeVolumePlugins())
case cloud != nil && azure.CloudProviderName == cloud.ProviderName():
return getProvisionablePluginFromVolumePlugins(azure_dd.ProbeVolumePlugins())
case cloud != nil && photon.ProviderName == cloud.ProviderName():
return getProvisionablePluginFromVolumePlugins(photon_pd.ProbeVolumePlugins())
}
return nil, nil
}

func getProvisionablePluginFromVolumePlugins(plugins []volume.VolumePlugin) (volume.ProvisionableVolumePlugin, error) {
for _, plugin := range plugins {
if provisonablePlugin, ok := plugin.(volume.ProvisionableVolumePlugin); ok {
Expand Down
2 changes: 2 additions & 0 deletions examples/cockroachdb/cockroachdb-statefulset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ spec:
volumeClaimTemplates:
- metadata:
name: datadir
annotations:
volume.alpha.kubernetes.io/storage-class: anything
spec:
accessModes:
- "ReadWriteOnce"
Expand Down
2 changes: 2 additions & 0 deletions examples/storage/cassandra/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ spec:
volumeClaimTemplates:
- metadata:
name: cassandra-data
annotations:
volume.alpha.kubernetes.io/storage-class: anything
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
Expand Down
2 changes: 2 additions & 0 deletions examples/storage/cassandra/cassandra-statefulset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ spec:
volumeClaimTemplates:
- metadata:
name: cassandra-data
annotations:
volume.alpha.kubernetes.io/storage-class: anything
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
Expand Down
2 changes: 2 additions & 0 deletions examples/volumes/nfs/provisioner/nfs-server-gce-pv.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ metadata:
name: nfs-pv-provisioning-demo
labels:
demo: nfs-pv-provisioning
annotations:
volume.alpha.kubernetes.io/storage-class: any
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
Expand Down
14 changes: 12 additions & 2 deletions pkg/apis/storage/util/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ import (
const IsDefaultStorageClassAnnotation = "storageclass.beta.kubernetes.io/is-default-class"
const BetaIsDefaultStorageClassAnnotation = "storageclass.beta.kubernetes.io/is-default-class"

// AlphaStorageClassAnnotation represents the previous alpha storage class
// annotation. it's no longer used and held here for posterity.
const AlphaStorageClassAnnotation = "volume.alpha.kubernetes.io/storage-class"

// BetaStorageClassAnnotation represents the beta/previous StorageClass annotation.
// It's currently still used and will be held for backwards compatibility
const BetaStorageClassAnnotation = "volume.beta.kubernetes.io/storage-class"
Expand Down Expand Up @@ -71,28 +75,34 @@ func GetClaimStorageClass(claim *api.PersistentVolumeClaim) string {

// GetStorageClassAnnotation returns the StorageClass value
// if the annotation is set, empty string if not
// TODO: remove Beta when no longer used or needed
// TODO: remove Alpha and Beta when no longer used or needed
func GetStorageClassAnnotation(obj metav1.ObjectMeta) string {
if class, ok := obj.Annotations[StorageClassAnnotation]; ok {
return class
}
if class, ok := obj.Annotations[BetaStorageClassAnnotation]; ok {
return class
}
if class, ok := obj.Annotations[AlphaStorageClassAnnotation]; ok {
return class
}

return ""
}

// HasStorageClassAnnotation returns a boolean
// if the annotation is set
// TODO: remove Beta when no longer used or needed
// TODO: remove Alpha and Beta when no longer used or needed
func HasStorageClassAnnotation(obj metav1.ObjectMeta) bool {
if _, found := obj.Annotations[StorageClassAnnotation]; found {
return found
}
if _, found := obj.Annotations[BetaStorageClassAnnotation]; found {
return found
}
if _, found := obj.Annotations[AlphaStorageClassAnnotation]; found {
return found
}

return false

Expand Down
17 changes: 15 additions & 2 deletions pkg/apis/storage/v1beta1/util/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ import (
const IsDefaultStorageClassAnnotation = "storageclass.beta.kubernetes.io/is-default-class"
const BetaIsDefaultStorageClassAnnotation = "storageclass.beta.kubernetes.io/is-default-class"

// AlphaStorageClassAnnotation represents the previous alpha storage class
// annotation. it's no longer used and held here for posterity.
const AlphaStorageClassAnnotation = "volume.alpha.kubernetes.io/storage-class"

// BetaStorageClassAnnotation represents the beta/previous StorageClass annotation.
// It's currently still used and will be held for backwards compatibility
const BetaStorageClassAnnotation = "volume.beta.kubernetes.io/storage-class"
Expand Down Expand Up @@ -71,28 +75,37 @@ func GetClaimStorageClass(claim *v1.PersistentVolumeClaim) string {

// GetStorageClassAnnotation returns the StorageClass value
// if the annotation is set, empty string if not
// TODO: remove Beta when no longer used or needed
// TODO: remove Alpha and Beta when no longer used or needed
func GetStorageClassAnnotation(obj metav1.ObjectMeta) string {
if class, ok := obj.Annotations[StorageClassAnnotation]; ok {
return class
}
if class, ok := obj.Annotations[BetaStorageClassAnnotation]; ok {
return class
}
if class, ok := obj.Annotations[AlphaStorageClassAnnotation]; ok {
return class
}

return ""
}

// HasStorageClassAnnotation returns a boolean
// if the annotation is set
// TODO: remove Beta when no longer used or needed
// TODO: remove Alpha and Beta when no longer used or needed
func HasStorageClassAnnotation(obj metav1.ObjectMeta) bool {
if _, found := obj.Annotations[StorageClassAnnotation]; found {
return found
}
if _, found := obj.Annotations[BetaStorageClassAnnotation]; found {
return found
}
if _, found := obj.Annotations[AlphaStorageClassAnnotation]; found {
return found
}

return false

}

// IsDefaultAnnotationText returns a pretty Yes/No String if
Expand Down
3 changes: 3 additions & 0 deletions pkg/controller/volume/persistentvolume/framework_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,9 @@ func wrapTestWithPluginCalls(expectedRecycleCalls, expectedDeleteCalls []error,
provisionCalls: expectedProvisionCalls,
}
ctrl.volumePluginMgr.InitPlugins([]vol.VolumePlugin{plugin}, ctrl)
if expectedProvisionCalls != nil {
ctrl.alphaProvisioner = plugin
}
return toWrap(ctrl, reactor, test)
}
}
Expand Down
8 changes: 8 additions & 0 deletions pkg/controller/volume/persistentvolume/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,14 @@ func (pvIndex *persistentVolumeOrderedIndex) findByClaim(claim *v1.PersistentVol
return volume, nil
}

// In Alpha dynamic provisioning, we do now want not match claims
// with existing PVs, findByClaim must find only PVs that are
// pre-bound to the claim (by dynamic provisioning). TODO: remove in
// 1.5
if metav1.HasAnnotation(claim.ObjectMeta, storageutil.AlphaStorageClassAnnotation) {
continue
}

// filter out:
// - volumes bound to another claim
// - volumes whose labels don't match the claim's selector, if specified
Expand Down
34 changes: 34 additions & 0 deletions pkg/controller/volume/persistentvolume/provision_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ var provision2Success = provisionCall{
expectedParameters: class2Parameters,
}

var provisionAlphaSuccess = provisionCall{
ret: nil,
}

// Test single call to syncVolume, expecting provisioning to happen.
// 1. Fill in the controller with initial data
// 2. Call the syncVolume *once*.
Expand Down Expand Up @@ -356,6 +360,36 @@ func TestProvisionSync(t *testing.T) {
runSyncTests(t, tests, storageClasses)
}

func TestAlphaProvisionSync(t *testing.T) {
tests := []controllerTest{
{
// Provision a volume with alpha annotation
"14-1 - successful alpha provisioning",
novolumes,
newVolumeArray("pvc-uid14-1", "1Gi", "uid14-1", "claim14-1", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, annBoundByController, annDynamicallyProvisioned),
newClaimArray("claim14-1", "uid14-1", "1Gi", "", v1.ClaimPending, storageutil.AlphaStorageClassAnnotation),
// Binding will be completed in the next syncClaim
newClaimArray("claim14-1", "uid14-1", "1Gi", "", v1.ClaimPending, storageutil.AlphaStorageClassAnnotation, annStorageProvisioner),
noevents, noerrors, wrapTestWithProvisionCalls([]provisionCall{provisionAlphaSuccess}, testSyncClaim),
},
{
// Provision success - there is already a volume available, still
// we provision a new one when requested.
"14-2 - no alpha provisioning when there is a volume available",
newVolumeArray("volume14-2", "1Gi", "", "", v1.VolumePending, v1.PersistentVolumeReclaimRetain),
[]*v1.PersistentVolume{
newVolume("volume14-2", "1Gi", "", "", v1.VolumePending, v1.PersistentVolumeReclaimRetain),
newVolume("pvc-uid14-2", "1Gi", "uid14-2", "claim14-2", v1.VolumeBound, v1.PersistentVolumeReclaimDelete, annBoundByController, annDynamicallyProvisioned),
},
newClaimArray("claim14-2", "uid14-2", "1Gi", "", v1.ClaimPending, storageutil.AlphaStorageClassAnnotation),
// Binding will be completed in the next syncClaim
newClaimArray("claim14-2", "uid14-2", "1Gi", "", v1.ClaimPending, storageutil.AlphaStorageClassAnnotation, annStorageProvisioner),
noevents, noerrors, wrapTestWithProvisionCalls([]provisionCall{provisionAlphaSuccess}, testSyncClaim),
},
}
runSyncTests(t, tests, []*storage.StorageClass{})
}

// Test multiple calls to syncClaim/syncVolume and periodic sync of all
// volume/claims. The test follows this pattern:
// 0. Load the controller with initial data.
Expand Down
49 changes: 47 additions & 2 deletions pkg/controller/volume/persistentvolume/pv_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,10 @@ type PersistentVolumeController struct {

createProvisionedPVRetryCount int
createProvisionedPVInterval time.Duration

// Provisioner for annAlphaClass.
// TODO: remove in 1.5
alphaProvisioner vol.ProvisionableVolumePlugin
}

// syncClaim is the main controller method to decide what to do with a claim.
Expand Down Expand Up @@ -239,7 +243,7 @@ func (ctrl *PersistentVolumeController) syncUnboundClaim(claim *v1.PersistentVol
glog.V(4).Infof("synchronizing unbound PersistentVolumeClaim[%s]: no volume found", claimToClaimKey(claim))
// No PV could be found
// OBSERVATION: pvc is "Pending", will retry
if storageutil.GetClaimStorageClass(claim) != "" {
if storageutil.GetClaimStorageClass(claim) != "" || metav1.HasAnnotation(claim.ObjectMeta, storageutil.AlphaStorageClassAnnotation) {
if err = ctrl.provisionClaim(claim); err != nil {
return err
}
Expand Down Expand Up @@ -1336,7 +1340,12 @@ func (ctrl *PersistentVolumeController) provisionClaimOperation(claimObj interfa
// Add annBoundByController (used in deleting the volume)
metav1.SetMetaDataAnnotation(&volume.ObjectMeta, annBoundByController, "yes")
metav1.SetMetaDataAnnotation(&volume.ObjectMeta, annDynamicallyProvisioned, plugin.GetPluginName())
metav1.SetMetaDataAnnotation(&volume.ObjectMeta, storageutil.StorageClassAnnotation, claimClass)
// For Alpha provisioning behavior, do not add storage.BetaStorageClassAnnotations for volumes created
// by storage.AlphaStorageClassAnnotation
// TODO: remove this check in 1.5, storage.StorageClassAnnotation will be always non-empty there.
if claimClass != "" {
metav1.SetMetaDataAnnotation(&volume.ObjectMeta, storageutil.StorageClassAnnotation, claimClass)
}

// Try to create the PV object several times
for i := 0; i < ctrl.createProvisionedPVRetryCount; i++ {
Expand Down Expand Up @@ -1438,6 +1447,20 @@ func (ctrl *PersistentVolumeController) newRecyclerEventRecorder(volume *v1.Pers
// It returns either the provisioning plugin or nil when an external
// provisioner is requested.
func (ctrl *PersistentVolumeController) findProvisionablePlugin(claim *v1.PersistentVolumeClaim) (vol.ProvisionableVolumePlugin, *storage.StorageClass, error) {
// TODO: remove this alpha behavior in 1.5
alpha := metav1.HasAnnotation(claim.ObjectMeta, storageutil.AlphaStorageClassAnnotation)
beta := metav1.HasAnnotation(claim.ObjectMeta, storageutil.BetaStorageClassAnnotation)
if alpha && beta {
// Both Alpha and Beta annotations are set. Do beta.
alpha = false
msg := fmt.Sprintf("both %q and %q annotations are present, using %q", storageutil.AlphaStorageClassAnnotation, storageutil.BetaStorageClassAnnotation, storageutil.BetaStorageClassAnnotation)
ctrl.eventRecorder.Event(claim, v1.EventTypeNormal, "ProvisioningIgnoreAlpha", msg)
}
if alpha {
// Fall back to fixed list of provisioner plugins
return ctrl.findAlphaProvisionablePlugin()
}

// provisionClaim() which leads here is never called with claimClass=="", we
// can save some checks.
claimClass := storageutil.GetClaimStorageClass(claim)
Expand Down Expand Up @@ -1465,6 +1488,28 @@ func (ctrl *PersistentVolumeController) findProvisionablePlugin(claim *v1.Persis
return plugin, class, nil
}

// findAlphaProvisionablePlugin returns a volume plugin compatible with
// Kubernetes 1.3.
// TODO: remove in Kubernetes 1.5
func (ctrl *PersistentVolumeController) findAlphaProvisionablePlugin() (vol.ProvisionableVolumePlugin, *storage.StorageClass, error) {
if ctrl.alphaProvisioner == nil {
return nil, nil, fmt.Errorf("cannot find volume plugin for alpha provisioning")
}

// Return a dummy StorageClass instance with no parameters
storageClass := &storage.StorageClass{
TypeMeta: metav1.TypeMeta{
Kind: "StorageClass",
},
ObjectMeta: metav1.ObjectMeta{
Name: "",
},
Provisioner: ctrl.alphaProvisioner.GetPluginName(),
}
glog.V(4).Infof("using alpha provisioner %s", ctrl.alphaProvisioner.GetPluginName())
return ctrl.alphaProvisioner, storageClass, nil
}

// findDeletablePlugin finds a deleter plugin for a given volume. It returns
// either the deleter plugin or nil when an external deleter is requested.
func (ctrl *PersistentVolumeController) findDeletablePlugin(volume *v1.PersistentVolume) (vol.DeletableVolumePlugin, error) {
Expand Down