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 { ...@@ -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 c.FuzzNoCustom(s) // fuzz self without calling this function again
s.Type = api.SecretTypeOpaque 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) { func(s *api.NamespaceSpec, c fuzz.Continue) {
s.Finalizers = []api.FinalizerName{api.FinalizerKubernetes} s.Finalizers = []api.FinalizerName{api.FinalizerKubernetes}
}, },
......
...@@ -317,6 +317,8 @@ const ( ...@@ -317,6 +317,8 @@ const (
type PersistentVolumePhase string type PersistentVolumePhase string
const ( const (
// used for PersistentVolumes that are not available
VolumePending PersistentVolumePhase = "Pending"
// used for PersistentVolumes that are not yet bound // used for PersistentVolumes that are not yet bound
VolumeAvailable PersistentVolumePhase = "Available" VolumeAvailable PersistentVolumePhase = "Available"
// used for PersistentVolumes that are bound // used for PersistentVolumes that are bound
......
...@@ -111,6 +111,16 @@ func init() { ...@@ -111,6 +111,16 @@ func init() {
obj.Type = SecretTypeOpaque 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) { func(obj *Endpoints) {
if obj.Protocol == "" { if obj.Protocol == "" {
obj.Protocol = ProtocolTCP obj.Protocol = ProtocolTCP
......
...@@ -164,6 +164,26 @@ func TestSetDefaultSecret(t *testing.T) { ...@@ -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. // Test that we use "legacy" fields if "modern" fields are not provided.
func TestSetDefaulEndpointsLegacy(t *testing.T) { func TestSetDefaulEndpointsLegacy(t *testing.T) {
in := &current.Endpoints{ in := &current.Endpoints{
......
...@@ -229,6 +229,8 @@ const ( ...@@ -229,6 +229,8 @@ const (
type PersistentVolumePhase string type PersistentVolumePhase string
const ( const (
// used for PersistentVolumes that are not available
VolumePending PersistentVolumePhase = "Pending"
// used for PersistentVolumes that are not yet bound // used for PersistentVolumes that are not yet bound
VolumeAvailable PersistentVolumePhase = "Available" VolumeAvailable PersistentVolumePhase = "Available"
// used for PersistentVolumes that are bound // used for PersistentVolumes that are bound
......
...@@ -112,6 +112,16 @@ func init() { ...@@ -112,6 +112,16 @@ func init() {
obj.Type = SecretTypeOpaque 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) { func(obj *Endpoints) {
if obj.Protocol == "" { if obj.Protocol == "" {
obj.Protocol = ProtocolTCP obj.Protocol = ProtocolTCP
......
...@@ -154,6 +154,26 @@ func TestSetDefaultService(t *testing.T) { ...@@ -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) { func TestSetDefaultSecret(t *testing.T) {
s := &current.Secret{} s := &current.Secret{}
obj2 := roundTrip(t, runtime.Object(s)) obj2 := roundTrip(t, runtime.Object(s))
......
...@@ -198,6 +198,8 @@ const ( ...@@ -198,6 +198,8 @@ const (
type PersistentVolumePhase string type PersistentVolumePhase string
const ( const (
// used for PersistentVolumes that are not available
VolumePending PersistentVolumePhase = "Pending"
// used for PersistentVolumes that are not yet bound // used for PersistentVolumes that are not yet bound
VolumeAvailable PersistentVolumePhase = "Available" VolumeAvailable PersistentVolumePhase = "Available"
// used for PersistentVolumes that are bound // used for PersistentVolumes that are bound
......
...@@ -102,6 +102,16 @@ func init() { ...@@ -102,6 +102,16 @@ func init() {
obj.Type = SecretTypeOpaque 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) { func(obj *Endpoints) {
for i := range obj.Subsets { for i := range obj.Subsets {
ss := &obj.Subsets[i] ss := &obj.Subsets[i]
......
...@@ -174,6 +174,26 @@ func TestSetDefaultSecret(t *testing.T) { ...@@ -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) { func TestSetDefaulEndpointsProtocol(t *testing.T) {
in := &current.Endpoints{Subsets: []current.EndpointSubset{ in := &current.Endpoints{Subsets: []current.EndpointSubset{
{Ports: []current.EndpointPort{{}, {Protocol: "UDP"}, {}}}, {Ports: []current.EndpointPort{{}, {Protocol: "UDP"}, {}}},
......
...@@ -334,6 +334,8 @@ const ( ...@@ -334,6 +334,8 @@ const (
type PersistentVolumePhase string type PersistentVolumePhase string
const ( const (
// used for PersistentVolumes that are not available
VolumePending PersistentVolumePhase = "Pending"
// used for PersistentVolumes that are not yet bound // used for PersistentVolumes that are not yet bound
VolumeAvailable PersistentVolumePhase = "Available" VolumeAvailable PersistentVolumePhase = "Available"
// used for PersistentVolumes that are bound // used for PersistentVolumes that are bound
......
...@@ -58,6 +58,9 @@ func validNewPersistentVolume(name string) *api.PersistentVolume { ...@@ -58,6 +58,9 @@ func validNewPersistentVolume(name string) *api.PersistentVolume {
HostPath: &api.HostPathVolumeSource{Path: "/foo"}, HostPath: &api.HostPathVolumeSource{Path: "/foo"},
}, },
}, },
Status: api.PersistentVolumeStatus{
Phase: api.VolumePending,
},
} }
return pv return pv
} }
......
...@@ -59,6 +59,9 @@ func validNewPersistentVolumeClaim(name, ns string) *api.PersistentVolumeClaim { ...@@ -59,6 +59,9 @@ func validNewPersistentVolumeClaim(name, ns string) *api.PersistentVolumeClaim {
}, },
}, },
}, },
Status: api.PersistentVolumeClaimStatus{
Phase: api.ClaimPending,
},
} }
return pv return pv
} }
......
...@@ -17,12 +17,12 @@ limitations under the License. ...@@ -17,12 +17,12 @@ limitations under the License.
package volumeclaimbinder package volumeclaimbinder
import ( import (
"fmt"
"reflect" "reflect"
"testing" "testing"
"time" "time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "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/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/testclient" "github.com/GoogleCloudPlatform/kubernetes/pkg/client/testclient"
...@@ -51,7 +51,6 @@ func TestRunStop(t *testing.T) { ...@@ -51,7 +51,6 @@ func TestRunStop(t *testing.T) {
} }
func TestExampleObjects(t *testing.T) { func TestExampleObjects(t *testing.T) {
scenarios := map[string]struct { scenarios := map[string]struct {
expected interface{} expected interface{}
}{ }{
...@@ -167,56 +166,7 @@ func TestExampleObjects(t *testing.T) { ...@@ -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) { func TestBindingWithExamples(t *testing.T) {
api.ForTesting_ReferencesAllowBlankSelfLinks = true api.ForTesting_ReferencesAllowBlankSelfLinks = true
o := testclient.NewObjects(api.Scheme) o := testclient.NewObjects(api.Scheme)
if err := testclient.AddObjectsFromPath("../../examples/persistent-volumes/claims/claim-01.yaml", o); err != nil { if err := testclient.AddObjectsFromPath("../../examples/persistent-volumes/claims/claim-01.yaml", o); err != nil {
...@@ -245,7 +195,7 @@ func TestBindingWithExamples(t *testing.T) { ...@@ -245,7 +195,7 @@ func TestBindingWithExamples(t *testing.T) {
} }
volumeIndex.Add(pv) volumeIndex.Add(pv)
syncVolume(mockClient, pv) syncVolume(volumeIndex, mockClient, pv)
if pv.Status.Phase != api.VolumeAvailable { if pv.Status.Phase != api.VolumeAvailable {
t.Errorf("Expected phase %s but got %s", api.VolumeBound, pv.Status.Phase) t.Errorf("Expected phase %s but got %s", api.VolumeBound, pv.Status.Phase)
...@@ -261,10 +211,13 @@ func TestBindingWithExamples(t *testing.T) { ...@@ -261,10 +211,13 @@ func TestBindingWithExamples(t *testing.T) {
t.Errorf("Expected ClaimRef but got nil for volume: %+v\n", pv) 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 { 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 { if pv.Status.Phase != api.VolumeBound {
...@@ -283,7 +236,7 @@ func TestBindingWithExamples(t *testing.T) { ...@@ -283,7 +236,7 @@ func TestBindingWithExamples(t *testing.T) {
// pretend the user deleted their claim // pretend the user deleted their claim
mockClient.claim = nil mockClient.claim = nil
syncVolume(mockClient, pv) syncVolume(volumeIndex, mockClient, pv)
if pv.Status.Phase != api.VolumeReleased { if pv.Status.Phase != api.VolumeReleased {
t.Errorf("Expected phase %s but got %s", api.VolumeReleased, pv.Status.Phase) 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 ...@@ -311,7 +264,7 @@ func (c *mockBinderClient) GetPersistentVolumeClaim(namespace, name string) (*ap
if c.claim != nil { if c.claim != nil {
return c.claim, nil return c.claim, nil
} else { } else {
return nil, fmt.Errorf("Claim does not exist") return nil, errors.NewNotFound("persistentVolume", name)
} }
} }
......
...@@ -76,8 +76,25 @@ func TestPersistentVolumeClaimBinder(t *testing.T) { ...@@ -76,8 +76,25 @@ func TestPersistentVolumeClaimBinder(t *testing.T) {
t.Errorf("expected 3 PVCs, got %#v", len(claims.Items)) t.Errorf("expected 3 PVCs, got %#v", len(claims.Items))
} }
// make sure the binder has caught up // the binder will eventually catch up and set status on Claims
time.Sleep(2 * time.Second) 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() { for _, claim := range createTestClaims() {
claim, err := client.PersistentVolumeClaims(api.NamespaceDefault).Get(claim.Name) claim, err := client.PersistentVolumeClaims(api.NamespaceDefault).Get(claim.Name)
...@@ -86,7 +103,7 @@ func TestPersistentVolumeClaimBinder(t *testing.T) { ...@@ -86,7 +103,7 @@ func TestPersistentVolumeClaimBinder(t *testing.T) {
} }
if (claim.Name == "claim01" || claim.Name == "claim02") && claim.Status.VolumeRef == nil { 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 { if claim.Name == "claim03" && claim.Status.VolumeRef != nil {
t.Errorf("Expected claim03 to be unbound: %v", claim) 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