github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/controller/firewaller/firewaller_base_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package firewaller_test 5 6 import ( 7 "github.com/juju/errors" 8 jc "github.com/juju/testing/checkers" 9 gc "gopkg.in/check.v1" 10 11 "github.com/juju/juju/apiserver/common" 12 "github.com/juju/juju/apiserver/facade" 13 "github.com/juju/juju/apiserver/facade/facadetest" 14 apiservertesting "github.com/juju/juju/apiserver/testing" 15 "github.com/juju/juju/core/instance" 16 "github.com/juju/juju/juju/testing" 17 "github.com/juju/juju/rpc/params" 18 "github.com/juju/juju/state" 19 statetesting "github.com/juju/juju/state/testing" 20 ) 21 22 // firewallerBaseSuite implements common testing suite for all API 23 // versions. It's not intended to be used directly or registered as a 24 // suite, but embedded. 25 type firewallerBaseSuite struct { 26 testing.JujuConnSuite 27 28 machines []*state.Machine 29 application *state.Application 30 charm *state.Charm 31 units []*state.Unit 32 relations []*state.Relation 33 34 authorizer apiservertesting.FakeAuthorizer 35 resources *common.Resources 36 } 37 38 func (s *firewallerBaseSuite) setUpTest(c *gc.C) { 39 s.JujuConnSuite.SetUpTest(c) 40 41 // Reset previous machines and units (if any) and create 3 42 // machines for the tests. 43 s.machines = nil 44 s.units = nil 45 // Note that the specific machine ids allocated are assumed 46 // to be numerically consecutive from zero. 47 for i := 0; i <= 2; i++ { 48 machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 49 c.Check(err, jc.ErrorIsNil) 50 s.machines = append(s.machines, machine) 51 } 52 // Create an application and three units for these machines. 53 s.charm = s.AddTestingCharm(c, "wordpress") 54 s.application = s.AddTestingApplication(c, "wordpress", s.charm) 55 // Add the rest of the units and assign them. 56 for i := 0; i <= 2; i++ { 57 unit, err := s.application.AddUnit(state.AddUnitParams{}) 58 c.Check(err, jc.ErrorIsNil) 59 err = unit.AssignToMachine(s.machines[i]) 60 c.Check(err, jc.ErrorIsNil) 61 s.units = append(s.units, unit) 62 } 63 64 // Create a relation. 65 s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql")) 66 eps, err := s.State.InferEndpoints("wordpress", "mysql") 67 c.Assert(err, jc.ErrorIsNil) 68 69 s.relations = make([]*state.Relation, 1) 70 s.relations[0], err = s.State.AddRelation(eps...) 71 c.Assert(err, jc.ErrorIsNil) 72 73 // Create a FakeAuthorizer so we can check permissions, 74 // set up assuming we logged in as the controller. 75 s.authorizer = apiservertesting.FakeAuthorizer{ 76 Controller: true, 77 } 78 79 // Create the resource registry separately to track invocations to 80 // Register. 81 s.resources = common.NewResources() 82 } 83 84 func (s *firewallerBaseSuite) testFirewallerFailsWithNonControllerUser( 85 c *gc.C, 86 factory func(_ facade.Context) error, 87 ) { 88 anAuthorizer := s.authorizer 89 anAuthorizer.Controller = false 90 ctx := facadetest.Context{ 91 Auth_: anAuthorizer, 92 Resources_: s.resources, 93 State_: s.State, 94 StatePool_: s.StatePool, 95 } 96 err := factory(ctx) 97 c.Assert(err, gc.NotNil) 98 c.Assert(err, gc.ErrorMatches, "permission denied") 99 } 100 101 func (s *firewallerBaseSuite) testLife( 102 c *gc.C, 103 facade interface { 104 Life(args params.Entities) (params.LifeResults, error) 105 }, 106 ) { 107 // Unassign unit 1 from its machine, so we can change its life cycle. 108 err := s.units[1].UnassignFromMachine() 109 c.Assert(err, jc.ErrorIsNil) 110 111 err = s.machines[1].EnsureDead() 112 c.Assert(err, jc.ErrorIsNil) 113 s.assertLife(c, 0, state.Alive) 114 s.assertLife(c, 1, state.Dead) 115 s.assertLife(c, 2, state.Alive) 116 117 args := addFakeEntities(params.Entities{Entities: []params.Entity{ 118 {Tag: s.machines[0].Tag().String()}, 119 {Tag: s.machines[1].Tag().String()}, 120 {Tag: s.machines[2].Tag().String()}, 121 {Tag: s.relations[0].Tag().String()}, 122 }}) 123 result, err := facade.Life(args) 124 c.Assert(err, jc.ErrorIsNil) 125 c.Assert(result, jc.DeepEquals, params.LifeResults{ 126 Results: []params.LifeResult{ 127 {Life: "alive"}, 128 {Life: "dead"}, 129 {Life: "alive"}, 130 {Life: "alive"}, 131 {Error: apiservertesting.NotFoundError("machine 42")}, 132 {Error: apiservertesting.NotFoundError(`unit "foo/0"`)}, 133 {Error: apiservertesting.NotFoundError(`application "bar"`)}, 134 {Error: apiservertesting.ErrUnauthorized}, 135 {Error: apiservertesting.ErrUnauthorized}, 136 {Error: apiservertesting.ErrUnauthorized}, 137 }, 138 }) 139 140 // Remove a machine and make sure it's detected. 141 err = s.machines[1].Remove() 142 c.Assert(err, jc.ErrorIsNil) 143 err = s.machines[1].Refresh() 144 c.Assert(err, jc.Satisfies, errors.IsNotFound) 145 146 args = params.Entities{ 147 Entities: []params.Entity{ 148 {Tag: s.machines[1].Tag().String()}, 149 }, 150 } 151 result, err = facade.Life(args) 152 c.Assert(err, jc.ErrorIsNil) 153 c.Assert(result, jc.DeepEquals, params.LifeResults{ 154 Results: []params.LifeResult{ 155 {Error: apiservertesting.NotFoundError("machine 1")}, 156 }, 157 }) 158 } 159 160 func (s *firewallerBaseSuite) testInstanceId( 161 c *gc.C, 162 facade interface { 163 InstanceId(args params.Entities) (params.StringResults, error) 164 }, 165 ) { 166 // Provision 2 machines first. 167 err := s.machines[0].SetProvisioned("i-am", "", "fake_nonce", nil) 168 c.Assert(err, jc.ErrorIsNil) 169 hwChars := instance.MustParseHardware("arch=s390x", "mem=4G") 170 err = s.machines[1].SetProvisioned("i-am-not", "", "fake_nonce", &hwChars) 171 c.Assert(err, jc.ErrorIsNil) 172 173 args := addFakeEntities(params.Entities{Entities: []params.Entity{ 174 {Tag: s.machines[0].Tag().String()}, 175 {Tag: s.machines[1].Tag().String()}, 176 {Tag: s.machines[2].Tag().String()}, 177 {Tag: s.application.Tag().String()}, 178 {Tag: s.units[2].Tag().String()}, 179 }}) 180 result, err := facade.InstanceId(args) 181 c.Assert(err, jc.ErrorIsNil) 182 c.Assert(result, jc.DeepEquals, params.StringResults{ 183 Results: []params.StringResult{ 184 {Result: "i-am"}, 185 {Result: "i-am-not"}, 186 {Error: apiservertesting.NotProvisionedError("2")}, 187 {Error: apiservertesting.ErrUnauthorized}, 188 {Error: apiservertesting.ErrUnauthorized}, 189 {Error: apiservertesting.NotFoundError("machine 42")}, 190 {Error: apiservertesting.ErrUnauthorized}, 191 {Error: apiservertesting.ErrUnauthorized}, 192 {Error: apiservertesting.ErrUnauthorized}, 193 {Error: apiservertesting.ErrUnauthorized}, 194 {Error: apiservertesting.ErrUnauthorized}, 195 }, 196 }) 197 } 198 199 func (s *firewallerBaseSuite) testWatchModelMachines( 200 c *gc.C, 201 facade interface { 202 WatchModelMachines() (params.StringsWatchResult, error) 203 }, 204 ) { 205 c.Assert(s.resources.Count(), gc.Equals, 0) 206 207 got, err := facade.WatchModelMachines() 208 c.Assert(err, jc.ErrorIsNil) 209 want := params.StringsWatchResult{ 210 StringsWatcherId: "1", 211 Changes: []string{"0", "1", "2"}, 212 } 213 c.Assert(got.StringsWatcherId, gc.Equals, want.StringsWatcherId) 214 c.Assert(got.Changes, jc.SameContents, want.Changes) 215 216 // Verify the resources were registered and stop them when done. 217 c.Assert(s.resources.Count(), gc.Equals, 1) 218 resource := s.resources.Get("1") 219 defer statetesting.AssertStop(c, resource) 220 221 // Check that the Watch has consumed the initial event ("returned" 222 // in the Watch call) 223 wc := statetesting.NewStringsWatcherC(c, resource.(state.StringsWatcher)) 224 wc.AssertNoChange() 225 } 226 227 const ( 228 cannotWatchUnits = false 229 ) 230 231 func (s *firewallerBaseSuite) testWatch( 232 c *gc.C, 233 watcher interface { 234 Watch(args params.Entities) (params.NotifyWatchResults, error) 235 }, 236 allowUnits bool, 237 ) { 238 c.Assert(s.resources.Count(), gc.Equals, 0) 239 s.WaitForModelWatchersIdle(c, s.Model.UUID()) 240 241 args := addFakeEntities(params.Entities{Entities: []params.Entity{ 242 {Tag: s.machines[0].Tag().String()}, 243 {Tag: s.application.Tag().String()}, 244 {Tag: s.units[0].Tag().String()}, 245 }}) 246 result, err := watcher.Watch(args) 247 c.Assert(err, jc.ErrorIsNil) 248 if allowUnits { 249 c.Assert(result, jc.DeepEquals, params.NotifyWatchResults{ 250 Results: []params.NotifyWatchResult{ 251 {Error: apiservertesting.ErrUnauthorized}, 252 {NotifyWatcherId: "1"}, 253 {NotifyWatcherId: "2"}, 254 {Error: apiservertesting.ErrUnauthorized}, 255 {Error: apiservertesting.NotFoundError(`unit "foo/0"`)}, 256 {Error: apiservertesting.NotFoundError(`application "bar"`)}, 257 {Error: apiservertesting.ErrUnauthorized}, 258 {Error: apiservertesting.ErrUnauthorized}, 259 {Error: apiservertesting.ErrUnauthorized}, 260 }, 261 }) 262 } else { 263 c.Assert(result, jc.DeepEquals, params.NotifyWatchResults{ 264 Results: []params.NotifyWatchResult{ 265 {Error: apiservertesting.ErrUnauthorized}, 266 {NotifyWatcherId: "1"}, 267 {Error: apiservertesting.ErrUnauthorized}, 268 {Error: apiservertesting.ErrUnauthorized}, 269 {Error: apiservertesting.ErrUnauthorized}, 270 {Error: apiservertesting.NotFoundError(`application "bar"`)}, 271 {Error: apiservertesting.ErrUnauthorized}, 272 {Error: apiservertesting.ErrUnauthorized}, 273 {Error: apiservertesting.ErrUnauthorized}, 274 }, 275 }) 276 } 277 278 // Verify the resources were registered and stop when done. 279 if allowUnits { 280 c.Assert(s.resources.Count(), gc.Equals, 2) 281 } else { 282 c.Assert(s.resources.Count(), gc.Equals, 1) 283 } 284 c.Assert(result.Results[1].NotifyWatcherId, gc.Equals, "1") 285 watcher1 := s.resources.Get("1") 286 defer statetesting.AssertStop(c, watcher1) 287 var watcher2 facade.Resource 288 if allowUnits { 289 c.Assert(result.Results[2].NotifyWatcherId, gc.Equals, "2") 290 watcher2 = s.resources.Get("2") 291 defer statetesting.AssertStop(c, watcher2) 292 } 293 294 // Check that the Watch has consumed the initial event ("returned" in 295 // the Watch call) 296 wc1 := statetesting.NewNotifyWatcherC(c, watcher1.(state.NotifyWatcher)) 297 wc1.AssertNoChange() 298 if allowUnits { 299 wc2 := statetesting.NewNotifyWatcherC(c, watcher2.(state.NotifyWatcher)) 300 wc2.AssertNoChange() 301 } 302 } 303 304 func (s *firewallerBaseSuite) testWatchUnits( 305 c *gc.C, 306 facade interface { 307 WatchUnits(args params.Entities) (params.StringsWatchResults, error) 308 }, 309 ) { 310 c.Assert(s.resources.Count(), gc.Equals, 0) 311 312 args := addFakeEntities(params.Entities{Entities: []params.Entity{ 313 {Tag: s.machines[0].Tag().String()}, 314 {Tag: s.application.Tag().String()}, 315 {Tag: s.units[0].Tag().String()}, 316 }}) 317 result, err := facade.WatchUnits(args) 318 c.Assert(err, jc.ErrorIsNil) 319 c.Assert(result, jc.DeepEquals, params.StringsWatchResults{ 320 Results: []params.StringsWatchResult{ 321 {Changes: []string{"wordpress/0"}, StringsWatcherId: "1"}, 322 {Error: apiservertesting.ErrUnauthorized}, 323 {Error: apiservertesting.ErrUnauthorized}, 324 {Error: apiservertesting.NotFoundError("machine 42")}, 325 {Error: apiservertesting.ErrUnauthorized}, 326 {Error: apiservertesting.ErrUnauthorized}, 327 {Error: apiservertesting.ErrUnauthorized}, 328 {Error: apiservertesting.ErrUnauthorized}, 329 {Error: apiservertesting.ErrUnauthorized}, 330 }, 331 }) 332 333 // Verify the resource was registered and stop when done 334 c.Assert(s.resources.Count(), gc.Equals, 1) 335 c.Assert(result.Results[0].StringsWatcherId, gc.Equals, "1") 336 resource := s.resources.Get("1") 337 defer statetesting.AssertStop(c, resource) 338 339 // Check that the Watch has consumed the initial event ("returned" in 340 // the Watch call) 341 wc := statetesting.NewStringsWatcherC(c, resource.(state.StringsWatcher)) 342 wc.AssertNoChange() 343 } 344 345 func (s *firewallerBaseSuite) testGetAssignedMachine( 346 c *gc.C, 347 facade interface { 348 GetAssignedMachine(args params.Entities) (params.StringResults, error) 349 }, 350 ) { 351 // Unassign a unit first. 352 err := s.units[2].UnassignFromMachine() 353 c.Assert(err, jc.ErrorIsNil) 354 355 args := addFakeEntities(params.Entities{Entities: []params.Entity{ 356 {Tag: s.units[0].Tag().String()}, 357 {Tag: s.units[1].Tag().String()}, 358 {Tag: s.units[2].Tag().String()}, 359 }}) 360 result, err := facade.GetAssignedMachine(args) 361 c.Assert(err, jc.ErrorIsNil) 362 c.Assert(result, jc.DeepEquals, params.StringResults{ 363 Results: []params.StringResult{ 364 {Result: s.machines[0].Tag().String()}, 365 {Result: s.machines[1].Tag().String()}, 366 {Error: apiservertesting.NotAssignedError("wordpress/2")}, 367 {Error: apiservertesting.ErrUnauthorized}, 368 {Error: apiservertesting.NotFoundError(`unit "foo/0"`)}, 369 {Error: apiservertesting.ErrUnauthorized}, 370 {Error: apiservertesting.ErrUnauthorized}, 371 {Error: apiservertesting.ErrUnauthorized}, 372 {Error: apiservertesting.ErrUnauthorized}, 373 }, 374 }) 375 376 // Now reset assign unit 2 again and check. 377 err = s.units[2].AssignToMachine(s.machines[0]) 378 c.Assert(err, jc.ErrorIsNil) 379 380 args = params.Entities{Entities: []params.Entity{ 381 {Tag: s.units[2].Tag().String()}, 382 }} 383 result, err = facade.GetAssignedMachine(args) 384 c.Assert(err, jc.ErrorIsNil) 385 c.Assert(result, jc.DeepEquals, params.StringResults{ 386 Results: []params.StringResult{ 387 {Result: s.machines[0].Tag().String()}, 388 }, 389 }) 390 } 391 392 func (s *firewallerBaseSuite) assertLife(c *gc.C, index int, expectLife state.Life) { 393 err := s.machines[index].Refresh() 394 c.Assert(err, jc.ErrorIsNil) 395 c.Assert(s.machines[index].Life(), gc.Equals, expectLife) 396 } 397 398 var commonFakeEntities = []params.Entity{ 399 {Tag: "machine-42"}, 400 {Tag: "unit-foo-0"}, 401 {Tag: "application-bar"}, 402 {Tag: "user-foo"}, 403 {Tag: "foo-bar"}, 404 {Tag: ""}, 405 } 406 407 func addFakeEntities(actual params.Entities) params.Entities { 408 for _, entity := range commonFakeEntities { 409 actual.Entities = append(actual.Entities, entity) 410 } 411 return actual 412 }