fix/upd instruction/copy | add tests
This commit is contained in:
parent
fa8a0a5ae5
commit
2e3700786d
|
@ -1,7 +1,6 @@
|
||||||
package instruction
|
package instruction
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
@ -17,7 +16,7 @@ func (d *copy) Raw() string { return strings.Join([]string{"copy", d.raw}, " ")
|
||||||
func (d *copy) Build(_args string) error {
|
func (d *copy) Build(_args string) error {
|
||||||
|
|
||||||
// 1. extract action (sub command)
|
// 1. extract action (sub command)
|
||||||
split := strings.Split(_args, " ")
|
split := strings.Fields(_args)
|
||||||
|
|
||||||
// 2. check syntax
|
// 2. check syntax
|
||||||
if len(split) != 2 {
|
if len(split) != 2 {
|
||||||
|
@ -34,12 +33,12 @@ func (d copy) Exec(ctx ExecutionContext) ([]byte, error) {
|
||||||
|
|
||||||
// 1. fail if source file not found
|
// 1. fail if source file not found
|
||||||
if _, err := os.Stat(d.Src); os.IsNotExist(err) {
|
if _, err := os.Stat(d.Src); os.IsNotExist(err) {
|
||||||
return nil, fmt.Errorf("cannot find script '%s'", d.Src)
|
return nil, &FileError{"cannot find file", d.Src, err}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. execute script
|
// 2. execute script
|
||||||
if err := ctx.Executor.Command("cp", d.Src, d.Dst).Run(); err != nil {
|
if err := ctx.Executor.Command("cp", "-r", d.Src, d.Dst).Run(); err != nil {
|
||||||
return nil, fmt.Errorf("cannot copy '%s' to '%s' | %s", d.Src, d.Dst, err)
|
return nil, &FileError{"cannot copy to", d.Dst, err}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -50,30 +49,33 @@ func (d copy) DryRun(ctx ExecutionContext) ([]byte, error) {
|
||||||
|
|
||||||
// 1. fail if source file not found
|
// 1. fail if source file not found
|
||||||
if _, err := os.Stat(d.Src); os.IsNotExist(err) {
|
if _, err := os.Stat(d.Src); os.IsNotExist(err) {
|
||||||
return nil, fmt.Errorf("cannot find file '%s'", d.Src)
|
return nil, &FileError{"cannot find file", d.Src, err}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. if destination to create : try to create (then remove)
|
// 2. if destination to create : try to create (then remove)
|
||||||
if _, err := os.Stat(d.Dst); os.IsNotExist(err) {
|
fi, err := os.Stat(d.Dst)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
|
||||||
file, err2 := os.OpenFile(d.Dst, os.O_APPEND|os.O_WRONLY|os.O_CREATE, os.FileMode(0777))
|
file, err2 := os.OpenFile(d.Dst, os.O_APPEND|os.O_WRONLY|os.O_CREATE, os.FileMode(0777))
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
return nil, fmt.Errorf("cannot copy to '%s' | %s", d.Dst, err2)
|
return nil, &FileError{"cannot copy to", d.Dst, err2}
|
||||||
}
|
}
|
||||||
file.Close()
|
file.Close()
|
||||||
|
|
||||||
if err2 := os.Remove(d.Dst); err2 != nil {
|
if err2 := os.Remove(d.Dst); err2 != nil {
|
||||||
return nil, fmt.Errorf("cannot remove dry-run file '%s' | %s", d.Dst, err2)
|
return nil, &FileError{"cannot remove dry-run file", d.Dst, err2}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
||||||
|
} else if fi != nil && fi.IsDir() {
|
||||||
|
return nil, nil // no error if dir
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. if destination exists : check write permission
|
// 3. if destination exists : check write permission
|
||||||
file, err := os.OpenFile(d.Dst, os.O_APPEND|os.O_WRONLY, 0600)
|
file, err := os.OpenFile(d.Dst, os.O_APPEND|os.O_WRONLY, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot copy to '%s' | %s", d.Dst, err)
|
return nil, &FileError{"cannot copy to", d.Dst, err}
|
||||||
}
|
}
|
||||||
file.Close()
|
file.Close()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,308 @@
|
||||||
|
package instruction
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCopyInvalidSyntax(t *testing.T) {
|
||||||
|
tests := []string{
|
||||||
|
"one-arg",
|
||||||
|
"src dst extra-arg",
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
inst := new(copy)
|
||||||
|
err := inst.Build(test)
|
||||||
|
if err != ErrInvalidSyntax {
|
||||||
|
t.Errorf("[%d] expected error <%s>, got <%s>", i, ErrInvalidSyntax, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestCopyBuildArgs(t *testing.T) {
|
||||||
|
tests := []string{
|
||||||
|
"source destination",
|
||||||
|
"\tsource destination",
|
||||||
|
" source destination",
|
||||||
|
"source\t destination",
|
||||||
|
"source destination",
|
||||||
|
"source \tdestination",
|
||||||
|
"source destination",
|
||||||
|
"source destination\t",
|
||||||
|
"source\t\tdestination\t",
|
||||||
|
"source \t\t destination\t",
|
||||||
|
"source\t \tdestination\t",
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
inst := new(copy)
|
||||||
|
err := inst.Build(test)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("[%d] unexpected error <%s>", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if inst.Src != "source" {
|
||||||
|
t.Errorf("[%d] expected 'source', got '%s'", i, inst.Src)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if inst.Dst != "destination" {
|
||||||
|
t.Errorf("[%d] expected 'source', got '%s'", i, inst.Dst)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopySourceNotExist(t *testing.T) {
|
||||||
|
defer os.RemoveAll("/tmp/destination")
|
||||||
|
raw := "/tmp/source /tmp/destination"
|
||||||
|
|
||||||
|
ctx, err := CreateContext("apt-get", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot create context")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
inst := new(copy)
|
||||||
|
err := inst.Build(raw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("[%d] unexpected error <%s>", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
|
_, err = inst.Exec(*ctx)
|
||||||
|
} else {
|
||||||
|
_, err = inst.DryRun(*ctx)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("[%d] expected error", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
ce, ok := err.(*FileError)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("[%d] expected error of type <*FileError>", i)
|
||||||
|
}
|
||||||
|
if ce.Reason != "cannot find file" || ce.File != "/tmp/source" {
|
||||||
|
t.Fatalf("[%d] expected error <%s '%s'> got <%s '%s'>", i, "cannot find file", "/tmp/source", ce.Reason, ce.File)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopySourceIsDir(t *testing.T) {
|
||||||
|
src, dst := "/tmp/sourcedir", "/tmp/destinationdir"
|
||||||
|
raw := fmt.Sprintf("%s %s", src, dst)
|
||||||
|
defer os.RemoveAll(src)
|
||||||
|
defer os.RemoveAll(dst)
|
||||||
|
|
||||||
|
ctx, err := CreateContext("apt-get", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot create context")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
// 1. create directory
|
||||||
|
os.RemoveAll(src)
|
||||||
|
if err := os.MkdirAll(src, os.FileMode(0777)); err != nil {
|
||||||
|
t.Fatalf("[%d] cannot create test directory | %s", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
inst := new(copy)
|
||||||
|
err := inst.Build(raw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("[%d] unexpected error <%s>", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
|
_, err = inst.Exec(*ctx)
|
||||||
|
} else {
|
||||||
|
_, err = inst.DryRun(*ctx)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("[%d] unexpected error <%s>", i, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
func TestCopyInvalidDestination(t *testing.T) {
|
||||||
|
src, dst := "/tmp/source", "/tmp/missing-directory/invalid-destination"
|
||||||
|
raw := fmt.Sprintf("%s %s", src, dst)
|
||||||
|
defer os.RemoveAll(src)
|
||||||
|
defer os.RemoveAll(dst)
|
||||||
|
|
||||||
|
ctx, err := CreateContext("apt-get", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot create context")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
|
||||||
|
os.RemoveAll(src)
|
||||||
|
// 1. create directory
|
||||||
|
if err := os.MkdirAll(src, os.FileMode(0777)); err != nil {
|
||||||
|
t.Fatalf("[%d] cannot create test directory | %s", i, err)
|
||||||
|
}
|
||||||
|
inst := new(copy)
|
||||||
|
err := inst.Build(raw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("[%d] unexpected error <%s>", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
|
_, err = inst.Exec(*ctx)
|
||||||
|
} else {
|
||||||
|
_, err = inst.DryRun(*ctx)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("[%d] expected error", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
ce, ok := err.(*FileError)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("[%d] expected error of type <*FileError>", i)
|
||||||
|
}
|
||||||
|
if ce.Reason != "cannot copy to" || ce.File != dst {
|
||||||
|
t.Fatalf("[%d] expected error <%s '%s'> got <%s '%s'>", i, "cannot copy to", dst, ce.Reason, ce.File)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestCopySourceIsFile(t *testing.T) {
|
||||||
|
src, dst := "/tmp/source", "/tmp/destination-file"
|
||||||
|
raw := fmt.Sprintf("%s %s", src, dst)
|
||||||
|
defer os.RemoveAll(src)
|
||||||
|
defer os.RemoveAll(dst)
|
||||||
|
|
||||||
|
ctx, err := CreateContext("apt-get", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot create context")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
|
||||||
|
os.RemoveAll(src)
|
||||||
|
|
||||||
|
// 1. create directory
|
||||||
|
fd, err := os.Create(src)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("[%d] cannot create test file | %s", i, err)
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
|
||||||
|
inst := new(copy)
|
||||||
|
err = inst.Build(raw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("[%d] unexpected error <%s>", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
|
_, err = inst.Exec(*ctx)
|
||||||
|
} else {
|
||||||
|
_, err = inst.DryRun(*ctx)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("[%d] unexpected error <%s>", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopySourceIsFileDestinationExists(t *testing.T) {
|
||||||
|
src, dst := "/tmp/source", "/tmp/destination-file"
|
||||||
|
raw := fmt.Sprintf("%s %s", src, dst)
|
||||||
|
defer os.RemoveAll(src)
|
||||||
|
defer os.RemoveAll(dst)
|
||||||
|
|
||||||
|
ctx, err := CreateContext("apt-get", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot create context")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
|
||||||
|
os.RemoveAll(src)
|
||||||
|
os.RemoveAll(dst)
|
||||||
|
|
||||||
|
// 1. create source
|
||||||
|
fd, err := os.Create(src)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("[%d] cannot create test file | %s", i, err)
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
// 1. create destination
|
||||||
|
fd, err = os.Create(src)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("[%d] cannot create test file | %s", i, err)
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
|
||||||
|
inst := new(copy)
|
||||||
|
err = inst.Build(raw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("[%d] unexpected error <%s>", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
|
_, err = inst.Exec(*ctx)
|
||||||
|
} else {
|
||||||
|
_, err = inst.DryRun(*ctx)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("[%d] unexpected error <%s>", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestCopyCannotWriteDestination(t *testing.T) {
|
||||||
|
src, dst := "/tmp/source", "/tmp/destination-perm"
|
||||||
|
raw := fmt.Sprintf("%s %s", src, dst)
|
||||||
|
defer os.RemoveAll(src)
|
||||||
|
defer os.RemoveAll(dst)
|
||||||
|
|
||||||
|
ctx, err := CreateContext("apt-get", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot create context")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
|
||||||
|
os.RemoveAll(src)
|
||||||
|
if err := os.RemoveAll(dst); err != nil {
|
||||||
|
t.Fatalf("[%d] cannot remove destination file\n", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. create source
|
||||||
|
fd, err := os.Create(src)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("[%d] cannot create test file | %s", i, err)
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
// 1. create destination
|
||||||
|
fd, err = os.Create(src)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("[%d] cannot create test file | %s", i, err)
|
||||||
|
}
|
||||||
|
err = fd.Chmod(os.FileMode(0555))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("[%d] cannot set permissions | %s", i, err)
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
|
||||||
|
inst := new(copy)
|
||||||
|
err = inst.Build(raw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("[%d] unexpected error <%s>", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
|
_, err = inst.Exec(*ctx)
|
||||||
|
} else {
|
||||||
|
_, err = inst.DryRun(*ctx)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("[%d] unexpected error <%s>", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,28 @@
|
||||||
package instruction
|
package instruction
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrInvalidAlias is raised when encountering an invalid token in an alias name
|
// ErrInvalidAlias is raised when encountering an invalid token in an alias name
|
||||||
var ErrInvalidAlias = errors.New("invalid alias name (contains '/')")
|
var ErrInvalidAlias = fmt.Errorf("invalid alias name (contains '/')")
|
||||||
|
|
||||||
// ErrInvalidSyntax is raised when encountering an invalid token
|
// ErrInvalidSyntax is raised when encountering an invalid token
|
||||||
var ErrInvalidSyntax = errors.New("invalid instruction format")
|
var ErrInvalidSyntax = fmt.Errorf("invalid instruction format")
|
||||||
|
|
||||||
// ErrUnknownInstruction is raised when encountering an unknown instruction
|
// ErrUnknownInstruction is raised when encountering an unknown instruction
|
||||||
// it can mean that you're not using the right version or that you've misspelled it
|
// it can mean that you're not using the right version or that you've misspelled it
|
||||||
var ErrUnknownInstruction = errors.New("unknown instruction")
|
var ErrUnknownInstruction = fmt.Errorf("unknown instruction")
|
||||||
|
|
||||||
|
type FileError struct {
|
||||||
|
Reason string
|
||||||
|
File string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ce FileError) Error() string {
|
||||||
|
if ce.Err == nil {
|
||||||
|
return fmt.Sprintf("%s '%s'", ce.Reason, ce.File)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s '%s' | %s", ce.Reason, ce.File, ce.Err)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue