Beispiel #1
0
// 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
}
Beispiel #2
0
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
}