github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/controller/modelmanager/createmodel.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // Package modelmanager provides the business logic for 5 // model management operations in the controller. 6 package modelmanager 7 8 import ( 9 "github.com/juju/errors" 10 "github.com/juju/loggo" 11 "github.com/juju/utils" 12 "github.com/juju/version" 13 14 "github.com/juju/juju/environs" 15 "github.com/juju/juju/environs/config" 16 "github.com/juju/juju/tools" 17 ) 18 19 var ( 20 logger = loggo.GetLogger("juju.controller.modelmanager") 21 ) 22 23 // ModelConfigCreator provides a method of creating a new model config. 24 // 25 // The zero value of ModelConfigCreator is usable with the limitations 26 // noted on each struct field. 27 type ModelConfigCreator struct { 28 // Provider will be used to obtain EnvironProviders for preparing 29 // and validating configuration. 30 Provider func(string) (environs.EnvironProvider, error) 31 32 // FindTools, if non-nil, will be used to validate the agent-version 33 // value in NewModelConfig if it differs from the base configuration. 34 // 35 // If FindTools is nil, agent-version may not be different to the 36 // base configuration. 37 FindTools func(version.Number) (tools.List, error) 38 } 39 40 // NewModelConfig returns a new model config given a base (controller) config 41 // and a set of attributes that will be specific to the new model, overriding 42 // any non-restricted attributes in the base configuration. The resulting 43 // config will be suitable for creating a new model in state. 44 // 45 // If "attrs" does not include a UUID, a new, random one will be generated 46 // and added to the config. 47 // 48 // The config will be validated with the provider before being returned. 49 func (c ModelConfigCreator) NewModelConfig( 50 cloud environs.CloudSpec, 51 base *config.Config, 52 attrs map[string]interface{}, 53 ) (*config.Config, error) { 54 55 if err := c.checkVersion(base, attrs); err != nil { 56 return nil, errors.Trace(err) 57 } 58 provider, err := c.Provider(cloud.Type) 59 if err != nil { 60 return nil, errors.Trace(err) 61 } 62 63 // Generate a new UUID for the model as necessary, 64 // and finalize the new config. 65 if _, ok := attrs[config.UUIDKey]; !ok { 66 uuid, err := utils.NewUUID() 67 if err != nil { 68 return nil, errors.Trace(err) 69 } 70 attrs[config.UUIDKey] = uuid.String() 71 } 72 attrs[config.TypeKey] = cloud.Type 73 cfg, err := finalizeConfig(provider, cloud, attrs) 74 if err != nil { 75 return nil, errors.Trace(err) 76 } 77 return cfg, nil 78 } 79 80 func (c *ModelConfigCreator) checkVersion(base *config.Config, attrs map[string]interface{}) error { 81 baseVersion, ok := base.AgentVersion() 82 if !ok { 83 return errors.Errorf("agent-version not found in base config") 84 } 85 86 // If there is no agent-version specified, use the current version. 87 // otherwise we need to check for tools 88 value, ok := attrs["agent-version"] 89 if !ok { 90 attrs["agent-version"] = baseVersion.String() 91 return nil 92 } 93 versionStr, ok := value.(string) 94 if !ok { 95 return errors.Errorf("agent-version must be a string but has type '%T'", value) 96 } 97 versionNumber, err := version.Parse(versionStr) 98 if err != nil { 99 return errors.Trace(err) 100 } 101 102 n := versionNumber.Compare(baseVersion) 103 switch { 104 case n > 0: 105 return errors.Errorf( 106 "agent-version (%s) cannot be greater than the controller (%s)", 107 versionNumber, baseVersion, 108 ) 109 case n == 0: 110 // If the version is the same as the base config, 111 // then assume tools are available. 112 return nil 113 case n < 0: 114 if c.FindTools == nil { 115 return errors.New( 116 "agent-version does not match base config, " + 117 "and no tools-finder is supplied", 118 ) 119 } 120 } 121 122 // Look to see if we have tools available for that version. 123 list, err := c.FindTools(versionNumber) 124 if err != nil { 125 return errors.Trace(err) 126 } 127 if len(list) == 0 { 128 return errors.Errorf("no agent binaries found for version %s", versionNumber) 129 } 130 logger.Tracef("found agent binaries: %#v", list) 131 return nil 132 } 133 134 // finalizeConfig creates the config object from attributes, 135 // and calls EnvironProvider.PrepareConfig. 136 func finalizeConfig( 137 provider environs.EnvironProvider, 138 cloud environs.CloudSpec, 139 attrs map[string]interface{}, 140 ) (*config.Config, error) { 141 cfg, err := config.New(config.UseDefaults, attrs) 142 if err != nil { 143 return nil, errors.Annotate(err, "creating config from values failed") 144 } 145 cfg, err = provider.PrepareConfig(environs.PrepareConfigParams{ 146 Cloud: cloud, 147 Config: cfg, 148 }) 149 if err != nil { 150 return nil, errors.Annotate(err, "provider config preparation failed") 151 } 152 cfg, err = provider.Validate(cfg, nil) 153 if err != nil { 154 return nil, errors.Annotate(err, "provider config validation failed") 155 } 156 return cfg, nil 157 }