示例#1
0
// ControlWorkSpec makes changes to a work spec that are not directly
// reflected in the work spec definition.  This allows work specs to
// be paused or to stop generating new continuous jobs.
// ControlWorkSpecOptions has a complete listing of what can be done.
func (jobs *JobServer) ControlWorkSpec(workSpecName string, options map[string]interface{}) (bool, string, error) {
	var (
		cwsOptions ControlWorkSpecOptions
		decoder    *mapstructure.Decoder
		err        error
		metadata   mapstructure.Metadata
		workSpec   coordinate.WorkSpec
		wsMeta     coordinate.WorkSpecMeta
	)

	workSpec, err = jobs.Namespace.WorkSpec(workSpecName)
	if err == nil {
		// We care a lot about "false" vs. not present for
		// these things.  Manually create the decoder.
		config := mapstructure.DecoderConfig{
			Result:   &cwsOptions,
			Metadata: &metadata,
		}
		decoder, err = mapstructure.NewDecoder(&config)
	}
	if err == nil {
		err = decoder.Decode(options)
	}

	// Get the existing metadata, then change it based on what
	// we got provided
	if err == nil {
		wsMeta, err = workSpec.Meta(false)
	}
	if err == nil {
		for _, key := range metadata.Keys {
			switch key {
			case "Continuous":
				wsMeta.Continuous = cwsOptions.Continuous
			case "Status":
				wsMeta.Paused = cwsOptions.Status == Paused
			case "Weight":
				wsMeta.Weight = cwsOptions.Weight
			case "Interval":
				wsMeta.Interval = time.Duration(cwsOptions.Interval) * time.Second
			case "MaxRunning":
				wsMeta.MaxRunning = cwsOptions.MaxRunning
			}
		}
	}
	if err == nil {
		err = workSpec.SetMeta(wsMeta)
	}
	return err == nil, "", err
}
示例#2
0
// ExtractAddWorkUnitItem converts an arbitrary object (which really
// should be a cborpc.PythonTuple or a list) into an AddWorkUnitItem.
func ExtractAddWorkUnitItem(obj interface{}) (result AddWorkUnitItem, err error) {
	var (
		decoder      *mapstructure.Decoder
		haveMetadata bool
		havePriority bool
		kvpList      []interface{}
		kvpMap       map[string]interface{}
		str          string
		bstr         []byte
		ok           bool
	)
	// If we got handed a string (or a byte string) turn it into
	// a work unit with no data
	if str, ok = obj.(string); ok {
		result.Key = str
		result.Data = make(map[string]interface{})
		return
	}
	if bstr, ok = obj.([]byte); ok {
		result.Key = string(bstr)
		result.Data = make(map[string]interface{})
		return
	}

	// Otherwise obj must be a tuple (or a list)
	if kvpList, ok = cborrpc.Detuplify(obj); !ok {
		err = ErrWorkUnitNotList
		return
	}
	// Turn that list into a string-keyed map
	if len(kvpList) < 2 {
		err = ErrWorkUnitTooShort
		return
	}
	kvpMap = make(map[string]interface{})
	kvpMap["key"] = kvpList[0]
	kvpMap["data"] = kvpList[1]
	if len(kvpList) >= 3 && kvpList[2] != nil {
		kvpMap["metadata"] = kvpList[2]
		haveMetadata = true
	}
	if len(kvpList) >= 4 && kvpList[3] != nil {
		kvpMap["priority"] = kvpList[3]
		havePriority = true
	}
	// Now we can invoke mapstructure
	config := mapstructure.DecoderConfig{
		DecodeHook: cborrpc.DecodeBytesAsString,
		Result:     &result,
	}
	decoder, err = mapstructure.NewDecoder(&config)
	if err == nil {
		err = decoder.Decode(kvpMap)
	}
	if err == nil && haveMetadata && !havePriority {
		// See if the caller passed metadata["priority"]
		// instead of an explicit priority field.
		if priority, ok := result.Metadata["priority"]; ok {
			if result.Priority, ok = priority.(float64); !ok {
				err = ErrBadPriority
			}
		}
	}
	return
}
示例#3
0
// GetWorkUnits retrieves the keys and data dictionaries for some number
// of work units.  If options contains "work_unit_keys", those specific
// work units are retrieved; otherwise the work units are based on
// which of GetWorkUnitsOptions are present.
//
// On success, the return value is a slice of cborrpc.PythonTuple
// objects where each contains the work unit key as a byte slice and
// the data dictionary.
func (jobs *JobServer) GetWorkUnits(workSpecName string, options map[string]interface{}) ([]interface{}, string, error) {
	var workUnits map[string]coordinate.WorkUnit
	gwuOptions := GetWorkUnitsOptions{
		Limit: 1000,
	}

	spec, err := jobs.Namespace.WorkSpec(workSpecName)
	var decoder *mapstructure.Decoder
	if err == nil {
		config := mapstructure.DecoderConfig{
			DecodeHook: mapstructure.ComposeDecodeHookFunc(gwuStateHook, cborrpc.DecodeBytesAsString),
			Result:     &gwuOptions,
		}
		decoder, err = mapstructure.NewDecoder(&config)
	}
	if err == nil {
		err = decoder.Decode(options)
	}
	if err == nil {
		query := coordinate.WorkUnitQuery{
			Names: gwuOptions.WorkUnitKeys,
		}
		if gwuOptions.WorkUnitKeys == nil {
			query.PreviousName = gwuOptions.Start
			query.Limit = gwuOptions.Limit
		}
		if gwuOptions.WorkUnitKeys == nil && gwuOptions.State != nil {
			query.Statuses = make([]coordinate.WorkUnitStatus, len(gwuOptions.State))
			for i, state := range gwuOptions.State {
				query.Statuses[i], err = translateWorkUnitStatus(state)
				if err != nil {
					break
				}
			}
		}
		if err == nil {
			workUnits, err = spec.WorkUnits(query)
		}
	}
	if err != nil {
		return nil, "", err
	}
	// The marshalled result is a list of pairs of (key, data).
	var result []interface{}
	for name, unit := range workUnits {
		var data map[string]interface{}
		attempt, err := unit.ActiveAttempt()
		if err == nil && attempt != nil {
			data, err = attempt.Data()
		}
		if err == nil && data == nil {
			data, err = unit.Data()
		}
		if err != nil {
			return nil, "", err
		}
		tuple := cborrpc.PythonTuple{Items: []interface{}{[]byte(name), data}}
		result = append(result, tuple)
	}
	return result, "", nil
}
示例#4
0
// ExtractAddWorkUnitItem converts an arbitrary object (which really
// should be a cborpc.PythonTuple or a list) into an AddWorkUnitItem.
func ExtractAddWorkUnitItem(obj interface{}, now time.Time) (result AddWorkUnitItem, err error) {
	var (
		decoder *mapstructure.Decoder
		kvpList []interface{}
		kvpMap  map[string]interface{}
		ok      bool
	)
	// If we got handed a string (or a byte string) turn it into
	// a work unit with no data
	if result.Key, ok = cborrpc.Destringify(obj); ok {
		result.Data = make(map[string]interface{})
		return
	}

	// Otherwise obj must be a tuple (or a list)
	if kvpList, ok = cborrpc.Detuplify(obj); !ok {
		err = ErrWorkUnitNotList
		return
	}
	// Turn that list into a string-keyed map
	if len(kvpList) < 2 {
		err = ErrWorkUnitTooShort
		return
	}
	kvpMap = make(map[string]interface{})
	kvpMap["key"] = kvpList[0]
	kvpMap["data"] = kvpList[1]
	if len(kvpList) >= 3 && kvpList[2] != nil {
		kvpMap["meta"] = kvpList[2]
	}

	// Now we can invoke mapstructure, on this modified struct
	var partial struct {
		Key  string
		Data map[string]interface{}
		Meta AddWorkUnitMeta
	}
	config := mapstructure.DecoderConfig{
		DecodeHook: cborrpc.DecodeBytesAsString,
		Result:     &partial,
	}
	decoder, err = mapstructure.NewDecoder(&config)
	if err == nil {
		err = decoder.Decode(kvpMap)
	}

	// If that worked, copy data over to a real result
	if err == nil {
		result.Key = partial.Key
		result.Data = partial.Data
		result.Meta = partial.Meta.ToMeta(now)
	}

	// If a priority value was given in the list, that overrides
	// what got extracted
	if err == nil && len(kvpList) >= 4 && kvpList[3] != nil {
		if result.Meta.Priority, ok = kvpList[3].(float64); !ok {
			err = ErrBadPriority
		}
	}

	return
}
示例#5
0
// Execute takes the callback and executes it via the LIFX protocol
func Execute(callback map[string]interface{}) {
	var (
		cb      lifxCallback
		decoder *mapstructure.Decoder
		err     error
	)

	log.WithField(`request`, callback).Debug(`Sending to LIFX`)

	decoder, err = mapstructure.NewDecoder(&mapstructure.DecoderConfig{
		DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
		Result:     &cb,
	})
	if err != nil {
		log.WithField(`error`, err).Error(`Initializing LIFX callback decoder`)
		return
	}

	if err = decoder.Decode(callback); err != nil {
		log.WithFields(log.Fields{
			`request`: callback,
			`error`:   err,
		}).Error(`Decoding LIFX callback`)
		return
	}

	boblights.cancel(&cb)

	if _, ok := callback[`power`]; ok {
		if len(cb.Lights) == 0 && len(cb.Groups) == 0 {
			if err = client.SetPowerDuration(cb.Power, cb.PowerDuration); err != nil {
				log.WithField(`error`, err).Error(`Setting power`)
			}
		}
		if len(cb.Lights) > 0 {
			for _, label := range cb.Lights {
				light, e := client.GetLightByLabel(label)
				if e != nil {
					log.WithFields(log.Fields{
						`label`: label,
						`error`: e,
					}).Error(`Finding light`)
					continue
				}
				if e = light.SetPowerDuration(cb.Power, cb.PowerDuration); e != nil {
					log.WithFields(log.Fields{
						`label`: label,
						`error`: e,
					}).Error(`Setting power`)
					continue
				}
			}
		}
		if len(cb.Groups) > 0 {
			for _, label := range cb.Groups {
				group, e := client.GetGroupByLabel(label)
				if e != nil {
					log.WithFields(log.Fields{
						`label`: label,
						`error`: e,
					}).Error(`Finding group`)
					continue
				}
				if e = group.SetPowerDuration(cb.Power, cb.PowerDuration); e != nil {
					log.WithFields(log.Fields{
						`label`: label,
						`error`: e,
					}).Error(`Setting power`)
					continue
				}
			}
		}
	}

	if _, ok := callback[`color`]; ok {
		if len(cb.Lights) == 0 && len(cb.Groups) == 0 {
			if err = client.SetColor(cb.Color, cb.ColorDuration); err != nil {
				log.WithField(`error`, err).Error(`Setting color`)
			}
		}
		if len(cb.Lights) > 0 {
			for _, label := range cb.Lights {
				light, err := client.GetLightByLabel(label)
				if err != nil {
					log.WithFields(log.Fields{
						`label`: label,
						`error`: err,
					}).Error(`Finding light`)
					continue
				}
				if err = light.SetColor(cb.Color, cb.ColorDuration); err != nil {
					log.WithFields(log.Fields{
						`label`: label,
						`error`: err,
					}).Error(`Setting color`)
					continue
				}
			}
		}
		if len(cb.Groups) > 0 {
			for _, label := range cb.Groups {
				group, err := client.GetGroupByLabel(label)
				if err != nil {
					log.WithFields(log.Fields{
						`label`: label,
						`error`: err,
					}).Error(`Finding group`)
					continue
				}
				if err = group.SetColor(cb.Color, cb.ColorDuration); err != nil {
					log.WithFields(log.Fields{
						`label`: label,
						`error`: err,
					}).Error(`Setting color`)
					continue
				}
			}
		}
	}

	if _, ok := callback[`boblight`]; ok {
		// Recommended LIFX device rate limit
		rateLimit := time.Second / 20
		// If requested rate limit is longer than minimum, use it
		if cb.Boblight.RateLimit > rateLimit {
			rateLimit = cb.Boblight.RateLimit
		}
		if len(cb.Lights) == 0 && len(cb.Groups) == 0 {
			lights, err := client.GetLights()
			if err != nil {
				return
			}
			for _, light := range lights {
				boblights.add(light, cb.Boblight.Lights, rateLimit)
			}
		}
		if len(cb.Lights) > 0 {
			for _, label := range cb.Lights {
				light, err := client.GetLightByLabel(label)
				if err != nil {
					log.WithFields(log.Fields{
						`label`: label,
						`error`: err,
					}).Error(`Finding light`)
					continue
				}
				boblights.add(light, cb.Boblight.Lights, rateLimit)
			}
		}
		if len(cb.Groups) > 0 {
			for _, label := range cb.Groups {
				group, err := client.GetGroupByLabel(label)
				if err != nil {
					log.WithFields(log.Fields{
						`label`: label,
						`error`: err,
					}).Error(`Finding group`)
					continue
				}
				for _, light := range group.Lights() {
					boblights.add(light, cb.Boblight.Lights, rateLimit)
				}
			}
		}
	}

}
示例#6
0
// Execute takes the callback and executes it via the LIFX protocol
func Execute(callback map[string]interface{}) {
	var (
		cb      lifxCallback
		decoder *mapstructure.Decoder
		err     error
	)

	log.WithField(`request`, callback).Debug(`Sending to LIFX`)

	decoder, err = mapstructure.NewDecoder(&mapstructure.DecoderConfig{
		DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
		Result:     &cb,
	})
	if err != nil {
		log.WithField(`error`, err).Error(`Initializing LIFX callback decoder`)
		return
	}

	if err = decoder.Decode(callback); err != nil {
		log.WithFields(log.Fields{
			`request`: callback,
			`error`:   err,
		}).Error(`Decoding LIFX callback`)
		return
	}

	if _, ok := callback[`power`]; ok {
		if len(cb.Lights) == 0 {
			if err = client.SetPowerDuration(cb.Power, cb.PowerDuration); err != nil {
				log.WithField(`error`, err).Error(`Setting power`)
			}
		} else {
			for _, label := range cb.Lights {
				light, err := client.GetLightByLabel(label)
				if err != nil {
					log.WithFields(log.Fields{
						`label`: label,
						`error`: err,
					}).Error(`Finding light`)
					continue
				}
				if err = light.SetPowerDuration(cb.Power, cb.PowerDuration); err != nil {
					log.WithFields(log.Fields{
						`label`: label,
						`error`: err,
					}).Error(`Setting power`)
					continue
				}
			}
		}
	}

	if _, ok := callback[`color`]; ok {
		if len(cb.Lights) == 0 {
			if err = client.SetColor(cb.Color, cb.ColorDuration); err != nil {
				log.WithField(`error`, err).Error(`Setting color`)
			}
		} else {
			for _, label := range cb.Lights {
				light, err := client.GetLightByLabel(label)
				if err != nil {
					log.WithFields(log.Fields{
						`label`: label,
						`error`: err,
					}).Error(`Finding light`)
					continue
				}
				if err = light.SetColor(cb.Color, cb.ColorDuration); err != nil {
					log.WithFields(log.Fields{
						`label`: label,
						`error`: err,
					}).Error(`Setting color`)
					continue
				}
			}
		}
	}

}