Skip to content

Commit

Permalink
improved documentation and test coverage a little
Browse files Browse the repository at this point in the history
  • Loading branch information
bmatcuk committed Aug 25, 2018
1 parent 73b408c commit 77833e3
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 49 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@

# Output of the go coverage tool, specifically when used with LiteIDE
*.out
coverage.txt
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,33 @@ The package name will be `vagrant`.


## Basic Usage
```go
func NewVagrantClient(vagrantfileDir string) (*VagrantClient, error)
```

First, you'll need to instantiate a VagrantClient object using
`NewVagrantClient`. The function takes one argument: the path to the directory
where the Vagrantfile lives. This instantiation will check that the vagrant
command exists and that the Vagrantfile can be read.

```go
func (*VagrantClient) Action() *CommandObject
CommandObject.Option1 = ...
CommandObject.Option2 = ...

func (*CommandObject) Run() error
func (*CommandObject) Start() error
func (*CommandObject) Wait() error
CommandObject.Output
CommandObject.Error
```

From there, every vagrant action follows the same pattern: call the appropriate
method on the client object to retrieve a command object. The command object
has fields for any optional arguments. Then either call the command's `Run()`
method, or call `Start()` and then `Wait()` on it later. Any output from the
command, including errors, will be fields on the command object.
command, including errors, will be fields on the command object. The exact
field names for options and output are listed below in the [Actions] section.

For example:

Expand Down Expand Up @@ -245,4 +262,5 @@ Response:
* **Error** - Set if an error occurred.


