cli: fix permission issues when creating symlinks on windows, misc fix (close #5111, close #4879) (#5164)

This commit is contained in:
Aravind 2020-07-03 09:49:09 +05:30 committed by GitHub
parent 9ef6de5113
commit cd0e2ec8e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 137 additions and 23 deletions

View File

@ -16,6 +16,7 @@
- console: support tracking partitioned tables (close #5071) (#5258) - console: support tracking partitioned tables (close #5071) (#5258)
- console: add button to cancel one-off scheduled events and cron-trigger events (close #5161) (#5236) - console: add button to cancel one-off scheduled events and cron-trigger events (close #5161) (#5236)
- console: handle generated and identity columns in console data section (close #4552, #4863) (#4761) - console: handle generated and identity columns in console data section (close #4552, #4863) (#4761)
- cli: fix plugins install failing due to permission issues on windows (close #5111)
- docs: add note for managed databases in postgres requirements (close #1677, #3783) (#5228) - docs: add note for managed databases in postgres requirements (close #1677, #3783) (#5228)
- docs: add 1-click deployment to Nhost page to the deployment guides (#5180) - docs: add 1-click deployment to Nhost page to the deployment guides (#5180)
- docs: add hasura cloud to getting started section (close #5206) (#5208) - docs: add hasura cloud to getting started section (close #5206) (#5208)

View File

@ -30,6 +30,7 @@ require (
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
github.com/microcosm-cc/bluemonday v1.0.2 // indirect github.com/microcosm-cc/bluemonday v1.0.2 // indirect
github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/mapstructure v1.1.2
github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852 github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852
github.com/parnurzeal/gorequest v0.2.16 github.com/parnurzeal/gorequest v0.2.16
github.com/pkg/errors v0.8.1 github.com/pkg/errors v0.8.1

View File

@ -5,6 +5,8 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/mitchellh/mapstructure"
"github.com/hasura/graphql-engine/cli/migrate/database" "github.com/hasura/graphql-engine/cli/migrate/database"
"github.com/qor/transition" "github.com/qor/transition"
@ -223,25 +225,25 @@ type HasuraError struct {
// MigrationFile is used internally for hasuractl // MigrationFile is used internally for hasuractl
migrationFile string migrationFile string
migrationQuery string migrationQuery string
Path string `json:"path"` Path string `json:"path"`
ErrorMessage string `json:"error"` ErrorMessage string `json:"error"`
Internal *SQLInternalError `json:"internal,omitempty"` Internal interface{} `json:"internal,omitempty"`
Message string `json:"message,omitempty"` Message string `json:"message,omitempty"`
Code string `json:"code"` Code string `json:"code"`
} }
type SQLInternalError struct { type SQLInternalError struct {
Arguments []string `json:"arguments"` Arguments []string `json:"arguments" mapstructure:"arguments,omitempty"`
Error PostgresError `json:"error"` Error PostgresError `json:"error" mapstructure:"error,omitempty"`
Prepared bool `json:"prepared"` Prepared bool `json:"prepared" mapstructure:"prepared,omitempty"`
Statement string `json:"statement"` Statement string `json:"statement" mapstructure:"statement,omitempty"`
} }
type PostgresError struct { type PostgresError struct {
StatusCode string `json:"status_code"` StatusCode string `json:"status_code" mapstructure:"status_code,omitempty"`
ExecStatus string `json:"exec_status"` ExecStatus string `json:"exec_status" mapstructure:"exec_status,omitempty"`
Message string `json:"message"` Message string `json:"message" mapstructure:"message,omitempty"`
Description string `json:"description"` Description string `json:"description" mapstructure:"description,omitempty"`
Hint string `json:"hint"` Hint string `json:"hint" mapstructure:"hint,omitempty"`
} }
type SchemaDump struct { type SchemaDump struct {
@ -258,14 +260,34 @@ func (h HasuraError) Error() string {
if h.migrationQuery != "" { if h.migrationQuery != "" {
errorStrings = append(errorStrings, fmt.Sprintf("%s", h.migrationQuery)) errorStrings = append(errorStrings, fmt.Sprintf("%s", h.migrationQuery))
} }
if h.Internal != nil { var internalError SQLInternalError
// postgres error var internalErrors []SQLInternalError
errorStrings = append(errorStrings, fmt.Sprintf("[%s] %s: %s", h.Internal.Error.StatusCode, h.Internal.Error.ExecStatus, h.Internal.Error.Message)) if v, ok := h.Internal.(map[string]interface{}); ok {
if len(h.Internal.Error.Description) > 0 { err := mapstructure.Decode(v, &internalError)
errorStrings = append(errorStrings, fmt.Sprintf("Description: %s", h.Internal.Error.Description)) if err == nil {
// postgres error
errorStrings = append(errorStrings, fmt.Sprintf("[%s] %s: %s", internalError.Error.StatusCode, internalError.Error.ExecStatus, internalError.Error.Message))
if len(internalError.Error.Description) > 0 {
errorStrings = append(errorStrings, fmt.Sprintf("Description: %s", internalError.Error.Description))
}
if len(internalError.Error.Hint) > 0 {
errorStrings = append(errorStrings, fmt.Sprintf("Hint: %s", internalError.Error.Hint))
}
} }
if len(h.Internal.Error.Hint) > 0 { }
errorStrings = append(errorStrings, fmt.Sprintf("Hint: %s", h.Internal.Error.Hint)) if v, ok := h.Internal.([]interface{}); ok {
err := mapstructure.Decode(v, &internalErrors)
if err == nil {
for _, internalError := range internalErrors {
// postgres error
errorStrings = append(errorStrings, fmt.Sprintf("[%s] %s: %s", internalError.Error.StatusCode, internalError.Error.ExecStatus, internalError.Error.Message))
if len(internalError.Error.Description) > 0 {
errorStrings = append(errorStrings, fmt.Sprintf("Description: %s", internalError.Error.Description))
}
if len(internalError.Error.Hint) > 0 {
errorStrings = append(errorStrings, fmt.Sprintf("Hint: %s", internalError.Error.Hint))
}
}
} }
} }
return strings.Join(errorStrings, "\r\n") return strings.Join(errorStrings, "\r\n")

View File

@ -0,0 +1,74 @@
package hasuradb
import (
"encoding/json"
"testing"
)
func TestHasuraError_Error(t *testing.T) {
type fields struct {
migrationFile string
migrationQuery string
Path string
ErrorMessage string
Internal interface{}
Message string
Code string
}
tests := []struct {
name string
fields fields
want string
}{
{
"can unmarshal internal error",
fields{
Internal: func() interface{} {
d := []byte(`
{
"error": {
"status_code": "1"
}
}`)
var v interface{}
json.Unmarshal(d, &v)
return v
}(),
},
"[] ()\r\n[1] : ",
},
{
"can unmarshal internal errors",
fields{
Internal: func() interface{} {
d := []byte(`
[{
"error": {
"status_code": "2"
}
}]`)
var v interface{}
json.Unmarshal(d, &v)
return v
}(),
},
"[] ()\r\n[2] : ",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
h := HasuraError{
migrationFile: tt.fields.migrationFile,
migrationQuery: tt.fields.migrationQuery,
Path: tt.fields.Path,
ErrorMessage: tt.fields.ErrorMessage,
Internal: tt.fields.Internal,
Message: tt.fields.Message,
Code: tt.fields.Code,
}
if got := h.Error(); got != tt.want {
t.Errorf("Error() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -15,6 +15,7 @@ import (
"regexp" "regexp"
"runtime" "runtime"
"strings" "strings"
"syscall"
"github.com/hasura/graphql-engine/cli/plugins/download" "github.com/hasura/graphql-engine/cli/plugins/download"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -145,7 +146,22 @@ func createOrUpdateLink(binDir, binary, plugin string) error {
// Create new // Create new
if err := os.Symlink(binary, dst); err != nil { if err := os.Symlink(binary, dst); err != nil {
return errors.Wrapf(err, "failed to create a symlink from %q to %q", binary, dst) if IsWindows() {
// If cloning the symlink fails on Windows because the user
// does not have the required privileges, ignore the error and
// fall back to copying the file contents.
//
// ERROR_PRIVILEGE_NOT_HELD is 1314 (0x522):
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms681385(v=vs.85).aspx
if lerr, ok := err.(*os.LinkError); ok && lerr.Err != syscall.Errno(1314) {
return err
}
if err := copyFile(binary, dst, 0755); err != nil {
return err
}
} else {
return errors.Wrapf(err, "failed to create a symlink from %q to %q", binary, dst)
}
} }
return nil return nil
} }
@ -159,7 +175,7 @@ func removeLink(path string) error {
return errors.Wrapf(err, "failed to read the symlink in %q", path) return errors.Wrapf(err, "failed to read the symlink in %q", path)
} }
if fi.Mode()&os.ModeSymlink == 0 { if fi.Mode()&os.ModeSymlink == 0 && !IsWindows() {
return errors.Errorf("file %q is not a symlink (mode=%s)", path, fi.Mode()) return errors.Errorf("file %q is not a symlink (mode=%s)", path, fi.Mode())
} }
if err := os.Remove(path); err != nil { if err := os.Remove(path); err != nil {