From 67f6d6cdb53894967b1c5bcdb359c8f0434ca3a1 Mon Sep 17 00:00:00 2001 From: Shawn Neal Date: Wed, 19 Aug 2015 10:38:31 -0700 Subject: [PATCH] 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. --- provisioner/guest_commands.go | 72 +++++++++++++++++ provisioner/guest_commands_test.go | 120 +++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 provisioner/guest_commands.go create mode 100644 provisioner/guest_commands_test.go diff --git a/provisioner/guest_commands.go b/provisioner/guest_commands.go new file mode 100644 index 000000000..4a65312f9 --- /dev/null +++ b/provisioner/guest_commands.go @@ -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 +} \ No newline at end of file diff --git a/provisioner/guest_commands_test.go b/provisioner/guest_commands_test.go new file mode 100644 index 000000000..d07c4bd44 --- /dev/null +++ b/provisioner/guest_commands_test.go @@ -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) + } +} \ No newline at end of file