func (s *ConfigSuite) TestCoerceForStorage(c *gc.C) { cfg := newTestConfig(c, testing.Attrs{ "resource-tags": "a=b c=d"}) tags, ok := cfg.ResourceTags() c.Assert(ok, jc.IsTrue) expectedTags := map[string]string{"a": "b", "c": "d"} c.Assert(tags, gc.DeepEquals, expectedTags) tagsStr := config.CoerceForStorage(cfg.AllAttrs())["resource-tags"].(string) tagItems := strings.Split(tagsStr, " ") tagsMap := make(map[string]string) for _, kv := range tagItems { parts := strings.Split(kv, "=") tagsMap[parts[0]] = parts[1] } c.Assert(tagsMap, gc.DeepEquals, expectedTags) }
func (s *ModelConfigSuite) TestUpdateModelConfigCoerce(c *gc.C) { attrs := map[string]interface{}{ "resource-tags": map[string]string{"a": "b", "c": "d"}, } err := s.State.UpdateModelConfig(attrs, nil, nil) c.Assert(err, jc.ErrorIsNil) modelSettings, err := s.State.ReadSettings(state.SettingsC, state.ModelGlobalKey) c.Assert(err, jc.ErrorIsNil) expectedTags := map[string]string{"a": "b", "c": "d"} tagsStr := config.CoerceForStorage(modelSettings.Map())["resource-tags"].(string) tagItems := strings.Split(tagsStr, " ") tagsMap := make(map[string]string) for _, kv := range tagItems { parts := strings.Split(kv, "=") tagsMap[parts[0]] = parts[1] } c.Assert(tagsMap, gc.DeepEquals, expectedTags) cfg, err := s.State.ModelConfig() c.Assert(err, jc.ErrorIsNil) c.Assert(cfg.AllAttrs()["resource-tags"], gc.DeepEquals, expectedTags) }
// UpdateModelConfig adds, updates or removes attributes in the current // configuration of the model with the provided updateAttrs and // removeAttrs. func (st *State) UpdateModelConfig(updateAttrs map[string]interface{}, removeAttrs []string, additionalValidation ValidateConfigFunc) error { if len(updateAttrs)+len(removeAttrs) == 0 { return nil } if len(removeAttrs) > 0 { var removed []string if updateAttrs == nil { updateAttrs = make(map[string]interface{}) } // For each removed attribute, pick up any inherited value // and if there's one, use that. inherited, err := st.inheritedConfigAttributes() if err != nil { return errors.Trace(err) } for _, attr := range removeAttrs { // We we are updating an attribute, that takes // precedence over removing. if _, ok := updateAttrs[attr]; ok { continue } if val, ok := inherited[attr]; ok { updateAttrs[attr] = val } else { removed = append(removed, attr) } } removeAttrs = removed } // TODO(axw) 2013-12-6 #1167616 // Ensure that the settings on disk have not changed // underneath us. The settings changes are actually // applied as a delta to what's on disk; if there has // been a concurrent update, the change may not be what // the user asked for. modelSettings, err := readSettings(st, settingsC, modelGlobalKey) if err != nil { return errors.Trace(err) } // Get the existing model config from state. oldConfig, err := st.ModelConfig() if err != nil { return errors.Trace(err) } if additionalValidation != nil { err = additionalValidation(updateAttrs, removeAttrs, oldConfig) if err != nil { return errors.Trace(err) } } validCfg, err := st.buildAndValidateModelConfig(updateAttrs, removeAttrs, oldConfig) if err != nil { return errors.Trace(err) } validAttrs := validCfg.AllAttrs() for k := range oldConfig.AllAttrs() { if _, ok := validAttrs[k]; !ok { modelSettings.Delete(k) } } // Some values require marshalling before storage. validAttrs = config.CoerceForStorage(validAttrs) modelSettings.Update(validAttrs) _, ops := modelSettings.settingsUpdateOps() return modelSettings.write(ops) }
// modelSetupOps returns the transactions necessary to set up a model. func (st *State) modelSetupOps(controllerUUID string, args ModelArgs, inherited *lineage) ([]txn.Op, error) { if inherited != nil { if err := checkControllerInheritedConfig(inherited.ControllerConfig); err != nil { return nil, errors.Trace(err) } } if err := checkModelConfig(args.Config); err != nil { return nil, errors.Trace(err) } controllerModelUUID := st.controllerModelTag.Id() modelUUID := args.Config.UUID() modelStatusDoc := statusDoc{ ModelUUID: modelUUID, Updated: st.clock.Now().UnixNano(), Status: status.Available, } modelUserOps := createModelUserOps( modelUUID, args.Owner, args.Owner, args.Owner.Name(), st.NowToTheSecond(), permission.AdminAccess, ) ops := []txn.Op{ createStatusOp(st, modelGlobalKey, modelStatusDoc), createConstraintsOp(st, modelGlobalKey, args.Constraints), } // Inc ref count for hosted models. if controllerModelUUID != modelUUID { ops = append(ops, incHostedModelCountOp()) } // Create the default storage pools for the model. defaultStoragePoolsOps, err := st.createDefaultStoragePoolsOps(args.StorageProviderRegistry) if err != nil { return nil, errors.Trace(err) } ops = append(ops, defaultStoragePoolsOps...) // Create the final map of config attributes for the model. // If we have ControllerInheritedConfig passed in, that means state // is being initialised and there won't be any config sources // in state. var configSources []modelConfigSource if inherited != nil { configSources = []modelConfigSource{ { name: config.JujuDefaultSource, sourceFunc: modelConfigSourceFunc(func() (attrValues, error) { return config.ConfigDefaults(), nil })}, { name: config.JujuControllerSource, sourceFunc: modelConfigSourceFunc(func() (attrValues, error) { return inherited.ControllerConfig, nil })}, { name: config.JujuRegionSource, sourceFunc: modelConfigSourceFunc(func() (attrValues, error) { // We return the values specific to this region for this model. return attrValues(inherited.RegionConfig[args.CloudRegion]), nil })}, } } else { rspec := &environs.RegionSpec{Cloud: args.CloudName, Region: args.CloudRegion} configSources = modelConfigSources(st, rspec) } modelCfg, err := composeModelConfigAttributes(args.Config.AllAttrs(), configSources...) if err != nil { return nil, errors.Trace(err) } // Some values require marshalling before storage. modelCfg = config.CoerceForStorage(modelCfg) ops = append(ops, createSettingsOp(settingsC, modelGlobalKey, modelCfg), createModelEntityRefsOp(modelUUID), createModelOp( args.Owner, args.Config.Name(), modelUUID, controllerUUID, args.CloudName, args.CloudRegion, args.CloudCredential, args.MigrationMode, ), createUniqueOwnerModelNameOp(args.Owner, args.Config.Name()), ) ops = append(ops, modelUserOps...) return ops, nil }