[Actions]: #available-actions
[godocs for SSHConfig]: https://godoc.org/github.com/bmatcuk/go-vagrant#SSHConfig
89 changes: 61 additions & 28 deletions base_command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,42 +26,67 @@ func (b *BaseCommand) cleanup() {
func TestBaseCommand_init(t *testing.T) {
client := newMockVagrantClient()
handler := newMockOutputHandler()
cmd := newBaseCommand(client)
cmd.Env = []string{"ENV1=value1"}
cmd.init(handler, "test", "arg1")
defer cmd.cleanup()

if cmd.cmd.Path != client.executable {
t.Errorf("Expected cmd path to be %v; got %v", client.executable, cmd.cmd.Path)
}
t.Run("base", func(t *testing.T) {
cmd := newBaseCommand(client)
cmd.Env = []string{"ENV1=value1"}
cmd.init(handler, "test", "arg1")
defer cmd.cleanup()

argsLength := len(cmd.cmd.Args)
if argsLength < 6 {
t.Errorf("Expected len(args) to be at least 6; got %v", argsLength)
} else {
if cmd.cmd.Args[0] != client.executable {
t.Errorf("Expected args[0] to be %v; got %v", client.executable, cmd.cmd.Args[0])
if cmd.cmd.Path != client.executable {
t.Errorf("Expected cmd path to be %v; got %v", client.executable, cmd.cmd.Path)
}
if cmd.cmd.Args[argsLength-3] != "test" {
t.Errorf("Expected args[1] to be 'test'; got %v", cmd.cmd.Args[argsLength-3])

argsLength := len(cmd.cmd.Args)
if argsLength < 6 {
t.Errorf("Expected len(args) to be at least 6; got %v", argsLength)
} else {
if cmd.cmd.Args[0] != client.executable {
t.Errorf("Expected args[0] to be %v; got %v", client.executable, cmd.cmd.Args[0])
}
if cmd.cmd.Args[argsLength-3] != "test" {
t.Errorf("Expected args[1] to be 'test'; got %v", cmd.cmd.Args[argsLength-3])
}
if cmd.cmd.Args[argsLength-2] != "--machine-readable" {
t.Errorf("Expected args[1] to be '--machine-readable'; got %v", cmd.cmd.Args[argsLength-2])
}
if cmd.cmd.Args[argsLength-1] != "arg1" {
t.Errorf("Expected args[2] to be 'arg1'; got %v", cmd.cmd.Args[argsLength-1])
}
}
if cmd.cmd.Args[argsLength-2] != "--machine-readable" {
t.Errorf("Expected args[1] to be '--machine-readable'; got %v", cmd.cmd.Args[argsLength-2])

if cmd.cmd.Env == nil {
t.Errorf("Expected cmd.Env to be set")
} else if cmd.cmd.Env[0] != "ENV1=value1" {
t.Errorf("Expected env[0] to be 'ENV1=value1'; got %v", cmd.cmd.Env[0])
}
if cmd.cmd.Args[argsLength-1] != "arg1" {
t.Errorf("Expected args[2] to be 'arg1'; got %v", cmd.cmd.Args[argsLength-1])

if cmd.cmd.Dir != client.VagrantfileDir {
t.Errorf("Expected Dir to be %v; got %v", client.VagrantfileDir, cmd.cmd.Dir)
}
}
})

if cmd.cmd.Env == nil {
t.Errorf("Expected cmd.Env to be set")
} else if cmd.cmd.Env[0] != "ENV1=value1" {
t.Errorf("Expected env[0] to be 'ENV1=value1'; got %v", cmd.cmd.Env[0])
}
t.Run("alreadyStarted", func(t *testing.T) {
cmd := newBaseCommand(client)
cmd.init(handler, "test")
defer cmd.cleanup()

if cmd.cmd.Dir != client.VagrantfileDir {
t.Errorf("Expected Dir to be %v; got %v", client.VagrantfileDir, cmd.cmd.Dir)
}
if err := cmd.init(handler, "test"); err == nil {
t.Errorf("Expected init to return an error on second call.")
}
})

t.Run("additionalArguments", func(t *testing.T) {
cmd := newBaseCommand(client)
cmd.AdditionalArgs = []string{"--myarg"}
cmd.init(handler, "test")
defer cmd.cleanup()

argsLength := len(cmd.cmd.Args)
if cmd.cmd.Args[argsLength-1] != "--myarg" {
t.Errorf("Expected last arg to be '--myarg'; got %v", cmd.cmd.Args[argsLength-1])
}
})
}

func TestBaseCommand_Run(t *testing.T) {
Expand All @@ -74,6 +99,14 @@ func TestBaseCommand_Run(t *testing.T) {
t.Fatalf("Error: %v", err)
}

if cmd.Process == nil {
t.Errorf("Expected Process to be set.")
}

if cmd.ProcessState == nil {
t.Errorf("Expected ProcessState to be set.")
}

if handler.subcommand != "test" {
t.Errorf("Expected subcommand 'test'; got %v", handler.subcommand)
}
Expand Down
7 changes: 6 additions & 1 deletion command_port_response_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ const portOutput = `
1534865103,,ui,info,The forwarded ports for the machine are listed below. Please note that\nthese values may differ from values configured in the Vagrantfile if the\nprovider supports automatic port collision detection and resolution.
1534865103,,ui,info,
1534865103,,ui,info, 22 (guest) => 2222 (host)
1534865103,,ui,info, 80 (guest) => 8080 (host)
1534865103,default,forwarded_port,22,2222
1534865103,default,forwarded_port,80,8080
`

func TestPortResponse_handleOutput(t *testing.T) {
Expand All @@ -30,10 +32,13 @@ func TestPortResponse_handleOutput(t *testing.T) {
t.Fatal("Expected forwarded ports for 'default', but there were none.")
}

if len(forwardedPorts) != 1 {
if len(forwardedPorts) != 2 {
t.Fatalf("Expected 1 forwarded port; got %v", len(forwardedPorts))
}
if forwardedPorts[0].Guest != 22 || forwardedPorts[0].Host != 2222 {
t.Errorf("Expected guest port 22 -> host 2222; got %v", forwardedPorts[0])
}
if forwardedPorts[1].Guest != 80 || forwardedPorts[1].Host != 8080 {
t.Errorf("Expected guest port 80 -> host 8080; got %v", forwardedPorts[1])
}
}
52 changes: 41 additions & 11 deletions command_sshconfig_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,49 @@ import (
"strings"
)

// The fields and values in this struct match the fields and values in an SSH
// config file. For example, you could build a SSH config file like:
// Host ...
// HostName ...
// Port ...
// User ...
// IdentityFile ...
type SSHConfig struct {
AdditionalFields map[string]string
ForwardAgent string
Host string
HostName string
IdentitiesOnly string
IdentityFile string
LogLevel string
// Any additional fields returned from the ssh-config command.
AdditionalFields map[string]string

// Whether or not to enable ForwardAgent - "yes" or "no"
ForwardAgent string

// The Host matches the vagrant machine name (ex: default)
Host string

// The HostName to connect to (ex: 127.0.0.1)
HostName string

// Whether or not to enable IdentitiesOnly - "yes" or "no"
IdentitiesOnly string

// Path to a private key file to use for the connection (ex: ~/.ssh/id_rsa)
IdentityFile string

// Level of logging to enable (ex: FATAL)
LogLevel string

// Whether or not to enable password authentication - "yes" or "no"
PasswordAuthentication string
Port int
StrictHostKeyChecking string
User string
UserKnownHostsFile string

// Port to connect to (ex: 22)
Port int

// Whether or not to enable StrictHostKeyChecking - "yes" or "no"
StrictHostKeyChecking string

// User to connect as (ex: root)
User string

// Path to a known hosts file (ex: /dev/null)
UserKnownHostsFile string
}

type SSHConfigResponse struct {
Expand Down
13 changes: 12 additions & 1 deletion command_sshconfig_response_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (

const sshConfigOutput = `
1534898948,default,metadata,provider,virtualbox
1534898948,default,ssh-config,Host default\n HostName 127.0.0.1\n User core\n Port 2222\n UserKnownHostsFile /dev/null\n StrictHostKeyChecking no\n PasswordAuthentication no\n IdentityFile /Users/user/.vagrant.d/insecure_private_key\n IdentitiesOnly yes\n LogLevel FATAL\n ForwardAgent yes\n
1534898948,default,ssh-config,Host default\n HostName 127.0.0.1\n User core\n Port 2222\n UserKnownHostsFile /dev/null\n StrictHostKeyChecking no\n PasswordAuthentication no\n IdentityFile /Users/user/.vagrant.d/insecure_private_key\n IdentitiesOnly yes\n LogLevel FATAL\n ForwardAgent yes\nUnknownField no\n
Host default
HostName 127.0.0.1
User core
Expand All @@ -18,6 +18,7 @@ Host default
IdentitiesOnly yes
LogLevel FATAL
ForwardAgent yes
UnknownField no
`

func TestSSHConfigResponse_handleOutput(t *testing.T) {
Expand Down Expand Up @@ -71,4 +72,14 @@ func TestSSHConfigResponse_handleOutput(t *testing.T) {
if config.ForwardAgent != "yes" {
t.Errorf("Expecting ForwardAgent to be 'yes'; got %v", config.ForwardAgent)
}
if len(config.AdditionalFields) == 0 {
t.Errorf("Expecting len(AdditionalFields) to be >0; got %v", len(config.AdditionalFields))
} else {
field, ok := config.AdditionalFields["UnknownField"]
if !ok {
t.Errorf("Expecting AdditionalFields to have an 'UnknownField'")
} else if field != "no" {
t.Errorf("Expecting 'UnknownField' to have a value of 'no'; got %v", field)
}
}
}
9 changes: 2 additions & 7 deletions output_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package vagrant
import (
"bufio"
"io"
"os"
"log"
"strings"
)

Expand Down Expand Up @@ -38,18 +38,13 @@ func (parser OutputParser) parseLine(line string, handler outputHandler) {
}

if parser.Verbose && key == "ui" {
outf := os.Stdout
level := "info"
msg := message
if len(msg) > 1 {
level = msg[0]
msg = msg[1:]
}
if level == "error" {
outf = os.Stderr
}
outf.WriteString(strings.Join(msg, ", "))
outf.Write([]byte{byte('\n')})
log.Printf("[%v] %v", strings.ToUpper(level), strings.Join(msg, ","))
}

handler.handleOutput(target, key, message)
Expand Down

0 comments on commit 77833e3

Please sign in to comment.