Skip to content

Commit

Permalink
Recycler framework
Browse files Browse the repository at this point in the history
  • Loading branch information
markturansky committed May 19, 2015
1 parent f85e6bc commit 7f8ef51
Show file tree
Hide file tree
Showing 26 changed files with 604 additions and 15 deletions.
5 changes: 5 additions & 0 deletions cmd/kube-controller-manager/app/controllermanager.go
Expand Up @@ -246,6 +246,11 @@ func (s *CMServer) Run(_ []string) error {

pvclaimBinder := volumeclaimbinder.NewPersistentVolumeClaimBinder(kubeClient, s.PVClaimBinderSyncPeriod)
pvclaimBinder.Run()
pvRecycler, err := volumeclaimbinder.NewPersistentVolumeRecycler(kubeClient, s.PVClaimBinderSyncPeriod, ProbePersistentVolumePlugins())
if err != nil {
glog.Errorf("Failed to start persistent volume recycler: %+v", err)
}
pvRecycler.Run()

if len(s.ServiceAccountKeyFile) > 0 {
privateKey, err := serviceaccount.ReadPrivateKey(s.ServiceAccountKeyFile)
Expand Down
28 changes: 28 additions & 0 deletions cmd/kube-controller-manager/app/plugins.go
Expand Up @@ -20,11 +20,39 @@ import (
// This file exists to force the desired plugin implementations to be linked.
// This should probably be part of some configuration fed into the build for a
// given binary target.

//Cloud providers
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/aws"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/gce"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/mesos"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/openstack"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/ovirt"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/rackspace"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/vagrant"

// Volume plugins
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume"
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume/aws_ebs"
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume/gce_pd"
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume/glusterfs"
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume/host_path"
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume/iscsi"
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume/nfs"
)

// ProbeVolumePlugins collects all persistent volume plugins into an easy to use list.
func ProbePersistentVolumePlugins() []volume.VolumePlugin {
allPlugins := []volume.VolumePlugin{}

// The list of plugins to probe is decided by the kubelet binary, not
// by dynamic linking or other "magic". Plugins will be analyzed and
// initialized later.
allPlugins = append(allPlugins, aws_ebs.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, gce_pd.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, host_path.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, nfs.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, iscsi.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, glusterfs.ProbeVolumePlugins()...)

return allPlugins
}
3 changes: 2 additions & 1 deletion examples/persistent-volumes/volumes/local-02.yaml
Expand Up @@ -6,8 +6,9 @@ metadata:
type: local
spec:
capacity:
storage: 5Gi
storage: 8Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/tmp/data02"
reclamationPolicy: Recycle
2 changes: 2 additions & 0 deletions pkg/api/testing/fuzzer.go
Expand Up @@ -232,6 +232,8 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
c.FuzzNoCustom(pv) // fuzz self without calling this function again
types := []api.PersistentVolumePhase{api.VolumePending, api.VolumeBound, api.VolumeReleased, api.VolumeAvailable}
pv.Status.Phase = types[c.Rand.Intn(len(types))]
reclamationPolicies := []api.ReclamationPolicy{api.DeleteOnRelease, api.RecycleOnRelease, api.RetainOnRelease}
pv.Spec.ReclamationPolicy = reclamationPolicies[c.Rand.Intn(len(reclamationPolicies))]
},
func(pvc *api.PersistentVolumeClaim, c fuzz.Continue) {
c.FuzzNoCustom(pvc) // fuzz self without calling this function again
Expand Down
22 changes: 22 additions & 0 deletions pkg/api/types.go
Expand Up @@ -256,8 +256,25 @@ type PersistentVolumeSpec struct {
// ClaimRef is expected to be non-nil when bound.
// claim.VolumeName is the authoritative bind between PV and PVC.
ClaimRef *ObjectReference `json:"claimRef,omitempty"`
// ReclamationPolicy represents what happens to a persistent volume when released from its claim.
ReclamationPolicy ReclamationPolicy `json:"reclamationPolicy,omitempty"`
}

// ReclamationPolicy describes a policy for end-of-life maintenance of persistent volumes
type ReclamationPolicy string

const (
// RecycleOnRelease means the volume will be recycled back into the pool of unbound persistent volumes on release from its claim.
// The volume plugin must support Recycling.
RecycleOnRelease ReclamationPolicy = "Recycle"
// DeleteOnRelease means the volume will be deleted from Kubernetes on release from its claim.
// The volume plugin must support Deletion.
DeleteOnRelease ReclamationPolicy = "Delete"
// RetainOnRelease means the volume will left in its current phase (Released) for manual reclamation by the administrator.
// The default policy is Retain.
RetainOnRelease ReclamationPolicy = "Retain"
)

type PersistentVolumeStatus struct {
// Phase indicates if a volume is available, bound to a claim, or released by a claim
Phase PersistentVolumePhase `json:"phase,omitempty"`
Expand Down Expand Up @@ -330,6 +347,11 @@ const (
// used for PersistentVolumes where the bound PersistentVolumeClaim was deleted
// released volumes must be recycled before becoming available again
VolumeReleased PersistentVolumePhase = "Released"
// used for PersistentVolumes that have been recycled and can be made available
// again to new PersistentVolumeClaims
VolumeRecycled PersistentVolumePhase = "Recycled"
// used for PersistentVolumes that have been removed from the infrastructure
VolumeDeleted PersistentVolumePhase = "Deleted"
)

type PersistentVolumeClaimPhase string
Expand Down
2 changes: 2 additions & 0 deletions pkg/api/v1/conversion_generated.go
Expand Up @@ -2570,6 +2570,7 @@ func convert_v1_PersistentVolumeSpec_To_api_PersistentVolumeSpec(in *PersistentV
} else {
out.ClaimRef = nil
}
out.ReclamationPolicy = api.ReclamationPolicy(in.ReclamationPolicy)
return nil
}

Expand Down Expand Up @@ -2608,6 +2609,7 @@ func convert_api_PersistentVolumeSpec_To_v1_PersistentVolumeSpec(in *api.Persist
} else {
out.ClaimRef = nil
}
out.ReclamationPolicy = ReclamationPolicy(in.ReclamationPolicy)
return nil
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/api/v1/defaults.go
Expand Up @@ -106,6 +106,9 @@ func addDefaultingFuncs() {
if obj.Status.Phase == "" {
obj.Status.Phase = VolumePending
}
if obj.Spec.ReclamationPolicy == "" {
obj.Spec.ReclamationPolicy = RetainOnRelease
}
},
func(obj *PersistentVolumeClaim) {
if obj.Status.Phase == "" {
Expand Down
22 changes: 22 additions & 0 deletions pkg/api/v1/types.go
Expand Up @@ -273,8 +273,25 @@ type PersistentVolumeSpec struct {
// ClaimRef is expected to be non-nil when bound.
// claim.VolumeName is the authoritative bind between PV and PVC.
ClaimRef *ObjectReference `json:"claimRef,omitempty" description:"when bound, a reference to the bound claim"`
// ReclamationPolicy represents what happens to a persistent volume when released from its claim.
ReclamationPolicy ReclamationPolicy `json:"reclamationPolicy,omitempty" description:"reclamationPolicy is what happens to a volume when released from its claim; one of Recycle, Delete, Retain. Default is Retain."`
}

// ReclamationPolicy describes a policy for end-of-life maintenance of persistent volumes
type ReclamationPolicy string

const (
// RecycleOnRelease means the volume will be recycled back into the pool of unbound persistent volumes on release from its claim.
// The volume plugin must support Recycling.
RecycleOnRelease ReclamationPolicy = "Recycle"
// DeleteOnRelease means the volume will be deleted from Kubernetes on release from its claim.
// The volume plugin must support Deletion.
DeleteOnRelease ReclamationPolicy = "Delete"
// RetainOnRelease means the volume will left in its current phase (Released) for manual reclamation by the administrator.
// The default policy is Retain.
RetainOnRelease ReclamationPolicy = "Retain"
)

type PersistentVolumeStatus struct {
// Phase indicates if a volume is available, bound to a claim, or released by a claim
Phase PersistentVolumePhase `json:"phase,omitempty" description:"the current phase of a persistent volume"`
Expand Down Expand Up @@ -347,6 +364,11 @@ const (
// used for PersistentVolumes where the bound PersistentVolumeClaim was deleted
// released volumes must be recycled before becoming available again
VolumeReleased PersistentVolumePhase = "Released"
// used for PersistentVolumes that have been recycled and can be made available
// again to new PersistentVolumeClaims
VolumeRecycled PersistentVolumePhase = "Recycled"
// used for PersistentVolumes that have been removed from the infrastructure
VolumeDeleted PersistentVolumePhase = "Deleted"
)

type PersistentVolumeClaimPhase string
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/v1beta1/defaults.go
Expand Up @@ -116,6 +116,9 @@ func addDefaultingFuncs() {
if obj.Status.Phase == "" {
obj.Status.Phase = VolumePending
}
if obj.Spec.ReclamationPolicy == "" {
obj.Spec.ReclamationPolicy = RetainOnRelease
}
},
func(obj *PersistentVolumeClaim) {
if obj.Status.Phase == "" {
Expand Down
22 changes: 22 additions & 0 deletions pkg/api/v1beta1/types.go
Expand Up @@ -181,8 +181,25 @@ type PersistentVolumeSpec struct {
AccessModes []PersistentVolumeAccessMode `json:"accessModes,omitempty" description:"all ways the volume can be mounted"`
// ClaimRef is a non-binding reference to the claim bound to this volume
ClaimRef *ObjectReference `json:"claimRef,omitempty" description:"when bound, a reference to the bound claim"`
// ReclamationPolicy represents what happens to a persistent volume when released from its claim.
ReclamationPolicy ReclamationPolicy `json:"reclamationPolicy,omitempty" description:"reclamationPolicy is what happens to a volume when released from its claim; one of Recycle, Delete, Retain. Default is Retain."`
}

// ReclamationPolicy describes a policy for end-of-life maintenance of persistent volumes
type ReclamationPolicy string

const (
// RecycleOnRelease means the volume will be recycled back into the pool of unbound persistent volumes on release from its claim.
// The volume plugin must support Recycling.
RecycleOnRelease ReclamationPolicy = "Recycle"
// DeleteOnRelease means the volume will be deleted from Kubernetes on release from its claim.
// The volume plugin must support Deletion.
DeleteOnRelease ReclamationPolicy = "Delete"
// RetainOnRelease means the volume will left in its current phase (Released) for manual reclamation by the administrator.
// The default policy is Retain.
RetainOnRelease ReclamationPolicy = "Retain"
)

type PersistentVolumeStatus struct {
// Phase indicates if a volume is available, bound to a claim, or released by a claim
Phase PersistentVolumePhase `json:"phase,omitempty" description:"the current phase of a persistent volume"`
Expand Down Expand Up @@ -252,6 +269,11 @@ const (
// used for PersistentVolumes where the bound PersistentVolumeClaim was deleted
// released volumes must be recycled before becoming available again
VolumeReleased PersistentVolumePhase = "Released"
// used for PersistentVolumes that have been recycled and can be made available
// again to new PersistentVolumeClaims
VolumeRecycled PersistentVolumePhase = "Recycled"
// used for PersistentVolumes that have been removed from the infrastructure
VolumeDeleted PersistentVolumePhase = "Deleted"
)

type PersistentVolumeClaimPhase string
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/v1beta2/defaults.go
Expand Up @@ -117,6 +117,9 @@ func addDefaultingFuncs() {
if obj.Status.Phase == "" {
obj.Status.Phase = VolumePending
}
if obj.Spec.ReclamationPolicy == "" {
obj.Spec.ReclamationPolicy = RetainOnRelease
}
},
func(obj *PersistentVolumeClaim) {
if obj.Status.Phase == "" {
Expand Down
22 changes: 22 additions & 0 deletions pkg/api/v1beta2/types.go
Expand Up @@ -140,8 +140,25 @@ type PersistentVolumeSpec struct {
// ClaimRef is expected to be non-nil when bound.
// claim.VolumeName is the authoritative bind between PV and PVC.
ClaimRef *ObjectReference `json:"claimRef,omitempty" description:"when bound, a reference to the bound claim"`
// ReclamationPolicy represents what happens to a persistent volume when released from its claim.
ReclamationPolicy ReclamationPolicy `json:"reclamationPolicy,omitempty" description:"reclamationPolicy is what happens to a volume when released from its claim; one of Recycle, Delete, Retain. Default is Retain."`
}

// ReclamationPolicy describes a policy for end-of-life maintenance of persistent volumes
type ReclamationPolicy string

const (
// RecycleOnRelease means the volume will be recycled back into the pool of unbound persistent volumes on release from its claim.
// The volume plugin must support Recycling.
RecycleOnRelease ReclamationPolicy = "Recycle"
// DeleteOnRelease means the volume will be deleted from Kubernetes on release from its claim.
// The volume plugin must support Deletion.
DeleteOnRelease ReclamationPolicy = "Delete"
// RetainOnRelease means the volume will left in its current phase (Released) for manual reclamation by the administrator.
// The default policy is Retain.
RetainOnRelease ReclamationPolicy = "Retain"
)

type PersistentVolumeStatus struct {
// Phase indicates if a volume is available, bound to a claim, or released by a claim
Phase PersistentVolumePhase `json:"phase,omitempty" description:"the current phase of a persistent volume"`
Expand Down Expand Up @@ -211,6 +228,11 @@ const (
// used for PersistentVolumes where the bound PersistentVolumeClaim was deleted
// released volumes must be recycled before becoming available again
VolumeReleased PersistentVolumePhase = "Released"
// used for PersistentVolumes that have been recycled and can be made available
// again to new PersistentVolumeClaims
VolumeRecycled PersistentVolumePhase = "Recycled"
// used for PersistentVolumes that have been removed from the infrastructure
VolumeDeleted PersistentVolumePhase = "Deleted"
)

type PersistentVolumeClaimPhase string
Expand Down
2 changes: 2 additions & 0 deletions pkg/api/v1beta3/conversion_generated.go
Expand Up @@ -2384,6 +2384,7 @@ func convert_v1beta3_PersistentVolumeSpec_To_api_PersistentVolumeSpec(in *Persis
} else {
out.ClaimRef = nil
}
out.ReclamationPolicy = api.ReclamationPolicy(in.ReclamationPolicy)
return nil
}

Expand Down Expand Up @@ -2422,6 +2423,7 @@ func convert_api_PersistentVolumeSpec_To_v1beta3_PersistentVolumeSpec(in *api.Pe
} else {
out.ClaimRef = nil
}
out.ReclamationPolicy = ReclamationPolicy(in.ReclamationPolicy)
return nil
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/api/v1beta3/defaults.go
Expand Up @@ -108,6 +108,9 @@ func addDefaultingFuncs() {
if obj.Status.Phase == "" {
obj.Status.Phase = VolumePending
}
if obj.Spec.ReclamationPolicy == "" {
obj.Spec.ReclamationPolicy = RetainOnRelease
}
},
func(obj *PersistentVolumeClaim) {
if obj.Status.Phase == "" {
Expand Down
22 changes: 22 additions & 0 deletions pkg/api/v1beta3/types.go
Expand Up @@ -273,8 +273,25 @@ type PersistentVolumeSpec struct {
// ClaimRef is expected to be non-nil when bound.
// claim.VolumeName is the authoritative bind between PV and PVC.
ClaimRef *ObjectReference `json:"claimRef,omitempty" description:"when bound, a reference to the bound claim"`
// ReclamationPolicy represents what happens to a persistent volume when released from its claim.
ReclamationPolicy ReclamationPolicy `json:"reclamationPolicy,omitempty" description:"reclamationPolicy is what happens to a volume when released from its claim; one of Recycle, Delete, Retain. Default is Retain."`
}

// ReclamationPolicy describes a policy for end-of-life maintenance of persistent volumes
type ReclamationPolicy string

const (
// RecycleOnRelease means the volume will be recycled back into the pool of unbound persistent volumes on release from its claim.
// The volume plugin must support Recycling.
RecycleOnRelease ReclamationPolicy = "Recycle"
// DeleteOnRelease means the volume will be deleted from Kubernetes on release from its claim.
// The volume plugin must support Deletion.
DeleteOnRelease ReclamationPolicy = "Delete"
// RetainOnRelease means the volume will left in its current phase (Released) for manual reclamation by the administrator.
// The default policy is Retain.
RetainOnRelease ReclamationPolicy = "Retain"
)

type PersistentVolumeStatus struct {
// Phase indicates if a volume is available, bound to a claim, or released by a claim
Phase PersistentVolumePhase `json:"phase,omitempty" description:"the current phase of a persistent volume"`
Expand Down Expand Up @@ -347,6 +364,11 @@ const (
// used for PersistentVolumes where the bound PersistentVolumeClaim was deleted
// released volumes must be recycled before becoming available again
VolumeReleased PersistentVolumePhase = "Released"
// used for PersistentVolumes that have been recycled and can be made available
// again to new PersistentVolumeClaims
VolumeRecycled PersistentVolumePhase = "Recycled"
// used for PersistentVolumes that have been removed from the infrastructure
VolumeDeleted PersistentVolumePhase = "Deleted"
)

type PersistentVolumeClaimPhase string
Expand Down
1 change: 1 addition & 0 deletions pkg/kubectl/describe.go
Expand Up @@ -310,6 +310,7 @@ func (d *PersistentVolumeDescriber) Describe(namespace, name string) (string, er
} else {
fmt.Fprintf(out, "Claim:\t%d\n", "")
}
fmt.Fprintf(out, "Reclamation Policy:\t%d\n", pv.Spec.ReclamationPolicy)
return nil
})
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/kubectl/resource_printer.go
Expand Up @@ -256,7 +256,7 @@ var resourceQuotaColumns = []string{"NAME"}
var namespaceColumns = []string{"NAME", "LABELS", "STATUS"}
var secretColumns = []string{"NAME", "TYPE", "DATA"}
var serviceAccountColumns = []string{"NAME", "SECRETS"}
var persistentVolumeColumns = []string{"NAME", "LABELS", "CAPACITY", "ACCESSMODES", "STATUS", "CLAIM"}
var persistentVolumeColumns = []string{"NAME", "LABELS", "CAPACITY", "ACCESSMODES", "STATUS", "RECLAMATIONPOLICY", "CLAIM"}
var persistentVolumeClaimColumns = []string{"NAME", "LABELS", "STATUS", "VOLUME"}
var componentStatusColumns = []string{"NAME", "STATUS", "MESSAGE", "ERROR"}

Expand Down Expand Up @@ -667,7 +667,7 @@ func printPersistentVolume(pv *api.PersistentVolume, w io.Writer) error {
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)
_, err := fmt.Fprintf(w, "%s\t%s\t%d\t%s\t%s\t%s\t%s\n", pv.Name, pv.Labels, aSize, modesStr, claimRefUID, string(pv.Spec.ReclamationPolicy), pv.Status.Phase)
return err
}

Expand Down
1 change: 1 addition & 0 deletions pkg/registry/persistentvolume/etcd/etcd_test.go
Expand Up @@ -59,6 +59,7 @@ func validNewPersistentVolume(name string) *api.PersistentVolume {
PersistentVolumeSource: api.PersistentVolumeSource{
HostPath: &api.HostPathVolumeSource{Path: "/foo"},
},
ReclamationPolicy: api.RetainOnRelease,
},
Status: api.PersistentVolumeStatus{
Phase: api.VolumePending,
Expand Down
15 changes: 15 additions & 0 deletions pkg/volume/host_path/host_path.go
Expand Up @@ -35,6 +35,7 @@ type hostPathPlugin struct {
}

var _ volume.VolumePlugin = &hostPathPlugin{}
var _ volume.RecyclableVolumePlugin = &hostPathPlugin{}

const (
hostPathPluginName = "kubernetes.io/host-path"
Expand Down Expand Up @@ -99,3 +100,17 @@ func (hp *hostPath) TearDown() error {
func (hp *hostPath) TearDownAt(dir string) error {
return fmt.Errorf("TearDownAt() does not make sense for host paths")
}

func (plugin *hostPathPlugin) NewRecycler(spec *volume.Spec) (volume.Recycler, error) {
if spec.VolumeSource.HostPath != nil {
return &hostPath{spec.VolumeSource.HostPath.Path}, nil
} else {
return &hostPath{spec.PersistentVolumeSource.HostPath.Path}, nil
}
}

// Recycler provides methods to reclaim the volume resource.
func (hp *hostPath) Recycle() error {
// TODO implement "basic scrub" recycler -- busybox w/ "rm -rf" for volume
return nil
}

0 comments on commit 7f8ef51

Please sign in to comment.