diff --git a/Makefile b/Makefile index 77d5f95c..21192dd1 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,10 @@ build-deps: update-deps: glide update +generate: + go get github.com/go-bindata/go-bindata + $(GOPATH)/bin/go-bindata -pkg config -o config/data.go data/config_template.yaml + compile: mkdir -p out/ go build -race $(GLIDE_NOVENDOR) diff --git a/cmd/description/descriptor.go b/cmd/description/descriptor.go index d9a948cb..5649aa6f 100644 --- a/cmd/description/descriptor.go +++ b/cmd/description/descriptor.go @@ -2,13 +2,10 @@ package description import ( "fmt" - "net/http" - "github.com/fatih/color" "github.com/gojektech/proctor/daemon" "github.com/gojektech/proctor/io" "github.com/gojektech/proctor/proc" - "github.com/gojektech/proctor/proctord/utility" "github.com/spf13/cobra" ) @@ -26,12 +23,7 @@ func NewCmd(printer io.Printer, proctorEngineClient daemon.Client) *cobra.Comman procList, err := proctorEngineClient.ListProcs() if err != nil { - if err.Error() == http.StatusText(http.StatusUnauthorized) { - printer.Println(utility.UnauthorizedError, color.FgRed) - return - } - - printer.Println(utility.GenericDescribeCmdError, color.FgRed) + printer.Println(err.Error(), color.FgRed) return } diff --git a/cmd/description/descriptor_test.go b/cmd/description/descriptor_test.go index af6921c8..4b1e0ab9 100644 --- a/cmd/description/descriptor_test.go +++ b/cmd/description/descriptor_test.go @@ -3,7 +3,6 @@ package description import ( "errors" "fmt" - "net/http" "testing" "github.com/fatih/color" @@ -11,7 +10,6 @@ import ( "github.com/gojektech/proctor/io" "github.com/gojektech/proctor/proc" "github.com/gojektech/proctor/proc/env" - "github.com/gojektech/proctor/proctord/utility" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" @@ -90,8 +88,8 @@ func (s *DescribeCmdTestSuite) TestDescribeCmdForIncorrectUsage() { } func (s *DescribeCmdTestSuite) TestDescribeCmdRunProctorEngineClientFailure() { - s.mockProctorEngineClient.On("ListProcs").Return([]proc.Metadata{}, errors.New("error")).Once() - s.mockPrinter.On("Println", utility.GenericDescribeCmdError, color.FgRed).Once() + s.mockProctorEngineClient.On("ListProcs").Return([]proc.Metadata{}, errors.New("test error")).Once() + s.mockPrinter.On("Println", "test error", color.FgRed).Once() s.testDescribeCmd.Run(&cobra.Command{}, []string{"any-proc"}) @@ -109,16 +107,6 @@ func (s *DescribeCmdTestSuite) TestDescribeCmdRunProcNotSupported() { s.mockPrinter.AssertExpectations(s.T()) } -func (s *DescribeCmdTestSuite) TestDescribeCmdRunProcForUnauthorizedUser() { - s.mockProctorEngineClient.On("ListProcs").Return([]proc.Metadata{}, errors.New(http.StatusText(http.StatusUnauthorized))).Once() - s.mockPrinter.On("Println", utility.UnauthorizedError, color.FgRed).Once() - - s.testDescribeCmd.Run(&cobra.Command{}, []string{"any-proc"}) - - s.mockProctorEngineClient.AssertExpectations(s.T()) - s.mockPrinter.AssertExpectations(s.T()) -} - func TestDescribeCmdTestSuite(t *testing.T) { suite.Run(t, new(DescribeCmdTestSuite)) } diff --git a/cmd/execution/executioner.go b/cmd/execution/executioner.go index a521ce2c..b23ad1d3 100644 --- a/cmd/execution/executioner.go +++ b/cmd/execution/executioner.go @@ -2,13 +2,11 @@ package execution import ( "fmt" - "net/http" "strings" "github.com/fatih/color" "github.com/gojektech/proctor/daemon" "github.com/gojektech/proctor/io" - "github.com/gojektech/proctor/proctord/utility" "github.com/spf13/cobra" ) @@ -50,11 +48,7 @@ func NewCmd(printer io.Printer, proctorEngineClient daemon.Client) *cobra.Comman executedProcName, err := proctorEngineClient.ExecuteProc(procName, procArgs) if err != nil { - if err.Error() == http.StatusText(http.StatusUnauthorized) { - printer.Println(utility.UnauthorizedError, color.FgRed) - return - } - printer.Println(utility.GenericProcCmdError, color.FgRed) + printer.Println(err.Error(), color.FgRed) return } diff --git a/cmd/execution/executioner_test.go b/cmd/execution/executioner_test.go index 9878fe0f..b1584750 100644 --- a/cmd/execution/executioner_test.go +++ b/cmd/execution/executioner_test.go @@ -3,13 +3,11 @@ package execution import ( "errors" "fmt" - "net/http" "testing" "github.com/fatih/color" "github.com/gojektech/proctor/daemon" "github.com/gojektech/proctor/io" - "github.com/gojektech/proctor/proctord/utility" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" @@ -118,26 +116,9 @@ func (s *ExecutionCmdTestSuite) TestExecutionCmdForProctorEngineExecutionFailure s.mockPrinter.On("Println", "With No Variables", color.FgRed).Once() procArgs := make(map[string]string) - s.mockProctorEngineClient.On("ExecuteProc", "say-hello-world", procArgs).Return("", errors.New("error")).Once() + s.mockProctorEngineClient.On("ExecuteProc", "say-hello-world", procArgs).Return("", errors.New("test error")).Once() - s.mockPrinter.On("Println", utility.GenericProcCmdError, color.FgRed).Once() - - s.testExecutionCmd.Run(&cobra.Command{}, args) - - s.mockProctorEngineClient.AssertExpectations(s.T()) - s.mockPrinter.AssertExpectations(s.T()) -} - -func (s *ExecutionCmdTestSuite) TestExecutionCmdForProctorEngineExecutionForUnauthorizedUser() { - args := []string{"say-hello-world"} - - s.mockPrinter.On("Println", fmt.Sprintf("%-40s %-100s", "Executing Proc", "say-hello-world"), color.Reset).Once() - s.mockPrinter.On("Println", "With No Variables", color.FgRed).Once() - - procArgs := make(map[string]string) - s.mockProctorEngineClient.On("ExecuteProc", "say-hello-world", procArgs).Return("", errors.New(http.StatusText(http.StatusUnauthorized))).Once() - - s.mockPrinter.On("Println", utility.UnauthorizedError, color.FgRed).Once() + s.mockPrinter.On("Println", "test error", color.FgRed).Once() s.testExecutionCmd.Run(&cobra.Command{}, args) diff --git a/cmd/list/lister.go b/cmd/list/lister.go index 8e67c59b..e9f05620 100644 --- a/cmd/list/lister.go +++ b/cmd/list/lister.go @@ -2,12 +2,9 @@ package list import ( "fmt" - "net/http" - "github.com/fatih/color" "github.com/gojektech/proctor/daemon" "github.com/gojektech/proctor/io" - "github.com/gojektech/proctor/proctord/utility" "github.com/spf13/cobra" ) @@ -20,12 +17,7 @@ func NewCmd(printer io.Printer, proctorEngineClient daemon.Client) *cobra.Comman Run: func(cmd *cobra.Command, args []string) { procList, err := proctorEngineClient.ListProcs() if err != nil { - if err.Error() == http.StatusText(http.StatusUnauthorized) { - printer.Println(utility.UnauthorizedError, color.FgRed) - return - } - - printer.Println(utility.GenericListCmdError, color.FgRed) + printer.Println(err.Error(), color.FgRed) return } diff --git a/cmd/list/lister_test.go b/cmd/list/lister_test.go index 5ba9495d..40a970c7 100644 --- a/cmd/list/lister_test.go +++ b/cmd/list/lister_test.go @@ -3,14 +3,12 @@ package list import ( "errors" "fmt" - "net/http" "testing" "github.com/fatih/color" "github.com/gojektech/proctor/daemon" "github.com/gojektech/proctor/io" "github.com/gojektech/proctor/proc" - "github.com/gojektech/proctor/proctord/utility" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" @@ -64,18 +62,8 @@ func (s *ListCmdTestSuite) TestListCmdRun() { } func (s *ListCmdTestSuite) TestListCmdRunProctorEngineClientFailure() { - s.mockProctorEngineClient.On("ListProcs").Return([]proc.Metadata{}, errors.New("error")).Once() - s.mockPrinter.On("Println", utility.GenericListCmdError, color.FgRed).Once() - - s.testListCmd.Run(&cobra.Command{}, []string{}) - - s.mockProctorEngineClient.AssertExpectations(s.T()) - s.mockPrinter.AssertExpectations(s.T()) -} - -func (s *ListCmdTestSuite) TestListCmdRunProctorEngineClientForUnauthorizedUser() { - s.mockProctorEngineClient.On("ListProcs").Return([]proc.Metadata{}, errors.New(http.StatusText(http.StatusUnauthorized))).Once() - s.mockPrinter.On("Println", utility.UnauthorizedError, color.FgRed).Once() + s.mockProctorEngineClient.On("ListProcs").Return([]proc.Metadata{}, errors.New("Error!!!\nUnknown Error.")).Once() + s.mockPrinter.On("Println", "Error!!!\nUnknown Error.", color.FgRed).Once() s.testListCmd.Run(&cobra.Command{}, []string{}) diff --git a/cmd/root.go b/cmd/root.go index 0c36dee4..1e4db2c4 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -9,7 +9,6 @@ import ( "github.com/gojektech/proctor/cmd/list" "github.com/gojektech/proctor/cmd/procs" "github.com/gojektech/proctor/cmd/version" - "github.com/gojektech/proctor/config" "github.com/gojektech/proctor/daemon" "github.com/gojektech/proctor/io" @@ -25,8 +24,6 @@ var ( ) func Execute(printer io.Printer, proctorEngineClient daemon.Client) { - cobra.OnInitialize(config.InitConfig) - versionCmd := version.NewCmd(printer) rootCmd.AddCommand(versionCmd) diff --git a/config/config.go b/config/config.go index 76d72578..d7bfa715 100644 --- a/config/config.go +++ b/config/config.go @@ -2,47 +2,76 @@ package config import ( "fmt" + "github.com/gojektech/proctor/proctord/utility" + "github.com/pkg/errors" "os" + "time" "github.com/spf13/viper" ) -func InitConfig() { - viper.SetConfigType("yaml") - viper.AutomaticEnv() - var configFileDir string +const ( + Environment = "ENVIRONMENT" + ProctorHost = "PROCTOR_HOST" + EmailId = "EMAIL_ID" + AccessToken = "ACCESS_TOKEN" + ConnectionTimeoutSecs = "CONNECTION_TIMEOUT_SECS" +) - if viper.GetString("ENVIRONMENT") == "test" { - configFileDir = "/tmp" - } else { - configFileDir = "$HOME/.proctor" - } +type ProctorConfig struct { + Host string + Email string + AccessToken string + ConnectionTimeoutSecs time.Duration +} + +type ConfigError struct { + error + Message string +} + +func (c *ConfigError) RootError() error { + return c.error +} - viper.AddConfigPath(configFileDir) +func LoadConfig() (ProctorConfig, ConfigError) { + viper.SetDefault(ConnectionTimeoutSecs, 10) + viper.AutomaticEnv() + + viper.AddConfigPath(ConfigFileDir()) viper.SetConfigName("proctor") + viper.SetConfigType("yaml") err := viper.ReadInConfig() if err != nil { - fmt.Println("Error reading proctor config") - os.Exit(1) + configFileUsed := viper.ConfigFileUsed() + message := "" + if _, err := os.Stat(configFileUsed); os.IsNotExist(err) { + bytes, _ := dataConfig_templateYamlBytes() + template := string(bytes) + message = fmt.Sprintf("Config file not found in %s/proctor.yaml\n", ConfigFileDir()) + message += fmt.Sprintf("Create a config file with template:\n\n%s\n", template) + } + return ProctorConfig{}, ConfigError{error: err, Message: message} } -} -func ProctorHost() string { - InitConfig() - proctorHost := viper.GetString("PROCTOR_HOST") - return proctorHost -} - -func EmailId() string { - InitConfig() - emailId := viper.GetString("EMAIL_ID") - return emailId + proctorHost := viper.GetString(ProctorHost) + if proctorHost == "" { + return ProctorConfig{}, ConfigError{error: errors.New("Mandatory Config Missing"), Message: utility.ConfigProctorHostMissingError} + } + emailId := viper.GetString(EmailId) + accessToken := viper.GetString(AccessToken) + connectionTimeout := time.Duration(viper.GetInt(ConnectionTimeoutSecs)) * time.Second + return ProctorConfig{Host: proctorHost, Email: emailId, AccessToken: accessToken, ConnectionTimeoutSecs: connectionTimeout}, ConfigError{} } -func AccessToken() string { - InitConfig() - accessToken := viper.GetString("ACCESS_TOKEN") - return accessToken +// Returns Config file directory +// This allows to test on dev environment without conflicting with installed proctor config file +func ConfigFileDir() string { + if os.Getenv(Environment) == "test" { + return "/tmp" + } else { + return fmt.Sprintf("%s/.proctor", os.Getenv("HOME")) + } } diff --git a/config/config_test.go b/config/config_test.go index e178d1f0..2762cc48 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -1,50 +1,111 @@ -package config_test +package config import ( + "fmt" "io/ioutil" "os" "testing" + "time" - "github.com/gojektech/proctor/config" "github.com/stretchr/testify/assert" ) -func TestProctorHost(t *testing.T) { - proctorConfigFilePath := "/tmp/proctor.yaml" - proctorHost := []byte("PROCTOR_HOST: any-random-host.com") - err := ioutil.WriteFile(proctorConfigFilePath, proctorHost, 0644) - defer os.Remove(proctorConfigFilePath) - assert.NoError(t, err) +func setUp() { + os.Setenv(Environment, "test") +} + +func TestReturnsConfigDirAsTmpIfEnvironmentIsTest(t *testing.T) { + os.Setenv(Environment, "test") + dir := ConfigFileDir() + assert.Equal(t, "/tmp", dir) +} - config.InitConfig() - configuredProctorHost := config.ProctorHost() +func TestReturnsConfigDirAsHomeDotProctorIfEnvironmentIsNotSet(t *testing.T) { + os.Unsetenv(Environment) - assert.Equal(t, "any-random-host.com", configuredProctorHost) + dir := ConfigFileDir() + expectedDir := fmt.Sprintf("%s/.proctor", os.Getenv("HOME")) + assert.Equal(t, expectedDir, dir) } -func TestProctorEmailId(t *testing.T) { - proctorConfigFilePath := "/tmp/proctor.yaml" - EmailId := []byte("EMAIL_ID: foobar@gmail.com") - err := ioutil.WriteFile(proctorConfigFilePath, EmailId, 0644) - defer os.Remove(proctorConfigFilePath) - assert.NoError(t, err) +func TestLoadConfigsFromEnvironmentVariables(t *testing.T) { + setUp() + proctorHost := "test.example.com" + email := "user@example.com" + accessToken := "test-token" + os.Setenv(ProctorHost, proctorHost) + os.Setenv(EmailId, email) + os.Setenv(AccessToken, accessToken) + os.Setenv(ConnectionTimeoutSecs, "20") + configFilePath := createProctorConfigFile(t, "") + defer os.Remove(configFilePath) - config.InitConfig() - configuredEmailId := config.EmailId() + proctorConfig, err := LoadConfig() - assert.Equal(t, "foobar@gmail.com", configuredEmailId) + assert.Empty(t, err) + assert.Equal(t, ProctorConfig{Host: proctorHost, Email: email, AccessToken: accessToken, ConnectionTimeoutSecs: time.Duration(20 * time.Second)}, proctorConfig) } -func TestProctorAccessToken(t *testing.T) { - proctorConfigFilePath := "/tmp/proctor.yaml" +func TestLoadConfigFromFile(t *testing.T) { + setUp() + unsetEnvs() - AccessToken := []byte("ACCESS_TOKEN: access-token") - err := ioutil.WriteFile(proctorConfigFilePath, AccessToken, 0644) - defer os.Remove(proctorConfigFilePath) - assert.NoError(t, err) + configFilePath := createProctorConfigFile(t, "PROCTOR_HOST: file.example.com\nEMAIL_ID: file@example.com\nACCESS_TOKEN: file-token\nCONNECTION_TIMEOUT_SECS: 30") + defer os.Remove(configFilePath) + + proctorConfig, err := LoadConfig() - config.InitConfig() - configuredAccessToken := config.AccessToken() + assert.Empty(t, err) + assert.Equal(t, ProctorConfig{Host: "file.example.com", Email: "file@example.com", AccessToken: "file-token", ConnectionTimeoutSecs: time.Duration(30 * time.Second)}, proctorConfig) +} + +func TestCheckForMandatoryConfig(t *testing.T) { + setUp() + unsetEnvs() + + configFilePath := createProctorConfigFile(t, "EMAIL_ID: file@example.com\nACCESS_TOKEN: file-token\nCONNECTION_TIMEOUT_SECS: 30") + defer os.Remove(configFilePath) + + _, err := LoadConfig() + + assert.Error(t, err, "Config Error!!!\nMandatory config PROCTOR_HOST is missing in Proctor Config file.") +} - assert.Equal(t, "access-token", configuredAccessToken) +func TestTakesDefaultValueForConfigs(t *testing.T) { + setUp() + unsetEnvs() + configFilePath := createProctorConfigFile(t, "PROCTOR_HOST: file.example.com\nEMAIL_ID: file@example.com\nACCESS_TOKEN: file-token") + defer os.Remove(configFilePath) + + proctorConfig, err := LoadConfig() + + assert.Empty(t, err) + assert.Equal(t, time.Duration(10*time.Second), proctorConfig.ConnectionTimeoutSecs) +} + +func TestShouldPrintInstructionsForConfigFileIfFileNotFound(t *testing.T) { + setUp() + configFilePath := fmt.Sprintf("%s/proctor.yaml", ConfigFileDir()) + os.Remove(configFilePath) + + expectedMessage := fmt.Sprintf("Config file not found in %s\nCreate a config file with template:\n\nPROCTOR_HOST: \nEMAIL_ID: \nACCESS_TOKEN: \n", configFilePath) + + _, err := LoadConfig() + + assert.Equal(t, expectedMessage, err.Message) +} + +func unsetEnvs() { + os.Unsetenv(ProctorHost) + os.Unsetenv(EmailId) + os.Unsetenv(AccessToken) + os.Unsetenv(ConnectionTimeoutSecs) +} + +func createProctorConfigFile(t *testing.T, content string) string { + fileContent := []byte(fmt.Sprintf(content)) + configFilePath := fmt.Sprintf("%s/proctor.yaml", ConfigFileDir()) + err := ioutil.WriteFile(configFilePath, fileContent, 0644) + assert.NoError(t, err) + return configFilePath } diff --git a/config/data.go b/config/data.go new file mode 100644 index 00000000..7f433388 --- /dev/null +++ b/config/data.go @@ -0,0 +1,237 @@ +// Code generated by go-bindata. +// sources: +// data/config_template.yaml +// DO NOT EDIT! + +package config + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func bindataRead(data []byte, name string) ([]byte, error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + clErr := gz.Close() + + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + if clErr != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +func (fi bindataFileInfo) Name() string { + return fi.name +} +func (fi bindataFileInfo) Size() int64 { + return fi.size +} +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} +func (fi bindataFileInfo) IsDir() bool { + return false +} +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var _dataConfig_templateYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x0a\x08\xf2\x77\x0e\xf1\x0f\x8a\xf7\xf0\x0f\x0e\xb1\x52\xb0\xc9\xc8\x2f\x2e\xb1\xe3\x72\xf5\x75\xf4\xf4\x89\xf7\x74\xb1\x52\xb0\x49\xcd\x4d\xcc\xcc\xb1\xe3\x72\x74\x76\x76\x0d\x0e\x8e\x0f\xf1\xf7\x76\xf5\xb3\x52\xb0\x49\x4c\x4e\x4e\x2d\x2e\xd6\x2d\xc9\xcf\x4e\xcd\xb3\x03\x04\x00\x00\xff\xff\x89\xcc\x27\xe3\x43\x00\x00\x00") + +func dataConfig_templateYamlBytes() ([]byte, error) { + return bindataRead( + _dataConfig_templateYaml, + "data/config_template.yaml", + ) +} + +func dataConfig_templateYaml() (*asset, error) { + bytes, err := dataConfig_templateYamlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "data/config_template.yaml", size: 67, mode: os.FileMode(420), modTime: time.Unix(1541498484, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "data/config_template.yaml": dataConfig_templateYaml, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"} +// AssetDir("data/img") would return []string{"a.png", "b.png"} +// AssetDir("foo.txt") and AssetDir("notexist") would return an error +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + cannonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(cannonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} + +var _bintree = &bintree{nil, map[string]*bintree{ + "data": &bintree{nil, map[string]*bintree{ + "config_template.yaml": &bintree{dataConfig_templateYaml, map[string]*bintree{}}, + }}, +}} + +// RestoreAsset restores an asset under the given directory +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) + if err != nil { + return err + } + return nil +} + +// RestoreAssets restores an asset under the given directory recursively +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + cannonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) +} diff --git a/daemon/client.go b/daemon/client.go index f60a4548..0eb0082b 100644 --- a/daemon/client.go +++ b/daemon/client.go @@ -3,8 +3,8 @@ package daemon import ( "bytes" "encoding/json" - "errors" "fmt" + "net" "net/http" "net/url" "os" @@ -26,9 +26,10 @@ type Client interface { } type client struct { - proctordHost string - emailId string - accessToken string + proctordHost string + emailId string + accessToken string + connectionTimeoutSecs time.Duration } type ProcToExecute struct { @@ -36,28 +37,31 @@ type ProcToExecute struct { Args map[string]string `json:"args"` } -func NewClient() Client { +func NewClient(proctorConfig config.ProctorConfig) Client { return &client{ - proctordHost: config.ProctorHost(), - emailId: config.EmailId(), - accessToken: config.AccessToken(), + proctordHost: proctorConfig.Host, + emailId: proctorConfig.Email, + accessToken: proctorConfig.AccessToken, + connectionTimeoutSecs: proctorConfig.ConnectionTimeoutSecs, } } func (c *client) ListProcs() ([]proc.Metadata, error) { - client := &http.Client{} + client := &http.Client{ + Timeout: c.connectionTimeoutSecs, + } req, err := http.NewRequest("GET", "http://"+c.proctordHost+"/jobs/metadata", nil) req.Header.Add(utility.UserEmailHeaderKey, c.emailId) req.Header.Add(utility.AccessTokenHeaderKey, c.accessToken) resp, err := client.Do(req) if err != nil { - return []proc.Metadata{}, errors.New(err.Error()) + return []proc.Metadata{}, buildNetworkError(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return []proc.Metadata{}, errors.New(http.StatusText(resp.StatusCode)) + return []proc.Metadata{}, buildHTTPError(c, resp) } var procList []proc.Metadata @@ -84,12 +88,12 @@ func (c *client) ExecuteProc(name string, args map[string]string) (string, error resp, err := client.Do(req) if err != nil { - return "", errors.New(err.Error()) + return "", buildNetworkError(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusCreated { - return "", errors.New(http.StatusText(resp.StatusCode)) + return "", buildHTTPError(c, resp) } var executedProc ProcToExecute @@ -119,7 +123,10 @@ func (c *client) StreamProcLogs(name string) error { if err != nil { animation.Stop() if response.StatusCode == http.StatusUnauthorized { - return errors.New(http.StatusText(http.StatusUnauthorized)) + if c.emailId == "" || c.accessToken == "" { + return fmt.Errorf("%s\n%s", utility.UnauthorizedErrorHeader, utility.UnauthorizedErrorMissingConfig) + } + return fmt.Errorf("%s\n%s", utility.UnauthorizedErrorHeader, utility.UnauthorizedErrorInvalidConfig) } return err } @@ -150,3 +157,21 @@ func (c *client) StreamProcLogs(name string) error { } } } + +func buildNetworkError(err error) error { + if netError, ok := err.(net.Error); ok && netError.Timeout() { + return fmt.Errorf("%s\n%s\n%s", utility.GenericTimeoutErrorHeader, netError.Error(), utility.GenericTimeoutErrorBody) + } + return fmt.Errorf("%s\n%s", utility.GenericNetworkErrorHeader, err.Error()) +} + +func buildHTTPError(c *client, resp *http.Response) error { + if resp.StatusCode == http.StatusUnauthorized { + if c.emailId == "" || c.accessToken == "" { + return fmt.Errorf("%s\n%s", utility.UnauthorizedErrorHeader, utility.UnauthorizedErrorMissingConfig) + } + return fmt.Errorf("%s\n%s", utility.UnauthorizedErrorHeader, utility.UnauthorizedErrorInvalidConfig) + } else { + return fmt.Errorf("%s\nStatus Code: %d, %s", utility.GenericResponseErrorHeader, resp.StatusCode, http.StatusText(resp.StatusCode)) + } +} diff --git a/daemon/client_test.go b/daemon/client_test.go index 9837bdb7..cc575285 100644 --- a/daemon/client_test.go +++ b/daemon/client_test.go @@ -2,11 +2,8 @@ package daemon import ( "errors" - "fmt" - "io/ioutil" "net/http" "net/http/httptest" - "os" "strings" "testing" @@ -21,17 +18,9 @@ import ( "github.com/stretchr/testify/assert" ) -func TestListProcs(t *testing.T) { - proctorConfigFilePath := "/tmp/proctor.yaml" - proctorConfig := []byte("PROCTOR_HOST: proctor.example.com\nACCESS_TOKEN: access-token\nEMAIL_ID: proctor@example.com") - err := ioutil.WriteFile(proctorConfigFilePath, proctorConfig, 0644) - defer os.Remove(proctorConfigFilePath) - fmt.Println(err) - - assert.NoError(t, err) - config.InitConfig() - - proctorClient := NewClient() +func TestListProcsReturnsListOfProcsWithDetails(t *testing.T) { + proctorConfig := config.ProctorConfig{Host: "proctor.example.com", Email: "proctor@example.com", AccessToken: "access-token"} + proctorClient := NewClient(proctorConfig) httpmock.Activate() defer httpmock.DeactivateAndReset() @@ -45,7 +34,7 @@ func TestListProcs(t *testing.T) { httpmock.RegisterStubRequest( httpmock.NewStubRequest( "GET", - "http://"+config.ProctorHost()+"/jobs/metadata", + "http://"+proctorConfig.Host+"/jobs/metadata", func(req *http.Request) (*http.Response, error) { return httpmock.NewStringResponse(200, body), nil }, @@ -63,25 +52,17 @@ func TestListProcs(t *testing.T) { assert.Equal(t, procListExpected, procList) } -func TestListProcsReturnInternalServerError(t *testing.T) { - proctorConfigFilePath := "/tmp/proctor.yaml" - proctorConfig := []byte("PROCTOR_HOST: proctor.example.com\nACCESS_TOKEN: access-token\nEMAIL_ID: proctor@example.com") - err := ioutil.WriteFile(proctorConfigFilePath, proctorConfig, 0644) - defer os.Remove(proctorConfigFilePath) - assert.NoError(t, err) - config.InitConfig() - - proctorClient := NewClient() +func TestListProcsReturnErrorFromResponseBody(t *testing.T) { + proctorConfig := config.ProctorConfig{Host: "proctor.example.com", Email: "proctor@example.com", AccessToken: "access-token"} + proctorClient := NewClient(proctorConfig) httpmock.Activate() defer httpmock.DeactivateAndReset() - var procListExpected = []proc.Metadata{} - httpmock.RegisterStubRequest( httpmock.NewStubRequest( "GET", - "http://"+config.ProctorHost()+"/jobs/metadata", + "http://"+proctorConfig.Host+"/jobs/metadata", func(req *http.Request) (*http.Response, error) { return httpmock.NewStringResponse(500, `{}`), nil }, @@ -95,32 +76,52 @@ func TestListProcsReturnInternalServerError(t *testing.T) { procList, err := proctorClient.ListProcs() - assert.Equal(t, procListExpected, procList) + assert.Equal(t, []proc.Metadata{}, procList) assert.Error(t, err) + assert.Equal(t, "Server Error!!!\nStatus Code: 500, Internal Server Error", err.Error()) } -func TestListProcsReturnClientSideConnectionError(t *testing.T) { - proctorConfigFilePath := "/tmp/proctor.yaml" - proctorConfig := []byte("PROCTOR_HOST: proctor.example.com\nACCESS_TOKEN: access-token\nEMAIL_ID: proctor@example.com") - err := ioutil.WriteFile(proctorConfigFilePath, proctorConfig, 0644) - connectionTimeOut := "Connection TimeOut" - defer os.Remove(proctorConfigFilePath) - assert.NoError(t, err) - config.InitConfig() - - proctorClient := NewClient() +func TestListProcsReturnClientSideTimeoutError(t *testing.T) { + proctorConfig := config.ProctorConfig{Host: "proctor.example.com", Email: "proctor@example.com", AccessToken: "access-token"} + proctorClient := NewClient(proctorConfig) httpmock.Activate() defer httpmock.DeactivateAndReset() - var procListExpected = []proc.Metadata{} + httpmock.RegisterStubRequest( + httpmock.NewStubRequest( + "GET", + "http://"+proctorConfig.Host+"/jobs/metadata", + func(req *http.Request) (*http.Response, error) { + return nil, TestConnectionError{message: "Unable to reach http://proctor.example.com/", timeout: true} + }, + ).WithHeader( + &http.Header{ + utility.UserEmailHeaderKey: []string{"proctor@example.com"}, + utility.AccessTokenHeaderKey: []string{"access-token"}, + }, + ), + ) + + procList, err := proctorClient.ListProcs() + + assert.Equal(t, errors.New("Connection Timeout!!!\nGet http://proctor.example.com/jobs/metadata: Unable to reach http://proctor.example.com/\nPlease check your Internet/VPN connection for connectivity to ProctorD."), err) + assert.Equal(t, []proc.Metadata{}, procList) +} + +func TestListProcsReturnClientSideConnectionError(t *testing.T) { + proctorConfig := config.ProctorConfig{Host: "proctor.example.com", Email: "proctor@example.com", AccessToken: "access-token"} + proctorClient := NewClient(proctorConfig) + + httpmock.Activate() + defer httpmock.DeactivateAndReset() httpmock.RegisterStubRequest( httpmock.NewStubRequest( "GET", - "http://"+config.ProctorHost()+"/jobs/metadata", + "http://"+proctorConfig.Host+"/jobs/metadata", func(req *http.Request) (*http.Response, error) { - return nil, errors.New(connectionTimeOut) + return nil, TestConnectionError{message: "Unknown Error", timeout: false} }, ).WithHeader( &http.Header{ @@ -132,60 +133,71 @@ func TestListProcsReturnClientSideConnectionError(t *testing.T) { procList, err := proctorClient.ListProcs() - assert.Equal(t, errors.New("Get http://proctor.example.com/jobs/metadata: Connection TimeOut"), err) - assert.Equal(t, procListExpected, procList) + assert.Equal(t, errors.New("Network Error!!!\nGet http://proctor.example.com/jobs/metadata: Unknown Error"), err) + assert.Equal(t, []proc.Metadata{}, procList) } func TestListProcsForUnauthorizedUser(t *testing.T) { - proctorConfigFilePath := "/tmp/proctor.yaml" - proctorConfig := []byte("PROCTOR_HOST: proctor.example.com\nACCESS_TOKEN: access-token\nEMAIL_ID: proctor@example.com") - err := ioutil.WriteFile(proctorConfigFilePath, proctorConfig, 0644) - defer os.Remove(proctorConfigFilePath) - assert.NoError(t, err) - config.InitConfig() + proctorConfig := config.ProctorConfig{Host: "proctor.example.com", Email: "proctor@example.com", AccessToken: "access-token"} + proctorClient := NewClient(proctorConfig) + httpmock.Activate() + defer httpmock.DeactivateAndReset() + + httpmock.RegisterStubRequest( + httpmock.NewStubRequest( + "GET", + "http://"+proctorConfig.Host+"/jobs/metadata", + func(req *http.Request) (*http.Response, error) { + return httpmock.NewStringResponse(401, `{}`), nil + }, + ).WithHeader( + &http.Header{ + utility.UserEmailHeaderKey: []string{"proctor@example.com"}, + utility.AccessTokenHeaderKey: []string{"access-token"}, + }, + ), + ) + + procList, err := proctorClient.ListProcs() - proctorClient := NewClient() + assert.Equal(t, []proc.Metadata{}, procList) + assert.Equal(t, "Unauthorized Access!!!\nPlease check the EMAIL_ID and ACCESS_TOKEN validity in proctor config file.", err.Error()) +} +func TestListProcsForUnauthorizedErrorWithConfigMissing(t *testing.T) { + proctorConfig := config.ProctorConfig{Host: "proctor.example.com", Email: "proctor@example.com", AccessToken: ""} + proctorClient := NewClient(proctorConfig) httpmock.Activate() defer httpmock.DeactivateAndReset() - var procListExpected = []proc.Metadata{} - httpmock.RegisterStubRequest( httpmock.NewStubRequest( "GET", - "http://"+config.ProctorHost()+"/jobs/metadata", + "http://"+proctorConfig.Host+"/jobs/metadata", func(req *http.Request) (*http.Response, error) { return httpmock.NewStringResponse(401, `{}`), nil }, ).WithHeader( &http.Header{ utility.UserEmailHeaderKey: []string{"proctor@example.com"}, - utility.AccessTokenHeaderKey: []string{"access-token"}, + utility.AccessTokenHeaderKey: []string{""}, }, ), ) procList, err := proctorClient.ListProcs() - assert.Equal(t, procListExpected, procList) - assert.Equal(t, err.Error(), http.StatusText(http.StatusUnauthorized)) + assert.Equal(t, []proc.Metadata{}, procList) + assert.Equal(t, "Unauthorized Access!!!\nEMAIL_ID or ACCESS_TOKEN is not present in proctor config file.", err.Error()) } func TestExecuteProc(t *testing.T) { - proctorConfigFilePath := "/tmp/proctor.yaml" - proctorConfig := []byte("PROCTOR_HOST: proctor.example.com\nACCESS_TOKEN: access-token\nEMAIL_ID: proctor@example.com") + proctorConfig := config.ProctorConfig{Host: "proctor.example.com", Email: "proctor@example.com", AccessToken: "access-token"} + proctorClient := NewClient(proctorConfig) expectedProcResponse := "proctor-777b1dfb-ea27-46d9-b02c-839b75a542e2" body := `{ "name": "proctor-777b1dfb-ea27-46d9-b02c-839b75a542e2"}` procName := "run-sample" procArgs := map[string]string{"SAMPLE_ARG1": "sample-value"} - err := ioutil.WriteFile(proctorConfigFilePath, proctorConfig, 0644) - defer os.Remove(proctorConfigFilePath) - - assert.NoError(t, err) - config.InitConfig() - - proctorClient := NewClient() httpmock.Activate() defer httpmock.DeactivateAndReset() @@ -193,7 +205,7 @@ func TestExecuteProc(t *testing.T) { httpmock.RegisterStubRequest( httpmock.NewStubRequest( "POST", - "http://"+config.ProctorHost()+"/jobs/execute", + "http://"+proctorConfig.Host+"/jobs/execute", func(req *http.Request) (*http.Response, error) { return httpmock.NewStringResponse(201, body), nil }, @@ -212,17 +224,11 @@ func TestExecuteProc(t *testing.T) { } func TestExecuteProcInternalServerError(t *testing.T) { - proctorConfigFilePath := "/tmp/proctor.yaml" - proctorConfig := []byte("PROCTOR_HOST: proctor.example.com\nACCESS_TOKEN: access-token\nEMAIL_ID: proctor@example.com") + proctorConfig := config.ProctorConfig{Host: "proctor.example.com", Email: "proctor@example.com", AccessToken: "access-token"} + proctorClient := NewClient(proctorConfig) expectedProcResponse := "" procName := "run-sample" procArgs := map[string]string{"SAMPLE_ARG1": "sample-value"} - err := ioutil.WriteFile(proctorConfigFilePath, proctorConfig, 0644) - defer os.Remove(proctorConfigFilePath) - assert.NoError(t, err) - config.InitConfig() - - proctorClient := NewClient() httpmock.Activate() defer httpmock.DeactivateAndReset() @@ -230,7 +236,7 @@ func TestExecuteProcInternalServerError(t *testing.T) { httpmock.RegisterStubRequest( httpmock.NewStubRequest( "POST", - "http://"+config.ProctorHost()+"/jobs/execute", + "http://"+proctorConfig.Host+"/jobs/execute", func(req *http.Request) (*http.Response, error) { return httpmock.NewStringResponse(500, ""), nil }, @@ -244,22 +250,41 @@ func TestExecuteProcInternalServerError(t *testing.T) { executeProcResponse, err := proctorClient.ExecuteProc(procName, procArgs) - assert.Error(t, err) + assert.Equal(t, "Server Error!!!\nStatus Code: 500, Internal Server Error", err.Error()) assert.Equal(t, expectedProcResponse, executeProcResponse) } func TestExecuteProcUnAuthorized(t *testing.T) { - proctorConfigFilePath := "/tmp/proctor.yaml" - proctorConfig := []byte("PROCTOR_HOST: proctor.example.com\nACCESS_TOKEN: access-token\nEMAIL_ID: proctor@example.com") - expectedProcResponse := "" - procName := "run-sample" - procArgs := map[string]string{"SAMPLE_ARG1": "sample-value"} - err := ioutil.WriteFile(proctorConfigFilePath, proctorConfig, 0644) - defer os.Remove(proctorConfigFilePath) - assert.NoError(t, err) - config.InitConfig() + proctorConfig := config.ProctorConfig{Host: "proctor.example.com", Email: "proctor@example.com", AccessToken: "access-token"} + proctorClient := NewClient(proctorConfig) - proctorClient := NewClient() + httpmock.Activate() + defer httpmock.DeactivateAndReset() + + httpmock.RegisterStubRequest( + httpmock.NewStubRequest( + "POST", + "http://"+proctorConfig.Host+"/jobs/execute", + func(req *http.Request) (*http.Response, error) { + return httpmock.NewStringResponse(401, ""), nil + }, + ).WithHeader( + &http.Header{ + utility.UserEmailHeaderKey: []string{"proctor@example.com"}, + utility.AccessTokenHeaderKey: []string{"access-token"}, + }, + ), + ) + + executeProcResponse, err := proctorClient.ExecuteProc("run-sample", map[string]string{"SAMPLE_ARG1": "sample-value"}) + + assert.Equal(t, "", executeProcResponse) + assert.Equal(t, "Unauthorized Access!!!\nPlease check the EMAIL_ID and ACCESS_TOKEN validity in proctor config file.", err.Error()) +} + +func TestExecuteProcUnAuthorizedWhenEmailAndAccessTokenNotSet(t *testing.T) { + proctorConfig := config.ProctorConfig{Host: "proctor.example.com"} + proctorClient := NewClient(proctorConfig) httpmock.Activate() defer httpmock.DeactivateAndReset() @@ -267,10 +292,38 @@ func TestExecuteProcUnAuthorized(t *testing.T) { httpmock.RegisterStubRequest( httpmock.NewStubRequest( "POST", - "http://"+config.ProctorHost()+"/jobs/execute", + "http://"+proctorConfig.Host+"/jobs/execute", func(req *http.Request) (*http.Response, error) { return httpmock.NewStringResponse(401, ""), nil }, + ).WithHeader( + &http.Header{ + utility.UserEmailHeaderKey: []string{""}, + utility.AccessTokenHeaderKey: []string{""}, + }, + ), + ) + + executeProcResponse, err := proctorClient.ExecuteProc("run-sample", map[string]string{"SAMPLE_ARG1": "sample-value"}) + + assert.Equal(t, "", executeProcResponse) + assert.Equal(t, "Unauthorized Access!!!\nEMAIL_ID or ACCESS_TOKEN is not present in proctor config file.", err.Error()) +} + +func TestExecuteProcsReturnClientSideConnectionError(t *testing.T) { + proctorConfig := config.ProctorConfig{Host: "proctor.example.com", Email: "proctor@example.com", AccessToken: "access-token"} + proctorClient := NewClient(proctorConfig) + + httpmock.Activate() + defer httpmock.DeactivateAndReset() + + httpmock.RegisterStubRequest( + httpmock.NewStubRequest( + "POST", + "http://"+proctorConfig.Host+"/jobs/execute", + func(req *http.Request) (*http.Response, error) { + return nil, TestConnectionError{message: "Unknown Error", timeout: false} + }, ).WithHeader( &http.Header{ utility.UserEmailHeaderKey: []string{"proctor@example.com"}, @@ -279,14 +332,13 @@ func TestExecuteProcUnAuthorized(t *testing.T) { ), ) - executeProcResponse, err := proctorClient.ExecuteProc(procName, procArgs) + response, err := proctorClient.ExecuteProc("run-sample", map[string]string{"SAMPLE_ARG1": "sample-value"}) - assert.Equal(t, expectedProcResponse, executeProcResponse) - assert.Error(t, errors.New(http.StatusText(http.StatusUnauthorized)), err) + assert.Equal(t, "", response) + assert.Equal(t, errors.New("Network Error!!!\nPost http://proctor.example.com/jobs/execute: Unknown Error"), err) } func TestLogStreamForAuthorizedUser(t *testing.T) { - proctorConfigFilePath := "/tmp/proctor.yaml" logStreamAuthorizer := func(t *testing.T) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { upgrader := websocket.Upgrader{} @@ -298,39 +350,27 @@ func TestLogStreamForAuthorizedUser(t *testing.T) { } testServer := httptest.NewServer(logStreamAuthorizer(t)) defer testServer.Close() + proctorConfig := config.ProctorConfig{Host: makeHostname(testServer.URL), Email: "proctor@example.com", AccessToken: "access-token"} + proctorClient := NewClient(proctorConfig) - proctorConfig := []byte(fmt.Sprintf("PROCTOR_HOST: %s\nACCESS_TOKEN: access-token\nEMAIL_ID: proctor@example.com", makeHostname(testServer.URL))) - err := ioutil.WriteFile(proctorConfigFilePath, proctorConfig, 0644) - defer os.Remove(proctorConfigFilePath) - assert.NoError(t, err) - config.InitConfig() - - proctorClient := NewClient() - err = proctorClient.StreamProcLogs("test-job-id") + err := proctorClient.StreamProcLogs("test-job-id") assert.NoError(t, err) } func TestLogStreamForBadWebSocketHandshake(t *testing.T) { - proctorConfigFilePath := "/tmp/proctor.yaml" badWebSocketHandshakeHandler := func() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) {} } testServer := httptest.NewServer(badWebSocketHandshakeHandler()) defer testServer.Close() + proctorConfig := config.ProctorConfig{Host: makeHostname(testServer.URL), Email: "proctor@example.com", AccessToken: "access-token"} + proctorClient := NewClient(proctorConfig) - proctorConfig := []byte(fmt.Sprintf("PROCTOR_HOST: %s\nACCESS_TOKEN: access-token\nEMAIL_ID: proctor@example.com", makeHostname(testServer.URL))) - err := ioutil.WriteFile(proctorConfigFilePath, proctorConfig, 0644) - defer os.Remove(proctorConfigFilePath) - assert.NoError(t, err) - config.InitConfig() - - proctorClient := NewClient() errStreamLogs := proctorClient.StreamProcLogs("test-job-id") assert.Equal(t, errors.New("websocket: bad handshake"), errStreamLogs) } func TestLogStreamForUnauthorizedUser(t *testing.T) { - proctorConfigFilePath := "/tmp/proctor.yaml" unauthorizedUserHandler := func() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusUnauthorized) @@ -338,14 +378,9 @@ func TestLogStreamForUnauthorizedUser(t *testing.T) { } testServer := httptest.NewServer(unauthorizedUserHandler()) defer testServer.Close() + proctorConfig := config.ProctorConfig{Host: makeHostname(testServer.URL), Email: "proctor@example.com", AccessToken: "access-token"} + proctorClient := NewClient(proctorConfig) - proctorConfig := []byte(fmt.Sprintf("PROCTOR_HOST: %s\nACCESS_TOKEN: access-token\nEMAIL_ID: proctor@example.com", makeHostname(testServer.URL))) - err := ioutil.WriteFile(proctorConfigFilePath, proctorConfig, 0644) - defer os.Remove(proctorConfigFilePath) - assert.NoError(t, err) - config.InitConfig() - - proctorClient := NewClient() errStreamLogs := proctorClient.StreamProcLogs("test-job-id") assert.Error(t, errors.New(http.StatusText(http.StatusUnauthorized)), errStreamLogs) } @@ -353,3 +388,12 @@ func TestLogStreamForUnauthorizedUser(t *testing.T) { func makeHostname(s string) string { return strings.TrimPrefix(s, "http://") } + +type TestConnectionError struct { + message string + timeout bool +} + +func (e TestConnectionError) Error() string { return e.message } +func (e TestConnectionError) Timeout() bool { return e.timeout } +func (e TestConnectionError) Temporary() bool { return false } diff --git a/data/config_template.yaml b/data/config_template.yaml new file mode 100644 index 00000000..48a4b754 --- /dev/null +++ b/data/config_template.yaml @@ -0,0 +1,3 @@ +PROCTOR_HOST: +EMAIL_ID: +ACCESS_TOKEN: \ No newline at end of file diff --git a/glide.lock b/glide.lock index 34df3158..c46d5d16 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 67ebf11da4bd491f297d869e5d2ac01ee6b3248b355be7312ea6a73df316c3ac -updated: 2018-10-10T10:38:35.770913+05:30 +hash: 99d5240e2827968d8fea8861618d19371699d856ce4634572dfab614345e80c7 +updated: 2018-11-08T12:28:48.456666+05:30 imports: - name: cloud.google.com/go version: 3b1ae45394a234c385be014e9a488f2bb6eef821 @@ -9,64 +9,57 @@ imports: - name: github.com/briandowns/spinner version: 48dbb65d7bd5c74ab50d53d04c949f20e3d14944 - name: github.com/davecgh/go-spew - version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d + version: 782f4967f2dc4564575ca782fe2d04090b5faca8 subpackages: - spew -- name: github.com/docker/distribution - version: cd27f179f2c10c5d300e6d09025b538c475b0d51 - subpackages: - - digest - - reference -- name: github.com/emicklei/go-restful - version: ff4f55a206334ef123e4f79bbf348980da81ca46 - subpackages: - - log -- name: github.com/emicklei/go-restful-swagger12 - version: dcef7f55730566d41eae5db10e7d6981829720f6 +- name: github.com/evanphx/json-patch + version: 36442dbdb585210f8d5a1b45e67aa323c197d5c4 - name: github.com/fatih/color version: 507f6050b8568533fb3f5504de8e5205fa62a114 - name: github.com/fsnotify/fsnotify - version: 4da3e2cfbabc9f751898f250b49f2439785783a1 + version: ccc981bf80385c528a65fbfdd49bf2d8da22aa23 - name: github.com/garyburd/redigo - version: 47dc60e71eed504e3ef8e77ee3c6fe720f3be57f + version: 569eae59ada904ea8db7a225c2b47165af08735a subpackages: - internal - redis - name: github.com/ghodss/yaml - version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee -- name: github.com/go-openapi/analysis - version: b44dc874b601d9e4e2f6e19140e794ba24bead3b -- name: github.com/go-openapi/jsonpointer - version: 46af16f9f7b149af66e5d1bd010e3574dc06de98 -- name: github.com/go-openapi/jsonreference - version: 13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272 -- name: github.com/go-openapi/loads - version: 18441dfa706d924a39a030ee2c3b1d8d81917b38 -- name: github.com/go-openapi/spec - version: 6aced65f8501fe1217321abf0749d354824ba2ff -- name: github.com/go-openapi/swag - version: 1d0bd113de87027671077d3c71eb3ac5d7dbba72 + version: c7ce16629ff4cd059ed96ed06419dd3856fd3577 - name: github.com/gogo/protobuf - version: c0656edd0d9eab7c66d1eb0c568f9039345796f7 + version: 342cbe0a04158f6dcb03ca0079991a51a4248c02 subpackages: - proto - sortkeys - name: github.com/golang/glog version: 44145f04b68cf362d9c4df2182967c2275eaefed - name: github.com/golang/protobuf - version: 4bd1920723d7b7c925de087aa32e2187708897f7 + version: b4deda0973fb4c70b50d226b1af49f3da59f5265 subpackages: - proto + - ptypes + - ptypes/any + - ptypes/duration + - ptypes/timestamp +- name: github.com/google/btree + version: 7d79101e329e5a3adf994758c578dab82b90c017 - name: github.com/google/gofuzz version: 44d81051d367757e1c7c6a5a86423ece9afcf63c -- name: github.com/gorilla/context - version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42 +- name: github.com/googleapis/gnostic + version: 0c5108395e2debce0d731cf0287ddf7242066aba + subpackages: + - OpenAPIv2 + - compiler + - extensions - name: github.com/gorilla/mux - version: 2d5fef06b891c971b14aa6f71ca5ab6c03a36e0e + version: 3d80bc801bb034e17cae38591335b3b1110f1c47 - name: github.com/gorilla/websocket version: ea4d1f681babbce9545c9c5f3d5194a789c89f5b +- name: github.com/gregjones/httpcache + version: 787624de3eb7bd915c329cba748687a3b22666a6 + subpackages: + - diskcache - name: github.com/hashicorp/hcl - version: 23c074d0eceb2b8a5bfdbb271ab780cde70f05a8 + version: 65a6292f0157eff210d03ed1bf6c59b190b8b906 subpackages: - hcl/ast - hcl/parser @@ -77,73 +70,80 @@ imports: - json/parser - json/scanner - json/token -- name: github.com/howeyc/gopass - version: bf9dde6d0d2c004a008c27aaee91170c786f6db8 - name: github.com/imdario/mergo - version: 6633656539c1639d9d78127b7d47c622b5d7b6dc + version: 9316a62528ac99aaecb4e47eadd6dc8aa6533d58 - name: github.com/inconshreveable/mousetrap version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 - name: github.com/jmoiron/sqlx - version: cf35089a197953c69420c8d0cecda90809764b1d + version: 82935fac6c1a317907c8f43ed3f7f85ea844a78b subpackages: - reflectx -- name: github.com/juju/ratelimit - version: 5b9ff866471762aa2ab2dced63c9fb6f53921342 +- name: github.com/json-iterator/go + version: f2b4162afba35581b6d4a50d3b8f34e33c144682 +- name: github.com/konsorten/go-windows-terminal-sequences + version: 5c8c8bd35d3832f5d134ae1e1e375b69a4d25242 - name: github.com/lib/pq - version: 88edab0803230a3898347e77b474f8c1820a1f20 + version: 9eb73efc1fcc404148b56765b0d3f61d9a5ef8ee subpackages: - oid - name: github.com/magiconair/properties - version: 49d762b9817ba1c2e9d0c69183c2b4a8b8f1d934 -- name: github.com/mailru/easyjson - version: d5b7844b561a7bc640052f1b935f7b800330d7e0 - subpackages: - - buffer - - jlexer - - jwriter + version: c2353362d570a7bfa228149c62842019201cfb71 - name: github.com/mattes/migrate - version: 035c07716cd373d88456ec4d701402df52584cb4 + version: 4768a648fbd9e04389a73a21139d14a4ccb1a61a subpackages: - database - database/postgres - source - source/file - name: github.com/mattn/go-colorable - version: 7dc3415be66d7cc68bf0182f35c8d31f8d2ad8a7 + version: efa589957cd060542a26d2dd7832fd6a6c6c3ade - name: github.com/mattn/go-isatty - version: 6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c + version: 3fb116b820352b7f0c281308a4d6250c22d94e27 - name: github.com/mitchellh/mapstructure - version: 06020f85339e21b2478f756a78e295255ffa4d6a + version: 3536a929edddb9a5b34bd6861dc4a9647cb459fe +- name: github.com/modern-go/concurrent + version: bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94 +- name: github.com/modern-go/reflect2 + version: 94122c33edd36123c84d5368cfb2b69df93a0ec8 +- name: github.com/newrelic/go-agent + version: 46d73e6be8b4faeee70850d0df829e4fe00d6819 + subpackages: + - internal + - internal/cat + - internal/jsonx + - internal/logger + - internal/sysinfo + - internal/utilization - name: github.com/pelletier/go-toml - version: 4e9e0ee19b60b13eb79915933f44d8ed5f268bdd + version: 81a861c69d25a841d0c4394f0e6f84bc8c5afae0 +- name: github.com/peterbourgon/diskv + version: 5f041e8faa004a95c88a202771f4cc3e991971e6 +- name: github.com/pkg/errors + version: 059132a15dd08d6704c67711dae0cf35ab991756 - name: github.com/pmezard/go-difflib - version: d8ed2627bdf02c080bf22230dbb337003b7aba2d + version: 792786c7400a136282c1664665ae0a8db921c6c2 subpackages: - difflib -- name: github.com/PuerkitoBio/purell - version: 8a290539e2e8629dbc4e6bad948158f790ec31f4 -- name: github.com/PuerkitoBio/urlesc - version: 5bd2802263f21d8788851d5305584c82a5c75d7e - name: github.com/satori/go.uuid - version: f58768cc1a7a7e77a3bd49e98cdd21419399b6a3 + version: b2ce2384e17bbe0c6d34077efa39dbab3e09123b - name: github.com/Sirupsen/logrus - version: ba1b36c82c5e05c4f912a88eab0dcd91a171688f + version: 44067abb194b1bc8b342e1f2120f8d3ea691b834 - name: github.com/spf13/afero - version: 8d919cbe7e2627e417f3e45c3c0e489a5b7e2536 + version: d40851caa0d747393da1ffb28f7f9d8b4eeffebd subpackages: - mem - name: github.com/spf13/cast - version: acbeb36b902d72a7a4c18e8f3241075e7ab763e4 + version: 8c9545af88b134710ab1cd196795e7f2388358d7 - name: github.com/spf13/cobra version: 7b2c5ac9fc04fc5efafb60700713d4fa609b777b - name: github.com/spf13/jwalterweatherman - version: 12bd96e66386c1960ab0f74ced1362f66f552f7b + version: 94f6ae3ed3bceceafa716478c5fbf8d29ca601a1 - name: github.com/spf13/pflag version: 298182f68c66c05229eb03ac171abe6e309ee79a - name: github.com/spf13/viper version: a1b837276271029e31f796ae5d03ba9ffb017244 - name: github.com/stretchr/objx - version: 8a3f7159479fbc75b30357fbc48f380b7320f08e + version: ef50b0de28773081167c97fc27cf29a0bf8b8c71 - name: github.com/stretchr/testify version: f35b8ab0b5a2cef36673838d662e249dd9c94686 subpackages: @@ -154,21 +154,17 @@ imports: - name: github.com/thingful/httpmock version: 4df9ac03f1f75f097c31dde17e969d212d3fb479 - name: github.com/tylerb/graceful - version: 4654dfbb6ad53cb5e27f37d99b02e16c1872fbbb -- name: github.com/ugorji/go - version: ded73eae5db7e7a0ef6f55aace87a2873c5d2b74 - subpackages: - - codec + version: d72b0151351a13d0421b763b88f791469c4f5dc7 - name: github.com/urfave/cli - version: cfb38830724cc34fedffe9a2a29fb54fa9169cd1 + version: b67dcf995b6a7b7f14fad5fcb7cc5441b05e814b - name: github.com/urfave/negroni - version: 5dbbc83f748fc3ad38585842b0aedab546d0ea1e + version: 0af0d913cfc4cc6c389cc402eb70bf8a8fec374d - name: golang.org/x/crypto - version: d172538b2cfce0c13cee31e647d0367aa8cd2486 + version: de0752318171da717af4ce24d0a2e8626afaeb11 subpackages: - ssh/terminal - name: golang.org/x/net - version: f2499483f923065a842d38eb4c7f1927e6fc6e6d + version: 1c05540f6879653db88113bc4a2b70aec4bd491f subpackages: - context - context/ctxhttp @@ -184,13 +180,15 @@ imports: - jws - jwt - name: golang.org/x/sys - version: 8f0908ab3b2457e2e15403d3697c9ef5cb4b57a9 + version: 95c6576299259db960f6c5b9b69ea52422860fce subpackages: - unix + - windows - name: golang.org/x/text - version: 2910a502d2bf9e43193af9d68ca516529614eed3 + version: b19bf474d317b857955b12035d2c5acb57ce8b01 subpackages: - cases + - internal - internal/tag - language - runes @@ -200,8 +198,12 @@ imports: - unicode/bidi - unicode/norm - width +- name: golang.org/x/time + version: f51c12702a4d776e4c1fa9b0fabab841babae631 + subpackages: + - rate - name: google.golang.org/appengine - version: 12d5545dc1cfa6047a286d5e853841b6471f4c19 + version: 4a4468ece617fc8205e99368fa2200e9d1fad421 subpackages: - internal - internal/app_identity @@ -215,26 +217,60 @@ imports: - name: gopkg.in/inf.v0 version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 - name: gopkg.in/yaml.v2 - version: 287cf08546ab5e7e37d55a84f7ed3fd1db036de5 + version: 5420a8b6744d3b0345ab293f6fcba19c978f1183 +- name: k8s.io/api + version: de5c567eef5cb050b5476484727b074df9185088 + subpackages: + - admissionregistration/v1alpha1 + - admissionregistration/v1beta1 + - apps/v1 + - apps/v1beta1 + - apps/v1beta2 + - auditregistration/v1alpha1 + - authentication/v1 + - authentication/v1beta1 + - authorization/v1 + - authorization/v1beta1 + - autoscaling/v1 + - autoscaling/v2beta1 + - autoscaling/v2beta2 + - batch/v1 + - batch/v1beta1 + - batch/v2alpha1 + - certificates/v1beta1 + - coordination/v1beta1 + - core/v1 + - events/v1beta1 + - extensions/v1beta1 + - networking/v1 + - policy/v1beta1 + - rbac/v1 + - rbac/v1alpha1 + - rbac/v1beta1 + - scheduling/v1alpha1 + - scheduling/v1beta1 + - settings/v1alpha1 + - storage/v1 + - storage/v1alpha1 + - storage/v1beta1 - name: k8s.io/apimachinery - version: 1fd2e63a9a370677308a42f24fd40c86438afddf + version: 0aa9751e8aaff1b6afa1ca5270d8e280878797e4 subpackages: + - pkg/api/apitesting + - pkg/api/apitesting/fuzzer + - pkg/api/apitesting/roundtrip - pkg/api/equality - pkg/api/errors - pkg/api/meta - pkg/api/resource - - pkg/apimachinery - - pkg/apimachinery/announced - - pkg/apimachinery/registered + - pkg/apis/meta/fuzzer - pkg/apis/meta/v1 - pkg/apis/meta/v1/unstructured - - pkg/apis/meta/v1alpha1 + - pkg/apis/meta/v1beta1 - pkg/conversion - pkg/conversion/queryparams - - pkg/conversion/unstructured - pkg/fields - pkg/labels - - pkg/openapi - pkg/runtime - pkg/runtime/schema - pkg/runtime/serializer @@ -251,19 +287,21 @@ imports: - pkg/util/framer - pkg/util/intstr - pkg/util/json + - pkg/util/mergepatch + - pkg/util/naming - pkg/util/net - - pkg/util/rand - pkg/util/runtime - pkg/util/sets + - pkg/util/strategicpatch - pkg/util/validation - pkg/util/validation/field - - pkg/util/wait - pkg/util/yaml - pkg/version - pkg/watch + - third_party/forked/golang/json - third_party/forked/golang/reflect - name: k8s.io/client-go - version: d92e8497f71b7b4e0494e5bd204b48d34bd6f254 + version: 193c4639ffa27c9afcc06c3858b6da1f522b3b81 subpackages: - discovery - discovery/fake @@ -272,8 +310,16 @@ imports: - kubernetes/scheme - kubernetes/typed/admissionregistration/v1alpha1 - kubernetes/typed/admissionregistration/v1alpha1/fake + - kubernetes/typed/admissionregistration/v1beta1 + - kubernetes/typed/admissionregistration/v1beta1/fake + - kubernetes/typed/apps/v1 + - kubernetes/typed/apps/v1/fake - kubernetes/typed/apps/v1beta1 - kubernetes/typed/apps/v1beta1/fake + - kubernetes/typed/apps/v1beta2 + - kubernetes/typed/apps/v1beta2/fake + - kubernetes/typed/auditregistration/v1alpha1 + - kubernetes/typed/auditregistration/v1alpha1/fake - kubernetes/typed/authentication/v1 - kubernetes/typed/authentication/v1/fake - kubernetes/typed/authentication/v1beta1 @@ -284,70 +330,53 @@ imports: - kubernetes/typed/authorization/v1beta1/fake - kubernetes/typed/autoscaling/v1 - kubernetes/typed/autoscaling/v1/fake - - kubernetes/typed/autoscaling/v2alpha1 - - kubernetes/typed/autoscaling/v2alpha1/fake + - kubernetes/typed/autoscaling/v2beta1 + - kubernetes/typed/autoscaling/v2beta1/fake + - kubernetes/typed/autoscaling/v2beta2 + - kubernetes/typed/autoscaling/v2beta2/fake - kubernetes/typed/batch/v1 - kubernetes/typed/batch/v1/fake + - kubernetes/typed/batch/v1beta1 + - kubernetes/typed/batch/v1beta1/fake - kubernetes/typed/batch/v2alpha1 - kubernetes/typed/batch/v2alpha1/fake - kubernetes/typed/certificates/v1beta1 - kubernetes/typed/certificates/v1beta1/fake + - kubernetes/typed/coordination/v1beta1 + - kubernetes/typed/coordination/v1beta1/fake - kubernetes/typed/core/v1 - kubernetes/typed/core/v1/fake + - kubernetes/typed/events/v1beta1 + - kubernetes/typed/events/v1beta1/fake - kubernetes/typed/extensions/v1beta1 - kubernetes/typed/extensions/v1beta1/fake - kubernetes/typed/networking/v1 - kubernetes/typed/networking/v1/fake - kubernetes/typed/policy/v1beta1 - kubernetes/typed/policy/v1beta1/fake + - kubernetes/typed/rbac/v1 + - kubernetes/typed/rbac/v1/fake - kubernetes/typed/rbac/v1alpha1 - kubernetes/typed/rbac/v1alpha1/fake - kubernetes/typed/rbac/v1beta1 - kubernetes/typed/rbac/v1beta1/fake + - kubernetes/typed/scheduling/v1alpha1 + - kubernetes/typed/scheduling/v1alpha1/fake + - kubernetes/typed/scheduling/v1beta1 + - kubernetes/typed/scheduling/v1beta1/fake - kubernetes/typed/settings/v1alpha1 - kubernetes/typed/settings/v1alpha1/fake - kubernetes/typed/storage/v1 - kubernetes/typed/storage/v1/fake + - kubernetes/typed/storage/v1alpha1 + - kubernetes/typed/storage/v1alpha1/fake - kubernetes/typed/storage/v1beta1 - kubernetes/typed/storage/v1beta1/fake - - pkg/api - - pkg/api/v1 - - pkg/api/v1/ref - - pkg/apis/admissionregistration - - pkg/apis/admissionregistration/v1alpha1 - - pkg/apis/apps - - pkg/apis/apps/v1beta1 - - pkg/apis/authentication - - pkg/apis/authentication/v1 - - pkg/apis/authentication/v1beta1 - - pkg/apis/authorization - - pkg/apis/authorization/v1 - - pkg/apis/authorization/v1beta1 - - pkg/apis/autoscaling - - pkg/apis/autoscaling/v1 - - pkg/apis/autoscaling/v2alpha1 - - pkg/apis/batch - - pkg/apis/batch/v1 - - pkg/apis/batch/v2alpha1 - - pkg/apis/certificates - - pkg/apis/certificates/v1beta1 - - pkg/apis/extensions - - pkg/apis/extensions/v1beta1 - - pkg/apis/networking - - pkg/apis/networking/v1 - - pkg/apis/policy - - pkg/apis/policy/v1beta1 - - pkg/apis/rbac - - pkg/apis/rbac/v1alpha1 - - pkg/apis/rbac/v1beta1 - - pkg/apis/settings - - pkg/apis/settings/v1alpha1 - - pkg/apis/storage - - pkg/apis/storage/v1 - - pkg/apis/storage/v1beta1 - - pkg/util - - pkg/util/parsers + - pkg/apis/clientauthentication + - pkg/apis/clientauthentication/v1alpha1 + - pkg/apis/clientauthentication/v1beta1 - pkg/version + - plugin/pkg/client/auth/exec - plugin/pkg/client/auth/gcp - rest - rest/watch @@ -359,16 +388,24 @@ imports: - tools/clientcmd/api/latest - tools/clientcmd/api/v1 - tools/metrics + - tools/reference - transport - util/cert + - util/connrotation - util/flowcontrol - util/homedir - util/integer - util/jsonpath +- name: k8s.io/kube-openapi + version: 72693cb1fadd73ae2742f6fe29af77d1aecdd8cd + subpackages: + - pkg/util/proto testImports: - name: github.com/goware/urlx version: 86bdc24560383254e8b977da31a823eddf904409 - name: github.com/jarcoal/httpmock - version: 4442edb3db31196622da56482fd8d0fa375fba4d -- name: github.com/pkg/errors - version: 30136e27e2ac8d167177e8a583aa4c3fea5be833 + version: 9c70cfe4a1dad2f143c2244b45f2161fc72ceddf +- name: github.com/PuerkitoBio/purell + version: 8a290539e2e8629dbc4e6bad948158f790ec31f4 +- name: github.com/PuerkitoBio/urlesc + version: 5bd2802263f21d8788851d5305584c82a5c75d7e diff --git a/glide.yaml b/glide.yaml index b93d0b40..e79d719d 100644 --- a/glide.yaml +++ b/glide.yaml @@ -15,4 +15,4 @@ import: - package: github.com/spf13/cobra version: v0.0.1 - package: github.com/thingful/httpmock - version: 0.0.2 + version: 0.0.2 \ No newline at end of file diff --git a/io/printer.go b/io/printer.go index d8b0b927..59003c17 100644 --- a/io/printer.go +++ b/io/printer.go @@ -6,10 +6,15 @@ type Printer interface { Println(string, ...color.Attribute) } +var printerInstance Printer + type printer struct{} -func NewPrinter() Printer { - return &printer{} +func GetPrinter() Printer { + if printerInstance == nil { + printerInstance = &printer{} + } + return printerInstance } func (p *printer) Println(msg string, attr ...color.Attribute) { diff --git a/main.go b/main.go index 37e24d3f..69d8bead 100644 --- a/main.go +++ b/main.go @@ -1,14 +1,23 @@ package main import ( + "github.com/fatih/color" "github.com/gojektech/proctor/cmd" + "github.com/gojektech/proctor/config" "github.com/gojektech/proctor/daemon" "github.com/gojektech/proctor/io" ) func main() { - printer := io.NewPrinter() - proctorEngineClient := daemon.NewClient() + printer := io.GetPrinter() + proctorConfig, err := config.LoadConfig() + if (config.ConfigError{}) != err { + printer.Println(err.RootError().Error(), color.FgRed) + printer.Println(err.Message, color.FgGreen) + printer.Println("Encountered error while loading config, exiting.", color.FgRed) + return + } + proctorEngineClient := daemon.NewClient(proctorConfig) cmd.Execute(printer, proctorEngineClient) } diff --git a/proctord/utility/utils.go b/proctord/utility/utils.go index 9d3eec3e..18cb22ae 100644 --- a/proctord/utility/utils.go +++ b/proctord/utility/utils.go @@ -2,11 +2,21 @@ package utility const ClientError = "malformed request" const ServerError = "Something went wrong" -const UnauthorizedError = "Unauthorized. Please check the email id and access token" + +const UnauthorizedErrorMissingConfig = "EMAIL_ID or ACCESS_TOKEN is not present in proctor config file." +const UnauthorizedErrorInvalidConfig = "Please check the EMAIL_ID and ACCESS_TOKEN validity in proctor config file." const GenericListCmdError = "Error fetching list of procs. Please check configuration and network connectivity" const GenericProcCmdError = "Error executing proc. Please check configuration and network connectivity" const GenericDescribeCmdError = "Error fetching description of proc. Please check configuration and network connectivity" +const UnauthorizedErrorHeader = "Unauthorized Access!!!" +const GenericTimeoutErrorHeader = "Connection Timeout!!!" +const GenericNetworkErrorHeader = "Network Error!!!" +const GenericResponseErrorHeader = "Server Error!!!" + +const ConfigProctorHostMissingError = "Config Error!!!\nMandatory config PROCTOR_HOST is missing in Proctor Config file." +const GenericTimeoutErrorBody = "Please check your Internet/VPN connection for connectivity to ProctorD." + const JobSubmissionSuccess = "success" const JobSubmissionClientError = "client_error" const JobSubmissionServerError = "server_error"