// Returns the current state of the instance func (self *Instance) getState0() (*instanceState, error) { jujuClient := self.GetJujuClient() primaryServiceId := self.primaryServiceId status, err := jujuClient.GetServiceStatus(primaryServiceId) // TODO: check err? jujuService, err := jujuClient.FindService(primaryServiceId) if err != nil { return nil, err } if status == nil { log.Warn("No state found for %v", primaryServiceId) return nil, nil } log.Debug("Service state: %v", status) state := &instanceState{} state.Model = model.MapToInstance(self.instanceId, status, jujuService) for k, v := range self.bundleType.GetDefaultOptions() { option, found := state.Model.OptionDescriptions[k] if !found { log.Warn("Option not found in OptionDescriptions %v in %v", k, state.Model.OptionDescriptions) continue } option.Default = v state.Model.OptionDescriptions[k] = option } state.Units = map[string]map[string]api.UnitStatus{} state.Units[primaryServiceId] = status.Units state.PublicAddresses = []string{} for _, unitStatus := range status.Units { if unitStatus.PublicAddress == "" { continue } state.PublicAddresses = append(state.PublicAddresses, unitStatus.PublicAddress) } serviceKeys, err := self.getBundleKeys() if err != nil { return nil, err } if serviceKeys == nil { return nil, rs.ErrNotFound() } // TODO: This is pretty expensive... we could just check to see if properties have been set for serviceId, _ := range serviceKeys { if serviceId == primaryServiceId { continue } status, err := jujuClient.GetServiceStatus(serviceId) if err != nil { log.Warn("Error while fetching status of service: %v", serviceId, err) state.Model.Status = "pending" } else if status == nil { log.Warn("No status for service: %v", serviceId) state.Model.Status = "pending" } else { log.Info("Got state of secondary service: %v => %v", serviceId, status) for _, unitStatus := range status.Units { model.MergeInstanceStatus(state.Model, &unitStatus) } } if status != nil { state.Units[serviceId] = status.Units } } // TODO: This is a bit of a hack also. How should we wait for properties to be set? annotations, err := jujuClient.GetServiceAnnotations(primaryServiceId) if err != nil { log.Warn("Error getting annotations", err) // TODO: Mask error? return nil, err } log.Info("Annotations on %v: %v", primaryServiceId, annotations) state.Model.Options = map[string]string{} state.SystemProperties = map[string]string{} state.RelationMetadata = map[string]string{} relationList := []relationProperty{} for tagName, v := range annotations { if strings.HasPrefix(tagName, ANNOTATION_PREFIX_INSTANCECONFIG) { key := tagName[len(ANNOTATION_PREFIX_INSTANCECONFIG):] state.Model.Options[key] = v continue } if strings.HasPrefix(tagName, ANNOTATION_PREFIX_SYSTEM) { key := tagName[len(ANNOTATION_PREFIX_SYSTEM):] state.SystemProperties[key] = v continue } if strings.HasPrefix(tagName, ANNOTATION_PREFIX_RELATIONINFO) { suffix := tagName[len(ANNOTATION_PREFIX_RELATIONINFO):] tokens := strings.SplitN(suffix, "_", 3) if len(tokens) < 3 { log.Debug("Ignoring unparseable tag: %v", tagName) continue } unitId := tokens[0] relationId := tokens[1] key := tokens[2] if key[0] != '_' { state.RelationMetadata[key] = v continue } relationTokens := strings.SplitN(relationId, ":", 2) if len(relationTokens) != 2 { log.Debug("Ignoring unparseable relation id in tag: %v", tagName) continue } relationProperty := relationProperty{} relationProperty.UnitId = unitId assert.That(key[0] == '_') relationProperty.Key = key[1:] relationProperty.Value = v relationProperty.RelationType = relationTokens[0] relationProperty.RelationKey = relationTokens[1] relationList = append(relationList, relationProperty) continue } } state.Relations = map[string]map[string]string{} for _, relation := range relationList { relationType := relation.RelationType relations, found := state.Relations[relationType] if !found { relations = map[string]string{} state.Relations[relationType] = relations } relations[relation.Key] = relation.Value } // TODO: Only if otherwise ready? annotationsReady := self.bundleType.IsStarted(state.Relations) // For a subordinate charm service (e.g. multimysql), we just watch for the annotation if annotationsReady && state.Model.Status == "" && len(status.SubordinateTo) != 0 { log.Info("Subordinate instance started (per annotations): %v", self) state.Model.Status = "started" } if !annotationsReady { log.Info("Instance not started (per annotations): %v", state.Relations) state.Model.Status = "pending" } log.Info("Status of %v: %v", primaryServiceId, state.Model.Status) // TODO: Fetch inherited properties from primary service and merge return state, nil }
func (self *ServiceConfig) deploy(jujuServiceId string, apiclient *juju.Client) (*DeployServiceInfo, error) { serviceInfo := &DeployServiceInfo{} jujuService, err := apiclient.FindService(jujuServiceId) if err != nil { return nil, err } charmUrl := self.Charm charmInfo, err := apiclient.CharmInfo(charmUrl) if err != nil { log.Warn("Error reading charm: %v", charmUrl, err) } if charmInfo == nil { log.Warn("Unable to find charm: %v", charmUrl) } charmUrl = charmInfo.URL if jujuService == nil { // Create new service numUnits := self.NumberUnits if charmInfo.Meta.Subordinate { numUnits = -1 } configYaml, err := makeConfigYaml(jujuServiceId, self.Options) if err != nil { return nil, err } log.Debug("Deploying with YAML: %v", configYaml) err = apiclient.ServiceDeploy( charmUrl, jujuServiceId, numUnits, configYaml) if err != nil { return nil, err } // for retry := 0; retry < 5; retry++ { // status, err := apiclient.GetStatus(jujuServiceId) // if err != nil { // return err // } // if status != nil { // break // } // log.Info("Service was not yet visible; waiting") // time.Sleep(1 * time.Second) // } } else { existingInstance := model.MapToInstance(jujuServiceId, nil, jujuService) existingServiceOptions := existingInstance.Options mergedServiceOptions := map[string]string{} { for key, value := range existingServiceOptions { mergedServiceOptions[key] = value } for key, value := range self.Options { mergedServiceOptions[key] = value } } if !reflect.DeepEqual(existingServiceOptions, mergedServiceOptions) { err = apiclient.SetConfig(jujuServiceId, mergedServiceOptions) if err != nil { return nil, err } } else { log.Debug("Configuration unchanged; won't reconfigure") } } if !charmInfo.Meta.Subordinate { // && self.Exposed != nil { status, err := apiclient.GetServiceStatus(jujuServiceId) if err != nil { return nil, err } if status == nil { return nil, fmt.Errorf("Service not found: %v", jujuServiceId) } serviceInfo.Status = status if status.Exposed != self.Exposed { err = apiclient.SetExposed(jujuServiceId, self.Exposed) if err != nil { log.Warn("Error setting service to Exposed=%v", self.Exposed, err) return nil, err } } actualUnits := len(status.Units) wantUnits := self.NumberUnits if actualUnits != wantUnits { if actualUnits < wantUnits { _, err = apiclient.AddServiceUnits(jujuServiceId, wantUnits-actualUnits) if err != nil { log.Warn("Error adding units", err) } } else { keys := []string{} for key, _ := range status.Units { keys = append(keys, key) } sort.Strings(keys) // TODO: Be more intelligent about which unit to kill? victims := keys[wantUnits:len(keys)] for _, victim := range victims { slash := strings.Index(victim, "/") unitId, err := strconv.Atoi(victim[slash+1:]) if err != nil { log.Warn("Error parsing UnitId: %v", victim) return nil, err } err = apiclient.DestroyUnit(jujuServiceId, unitId) if err != nil { log.Warn("Error removing unit: %v/%v", jujuServiceId, unitId, err) return nil, err } } } } } // for _, openPort := range self.OpenPorts { // apiclient.Run(jujuServiceId, nil, ["open-port", openPort]) // //// err = apiclient.OpenPort(jujuServiceId, openPort) // if err != nil { // log.Warn("Error opening port: %v/%v", jujuServiceId, openPort, err) // return nil, err // } // } return serviceInfo, nil }