From 1e90d1336cb6dc84681458c43f898695ab1b9b3d Mon Sep 17 00:00:00 2001 From: Chris Bednarski Date: Sat, 14 Feb 2015 03:01:13 -0800 Subject: [PATCH] Implemented add and fix with some error handling (no saving yet) --- cmd/main.go | 107 +++++++++++++++++++++++++++++++--------------------- commands.go | 96 +++++++++++++++++++++++++++++++++++++++------- hostess.go | 16 +++++++- 3 files changed, 161 insertions(+), 58 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 1923e5c..3c64fbb 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,9 +1,9 @@ package main import ( - "flag" "fmt" "github.com/cbednarski/hostess" + "github.com/codegangsta/cli" "os" ) @@ -20,6 +20,8 @@ const help = `Hostess: an idempotent tool for managing /etc/hosts Commands will exit 0 or 1 in a sensible way so you can use the exit code for bash and make scripting. Add -h to any command to learn more about it. +To preview a hostess-managed hostsfile run ` + "`" + `hostess fix -n` + "`" + ` + WARNING: This program is BETA and not all commands are implemented. Commands: @@ -37,7 +39,7 @@ Commands: Options: - -f Force write to the hostfile even if there are errors or conflicts + -f Force write to the hostsfile even if there are errors or conflicts -n No-op. Show changes but don't write them. -q Supress error messages -s Supress success messages (implies -q) @@ -50,56 +52,77 @@ Report bugs at https://github.com/cbednarski/hostess ` func main() { - hostfile := hostess.NewHostfile(hostess.GetHostsPath()) - hostfile.Load() - hostfile.Parse() - - flags := make(map[string]*bool) + app := cli.NewApp() + app.Name = "hostess" + app.Usage = help + app.Version = "0.1.0" - flags["force"] = flag.Bool("f", false, "Forcibly apply changes, even if there are errors or conflicts") - flags["noop"] = flag.Bool("n", false, "No-op. Show changes but don't write them.") - flags["quiet"] = flag.Bool("q", false, "Suppress error and conflict messages.") - flags["silent"] = flag.Bool("s", false, "Suppress all output. Check exit codes for success / failure.") - flags["help"] = flag.Bool("h", false, "Help") - - flag.Parse() + var err error = nil - // Guard against zero arguments - var args []string - if len(flag.Args()) > 0 { - args = flag.Args()[1:] + app.Flags = []cli.Flag{ + cli.BoolFlag{ + Name: "f", + Usage: "Force", + }, + cli.BoolFlag{ + Name: "n", + Usage: "Noop", + }, + cli.BoolFlag{ + Name: "q", + Usage: "Quiet", + }, + cli.BoolFlag{ + Name: "s", + Usage: "Silent", + }, } - var err error = nil - - switch flag.Arg(0) { - case "add": - err = hostess.Add(args, flags) - case "del": - err = hostess.Del(args, flags) - case "has": - err = hostess.Has(args, flags) - case "off": - err = hostess.Fix(args, flags) - case "on": - err = hostess.Fix(args, flags) - case "ls": - err = hostess.Fix(args, flags) - case "fix": - err = hostess.Fix(args, flags) - case "dump": - err = hostess.Fix(args, flags) - case "apply": - err = hostess.Fix(args, flags) - - default: - fmt.Print(help) + app.Commands = []cli.Command{ + { + Name: "add", + // Usage: "add a task to the list", + Action: hostess.Add, + Flags: app.Flags, + }, + { + Name: "fix", + // Usage: "add a task to the list", + Action: hostess.Fix, + Flags: app.Flags, + }, } + // switch flag.Arg(0) { + // case "add": + // err = hostess.Add(args, flags) + // case "del": + // err = hostess.Del(args, flags) + // case "has": + // err = hostess.Has(args, flags) + // case "off": + // err = hostess.Off(args, flags) + // case "on": + // err = hostess.On(args, flags) + // case "ls": + // err = hostess.Ls(args, flags) + // case "fix": + // err = hostess.Fix(args, flags) + // case "dump": + // err = hostess.Dump(args, flags) + // case "apply": + // err = hostess.Apply(args, flags) + + // default: + // fmt.Print(help) + // } + if err != nil { fmt.Println(err) os.Exit(1) } + app.Run(os.Args) + os.Exit(0) } diff --git a/commands.go b/commands.go index 90ba9d7..7785c1e 100644 --- a/commands.go +++ b/commands.go @@ -1,44 +1,112 @@ package hostess import ( - "errors" + // "errors" + "fmt" + "github.com/codegangsta/cli" + "os" ) -func Add(args []string, flags map[string]*bool) error { - if len(args) > 2 { - return errors.New("Unexpected arguments") +func MaybeErrorln(c *cli.Context, message string) { + if !c.Bool("q") { + fmt.Printf("%s: %s\n", c.Command.Name, message) } - return nil } -func Del(args []string, flags map[string]*bool) error { - return nil +func MaybeError(c *cli.Context, message string) { + if !c.Bool("q") { + fmt.Printf("%s: %s\n", c.Command.Name, message) + } + os.Exit(1) +} + +func MaybePrintln(c *cli.Context, message string) { + if !c.Bool("s") { + fmt.Println(message) + } +} + +func MaybeLoadHostFile(c *cli.Context) *Hostfile { + hostsfile, errs := LoadHostFile() + if len(errs) > 0 && !c.Bool("f") { + for _, err := range errs { + MaybeErrorln(c, err.Error()) + } + MaybeError(c, "Errors while parsing hostsfile") + } + return hostsfile +} + +func SprintEnabled(on bool) string { + if on { + return "(On)" + } else { + return "(Off)" + } } -func Has(args []string, flags map[string]*bool) error { +func Add(c *cli.Context) { + if len(c.Args()) != 2 { + MaybeError(c, "expected ") + } + + hostsfile := MaybeLoadHostFile(c) + hostname := Hostname{c.Args()[0], c.Args()[1], true} + err := hostsfile.Add(hostname) + if err == nil { + if c.Bool("n") { + fmt.Println(hostsfile.Format()) + } else { + MaybePrintln(c, fmt.Sprintf("Added %s -> %s %s", hostname.Domain, hostname.Ip, SprintEnabled(hostname.Enabled))) + hostsfile.Save() + } + } else { + MaybeError(c, err.Error()) + } +} + +func Del(c *cli.Context) error { return nil } -func Off(args []string, flags map[string]*bool) error { +func Has(c *cli.Context) error { return nil } -func On(args []string, flags map[string]*bool) error { +func Off(c *cli.Context) error { return nil } -func Ls(args []string, flags map[string]*bool) error { +func On(c *cli.Context) error { return nil } -func Fix(args []string, flags map[string]*bool) error { +func Ls(c *cli.Context) error { return nil } -func Dump(args []string, flags map[string]*bool) error { +const fix_help = `Programmatically rewrite your hostsfile. + +Domains pointing to the same IP will be consolidated, sorted, and extra +whitespace and comments will be removed. + + hostess fix Rewrite the hostsfile + hostess fix -n Show the new hostsfile. Don't write it +` + +func Fix(c *cli.Context) { + hostfile := MaybeLoadHostFile(c) + if c.Bool("n") { + fmt.Println(hostfile.Format()) + } else { + hostfile.Save() + } +} + +func Dump(c *cli.Context) error { return nil } -func Apply(args []string, flags map[string]*bool) error { +func Apply(c *cli.Context) error { return nil } diff --git a/hostess.go b/hostess.go index 7bf83be..51add52 100644 --- a/hostess.go +++ b/hostess.go @@ -150,6 +150,13 @@ func NewHostfile(path string) *Hostfile { return &Hostfile{path, make(map[string]*Hostname), ""} } +func LoadHostFile() (*Hostfile, []error) { + hostfile := NewHostfile(GetHostsPath()) + hostfile.Load() + errs := hostfile.Parse() + return hostfile, errs +} + func (h *Hostfile) Load() string { data, err := ioutil.ReadFile(h.Path) if err != nil { @@ -160,12 +167,17 @@ func (h *Hostfile) Load() string { return h.data } -func (h *Hostfile) Parse() { +func (h *Hostfile) Parse() []error { + var errs []error for _, v := range strings.Split(h.data, "\n") { for _, hostname := range parseLine(v) { - h.Add(hostname) + err := h.Add(hostname) + if err != nil { + errs = append(errs, err) + } } } + return errs } func getSortedMapKeys(m map[string][]string) []string {