diff --git a/cmd/hostess/hostess.go b/cmd/hostess/hostess.go index 441ea16..7b8692e 100644 --- a/cmd/hostess/hostess.go +++ b/cmd/hostess/hostess.go @@ -1,9 +1,10 @@ package main import ( + "os" + "github.com/cbednarski/hostess" "github.com/codegangsta/cli" - "os" ) func getCommand() string { @@ -111,7 +112,7 @@ func main() { }, { Name: "apply", - Usage: "apply a JSON hosts dict to your hosts file", + Usage: "add hostnames from a JSON file to the hosts file", Action: hostess.Apply, Flags: app.Flags, }, diff --git a/commands.go b/commands.go index 78c6294..99f8a4f 100644 --- a/commands.go +++ b/commands.go @@ -3,9 +3,11 @@ package hostess import ( "bytes" "fmt" - "github.com/codegangsta/cli" + "io/ioutil" "os" "strings" + + "github.com/codegangsta/cli" ) // ErrCantWriteHostFile indicates that we are unable to write to the hosts file @@ -231,10 +233,32 @@ func Fix(c *cli.Context) { // Dump command outputs hosts file contents as JSON func Dump(c *cli.Context) { - + hostsfile := AlwaysLoadHostFile(c) + output, err := hostsfile.Hosts.Dump() + if err != nil { + MaybeError(c, err.Error()) + } + fmt.Println(fmt.Sprintf("%s", output)) } // Apply command adds hostnames to the hosts file from JSON func Apply(c *cli.Context) { + if len(c.Args()) != 1 { + MaybeError(c, "Usage should be apply [filename]") + } + filename := c.Args()[0] + + data, err := ioutil.ReadFile(filename) + if err != nil { + MaybeError(c, fmt.Sprintf("Unable to read %s: %s", filename, err)) + } + + hostfile := AlwaysLoadHostFile(c) + err = hostfile.Hosts.Apply(data) + if err != nil { + MaybeError(c, fmt.Sprintf("Error applying changes to hosts file: %s", err)) + } + MaybeSaveHostFile(c, hostfile) + MaybePrintln(c, fmt.Sprintf("%s applied", filename)) } diff --git a/hostlist.go b/hostlist.go index 8b91b9e..3bc6a6c 100644 --- a/hostlist.go +++ b/hostlist.go @@ -1,6 +1,7 @@ package hostess import ( + "encoding/json" "errors" "fmt" "net" @@ -411,3 +412,23 @@ func (h *Hostlist) Format() []byte { return out } + +// Dump exports all entries in the Hostlist as JSON +func (h *Hostlist) Dump() ([]byte, error) { + return json.MarshalIndent(h, "", " ") +} + +// Apply imports all entries from the JSON input to this Hostlist +func (h *Hostlist) Apply(data []byte) error { + var hostnames Hostlist + err := json.Unmarshal(data, &hostnames) + if err != nil { + return err + } + + for _, hostname := range hostnames { + h.Add(hostname) + } + + return nil +} diff --git a/hostlist_test.go b/hostlist_test.go index dcd4e4c..ec6deac 100644 --- a/hostlist_test.go +++ b/hostlist_test.go @@ -1,10 +1,12 @@ package hostess_test import ( + "bytes" "fmt" - "github.com/cbednarski/hostess" "net" "testing" + + "github.com/cbednarski/hostess" ) func TestAddDuplicate(t *testing.T) { @@ -197,3 +199,50 @@ func ExampleHostlist_1() { // # 127.0.0.1 google.com // ::1 google.com } + +const hostsjson = `[ + { + "domain": "google.com", + "ip": "127.0.0.1", + "enabled": false + }, + { + "domain": "google.com", + "ip": "::1", + "enabled": true + } +]` + +func TestDump(t *testing.T) { + hosts := hostess.NewHostlist() + hosts.Add(hostess.NewHostname("google.com", "127.0.0.1", false)) + hosts.Add(hostess.NewHostname("google.com", "::1", true)) + + expected := []byte(hostsjson) + actual, _ := hosts.Dump() + + if !bytes.Equal(actual, expected) { + t.Errorf("JSON output did not match expected output: %s", Diff(string(expected), string(actual))) + } + +} + +func TestApply(t *testing.T) { + hosts := hostess.NewHostlist() + hosts.Apply([]byte(hostsjson)) + + hostnameA := hostess.NewHostname("google.com", "127.0.0.1", false) + if !hosts.Contains(hostnameA) { + t.Errorf("Expected to find %s", hostnameA.Format()) + } + + hostnameB := hostess.NewHostname("google.com", "::1", true) + if !hosts.Contains(hostnameB) { + t.Errorf("Expected to find %s", hostnameB.Format()) + } + + hosts.Apply([]byte(hostsjson)) + if hosts.Len() != 2 { + t.Error("Hostslist contains the wrong number of items, expected 2") + } +} diff --git a/hostname.go b/hostname.go index e8cbf95..b7529d2 100644 --- a/hostname.go +++ b/hostname.go @@ -33,10 +33,10 @@ func LooksLikeIPv6(ip string) bool { // can cause unexpected behavior. Instead, use Hostlist's Add, Remove, Enable, // and Disable methods. type Hostname struct { - Domain string - IP net.IP - Enabled bool - IPv6 bool + Domain string `json:"domain"` + IP net.IP `json:"ip"` + Enabled bool `json:"enabled"` + IPv6 bool `json:"-"` } // NewHostname creates a new Hostname struct and automatically sets the IPv6