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 }