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: &params.Error{Message: "bad"},
    95  		})
    96  		results = append(results, params.FilesystemDetailsResult{
    97  			Error: &params.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: &params.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: &params.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: &params.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: &params.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: &params.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: &params.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: &params.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  }