예제 #1
0
파일: job.go 프로젝트: JuanCarlosM/fleet
// Requirements returns all relevant options from the [X-Fleet] section of a unit file.
// Relevant options are identified with a `X-` prefix in the unit.
// This prefix is stripped from relevant options before being returned.
// Furthermore, specifier substitution (using unitPrintf) is performed on all requirements.
func (j *Job) Requirements() map[string][]string {
	uni := unit.NewUnitNameInfo(j.Name)
	requirements := make(map[string][]string)
	for key, values := range j.Unit.Contents["X-Fleet"] {
		if !strings.HasPrefix(key, "X-") {
			continue
		}

		// Strip off leading X-
		key = key[2:]

		if _, ok := requirements[key]; !ok {
			requirements[key] = make([]string, 0)
		}

		if uni != nil {
			for i, v := range values {
				values[i] = unitPrintf(v, *uni)
			}
		}

		requirements[key] = values
	}

	return requirements
}
예제 #2
0
파일: fleetctl.go 프로젝트: pulcy/j2
// isLocalUnitDifferent compares a Unit on the file system with a one
// provided from the Registry.
// isLocalUnitDifferent first tries to load the passed Unit from the
// local file system and compares it with the Unit that is in the
// Registry. If it fails to load that Unit from the filesystem and
// fatal was not set, it will check again if that file name is an
// instance of a template, if so it will load the template Unit and
// compare it with the provided Unit.
// It takes three arguments; a path to the local Unit on the file system,
// the Unit in the registry, and a last boolean to fail in case fatal errors
// happen.
// Returns true if the local Unit on file system is different from the
// one provided, false otherwise; and any error encountered.
func isLocalUnitDifferent(cCmd *cobra.Command, file string, su *schema.Unit, fatal bool) (bool, error) {
	replace, _ := cCmd.Flags().GetBool("replace")

	result, err := matchLocalFileAndUnit(file, su)
	if err == nil {
		// Warn in case unit differs from local file
		if result == false && !replace {
			stderr("WARNING: Unit %s in registry differs from local unit file %s. Add --replace to override.", su.Name, file)
		}
		return !result, nil
	} else if fatal {
		return false, err
	}

	info := unit.NewUnitNameInfo(path.Base(file))
	if info == nil {
		return false, fmt.Errorf("error extracting information from unit name %s", file)
	} else if !info.IsInstance() {
		return false, fmt.Errorf("error Unit %s does not seem to be a template unit", file)
	}

	templFile := path.Join(path.Dir(file), info.Template)
	result, err = matchLocalFileAndUnit(templFile, su)
	if err == nil {
		// Warn in case unit differs from local template unit file
		if result == false && !replace {
			stderr("WARNING: Unit %s in registry differs from local template unit file %s. Add --replace to override.", su.Name, file)
		}
		return !result, nil
	}

	return false, err
}
예제 #3
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
	}

	if eu == nil {
		if len(su.Options) == 0 {
			err := errors.New("unit does not exist and options field empty")
			sendError(rw, http.StatusConflict, err)
		} else if err := ValidateOptions(su.Options); err != nil {
			sendError(rw, http.StatusBadRequest, err)
		} else {
			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)
}
예제 #4
0
파일: job.go 프로젝트: ericcapricorn/deis
// requirements returns all relevant options from the [X-Fleet] section of a unit file.
// Relevant options are identified with a `X-` prefix in the unit.
// This prefix is stripped from relevant options before being returned.
// Furthermore, specifier substitution (using unitPrintf) is performed on all requirements.
func (j *Job) requirements() map[string][]string {
	uni := unit.NewUnitNameInfo(j.Name)
	requirements := make(map[string][]string)
	for key, values := range j.Unit.Contents["X-Fleet"] {
		if _, ok := requirements[key]; !ok {
			requirements[key] = make([]string, 0)
		}

		if uni != nil {
			for i, v := range values {
				values[i] = unitPrintf(v, *uni)
			}
		}
		requirements[key] = values
	}

	return requirements
}
예제 #5
0
func warnOnDifferentLocalUnit(name string, j *job.Job) {
	if _, err := os.Stat(name); !os.IsNotExist(err) {
		unit, err := getUnitFromFile(name)
		if err == nil && unit.Hash() != j.Unit.Hash() {
			fmt.Fprintf(os.Stderr, "WARNING: Job(%s) in Registry differs from local Unit(%s)\n", j.Name, name)
			return
		}
	}
	if uni := unit.NewUnitNameInfo(path.Base(name)); uni != nil && uni.IsInstance() {
		file := path.Join(path.Dir(name), uni.Template)
		if _, err := os.Stat(file); !os.IsNotExist(err) {
			tmpl, err := getUnitFromFile(file)
			if err == nil && tmpl.Hash() != j.Unit.Hash() {
				fmt.Fprintf(os.Stderr, "WARNING: Job(%s) in Registry differs from local template Unit(%s)\n", j.Name, uni.Template)
			}
		}
	}
}
예제 #6
0
파일: fleetctl.go 프로젝트: pquast/fleet
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)
			}
		}
	}
}
예제 #7
0
func TestInstanceUnitPrintf(t *testing.T) {
	u := unit.NewUnitNameInfo("*****@*****.**")
	if u == nil {
		t.Fatal("NewNamedUnit returned nil - aborting")
	}
	for _, tt := range []struct {
		in   string
		want string
	}{
		{"%n", "*****@*****.**"},
		{"%N", "foo@bar"},
		{"%p", "foo"},
		{"%i", "bar"},
	} {
		got := unitPrintf(tt.in, *u)
		if got != tt.want {
			t.Errorf("Replacement of %q failed: got %q, want %q", tt.in, got, tt.want)
		}
	}
}
예제 #8
0
파일: fleetctl.go 프로젝트: pulcy/j2
// getUnitFile attempts to get a UnitFile configuration
// It takes a unit file name as a parameter and tries first to lookup
// the unit from the local disk. If it fails, it checks if the provided
// file name may reference an instance of a template unit, if so, it
// tries to get the template configuration either from the registry or
// the local disk.
// It returns a UnitFile configuration or nil; and any error ecountered
func getUnitFile(cCmd *cobra.Command, file string) (*unit.UnitFile, error) {
	var uf *unit.UnitFile
	name := unitNameMangle(file)

	log.Debugf("Looking for Unit(%s) or its corresponding template", name)

	// Assume that the file references a local unit file on disk and
	// attempt to load it, if it exists
	if _, err := os.Stat(file); !os.IsNotExist(err) {
		uf, err = getUnitFromFile(file)
		if err != nil {
			return nil, fmt.Errorf("failed getting Unit(%s) from file: %v", file, err)
		}
	} else {
		// Otherwise (if the unit file does not exist), check if the
		// name appears to be an instance of a template unit
		info := unit.NewUnitNameInfo(name)
		if info == nil {
			return nil, fmt.Errorf("error extracting information from unit name %s", name)
		} else if !info.IsInstance() {
			return nil, fmt.Errorf("unable to find Unit(%s) in Registry or on filesystem", name)
		}

		// If it is an instance check for a corresponding template
		// unit in the Registry or disk.
		// If we found a template unit, later we create a
		// near-identical instance unit in the Registry - same
		// unit file as the template, but different name
		uf, err = getUnitFileFromTemplate(cCmd, info, file)
		if err != nil {
			return nil, fmt.Errorf("failed getting Unit(%s) from template: %v", file, err)
		}
	}

	log.Debugf("Found Unit(%s)", name)
	return uf, nil
}
예제 #9
0
파일: fleetctl.go 프로젝트: pquast/fleet
// 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
}
예제 #10
0
// lazyCreateJobs iterates over a set of Job names and, for each, attempts to
// ensure that a Job 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 Job 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.
// If signAndVerify is true, the Job will be signed (if it is created), or have
// its signature verified if it already exists in the Registry.
func lazyCreateJobs(args []string, signAndVerify bool) error {
	for _, arg := range args {
		// TODO(jonboulle): this loop is getting too unwieldy; factor it out

		jobName := unitNameMangle(arg)

		// First, check if there already exists a Job by the given name in the Registry
		j, err := cAPI.Job(jobName)
		if err != nil {
			return fmt.Errorf("error retrieving Job(%s) from Registry: %v", jobName, err)
		}
		if j != nil {
			log.V(1).Infof("Found Job(%s) in Registry, no need to recreate it", jobName)
			warnOnDifferentLocalUnit(arg, j)
			if signAndVerify {
				if err := verifyJob(j); err != nil {
					return err
				}
			}
			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", jobName, err)
			}
			j, err = createJob(jobName, unit)
			if err != nil {
				return err
			}
			if signAndVerify {
				if err := signJob(j); 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(jobName)
		if uni == nil {
			return fmt.Errorf("error extracting information from unit name %s", jobName)
		} else if !uni.IsInstance() {
			return fmt.Errorf("unable to find Unit(%s) in Registry or on filesystem", jobName)
		}
		tmpl, err := cAPI.Job(uni.Template)
		if err != nil {
			return fmt.Errorf("error retrieving template Job(%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 u *unit.Unit
		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", jobName, uni.Template)
			}
			u, err = getUnitFromFile(file)
			if err != nil {
				return fmt.Errorf("failed getting template Unit(%s) from file: %v", uni.Template, err)
			}
		} else {
			warnOnDifferentLocalUnit(arg, tmpl)
			u = &tmpl.Unit
		}

		// If we found a template Unit or Job, create a near-identical instance Job in
		// the Registry - same Unit as the template, but different name
		j, err = createJob(jobName, u)
		if err != nil {
			return err
		}
		if signAndVerify {
			if err := signJob(j); err != nil {
				return err
			}
		}
	}
	return nil
}
예제 #11
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)
}
예제 #12
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
}