From 7fb866ae4c97031b76a8bb1e90ff9ed840507fa9 Mon Sep 17 00:00:00 2001 From: Kenjiro Nakayama Date: Wed, 10 Aug 2016 23:30:19 +0900 Subject: [PATCH] Use go-getter to get jobfile by URL --- command/helpers.go | 66 +++++++++++++++++++++++++++++++++ command/helpers_test.go | 40 ++++++++++++++++++++ command/plan.go | 79 ++++----------------------------------- command/plan_test.go | 4 +- command/run.go | 80 +++++----------------------------------- command/run_test.go | 4 +- command/validate.go | 79 ++------------------------------------- command/validate_test.go | 4 +- 8 files changed, 133 insertions(+), 223 deletions(-) diff --git a/command/helpers.go b/command/helpers.go index 123f4b70e..e36de54d5 100644 --- a/command/helpers.go +++ b/command/helpers.go @@ -4,10 +4,16 @@ import ( "bytes" "fmt" "io" + "io/ioutil" + "os" "strconv" "time" + gg "github.com/hashicorp/go-getter" "github.com/hashicorp/nomad/api" + "github.com/hashicorp/nomad/jobspec" + "github.com/hashicorp/nomad/nomad/structs" + "github.com/ryanuber/columnize" ) @@ -222,3 +228,63 @@ READ: // Just stream from the underlying reader now return l.ReadCloser.Read(p) } + +type Helper struct { + // The fields below can be overwritten for tests + testStdin io.Reader +} + +// StructJob returns the Job struct from jobfile. +func (h *Helper) StructJob(jpath string) (*structs.Job, error) { + var jobfile io.Reader + switch jpath { + case "-": + if h.testStdin != nil { + jobfile = h.testStdin + } else { + jobfile = os.Stdin + } + default: + if len(jpath) == 0 { + return nil, fmt.Errorf("Error jobfile path has to be specified.") + } + + job, err := ioutil.TempFile("", "jobfile") + if err != nil { + return nil, err + } + defer os.Remove(job.Name()) + + // Get the pwd + pwd, err := os.Getwd() + if err != nil { + return nil, err + } + + client := &gg.Client{ + Src: jpath, + Pwd: pwd, + Dst: job.Name(), + } + + if err := client.Get(); err != nil { + return nil, fmt.Errorf("Error getting jobfile from %q: %v", jpath, err) + } else { + file, err := os.Open(job.Name()) + defer file.Close() + if err != nil { + return nil, fmt.Errorf("Error opening file %q: %v", jpath, err) + } + jobfile = file + } + } + + // Parse the JobFile + jobStruct, err := jobspec.Parse(jobfile) + if err != nil { + fmt.Errorf("Error parsing job file from %s: %v", jpath, err) + return nil, err + } + + return jobStruct, nil +} diff --git a/command/helpers_test.go b/command/helpers_test.go index f43e701e7..8ac1c6548 100644 --- a/command/helpers_test.go +++ b/command/helpers_test.go @@ -3,6 +3,7 @@ package command import ( "io" "io/ioutil" + "os" "reflect" "strings" "testing" @@ -185,3 +186,42 @@ func TestHelpers_LineLimitReader_TimeLimit(t *testing.T) { t.Fatalf("did not exit by time limit") } } + +func TestStructJob(t *testing.T) { + fh, err := ioutil.TempFile("", "nomad") + if err != nil { + t.Fatalf("err: %s", err) + } + defer os.Remove(fh.Name()) + _, err = fh.WriteString(` +job "job1" { + type = "service" + datacenters = [ "dc1" ] + group "group1" { + count = 1 + task "task1" { + driver = "exec" + resources = {} + } + restart{ + attempts = 10 + mode = "delay" + } + } +}`) + if err != nil { + t.Fatalf("err: %s", err) + } + + h := &Helper{} + sj, err := h.StructJob(fh.Name()) + if err != nil { + t.Fatalf("err: %s", err) + } + + err = sj.Validate() + if err != nil { + t.Fatalf("err: %s", err) + } + +} diff --git a/command/plan.go b/command/plan.go index be1895091..dff4334ba 100644 --- a/command/plan.go +++ b/command/plan.go @@ -1,17 +1,12 @@ package command import ( - "crypto/tls" "fmt" - "io" - "net/http" - "os" "sort" "strings" "time" "github.com/hashicorp/nomad/api" - "github.com/hashicorp/nomad/jobspec" "github.com/hashicorp/nomad/nomad/structs" "github.com/hashicorp/nomad/scheduler" "github.com/mitchellh/colorstring" @@ -30,10 +25,8 @@ potentially invalid.` type PlanCommand struct { Meta + Helper color *colorstring.Colorize - - // The fields below can be overwritten for tests - testStdin io.Reader } func (c *PlanCommand) Help() string { @@ -76,9 +69,6 @@ Plan Options: -verbose Increase diff verbosity. - - -k - Allow insecure SSL connections to access jobfile. ` return strings.TrimSpace(helpText) } @@ -88,13 +78,12 @@ func (c *PlanCommand) Synopsis() string { } func (c *PlanCommand) Run(args []string) int { - var diff, verbose, insecure bool + var diff, verbose bool flags := c.Meta.FlagSet("plan", FlagSetClient) flags.Usage = func() { c.Ui.Output(c.Help()) } flags.BoolVar(&diff, "diff", true, "") flags.BoolVar(&verbose, "verbose", false, "") - flags.BoolVar(&insecure, "k", false, "") if err := flags.Parse(args); err != nil { return 255 @@ -107,66 +96,12 @@ func (c *PlanCommand) Run(args []string) int { return 255 } - var path, url string - // If prefix has http(s)://, read the Jobfile from the URL - if strings.Index(args[0], "http://") == 0 || strings.Index(args[0], "https://") == 0 { - path = "_url" - url = args[0] - } else { - // Read the Jobfile - path = args[0] - } - - var f io.Reader - switch path { - case "-": - if c.testStdin != nil { - f = c.testStdin - } else { - f = os.Stdin - } - path = "stdin" - case "_url": - if len(url) == 0 { - c.Ui.Error(fmt.Sprintf("Error invalid Jobfile name")) - } - var resp *http.Response - var err error - if insecure == true { - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - client := &http.Client{Transport: tr} - resp, err = client.Get(url) - } else { - resp, err = http.Get(url) - } - if err != nil { - c.Ui.Error(fmt.Sprintf("Error accessing URL %s: %v", url, err)) - return 1 - } - defer resp.Body.Close() - if resp.StatusCode != 200 { - c.Ui.Error(fmt.Sprintf("Error reading URL (%d) : %s", resp.StatusCode, resp.Status)) - return 1 - } - f = resp.Body - path = url - default: - file, err := os.Open(path) - defer file.Close() - if err != nil { - c.Ui.Error(fmt.Sprintf("Error opening file %q: %v", path, err)) - return 255 - } - f = file - } - - // Parse the JobFile - job, err := jobspec.Parse(f) + path := args[0] + // Get Job struct from Jobfile + job, err := c.Helper.StructJob(args[0]) if err != nil { - c.Ui.Error(fmt.Sprintf("Error parsing job file %s: %v", path, err)) - return 255 + c.Ui.Error(fmt.Sprintf("Error getting job struct: %s", err)) + return 1 } // Initialize any fields that need to be. diff --git a/command/plan_test.go b/command/plan_test.go index 09bd666ba..c5eda47d1 100644 --- a/command/plan_test.go +++ b/command/plan_test.go @@ -110,8 +110,8 @@ func TestPlanCommand_From_STDIN(t *testing.T) { ui := new(cli.MockUi) cmd := &PlanCommand{ - Meta: Meta{Ui: ui}, - testStdin: stdinR, + Meta: Meta{Ui: ui}, + Helper: Helper{testStdin: stdinR}, } go func() { diff --git a/command/run.go b/command/run.go index 2da1a82ba..eaf116b27 100644 --- a/command/run.go +++ b/command/run.go @@ -2,20 +2,15 @@ package command import ( "bytes" - "crypto/tls" "encoding/gob" "encoding/json" "fmt" - "io" - "net/http" - "os" "regexp" "strconv" "strings" "time" "github.com/hashicorp/nomad/api" - "github.com/hashicorp/nomad/jobspec" "github.com/hashicorp/nomad/nomad/structs" ) @@ -26,9 +21,7 @@ var ( type RunCommand struct { Meta - - // The fields below can be overwritten for tests - testStdin io.Reader + Helper } func (c *RunCommand) Help() string { @@ -82,10 +75,6 @@ Run Options: -output Output the JSON that would be submitted to the HTTP API without submitting the job. - - -k - Allow insecure SSL connections to access jobfile. - ` return strings.TrimSpace(helpText) } @@ -95,7 +84,7 @@ func (c *RunCommand) Synopsis() string { } func (c *RunCommand) Run(args []string) int { - var detach, verbose, output, insecure bool + var detach, verbose, output bool var checkIndexStr string flags := c.Meta.FlagSet("run", FlagSetClient) @@ -103,7 +92,6 @@ func (c *RunCommand) Run(args []string) int { flags.BoolVar(&detach, "detach", false, "") flags.BoolVar(&verbose, "verbose", false, "") flags.BoolVar(&output, "output", false, "") - flags.BoolVar(&insecure, "k", false, "") flags.StringVar(&checkIndexStr, "check-index", "", "") if err := flags.Parse(args); err != nil { @@ -123,65 +111,17 @@ func (c *RunCommand) Run(args []string) int { return 1 } - var path, url string - // If prefix has http(s)://, read the Jobfile from the URL - if strings.Index(args[0], "http://") == 0 || strings.Index(args[0], "https://") == 0 { - path = "_url" - url = args[0] - } else { - // Read the Jobfile - path = args[0] + // Check that we got exactly one node + args = flags.Args() + if len(args) != 1 { + c.Ui.Error(c.Help()) + return 1 } - var f io.Reader - switch path { - case "-": - if c.testStdin != nil { - f = c.testStdin - } else { - f = os.Stdin - } - path = "stdin" - case "_url": - if len(url) == 0 { - c.Ui.Error(fmt.Sprintf("Error invalid Jobfile name")) - } - var resp *http.Response - var err error - if insecure == true { - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - client := &http.Client{Transport: tr} - resp, err = client.Get(url) - } else { - resp, err = http.Get(url) - } - if err != nil { - c.Ui.Error(fmt.Sprintf("Error accessing URL %s: %v", url, err)) - return 1 - } - defer resp.Body.Close() - if resp.StatusCode != 200 { - c.Ui.Error(fmt.Sprintf("Error reading URL (%d) : %s", resp.StatusCode, resp.Status)) - return 1 - } - f = resp.Body - path = url - default: - file, err := os.Open(path) - defer file.Close() - if err != nil { - c.Ui.Error(fmt.Sprintf("Error opening file %q: %v", path, err)) - return 1 - } - f = file - } - - // Parse the JobFile - job, err := jobspec.Parse(f) + // Get Job struct from Jobfile + job, err := c.Helper.StructJob(args[0]) if err != nil { - c.Ui.Error(fmt.Sprintf("Error parsing job file from %s: %v", path, err)) + c.Ui.Error(fmt.Sprintf("Error getting job struct: %s", err)) return 1 } diff --git a/command/run_test.go b/command/run_test.go index 9f47aa7a0..9d2fab648 100644 --- a/command/run_test.go +++ b/command/run_test.go @@ -156,8 +156,8 @@ func TestRunCommand_From_STDIN(t *testing.T) { ui := new(cli.MockUi) cmd := &RunCommand{ - Meta: Meta{Ui: ui}, - testStdin: stdinR, + Meta: Meta{Ui: ui}, + Helper: Helper{testStdin: stdinR}, } go func() { diff --git a/command/validate.go b/command/validate.go index d717fa6c8..72acbae0e 100644 --- a/command/validate.go +++ b/command/validate.go @@ -1,21 +1,13 @@ package command import ( - "crypto/tls" "fmt" - "io" - "net/http" - "os" "strings" - - "github.com/hashicorp/nomad/jobspec" ) type ValidateCommand struct { Meta - - // The fields below can be overwritten for tests - testStdin io.Reader + Helper } func (c *ValidateCommand) Help() string { @@ -27,10 +19,6 @@ Usage: nomad validate [options] If the supplied path is "-", the jobfile is read from stdin. Otherwise it is read from the file at the supplied path. - - -k - Allow insecure SSL connections to access jobfile. - ` return strings.TrimSpace(helpText) } @@ -40,10 +28,7 @@ func (c *ValidateCommand) Synopsis() string { } func (c *ValidateCommand) Run(args []string) int { - var insecure bool - flags := c.Meta.FlagSet("validate", FlagSetNone) - flags.BoolVar(&insecure, "k", false, "") flags.Usage = func() { c.Ui.Output(c.Help()) } if err := flags.Parse(args); err != nil { return 1 @@ -56,66 +41,10 @@ func (c *ValidateCommand) Run(args []string) int { return 1 } - // Read the Jobfile - var path, url string - // If prefix has http(s)://, read the Jobfile from the URL - if strings.Index(args[0], "http://") == 0 || strings.Index(args[0], "https://") == 0 { - path = "_url" - url = args[0] - } else { - // Read the Jobfile - path = args[0] - } - - var f io.Reader - switch path { - case "-": - if c.testStdin != nil { - f = c.testStdin - } else { - f = os.Stdin - } - path = "stdin" - case "_url": - if len(url) == 0 { - c.Ui.Error(fmt.Sprintf("Error invalid Jobfile name")) - } - var resp *http.Response - var err error - if insecure == true { - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - client := &http.Client{Transport: tr} - resp, err = client.Get(url) - } else { - resp, err = http.Get(url) - } - if err != nil { - c.Ui.Error(fmt.Sprintf("Error accessing URL %s: %v", url, err)) - return 1 - } - defer resp.Body.Close() - if resp.StatusCode != 200 { - c.Ui.Error(fmt.Sprintf("Error reading URL (%d) : %s", resp.StatusCode, resp.Status)) - return 1 - } - f = resp.Body - path = url - default: - file, err := os.Open(path) - defer file.Close() - if err != nil { - c.Ui.Error(fmt.Sprintf("Error opening file %q: %v", path, err)) - return 1 - } - f = file - } - - // Parse the JobFile - job, err := jobspec.Parse(f) + // Get Job struct from Jobfile + job, err := c.Helper.StructJob(args[0]) if err != nil { - c.Ui.Error(fmt.Sprintf("Error parsing job file from %s: %v", path, err)) + c.Ui.Error(fmt.Sprintf("Error getting job struct: %s", err)) return 1 } diff --git a/command/validate_test.go b/command/validate_test.go index 05f445d6d..781090b7d 100644 --- a/command/validate_test.go +++ b/command/validate_test.go @@ -111,8 +111,8 @@ func TestValidateCommand_From_STDIN(t *testing.T) { ui := new(cli.MockUi) cmd := &ValidateCommand{ - Meta: Meta{Ui: ui}, - testStdin: stdinR, + Meta: Meta{Ui: ui}, + Helper: Helper{testStdin: stdinR}, } go func() {