github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/applicationofferuser_test.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state_test 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/names/v5" 9 jc "github.com/juju/testing/checkers" 10 "github.com/juju/utils/v3" 11 gc "gopkg.in/check.v1" 12 13 "github.com/juju/juju/core/crossmodel" 14 "github.com/juju/juju/core/permission" 15 "github.com/juju/juju/state" 16 "github.com/juju/juju/testing/factory" 17 ) 18 19 type ApplicationOfferUserSuite struct { 20 ConnSuite 21 } 22 23 var _ = gc.Suite(&ApplicationOfferUserSuite{}) 24 25 func (s *ApplicationOfferUserSuite) makeOffer(c *gc.C, access permission.Access) (*crossmodel.ApplicationOffer, names.UserTag) { 26 s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql")) 27 offers := state.NewApplicationOffers(s.State) 28 offer, err := offers.AddOffer(crossmodel.AddApplicationOfferArgs{ 29 OfferName: "someoffer", 30 ApplicationName: "mysql", 31 Owner: "test-admin", 32 HasRead: []string{"everyone@external"}, 33 }) 34 c.Assert(err, jc.ErrorIsNil) 35 36 user := s.Factory.MakeUser(c, 37 &factory.UserParams{ 38 Name: "validusername", 39 Access: permission.ReadAccess, 40 }) 41 42 // Initially no access. 43 _, err = s.State.GetOfferAccess(offer.OfferUUID, user.UserTag()) 44 c.Assert(err, jc.Satisfies, errors.IsNotFound) 45 46 err = s.State.CreateOfferAccess(names.NewApplicationOfferTag(offer.OfferUUID), user.UserTag(), access) 47 c.Assert(err, jc.ErrorIsNil) 48 return offer, user.UserTag() 49 } 50 51 func (s *ApplicationOfferUserSuite) assertAddOffer(c *gc.C, wantedAccess permission.Access) string { 52 offer, user := s.makeOffer(c, wantedAccess) 53 54 access, err := s.State.GetOfferAccess(offer.OfferUUID, user) 55 c.Assert(err, jc.ErrorIsNil) 56 c.Assert(access, gc.Equals, wantedAccess) 57 58 // Creator of offer has admin. 59 access, err = s.State.GetOfferAccess(offer.OfferUUID, names.NewUserTag("test-admin")) 60 c.Assert(err, jc.ErrorIsNil) 61 c.Assert(access, gc.Equals, permission.AdminAccess) 62 63 // Everyone has read. 64 access, err = s.State.GetOfferAccess(offer.OfferUUID, names.NewUserTag("everyone@external")) 65 c.Assert(err, jc.ErrorIsNil) 66 c.Assert(access, gc.Equals, permission.ReadAccess) 67 return offer.OfferUUID 68 } 69 70 func (s *ApplicationOfferUserSuite) TestAddReadOnlyOfferUser(c *gc.C) { 71 s.assertAddOffer(c, permission.ReadAccess) 72 } 73 74 func (s *ApplicationOfferUserSuite) TestAddConsumeOfferUser(c *gc.C) { 75 s.assertAddOffer(c, permission.ConsumeAccess) 76 } 77 78 func (s *ApplicationOfferUserSuite) TestGetOfferAccess(c *gc.C) { 79 offerUUID := s.assertAddOffer(c, permission.ConsumeAccess) 80 users, err := s.State.GetOfferUsers(offerUUID) 81 c.Assert(err, jc.ErrorIsNil) 82 c.Assert(users, jc.DeepEquals, map[string]permission.Access{ 83 "everyone@external": permission.ReadAccess, 84 "test-admin": permission.AdminAccess, 85 "validusername": permission.ConsumeAccess, 86 }) 87 } 88 89 func (s *ApplicationOfferUserSuite) TestAddAdminModelUser(c *gc.C) { 90 s.assertAddOffer(c, permission.AdminAccess) 91 } 92 93 func (s *ApplicationOfferUserSuite) TestUpdateOfferAccess(c *gc.C) { 94 offer, user := s.makeOffer(c, permission.AdminAccess) 95 err := s.State.UpdateOfferAccess(names.NewApplicationOfferTag(offer.OfferUUID), user, permission.ReadAccess) 96 c.Assert(err, jc.ErrorIsNil) 97 98 access, err := s.State.GetOfferAccess(offer.OfferUUID, user) 99 c.Assert(err, jc.ErrorIsNil) 100 c.Assert(access, gc.Equals, permission.ReadAccess) 101 } 102 103 func (s *ApplicationOfferUserSuite) setupOfferRelation(c *gc.C, offerUUID, user string) *state.Relation { 104 // Make a relation to the offer. 105 wordpress := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 106 wordpressEP, err := wordpress.Endpoint("db") 107 c.Assert(err, jc.ErrorIsNil) 108 mysql, err := s.State.Application("mysql") 109 c.Assert(err, jc.ErrorIsNil) 110 mysqlEP, err := mysql.Endpoint("server") 111 c.Assert(err, jc.ErrorIsNil) 112 rel, err := s.State.AddRelation(wordpressEP, mysqlEP) 113 c.Assert(err, jc.ErrorIsNil) 114 _, err = s.State.AddOfferConnection(state.AddOfferConnectionParams{ 115 SourceModelUUID: utils.MustNewUUID().String(), 116 OfferUUID: offerUUID, 117 RelationKey: rel.Tag().Id(), 118 RelationId: rel.Id(), 119 Username: user, 120 }) 121 c.Assert(err, jc.ErrorIsNil) 122 return rel 123 } 124 125 func (s *ApplicationOfferUserSuite) TestUpdateOfferAccessSetsRelationSuspended(c *gc.C) { 126 offer, user := s.makeOffer(c, permission.ConsumeAccess) 127 rel := s.setupOfferRelation(c, offer.OfferUUID, user.Name()) 128 129 // Downgrade consume access and check the relation is suspended. 130 err := s.State.UpdateOfferAccess(names.NewApplicationOfferTag(offer.OfferUUID), user, permission.ReadAccess) 131 c.Assert(err, jc.ErrorIsNil) 132 err = rel.Refresh() 133 c.Assert(err, jc.ErrorIsNil) 134 c.Assert(rel.Suspended(), jc.IsTrue) 135 } 136 137 func (s *ApplicationOfferUserSuite) TestUpdateOfferAccessSetsRelationSuspendedRace(c *gc.C) { 138 offer, user := s.makeOffer(c, permission.ConsumeAccess) 139 rel := s.setupOfferRelation(c, offer.OfferUUID, user.Name()) 140 var rel2 *state.Relation 141 142 defer state.SetBeforeHooks(c, s.State, func() { 143 // Add another relation to the offered app. 144 curl := "local:quantal/quantal-wordpress-3" 145 wpch, err := s.State.Charm(curl) 146 c.Assert(err, jc.ErrorIsNil) 147 wordpress2 := s.AddTestingApplication(c, "wordpress2", wpch) 148 wordpressEP, err := wordpress2.Endpoint("db") 149 c.Assert(err, jc.ErrorIsNil) 150 mysql, err := s.State.Application("mysql") 151 c.Assert(err, jc.ErrorIsNil) 152 mysqlEP, err := mysql.Endpoint("server") 153 c.Assert(err, jc.ErrorIsNil) 154 rel2, err = s.State.AddRelation(wordpressEP, mysqlEP) 155 c.Assert(err, jc.ErrorIsNil) 156 _, err = s.State.AddOfferConnection(state.AddOfferConnectionParams{ 157 SourceModelUUID: utils.MustNewUUID().String(), 158 OfferUUID: offer.OfferUUID, 159 RelationKey: rel2.Tag().Id(), 160 RelationId: rel2.Id(), 161 Username: user.Name(), 162 }) 163 c.Assert(err, jc.ErrorIsNil) 164 }).Check() 165 166 // Downgrade consume access and check both relations are suspended. 167 err := s.State.UpdateOfferAccess(names.NewApplicationOfferTag(offer.OfferUUID), user, permission.ReadAccess) 168 c.Assert(err, jc.ErrorIsNil) 169 err = rel.Refresh() 170 c.Assert(err, jc.ErrorIsNil) 171 c.Assert(rel.Suspended(), jc.IsTrue) 172 err = rel2.Refresh() 173 c.Assert(err, jc.ErrorIsNil) 174 c.Assert(rel2.Suspended(), jc.IsTrue) 175 } 176 177 func (s *ApplicationOfferUserSuite) TestCreateOfferAccessNoUserFails(c *gc.C) { 178 app := s.Factory.MakeApplication(c, nil) 179 offers := state.NewApplicationOffers(s.State) 180 _, err := offers.AddOffer(crossmodel.AddApplicationOfferArgs{ 181 OfferName: "someoffer", 182 ApplicationName: app.Name(), 183 Owner: "test-admin", 184 }) 185 c.Assert(err, jc.ErrorIsNil) 186 err = s.State.CreateOfferAccess( 187 names.NewApplicationOfferTag("f47ac10b-58cc-4372-a567-0e02b2c3d479"), 188 names.NewUserTag("validusername"), permission.ReadAccess) 189 c.Assert(err, gc.ErrorMatches, `user "validusername" does not exist locally: user "validusername" not found`) 190 } 191 192 func (s *ApplicationOfferUserSuite) TestRemoveOfferAccess(c *gc.C) { 193 offer, user := s.makeOffer(c, permission.ConsumeAccess) 194 195 err := s.State.RemoveOfferAccess(names.NewApplicationOfferTag(offer.OfferUUID), user) 196 c.Assert(err, jc.ErrorIsNil) 197 198 _, err = s.State.GetOfferAccess(offer.OfferUUID, user) 199 c.Assert(err, jc.Satisfies, errors.IsNotFound) 200 } 201 202 func (s *ApplicationOfferUserSuite) TestRemoveOfferAccessNoUser(c *gc.C) { 203 offer, _ := s.makeOffer(c, permission.ConsumeAccess) 204 err := s.State.RemoveOfferAccess(names.NewApplicationOfferTag(offer.OfferUUID), names.NewUserTag("fred")) 205 c.Assert(err, jc.Satisfies, errors.IsNotFound) 206 } 207 208 func (s *ApplicationOfferUserSuite) TestRemoveOfferAccessSetsRelationSuspended(c *gc.C) { 209 offer, user := s.makeOffer(c, permission.ConsumeAccess) 210 rel := s.setupOfferRelation(c, offer.OfferUUID, user.Name()) 211 212 // Remove any access and check the relation is suspended. 213 err := s.State.RemoveOfferAccess(names.NewApplicationOfferTag(offer.OfferUUID), user) 214 c.Assert(err, jc.ErrorIsNil) 215 err = rel.Refresh() 216 c.Assert(err, jc.ErrorIsNil) 217 c.Assert(rel.Suspended(), jc.IsTrue) 218 }