Commit a04420e5 authored by markturansky's avatar markturansky

Added pending phase for volumes. added defaults for PV/PVC. refactored to…

Added pending phase for volumes. added defaults for PV/PVC. refactored to better phase transitioning in control loops
parent 37d7f3f4
......@@ -201,6 +201,14 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
c.FuzzNoCustom(s) // fuzz self without calling this function again
s.Type = api.SecretTypeOpaque
},
func(pv *api.PersistentVolume, c fuzz.Continue) {
c.FuzzNoCustom(pv) // fuzz self without calling this function again
pv.Status.Phase = api.VolumePending
},
func(pvc *api.PersistentVolumeClaim, c fuzz.Continue) {
c.FuzzNoCustom(pvc) // fuzz self without calling this function again
pvc.Status.Phase = api.ClaimPending
},
func(s *api.NamespaceSpec, c fuzz.Continue) {
s.Finalizers = []api.FinalizerName{api.FinalizerKubernetes}
},
......
......@@ -317,6 +317,8 @@ const (
type PersistentVolumePhase string
const (
// used for PersistentVolumes that are not available
VolumePending PersistentVolumePhase = "Pending"
// used for PersistentVolumes that are not yet bound
VolumeAvailable PersistentVolumePhase = "Available"
// used for PersistentVolumes that are bound
......
......@@ -111,6 +111,16 @@ func init() {
obj.Type = SecretTypeOpaque
}
},
func(obj *PersistentVolume) {
if obj.Status.Phase == "" {
obj.Status.Phase = VolumePending
}
},
func(obj *PersistentVolumeClaim) {
if obj.Status.Phase == "" {
obj.Status.Phase = ClaimPending
}
},
func(obj *Endpoints) {
if obj.Protocol == "" {
obj.Protocol = ProtocolTCP
......
......@@ -164,6 +164,26 @@ func TestSetDefaultSecret(t *testing.T) {
}
}
func TestSetDefaultPersistentVolume(t *testing.T) {
pv := &current.PersistentVolume{}
obj2 := roundTrip(t, runtime.Object(pv))
pv2 := obj2.(*current.PersistentVolume)
if pv2.Status.Phase != current.VolumePending {
t.Errorf("Expected volume phase %v, got %v", current.VolumePending, pv2.Status.Phase)
}
}
func TestSetDefaultPersistentVolumeClaim(t *testing.T) {
pvc := &current.PersistentVolumeClaim{}
obj2 := roundTrip(t, runtime.Object(pvc))
pvc2 := obj2.(*current.PersistentVolumeClaim)
if pvc2.Status.Phase != current.ClaimPending {
t.Errorf("Expected claim phase %v, got %v", current.ClaimPending, pvc2.Status.Phase)
}
}
// Test that we use "legacy" fields if "modern" fields are not provided.
func TestSetDefaulEndpointsLegacy(t *testing.T) {
in := &current.Endpoints{
......
......@@ -229,6 +229,8 @@ const (
type PersistentVolumePhase string
const (
// used for PersistentVolumes that are not available
VolumePending PersistentVolumePhase = "Pending"
// used for PersistentVolumes that are not yet bound
VolumeAvailable PersistentVolumePhase = "Available"
// used for PersistentVolumes that are bound
......
......@@ -112,6 +112,16 @@ func init() {
obj.Type = SecretTypeOpaque
}
},
func(obj *PersistentVolume) {
if obj.Status.Phase == "" {
obj.Status.Phase = VolumePending
}
},
func(obj *PersistentVolumeClaim) {
if obj.Status.Phase == "" {
obj.Status.Phase = ClaimPending
}
},
func(obj *Endpoints) {
if obj.Protocol == "" {
obj.Protocol = ProtocolTCP
......
......@@ -154,6 +154,26 @@ func TestSetDefaultService(t *testing.T) {
}
}
func TestSetDefaultPersistentVolume(t *testing.T) {
pv := &current.PersistentVolume{}
obj2 := roundTrip(t, runtime.Object(pv))
pv2 := obj2.(*current.PersistentVolume)
if pv2.Status.Phase != current.VolumePending {
t.Errorf("Expected volume phase %v, got %v", current.VolumePending, pv2.Status.Phase)
}
}
func TestSetDefaultPersistentVolumeClaim(t *testing.T) {
pvc := &current.PersistentVolumeClaim{}
obj2 := roundTrip(t, runtime.Object(pvc))
pvc2 := obj2.(*current.PersistentVolumeClaim)
if pvc2.Status.Phase != current.ClaimPending {
t.Errorf("Expected claim phase %v, got %v", current.ClaimPending, pvc2.Status.Phase)
}
}
func TestSetDefaultSecret(t *testing.T) {
s := &current.Secret{}
obj2 := roundTrip(t, runtime.Object(s))
......
......@@ -198,6 +198,8 @@ const (
type PersistentVolumePhase string
const (
// used for PersistentVolumes that are not available
VolumePending PersistentVolumePhase = "Pending"
// used for PersistentVolumes that are not yet bound
VolumeAvailable PersistentVolumePhase = "Available"
// used for PersistentVolumes that are bound
......
......@@ -102,6 +102,16 @@ func init() {
obj.Type = SecretTypeOpaque
}
},
func(obj *PersistentVolume) {
if obj.Status.Phase == "" {
obj.Status.Phase = VolumePending
}
},
func(obj *PersistentVolumeClaim) {
if obj.Status.Phase == "" {
obj.Status.Phase = ClaimPending
}
},
func(obj *Endpoints) {
for i := range obj.Subsets {
ss := &obj.Subsets[i]
......
......@@ -174,6 +174,26 @@ func TestSetDefaultSecret(t *testing.T) {
}
}
func TestSetDefaultPersistentVolume(t *testing.T) {
pv := &current.PersistentVolume{}
obj2 := roundTrip(t, runtime.Object(pv))
pv2 := obj2.(*current.PersistentVolume)
if pv2.Status.Phase != current.VolumePending {
t.Errorf("Expected volume phase %v, got %v", current.VolumePending, pv2.Status.Phase)
}
}
func TestSetDefaultPersistentVolumeClaim(t *testing.T) {
pvc := &current.PersistentVolumeClaim{}
obj2 := roundTrip(t, runtime.Object(pvc))
pvc2 := obj2.(*current.PersistentVolumeClaim)
if pvc2.Status.Phase != current.ClaimPending {
t.Errorf("Expected claim phase %v, got %v", current.ClaimPending, pvc2.Status.Phase)
}
}
func TestSetDefaulEndpointsProtocol(t *testing.T) {
in := &current.Endpoints{Subsets: []current.EndpointSubset{
{Ports: []current.EndpointPort{{}, {Protocol: "UDP"}, {}}},
......
......@@ -334,6 +334,8 @@ const (
type PersistentVolumePhase string
const (
// used for PersistentVolumes that are not available
VolumePending PersistentVolumePhase = "Pending"
// used for PersistentVolumes that are not yet bound
VolumeAvailable PersistentVolumePhase = "Available"
// used for PersistentVolumes that are bound
......
......@@ -58,6 +58,9 @@ func validNewPersistentVolume(name string) *api.PersistentVolume {
HostPath: &api.HostPathVolumeSource{Path: "/foo"},
},
},
Status: api.PersistentVolumeStatus{
Phase: api.VolumePending,
},
}
return pv
}
......
......@@ -59,6 +59,9 @@ func validNewPersistentVolumeClaim(name, ns string) *api.PersistentVolumeClaim {
},
},
},
Status: api.PersistentVolumeClaimStatus{
Phase: api.ClaimPending,
},
}
return pv
}
......
......@@ -17,12 +17,12 @@ limitations under the License.
package volumeclaimbinder
import (
"fmt"
"reflect"
"testing"
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/testclient"
......@@ -51,7 +51,6 @@ func TestRunStop(t *testing.T) {
}
func TestExampleObjects(t *testing.T) {
scenarios := map[string]struct {
expected interface{}
}{
......@@ -167,56 +166,7 @@ func TestExampleObjects(t *testing.T) {
}
}
func TestRequiresUpdate(t *testing.T) {
old := &api.PersistentVolume{
Spec: api.PersistentVolumeSpec{
AccessModes: []api.AccessModeType{api.ReadWriteOnce},
Capacity: api.ResourceList{
api.ResourceName(api.ResourceStorage): resource.MustParse("5Gi"),
},
PersistentVolumeSource: api.PersistentVolumeSource{
HostPath: &api.HostPathVolumeSource{
Path: "/tmp/data02",
},
},
},
}
new := &api.PersistentVolume{
Spec: api.PersistentVolumeSpec{
AccessModes: []api.AccessModeType{api.ReadWriteOnce},
Capacity: api.ResourceList{
api.ResourceName(api.ResourceStorage): resource.MustParse("5Gi"),
},
PersistentVolumeSource: api.PersistentVolumeSource{
HostPath: &api.HostPathVolumeSource{
Path: "/tmp/data02",
},
},
ClaimRef: &api.ObjectReference{Name: "foo"},
},
}
if !updateRequired(old, new) {
t.Errorf("Update expected for the new volume with added ClaimRef")
}
old.Spec.ClaimRef = new.Spec.ClaimRef
old.Status.Phase = api.VolumeBound
if !updateRequired(old, new) {
t.Errorf("Update expected for the new volume with added Status")
}
new.Status.Phase = old.Status.Phase
if updateRequired(old, new) {
t.Errorf("No updated expected for identical objects")
}
}
func TestBindingWithExamples(t *testing.T) {
api.ForTesting_ReferencesAllowBlankSelfLinks = true
o := testclient.NewObjects(api.Scheme)
if err := testclient.AddObjectsFromPath("../../examples/persistent-volumes/claims/claim-01.yaml", o); err != nil {
......@@ -245,7 +195,7 @@ func TestBindingWithExamples(t *testing.T) {
}
volumeIndex.Add(pv)
syncVolume(mockClient, pv)
syncVolume(volumeIndex, mockClient, pv)
if pv.Status.Phase != api.VolumeAvailable {
t.Errorf("Expected phase %s but got %s", api.VolumeBound, pv.Status.Phase)
......@@ -261,10 +211,13 @@ func TestBindingWithExamples(t *testing.T) {
t.Errorf("Expected ClaimRef but got nil for volume: %+v\n", pv)
}
syncVolume(mockClient, pv)
// first sync verifies the new bound claim, advances state, triggering update
syncVolume(volumeIndex, mockClient, pv)
// second sync verifies claim, sees missing claim status and builds it
syncVolume(volumeIndex, mockClient, pv)
if claim.Status.VolumeRef == nil {
t.Error("Expected claim to be bound to volume")
t.Fatalf("Expected claim to be bound to volume")
}
if pv.Status.Phase != api.VolumeBound {
......@@ -283,7 +236,7 @@ func TestBindingWithExamples(t *testing.T) {
// pretend the user deleted their claim
mockClient.claim = nil
syncVolume(mockClient, pv)
syncVolume(volumeIndex, mockClient, pv)
if pv.Status.Phase != api.VolumeReleased {
t.Errorf("Expected phase %s but got %s", api.VolumeReleased, pv.Status.Phase)
......@@ -311,7 +264,7 @@ func (c *mockBinderClient) GetPersistentVolumeClaim(namespace, name string) (*ap
if c.claim != nil {
return c.claim, nil
} else {
return nil, fmt.Errorf("Claim does not exist")
return nil, errors.NewNotFound("persistentVolume", name)
}
}
......
......@@ -76,8 +76,25 @@ func TestPersistentVolumeClaimBinder(t *testing.T) {
t.Errorf("expected 3 PVCs, got %#v", len(claims.Items))
}
// make sure the binder has caught up
time.Sleep(2 * time.Second)
// the binder will eventually catch up and set status on Claims
watch, err := client.PersistentVolumeClaims(api.NamespaceDefault).Watch(labels.Everything(), fields.Everything(), "0")
if err != nil {
t.Fatalf("Couldn't subscribe to PersistentVolumeClaims: %v", err)
}
defer watch.Stop()
boundCount := 0
expectedBoundCount := 2
for {
event := <-watch.ResultChan()
claim := event.Object.(*api.PersistentVolumeClaim)
if claim.Status.VolumeRef != nil {
boundCount++
}
if boundCount == expectedBoundCount {
break
}
}
for _, claim := range createTestClaims() {
claim, err := client.PersistentVolumeClaims(api.NamespaceDefault).Get(claim.Name)
......@@ -86,7 +103,7 @@ func TestPersistentVolumeClaimBinder(t *testing.T) {
}
if (claim.Name == "claim01" || claim.Name == "claim02") && claim.Status.VolumeRef == nil {
t.Errorf("Expected claim to be bound: %v", claim)
t.Errorf("Expected claim to be bound: %+v", claim)
}
if claim.Name == "claim03" && claim.Status.VolumeRef != nil {
t.Errorf("Expected claim03 to be unbound: %v", claim)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment