Cameradar 3.0.0: Uses ullaakut/nmap, runs faster, removed legacy code (#188)
Unit tests functional and coverage back to 100% Add more routes to dictionary, add more credentials, add default port 5554, rename cameradar logs ENV variable, improve unit test readability, remove tmp file
This commit is contained in:
committed by
GitHub
parent
878ca9f032
commit
5849898283
+171
@@ -0,0 +1,171 @@
|
||||
package sh
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/magefile/mage/mg"
|
||||
)
|
||||
|
||||
// RunCmd returns a function that will call Run with the given command. This is
|
||||
// useful for creating command aliases to make your scripts easier to read, like
|
||||
// this:
|
||||
//
|
||||
// // in a helper file somewhere
|
||||
// var g0 = sh.RunCmd("go") // go is a keyword :(
|
||||
//
|
||||
// // somewhere in your main code
|
||||
// if err := g0("install", "github.com/gohugo/hugo"); err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// Args passed to command get baked in as args to the command when you run it.
|
||||
// Any args passed in when you run the returned function will be appended to the
|
||||
// original args. For example, this is equivalent to the above:
|
||||
//
|
||||
// var goInstall = sh.RunCmd("go", "install") goInstall("github.com/gohugo/hugo")
|
||||
//
|
||||
// RunCmd uses Exec underneath, so see those docs for more details.
|
||||
func RunCmd(cmd string, args ...string) func(args ...string) error {
|
||||
return func(args2 ...string) error {
|
||||
return Run(cmd, append(args, args2...)...)
|
||||
}
|
||||
}
|
||||
|
||||
// OutCmd is like RunCmd except the command returns the output of the
|
||||
// command.
|
||||
func OutCmd(cmd string, args ...string) func(args ...string) (string, error) {
|
||||
return func(args2 ...string) (string, error) {
|
||||
return Output(cmd, append(args, args2...)...)
|
||||
}
|
||||
}
|
||||
|
||||
// Run is like RunWith, but doesn't specify any environment variables.
|
||||
func Run(cmd string, args ...string) error {
|
||||
return RunWith(nil, cmd, args...)
|
||||
}
|
||||
|
||||
// RunV is like Run, but always sends the command's stdout to os.Stdout.
|
||||
func RunV(cmd string, args ...string) error {
|
||||
_, err := Exec(nil, os.Stdout, os.Stderr, cmd, args...)
|
||||
return err
|
||||
}
|
||||
|
||||
// RunWith runs the given command, directing stderr to this program's stderr and
|
||||
// printing stdout to stdout if mage was run with -v. It adds adds env to the
|
||||
// environment variables for the command being run. Environment variables should
|
||||
// be in the format name=value.
|
||||
func RunWith(env map[string]string, cmd string, args ...string) error {
|
||||
var output io.Writer
|
||||
if mg.Verbose() {
|
||||
output = os.Stdout
|
||||
}
|
||||
_, err := Exec(env, output, os.Stderr, cmd, args...)
|
||||
return err
|
||||
}
|
||||
|
||||
// Output runs the command and returns the text from stdout.
|
||||
func Output(cmd string, args ...string) (string, error) {
|
||||
buf := &bytes.Buffer{}
|
||||
_, err := Exec(nil, buf, os.Stderr, cmd, args...)
|
||||
return strings.TrimSuffix(buf.String(), "\n"), err
|
||||
}
|
||||
|
||||
// OutputWith is like RunWith, ubt returns what is written to stdout.
|
||||
func OutputWith(env map[string]string, cmd string, args ...string) (string, error) {
|
||||
buf := &bytes.Buffer{}
|
||||
_, err := Exec(env, buf, os.Stderr, cmd, args...)
|
||||
return strings.TrimSuffix(buf.String(), "\n"), err
|
||||
}
|
||||
|
||||
// Exec executes the command, piping its stderr to mage's stderr and
|
||||
// piping its stdout to the given writer. If the command fails, it will return
|
||||
// an error that, if returned from a target or mg.Deps call, will cause mage to
|
||||
// exit with the same code as the command failed with. Env is a list of
|
||||
// environment variables to set when running the command, these override the
|
||||
// current environment variables set (which are also passed to the command). cmd
|
||||
// and args may include references to environment variables in $FOO format, in
|
||||
// which case these will be expanded before the command is run.
|
||||
//
|
||||
// Ran reports if the command ran (rather than was not found or not executable).
|
||||
// Code reports the exit code the command returned if it ran. If err == nil, ran
|
||||
// is always true and code is always 0.
|
||||
func Exec(env map[string]string, stdout, stderr io.Writer, cmd string, args ...string) (ran bool, err error) {
|
||||
expand := func(s string) string {
|
||||
s2, ok := env[s]
|
||||
if ok {
|
||||
return s2
|
||||
}
|
||||
return os.Getenv(s)
|
||||
}
|
||||
cmd = os.Expand(cmd, expand)
|
||||
for i := range args {
|
||||
args[i] = os.Expand(args[i], expand)
|
||||
}
|
||||
ran, code, err := run(env, stdout, stderr, cmd, args...)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if ran {
|
||||
return ran, mg.Fatalf(code, `running "%s %s" failed with exit code %d`, cmd, strings.Join(args, " "), code)
|
||||
}
|
||||
return ran, fmt.Errorf(`failed to run "%s %s: %v"`, cmd, strings.Join(args, " "), err)
|
||||
}
|
||||
|
||||
func run(env map[string]string, stdout, stderr io.Writer, cmd string, args ...string) (ran bool, code int, err error) {
|
||||
c := exec.Command(cmd, args...)
|
||||
c.Env = os.Environ()
|
||||
for k, v := range env {
|
||||
c.Env = append(c.Env, k+"="+v)
|
||||
}
|
||||
c.Stderr = stderr
|
||||
c.Stdout = stdout
|
||||
c.Stdin = os.Stdin
|
||||
log.Println("exec:", cmd, strings.Join(args, " "))
|
||||
err = c.Run()
|
||||
return CmdRan(err), ExitStatus(err), err
|
||||
}
|
||||
|
||||
// CmdRan examines the error to determine if it was generated as a result of a
|
||||
// command running via os/exec.Command. If the error is nil, or the command ran
|
||||
// (even if it exited with a non-zero exit code), CmdRan reports true. If the
|
||||
// error is an unrecognized type, or it is an error from exec.Command that says
|
||||
// the command failed to run (usually due to the command not existing or not
|
||||
// being executable), it reports false.
|
||||
func CmdRan(err error) bool {
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
ee, ok := err.(*exec.ExitError)
|
||||
if ok {
|
||||
return ee.Exited()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type exitStatus interface {
|
||||
ExitStatus() int
|
||||
}
|
||||
|
||||
// ExitStatus returns the exit status of the error if it is an exec.ExitError
|
||||
// or if it implements ExitStatus() int.
|
||||
// 0 if it is nil or 1 if it is a different error.
|
||||
func ExitStatus(err error) int {
|
||||
if err == nil {
|
||||
return 0
|
||||
}
|
||||
if e, ok := err.(exitStatus); ok {
|
||||
return e.ExitStatus()
|
||||
}
|
||||
if e, ok := err.(*exec.ExitError); ok {
|
||||
if ex, ok := e.Sys().(exitStatus); ok {
|
||||
return ex.ExitStatus()
|
||||
}
|
||||
}
|
||||
return 1
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package sh
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOutCmd(t *testing.T) {
|
||||
cmd := OutCmd(os.Args[0], "-printArgs", "foo", "bar")
|
||||
out, err := cmd("baz", "bat")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := "[foo bar baz bat]"
|
||||
if out != expected {
|
||||
t.Fatalf("expected %q but got %q", expected, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExitCode(t *testing.T) {
|
||||
ran, err := Exec(nil, nil, nil, os.Args[0], "-helper", "-exit", "99")
|
||||
if err == nil {
|
||||
t.Fatal("unexpected nil error from run")
|
||||
}
|
||||
if !ran {
|
||||
t.Errorf("ran returned as false, but should have been true")
|
||||
}
|
||||
code := ExitStatus(err)
|
||||
if code != 99 {
|
||||
t.Fatalf("expected exit status 99, but got %v", code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnv(t *testing.T) {
|
||||
env := "SOME_REALLY_LONG_MAGEFILE_SPECIFIC_THING"
|
||||
out := &bytes.Buffer{}
|
||||
ran, err := Exec(map[string]string{env: "foobar"}, out, nil, os.Args[0], "-printVar", env)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error from runner: %#v", err)
|
||||
}
|
||||
if !ran {
|
||||
t.Errorf("expected ran to be true but was false.")
|
||||
}
|
||||
if out.String() != "foobar\n" {
|
||||
t.Errorf("expected foobar, got %q", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotRun(t *testing.T) {
|
||||
ran, err := Exec(nil, nil, nil, "thiswontwork")
|
||||
if err == nil {
|
||||
t.Fatal("unexpected nil error")
|
||||
}
|
||||
if ran {
|
||||
t.Fatal("expected ran to be false but was true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAutoExpand(t *testing.T) {
|
||||
if err := os.Setenv("MAGE_FOOBAR", "baz"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s, err := Output("echo", "$MAGE_FOOBAR")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s != "baz" {
|
||||
t.Fatalf(`Expected "baz" but got %q`, s)
|
||||
}
|
||||
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package sh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Rm removes the given file or directory even if non-empty. It will not return
|
||||
// an error if the target doesn't exist, only if the target cannot be removed.
|
||||
func Rm(path string) error {
|
||||
err := os.RemoveAll(path)
|
||||
if err == nil || os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf(`failed to remove %s: %v`, path, err)
|
||||
}
|
||||
|
||||
// Copy robustly copies the source file to the destination, overwriting the destination if necessary.
|
||||
func Copy(dst string, src string) error {
|
||||
from, err := os.Open(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`can't copy %s: %v`, src, err)
|
||||
}
|
||||
defer from.Close()
|
||||
finfo, err := from.Stat()
|
||||
if err != nil {
|
||||
return fmt.Errorf(`can't stat %s: %v`, src, err)
|
||||
}
|
||||
to, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, finfo.Mode())
|
||||
if err != nil {
|
||||
return fmt.Errorf(`can't copy to %s: %v`, dst, err)
|
||||
}
|
||||
_, err = io.Copy(to, from)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`error copying %s to %s: %v`, src, dst, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
package sh_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/magefile/mage/sh"
|
||||
)
|
||||
|
||||
// compareFiles checks that two files are identical for testing purposes. That means they have the same length,
|
||||
// the same contents, and the same permissions. It does NOT mean they have the same timestamp, as that is expected
|
||||
// to change in normal Mage sh.Copy operation.
|
||||
func compareFiles(file1 string, file2 string) error {
|
||||
s1, err := os.Stat(file1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't stat %s: %v", file1, err)
|
||||
}
|
||||
s2, err := os.Stat(file2)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't stat %s: %v", file2, err)
|
||||
}
|
||||
if s1.Size() != s2.Size() {
|
||||
return fmt.Errorf("files %s and %s have different sizes: %d vs %d", file1, file2, s1.Size(), s2.Size())
|
||||
}
|
||||
if s1.Mode() != s2.Mode() {
|
||||
return fmt.Errorf("files %s and %s have different permissions: %#4o vs %#4o", file1, file2, s1.Mode(), s2.Mode())
|
||||
}
|
||||
f1bytes, err := ioutil.ReadFile(file1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't read %s: %v", file1, err)
|
||||
}
|
||||
f2bytes, err := ioutil.ReadFile(file2)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't read %s: %v", file2, err)
|
||||
}
|
||||
if !bytes.Equal(f1bytes, f2bytes) {
|
||||
return fmt.Errorf("files %s and %s have different contents", file1, file2)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestHelpers(t *testing.T) {
|
||||
|
||||
mytmpdir, err := ioutil.TempDir("", "mage")
|
||||
if err != nil {
|
||||
t.Fatalf("can't create test directory: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
derr := os.RemoveAll(mytmpdir)
|
||||
if derr != nil {
|
||||
fmt.Printf("error cleaning up after TestHelpers: %v", derr)
|
||||
}
|
||||
}()
|
||||
srcname := filepath.Join(mytmpdir, "test1.txt")
|
||||
err = ioutil.WriteFile(srcname, []byte("All work and no play makes Jack a dull boy."), 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("can't create test file %s: %v", srcname, err)
|
||||
}
|
||||
destname := filepath.Join(mytmpdir, "test2.txt")
|
||||
|
||||
t.Run("sh/copy", func(t *testing.T) {
|
||||
cerr := sh.Copy(destname, srcname)
|
||||
if cerr != nil {
|
||||
t.Errorf("test file copy from %s to %s failed: %v", srcname, destname, cerr)
|
||||
}
|
||||
cerr = compareFiles(srcname, destname)
|
||||
if cerr != nil {
|
||||
t.Errorf("test file copy verification failed: %v", cerr)
|
||||
}
|
||||
})
|
||||
|
||||
// While we've got a temporary directory, test how forgiving sh.Rm is
|
||||
t.Run("sh/rm/ne", func(t *testing.T) {
|
||||
nef := filepath.Join(mytmpdir, "file_not_exist.txt")
|
||||
rerr := sh.Rm(nef)
|
||||
if rerr != nil {
|
||||
t.Errorf("sh.Rm complained when removing nonexistent file %s: %v", nef, rerr)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("sh/copy/ne", func(t *testing.T) {
|
||||
nef := filepath.Join(mytmpdir, "file_not_exist.txt")
|
||||
nedf := filepath.Join(mytmpdir, "file_not_exist2.txt")
|
||||
cerr := sh.Copy(nedf, nef)
|
||||
if cerr == nil {
|
||||
t.Errorf("sh.Copy succeeded copying nonexistent file %s", nef)
|
||||
}
|
||||
})
|
||||
|
||||
// We test sh.Rm by clearing up our own test files and directories
|
||||
t.Run("sh/rm", func(t *testing.T) {
|
||||
rerr := sh.Rm(destname)
|
||||
if rerr != nil {
|
||||
t.Errorf("failed to remove file %s: %v", destname, rerr)
|
||||
}
|
||||
rerr = sh.Rm(srcname)
|
||||
if rerr != nil {
|
||||
t.Errorf("failed to remove file %s: %v", srcname, rerr)
|
||||
}
|
||||
rerr = sh.Rm(mytmpdir)
|
||||
if rerr != nil {
|
||||
t.Errorf("failed to remove dir %s: %v", mytmpdir, rerr)
|
||||
}
|
||||
_, rerr = os.Stat(mytmpdir)
|
||||
if rerr == nil {
|
||||
t.Errorf("removed dir %s but it's still there?", mytmpdir)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("sh/rm/nedir", func(t *testing.T) {
|
||||
rerr := sh.Rm(mytmpdir)
|
||||
if rerr != nil {
|
||||
t.Errorf("sh.Rm complained removing nonexistent dir %s", mytmpdir)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package sh
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
helperCmd bool
|
||||
printArgs bool
|
||||
stderr string
|
||||
stdout string
|
||||
exitCode int
|
||||
printVar string
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.BoolVar(&helperCmd, "helper", false, "")
|
||||
flag.BoolVar(&printArgs, "printArgs", false, "")
|
||||
flag.StringVar(&stderr, "stderr", "", "")
|
||||
flag.StringVar(&stdout, "stdout", "", "")
|
||||
flag.IntVar(&exitCode, "exit", 0, "")
|
||||
flag.StringVar(&printVar, "printVar", "", "")
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
flag.Parse()
|
||||
|
||||
if printArgs {
|
||||
fmt.Println(flag.Args())
|
||||
return
|
||||
}
|
||||
if printVar != "" {
|
||||
fmt.Println(os.Getenv(printVar))
|
||||
return
|
||||
}
|
||||
|
||||
if helperCmd {
|
||||
fmt.Fprintln(os.Stderr, stderr)
|
||||
fmt.Fprintln(os.Stdout, stdout)
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
Reference in New Issue
Block a user