diff --git a/cmd/main_test.go b/cmd/main_test.go index cbdc1a7..c8386d4 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -7,14 +7,16 @@ import ( "testing" "time" + "github.com/spf13/viper" "github.com/stretchr/testify/assert" ) func TestOutput(t *testing.T) { - outCmd := RootCmd() - outCmd.Flags().Set("config", "../etc/job.yaml") - outCmd.Flags().Set("output", "true") - assert.Nil(t, Main(outCmd, []string{})) + cmd := RootCmd() + viper.BindPFlags(cmd.Flags()) + cmd.Flags().Set("config", "../etc/job.yaml") + cmd.Flags().Set("output", "true") + assert.Nil(t, Main(cmd, []string{})) } func TestMain(t *testing.T) { @@ -53,26 +55,30 @@ func TestMain(t *testing.T) { ) defer ts.Close() - mainCmd := RootCmd() - mainCmd.Flags().Set("config", "../etc/job.yaml") - mainCmd.Flags().Set("report", "true") - assert.Nil(t, Main(mainCmd, []string{})) + cmd := RootCmd() + viper.BindPFlags(cmd.Flags()) + cmd.Flags().Set("config", "../etc/job.yaml") + cmd.Flags().Set("report", "true") + assert.Nil(t, Main(cmd, []string{})) } func TestVersion(t *testing.T) { - verCmd := RootCmd() - verCmd.Flags().Set("version", "true") - assert.Nil(t, Main(verCmd, []string{})) + cmd := RootCmd() + viper.BindPFlags(cmd.Flags()) + cmd.Flags().Set("version", "true") + assert.Nil(t, Main(cmd, []string{})) } func TestReport(t *testing.T) { - rptCmd := RootCmd() - rptCmd.Flags().Set("report", "true") - assert.Nil(t, Main(rptCmd, []string{"echo", "hello"})) + cmd := RootCmd() + viper.BindPFlags(cmd.Flags()) + cmd.Flags().Set("report", "true") + assert.Nil(t, Main(cmd, []string{"echo", "hello"})) } func TestVerbose(t *testing.T) { - verbCmd := RootCmd() - verbCmd.Flags().Set("verbose", "true") - assert.Nil(t, Main(verbCmd, []string{"echox", "hello"})) + cmd := RootCmd() + viper.BindPFlags(cmd.Flags()) + cmd.Flags().Set("verbose", "true") + assert.Nil(t, Main(cmd, []string{"echox", "hello"})) } diff --git a/cmd/root.go b/cmd/root.go index 75de7dc..a48dc6e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -31,7 +31,7 @@ var rootCmd *cobra.Command //RootCmd new root cmd func RootCmd() *cobra.Command { - return &cobra.Command{ + cmd := &cobra.Command{ Use: "job [flags] [command args ...]", Short: "Job, make your short-term command as a long-term job", Example: ` @@ -58,6 +58,28 @@ func RootCmd() *cobra.Command { exitForErr(Main(cmd, args)) }, } + cmd.Flags().StringP("config", "f", "", "job config file path") + cmd.Flags().StringP("name", "N", "", "job name definition") + metadata = cmd.Flags().StringToStringP("metadata", "M", map[string]string{}, "job metadata definition") + envs = cmd.Flags().StringToStringP("cmd-env", "e", map[string]string{}, "job command environmental variables") + cmd.Flags().IntP("cmd-retry", "r", 0, "job command retry times when failed") + cmd.Flags().DurationP("cmd-timeout", "t", 0, "job command timeout duration") + cmd.Flags().BoolP("cmd-stdout-discard", "d", false, "job command stdout discard ?") + + cmd.Flags().IntP("concurrent", "c", 0, "job concurrent numbers ") + cmd.Flags().IntP("repeat-times", "n", 1, "job repeat times, 0 means forever") + cmd.Flags().DurationP("repeat-interval", "i", 0*time.Second, "job repeat interval duration") + cmd.Flags().StringP("schedule", "s", "", "job schedule in crontab format") + cmd.Flags().DurationP("timeout", "T", 0, "job timeout duration") + cmd.Flags().BoolP("guarantee", "G", false, "job guarantee mode enable ?") + cmd.Flags().BoolP("report", "R", false, "job report enable ?") + cmd.Flags().StringP("report-push-gateway", "P", "", "job report to prometheus push gateway address") + cmd.Flags().DurationP("report-push-interval", "I", 0*time.Second, "job report to prometheus push gateway interval") + cmd.Flags().BoolP("output", "o", false, "job yaml config output enable ?") + // cmd.Flags().StringP("output-command-format", "F", "shell", "job yaml config output command format ?") + cmd.Flags().BoolP("verbose", "V", false, "job verbose log enable ?") + cmd.Flags().BoolP("version", "v", false, "job version") + return cmd } // Execute adds all child commands to the root command and sets flags appropriately. @@ -70,30 +92,6 @@ func Execute() { func init() { rootCmd = RootCmd() - rootCmd.Flags().StringP("config", "f", "", "job config file path") - rootCmd.Flags().StringP("name", "N", "", "job name definition") - metadata = rootCmd.Flags().StringToStringP("metadata", "M", map[string]string{}, "job metadata definition") - envs = rootCmd.Flags().StringToStringP("cmd-env", "e", map[string]string{}, "job command environmental variables") - rootCmd.Flags().IntP("cmd-retry", "r", 0, "job command retry times when failed") - rootCmd.Flags().DurationP("cmd-timeout", "t", 0, "job command timeout duration") - rootCmd.Flags().BoolP("cmd-stdout-discard", "d", false, "job command stdout discard ?") - - rootCmd.Flags().IntP("concurrent", "c", 0, "job concurrent numbers ") - rootCmd.Flags().IntP("repeat-times", "n", 1, "job repeat times, 0 means forever") - rootCmd.Flags().DurationP("repeat-interval", "i", 0*time.Second, "job repeat interval duration") - rootCmd.Flags().StringP("schedule", "s", "", "job schedule in crontab format") - rootCmd.Flags().DurationP("timeout", "T", 0, "job timeout duration") - rootCmd.Flags().BoolP("guarantee", "G", false, "job guarantee mode enable ?") - rootCmd.Flags().BoolP("report", "R", false, "job report enable ?") - rootCmd.Flags().StringP("report-push-gateway", "P", "", "job report to prometheus push gateway address") - rootCmd.Flags().DurationP("report-push-interval", "I", 0*time.Second, "job report to prometheus push gateway interval") - rootCmd.Flags().BoolP("output", "o", false, "job yaml config output enable ?") - // rootCmd.Flags().StringP("output-command-format", "F", "shell", "job yaml config output command format ?") - rootCmd.Flags().BoolP("verbose", "V", false, "job verbose log enable ?") - rootCmd.Flags().BoolP("version", "v", false, "job version") - - // TODO support Distributed-Job - // rootCmd.Flags().StringP("host", "H", "", "dispatch JOB to the Host") viper.BindPFlags(rootCmd.Flags()) rootCmd.HelpFunc() } diff --git a/cmd/root_test.go b/cmd/root_test.go new file mode 100644 index 0000000..6869cb2 --- /dev/null +++ b/cmd/root_test.go @@ -0,0 +1,29 @@ +// Copyright © 2019 NAME HERE +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" +) + +func TestRootCmd(t *testing.T) { + cmd := RootCmd() + viper.BindPFlags(cmd.Flags()) + assert.Nil(t, cmd.Flags().Set("output", "true")) + assert.Equal(t, true, viper.GetBool("output")) +} diff --git a/config/parser_test.go b/config/parser_test.go index 315ca4e..d1179db 100644 --- a/config/parser_test.go +++ b/config/parser_test.go @@ -10,4 +10,10 @@ func TestParseJDs(t *testing.T) { jds, err := ParseJDs("../etc/job.yaml") assert.Nil(t, err) assert.Equal(t, 7, len(jds)) + + _, err = ParseJDs("../etc/noexist.yaml") + assert.NotNil(t, err) + + _, err = ParseJDs("../etc/err-yaml") + assert.NotNil(t, err) } diff --git a/etc/err-yaml b/etc/err-yaml new file mode 100644 index 0000000..51da1aa --- /dev/null +++ b/etc/err-yaml @@ -0,0 +1,21 @@ +Job: + name: "demo" + command: + shell: + name: "echo" + args: + - hello + - world + envs: + - name: "key" + value: "val" + retry: 3 + timeout: 3s + guarantee: false + crontab: "" + wait: false +-- +Job: + name: "get" + command: + retry: 3 \ No newline at end of file diff --git a/etc/http.yaml b/etc/http.yaml new file mode 100644 index 0000000..0870d9a --- /dev/null +++ b/etc/http.yaml @@ -0,0 +1,149 @@ +Job: + name: "get" + command: + retry: 3 + timeout: 3s + stdout: true + http: + request: + url: "http://localhost:8080/get" + method: GET + crontab: "" + concurrent: 1 + repeat: + times: 1 + interval: "0s" + timeout: 3s + report: false + order: + weight: 1 + precondition: + wait: false +--- +Job: + name: "json" + command: + retry: 3 + timeout: 3s + stdout: true + http: + request: + url: "http://localhost:8080/json" + method: POST + headers: + Content-Type: application/json + body: + json: + hello: "demo" + person: + name: jay + age: 39 + crontab: "" + concurrent: 1 + repeat: + times: 1 + interval: "0s" + timeout: 3s + report: false + order: + weight: 2 + precondition: + wait: false +--- +Job: + name: "text" + command: + retry: 3 + timeout: 3s + stdout: true + http: + request: + url: "http://localhost:8080/text" + method: GET + body: + text: "text" + crontab: "" + concurrent: 1 + repeat: + times: 1 + interval: "0s" + timeout: 3s + report: false + order: + weight: 3 + precondition: + wait: false +--- +Job: + name: "xml" + command: + retry: 3 + timeout: 3s + stdout: true + http: + request: + url: "http://localhost:8080/xml" + method: POST + headers: + Content-Type: application/xml + body: + json: + hello: "demo" + person: + name: jay + age: 39 + crontab: "" + concurrent: 1 + repeat: + times: 1 + interval: "0s" + timeout: 3s + report: false + order: + weight: 4 + precondition: + wait: false +--- +Job: + name: "nofound" + command: + retry: 3 + timeout: 3s + stdout: true + http: + request: + url: "http://localhost:8080/nofound" + method: GET + crontab: "" + concurrent: 1 + repeat: + times: 1 + interval: "0s" + timeout: 3s + report: false + order: + weight: 5 + precondition: + wait: false +--- +Job: + name: "timeout" + command: + retry: 3 + timeout: 3s + stdout: true + http: + request: + url: "http://localhost:8080/timeout" + method: GET + crontab: "" + concurrent: 1 + repeat: + times: 1 + interval: "0s" + timeout: 1s + report: false + order: + weight: 6 + precondition: + wait: false \ No newline at end of file diff --git a/exec/http_test.go b/exec/http_test.go index be73727..c46dacd 100644 --- a/exec/http_test.go +++ b/exec/http_test.go @@ -1,57 +1,65 @@ package exec import ( + "context" "io" "net/http" "net/http/httptest" + "strings" "testing" "time" + + "github.com/liujianping/job/config" + "github.com/stretchr/testify/assert" ) func TestHTTPCommand_Execute(t *testing.T) { ts := httptest.NewServer( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.RequestURI { - case "/head": - if r.Header.Get("X-HEAD") != "x-head-value" { - http.Error(w, "head not equal", http.StatusBadRequest) - return - } + case "/get": w.WriteHeader(http.StatusOK) io.WriteString(w, `ok`) return - case "/auth": - if user, pass, ok := r.BasicAuth(); ok { - if user == "jay" && pass == "123" { - w.WriteHeader(http.StatusOK) - io.WriteString(w, `ok`) - return - } - } - http.Error(w, "unauthorized", http.StatusUnauthorized) + case "/post": + w.WriteHeader(http.StatusOK) + io.WriteString(w, `ok`) return - case "/error": - http.Error(w, "error", http.StatusBadRequest) + case "/json": + w.WriteHeader(http.StatusOK) + io.WriteString(w, `ok`) + return + case "/text": + w.WriteHeader(http.StatusOK) + io.WriteString(w, `ok`) return - case "/sleep": - time.Sleep(time.Second) + case "/xml": w.WriteHeader(http.StatusOK) - io.WriteString(w, `sleeped`) + io.WriteString(w, `ok`) return - case "/ping": + case "/timeout": + time.Sleep(5 * time.Second) w.WriteHeader(http.StatusOK) io.WriteString(w, `ok`) return default: http.NotFound(w, r) } - if r.Header.Get("X-HEAD") != "x-head-value" { - http.Error(w, "head not equal", http.StatusBadRequest) - return - } - w.WriteHeader(http.StatusOK) - io.WriteString(w, `ok`) }), ) defer ts.Close() + + jds, err := config.ParseJDs("../etc/http.yaml") + assert.Nil(t, err) + + for _, jd := range jds { + jd.Command.HTTP.Request.URL = strings.Replace(jd.Command.HTTP.Request.URL, "http://localhost:8080", ts.URL, -1) + job := NewJob(jd, nil) + + if job.String() == "timeout" { + assert.NotNil(t, job.Execute(context.TODO()), job.String()) + } else { + assert.Nil(t, job.Execute(context.TODO()), job.String()) + } + } } diff --git a/exec/job_test.go b/exec/job_test.go index e52e3d6..7e78d3b 100644 --- a/exec/job_test.go +++ b/exec/job_test.go @@ -3,6 +3,7 @@ package exec import ( "context" "testing" + "time" "github.com/liujianping/job/config" "github.com/stretchr/testify/assert" @@ -10,20 +11,45 @@ import ( func TestJob_Execute(t *testing.T) { cmd := config.CommandJD() - cmd.Command.Shell.Name = "echo" - cmd.Command.Shell.Args = []string{"hello", "world"} + opts := []config.Option{} + opt := config.Name("name") + assert.NotNil(t, opt) + opts = append(opts, opt) + + opt = config.Metadata("key", "val") + assert.NotNil(t, opt) + opts = append(opts, opt) + + opt = config.CommandName("echo") + assert.NotNil(t, opt) + opts = append(opts, opt) + + opt = config.CommandArgs("aa", "bb", "cc") + assert.NotNil(t, opt) + opts = append(opts, opt) + + opt = config.CommandEnv("key", "val") + assert.NotNil(t, opt) + opts = append(opts, opt) + + opt = config.CommandRetry(3) + assert.NotNil(t, opt) + opts = append(opts, opt) + + opt = config.CommandStdoutDiscard(true) + assert.NotNil(t, opt) + opts = append(opts, opt) + + opt = config.CommandTimeout(time.Second) + assert.NotNil(t, opt) + opts = append(opts, opt) + + for _, o := range opts { + o(cmd) + } job := NewJob(cmd, nil) assert.NotNil(t, job) err := job.Execute(context.TODO()) assert.Nil(t, err) - - http := config.HTTPCommandJD() - http.Command.HTTP.Request.URL = "https://github.com" - http.Command.HTTP.Request.Method = "GET" - - job2 := NewJob(http, nil) - assert.NotNil(t, job2) - err2 := job2.Execute(context.TODO()) - assert.Nil(t, err2) } diff --git a/exec/reporter_test.go b/exec/reporter_test.go index ee51dad..4653f2f 100644 --- a/exec/reporter_test.go +++ b/exec/reporter_test.go @@ -1,6 +1,7 @@ package exec import ( + "context" "testing" "github.com/stretchr/testify/assert" @@ -20,5 +21,7 @@ func TestReporter_Execute(t *testing.T) { } } report.Stop() + + assert.Nil(t, report.Execute(context.TODO())) report.Finalize() }