Adds provisioner guest commands abstraction

Provisioners often needs to perform command line operations on guests that may have different syntax and shells. The GuestCommands type abstracts these away so provisioners can avoid littering branching logic all over the place.
This commit is contained in:
Shawn Neal 2015-08-19 10:38:31 -07:00
parent 024132a698
commit 67f6d6cdb5
2 changed files with 192 additions and 0 deletions

View File

@ -0,0 +1,72 @@
package provisioner
import (
"fmt"
"strings"
)
const UnixOSType = "unix"
const WindowsOSType = "windows"
const DefaultOSType = UnixOSType
type guestOSTypeCommand struct {
chmod string
mkdir string
removeDir string
}
var guestOSTypeCommands = map[string]guestOSTypeCommand{
UnixOSType: guestOSTypeCommand{
chmod: "chmod %s '%s'",
mkdir: "mkdir -p '%s'",
removeDir: "rm -rf '%s'",
},
WindowsOSType: guestOSTypeCommand{
chmod: "echo 'skipping chmod %s %s'", // no-op
mkdir: "New-Item -ItemType directory -Force -ErrorAction SilentlyContinue -Path %s",
removeDir: "rm %s -recurse -force",
},
}
type GuestCommands struct {
GuestOSType string
Sudo bool
}
func NewGuestCommands(osType string, sudo bool) (*GuestCommands, error) {
_, ok := guestOSTypeCommands[osType]
if !ok {
return nil, fmt.Errorf("Invalid osType: \"%s\"", osType)
}
return &GuestCommands{GuestOSType: osType, Sudo: sudo}, nil
}
func (g *GuestCommands) Chmod(path string, mode string) string {
return g.sudo(fmt.Sprintf(g.commands().chmod, mode, g.escapePath(path)))
}
func (g *GuestCommands) CreateDir(path string) string {
return g.sudo(fmt.Sprintf(g.commands().mkdir, g.escapePath(path)))
}
func (g *GuestCommands) RemoveDir(path string) string {
return g.sudo(fmt.Sprintf(g.commands().removeDir, g.escapePath(path)))
}
func (g *GuestCommands) commands() guestOSTypeCommand {
return guestOSTypeCommands[g.GuestOSType]
}
func (g *GuestCommands) escapePath(path string) string {
if g.GuestOSType == WindowsOSType {
return strings.Replace(path, " ", "` ", -1)
}
return path
}
func (g *GuestCommands) sudo(cmd string) string {
if g.GuestOSType == UnixOSType && g.Sudo {
return "sudo " + cmd
}
return cmd
}

View File

@ -0,0 +1,120 @@
package provisioner
import (
"testing"
)
func TestNewGuestCommands(t *testing.T) {
_, err := NewGuestCommands("Amiga", true)
if err == nil {
t.Fatalf("Should have returned an err for unsupported OS type")
}
}
func TestCreateDir(t *testing.T) {
// *nix OS
guestCmd, err := NewGuestCommands(UnixOSType, false)
if err != nil {
t.Fatalf("Failed to create new GuestCommands for OS: %s", UnixOSType)
}
cmd := guestCmd.CreateDir("/tmp/tempdir")
if cmd != "mkdir -p '/tmp/tempdir'" {
t.Fatalf("Unexpected Unix create dir cmd: %s", cmd)
}
// *nix OS w/sudo
guestCmd, err = NewGuestCommands(UnixOSType, true)
if err != nil {
t.Fatalf("Failed to create new sudo GuestCommands for OS: %s", UnixOSType)
}
cmd = guestCmd.CreateDir("/tmp/tempdir")
if cmd != "sudo mkdir -p '/tmp/tempdir'" {
t.Fatalf("Unexpected Unix sudo create dir cmd: %s", cmd)
}
// Windows OS
guestCmd, err = NewGuestCommands(WindowsOSType, false)
if err != nil {
t.Fatalf("Failed to create new GuestCommands for OS: %s", WindowsOSType)
}
cmd = guestCmd.CreateDir("C:\\Windows\\Temp\\tempdir")
if cmd != "New-Item -ItemType directory -Force -ErrorAction SilentlyContinue -Path C:\\Windows\\Temp\\tempdir" {
t.Fatalf("Unexpected Windows create dir cmd: %s", cmd)
}
// Windows OS w/ space in path
cmd = guestCmd.CreateDir("C:\\Windows\\Temp\\temp dir")
if cmd != "New-Item -ItemType directory -Force -ErrorAction SilentlyContinue -Path C:\\Windows\\Temp\\temp` dir" {
t.Fatalf("Unexpected Windows create dir cmd: %s", cmd)
}
}
func TestChmod(t *testing.T) {
// *nix
guestCmd, err := NewGuestCommands(UnixOSType, false)
if err != nil {
t.Fatalf("Failed to create new GuestCommands for OS: %s", UnixOSType)
}
cmd := guestCmd.Chmod("/usr/local/bin/script.sh", "0666")
if cmd != "chmod 0666 '/usr/local/bin/script.sh'" {
t.Fatalf("Unexpected Unix chmod 0666 cmd: %s", cmd)
}
// sudo *nix
guestCmd, err = NewGuestCommands(UnixOSType, true)
if err != nil {
t.Fatalf("Failed to create new sudo GuestCommands for OS: %s", UnixOSType)
}
cmd = guestCmd.Chmod("/usr/local/bin/script.sh", "+x")
if cmd != "sudo chmod +x '/usr/local/bin/script.sh'" {
t.Fatalf("Unexpected Unix chmod +x cmd: %s", cmd)
}
// Windows
guestCmd, err = NewGuestCommands(WindowsOSType, false)
if err != nil {
t.Fatalf("Failed to create new GuestCommands for OS: %s", WindowsOSType)
}
cmd = guestCmd.Chmod("C:\\Program Files\\SomeApp\\someapp.exe", "+x")
if cmd != "echo 'skipping chmod +x C:\\Program` Files\\SomeApp\\someapp.exe'" {
t.Fatalf("Unexpected Windows chmod +x cmd: %s", cmd)
}
}
func TestRemoveDir(t *testing.T) {
// *nix
guestCmd, err := NewGuestCommands(UnixOSType, false)
if err != nil {
t.Fatalf("Failed to create new GuestCommands for OS: %s", UnixOSType)
}
cmd := guestCmd.RemoveDir("/tmp/somedir")
if cmd != "rm -rf '/tmp/somedir'" {
t.Fatalf("Unexpected Unix remove dir cmd: %s", cmd)
}
// sudo *nix
guestCmd, err = NewGuestCommands(UnixOSType, true)
if err != nil {
t.Fatalf("Failed to create new sudo GuestCommands for OS: %s", UnixOSType)
}
cmd = guestCmd.RemoveDir("/tmp/somedir")
if cmd != "sudo rm -rf '/tmp/somedir'" {
t.Fatalf("Unexpected Unix sudo remove dir cmd: %s", cmd)
}
// Windows OS
guestCmd, err = NewGuestCommands(WindowsOSType, false)
if err != nil {
t.Fatalf("Failed to create new GuestCommands for OS: %s", WindowsOSType)
}
cmd = guestCmd.RemoveDir("C:\\Temp\\SomeDir")
if cmd != "rm C:\\Temp\\SomeDir -recurse -force" {
t.Fatalf("Unexpected Windows remove dir cmd: %s", cmd)
}
// Windows OS w/ space in path
cmd = guestCmd.RemoveDir("C:\\Temp\\Some Dir")
if cmd != "rm C:\\Temp\\Some` Dir -recurse -force" {
t.Fatalf("Unexpected Windows remove dir cmd: %s", cmd)
}
}