github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/crossmodel/list_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package crossmodel_test
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  
    10  	"github.com/juju/cmd"
    11  	"github.com/juju/cmd/cmdtesting"
    12  	"github.com/juju/errors"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  	"gopkg.in/juju/charm.v6"
    16  
    17  	"github.com/juju/juju/cmd/juju/crossmodel"
    18  	model "github.com/juju/juju/core/crossmodel"
    19  	"github.com/juju/juju/core/relation"
    20  )
    21  
    22  type ListSuite struct {
    23  	BaseCrossModelSuite
    24  
    25  	mockAPI *mockListAPI
    26  
    27  	applications []*model.ApplicationOfferDetails
    28  	endpoints    []charm.Relation
    29  }
    30  
    31  var _ = gc.Suite(&ListSuite{})
    32  
    33  func (s *ListSuite) SetUpTest(c *gc.C) {
    34  	s.BaseCrossModelSuite.SetUpTest(c)
    35  
    36  	s.endpoints = []charm.Relation{
    37  		{Name: "mysql", Interface: "db2", Role: charm.RoleRequirer},
    38  		{Name: "log", Interface: "http", Role: charm.RoleProvider},
    39  	}
    40  
    41  	s.applications = []*model.ApplicationOfferDetails{
    42  		s.createOfferItem("hosted-db2", "myctrl", nil),
    43  	}
    44  
    45  	s.mockAPI = &mockListAPI{
    46  		list: func(filters ...model.ApplicationOfferFilter) ([]*model.ApplicationOfferDetails, error) {
    47  			s.mockAPI.filters = filters
    48  			return s.applications, nil
    49  		},
    50  	}
    51  }
    52  
    53  func (s *ListSuite) TestListNoCurrentModel(c *gc.C) {
    54  	s.store.Models["test-master"].CurrentModel = ""
    55  	_, err := s.runList(c, nil)
    56  	c.Assert(err, gc.ErrorMatches, `current model for controller test-master not found`)
    57  }
    58  
    59  func (s *ListSuite) TestListError(c *gc.C) {
    60  	msg := "fail api"
    61  
    62  	s.mockAPI.list = func(filters ...model.ApplicationOfferFilter) ([]*model.ApplicationOfferDetails, error) {
    63  		return nil, errors.New(msg)
    64  	}
    65  
    66  	_, err := s.runList(c, nil)
    67  	c.Assert(err, gc.ErrorMatches, fmt.Sprintf(".*%v.*", msg))
    68  }
    69  
    70  func (s *ListSuite) TestListFilterArgs(c *gc.C) {
    71  	_, err := s.runList(c, []string{
    72  		"--interface", "mysql", "--application", "mysql-lite", "--connected-user", "user", "--allowed-consumer", "consumer"})
    73  	c.Assert(err, jc.ErrorIsNil)
    74  	c.Assert(s.mockAPI.filters, gc.HasLen, 1)
    75  	c.Assert(s.mockAPI.filters[0], jc.DeepEquals, model.ApplicationOfferFilter{
    76  		OwnerName:       "fred",
    77  		ModelName:       "test",
    78  		ApplicationName: "mysql-lite",
    79  		Endpoints: []model.EndpointFilterTerm{{
    80  			Interface: "mysql",
    81  		}},
    82  		ConnectedUsers:   []string{"user"},
    83  		AllowedConsumers: []string{"consumer"},
    84  	})
    85  }
    86  
    87  func (s *ListSuite) TestListOfferArg(c *gc.C) {
    88  	_, err := s.runList(c, []string{"mysql-lite"})
    89  	c.Assert(err, jc.ErrorIsNil)
    90  	c.Assert(s.mockAPI.filters, gc.HasLen, 1)
    91  	c.Assert(s.mockAPI.filters[0], jc.DeepEquals, model.ApplicationOfferFilter{
    92  		OwnerName: "fred",
    93  		ModelName: "test",
    94  		OfferName: "^mysql-lite$",
    95  	})
    96  }
    97  
    98  func (s *ListSuite) TestListFormatError(c *gc.C) {
    99  	s.applications = append(s.applications, s.createOfferItem("zdi^%", "different_store", nil))
   100  
   101  	_, err := s.runList(c, nil)
   102  	c.Assert(err, gc.ErrorMatches, ".*failed to format.*")
   103  }
   104  
   105  func (s *ListSuite) TestListSummary(c *gc.C) {
   106  	// For summary output, we don't care about the content, just the count.
   107  	conns1 := []model.OfferConnection{{Status: relation.Joined}, {}, {}}
   108  	conns2 := []model.OfferConnection{{}, {}}
   109  	// Insert in random order to check sorting.
   110  	s.applications = append(s.applications, s.createOfferItem("zdiff-db2", "differentstore", conns1))
   111  	s.applications = append(s.applications, s.createOfferItem("adiff-db2", "vendor", conns2))
   112  
   113  	s.assertValidList(
   114  		c,
   115  		[]string{"--format", "summary"},
   116  		`
   117  Offer       Application     Charm     Connected  Store           URL                                  Endpoint  Interface  Role
   118  adiff-db2   app-adiff-db2   cs:db2-5  0/2        vendor          vendor:fred/model.adiff-db2          log       http       provider
   119                                                                                                        mysql     db2        requirer
   120  hosted-db2  app-hosted-db2  cs:db2-5  0/0        myctrl          myctrl:fred/model.hosted-db2         log       http       provider
   121                                                                                                        mysql     db2        requirer
   122  zdiff-db2   app-zdiff-db2   cs:db2-5  1/3        differentstore  differentstore:fred/model.zdiff-db2  log       http       provider
   123                                                                                                        mysql     db2        requirer
   124  
   125  `[1:],
   126  		"",
   127  	)
   128  }
   129  
   130  func (s *ListSuite) TestListTabularNoConnections(c *gc.C) {
   131  	s.assertValidList(
   132  		c,
   133  		[]string{"--format", "tabular"},
   134  		`
   135  Offer       User  Relation id  Status  Endpoint  Interface  Role  Ingress subnets
   136  hosted-db2  -                                                     
   137  
   138  `[1:],
   139  		"",
   140  	)
   141  }
   142  
   143  func (s *ListSuite) setupListTabular() {
   144  	// For summary output, we don't care about the content, just the count.
   145  	conns1 := []model.OfferConnection{
   146  		{
   147  			SourceModelUUID: "model-uuid1",
   148  			Username:        "mary",
   149  			RelationId:      2,
   150  			Endpoint:        "db",
   151  			Status:          "joined",
   152  		}, {
   153  			SourceModelUUID: "model-uuid2",
   154  			Username:        "fred",
   155  			RelationId:      1,
   156  			Endpoint:        "server",
   157  			Status:          "joined",
   158  		}, {
   159  			SourceModelUUID: "model-uuid3",
   160  			Username:        "mary",
   161  			RelationId:      1,
   162  			Endpoint:        "server",
   163  			Status:          "joined",
   164  			IngressSubnets:  []string{"192.168.0.1/32", "10.0.0.0/8"},
   165  		},
   166  	}
   167  	conns2 := []model.OfferConnection{
   168  		{
   169  			SourceModelUUID: "model-uuid3",
   170  			Username:        "mary",
   171  			RelationId:      3,
   172  			Endpoint:        "db",
   173  			Status:          "joined",
   174  		},
   175  	}
   176  	// Insert in random order to check sorting.
   177  	s.applications = append(s.applications, s.createOfferItem("zdiff-db2", "differentstore", conns1))
   178  	s.applications = append(s.applications, s.createOfferItem("adiff-db2", "vendor", conns2))
   179  	s.applications[1].Endpoints = []charm.Relation{
   180  		{Name: "db", Interface: "db2", Role: charm.RoleProvider},
   181  		{Name: "server", Interface: "mysql", Role: charm.RoleProvider},
   182  	}
   183  	s.applications[2].Endpoints = []charm.Relation{
   184  		{Name: "db", Interface: "db2", Role: charm.RoleProvider},
   185  	}
   186  }
   187  
   188  func (s *ListSuite) TestListTabular(c *gc.C) {
   189  	s.setupListTabular()
   190  	s.assertValidList(
   191  		c,
   192  		[]string{"--format", "tabular"},
   193  		`
   194  Offer       User  Relation id  Status  Endpoint  Interface  Role      Ingress subnets
   195  adiff-db2   mary  3            joined  db        db2        provider  
   196  hosted-db2  -                                                         
   197  zdiff-db2   fred  1            joined  server    mysql      provider  
   198              mary  1            joined  server    mysql      provider  192.168.0.1/32,10.0.0.0/8
   199              mary  2            joined  db        db2        provider  
   200  
   201  `[1:],
   202  		"",
   203  	)
   204  }
   205  
   206  func (s *ListSuite) TestListTabularActiveOnly(c *gc.C) {
   207  	s.setupListTabular()
   208  	s.assertValidList(
   209  		c,
   210  		[]string{"--format", "tabular", "--active-only"},
   211  		`
   212  Offer      User  Relation id  Status  Endpoint  Interface  Role      Ingress subnets
   213  adiff-db2  mary  3            joined  db        db2        provider  
   214  zdiff-db2  fred  1            joined  server    mysql      provider  
   215             mary  1            joined  server    mysql      provider  192.168.0.1/32,10.0.0.0/8
   216             mary  2            joined  db        db2        provider  
   217  
   218  `[1:],
   219  		"",
   220  	)
   221  }
   222  
   223  func (s *ListSuite) TestListYAML(c *gc.C) {
   224  	// Since applications are in the map and ordering is unreliable, ensure that there is only one endpoint.
   225  	// We only need one to demonstrate display anyway :D
   226  	s.applications[0].Endpoints = []charm.Relation{{Name: "mysql", Interface: "db2", Role: charm.RoleRequirer}}
   227  	s.applications[0].Connections = []model.OfferConnection{
   228  		{
   229  			SourceModelUUID: "model-uuid",
   230  			Username:        "mary",
   231  			Status:          "joined",
   232  			Endpoint:        "db",
   233  		},
   234  		{
   235  			SourceModelUUID: "another-model-uuid",
   236  			Username:        "fred",
   237  			Status:          "error",
   238  			Message:         "firewall issue",
   239  			RelationId:      2,
   240  			Endpoint:        "http",
   241  			IngressSubnets:  []string{"192.168.0.1/32", "10.0.0.0/8"},
   242  		},
   243  	}
   244  	s.applications[0].Users = []model.OfferUserDetails{{
   245  		UserName: "fred", DisplayName: "Fred", Access: "consume",
   246  	}}
   247  
   248  	s.assertValidList(
   249  		c,
   250  		[]string{"--format", "yaml"},
   251  		`
   252  hosted-db2:
   253    application: app-hosted-db2
   254    store: myctrl
   255    charm: cs:db2-5
   256    offer-url: myctrl:fred/model.hosted-db2
   257    endpoints:
   258      mysql:
   259        interface: db2
   260        role: requirer
   261    connections:
   262    - source-model-uuid: model-uuid
   263      username: mary
   264      relation-id: 0
   265      endpoint: db
   266      status:
   267        current: joined
   268    - source-model-uuid: another-model-uuid
   269      username: fred
   270      relation-id: 2
   271      endpoint: http
   272      status:
   273        current: error
   274        message: firewall issue
   275      ingress-subnets:
   276      - 192.168.0.1/32
   277      - 10.0.0.0/8
   278    users:
   279      fred:
   280        display-name: Fred
   281        access: consume
   282  `[1:],
   283  		"",
   284  	)
   285  }
   286  
   287  func (s *ListSuite) createOfferItem(name, store string, connections []model.OfferConnection) *model.ApplicationOfferDetails {
   288  	return &model.ApplicationOfferDetails{
   289  		ApplicationName: "app-" + name,
   290  		OfferName:       name,
   291  		OfferURL:        fmt.Sprintf("%s:%s.%s", store, "fred/model", name),
   292  		CharmURL:        "cs:db2-5",
   293  		Endpoints:       s.endpoints,
   294  		Connections:     connections,
   295  	}
   296  }
   297  
   298  func (s *ListSuite) runList(c *gc.C, args []string) (*cmd.Context, error) {
   299  	return cmdtesting.RunCommand(c, crossmodel.NewListEndpointsCommandForTest(s.store, s.mockAPI), args...)
   300  }
   301  
   302  func (s *ListSuite) assertValidList(c *gc.C, args []string, expectedValid, expectedErr string) {
   303  	context, err := s.runList(c, args)
   304  	c.Assert(err, jc.ErrorIsNil)
   305  
   306  	obtainedErr := strings.Replace(cmdtesting.Stderr(context), "\n", "", -1)
   307  	c.Assert(obtainedErr, gc.Matches, expectedErr)
   308  
   309  	obtainedValid := cmdtesting.Stdout(context)
   310  	c.Assert(obtainedValid, gc.Matches, expectedValid)
   311  }
   312  
   313  type mockListAPI struct {
   314  	filters []model.ApplicationOfferFilter
   315  	list    func(filters ...model.ApplicationOfferFilter) ([]*model.ApplicationOfferDetails, error)
   316  }
   317  
   318  func (s mockListAPI) Close() error {
   319  	return nil
   320  }
   321  
   322  func (s mockListAPI) ListOffers(filters ...model.ApplicationOfferFilter) ([]*model.ApplicationOfferDetails, error) {
   323  	return s.list(filters...)
   324  }