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