Fix argument parsing for CLI

Add tests for main and commands
Fix documentation for installation so the program works with sudo
pull/38/head
Chris Bednarski 4 years ago
parent 127256655c
commit d3e97d1758

@ -5,7 +5,8 @@ test:
go vet ./...
install:
go install .
go build -o bin/hostess .
sudo mv bin/hostess /usr/local/bin/hostess
release: test
GOOS=windows GOARCH=amd64 go build -ldflags "-X main.Version=${RELEASE_VERSION}" -o bin/hostess_windows_amd64.exe .

@ -20,7 +20,9 @@ and call it a day.
Download a [precompiled release](https://github.com/cbednarski/hostess/releases)
from GitHub, or build from source (with a [recent version of Go](https://golang.org/dl)):
go get -u github.com/cbednarski/hostess
git clone https://github.com/cbednarski/hostess
cd hostess
make install
## Usage

@ -10,6 +10,8 @@ import (
"strings"
)
const EnvHostessPath = `HOSTESS_PATH`
const defaultOSX = `
##
# Host Database
@ -53,7 +55,7 @@ func NewHostfile() *Hostfile {
// GetHostsPath returns the location of the hostfile; either env HOSTESS_PATH
// or /etc/hosts if HOSTESS_PATH is not set.
func GetHostsPath() string {
path := os.Getenv("HOSTESS_PATH")
path := os.Getenv(EnvHostessPath)
if path == "" {
if runtime.GOOS == "windows" {
path = "C:\\Windows\\System32\\drivers\\etc\\hosts"

@ -9,6 +9,8 @@ import (
"strings"
)
const EnvHostessFmt = `HOSTESS_FMT`
// ErrInvalidVersionArg is raised when a function expects IPv 4 or 6 but is
// passed a value not 4 or 6.
var ErrInvalidVersionArg = errors.New("version argument must be 4 or 6")

@ -65,8 +65,8 @@ func CommandUsage(command string) error {
return fmt.Errorf("Usage: %s %s <hostname>", os.Args[0], command)
}
func wrappedMain() error {
cli := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
func wrappedMain(args []string) error {
cli := flag.NewFlagSet(args[0], flag.ExitOnError)
ipv4 := cli.Bool("4", false, "IPv4")
ipv6 := cli.Bool("6", false, "IPv6")
preview := cli.Bool("n", false, "preview")
@ -74,7 +74,7 @@ func wrappedMain() error {
fmt.Printf(help, hostess.GetHostsPath())
}
if err := cli.Parse(os.Args[1:]); err != nil {
if err := cli.Parse(args[1:]); err != nil {
return err
}
@ -96,7 +96,7 @@ func wrappedMain() error {
fmt.Println(Version)
return nil
case "-h", "--help", "help":
case "", "-h", "--help", "help":
cli.Usage()
return nil
@ -104,46 +104,46 @@ func wrappedMain() error {
return Format(options)
case "add":
if len(cli.Args()) != 2 {
if len(cli.Args()) != 3 {
return fmt.Errorf("Usage: %s add <hostname> <ip>", cli.Name())
}
return Add(options, cli.Arg(0), cli.Arg(1))
return Add(options, cli.Arg(1), cli.Arg(2))
case "rm":
if cli.Arg(0) == "" {
if cli.Arg(1) == "" {
return CommandUsage(command)
}
return Remove(options, cli.Arg(0))
return Remove(options, cli.Arg(1))
case "on":
if cli.Arg(0) == "" {
if cli.Arg(1) == "" {
return CommandUsage(command)
}
return Enable(options, cli.Arg(0))
return Enable(options, cli.Arg(1))
case "off":
if cli.Arg(0) == "" {
if cli.Arg(1) == "" {
return CommandUsage(command)
}
return Disable(options, cli.Arg(0))
return Disable(options, cli.Arg(1))
case "ls":
return List(options)
case "has":
if cli.Arg(0) == "" {
if cli.Arg(1) == "" {
return CommandUsage(command)
}
return Has(options, cli.Arg(0))
return Has(options, cli.Arg(1))
case "dump":
return Dump(options)
case "apply":
if cli.Arg(0) == "" {
return fmt.Errorf("Usage: %s apply <filename>", os.Args[0])
if cli.Arg(1) == "" {
return fmt.Errorf("Usage: %s apply <filename>", args[0])
}
return Apply(options, cli.Arg(0))
return Apply(options, cli.Arg(1))
default:
return ErrInvalidCommand
@ -151,5 +151,5 @@ func wrappedMain() error {
}
func main() {
ExitWithError(wrappedMain())
ExitWithError(wrappedMain(os.Args))
}

@ -0,0 +1,172 @@
package main
import (
"io"
"io/ioutil"
"os"
"strings"
"testing"
"github.com/cbednarski/hostess/hostess"
)
// CopyHostsFile creates a temporary hosts file in the system temp directory,
// sets the HOSTESS_PATH environment variable, and returns the file path and a
// cleanup function
func CopyHostsFile(t *testing.T) (string, func()) {
t.Helper()
fixture, err := os.Open("testdata/ubuntu.hosts")
if err != nil {
t.Fatal(err)
}
temp, err := ioutil.TempFile("", "hostess-test-*")
if err != nil {
t.Fatal(err)
}
if _, err := io.Copy(temp, fixture); err != nil {
t.Fatal(err)
}
if err := os.Setenv(hostess.EnvHostessPath, temp.Name()); err != nil {
t.Fatal(err)
}
cleanup := func() {
os.Remove(temp.Name())
}
return temp.Name(), cleanup
}
func TestFormat(t *testing.T) {
temp, cleanup := CopyHostsFile(t)
defer cleanup()
if err := wrappedMain(strings.Split("hostess fmt", " ")); err != nil {
t.Fatal(err)
}
data, err := ioutil.ReadFile(temp)
if err != nil {
t.Fatal(err)
}
output := string(data)
expected := `127.0.0.1 localhost myapp.local
127.0.1.1 ubuntu
192.168.0.30 raspberrypi
::1 ip6-localhost ip6-loopback
fe00:: ip6-localnet
ff00:: ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
`
if output != expected {
t.Errorf("--- Expected ---\n%s\n--- Found ---\n%s\n", expected, output)
}
}
func TestAddHostname(t *testing.T) {
temp, cleanup := CopyHostsFile(t)
defer cleanup()
if err := wrappedMain(strings.Split("hostess add my.new.website 127.0.0.1", " ")); err != nil {
t.Fatal(err)
}
if err := wrappedMain(strings.Split("hostess add mediaserver 192.168.0.82", " ")); err != nil {
t.Fatal(err)
}
if err := wrappedMain(strings.Split("hostess add myapp.local 10.20.0.23", " ")); err != nil {
t.Fatal(err)
}
data, err := ioutil.ReadFile(temp)
if err != nil {
t.Fatal(err)
}
output := string(data)
expected := `127.0.0.1 localhost my.new.website
127.0.1.1 ubuntu
10.20.0.23 myapp.local
192.168.0.30 raspberrypi
192.168.0.82 mediaserver
::1 ip6-localhost ip6-loopback
fe00:: ip6-localnet
ff00:: ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
`
if output != expected {
t.Errorf("--- Expected ---\n%s\n--- Found ---\n%s\n", expected, output)
}
}
func TestRemoveHostname(t *testing.T) {
temp, cleanup := CopyHostsFile(t)
defer cleanup()
if err := wrappedMain(strings.Split("hostess rm myapp.local", " ")); err != nil {
t.Fatal(err)
}
if err := wrappedMain(strings.Split("hostess rm raspberrypi", " ")); err != nil {
t.Fatal(err)
}
data, err := ioutil.ReadFile(temp)
if err != nil {
t.Fatal(err)
}
output := string(data)
expected := `127.0.0.1 localhost
127.0.1.1 ubuntu
::1 ip6-localhost ip6-loopback
fe00:: ip6-localnet
ff00:: ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
`
if output != expected {
t.Errorf("--- Expected ---\n%s\n--- Found ---\n%s\n", expected, output)
}
}
func TestHostnameOff(t *testing.T) {
temp, cleanup := CopyHostsFile(t)
defer cleanup()
if err := wrappedMain(strings.Split("hostess off myapp.local", " ")); err != nil {
t.Fatal(err)
}
if err := wrappedMain(strings.Split("hostess off raspberrypi", " ")); err != nil {
t.Fatal(err)
}
data, err := ioutil.ReadFile(temp)
if err != nil {
t.Fatal(err)
}
output := string(data)
expected := `127.0.0.1 localhost
# 127.0.0.1 myapp.local
127.0.1.1 ubuntu
# 192.168.0.30 raspberrypi
::1 ip6-localhost ip6-loopback
fe00:: ip6-localnet
ff00:: ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
`
if output != expected {
t.Errorf("--- Expected ---\n%s\n--- Found ---\n%s\n", expected, output)
}
}

@ -0,0 +1,10 @@
127.0.0.1 localhost myapp.local
127.0.1.1 ubuntu
192.168.0.30 raspberrypi
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
Loading…
Cancel
Save