Beispiel #1
0
func (d *deployer) deployUnit(wantedUnit *schema.Unit) error {
	currentUnit, err := d.fleetapi.Unit(wantedUnit.Name)
	if err != nil {
		return err
	}
	if currentUnit == nil {
		err := d.fleetapi.CreateUnit(wantedUnit)
		if err != nil {
			return err
		}
		return nil
	}

	wuf := schema.MapSchemaUnitOptionsToUnitFile(wantedUnit.Options)
	cuf := schema.MapSchemaUnitOptionsToUnitFile(currentUnit.Options)
	if wuf.Hash() != cuf.Hash() {
		log.Printf("INFO Service %s differs from the cluster version", wantedUnit.Name)
		wantedUnit.DesiredState = "inactive"
		err = d.fleetapi.DestroyUnit(wantedUnit.Name)
		if err != nil {
			return err
		}
		err = d.fleetapi.CreateUnit(wantedUnit)
		if err != nil {
			return err
		}
	}
	return nil
}
Beispiel #2
0
func runCatUnit(args []string) (exit int) {
	if len(args) != 1 {
		stderr("One unit file must be provided")
		return 1
	}

	name := unitNameMangle(args[0])
	u, err := cAPI.Unit(name)
	if err != nil {
		stderr("Error retrieving Unit %s: %v", name, err)
		return 1
	}
	if u == nil {
		stderr("Unit %s not found", name)
		return 1
	}

	uf := schema.MapSchemaUnitOptionsToUnitFile(u.Options)

	// Must not add a newline here. The contents of the unit file
	// must not be modified.
	fmt.Print(uf.String())

	return
}
Beispiel #3
0
// getUnitFileFromTemplate attempts to get a Unit from a template unit that
// is either in the registry or on the file system
// It takes two arguments, the template information and the unit file name
// It returns the Unit or nil; and any error encountered
func getUnitFileFromTemplate(cCmd *cobra.Command, uni *unit.UnitNameInfo, fileName string) (*unit.UnitFile, error) {
	var uf *unit.UnitFile

	tmpl, err := cAPI.Unit(uni.Template)
	if err != nil {
		return nil, fmt.Errorf("error retrieving template Unit(%s) from Registry: %v", uni.Template, err)
	}

	if tmpl != nil {
		isLocalUnitDifferent(cCmd, fileName, tmpl, false)
		uf = schema.MapSchemaUnitOptionsToUnitFile(tmpl.Options)
		log.Debugf("Template Unit(%s) found in registry", uni.Template)
	} else {
		// Finally, if we could not find a template unit in the Registry,
		// check the local disk for one instead
		filePath := path.Join(path.Dir(fileName), uni.Template)
		if _, err := os.Stat(filePath); os.IsNotExist(err) {
			return nil, fmt.Errorf("unable to find template Unit(%s) in Registry or on filesystem", uni.Template)
		}

		uf, err = getUnitFromFile(filePath)
		if err != nil {
			return nil, fmt.Errorf("unable to load template Unit(%s) from file: %v", uni.Template, err)
		}
	}

	return uf, nil
}
Beispiel #4
0
// ValidateOptions ensures that a set of UnitOptions is valid; if not, an error
// is returned detailing the issue encountered.  If there are several problems
// with a set of options, only the first is returned.
func ValidateOptions(opts []*schema.UnitOption) error {
	uf := schema.MapSchemaUnitOptionsToUnitFile(opts)
	// Sanity check using go-systemd's deserializer, which will do things
	// like check for excessive line lengths
	_, err := gsunit.Deserialize(gsunit.Serialize(uf.Options))
	if err != nil {
		return err
	}

	j := &job.Job{
		Unit: *uf,
	}
	conflicts := pkg.NewUnsafeSet(j.Conflicts()...)
	replaces := pkg.NewUnsafeSet(j.Replaces()...)
	peers := pkg.NewUnsafeSet(j.Peers()...)
	for _, peer := range peers.Values() {
		for _, conflict := range conflicts.Values() {
			matched, _ := path.Match(conflict, peer)
			if matched {
				return fmt.Errorf("unresolvable requirements: peer %q matches conflict %q", peer, conflict)
			}
		}
		for _, replace := range replaces.Values() {
			matched, _ := path.Match(replace, peer)
			if matched {
				return fmt.Errorf("unresolvable requirements: peer %q matches replace %q", peer, replace)
			}
		}
	}
	hasPeers := peers.Length() != 0
	hasConflicts := conflicts.Length() != 0
	hasReplaces := replaces.Length() != 0
	_, hasReqTarget := j.RequiredTarget()
	u := &job.Unit{
		Unit: *uf,
	}
	isGlobal := u.IsGlobal()

	switch {
	case hasReqTarget && hasPeers:
		return errors.New("MachineID cannot be used with Peers")
	case hasReqTarget && hasConflicts:
		return errors.New("MachineID cannot be used with Conflicts")
	case hasReqTarget && isGlobal:
		return errors.New("MachineID cannot be used with Global")
	case hasReqTarget && hasReplaces:
		return errors.New("MachineID cannot be used with Replaces")
	case isGlobal && hasPeers:
		return errors.New("Global cannot be used with Peers")
	case isGlobal && hasReplaces:
		return errors.New("Global cannot be used with Replaces")
	case hasConflicts && hasReplaces:
		return errors.New("Conflicts cannot be used with Replaces")
	}

	return nil
}
Beispiel #5
0
// matchLocalFileAndUnit compares a file with a Unit
// Returns true if the contents of the file matches the unit one, false
// otherwise; and any error encountered.
func matchLocalFileAndUnit(file string, su *schema.Unit) (bool, error) {
	result := false
	a := schema.MapSchemaUnitOptionsToUnitFile(su.Options)

	_, err := os.Stat(file)
	if err == nil {
		b, err := getUnitFromFile(file)
		if err == nil {
			result = unit.MatchUnitFiles(a, b)
		}
	}

	return result, err
}
Beispiel #6
0
// matchLocalFileAndUnit compares a file with a Unit
// Returns true if the contents of the file matches the unit one, false
// otherwise; and any error encountered.
func matchLocalFileAndUnit(file string, su *schema.Unit) (bool, error) {
	a := schema.MapSchemaUnitOptionsToUnitFile(su.Options)

	_, err := os.Stat(file)
	if err != nil {
		return false, err
	}

	b, err := getUnitFromFile(file)
	if err != nil {
		return false, err
	}

	return unit.MatchUnitFiles(a, b), nil
}
Beispiel #7
0
func warnOnDifferentLocalUnit(loc string, su *schema.Unit) {
	suf := schema.MapSchemaUnitOptionsToUnitFile(su.Options)
	if _, err := os.Stat(loc); !os.IsNotExist(err) {
		luf, err := getUnitFromFile(loc)
		if err == nil && luf.Hash() != suf.Hash() {
			stderr("WARNING: Unit %s in registry differs from local unit file %s", su.Name, loc)
			return
		}
	}
	if uni := unit.NewUnitNameInfo(path.Base(loc)); uni != nil && uni.IsInstance() {
		file := path.Join(path.Dir(loc), uni.Template)
		if _, err := os.Stat(file); !os.IsNotExist(err) {
			tmpl, err := getUnitFromFile(file)
			if err == nil && tmpl.Hash() != suf.Hash() {
				stderr("WARNING: Unit %s in registry differs from local template unit file %s", su.Name, uni.Template)
			}
		}
	}
}
Beispiel #8
0
func (f *FleetTunnel) Cat(unitName string) (string, error) {
	log.Debugf("cat unit %v", unitName)

	var u *schema.Unit
	op := func() error {
		var err error
		u, err = f.cAPI.Unit(unitName)
		return maskAny(err)
	}
	if err := backoff.Retry(op, backoff.NewExponentialBackOff()); err != nil {
		return "", maskAny(err)
	}
	if u == nil {
		return "", maskAny(errgo.WithCausef(nil, NotFoundError, unitName))
	}

	uf := schema.MapSchemaUnitOptionsToUnitFile(u.Options)

	return uf.String(), nil
}
Beispiel #9
0
func runCatUnit(args []string) (exit int) {
	if len(args) != 1 {
		fmt.Fprintln(os.Stderr, "One unit file must be provided.")
		return 1
	}

	name := unitNameMangle(args[0])
	u, err := cAPI.Unit(name)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error retrieving Unit %s: %v\n", name, err)
		return 1
	}
	if u == nil {
		fmt.Fprintf(os.Stderr, "Unit %s not found.\n", name)
		return 1
	}

	uf := schema.MapSchemaUnitOptionsToUnitFile(u.Options)
	fmt.Print(uf.String())
	return
}
Beispiel #10
0
func appendJobsForTests(jobs *[]job.Job, machine machine.MachineState, prefix string, unitCount int, template bool) {
	if template {
		// for start or load operations we may need to wait
		// during the creation of units, and since this is a
		// faked registry just set the 'Global' flag so we don't
		// block forever
		Options := []*schema.UnitOption{
			&schema.UnitOption{
				Section: "Unit",
				Name:    "Description",
				Value:   fmt.Sprintf("Template %[email protected]", prefix),
			},
			&schema.UnitOption{
				Section: "X-Fleet",
				Name:    "Global",
				Value:   "true",
			},
		}
		uf := schema.MapSchemaUnitOptionsToUnitFile(Options)
		j := job.Job{
			Name:            fmt.Sprintf("%[email protected]", prefix),
			Unit:            *uf,
			TargetMachineID: machine.ID,
		}
		*jobs = append(*jobs, j)
	} else {
		for i := 1; i <= unitCount; i++ {
			j := job.Job{
				Name:            fmt.Sprintf("%s%d.service", prefix, i),
				Unit:            unit.UnitFile{},
				TargetMachineID: machine.ID,
			}
			*jobs = append(*jobs, j)
		}
	}

	return
}
Beispiel #11
0
// ValidateOptions ensures that a set of UnitOptions is valid; if not, an error
// is returned detailing the issue encountered.  If there are several problems
// with a set of options, only the first is returned.
func ValidateOptions(opts []*schema.UnitOption) error {
	uf := schema.MapSchemaUnitOptionsToUnitFile(opts)
	j := &job.Job{
		Unit: *uf,
	}
	conflicts := pkg.NewUnsafeSet(j.Conflicts()...)
	peers := pkg.NewUnsafeSet(j.Peers()...)
	for _, peer := range peers.Values() {
		for _, conflict := range conflicts.Values() {
			matched, _ := path.Match(conflict, peer)
			if matched {
				return fmt.Errorf("unresolvable requirements: peer %q matches conflict %q", peer, conflict)
			}
		}
	}
	hasPeers := peers.Length() != 0
	hasConflicts := conflicts.Length() != 0
	_, hasReqTarget := j.RequiredTarget()
	u := &job.Unit{
		Unit: *uf,
	}
	isGlobal := u.IsGlobal()

	switch {
	case hasReqTarget && hasPeers:
		return errors.New("MachineID cannot be used with Peers")
	case hasReqTarget && hasConflicts:
		return errors.New("MachineID cannot be used with Conflicts")
	case hasReqTarget && isGlobal:
		return errors.New("MachineID cannot be used with Global")
	case isGlobal && hasPeers:
		return errors.New("Global cannot be used with Peers")
	case isGlobal && hasConflicts:
		return errors.New("Global cannot be used with Conflicts")
	}

	return nil
}
Beispiel #12
0
		"dstate": func(u schema.Unit, full bool) string {
			if u.DesiredState == "" {
				return "-"
			}
			return u.DesiredState
		},
		"target":   mapTargetField,
		"tmachine": mapTargetField,
		"state": func(u schema.Unit, full bool) string {
			if suToGlobal(u) || u.CurrentState == "" {
				return "-"
			}
			return u.CurrentState
		},
		"hash": func(u schema.Unit, full bool) string {
			uf := schema.MapSchemaUnitOptionsToUnitFile(u.Options)
			if !full {
				return uf.Hash().Short()
			}
			return uf.Hash().String()
		},
		"desc": func(u schema.Unit, full bool) string {
			uf := schema.MapSchemaUnitOptionsToUnitFile(u.Options)
			d := uf.Description()
			if d == "" {
				return "-"
			}
			return d
		},
	}
)
Beispiel #13
0
// lazyCreateUnits iterates over a set of unit names and, for each, attempts to
// ensure that a unit by that name exists in the Registry, by checking a number
// of conditions and acting on the first one that succeeds, in order of:
//  1. a unit by that name already existing in the Registry
//  2. a unit file by that name existing on disk
//  3. a corresponding unit template (if applicable) existing in the Registry
//  4. a corresponding unit template (if applicable) existing on disk
// Any error encountered during these steps is returned immediately (i.e.
// subsequent Jobs are not acted on). An error is also returned if none of the
// above conditions match a given Job.
func lazyCreateUnits(args []string) error {
	for _, arg := range args {
		// TODO(jonboulle): this loop is getting too unwieldy; factor it out

		arg = maybeAppendDefaultUnitType(arg)
		name := unitNameMangle(arg)

		// First, check if there already exists a Unit by the given name in the Registry
		u, err := cAPI.Unit(name)
		if err != nil {
			return fmt.Errorf("error retrieving Unit(%s) from Registry: %v", name, err)
		}
		if u != nil {
			log.Debugf("Found Unit(%s) in Registry, no need to recreate it", name)
			warnOnDifferentLocalUnit(arg, u)
			continue
		}

		// Failing that, assume the name references a local unit file on disk, and attempt to load that, if it exists
		if _, err := os.Stat(arg); !os.IsNotExist(err) {
			unit, err := getUnitFromFile(arg)
			if err != nil {
				return fmt.Errorf("failed getting Unit(%s) from file: %v", arg, err)
			}
			u, err = createUnit(name, unit)
			if err != nil {
				return err
			}
			continue
		}

		// Otherwise (if the unit file does not exist), check if the name appears to be an instance unit,
		// and if so, check for a corresponding template unit in the Registry
		uni := unit.NewUnitNameInfo(name)
		if uni == nil {
			return fmt.Errorf("error extracting information from unit name %s", name)
		} else if !uni.IsInstance() {
			return fmt.Errorf("unable to find Unit(%s) in Registry or on filesystem", name)
		}
		tmpl, err := cAPI.Unit(uni.Template)
		if err != nil {
			return fmt.Errorf("error retrieving template Unit(%s) from Registry: %v", uni.Template, err)
		}

		// Finally, if we could not find a template unit in the Registry, check the local disk for one instead
		var uf *unit.UnitFile
		if tmpl == nil {
			file := path.Join(path.Dir(arg), uni.Template)
			if _, err := os.Stat(file); os.IsNotExist(err) {
				return fmt.Errorf("unable to find Unit(%s) or template Unit(%s) in Registry or on filesystem", name, uni.Template)
			}
			uf, err = getUnitFromFile(file)
			if err != nil {
				return fmt.Errorf("failed getting template Unit(%s) from file: %v", uni.Template, err)
			}
		} else {
			warnOnDifferentLocalUnit(arg, tmpl)
			uf = schema.MapSchemaUnitOptionsToUnitFile(tmpl.Options)
		}

		// If we found a template unit, create a near-identical instance unit in
		// the Registry - same unit file as the template, but different name
		u, err = createUnit(name, uf)
		if err != nil {
			return err
		}
	}
	return nil
}
Beispiel #14
0
func (ur *unitsResource) set(rw http.ResponseWriter, req *http.Request, item string) {
	if err := validateContentType(req); err != nil {
		sendError(rw, http.StatusUnsupportedMediaType, err)
		return
	}

	var su schema.Unit
	dec := json.NewDecoder(req.Body)
	err := dec.Decode(&su)
	if err != nil {
		sendError(rw, http.StatusBadRequest, fmt.Errorf("unable to decode body: %v", err))
		return
	}
	if su.Name == "" {
		su.Name = item
	}
	if item != su.Name {
		sendError(rw, http.StatusBadRequest, fmt.Errorf("name in URL %q differs from unit name in request body %q", item, su.Name))
		return
	}
	if err := ValidateName(su.Name); err != nil {
		sendError(rw, http.StatusBadRequest, err)
		return
	}

	eu, err := ur.cAPI.Unit(su.Name)
	if err != nil {
		log.Errorf("Failed fetching Unit(%s) from Registry: %v", su.Name, err)
		sendError(rw, http.StatusInternalServerError, nil)
		return
	}

	newUnit := false
	if eu == nil {
		if len(su.Options) == 0 {
			err := errors.New("unit does not exist and options field empty")
			sendError(rw, http.StatusConflict, err)
			return
		} else if err := ValidateOptions(su.Options); err != nil {
			sendError(rw, http.StatusBadRequest, err)
			return
		} else {
			// New valid unit
			newUnit = true
		}
	} else if eu.Name == su.Name && len(su.Options) > 0 {
		// There is already a unit with the same name that
		// was submitted before. Check their hashes, if they do
		// not match then this is probably a new version which
		// needs its own new unit entry.
		// In the other case if su.Options == 0 then probably we
		// don't want to update the Unit options nor its content
		// but only set the target job state of the
		// corresponding unit, in this case just ignore.
		a := schema.MapSchemaUnitOptionsToUnitFile(su.Options)
		b := schema.MapSchemaUnitOptionsToUnitFile(eu.Options)
		newUnit = !unit.MatchUnitFiles(a, b)
	}

	if newUnit {
		ur.create(rw, su.Name, &su)
		return
	}

	if len(su.DesiredState) == 0 {
		err := errors.New("must provide DesiredState to update existing unit")
		sendError(rw, http.StatusConflict, err)
		return
	}

	un := unit.NewUnitNameInfo(su.Name)
	if un.IsTemplate() && job.JobState(su.DesiredState) != job.JobStateInactive {
		err := fmt.Errorf("cannot activate template %q", su.Name)
		sendError(rw, http.StatusBadRequest, err)
		return
	}

	ur.update(rw, su.Name, su.DesiredState)
}
Beispiel #15
0
// lazyCreateUnits iterates over a set of unit names and, for each, attempts to
// ensure that a unit by that name exists in the Registry, by checking a number
// of conditions and acting on the first one that succeeds, in order of:
//  1. a unit by that name already existing in the Registry
//  2. a unit file by that name existing on disk
//  3. a corresponding unit template (if applicable) existing in the Registry
//  4. a corresponding unit template (if applicable) existing on disk
// Any error encountered during these steps is returned immediately (i.e.
// subsequent Jobs are not acted on). An error is also returned if none of the
// above conditions match a given Job.
func lazyCreateUnits(args []string) error {
	errchan := make(chan error)
	var wg sync.WaitGroup
	for _, arg := range args {
		// TODO(jonboulle): this loop is getting too unwieldy; factor it out

		arg = maybeAppendDefaultUnitType(arg)
		name := unitNameMangle(arg)

		// First, check if there already exists a Unit by the given name in the Registry
		u, err := cAPI.Unit(name)
		if err != nil {
			return fmt.Errorf("error retrieving Unit(%s) from Registry: %v", name, err)
		}
		if u != nil {
			log.Debugf("Found Unit(%s) in Registry, no need to recreate it", name)
			warnOnDifferentLocalUnit(arg, u)
			continue
		}

		var uf *unit.UnitFile
		// Failing that, assume the name references a local unit file on disk, and attempt to load that, if it exists
		// TODO(mischief): consolidate these two near-identical codepaths
		if _, err := os.Stat(arg); !os.IsNotExist(err) {
			uf, err = getUnitFromFile(arg)
			if err != nil {
				return fmt.Errorf("failed getting Unit(%s) from file: %v", arg, err)
			}
		} else {
			// Otherwise (if the unit file does not exist), check if the name appears to be an instance unit,
			// and if so, check for a corresponding template unit in the Registry
			uni := unit.NewUnitNameInfo(name)
			if uni == nil {
				return fmt.Errorf("error extracting information from unit name %s", name)
			} else if !uni.IsInstance() {
				return fmt.Errorf("unable to find Unit(%s) in Registry or on filesystem", name)
			}
			tmpl, err := cAPI.Unit(uni.Template)
			if err != nil {
				return fmt.Errorf("error retrieving template Unit(%s) from Registry: %v", uni.Template, err)
			}

			// Finally, if we could not find a template unit in the Registry, check the local disk for one instead
			if tmpl == nil {
				file := path.Join(path.Dir(arg), uni.Template)
				if _, err := os.Stat(file); os.IsNotExist(err) {
					return fmt.Errorf("unable to find Unit(%s) or template Unit(%s) in Registry or on filesystem", name, uni.Template)
				}
				uf, err = getUnitFromFile(file)
				if err != nil {
					return fmt.Errorf("failed getting template Unit(%s) from file: %v", uni.Template, err)
				}
			} else {
				warnOnDifferentLocalUnit(arg, tmpl)
				uf = schema.MapSchemaUnitOptionsToUnitFile(tmpl.Options)
			}

			// If we found a template unit, create a near-identical instance unit in
			// the Registry - same unit file as the template, but different name
		}

		_, err = createUnit(name, uf)
		if err != nil {
			return err
		}

		wg.Add(1)
		go checkUnitState(name, job.JobStateInactive, sharedFlags.BlockAttempts, os.Stdout, &wg, errchan)
	}

	go func() {
		wg.Wait()
		close(errchan)
	}()

	haserr := false
	for msg := range errchan {
		stderr("Error waiting on unit creation: %v", msg)
		haserr = true
	}

	if haserr {
		return fmt.Errorf("One or more errors creating units")
	}

	return nil
}