Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

functional: add new tests TestUnit{Submit,Load,Start}Replace #20

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions api/units.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ func (ur *unitsResource) set(rw http.ResponseWriter, req *http.Request, item str
}

var su schema.Unit
newUnit := false
dec := json.NewDecoder(req.Body)
err := dec.Decode(&su)
if err != nil {
Expand Down Expand Up @@ -111,6 +112,21 @@ func (ur *unitsResource) set(rw http.ResponseWriter, req *http.Request, item str
ur.create(rw, su.Name, &su)
}
return
} else if eu.Name == su.Name && len(su.Options) > 0 {
// There is already a unit with the same name
// check the hashes if they do not match then we will
// create a new unit with the same name and later
// the job will be update to this new unit.
// if su.Options == 0 then probably we don't want to update
// the Unit options but only its target state.
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 {
Expand Down
116 changes: 92 additions & 24 deletions fleetctl/fleetctl.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ var (
Full bool
NoLegend bool
NoBlock bool
Replace bool
BlockAttempts int
Fields string
SSHPort int
Expand Down Expand Up @@ -555,7 +556,7 @@ func getUnitFileFromTemplate(uni *unit.UnitNameInfo, fileName string) (*unit.Uni
}

if tmpl != nil {
warnOnDifferentLocalUnit(fileName, tmpl)
isLocalUnitDifferent(fileName, tmpl, true, false)
uf = schema.MapSchemaUnitOptionsToUnitFile(tmpl.Options)
log.Debugf("Template Unit(%s) found in registry", uni.Template)
} else {
Expand Down Expand Up @@ -632,10 +633,11 @@ func findUnits(args []string) (sus []schema.Unit, err error) {
return filtered, nil
}

func createUnit(name string, uf *unit.UnitFile) (*schema.Unit, error) {
func createUnit(name string, uf *unit.UnitFile, oldUnit *schema.Unit) (*schema.Unit, error) {
if uf == nil {
return nil, fmt.Errorf("nil unit provided")
}

u := schema.Unit{
Name: name,
Options: schema.MapUnitFileToSchemaUnitOptions(uf),
Expand Down Expand Up @@ -663,6 +665,47 @@ func createUnit(name string, uf *unit.UnitFile) (*schema.Unit, error) {
return &u, nil
}

// checkUnitCreation checks if the unit should be created
// It takes a unit file path as a prameter.
// It returns 0 on success and if the unit should be created, 1 if the
// unit should not be created; and any error acountered
func checkUnitCreation(arg string, old *schema.Unit) (int, error) {
name := unitNameMangle(arg)

// First, check if there already exists a Unit by the given name in the Registry
unit, err := cAPI.Unit(name)
if err != nil {
return 1, fmt.Errorf("error retrieving Unit(%s) from Registry: %v", name, err)
}

// check if the unit is running
if unit == nil {
if sharedFlags.Replace {
log.Debugf("Unit(%s) was not found in Registry", name)
}
return 0, nil
}

// if sharedFlags.Replace is not set then we warn
different, err := isLocalUnitDifferent(arg, unit, !sharedFlags.Replace, false)

// if sharedFlags.Replace is set then we fail for errors
if sharedFlags.Replace {
if err != nil {
return 1, err
} else if different {
*old = *unit
return 0, nil
} else {
stdout("Found same Unit(%s) in Registry, nothing to do", unit.Name)
}
}

log.Debugf("Found same Unit(%s) in Registry, no need to recreate it", name)

return 1, nil
}

// 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:
Expand All @@ -677,17 +720,14 @@ func lazyCreateUnits(args []string) error {
errchan := make(chan error)
var wg sync.WaitGroup
for _, arg := range args {
var oldUnit schema.Unit
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)
ret, err := checkUnitCreation(arg, &oldUnit)
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)
return err
} else if ret != 0 {
continue
}

Expand All @@ -699,7 +739,7 @@ func lazyCreateUnits(args []string) error {
return err
}

_, err = createUnit(name, uf)
_, err = createUnit(name, uf, &oldUnit)
if err != nil {
return err
}
Expand All @@ -726,24 +766,52 @@ func lazyCreateUnits(args []string) error {
return nil
}

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
// matchLocalFileAndUnit compares a file with a Unit
// Returns true if the contents of the file matches the unit one, false
// otherwise; and any error ocountered
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)
}
}
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)
}

return result, err
}

func isLocalUnitDifferent(file string, su *schema.Unit, warnIfDifferent bool, fatal bool) (bool, error) {
result, err := matchLocalFileAndUnit(file, su)
if err == nil {
if result == false && warnIfDifferent {
stderr("WARNING: Unit %s in registry differs from local unit file %s", 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 name %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 {
if result == false && warnIfDifferent {
stderr("WARNING: Unit %s in registry differs from local template unit file %s", su.Name, info.Template)
}
return !result, nil
}

return false, err
}

func lazyLoadUnits(args []string) ([]*schema.Unit, error) {
Expand Down
1 change: 1 addition & 0 deletions fleetctl/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func init() {
cmdLoadUnits.Flags.BoolVar(&sharedFlags.Sign, "sign", false, "DEPRECATED - this option cannot be used")
cmdLoadUnits.Flags.IntVar(&sharedFlags.BlockAttempts, "block-attempts", 0, "Wait until the jobs are loaded, performing up to N attempts before giving up. A value of 0 indicates no limit. Does not apply to global units.")
cmdLoadUnits.Flags.BoolVar(&sharedFlags.NoBlock, "no-block", false, "Do not wait until the jobs have been loaded before exiting. Always the case for global units.")
cmdLoadUnits.Flags.BoolVar(&sharedFlags.Replace, "replace", false, "Replace the old unit and load the new one.")
}

func runLoadUnits(args []string) (exit int) {
Expand Down
1 change: 1 addition & 0 deletions fleetctl/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func init() {
cmdStartUnit.Flags.BoolVar(&sharedFlags.Sign, "sign", false, "DEPRECATED - this option cannot be used")
cmdStartUnit.Flags.IntVar(&sharedFlags.BlockAttempts, "block-attempts", 0, "Wait until the units are launched, performing up to N attempts before giving up. A value of 0 indicates no limit. Does not apply to global units.")
cmdStartUnit.Flags.BoolVar(&sharedFlags.NoBlock, "no-block", false, "Do not wait until the units have launched before exiting. Always the case for global units.")
cmdStartUnit.Flags.BoolVar(&sharedFlags.Replace, "replace", false, "Replace the old unit and start the new one.")
}

func runStartUnit(args []string) (exit int) {
Expand Down
1 change: 1 addition & 0 deletions fleetctl/submit.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Submit a directory of units with glob matching:

func init() {
cmdSubmitUnit.Flags.BoolVar(&sharedFlags.Sign, "sign", false, "DEPRECATED - this option cannot be used")
cmdSubmitUnit.Flags.BoolVar(&sharedFlags.Replace, "replace", false, "Replace the old unit with the new one.")
}

func runSubmitUnits(args []string) (exit int) {
Expand Down
Loading