github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/cmd/juju/storage/filesystemlist_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package storage_test 5 6 import ( 7 "encoding/json" 8 9 "github.com/juju/cmd" 10 "github.com/juju/errors" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 goyaml "gopkg.in/yaml.v1" 14 15 "github.com/juju/juju/apiserver/params" 16 "github.com/juju/juju/cmd/envcmd" 17 "github.com/juju/juju/cmd/juju/storage" 18 "github.com/juju/juju/testing" 19 ) 20 21 type filesystemListSuite struct { 22 SubStorageSuite 23 mockAPI *mockFilesystemListAPI 24 } 25 26 var _ = gc.Suite(&filesystemListSuite{}) 27 28 func (s *filesystemListSuite) SetUpTest(c *gc.C) { 29 s.SubStorageSuite.SetUpTest(c) 30 31 s.mockAPI = &mockFilesystemListAPI{} 32 s.PatchValue(storage.GetFilesystemListAPI, 33 func(c *storage.FilesystemListCommand) (storage.FilesystemListAPI, error) { 34 return s.mockAPI, nil 35 }) 36 } 37 38 func (s *filesystemListSuite) TestFilesystemListEmpty(c *gc.C) { 39 s.mockAPI.listFilesystems = func([]string) ([]params.FilesystemDetailsResult, error) { 40 return nil, nil 41 } 42 s.assertValidList( 43 c, 44 []string{"--format", "yaml"}, 45 "", 46 ) 47 } 48 49 func (s *filesystemListSuite) TestFilesystemListError(c *gc.C) { 50 s.mockAPI.listFilesystems = func([]string) ([]params.FilesystemDetailsResult, error) { 51 return nil, errors.New("just my luck") 52 } 53 context, err := runFilesystemList(c, "--format", "yaml") 54 c.Assert(errors.Cause(err), gc.ErrorMatches, "just my luck") 55 s.assertUserFacingOutput(c, context, "", "") 56 } 57 58 func (s *filesystemListSuite) TestFilesystemListArgs(c *gc.C) { 59 var called bool 60 expectedArgs := []string{"a", "b", "c"} 61 s.mockAPI.listFilesystems = func(arg []string) ([]params.FilesystemDetailsResult, error) { 62 c.Assert(arg, jc.DeepEquals, expectedArgs) 63 called = true 64 return nil, nil 65 } 66 s.assertValidList( 67 c, 68 append([]string{"--format", "yaml"}, expectedArgs...), 69 "", 70 ) 71 c.Assert(called, jc.IsTrue) 72 } 73 74 func (s *filesystemListSuite) TestFilesystemListYaml(c *gc.C) { 75 s.assertUnmarshalledOutput( 76 c, 77 goyaml.Unmarshal, 78 "", // no error 79 "--format", "yaml") 80 } 81 82 func (s *filesystemListSuite) TestFilesystemListJSON(c *gc.C) { 83 s.assertUnmarshalledOutput( 84 c, 85 json.Unmarshal, 86 "", // no error 87 "--format", "json") 88 } 89 90 func (s *filesystemListSuite) TestFilesystemListWithErrorResults(c *gc.C) { 91 s.mockAPI.listFilesystems = func([]string) ([]params.FilesystemDetailsResult, error) { 92 results, _ := mockFilesystemListAPI{}.ListFilesystems(nil) 93 results = append(results, params.FilesystemDetailsResult{ 94 Error: ¶ms.Error{Message: "bad"}, 95 }) 96 results = append(results, params.FilesystemDetailsResult{ 97 Error: ¶ms.Error{Message: "ness"}, 98 }) 99 return results, nil 100 } 101 // we should see the error in stderr, but it should not 102 // otherwise affect the rendering of valid results. 103 s.assertUnmarshalledOutput(c, json.Unmarshal, "bad\nness\n", "--format", "json") 104 s.assertUnmarshalledOutput(c, goyaml.Unmarshal, "bad\nness\n", "--format", "yaml") 105 } 106 107 var expectedFilesystemListTabular = ` 108 MACHINE UNIT STORAGE ID VOLUME PROVIDER-ID MOUNTPOINT SIZE STATE MESSAGE 109 0 abc/0 db-dir/1001 0/0 0/1 provider-supplied-filesystem-0-0 /mnt/fuji 512MiB attached 110 0 transcode/0 shared-fs/0 4 provider-supplied-filesystem-4 /mnt/doom 1.0GiB attached 111 0 1 provider-supplied-filesystem-1 2.0GiB attaching failed to attach, will retry 112 1 transcode/1 shared-fs/0 4 provider-supplied-filesystem-4 /mnt/huang 1.0GiB attached 113 1 2 provider-supplied-filesystem-2 /mnt/zion 3.0MiB attached 114 1 3 42MiB pending 115 116 `[1:] 117 118 func (s *filesystemListSuite) TestFilesystemListTabular(c *gc.C) { 119 s.assertValidList(c, []string{}, expectedFilesystemListTabular) 120 121 // Do it again, reversing the results returned by the API. 122 // We should get everything sorted in the appropriate order. 123 s.mockAPI.listFilesystems = func([]string) ([]params.FilesystemDetailsResult, error) { 124 results, _ := mockFilesystemListAPI{}.ListFilesystems(nil) 125 n := len(results) 126 for i := 0; i < n/2; i++ { 127 results[i], results[n-i-1] = results[n-i-1], results[i] 128 } 129 return results, nil 130 } 131 s.assertValidList(c, []string{}, expectedFilesystemListTabular) 132 } 133 134 func (s *filesystemListSuite) assertUnmarshalledOutput(c *gc.C, unmarshal unmarshaller, expectedErr string, args ...string) { 135 context, err := runFilesystemList(c, args...) 136 c.Assert(err, jc.ErrorIsNil) 137 138 var result struct { 139 Filesystems map[string]storage.FilesystemInfo 140 } 141 err = unmarshal([]byte(testing.Stdout(context)), &result) 142 c.Assert(err, jc.ErrorIsNil) 143 144 expected := s.expect(c, nil) 145 c.Assert(result.Filesystems, jc.DeepEquals, expected) 146 147 obtainedErr := testing.Stderr(context) 148 c.Assert(obtainedErr, gc.Equals, expectedErr) 149 } 150 151 // expect returns the FilesystemInfo mapping we should expect to unmarshal 152 // from rendered YAML or JSON. 153 func (s *filesystemListSuite) expect(c *gc.C, machines []string) map[string]storage.FilesystemInfo { 154 all, err := s.mockAPI.ListFilesystems(machines) 155 c.Assert(err, jc.ErrorIsNil) 156 157 var valid []params.FilesystemDetailsResult 158 for _, result := range all { 159 if result.Error == nil { 160 valid = append(valid, result) 161 } 162 } 163 result, err := storage.ConvertToFilesystemInfo(valid) 164 c.Assert(err, jc.ErrorIsNil) 165 return result 166 } 167 168 func (s *filesystemListSuite) assertValidList(c *gc.C, args []string, expectedOut string) { 169 context, err := runFilesystemList(c, args...) 170 c.Assert(err, jc.ErrorIsNil) 171 s.assertUserFacingOutput(c, context, expectedOut, "") 172 } 173 174 func runFilesystemList(c *gc.C, args ...string) (*cmd.Context, error) { 175 return testing.RunCommand(c, 176 envcmd.Wrap(&storage.FilesystemListCommand{}), 177 args...) 178 } 179 180 func (s *filesystemListSuite) assertUserFacingOutput(c *gc.C, context *cmd.Context, expectedOut, expectedErr string) { 181 obtainedOut := testing.Stdout(context) 182 c.Assert(obtainedOut, gc.Equals, expectedOut) 183 184 obtainedErr := testing.Stderr(context) 185 c.Assert(obtainedErr, gc.Equals, expectedErr) 186 } 187 188 type mockFilesystemListAPI struct { 189 listFilesystems func([]string) ([]params.FilesystemDetailsResult, error) 190 } 191 192 func (s mockFilesystemListAPI) Close() error { 193 return nil 194 } 195 196 func (s mockFilesystemListAPI) ListFilesystems(machines []string) ([]params.FilesystemDetailsResult, error) { 197 if s.listFilesystems != nil { 198 return s.listFilesystems(machines) 199 } 200 results := []params.FilesystemDetailsResult{{ 201 // filesystem 0/0 is attached to machine 0, assigned to 202 // storage db-dir/1001, which is attached to unit 203 // abc/0. 204 Result: ¶ms.FilesystemDetails{ 205 FilesystemTag: "filesystem-0-0", 206 VolumeTag: "volume-0-1", 207 Info: params.FilesystemInfo{ 208 FilesystemId: "provider-supplied-filesystem-0-0", 209 Size: 512, 210 }, 211 Status: createTestStatus(params.StatusAttached, ""), 212 MachineAttachments: map[string]params.FilesystemAttachmentInfo{ 213 "machine-0": params.FilesystemAttachmentInfo{ 214 MountPoint: "/mnt/fuji", 215 }, 216 }, 217 Storage: ¶ms.StorageDetails{ 218 StorageTag: "storage-db-dir-1001", 219 OwnerTag: "unit-abc-0", 220 Kind: params.StorageKindBlock, 221 Status: createTestStatus(params.StatusAttached, ""), 222 Attachments: map[string]params.StorageAttachmentDetails{ 223 "unit-abc-0": params.StorageAttachmentDetails{ 224 StorageTag: "storage-db-dir-1001", 225 UnitTag: "unit-abc-0", 226 MachineTag: "machine-0", 227 Location: "/mnt/fuji", 228 }, 229 }, 230 }, 231 }, 232 }, { 233 // filesystem 1 is attaching to machine 0, but is not assigned 234 // to any storage. 235 Result: ¶ms.FilesystemDetails{ 236 FilesystemTag: "filesystem-1", 237 Info: params.FilesystemInfo{ 238 FilesystemId: "provider-supplied-filesystem-1", 239 Size: 2048, 240 }, 241 Status: createTestStatus(params.StatusAttaching, "failed to attach, will retry"), 242 MachineAttachments: map[string]params.FilesystemAttachmentInfo{ 243 "machine-0": params.FilesystemAttachmentInfo{}, 244 }, 245 }, 246 }, { 247 // filesystem 3 is due to be attached to machine 1, but is not 248 // assigned to any storage and has not yet been provisioned. 249 Result: ¶ms.FilesystemDetails{ 250 FilesystemTag: "filesystem-3", 251 Info: params.FilesystemInfo{ 252 Size: 42, 253 }, 254 Status: createTestStatus(params.StatusPending, ""), 255 MachineAttachments: map[string]params.FilesystemAttachmentInfo{ 256 "machine-1": params.FilesystemAttachmentInfo{}, 257 }, 258 }, 259 }, { 260 // filesystem 2 is due to be attached to machine 1, but is not 261 // assigned to any storage. 262 Result: ¶ms.FilesystemDetails{ 263 FilesystemTag: "filesystem-2", 264 Info: params.FilesystemInfo{ 265 FilesystemId: "provider-supplied-filesystem-2", 266 Size: 3, 267 }, 268 Status: createTestStatus(params.StatusAttached, ""), 269 MachineAttachments: map[string]params.FilesystemAttachmentInfo{ 270 "machine-1": params.FilesystemAttachmentInfo{ 271 MountPoint: "/mnt/zion", 272 }, 273 }, 274 }, 275 }, { 276 // filesystem 4 is attached to machines 0 and 1, and is assigned 277 // to shared storage. 278 Result: ¶ms.FilesystemDetails{ 279 FilesystemTag: "filesystem-4", 280 Info: params.FilesystemInfo{ 281 FilesystemId: "provider-supplied-filesystem-4", 282 Size: 1024, 283 }, 284 Status: createTestStatus(params.StatusAttached, ""), 285 MachineAttachments: map[string]params.FilesystemAttachmentInfo{ 286 "machine-0": params.FilesystemAttachmentInfo{ 287 MountPoint: "/mnt/doom", 288 ReadOnly: true, 289 }, 290 "machine-1": params.FilesystemAttachmentInfo{ 291 MountPoint: "/mnt/huang", 292 ReadOnly: true, 293 }, 294 }, 295 Storage: ¶ms.StorageDetails{ 296 StorageTag: "storage-shared-fs-0", 297 OwnerTag: "service-transcode", 298 Kind: params.StorageKindBlock, 299 Status: createTestStatus(params.StatusAttached, ""), 300 Attachments: map[string]params.StorageAttachmentDetails{ 301 "unit-transcode-0": params.StorageAttachmentDetails{ 302 StorageTag: "storage-shared-fs-0", 303 UnitTag: "unit-transcode-0", 304 MachineTag: "machine-0", 305 Location: "/mnt/bits", 306 }, 307 "unit-transcode-1": params.StorageAttachmentDetails{ 308 StorageTag: "storage-shared-fs-0", 309 UnitTag: "unit-transcode-1", 310 MachineTag: "machine-1", 311 Location: "/mnt/pieces", 312 }, 313 }, 314 }, 315 }, 316 }} 317 return results, nil 318 }