From 97046478cbbda5d86283d734b7271a8daf6b8d79 Mon Sep 17 00:00:00 2001 From: Djalal Harouni Date: Wed, 17 Feb 2016 12:16:17 +0100 Subject: [PATCH 01/11] fleetctl: add tryWaitForUnitStates() and getBlockAttempts() * tryWaitForUnitStates() tries to wait for units to reach the desired state. * getBlockAttempts() gets the correct value of how many attempts to try before giving up on an operation. These helpers will be used to make the code more consistent and clean. We do not intended to change any behaviour here. --- fleetctl/fleetctl.go | 50 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/fleetctl/fleetctl.go b/fleetctl/fleetctl.go index 71785606d..09a75b6aa 100644 --- a/fleetctl/fleetctl.go +++ b/fleetctl/fleetctl.go @@ -746,6 +746,56 @@ func setTargetStateOfUnits(units []string, state job.JobState) ([]*schema.Unit, return triggered, nil } +// getBlockAttempts gets the correct value of how many attempts to try +// before giving up on an operation. +// It returns a negative value which means try forever, if zero is +// returned then do not make any attempt, and if a positive value is +// returned then try up to that value +func getBlockAttempts() int { + // By default we wait forever + var attempts int = -1 + + // Up to BlockAttempts + if sharedFlags.BlockAttempts > 0 { + attempts = sharedFlags.BlockAttempts + } + + // NoBlock we do not wait + if sharedFlags.NoBlock { + attempts = 0 + } + + return attempts +} + +// tryWaitForUnitStates tries to wait for units to reach the desired state. +// It takes 5 arguments, the units to wait for, the desired state, the +// desired JobState, how many attempts before timing out and a writer +// interface. +// tryWaitForUnitStates polls each of the indicated units until they +// reach the desired state. If maxAttempts is zero, then it will not +// wait, it will assume that all units reached their desired state. +// If maxAttempts is negative tryWaitForUnitStates will retry forever, and +// if it is greater than zero, it will retry up to the indicated value. +// It returns 0 on success or 1 on errors. +func tryWaitForUnitStates(units []string, state string, js job.JobState, maxAttempts int, out io.Writer) (ret int) { + // We do not wait just assume we reached the desired state + if maxAttempts == 0 { + for _, name := range units { + stdout("Triggered unit %s %s", name, state) + } + return + } + + errchan := waitForUnitStates(units, js, maxAttempts, out) + for err := range errchan { + stderr("Error waiting for units: %v", err) + ret = 1 + } + + return +} + // waitForUnitStates polls each of the indicated units until each of their // states is equal to that which the caller indicates, or until the // polling operation times out. waitForUnitStates will retry forever, or From c07bce64698692e5fa9b538131a307398153dc53 Mon Sep 17 00:00:00 2001 From: Djalal Harouni Date: Wed, 2 Mar 2016 13:35:56 +0100 Subject: [PATCH 02/11] fleetctl: {load|start|stop|unload} use the tryWaitForUnitStates() and getBlockAttempts() --- fleetctl/load.go | 14 +++----------- fleetctl/start.go | 14 +++----------- fleetctl/stop.go | 14 +++----------- fleetctl/unload.go | 14 +++----------- 4 files changed, 12 insertions(+), 44 deletions(-) diff --git a/fleetctl/load.go b/fleetctl/load.go index 497f86058..8659a1498 100644 --- a/fleetctl/load.go +++ b/fleetctl/load.go @@ -46,6 +46,8 @@ func init() { } func runLoadUnits(args []string) (exit int) { + attempts := getBlockAttempts() + if err := lazyCreateUnits(args); err != nil { stderr("Error creating units: %v", err) return 1 @@ -66,17 +68,7 @@ func runLoadUnits(args []string) (exit int) { } } - if !sharedFlags.NoBlock { - errchan := waitForUnitStates(loading, job.JobStateLoaded, sharedFlags.BlockAttempts, os.Stdout) - for err := range errchan { - stderr("Error waiting for units: %v", err) - exit = 1 - } - } else { - for _, name := range loading { - stdout("Triggered unit %s load", name) - } - } + exit = tryWaitForUnitStates(loading, "load", job.JobStateLoaded, attempts, os.Stdout) return } diff --git a/fleetctl/start.go b/fleetctl/start.go index 77ea0d343..17c8a53da 100644 --- a/fleetctl/start.go +++ b/fleetctl/start.go @@ -54,6 +54,8 @@ func init() { } func runStartUnit(args []string) (exit int) { + attempts := getBlockAttempts() + if err := lazyCreateUnits(args); err != nil { stderr("Error creating units: %v", err) return 1 @@ -74,17 +76,7 @@ func runStartUnit(args []string) (exit int) { } } - if !sharedFlags.NoBlock { - errchan := waitForUnitStates(starting, job.JobStateLaunched, sharedFlags.BlockAttempts, os.Stdout) - for err := range errchan { - stderr("Error waiting for units: %v", err) - exit = 1 - } - } else { - for _, name := range starting { - stdout("Triggered unit %s start", name) - } - } + exit = tryWaitForUnitStates(starting, "start", job.JobStateLaunched, attempts, os.Stdout) return } diff --git a/fleetctl/stop.go b/fleetctl/stop.go index 262946bfb..c63904107 100644 --- a/fleetctl/stop.go +++ b/fleetctl/stop.go @@ -52,6 +52,8 @@ func init() { } func runStopUnit(args []string) (exit int) { + attempts := getBlockAttempts() + units, err := findUnits(args) if err != nil { stderr("%v", err) @@ -79,17 +81,7 @@ func runStopUnit(args []string) (exit int) { } } - if !sharedFlags.NoBlock { - errchan := waitForUnitStates(stopping, job.JobStateLoaded, sharedFlags.BlockAttempts, os.Stdout) - for err := range errchan { - stderr("Error waiting for units: %v", err) - exit = 1 - } - } else { - for _, name := range stopping { - stdout("Triggered unit %s stop", name) - } - } + exit = tryWaitForUnitStates(stopping, "stop", job.JobStateLoaded, attempts, os.Stdout) return } diff --git a/fleetctl/unload.go b/fleetctl/unload.go index 6758f2825..c9700496a 100644 --- a/fleetctl/unload.go +++ b/fleetctl/unload.go @@ -36,6 +36,8 @@ func init() { } func runUnloadUnit(args []string) (exit int) { + attempts := getBlockAttempts() + units, err := findUnits(args) if err != nil { stderr("%v", err) @@ -60,17 +62,7 @@ func runUnloadUnit(args []string) (exit int) { } } - if !sharedFlags.NoBlock { - errchan := waitForUnitStates(wait, job.JobStateInactive, sharedFlags.BlockAttempts, os.Stdout) - for err := range errchan { - stderr("Error waiting for units: %v", err) - exit = 1 - } - } else { - for _, name := range wait { - stdout("Triggered unit %s unload", name) - } - } + exit = tryWaitForUnitStates(wait, "unload", job.JobStateInactive, attempts, os.Stdout) return } From 50eadba3cdc6c5479e899544d2ed3ab965a89378 Mon Sep 17 00:00:00 2001 From: Djalal Harouni Date: Wed, 2 Mar 2016 13:42:48 +0100 Subject: [PATCH 03/11] fleetctl: move logic to lookup a unit into getUnitFile() and getUnitFileFromTemplate() --- fleetctl/fleetctl.go | 119 ++++++++++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 41 deletions(-) diff --git a/fleetctl/fleetctl.go b/fleetctl/fleetctl.go index 09a75b6aa..3b99f7f2a 100644 --- a/fleetctl/fleetctl.go +++ b/fleetctl/fleetctl.go @@ -483,6 +483,36 @@ func getChecker() *ssh.HostKeyChecker { return ssh.NewHostKeyChecker(keyFile) } +func getUnitFile(file string) (*unit.UnitFile, error) { + var uf *unit.UnitFile + name := unitNameMangle(file) + + log.Debugf("Looking up 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 unit, and if so, check for + // a corresponding template unit in the Registry or disk + uf, err = getUnitFileFromTemplate(file) + if err != nil { + return nil, err + } + + // If we found a template unit, create a near-identical instance unit in + // the Registry - same unit file as the template, but different name + } + + log.Debugf("Found Unit(%s)", name) + return uf, nil +} + // getUnitFromFile attempts to load a Unit from a given filename // It returns the Unit or nil, and any error encountered func getUnitFromFile(file string) (*unit.UnitFile, error) { @@ -497,6 +527,48 @@ func getUnitFromFile(file string) (*unit.UnitFile, error) { return unit.NewUnitFile(string(out)) } +// getUnitFileFromTemplate checks if the name appears to be an instance unit +// and gets its corresponding template unit from the registry or local disk +// It returns the Unit or nil; and any error encountered +func getUnitFileFromTemplate(arg string) (*unit.UnitFile, error) { + var uf *unit.UnitFile + name := unitNameMangle(arg) + + // 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 nil, fmt.Errorf("error extracting information from unit name %s", name) + } else if !uni.IsInstance() { + return nil, fmt.Errorf("unable to find Unit(%s) in Registry or on filesystem", name) + } + + 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 { + warnOnDifferentLocalUnit(arg, tmpl) + 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 + file := path.Join(path.Dir(arg), uni.Template) + if _, err := os.Stat(file); os.IsNotExist(err) { + return nil, 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 nil, fmt.Errorf("failed getting template Unit(%s) from file: %v", uni.Template, err) + } + } + + return uf, nil +} + func getTunnelFlag() string { tun := globalFlags.Tunnel if tun != "" && !strings.Contains(tun, ":") { @@ -599,8 +671,6 @@ 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) @@ -615,45 +685,12 @@ func lazyCreateUnits(args []string) error { 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 + // Assume that the name references a local unit file on + // disk or if it is an instance unit and if so get its + // corresponding unit + uf, err := getUnitFile(arg) + if err != nil { + return err } _, err = createUnit(name, uf) From d48c60f949839d09cb780b30cd18982bd9e7bbd1 Mon Sep 17 00:00:00 2001 From: Djalal Harouni Date: Wed, 17 Feb 2016 16:15:10 +0100 Subject: [PATCH 04/11] fleetctl: inline getBlockAttempts() in tryWaitForUnitStates() calls --- fleetctl/load.go | 4 +--- fleetctl/start.go | 4 +--- fleetctl/stop.go | 4 +--- fleetctl/unload.go | 4 +--- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/fleetctl/load.go b/fleetctl/load.go index 8659a1498..0f7b0923c 100644 --- a/fleetctl/load.go +++ b/fleetctl/load.go @@ -46,8 +46,6 @@ func init() { } func runLoadUnits(args []string) (exit int) { - attempts := getBlockAttempts() - if err := lazyCreateUnits(args); err != nil { stderr("Error creating units: %v", err) return 1 @@ -68,7 +66,7 @@ func runLoadUnits(args []string) (exit int) { } } - exit = tryWaitForUnitStates(loading, "load", job.JobStateLoaded, attempts, os.Stdout) + exit = tryWaitForUnitStates(loading, "load", job.JobStateLoaded, getBlockAttempts(), os.Stdout) return } diff --git a/fleetctl/start.go b/fleetctl/start.go index 17c8a53da..2ded29539 100644 --- a/fleetctl/start.go +++ b/fleetctl/start.go @@ -54,8 +54,6 @@ func init() { } func runStartUnit(args []string) (exit int) { - attempts := getBlockAttempts() - if err := lazyCreateUnits(args); err != nil { stderr("Error creating units: %v", err) return 1 @@ -76,7 +74,7 @@ func runStartUnit(args []string) (exit int) { } } - exit = tryWaitForUnitStates(starting, "start", job.JobStateLaunched, attempts, os.Stdout) + exit = tryWaitForUnitStates(starting, "start", job.JobStateLaunched, getBlockAttempts(), os.Stdout) return } diff --git a/fleetctl/stop.go b/fleetctl/stop.go index c63904107..941b33d7b 100644 --- a/fleetctl/stop.go +++ b/fleetctl/stop.go @@ -52,8 +52,6 @@ func init() { } func runStopUnit(args []string) (exit int) { - attempts := getBlockAttempts() - units, err := findUnits(args) if err != nil { stderr("%v", err) @@ -81,7 +79,7 @@ func runStopUnit(args []string) (exit int) { } } - exit = tryWaitForUnitStates(stopping, "stop", job.JobStateLoaded, attempts, os.Stdout) + exit = tryWaitForUnitStates(stopping, "stop", job.JobStateLoaded, getBlockAttempts(), os.Stdout) return } diff --git a/fleetctl/unload.go b/fleetctl/unload.go index c9700496a..48eb833fc 100644 --- a/fleetctl/unload.go +++ b/fleetctl/unload.go @@ -36,8 +36,6 @@ func init() { } func runUnloadUnit(args []string) (exit int) { - attempts := getBlockAttempts() - units, err := findUnits(args) if err != nil { stderr("%v", err) @@ -62,7 +60,7 @@ func runUnloadUnit(args []string) (exit int) { } } - exit = tryWaitForUnitStates(wait, "unload", job.JobStateInactive, attempts, os.Stdout) + exit = tryWaitForUnitStates(wait, "unload", job.JobStateInactive, getBlockAttempts(), os.Stdout) return } From d107bedc15c299be4ea3421189e298beec97a19a Mon Sep 17 00:00:00 2001 From: Djalal Harouni Date: Wed, 2 Mar 2016 13:45:04 +0100 Subject: [PATCH 05/11] fleetctl: improve code comment about getUnitFileFromTemplate() Improve code comments about getUnitFileFromTemplate() and kill some other useless code comments. --- fleetctl/fleetctl.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/fleetctl/fleetctl.go b/fleetctl/fleetctl.go index 3b99f7f2a..67fab4562 100644 --- a/fleetctl/fleetctl.go +++ b/fleetctl/fleetctl.go @@ -487,7 +487,7 @@ func getUnitFile(file string) (*unit.UnitFile, error) { var uf *unit.UnitFile name := unitNameMangle(file) - log.Debugf("Looking up for Unit(%s) or its corresponding template", name) + 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 @@ -499,14 +499,14 @@ func getUnitFile(file string) (*unit.UnitFile, error) { } 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 or disk + // 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(file) if err != nil { return nil, err } - - // If we found a template unit, create a near-identical instance unit in - // the Registry - same unit file as the template, but different name } log.Debugf("Found Unit(%s)", name) @@ -792,12 +792,10 @@ func getBlockAttempts() int { // By default we wait forever var attempts int = -1 - // Up to BlockAttempts if sharedFlags.BlockAttempts > 0 { attempts = sharedFlags.BlockAttempts } - // NoBlock we do not wait if sharedFlags.NoBlock { attempts = 0 } From 08c1eac528e070d0172a461c594b67bdc2a98f1a Mon Sep 17 00:00:00 2001 From: Djalal Harouni Date: Sat, 20 Feb 2016 10:22:40 +0100 Subject: [PATCH 06/11] fleetctl: getBlockAttempts() standarise on negative meaning do not block --- fleetctl/fleetctl.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fleetctl/fleetctl.go b/fleetctl/fleetctl.go index 67fab4562..9a7d76a4a 100644 --- a/fleetctl/fleetctl.go +++ b/fleetctl/fleetctl.go @@ -785,19 +785,19 @@ func setTargetStateOfUnits(units []string, state job.JobState) ([]*schema.Unit, // getBlockAttempts gets the correct value of how many attempts to try // before giving up on an operation. -// It returns a negative value which means try forever, if zero is -// returned then do not make any attempt, and if a positive value is +// It returns a negative value which means do not block, if zero is +// returned then it means try forever, and if a positive value is // returned then try up to that value func getBlockAttempts() int { // By default we wait forever - var attempts int = -1 + var attempts int = 0 if sharedFlags.BlockAttempts > 0 { attempts = sharedFlags.BlockAttempts } if sharedFlags.NoBlock { - attempts = 0 + attempts = -1 } return attempts @@ -808,14 +808,14 @@ func getBlockAttempts() int { // desired JobState, how many attempts before timing out and a writer // interface. // tryWaitForUnitStates polls each of the indicated units until they -// reach the desired state. If maxAttempts is zero, then it will not +// reach the desired state. If maxAttempts is negative, then it will not // wait, it will assume that all units reached their desired state. -// If maxAttempts is negative tryWaitForUnitStates will retry forever, and +// If maxAttempts is zero tryWaitForUnitStates will retry forever, and // if it is greater than zero, it will retry up to the indicated value. // It returns 0 on success or 1 on errors. func tryWaitForUnitStates(units []string, state string, js job.JobState, maxAttempts int, out io.Writer) (ret int) { // We do not wait just assume we reached the desired state - if maxAttempts == 0 { + if maxAttempts <= -1 { for _, name := range units { stdout("Triggered unit %s %s", name, state) } From cfe78aa2c47226a58a583c3070baa5b2f2d7c13f Mon Sep 17 00:00:00 2001 From: Djalal Harouni Date: Wed, 2 Mar 2016 13:48:05 +0100 Subject: [PATCH 07/11] fleetctl:test: push tests for getBlockAttempts() --- fleetctl/fleetctl_test.go | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/fleetctl/fleetctl_test.go b/fleetctl/fleetctl_test.go index 0d50de40c..643292809 100644 --- a/fleetctl/fleetctl_test.go +++ b/fleetctl/fleetctl_test.go @@ -181,6 +181,37 @@ func TestUnitNameMangle(t *testing.T) { } } +func TestGetBlockAttempts(t *testing.T) { + oldNoBlock := sharedFlags.NoBlock + oldBlockAttempts := sharedFlags.BlockAttempts + + defer func() { + sharedFlags.NoBlock = oldNoBlock + sharedFlags.BlockAttempts = oldBlockAttempts + }() + + var blocktests = []struct { + noBlock bool + blockAttempts int + expected int + }{ + {true, 0, -1}, + {true, -1, -1}, + {true, 9999, -1}, + {false, 0, 0}, + {false, -1, 0}, + {false, 9999, 9999}, + } + + for _, tt := range blocktests { + sharedFlags.NoBlock = tt.noBlock + sharedFlags.BlockAttempts = tt.blockAttempts + if n := getBlockAttempts(); n != tt.expected { + t.Errorf("got %d, want %d", n, tt.expected) + } + } +} + func newUnitFile(t *testing.T, contents string) *unit.UnitFile { uf, err := unit.NewUnitFile(contents) if err != nil { From 4c1d841fa1e1582a31accf5a7afabc801f5ea9f4 Mon Sep 17 00:00:00 2001 From: Djalal Harouni Date: Wed, 2 Mar 2016 13:51:05 +0100 Subject: [PATCH 08/11] fleetctl: improve getUnitFile() error handling and add some documentation Improve getUnitFile() error handling and add some documentation to getUnitFileFromTemplate() --- fleetctl/fleetctl.go | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/fleetctl/fleetctl.go b/fleetctl/fleetctl.go index 9a7d76a4a..8064fd20c 100644 --- a/fleetctl/fleetctl.go +++ b/fleetctl/fleetctl.go @@ -498,14 +498,20 @@ func getUnitFile(file string) (*unit.UnitFile, error) { } } 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 or disk. + // name appears to be an instance of a template unit + info, err := getUnitInstanceInfo(name) + if err != nil { + return nil, fmt.Errorf("failed getting template Unit(%s) info: %v", name, err) + } + + // 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(file) + uf, err = getUnitFileFromTemplate(file, info) if err != nil { - return nil, err + return nil, fmt.Errorf("failed getting Unit(%s) from template: %v", file, err) } } @@ -527,25 +533,27 @@ func getUnitFromFile(file string) (*unit.UnitFile, error) { return unit.NewUnitFile(string(out)) } -// getUnitFileFromTemplate checks if the name appears to be an instance unit -// and gets its corresponding template unit from the registry or local disk -// It returns the Unit or nil; and any error encountered -func getUnitFileFromTemplate(arg string) (*unit.UnitFile, error) { - var uf *unit.UnitFile - name := unitNameMangle(arg) - - // Check if the name appears to be an instance unit, and if so, - // check for a corresponding template unit in the Registry +func getUnitInstanceInfo(name string) (*unit.UnitNameInfo, error) { + // Check if the name appears to be an instance unit uni := unit.NewUnitNameInfo(name) if uni == nil { - return nil, fmt.Errorf("error extracting information from unit name %s", name) + return nil, errors.New("unable to extract information from unit name") } else if !uni.IsInstance() { - return nil, fmt.Errorf("unable to find Unit(%s) in Registry or on filesystem", name) + return nil, errors.New("Not an instance of a template unit") } + return uni, nil +} + +// getUnitFileFromTemplate checks if the name appears to be an instance unit +// and gets its corresponding template unit from the registry or local disk +// It returns the Unit or nil; and any error encountered +func getUnitFileFromTemplate(arg string, uni *unit.UnitNameInfo) (*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) + return nil, fmt.Errorf("unable to retrieve Unit(%s) from Registry: %v", uni.Template, err) } if tmpl != nil { @@ -557,12 +565,12 @@ func getUnitFileFromTemplate(arg string) (*unit.UnitFile, error) { // check the local disk for one instead file := path.Join(path.Dir(arg), uni.Template) if _, err := os.Stat(file); os.IsNotExist(err) { - return nil, fmt.Errorf("unable to find Unit(%s) or template Unit(%s) in Registry or on filesystem", name, uni.Template) + return nil, fmt.Errorf("unable to find Unit(%s) on filesystem", file) } uf, err = getUnitFromFile(file) if err != nil { - return nil, fmt.Errorf("failed getting template Unit(%s) from file: %v", uni.Template, err) + return nil, fmt.Errorf("unable to load Unit(%s) from file: %v", file, err) } } From 9f8eaeb1b7cd78ab0f93308fd68f3f27358a07bf Mon Sep 17 00:00:00 2001 From: Djalal Harouni Date: Wed, 2 Mar 2016 13:53:44 +0100 Subject: [PATCH 09/11] fleetctl: for errors indicate that getUnitFileFromTemplate() tried both registry and filesystem --- fleetctl/fleetctl.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fleetctl/fleetctl.go b/fleetctl/fleetctl.go index 8064fd20c..5e7d2e8c4 100644 --- a/fleetctl/fleetctl.go +++ b/fleetctl/fleetctl.go @@ -501,7 +501,7 @@ func getUnitFile(file string) (*unit.UnitFile, error) { // name appears to be an instance of a template unit info, err := getUnitInstanceInfo(name) if err != nil { - return nil, fmt.Errorf("failed getting template Unit(%s) info: %v", name, err) + return nil, fmt.Errorf("failed getting Unit(%s) info: %v", name, err) } // If it is an instance check for a corresponding template @@ -539,7 +539,7 @@ func getUnitInstanceInfo(name string) (*unit.UnitNameInfo, error) { if uni == nil { return nil, errors.New("unable to extract information from unit name") } else if !uni.IsInstance() { - return nil, errors.New("Not an instance of a template unit") + return nil, errors.New("unable to find Unit in Registry or on filesystem") } return uni, nil @@ -565,7 +565,7 @@ func getUnitFileFromTemplate(arg string, uni *unit.UnitNameInfo) (*unit.UnitFile // check the local disk for one instead file := path.Join(path.Dir(arg), uni.Template) if _, err := os.Stat(file); os.IsNotExist(err) { - return nil, fmt.Errorf("unable to find Unit(%s) on filesystem", file) + return nil, fmt.Errorf("unable to find Unit(%s) in Registry or on filesystem", uni.Template) } uf, err = getUnitFromFile(file) From 9b8486d9829bc940e7ee662ec52e890a89057717 Mon Sep 17 00:00:00 2001 From: Djalal Harouni Date: Wed, 2 Mar 2016 13:07:21 +0100 Subject: [PATCH 10/11] fleetctl: improve getUnitFile() and getUnitFileFromTemplate() Godoc --- fleetctl/fleetctl.go | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/fleetctl/fleetctl.go b/fleetctl/fleetctl.go index 5e7d2e8c4..8d4954088 100644 --- a/fleetctl/fleetctl.go +++ b/fleetctl/fleetctl.go @@ -483,6 +483,13 @@ func getChecker() *ssh.HostKeyChecker { return ssh.NewHostKeyChecker(keyFile) } +// 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(file string) (*unit.UnitFile, error) { var uf *unit.UnitFile name := unitNameMangle(file) @@ -509,7 +516,7 @@ func getUnitFile(file string) (*unit.UnitFile, error) { // 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(file, info) + uf, err = getUnitFileFromTemplate(info, file) if err != nil { return nil, fmt.Errorf("failed getting Unit(%s) from template: %v", file, err) } @@ -545,10 +552,11 @@ func getUnitInstanceInfo(name string) (*unit.UnitNameInfo, error) { return uni, nil } -// getUnitFileFromTemplate checks if the name appears to be an instance unit -// and gets its corresponding template unit from the registry or local disk +// 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(arg string, uni *unit.UnitNameInfo) (*unit.UnitFile, error) { +func getUnitFileFromTemplate(uni *unit.UnitNameInfo, fileName string) (*unit.UnitFile, error) { var uf *unit.UnitFile tmpl, err := cAPI.Unit(uni.Template) @@ -557,20 +565,20 @@ func getUnitFileFromTemplate(arg string, uni *unit.UnitNameInfo) (*unit.UnitFile } if tmpl != nil { - warnOnDifferentLocalUnit(arg, tmpl) + warnOnDifferentLocalUnit(fileName, tmpl) 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 - file := path.Join(path.Dir(arg), uni.Template) - if _, err := os.Stat(file); os.IsNotExist(err) { + filePath := path.Join(path.Dir(fileName), uni.Template) + if _, err := os.Stat(filePath); os.IsNotExist(err) { return nil, fmt.Errorf("unable to find Unit(%s) in Registry or on filesystem", uni.Template) } - uf, err = getUnitFromFile(file) + uf, err = getUnitFromFile(filePath) if err != nil { - return nil, fmt.Errorf("unable to load Unit(%s) from file: %v", file, err) + return nil, fmt.Errorf("unable to load Unit(%s) from file: %v", filePath, err) } } From 9581bb6161db645b9b85f2ad45efaf5a2fcb4c88 Mon Sep 17 00:00:00 2001 From: Djalal Harouni Date: Fri, 4 Mar 2016 15:16:15 +0100 Subject: [PATCH 11/11] fleetctl: just inline getUnitInstanceInfo() and restore previous error messages --- fleetctl/fleetctl.go | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/fleetctl/fleetctl.go b/fleetctl/fleetctl.go index 8d4954088..cc3b7fffb 100644 --- a/fleetctl/fleetctl.go +++ b/fleetctl/fleetctl.go @@ -506,9 +506,11 @@ func getUnitFile(file string) (*unit.UnitFile, error) { } else { // Otherwise (if the unit file does not exist), check if the // name appears to be an instance of a template unit - info, err := getUnitInstanceInfo(name) - if err != nil { - return nil, fmt.Errorf("failed getting Unit(%s) info: %v", name, err) + 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 @@ -540,18 +542,6 @@ func getUnitFromFile(file string) (*unit.UnitFile, error) { return unit.NewUnitFile(string(out)) } -func getUnitInstanceInfo(name string) (*unit.UnitNameInfo, error) { - // Check if the name appears to be an instance unit - uni := unit.NewUnitNameInfo(name) - if uni == nil { - return nil, errors.New("unable to extract information from unit name") - } else if !uni.IsInstance() { - return nil, errors.New("unable to find Unit in Registry or on filesystem") - } - - return uni, nil -} - // 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