1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
11
"github.com/juju/errors"
12
jc "github.com/juju/testing/checkers"
13
gc "gopkg.in/check.v1"
14
goyaml "gopkg.in/yaml.v2"
16
"github.com/juju/juju/apiserver/params"
17
"github.com/juju/juju/cmd/juju/storage"
18
"github.com/juju/juju/status"
19
"github.com/juju/juju/testing"
22
func (s *ListSuite) TestVolumeListEmpty(c *gc.C) {
23
s.mockAPI.listVolumes = func([]string) ([]params.VolumeDetailsListResult, error) {
26
s.assertValidVolumeList(
28
[]string{"--format", "yaml"},
33
func (s *ListSuite) TestVolumeListError(c *gc.C) {
34
s.mockAPI.listVolumes = func([]string) ([]params.VolumeDetailsListResult, error) {
35
return nil, errors.New("just my luck")
37
context, err := s.runVolumeList(c, "--format", "yaml")
38
c.Assert(errors.Cause(err), gc.ErrorMatches, "just my luck")
39
s.assertUserFacingVolumeOutput(c, context, "", "")
42
func (s *ListSuite) TestVolumeListArgs(c *gc.C) {
44
expectedArgs := []string{"a", "b", "c"}
45
s.mockAPI.listVolumes = func(arg []string) ([]params.VolumeDetailsListResult, error) {
46
c.Assert(arg, jc.DeepEquals, expectedArgs)
50
s.assertValidVolumeList(
52
append([]string{"--format", "yaml"}, expectedArgs...),
55
c.Assert(called, jc.IsTrue)
58
func (s *ListSuite) TestVolumeListYaml(c *gc.C) {
59
s.assertUnmarshalledVolumeOutput(
66
func (s *ListSuite) TestVolumeListJSON(c *gc.C) {
67
s.assertUnmarshalledVolumeOutput(
74
func (s *ListSuite) TestVolumeListWithErrorResults(c *gc.C) {
75
s.mockAPI.listVolumes = func([]string) ([]params.VolumeDetailsListResult, error) {
76
results, _ := mockListAPI{}.ListVolumes(nil)
77
results = append(results, params.VolumeDetailsListResult{
78
Error: ¶ms.Error{Message: "bad"},
80
results = append(results, params.VolumeDetailsListResult{
81
Error: ¶ms.Error{Message: "ness"},
85
// we should see the error in stderr, but it should not
86
// otherwise affect the rendering of valid results.
87
s.assertUnmarshalledVolumeOutput(c, json.Unmarshal, "bad\nness\n", "--format", "json")
88
s.assertUnmarshalledVolumeOutput(c, goyaml.Unmarshal, "bad\nness\n", "--format", "yaml")
91
var expectedVolumeListTabular = `
92
MACHINE UNIT STORAGE ID PROVIDER-ID DEVICE SIZE STATE MESSAGE
93
0 abc/0 db-dir/1001 0/0 provider-supplied-volume-0-0 loop0 512MiB attached
94
0 transcode/0 shared-fs/0 4 provider-supplied-volume-4 xvdf2 1.0GiB attached
95
0 1 provider-supplied-volume-1 2.0GiB attaching failed to attach, will retry
96
1 transcode/1 shared-fs/0 4 provider-supplied-volume-4 xvdf3 1.0GiB attached
97
1 2 provider-supplied-volume-2 xvdf1 3.0MiB attached
102
func (s *ListSuite) TestVolumeListTabular(c *gc.C) {
103
s.assertValidVolumeList(c, []string{}, expectedVolumeListTabular)
105
// Do it again, reversing the results returned by the API.
106
// We should get everything sorted in the appropriate order.
107
s.mockAPI.listVolumes = func([]string) ([]params.VolumeDetailsListResult, error) {
108
results, _ := mockListAPI{}.ListVolumes(nil)
110
for i := 0; i < n/2; i++ {
111
results[i], results[n-i-1] = results[n-i-1], results[i]
115
s.assertValidVolumeList(c, []string{}, expectedVolumeListTabular)
118
func (s *ListSuite) assertUnmarshalledVolumeOutput(c *gc.C, unmarshal unmarshaller, expectedErr string, args ...string) {
119
context, err := s.runVolumeList(c, args...)
120
c.Assert(err, jc.ErrorIsNil)
123
Volumes map[string]storage.VolumeInfo
125
err = unmarshal([]byte(testing.Stdout(context)), &result)
126
c.Assert(err, jc.ErrorIsNil)
128
expected := s.expectVolume(c, nil)
129
c.Assert(result.Volumes, jc.DeepEquals, expected)
131
obtainedErr := testing.Stderr(context)
132
c.Assert(obtainedErr, gc.Equals, expectedErr)
135
// expect returns the VolumeInfo mapping we should expect to unmarshal
136
// from rendered YAML or JSON.
137
func (s *ListSuite) expectVolume(c *gc.C, machines []string) map[string]storage.VolumeInfo {
138
all, err := s.mockAPI.ListVolumes(machines)
139
c.Assert(err, jc.ErrorIsNil)
141
var valid []params.VolumeDetails
142
for _, result := range all {
143
if result.Error == nil {
144
valid = append(valid, result.Result...)
147
result, err := storage.ConvertToVolumeInfo(valid)
148
c.Assert(err, jc.ErrorIsNil)
152
func (s *ListSuite) assertValidVolumeList(c *gc.C, args []string, expectedOut string) {
153
context, err := s.runVolumeList(c, args...)
154
c.Assert(err, jc.ErrorIsNil)
155
s.assertUserFacingVolumeOutput(c, context, expectedOut, "")
158
func (s *ListSuite) runVolumeList(c *gc.C, args ...string) (*cmd.Context, error) {
159
return testing.RunCommand(c,
160
storage.NewListCommandForTest(s.mockAPI, s.store), append(args, "--volume")...)
163
func (s *ListSuite) assertUserFacingVolumeOutput(c *gc.C, context *cmd.Context, expectedOut, expectedErr string) {
164
obtainedOut := testing.Stdout(context)
165
c.Assert(obtainedOut, gc.Equals, expectedOut)
167
obtainedErr := testing.Stderr(context)
168
c.Assert(obtainedErr, gc.Equals, expectedErr)
171
func (s mockListAPI) ListVolumes(machines []string) ([]params.VolumeDetailsListResult, error) {
172
if s.listVolumes != nil {
173
return s.listVolumes(machines)
175
results := []params.VolumeDetailsListResult{{Result: []params.VolumeDetails{
176
// volume 0/0 is attached to machine 0, assigned to
177
// storage db-dir/1001, which is attached to unit
180
VolumeTag: "volume-0-0",
181
Info: params.VolumeInfo{
182
VolumeId: "provider-supplied-volume-0-0",
185
Status: createTestStatus(status.StatusAttached, ""),
186
MachineAttachments: map[string]params.VolumeAttachmentInfo{
187
"machine-0": params.VolumeAttachmentInfo{
191
Storage: ¶ms.StorageDetails{
192
StorageTag: "storage-db-dir-1001",
193
OwnerTag: "unit-abc-0",
194
Kind: params.StorageKindBlock,
195
Status: createTestStatus(status.StatusAttached, ""),
196
Attachments: map[string]params.StorageAttachmentDetails{
197
"unit-abc-0": params.StorageAttachmentDetails{
198
StorageTag: "storage-db-dir-1001",
199
UnitTag: "unit-abc-0",
200
MachineTag: "machine-0",
201
Location: "/dev/loop0",
206
// volume 1 is attaching to machine 0, but is not assigned
209
VolumeTag: "volume-1",
210
Info: params.VolumeInfo{
211
VolumeId: "provider-supplied-volume-1",
212
HardwareId: "serial blah blah",
216
Status: createTestStatus(status.StatusAttaching, "failed to attach, will retry"),
217
MachineAttachments: map[string]params.VolumeAttachmentInfo{
218
"machine-0": params.VolumeAttachmentInfo{},
221
// volume 3 is due to be attached to machine 1, but is not
222
// assigned to any storage and has not yet been provisioned.
224
VolumeTag: "volume-3",
225
Info: params.VolumeInfo{
228
Status: createTestStatus(status.StatusPending, ""),
229
MachineAttachments: map[string]params.VolumeAttachmentInfo{
230
"machine-1": params.VolumeAttachmentInfo{},
233
// volume 2 is due to be attached to machine 1, but is not
234
// assigned to any storage and has not yet been provisioned.
236
VolumeTag: "volume-2",
237
Info: params.VolumeInfo{
238
VolumeId: "provider-supplied-volume-2",
241
Status: createTestStatus(status.StatusAttached, ""),
242
MachineAttachments: map[string]params.VolumeAttachmentInfo{
243
"machine-1": params.VolumeAttachmentInfo{
248
// volume 4 is attached to machines 0 and 1, and is assigned
249
// to shared storage.
251
VolumeTag: "volume-4",
252
Info: params.VolumeInfo{
253
VolumeId: "provider-supplied-volume-4",
257
Status: createTestStatus(status.StatusAttached, ""),
258
MachineAttachments: map[string]params.VolumeAttachmentInfo{
259
"machine-0": params.VolumeAttachmentInfo{
263
"machine-1": params.VolumeAttachmentInfo{
268
Storage: ¶ms.StorageDetails{
269
StorageTag: "storage-shared-fs-0",
270
OwnerTag: "application-transcode",
271
Kind: params.StorageKindBlock,
272
Status: createTestStatus(status.StatusAttached, ""),
273
Attachments: map[string]params.StorageAttachmentDetails{
274
"unit-transcode-0": params.StorageAttachmentDetails{
275
StorageTag: "storage-shared-fs-0",
276
UnitTag: "unit-transcode-0",
277
MachineTag: "machine-0",
278
Location: "/mnt/bits",
280
"unit-transcode-1": params.StorageAttachmentDetails{
281
StorageTag: "storage-shared-fs-0",
282
UnitTag: "unit-transcode-1",
283
MachineTag: "machine-1",
284
Location: "/mnt/pieces",
293
func createTestStatus(testStatus status.Status, message string) params.EntityStatus {
294
return params.EntityStatus{