github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/juju/storage/volumelist_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  	"time"
     9  
    10  	"github.com/juju/cmd"
    11  	"github.com/juju/errors"
    12  	jc "github.com/juju/testing/checkers"
    13  	gc "gopkg.in/check.v1"
    14  	goyaml "gopkg.in/yaml.v2"
    15  
    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"
    20  )
    21  
    22  func (s *ListSuite) TestVolumeListEmpty(c *gc.C) {
    23  	s.mockAPI.listVolumes = func([]string) ([]params.VolumeDetailsListResult, error) {
    24  		return nil, nil
    25  	}
    26  	s.assertValidVolumeList(
    27  		c,
    28  		[]string{"--format", "yaml"},
    29  		"",
    30  	)
    31  }
    32  
    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")
    36  	}
    37  	context, err := s.runVolumeList(c, "--format", "yaml")
    38  	c.Assert(errors.Cause(err), gc.ErrorMatches, "just my luck")
    39  	s.assertUserFacingVolumeOutput(c, context, "", "")
    40  }
    41  
    42  func (s *ListSuite) TestVolumeListArgs(c *gc.C) {
    43  	var called bool
    44  	expectedArgs := []string{"a", "b", "c"}
    45  	s.mockAPI.listVolumes = func(arg []string) ([]params.VolumeDetailsListResult, error) {
    46  		c.Assert(arg, jc.DeepEquals, expectedArgs)
    47  		called = true
    48  		return nil, nil
    49  	}
    50  	s.assertValidVolumeList(
    51  		c,
    52  		append([]string{"--format", "yaml"}, expectedArgs...),
    53  		"",
    54  	)
    55  	c.Assert(called, jc.IsTrue)
    56  }
    57  
    58  func (s *ListSuite) TestVolumeListYaml(c *gc.C) {
    59  	s.assertUnmarshalledVolumeOutput(
    60  		c,
    61  		goyaml.Unmarshal,
    62  		"", // no error
    63  		"--format", "yaml")
    64  }
    65  
    66  func (s *ListSuite) TestVolumeListJSON(c *gc.C) {
    67  	s.assertUnmarshalledVolumeOutput(
    68  		c,
    69  		json.Unmarshal,
    70  		"", // no error
    71  		"--format", "json")
    72  }
    73  
    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: &params.Error{Message: "bad"},
    79  		})
    80  		results = append(results, params.VolumeDetailsListResult{
    81  			Error: &params.Error{Message: "ness"},
    82  		})
    83  		return results, nil
    84  	}
    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")
    89  }
    90  
    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   
    98  1                                  3                                          42MiB   pending    
    99  
   100  `[1:]
   101  
   102  func (s *ListSuite) TestVolumeListTabular(c *gc.C) {
   103  	s.assertValidVolumeList(c, []string{}, expectedVolumeListTabular)
   104  
   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)
   109  		n := len(results)
   110  		for i := 0; i < n/2; i++ {
   111  			results[i], results[n-i-1] = results[n-i-1], results[i]
   112  		}
   113  		return results, nil
   114  	}
   115  	s.assertValidVolumeList(c, []string{}, expectedVolumeListTabular)
   116  }
   117  
   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)
   121  
   122  	var result struct {
   123  		Volumes map[string]storage.VolumeInfo
   124  	}
   125  	err = unmarshal([]byte(testing.Stdout(context)), &result)
   126  	c.Assert(err, jc.ErrorIsNil)
   127  
   128  	expected := s.expectVolume(c, nil)
   129  	c.Assert(result.Volumes, jc.DeepEquals, expected)
   130  
   131  	obtainedErr := testing.Stderr(context)
   132  	c.Assert(obtainedErr, gc.Equals, expectedErr)
   133  }
   134  
   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)
   140  
   141  	var valid []params.VolumeDetails
   142  	for _, result := range all {
   143  		if result.Error == nil {
   144  			valid = append(valid, result.Result...)
   145  		}
   146  	}
   147  	result, err := storage.ConvertToVolumeInfo(valid)
   148  	c.Assert(err, jc.ErrorIsNil)
   149  	return result
   150  }
   151  
   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, "")
   156  }
   157  
   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")...)
   161  }
   162  
   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)
   166  
   167  	obtainedErr := testing.Stderr(context)
   168  	c.Assert(obtainedErr, gc.Equals, expectedErr)
   169  }
   170  
   171  func (s mockListAPI) ListVolumes(machines []string) ([]params.VolumeDetailsListResult, error) {
   172  	if s.listVolumes != nil {
   173  		return s.listVolumes(machines)
   174  	}
   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
   178  		// abc/0.
   179  		{
   180  			VolumeTag: "volume-0-0",
   181  			Info: params.VolumeInfo{
   182  				VolumeId: "provider-supplied-volume-0-0",
   183  				Size:     512,
   184  			},
   185  			Status: createTestStatus(status.StatusAttached, ""),
   186  			MachineAttachments: map[string]params.VolumeAttachmentInfo{
   187  				"machine-0": params.VolumeAttachmentInfo{
   188  					DeviceName: "loop0",
   189  				},
   190  			},
   191  			Storage: &params.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",
   202  					},
   203  				},
   204  			},
   205  		},
   206  		// volume 1 is attaching to machine 0, but is not assigned
   207  		// to any storage.
   208  		{
   209  			VolumeTag: "volume-1",
   210  			Info: params.VolumeInfo{
   211  				VolumeId:   "provider-supplied-volume-1",
   212  				HardwareId: "serial blah blah",
   213  				Persistent: true,
   214  				Size:       2048,
   215  			},
   216  			Status: createTestStatus(status.StatusAttaching, "failed to attach, will retry"),
   217  			MachineAttachments: map[string]params.VolumeAttachmentInfo{
   218  				"machine-0": params.VolumeAttachmentInfo{},
   219  			},
   220  		},
   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.
   223  		{
   224  			VolumeTag: "volume-3",
   225  			Info: params.VolumeInfo{
   226  				Size: 42,
   227  			},
   228  			Status: createTestStatus(status.StatusPending, ""),
   229  			MachineAttachments: map[string]params.VolumeAttachmentInfo{
   230  				"machine-1": params.VolumeAttachmentInfo{},
   231  			},
   232  		},
   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.
   235  		{
   236  			VolumeTag: "volume-2",
   237  			Info: params.VolumeInfo{
   238  				VolumeId: "provider-supplied-volume-2",
   239  				Size:     3,
   240  			},
   241  			Status: createTestStatus(status.StatusAttached, ""),
   242  			MachineAttachments: map[string]params.VolumeAttachmentInfo{
   243  				"machine-1": params.VolumeAttachmentInfo{
   244  					DeviceName: "xvdf1",
   245  				},
   246  			},
   247  		},
   248  		// volume 4 is attached to machines 0 and 1, and is assigned
   249  		// to shared storage.
   250  		{
   251  			VolumeTag: "volume-4",
   252  			Info: params.VolumeInfo{
   253  				VolumeId:   "provider-supplied-volume-4",
   254  				Persistent: true,
   255  				Size:       1024,
   256  			},
   257  			Status: createTestStatus(status.StatusAttached, ""),
   258  			MachineAttachments: map[string]params.VolumeAttachmentInfo{
   259  				"machine-0": params.VolumeAttachmentInfo{
   260  					DeviceName: "xvdf2",
   261  					ReadOnly:   true,
   262  				},
   263  				"machine-1": params.VolumeAttachmentInfo{
   264  					DeviceName: "xvdf3",
   265  					ReadOnly:   true,
   266  				},
   267  			},
   268  			Storage: &params.StorageDetails{
   269  				StorageTag: "storage-shared-fs-0",
   270  				OwnerTag:   "service-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",
   279  					},
   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",
   285  					},
   286  				},
   287  			},
   288  		},
   289  	}}}
   290  	return results, nil
   291  }
   292  
   293  func createTestStatus(testStatus status.Status, message string) params.EntityStatus {
   294  	return params.EntityStatus{
   295  		Status: testStatus,
   296  		Info:   message,
   297  		Since:  &time.Time{},
   298  	}
   299  }