github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/testing/factory/factory.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package factory 5 6 import ( 7 "fmt" 8 "math/rand" 9 "strconv" 10 "sync/atomic" 11 "time" 12 13 "github.com/juju/names" 14 jc "github.com/juju/testing/checkers" 15 "github.com/juju/utils" 16 gc "gopkg.in/check.v1" 17 "gopkg.in/juju/charm.v5" 18 19 "github.com/juju/juju/environs" 20 "github.com/juju/juju/instance" 21 "github.com/juju/juju/network" 22 "github.com/juju/juju/state" 23 "github.com/juju/juju/testcharms" 24 "github.com/juju/juju/testing" 25 ) 26 27 const ( 28 symbols = "abcdefghijklmopqrstuvwxyz" 29 ) 30 31 type Factory struct { 32 st *state.State 33 } 34 35 var index uint32 36 37 func NewFactory(st *state.State) *Factory { 38 return &Factory{st: st} 39 } 40 41 type UserParams struct { 42 Name string 43 DisplayName string 44 Password string 45 Creator names.Tag 46 NoEnvUser bool 47 Disabled bool 48 } 49 50 // EnvUserParams defines the parameters for creating an environment user. 51 type EnvUserParams struct { 52 User string 53 DisplayName string 54 CreatedBy names.Tag 55 } 56 57 // CharmParams defines the parameters for creating a charm. 58 type CharmParams struct { 59 Name string 60 Series string 61 Revision string 62 URL string 63 } 64 65 // Params for creating a machine. 66 type MachineParams struct { 67 Series string 68 Jobs []state.MachineJob 69 Password string 70 Nonce string 71 InstanceId instance.Id 72 Characteristics *instance.HardwareCharacteristics 73 Addresses []network.Address 74 Volumes []state.MachineVolumeParams 75 Filesystems []state.MachineFilesystemParams 76 } 77 78 // ServiceParams is used when specifying parameters for a new service. 79 type ServiceParams struct { 80 Name string 81 Charm *state.Charm 82 Creator names.Tag 83 } 84 85 // UnitParams are used to create units. 86 type UnitParams struct { 87 Service *state.Service 88 Machine *state.Machine 89 Password string 90 SetCharmURL bool 91 } 92 93 // RelationParams are used to create relations. 94 type RelationParams struct { 95 Endpoints []state.Endpoint 96 } 97 98 type MetricParams struct { 99 Unit *state.Unit 100 Time *time.Time 101 Metrics []state.Metric 102 Sent bool 103 } 104 105 type EnvParams struct { 106 Name string 107 Owner names.Tag 108 ConfigAttrs testing.Attrs 109 Prepare bool 110 } 111 112 // RandomSuffix adds a random 5 character suffix to the presented string. 113 func (*Factory) RandomSuffix(prefix string) string { 114 result := prefix 115 for i := 0; i < 5; i++ { 116 result += string(symbols[rand.Intn(len(symbols))]) 117 } 118 return result 119 } 120 121 func uniqueInteger() int { 122 return int(atomic.AddUint32(&index, 1)) 123 } 124 125 func uniqueString(prefix string) string { 126 if prefix == "" { 127 prefix = "no-prefix" 128 } 129 return fmt.Sprintf("%s-%d", prefix, uniqueInteger()) 130 } 131 132 // MakeUser will create a user with values defined by the params. 133 // For attributes of UserParams that are the default empty values, 134 // some meaningful valid values are used instead. 135 // If params is not specified, defaults are used. 136 func (factory *Factory) MakeUser(c *gc.C, params *UserParams) *state.User { 137 if params == nil { 138 params = &UserParams{} 139 } 140 if params.Name == "" { 141 params.Name = uniqueString("username") 142 } 143 if params.DisplayName == "" { 144 params.DisplayName = uniqueString("display name") 145 } 146 if params.Password == "" { 147 params.Password = "password" 148 } 149 if params.Creator == nil { 150 env, err := factory.st.Environment() 151 c.Assert(err, jc.ErrorIsNil) 152 params.Creator = env.Owner() 153 } 154 creatorUserTag := params.Creator.(names.UserTag) 155 user, err := factory.st.AddUser( 156 params.Name, params.DisplayName, params.Password, creatorUserTag.Name()) 157 c.Assert(err, jc.ErrorIsNil) 158 if !params.NoEnvUser { 159 _, err := factory.st.AddEnvironmentUser(user.UserTag(), names.NewUserTag(user.CreatedBy()), params.DisplayName) 160 c.Assert(err, jc.ErrorIsNil) 161 } 162 if params.Disabled { 163 err := user.Disable() 164 c.Assert(err, jc.ErrorIsNil) 165 } 166 return user 167 } 168 169 // MakeEnvUser will create a envUser with values defined by the params. For 170 // attributes of EnvUserParams that are the default empty values, some 171 // meaningful valid values are used instead. If params is not specified, 172 // defaults are used. 173 func (factory *Factory) MakeEnvUser(c *gc.C, params *EnvUserParams) *state.EnvironmentUser { 174 if params == nil { 175 params = &EnvUserParams{} 176 } 177 if params.User == "" { 178 user := factory.MakeUser(c, &UserParams{NoEnvUser: true}) 179 params.User = user.UserTag().Username() 180 } 181 if params.DisplayName == "" { 182 params.DisplayName = uniqueString("display name") 183 } 184 if params.CreatedBy == nil { 185 env, err := factory.st.Environment() 186 c.Assert(err, jc.ErrorIsNil) 187 params.CreatedBy = env.Owner() 188 } 189 createdByUserTag := params.CreatedBy.(names.UserTag) 190 envUser, err := factory.st.AddEnvironmentUser(names.NewUserTag(params.User), createdByUserTag, params.DisplayName) 191 c.Assert(err, jc.ErrorIsNil) 192 return envUser 193 } 194 195 func (factory *Factory) paramsFillDefaults(c *gc.C, params *MachineParams) *MachineParams { 196 if params == nil { 197 params = &MachineParams{} 198 } 199 if params.Series == "" { 200 params.Series = "quantal" 201 } 202 if params.Nonce == "" { 203 params.Nonce = "nonce" 204 } 205 if len(params.Jobs) == 0 { 206 params.Jobs = []state.MachineJob{state.JobHostUnits} 207 } 208 if params.InstanceId == "" { 209 params.InstanceId = instance.Id(uniqueString("id")) 210 } 211 if params.Password == "" { 212 var err error 213 params.Password, err = utils.RandomPassword() 214 c.Assert(err, jc.ErrorIsNil) 215 } 216 return params 217 } 218 219 func machineParamsToTemplate(p *MachineParams) state.MachineTemplate { 220 return state.MachineTemplate{ 221 Series: p.Series, 222 Nonce: p.Nonce, 223 Jobs: p.Jobs, 224 InstanceId: p.InstanceId, 225 } 226 } 227 228 // MakeMachineNested will make a machine nested in the machine with ID given. 229 func (factory *Factory) MakeMachineNested(c *gc.C, parentId string, params *MachineParams) *state.Machine { 230 params = factory.paramsFillDefaults(c, params) 231 mTmpl := machineParamsToTemplate(params) 232 // Cannot specify an instance id for a new container. 233 mTmpl.InstanceId = "" 234 // Cannot specify a nonce without an instance ID. 235 mTmpl.Nonce = "" 236 237 m, err := factory.st.AddMachineInsideMachine( 238 mTmpl, 239 parentId, 240 instance.LXC, 241 ) 242 c.Assert(err, jc.ErrorIsNil) 243 return m 244 } 245 246 // MakeMachine will add a machine with values defined in params. For some 247 // values in params, if they are missing, some meaningful empty values will be 248 // set. 249 // If params is not specified, defaults are used. 250 func (factory *Factory) MakeMachine(c *gc.C, params *MachineParams) *state.Machine { 251 machine, _ := factory.MakeMachineReturningPassword(c, params) 252 return machine 253 } 254 255 // MakeMachineReturningPassword will add a machine with values defined in 256 // params. For some values in params, if they are missing, some meaningful 257 // empty values will be set. If params is not specified, defaults are used. 258 // The machine and its password are returned. 259 func (factory *Factory) MakeMachineReturningPassword(c *gc.C, params *MachineParams) (*state.Machine, string) { 260 params = factory.paramsFillDefaults(c, params) 261 machineTemplate := state.MachineTemplate{ 262 Series: params.Series, 263 Jobs: params.Jobs, 264 Volumes: params.Volumes, 265 Filesystems: params.Filesystems, 266 } 267 machine, err := factory.st.AddOneMachine(machineTemplate) 268 c.Assert(err, jc.ErrorIsNil) 269 err = machine.SetProvisioned(params.InstanceId, params.Nonce, params.Characteristics) 270 c.Assert(err, jc.ErrorIsNil) 271 err = machine.SetPassword(params.Password) 272 c.Assert(err, jc.ErrorIsNil) 273 if len(params.Addresses) > 0 { 274 err := machine.SetProviderAddresses(params.Addresses...) 275 c.Assert(err, jc.ErrorIsNil) 276 } 277 return machine, params.Password 278 } 279 280 // MakeCharm creates a charm with the values specified in params. 281 // Sensible default values are substituted for missing ones. 282 // Supported charms depend on the charm/testing package. 283 // Currently supported charms: 284 // all-hooks, category, dummy, format2, logging, monitoring, mysql, 285 // mysql-alternative, riak, terracotta, upgrade1, upgrade2, varnish, 286 // varnish-alternative, wordpress. 287 // If params is not specified, defaults are used. 288 func (factory *Factory) MakeCharm(c *gc.C, params *CharmParams) *state.Charm { 289 if params == nil { 290 params = &CharmParams{} 291 } 292 if params.Name == "" { 293 params.Name = "mysql" 294 } 295 if params.Series == "" { 296 params.Series = "quantal" 297 } 298 if params.Revision == "" { 299 params.Revision = fmt.Sprintf("%d", uniqueInteger()) 300 } 301 if params.URL == "" { 302 params.URL = fmt.Sprintf("cs:%s/%s-%s", params.Series, params.Name, params.Revision) 303 } 304 305 ch := testcharms.Repo.CharmDir(params.Name) 306 307 curl := charm.MustParseURL(params.URL) 308 bundleSHA256 := uniqueString("bundlesha") 309 charm, err := factory.st.AddCharm(ch, curl, "fake-storage-path", bundleSHA256) 310 c.Assert(err, jc.ErrorIsNil) 311 return charm 312 } 313 314 // MakeService creates a service with the specified parameters, substituting 315 // sane defaults for missing values. 316 // If params is not specified, defaults are used. 317 func (factory *Factory) MakeService(c *gc.C, params *ServiceParams) *state.Service { 318 if params == nil { 319 params = &ServiceParams{} 320 } 321 if params.Charm == nil { 322 params.Charm = factory.MakeCharm(c, nil) 323 } 324 if params.Name == "" { 325 params.Name = params.Charm.Meta().Name 326 } 327 if params.Creator == nil { 328 creator := factory.MakeUser(c, nil) 329 params.Creator = creator.Tag() 330 } 331 _ = params.Creator.(names.UserTag) 332 service, err := factory.st.AddService(params.Name, params.Creator.String(), params.Charm, nil, nil) 333 c.Assert(err, jc.ErrorIsNil) 334 return service 335 } 336 337 // MakeUnit creates a service unit with specified params, filling in 338 // sane defaults for missing values. 339 // If params is not specified, defaults are used. 340 func (factory *Factory) MakeUnit(c *gc.C, params *UnitParams) *state.Unit { 341 unit, _ := factory.MakeUnitReturningPassword(c, params) 342 return unit 343 } 344 345 // MakeUnit creates a service unit with specified params, filling in sane 346 // defaults for missing values. If params is not specified, defaults are used. 347 // The unit and its password are returned. 348 func (factory *Factory) MakeUnitReturningPassword(c *gc.C, params *UnitParams) (*state.Unit, string) { 349 if params == nil { 350 params = &UnitParams{} 351 } 352 if params.Machine == nil { 353 params.Machine = factory.MakeMachine(c, nil) 354 } 355 if params.Service == nil { 356 params.Service = factory.MakeService(c, nil) 357 } 358 if params.Password == "" { 359 var err error 360 params.Password, err = utils.RandomPassword() 361 c.Assert(err, jc.ErrorIsNil) 362 } 363 unit, err := params.Service.AddUnit() 364 c.Assert(err, jc.ErrorIsNil) 365 err = unit.AssignToMachine(params.Machine) 366 c.Assert(err, jc.ErrorIsNil) 367 if params.SetCharmURL { 368 serviceCharmURL, _ := params.Service.CharmURL() 369 err = unit.SetCharmURL(serviceCharmURL) 370 c.Assert(err, jc.ErrorIsNil) 371 } 372 err = unit.SetPassword(params.Password) 373 c.Assert(err, jc.ErrorIsNil) 374 375 return unit, params.Password 376 } 377 378 // MakeMetric makes a metric with specified params, filling in 379 // sane defaults for missing values. 380 // If params is not specified, defaults are used. 381 func (factory *Factory) MakeMetric(c *gc.C, params *MetricParams) *state.MetricBatch { 382 now := time.Now().Round(time.Second).UTC() 383 if params == nil { 384 params = &MetricParams{} 385 } 386 if params.Unit == nil { 387 meteredCharm := factory.MakeCharm(c, &CharmParams{Name: "metered", URL: "cs:quantal/metered"}) 388 meteredService := factory.MakeService(c, &ServiceParams{Charm: meteredCharm}) 389 params.Unit = factory.MakeUnit(c, &UnitParams{Service: meteredService, SetCharmURL: true}) 390 } 391 if params.Time == nil { 392 params.Time = &now 393 } 394 if params.Metrics == nil { 395 params.Metrics = []state.Metric{{"pings", strconv.Itoa(uniqueInteger()), *params.Time}} 396 } 397 398 chURL, ok := params.Unit.CharmURL() 399 c.Assert(ok, gc.Equals, true) 400 401 metric, err := params.Unit.AddMetrics(utils.MustNewUUID().String(), *params.Time, chURL.String(), params.Metrics) 402 c.Assert(err, jc.ErrorIsNil) 403 if params.Sent { 404 err := metric.SetSent() 405 c.Assert(err, jc.ErrorIsNil) 406 } 407 return metric 408 } 409 410 // MakeRelation create a relation with specified params, filling in sane 411 // defaults for missing values. 412 // If params is not specified, defaults are used. 413 func (factory *Factory) MakeRelation(c *gc.C, params *RelationParams) *state.Relation { 414 if params == nil { 415 params = &RelationParams{} 416 } 417 if len(params.Endpoints) == 0 { 418 s1 := factory.MakeService(c, &ServiceParams{ 419 Charm: factory.MakeCharm(c, &CharmParams{ 420 Name: "mysql", 421 }), 422 }) 423 e1, err := s1.Endpoint("server") 424 c.Assert(err, jc.ErrorIsNil) 425 426 s2 := factory.MakeService(c, &ServiceParams{ 427 Charm: factory.MakeCharm(c, &CharmParams{ 428 Name: "wordpress", 429 }), 430 }) 431 e2, err := s2.Endpoint("db") 432 c.Assert(err, jc.ErrorIsNil) 433 434 params.Endpoints = []state.Endpoint{e1, e2} 435 } 436 437 relation, err := factory.st.AddRelation(params.Endpoints...) 438 c.Assert(err, jc.ErrorIsNil) 439 440 return relation 441 } 442 443 // MakeEnvironment creates an environment with specified params, 444 // filling in sane defaults for missing values. If params is nil, 445 // defaults are used for all values. 446 // 447 // By default the new enviroment shares the same owner as the calling 448 // Factory's environment. 449 func (factory *Factory) MakeEnvironment(c *gc.C, params *EnvParams) *state.State { 450 if params == nil { 451 params = new(EnvParams) 452 } 453 if params.Name == "" { 454 params.Name = uniqueString("testenv") 455 } 456 if params.Owner == nil { 457 origEnv, err := factory.st.Environment() 458 c.Assert(err, jc.ErrorIsNil) 459 params.Owner = origEnv.Owner() 460 } 461 // It only makes sense to make an environment with the same provider 462 // as the initial environment, or things will break elsewhere. 463 currentCfg, err := factory.st.EnvironConfig() 464 c.Assert(err, jc.ErrorIsNil) 465 466 uuid, err := utils.NewUUID() 467 c.Assert(err, jc.ErrorIsNil) 468 cfg := testing.CustomEnvironConfig(c, testing.Attrs{ 469 "name": params.Name, 470 "uuid": uuid.String(), 471 "type": currentCfg.Type(), 472 "state-port": currentCfg.StatePort(), 473 "api-port": currentCfg.APIPort(), 474 }.Merge(params.ConfigAttrs)) 475 _, st, err := factory.st.NewEnvironment(cfg, params.Owner.(names.UserTag)) 476 c.Assert(err, jc.ErrorIsNil) 477 if params.Prepare { 478 // Prepare the environment. 479 provider, err := environs.Provider(cfg.Type()) 480 c.Assert(err, jc.ErrorIsNil) 481 cfg, err = provider.PrepareForCreateEnvironment(cfg) 482 c.Assert(err, jc.ErrorIsNil) 483 // Now save the config back. 484 err = st.UpdateEnvironConfig(cfg.AllAttrs(), nil, nil) 485 c.Assert(err, jc.ErrorIsNil) 486 } 487 return st 488 }