func TestPersistentClaimReadOnlyFlag(t *testing.T) { pv := &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ Name: "pvA", }, Spec: api.PersistentVolumeSpec{ PersistentVolumeSource: api.PersistentVolumeSource{ Glusterfs: &api.GlusterfsVolumeSource{EndpointsName: "ep", Path: "vol", ReadOnly: false}, }, ClaimRef: &api.ObjectReference{ Name: "claimA", }, }, } claim := &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{ Name: "claimA", Namespace: "nsA", }, Spec: api.PersistentVolumeClaimSpec{ VolumeName: "pvA", }, Status: api.PersistentVolumeClaimStatus{ Phase: api.ClaimBound, }, } ep := &api.Endpoints{ ObjectMeta: api.ObjectMeta{ Name: "ep", }, Subsets: []api.EndpointSubset{{ Addresses: []api.EndpointAddress{{IP: "127.0.0.1"}}, Ports: []api.EndpointPort{{"foo", 80, api.ProtocolTCP}}, }}, } o := testclient.NewObjects(api.Scheme, api.Scheme) o.Add(pv) o.Add(claim) o.Add(ep) client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)} plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(), volume.NewFakeVolumeHost("/tmp/fake", client, nil)) plug, _ := plugMgr.FindPluginByName(glusterfsPluginName) // readOnly bool is supplied by persistent-claim volume source when its builder creates other volumes spec := volume.NewSpecFromPersistentVolume(pv, true) pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: types.UID("poduid")}} builder, _ := plug.NewBuilder(spec, pod, volume.VolumeOptions{}, nil) if !builder.IsReadOnly() { t.Errorf("Expected true for builder.IsReadOnly") } }
// NewSimpleFake returns a client that will respond with the provided objects func NewSimpleFake(objects ...runtime.Object) *Fake { o := ktestclient.NewObjects(kapi.Scheme, kapi.Scheme) for _, obj := range objects { if err := o.Add(obj); err != nil { panic(err) } } return &Fake{ReactFn: ktestclient.ObjectReaction(o, latest.RESTMapper)} }
func TestPersistentClaimReadOnlyFlag(t *testing.T) { pv := &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ Name: "pvA", }, Spec: api.PersistentVolumeSpec{ PersistentVolumeSource: api.PersistentVolumeSource{ ISCSI: &api.ISCSIVolumeSource{ TargetPortal: "127.0.0.1:3260", IQN: "iqn.2014-12.server:storage.target01", FSType: "ext4", Lun: 0, }, }, ClaimRef: &api.ObjectReference{ Name: "claimA", }, }, } claim := &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{ Name: "claimA", Namespace: "nsA", }, Spec: api.PersistentVolumeClaimSpec{ VolumeName: "pvA", }, Status: api.PersistentVolumeClaimStatus{ Phase: api.ClaimBound, }, } o := testclient.NewObjects(api.Scheme, api.Scheme) o.Add(pv) o.Add(claim) client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)} plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(), volume.NewFakeVolumeHost("/tmp/fake", client, nil)) plug, _ := plugMgr.FindPluginByName(iscsiPluginName) // readOnly bool is supplied by persistent-claim volume source when its builder creates other volumes spec := volume.NewSpecFromPersistentVolume(pv, true) pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: types.UID("poduid")}} builder, _ := plug.NewBuilder(spec, pod, volume.VolumeOptions{}, nil) if !builder.IsReadOnly() { t.Errorf("Expected true for builder.IsReadOnly") } }
func TestPersistentClaimReadOnlyFlag(t *testing.T) { pv := &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ Name: "pvA", }, Spec: api.PersistentVolumeSpec{ PersistentVolumeSource: api.PersistentVolumeSource{ RBD: &api.RBDVolumeSource{ CephMonitors: []string{"a", "b"}, RBDImage: "bar", FSType: "ext4", }, }, ClaimRef: &api.ObjectReference{ Name: "claimA", }, }, } claim := &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{ Name: "claimA", Namespace: "nsA", }, Spec: api.PersistentVolumeClaimSpec{ VolumeName: "pvA", }, Status: api.PersistentVolumeClaimStatus{ Phase: api.ClaimBound, }, } o := testclient.NewObjects(api.Scheme, api.Scheme) o.Add(pv) o.Add(claim) client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)} plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(), volume.NewFakeVolumeHost("/tmp/fake", client, nil)) plug, _ := plugMgr.FindPluginByName(rbdPluginName) // readOnly bool is supplied by persistent-claim volume source when its builder creates other volumes spec := volume.NewSpecFromPersistentVolume(pv, true) pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: types.UID("poduid")}} builder, _ := plug.NewBuilder(spec, pod, volume.VolumeOptions{}, nil) if !builder.IsReadOnly() { t.Errorf("Expected true for builder.IsReadOnly") } }
func TestNewBuilderClaimNotBound(t *testing.T) { pv := &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ Name: "pvC", }, Spec: api.PersistentVolumeSpec{ PersistentVolumeSource: api.PersistentVolumeSource{ GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}, }, }, } claim := &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{ Name: "claimC", Namespace: "nsA", }, } podVolume := api.VolumeSource{ PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{ ReadOnly: false, ClaimName: "claimC", }, } o := testclient.NewObjects(api.Scheme, api.Scheme) o.Add(pv) o.Add(claim) client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, api.RESTMapper)} plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(testProbeVolumePlugins(), newTestHost(t, client)) plug, err := plugMgr.FindPluginByName("kubernetes.io/persistent-claim") if err != nil { t.Errorf("Can't find the plugin by name") } spec := &volume.Spec{ Name: "vol1", Volume: &api.Volume{VolumeSource: podVolume}, } pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: types.UID("poduid")}} builder, err := plug.NewBuilder(spec, pod, volume.VolumeOptions{}, nil) if builder != nil { t.Errorf("Expected a nil builder if the claim wasn't bound") } }
func TestErrors(t *testing.T) { o := testclient.NewObjects(kapi.Scheme, kapi.Scheme) o.Add(&kapi.List{ Items: []runtime.Object{ &(errors.NewNotFound("DeploymentConfigList", "").(*errors.StatusError).ErrStatus), &(errors.NewForbidden("DeploymentConfigList", "", nil).(*errors.StatusError).ErrStatus), }, }) oc, _ := NewFixtureClients(o) _, err := oc.DeploymentConfigs("test").List(labels.Everything(), fields.Everything()) if !errors.IsNotFound(err) { t.Fatalf("unexpected error: %v", err) } t.Logf("error: %#v", err.(*errors.StatusError).Status()) _, err = oc.DeploymentConfigs("test").List(labels.Everything(), fields.Everything()) if !errors.IsForbidden(err) { t.Fatalf("unexpected error: %v", err) } }
func TestRunStop(t *testing.T) { o := testclient.NewObjects(api.Scheme, api.Scheme) client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, api.RESTMapper)} binder := NewPersistentVolumeClaimBinder(client, 1*time.Second) if len(binder.stopChannels) != 0 { t.Errorf("Non-running binder should not have any stopChannels. Got %v", len(binder.stopChannels)) } binder.Run() if len(binder.stopChannels) != 2 { t.Errorf("Running binder should have exactly 2 stopChannels. Got %v", len(binder.stopChannels)) } binder.Stop() if len(binder.stopChannels) != 0 { t.Errorf("Non-running binder should not have any stopChannels. Got %v", len(binder.stopChannels)) } }
func TestRunStop(t *testing.T) { o := testclient.NewObjects(api.Scheme, api.Scheme) client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, api.RESTMapper)} nsMgr := NewNamespaceManager(client, 1*time.Second) if nsMgr.StopEverything != nil { t.Errorf("Non-running manager should not have a stop channel. Got %v", nsMgr.StopEverything) } nsMgr.Run() if nsMgr.StopEverything == nil { t.Errorf("Running manager should have a stop channel. Got nil") } nsMgr.Stop() if nsMgr.StopEverything != nil { t.Errorf("Non-running manager should not have a stop channel. Got %v", nsMgr.StopEverything) } }
func TestNewClient(t *testing.T) { o := testclient.NewObjects(kapi.Scheme, kapi.Scheme) if err := testclient.AddObjectsFromPath("../../../test/integration/fixtures/test-deployment-config.json", o, kapi.Scheme); err != nil { t.Fatal(err) } oc, _ := NewFixtureClients(o) list, err := oc.DeploymentConfigs("test").List(labels.Everything(), fields.Everything()) if err != nil { t.Fatal(err) } if len(list.Items) != 1 { t.Fatalf("unexpected list %#v", list) } // same result list, err = oc.DeploymentConfigs("test").List(labels.Everything(), fields.Everything()) if err != nil { t.Fatal(err) } if len(list.Items) != 1 { t.Fatalf("unexpected list %#v", list) } t.Logf("list: %#v", list) }
func TestExampleObjects(t *testing.T) { scenarios := map[string]struct { expected interface{} }{ "claims/claim-01.yaml": { expected: &api.PersistentVolumeClaim{ Spec: api.PersistentVolumeClaimSpec{ AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("3Gi"), }, }, }, }, }, "claims/claim-02.yaml": { expected: &api.PersistentVolumeClaim{ Spec: api.PersistentVolumeClaimSpec{ AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("8Gi"), }, }, }, }, }, "volumes/local-01.yaml": { expected: &api.PersistentVolume{ Spec: api.PersistentVolumeSpec{ AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, Capacity: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("10Gi"), }, PersistentVolumeSource: api.PersistentVolumeSource{ HostPath: &api.HostPathVolumeSource{ Path: "/tmp/data01", }, }, }, }, }, "volumes/local-02.yaml": { expected: &api.PersistentVolume{ Spec: api.PersistentVolumeSpec{ AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, Capacity: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("8Gi"), }, PersistentVolumeSource: api.PersistentVolumeSource{ HostPath: &api.HostPathVolumeSource{ Path: "/tmp/data02", }, }, PersistentVolumeReclaimPolicy: api.PersistentVolumeReclaimRecycle, }, }, }, } for name, scenario := range scenarios { o := testclient.NewObjects(api.Scheme, api.Scheme) if err := testclient.AddObjectsFromPath("../../docs/user-guide/persistent-volumes/"+name, o, api.Scheme); err != nil { t.Fatal(err) } client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, api.RESTMapper)} if reflect.TypeOf(scenario.expected) == reflect.TypeOf(&api.PersistentVolumeClaim{}) { pvc, err := client.PersistentVolumeClaims("ns").Get("doesntmatter") if err != nil { t.Errorf("Error retrieving object: %v", err) } expected := scenario.expected.(*api.PersistentVolumeClaim) if pvc.Spec.AccessModes[0] != expected.Spec.AccessModes[0] { t.Errorf("Unexpected mismatch. Got %v wanted %v", pvc.Spec.AccessModes[0], expected.Spec.AccessModes[0]) } aQty := pvc.Spec.Resources.Requests[api.ResourceStorage] bQty := expected.Spec.Resources.Requests[api.ResourceStorage] aSize := aQty.Value() bSize := bQty.Value() if aSize != bSize { t.Errorf("Unexpected mismatch. Got %v wanted %v", aSize, bSize) } } if reflect.TypeOf(scenario.expected) == reflect.TypeOf(&api.PersistentVolume{}) { pv, err := client.PersistentVolumes().Get("doesntmatter") if err != nil { t.Errorf("Error retrieving object: %v", err) } expected := scenario.expected.(*api.PersistentVolume) if pv.Spec.AccessModes[0] != expected.Spec.AccessModes[0] { t.Errorf("Unexpected mismatch. Got %v wanted %v", pv.Spec.AccessModes[0], expected.Spec.AccessModes[0]) } aQty := pv.Spec.Capacity[api.ResourceStorage] bQty := expected.Spec.Capacity[api.ResourceStorage] aSize := aQty.Value() bSize := bQty.Value() if aSize != bSize { t.Errorf("Unexpected mismatch. Got %v wanted %v", aSize, bSize) } if pv.Spec.HostPath.Path != expected.Spec.HostPath.Path { t.Errorf("Unexpected mismatch. Got %v wanted %v", pv.Spec.HostPath.Path, expected.Spec.HostPath.Path) } } } }
func TestMissingFromIndex(t *testing.T) { api.ForTesting_ReferencesAllowBlankSelfLinks = true o := testclient.NewObjects(api.Scheme, api.Scheme) if err := testclient.AddObjectsFromPath("../../docs/user-guide/persistent-volumes/claims/claim-01.yaml", o, api.Scheme); err != nil { t.Fatal(err) } if err := testclient.AddObjectsFromPath("../../docs/user-guide/persistent-volumes/volumes/local-01.yaml", o, api.Scheme); err != nil { t.Fatal(err) } client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, api.RESTMapper)} pv, err := client.PersistentVolumes().Get("any") if err != nil { t.Error("Unexpected error getting PV from client: %v", err) } claim, error := client.PersistentVolumeClaims("ns").Get("any") if error != nil { t.Errorf("Unexpected error getting PVC from client: %v", err) } volumeIndex := NewPersistentVolumeOrderedIndex() mockClient := &mockBinderClient{ volume: pv, claim: claim, } // the default value of the PV is Pending. // if has previously been processed by the binder, it's status in etcd would be Available. // Only Pending volumes were being indexed and made ready for claims. pv.Status.Phase = api.VolumeAvailable // adds the volume to the index, making the volume available syncVolume(volumeIndex, mockClient, pv) if pv.Status.Phase != api.VolumeAvailable { t.Errorf("Expected phase %s but got %s", api.VolumeBound, pv.Status.Phase) } // an initial sync for a claim will bind it to an unbound volume, triggers state change err = syncClaim(volumeIndex, mockClient, claim) if err != nil { t.Fatalf("Expected Clam to be bound, instead got an error: %+v\n", err) } // state change causes another syncClaim to update statuses syncClaim(volumeIndex, mockClient, claim) // claim updated volume's status, causing an update and syncVolume call syncVolume(volumeIndex, mockClient, pv) if pv.Spec.ClaimRef == nil { t.Errorf("Expected ClaimRef but got nil for pv.Status.ClaimRef: %+v\n", pv) } if pv.Status.Phase != api.VolumeBound { t.Errorf("Expected phase %s but got %s", api.VolumeBound, pv.Status.Phase) } if claim.Status.Phase != api.ClaimBound { t.Errorf("Expected phase %s but got %s", api.ClaimBound, claim.Status.Phase) } if len(claim.Status.AccessModes) != len(pv.Spec.AccessModes) { t.Errorf("Expected phase %s but got %s", api.ClaimBound, claim.Status.Phase) } if claim.Status.AccessModes[0] != pv.Spec.AccessModes[0] { t.Errorf("Expected access mode %s but got %s", claim.Status.AccessModes[0], pv.Spec.AccessModes[0]) } // pretend the user deleted their claim mockClient.claim = nil syncVolume(volumeIndex, mockClient, pv) if pv.Status.Phase != api.VolumeReleased { t.Errorf("Expected phase %s but got %s", api.VolumeReleased, pv.Status.Phase) } }
func TestBindingWithExamples(t *testing.T) { api.ForTesting_ReferencesAllowBlankSelfLinks = true o := testclient.NewObjects(api.Scheme, api.Scheme) if err := testclient.AddObjectsFromPath("../../docs/user-guide/persistent-volumes/claims/claim-01.yaml", o, api.Scheme); err != nil { t.Fatal(err) } if err := testclient.AddObjectsFromPath("../../docs/user-guide/persistent-volumes/volumes/local-01.yaml", o, api.Scheme); err != nil { t.Fatal(err) } client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, api.RESTMapper)} pv, err := client.PersistentVolumes().Get("any") pv.Spec.PersistentVolumeReclaimPolicy = api.PersistentVolumeReclaimRecycle if err != nil { t.Error("Unexpected error getting PV from client: %v", err) } claim, error := client.PersistentVolumeClaims("ns").Get("any") if error != nil { t.Errorf("Unexpected error getting PVC from client: %v", err) } volumeIndex := NewPersistentVolumeOrderedIndex() mockClient := &mockBinderClient{ volume: pv, claim: claim, } plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(host_path.ProbeRecyclableVolumePlugins(newMockRecycler), volume.NewFakeVolumeHost("/tmp/fake", nil, nil)) recycler := &PersistentVolumeRecycler{ kubeClient: client, client: mockClient, pluginMgr: plugMgr, } // adds the volume to the index, making the volume available syncVolume(volumeIndex, mockClient, pv) if pv.Status.Phase != api.VolumeAvailable { t.Errorf("Expected phase %s but got %s", api.VolumeBound, pv.Status.Phase) } // an initial sync for a claim will bind it to an unbound volume, triggers state change syncClaim(volumeIndex, mockClient, claim) // state change causes another syncClaim to update statuses syncClaim(volumeIndex, mockClient, claim) // claim updated volume's status, causing an update and syncVolume call syncVolume(volumeIndex, mockClient, pv) if pv.Spec.ClaimRef == nil { t.Errorf("Expected ClaimRef but got nil for pv.Status.ClaimRef: %+v\n", pv) } if pv.Status.Phase != api.VolumeBound { t.Errorf("Expected phase %s but got %s", api.VolumeBound, pv.Status.Phase) } if claim.Status.Phase != api.ClaimBound { t.Errorf("Expected phase %s but got %s", api.ClaimBound, claim.Status.Phase) } if len(claim.Status.AccessModes) != len(pv.Spec.AccessModes) { t.Errorf("Expected phase %s but got %s", api.ClaimBound, claim.Status.Phase) } if claim.Status.AccessModes[0] != pv.Spec.AccessModes[0] { t.Errorf("Expected access mode %s but got %s", claim.Status.AccessModes[0], pv.Spec.AccessModes[0]) } // pretend the user deleted their claim mockClient.claim = nil syncVolume(volumeIndex, mockClient, pv) if pv.Status.Phase != api.VolumeReleased { t.Errorf("Expected phase %s but got %s", api.VolumeReleased, pv.Status.Phase) } if pv.Spec.ClaimRef == nil { t.Errorf("Expected non-nil ClaimRef: %+v", pv.Spec) } mockClient.volume = pv // released volumes with a PersistentVolumeReclaimPolicy (recycle/delete) can have further processing err = recycler.reclaimVolume(pv) if err != nil { t.Errorf("Unexpected error reclaiming volume: %+v", err) } if pv.Status.Phase != api.VolumePending { t.Errorf("Expected phase %s but got %s", api.VolumePending, pv.Status.Phase) } // after the recycling changes the phase to Pending, the binder picks up again // to remove any vestiges of binding and make the volume Available again syncVolume(volumeIndex, mockClient, pv) if pv.Status.Phase != api.VolumeAvailable { t.Errorf("Expected phase %s but got %s", api.VolumeAvailable, pv.Status.Phase) } if pv.Spec.ClaimRef != nil { t.Errorf("Expected nil ClaimRef: %+v", pv.Spec) } }
func TestNewBuilder(t *testing.T) { tests := []struct { pv *api.PersistentVolume claim *api.PersistentVolumeClaim plugin volume.VolumePlugin podVolume api.VolumeSource testFunc func(builder volume.Builder, plugin volume.VolumePlugin) error expectedFailure bool }{ { pv: &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ Name: "pvA", }, Spec: api.PersistentVolumeSpec{ PersistentVolumeSource: api.PersistentVolumeSource{ GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}, }, ClaimRef: &api.ObjectReference{ Name: "claimA", }, }, }, claim: &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{ Name: "claimA", Namespace: "nsA", }, Spec: api.PersistentVolumeClaimSpec{ VolumeName: "pvA", }, Status: api.PersistentVolumeClaimStatus{ Phase: api.ClaimBound, }, }, podVolume: api.VolumeSource{ PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{ ReadOnly: false, ClaimName: "claimA", }, }, plugin: gce_pd.ProbeVolumePlugins()[0], testFunc: func(builder volume.Builder, plugin volume.VolumePlugin) error { if !strings.Contains(builder.GetPath(), util.EscapeQualifiedNameForDisk(plugin.Name())) { return fmt.Errorf("builder path expected to contain plugin name. Got: %s", builder.GetPath()) } return nil }, expectedFailure: false, }, { pv: &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ Name: "pvB", }, Spec: api.PersistentVolumeSpec{ PersistentVolumeSource: api.PersistentVolumeSource{ HostPath: &api.HostPathVolumeSource{Path: "/tmp"}, }, ClaimRef: &api.ObjectReference{ Name: "claimB", }, }, }, claim: &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{ Name: "claimB", Namespace: "nsB", }, Spec: api.PersistentVolumeClaimSpec{ VolumeName: "pvA", }, }, podVolume: api.VolumeSource{ PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{ ReadOnly: false, ClaimName: "claimB", }, }, plugin: host_path.ProbeVolumePlugins(nil)[0], testFunc: func(builder volume.Builder, plugin volume.VolumePlugin) error { if builder.GetPath() != "/tmp" { return fmt.Errorf("Expected HostPath.Path /tmp, got: %s", builder.GetPath()) } return nil }, expectedFailure: false, }, { pv: &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ Name: "pvA", }, Spec: api.PersistentVolumeSpec{ PersistentVolumeSource: api.PersistentVolumeSource{ GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}, }, }, }, claim: &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{ Name: "claimA", Namespace: "nsA", }, Spec: api.PersistentVolumeClaimSpec{ VolumeName: "pvA", }, Status: api.PersistentVolumeClaimStatus{ Phase: api.ClaimBound, }, }, podVolume: api.VolumeSource{ PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{ ReadOnly: false, ClaimName: "claimA", }, }, plugin: gce_pd.ProbeVolumePlugins()[0], testFunc: func(builder volume.Builder, plugin volume.VolumePlugin) error { if builder != nil { return fmt.Errorf("Unexpected non-nil builder: %+v", builder) } return nil }, expectedFailure: true, // missing pv.Spec.ClaimRef }, { pv: &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ Name: "pvA", }, Spec: api.PersistentVolumeSpec{ PersistentVolumeSource: api.PersistentVolumeSource{ GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}, }, ClaimRef: &api.ObjectReference{ Name: "claimB", UID: types.UID("abc123"), }, }, }, claim: &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{ Name: "claimA", Namespace: "nsA", UID: types.UID("def456"), }, Spec: api.PersistentVolumeClaimSpec{ VolumeName: "pvA", }, Status: api.PersistentVolumeClaimStatus{ Phase: api.ClaimBound, }, }, podVolume: api.VolumeSource{ PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{ ReadOnly: false, ClaimName: "claimA", }, }, plugin: gce_pd.ProbeVolumePlugins()[0], testFunc: func(builder volume.Builder, plugin volume.VolumePlugin) error { if builder != nil { return fmt.Errorf("Unexpected non-nil builder: %+v", builder) } return nil }, expectedFailure: true, // mismatched pv.Spec.ClaimRef and pvc }, } for _, item := range tests { o := testclient.NewObjects(api.Scheme, api.Scheme) o.Add(item.pv) o.Add(item.claim) client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, api.RESTMapper)} plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(testProbeVolumePlugins(), newTestHost(t, client)) plug, err := plugMgr.FindPluginByName("kubernetes.io/persistent-claim") if err != nil { t.Errorf("Can't find the plugin by name") } spec := &volume.Spec{ Name: "vol1", Volume: &api.Volume{VolumeSource: item.podVolume}, } pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: types.UID("poduid")}} builder, err := plug.NewBuilder(spec, pod, volume.VolumeOptions{}, nil) if !item.expectedFailure { if err != nil { t.Errorf("Failed to make a new Builder: %v", err) } if builder == nil { t.Errorf("Got a nil Builder: %v", builder) } } if err := item.testFunc(builder, item.plugin); err != nil { t.Errorf("Unexpected error %+v", err) } } }
func TestProjectStatus(t *testing.T) { testCases := map[string]struct { Path string Extra []runtime.Object ErrFn func(error) bool Contains []string Time time.Time }{ "missing project": { ErrFn: func(err error) bool { return errors.IsNotFound(err) }, }, "empty project with display name": { Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{ Name: "example", Namespace: "", Annotations: map[string]string{ projectapi.ProjectDisplayName: "Test", }, }, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project Test (example) on server https://example.com:8443\n", "You have no services, deployment configs, or build configs.", }, }, "empty service": { Path: "../../../../test/fixtures/app-scenarios/k8s-service-with-nothing.json", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "service/empty-service", "<initializing>:5432", "To see more, use", }, }, "service with RC": { Path: "../../../../test/fixtures/app-scenarios/k8s-unserviced-rc.json", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "service/database-rc", "rc/database-rc-1 runs mysql", "0/1 pods growing to 1", "To see more, use", }, }, "rc with unmountable and missing secrets": { Path: "../../../../pkg/api/graph/test/bad_secret_with_just_rc.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "rc/my-rc runs openshift/mysql-55-centos7", "0/1 pods growing to 1", "rc/my-rc is attempting to mount a secret secret/existing-secret disallowed by sa/default", "rc/my-rc is attempting to mount a secret secret/dne disallowed by sa/default", "rc/my-rc is attempting to mount a missing secret secret/dne", }, }, "dueling rcs": { Path: "../../../../pkg/api/graph/test/dueling-rcs.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "dueling-rc", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "rc/rc-1 is competing for pod/conflicted-pod with rc/rc-2", "rc/rc-2 is competing for pod/conflicted-pod with rc/rc-1", }, }, "service with pod": { Path: "../../../../pkg/api/graph/test/service-with-pod.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "service/frontend-app", "pod/frontend-app-1-bjwh8 runs openshift/ruby-hello-world", "To see more, use", }, }, "standalone rc": { Path: "../../../../pkg/api/graph/test/bare-rc.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", " rc/database-1 runs openshift/mysql-55-centos7", "rc/frontend-rc-1 runs openshift/ruby-hello-world", }, }, "unstarted build": { Path: "../../../../test/fixtures/app-scenarios/new-project-no-build.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "service/sinatra-example-2 - 172.30.17.48:8080", "builds git://github.com", "with docker.io/openshift/ruby-20-centos7:latest", "not built yet", "#1 deployment waiting on image or update", "To see more, use", }, }, "unpushable build": { Path: "../../../../pkg/api/graph/test/unpushable-build.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "bc/ruby-hello-world is pushing to imagestreamtag/ruby-hello-world:latest that is using is/ruby-hello-world, but the administrator has not configured the integrated Docker registry.", }, }, "cyclical build": { Path: "../../../../pkg/api/graph/test/circular.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "Cycle detected in build configurations:", }, }, "running build": { Path: "../../../../test/fixtures/app-scenarios/new-project-one-build.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "service/sinatra-example-1 - 172.30.17.47:8080", "builds git://github.com", "with docker.io/openshift/ruby-20-centos7:latest", "build 1 running for about a minute", "#1 deployment waiting on image or update", "To see more, use", }, Time: mustParseTime("2015-04-06T21:20:03Z"), }, "a/b test DeploymentConfig": { Path: "../../../../test/fixtures/app-scenarios/new-project-two-deployment-configs.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "service/sinatra-app-example - 172.30.17.49:8080", "sinatra-app-example-a deploys", "sinatra-app-example-b deploys", "with docker.io/openshift/ruby-20-centos7:latest", "build 1 running for about a minute", "- 7a4f354: Prepare v1beta3 Template types (Roy Programmer <*****@*****.**>)", "To see more, use", }, Time: mustParseTime("2015-04-06T21:20:03Z"), }, "with real deployments": { Path: "../../../../test/fixtures/app-scenarios/new-project-deployed-app.yaml", Extra: []runtime.Object{ &projectapi.Project{ ObjectMeta: kapi.ObjectMeta{Name: "example", Namespace: ""}, }, }, ErrFn: func(err error) bool { return err == nil }, Contains: []string{ "In project example on server https://example.com:8443\n", "service/database - 172.30.17.240:5434 -> 3306", "service/frontend - 172.30.17.154:5432 -> 8080", "database deploys", "frontend deploys", "with docker.io/openshift/ruby-20-centos7:latest", "#2 deployment failed less than a second ago: unable to contact server - 0/1 pods", "#2 deployment running for 7 seconds - 2/1 pods", "#1 deployed 8 seconds ago", "#1 deployed less than a second ago", "To see more, use", }, Time: mustParseTime("2015-04-07T04:12:25Z"), }, } oldTimeFn := timeNowFn defer func() { timeNowFn = oldTimeFn }() for k, test := range testCases { timeNowFn = func() time.Time { if !test.Time.IsZero() { return test.Time } return time.Now() } o := ktestclient.NewObjects(kapi.Scheme, kapi.Scheme) if len(test.Path) > 0 { if err := ktestclient.AddObjectsFromPath(test.Path, o, kapi.Scheme); err != nil { t.Fatal(err) } } for _, obj := range test.Extra { o.Add(obj) } oc, kc := testclient.NewFixtureClients(o) d := ProjectStatusDescriber{C: oc, K: kc, Server: "https://example.com:8443"} out, err := d.Describe("example", "") if !test.ErrFn(err) { t.Errorf("%s: unexpected error: %v", k, err) } if err != nil { continue } for _, s := range test.Contains { if !strings.Contains(out, s) { t.Errorf("%s: did not have %q:\n%s\n---", k, s, out) } } } }
func TestChainDescriber(t *testing.T) { tests := []struct { testName string namespaces kutil.StringSet output string defaultNamespace string name string tag string path string humanReadable map[string]int dot []string expectedErr error includeInputImg bool }{ { testName: "human readable test - single namespace", namespaces: kutil.NewStringSet("test"), output: "", defaultNamespace: "test", name: "ruby-20-centos7", tag: "latest", path: "../../../../pkg/cmd/experimental/buildchain/test/single-namespace-bcs.yaml", humanReadable: map[string]int{ "imagestreamtag/ruby-20-centos7:latest": 1, "\tbc/ruby-hello-world": 1, "\t\timagestreamtag/ruby-hello-world:latest": 1, "\tbc/ruby-sample-build": 1, "\t\timagestreamtag/origin-ruby-sample:latest": 1, }, expectedErr: nil, }, { testName: "dot test - single namespace", namespaces: kutil.NewStringSet("test"), output: "dot", defaultNamespace: "test", name: "ruby-20-centos7", tag: "latest", path: "../../../../pkg/cmd/experimental/buildchain/test/single-namespace-bcs.yaml", dot: []string{ "digraph \"ruby-20-centos7:latest\" {", "// Node definitions.", "[label=\"BuildConfig|test/ruby-hello-world\"];", "[label=\"BuildConfig|test/ruby-sample-build\"];", "[label=\"ImageStreamTag|test/ruby-hello-world:latest\"];", "[label=\"ImageStreamTag|test/ruby-20-centos7:latest\"];", "[label=\"ImageStreamTag|test/origin-ruby-sample:latest\"];", "", "// Edge definitions.", "[label=\"BuildOutput\"];", "[label=\"BuildOutput\"];", "[label=\"BuildInputImage,BuildTriggerImage\"];", "[label=\"BuildInputImage,BuildTriggerImage\"];", "}", }, expectedErr: nil, }, { testName: "human readable test - multiple namespaces", namespaces: kutil.NewStringSet("test", "master", "default"), output: "", defaultNamespace: "master", name: "ruby-20-centos7", tag: "latest", path: "../../../../pkg/cmd/experimental/buildchain/test/multiple-namespaces-bcs.yaml", humanReadable: map[string]int{ "<master imagestreamtag/ruby-20-centos7:latest>": 1, "\t<default bc/ruby-hello-world>": 1, "\t\t<test imagestreamtag/ruby-hello-world:latest>": 1, "\t<test bc/ruby-sample-build>": 1, "\t\t<another imagestreamtag/origin-ruby-sample:latest>": 1, }, expectedErr: nil, }, { testName: "dot test - multiple namespaces", namespaces: kutil.NewStringSet("test", "master", "default"), output: "dot", defaultNamespace: "master", name: "ruby-20-centos7", tag: "latest", path: "../../../../pkg/cmd/experimental/buildchain/test/multiple-namespaces-bcs.yaml", dot: []string{ "digraph \"ruby-20-centos7:latest\" {", "// Node definitions.", "[label=\"BuildConfig|default/ruby-hello-world\"];", "[label=\"BuildConfig|test/ruby-sample-build\"];", "[label=\"ImageStreamTag|test/ruby-hello-world:latest\"];", "[label=\"ImageStreamTag|master/ruby-20-centos7:latest\"];", "[label=\"ImageStreamTag|another/origin-ruby-sample:latest\"];", "", "// Edge definitions.", "[label=\"BuildOutput\"];", "[label=\"BuildOutput\"];", "[label=\"BuildInputImage,BuildTriggerImage\"];", "[label=\"BuildInputImage,BuildTriggerImage\"];", "}", }, expectedErr: nil, }, { testName: "human readable - multiple triggers - triggeronly", name: "ruby-20-centos7", defaultNamespace: "test", tag: "latest", path: "../../../../pkg/cmd/experimental/buildchain/test/multiple-trigger-bcs.yaml", namespaces: kutil.NewStringSet("test"), humanReadable: map[string]int{ "imagestreamtag/ruby-20-centos7:latest": 1, "\tbc/parent1": 1, "\t\timagestreamtag/parent1img:latest": 1, "\t\t\tbc/child2": 2, "\t\t\t\timagestreamtag/child2img:latest": 2, "\tbc/parent2": 1, "\t\timagestreamtag/parent2img:latest": 1, "\t\t\tbc/child3": 2, "\t\t\t\timagestreamtag/child3img:latest": 2, "\t\t\tbc/child1": 1, "\t\t\t\timagestreamtag/child1img:latest": 1, "\tbc/parent3": 1, "\t\timagestreamtag/parent3img:latest": 1, }, }, { testName: "human readable - multiple triggers - trigger+input", name: "ruby-20-centos7", defaultNamespace: "test", tag: "latest", path: "../../../../pkg/cmd/experimental/buildchain/test/multiple-trigger-bcs.yaml", namespaces: kutil.NewStringSet("test"), includeInputImg: true, humanReadable: map[string]int{ "imagestreamtag/ruby-20-centos7:latest": 1, "\tbc/parent1": 1, "\t\timagestreamtag/parent1img:latest": 1, "\t\t\tbc/child1": 2, "\t\t\t\timagestreamtag/child1img:latest": 2, "\t\t\tbc/child2": 2, "\t\t\t\timagestreamtag/child2img:latest": 2, "\t\t\tbc/child3": 3, "\t\t\t\timagestreamtag/child3img:latest": 3, "\tbc/parent2": 1, "\t\timagestreamtag/parent2img:latest": 1, "\tbc/parent3": 1, "\t\timagestreamtag/parent3img:latest": 1, }, }, } for _, test := range tests { o := ktestclient.NewObjects(kapi.Scheme, kapi.Scheme) if len(test.path) > 0 { if err := ktestclient.AddObjectsFromPath(test.path, o, kapi.Scheme); err != nil { t.Fatal(err) } } oc, _ := testclient.NewFixtureClients(o) ist := imagegraph.MakeImageStreamTagObjectMeta(test.defaultNamespace, test.name, test.tag) desc, err := NewChainDescriber(oc, test.namespaces, test.output).Describe(ist, test.includeInputImg) t.Logf("%s: output:\n%s\n\n", test.testName, desc) if err != test.expectedErr { t.Fatalf("%s: error mismatch: expected %v, got %v", test.testName, test.expectedErr, err) } got := strings.Split(desc, "\n") switch test.output { case "dot": if len(test.dot) != len(got) { t.Fatalf("%s: expected %d lines, got %d:\n%s", test.testName, len(test.dot), len(got), desc) } for _, expected := range test.dot { if !strings.Contains(desc, expected) { t.Errorf("%s: unexpected description:\n%s\nexpected line in it:\n%s", test.testName, desc, expected) } } case "": if lenReadable(test.humanReadable) != len(got) { t.Fatalf("%s: expected %d lines, got %d:\n%s", test.testName, lenReadable(test.humanReadable), len(got), desc) } for _, line := range got { if _, ok := test.humanReadable[line]; !ok { t.Errorf("%s: unexpected line: %s", test.testName, line) } test.humanReadable[line]-- } for line, cnt := range test.humanReadable { if cnt != 0 { t.Errorf("%s: unexpected number of lines for [%s]: %d", test.testName, line, cnt) } } } } }