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: 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)
- 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 1-click deployment to Nhost page to the deployment guides (#5180)
- 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/microcosm-cc/bluemonday v1.0.2 // indirect
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/parnurzeal/gorequest v0.2.16
github.com/pkg/errors v0.8.1

View File

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

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"
"runtime"
"strings"
"syscall"
"github.com/hasura/graphql-engine/cli/plugins/download"
"github.com/pkg/errors"
@ -145,7 +146,22 @@ func createOrUpdateLink(binDir, binary, plugin string) error {
// Create new
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
}
@ -159,7 +175,7 @@ func removeLink(path string) error {
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())
}
if err := os.Remove(path); err != nil {