cli: allow customization of server api paths (close #4016) (#4174)

This commit is contained in:
Aravind Shankar 2020-04-08 16:29:21 +05:30 committed by GitHub
parent b02cd336d0
commit 21b61af109
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 94 additions and 28 deletions

View File

@ -11,6 +11,7 @@ The order and collapsed state of columns is now persisted across page navigation
### Bug fixes and improvements ### Bug fixes and improvements
- cli: allow customization of server api paths (close #4016)
- cli: clean up migration files created during a failed migrate api (close #4312) (#4319) - cli: clean up migration files created during a failed migrate api (close #4312) (#4319)
- cli: add support for multiple versions of plugin (close #4105) - cli: add support for multiple versions of plugin (close #4105)
- cli: template assets path in console HTML for unversioned builds - cli: template assets path in console HTML for unversioned builds

View File

@ -12,7 +12,9 @@ import (
"io/ioutil" "io/ioutil"
"net/url" "net/url"
"os" "os"
"path"
"path/filepath" "path/filepath"
"reflect"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -61,6 +63,36 @@ const (
V2 V2
) )
// ServerAPIPaths has the custom paths defined for server api
type ServerAPIPaths struct {
Query string `yaml:"query,omitempty"`
GraphQL string `yaml:"graphql,omitempty"`
Config string `yaml:"config,omitempty"`
PGDump string `yaml:"pg_dump,omitempty"`
Version string `yaml:"version,omitempty"`
}
// GetQueryParams - encodes the values in url
func (s ServerAPIPaths) GetQueryParams() url.Values {
vals := url.Values{}
t := reflect.TypeOf(s)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
tag := field.Tag.Get("yaml")
splitTag := strings.Split(tag, ",")
if len(splitTag) == 0 {
continue
}
name := splitTag[0]
if name == "-" {
continue
}
v := reflect.ValueOf(s).Field(i)
vals.Add(name, v.String())
}
return vals
}
// ErrInvalidConfigVersion - if the config version is not valid // ErrInvalidConfigVersion - if the config version is not valid
var ErrInvalidConfigVersion error = fmt.Errorf("invalid config version") var ErrInvalidConfigVersion error = fmt.Errorf("invalid config version")
@ -105,10 +137,19 @@ type ServerConfig struct {
AccessKey string `yaml:"access_key,omitempty"` AccessKey string `yaml:"access_key,omitempty"`
// AdminSecret (optional) Admin secret required to query the endpoint // AdminSecret (optional) Admin secret required to query the endpoint
AdminSecret string `yaml:"admin_secret,omitempty"` AdminSecret string `yaml:"admin_secret,omitempty"`
// APIPaths (optional) API paths for server
APIPaths *ServerAPIPaths `yaml:"api_paths,omitempty"`
ParsedEndpoint *url.URL `yaml:"-"` ParsedEndpoint *url.URL `yaml:"-"`
} }
// GetVersionEndpoint provides the url to contact the version API
func (s *ServerConfig) GetVersionEndpoint() string {
nurl := *s.ParsedEndpoint
nurl.Path = path.Join(nurl.Path, s.APIPaths.Version)
return nurl.String()
}
// ParseEndpoint ensures the endpoint is valid. // ParseEndpoint ensures the endpoint is valid.
func (s *ServerConfig) ParseEndpoint() error { func (s *ServerConfig) ParseEndpoint() error {
nurl, err := url.Parse(s.Endpoint) nurl, err := url.Parse(s.Endpoint)
@ -398,7 +439,7 @@ func (ec *ExecutionContext) Validate() error {
} }
func (ec *ExecutionContext) checkServerVersion() error { func (ec *ExecutionContext) checkServerVersion() error {
v, err := version.FetchServerVersion(ec.Config.ServerConfig.Endpoint) v, err := version.FetchServerVersion(ec.Config.ServerConfig.GetVersionEndpoint())
if err != nil { if err != nil {
return errors.Wrap(err, "failed to get version from server") return errors.Wrap(err, "failed to get version from server")
} }
@ -441,6 +482,11 @@ func (ec *ExecutionContext) readConfig() error {
v.SetDefault("endpoint", "http://localhost:8080") v.SetDefault("endpoint", "http://localhost:8080")
v.SetDefault("admin_secret", "") v.SetDefault("admin_secret", "")
v.SetDefault("access_key", "") v.SetDefault("access_key", "")
v.SetDefault("api_paths.query", "v1/query")
v.SetDefault("api_paths.graphql", "v1/graphql")
v.SetDefault("api_paths.config", "v1alpha1/config")
v.SetDefault("api_paths.pg_dump", "v1alpha1/pg_dump")
v.SetDefault("api_paths.version", "v1/version")
v.SetDefault("metadata_directory", "") v.SetDefault("metadata_directory", "")
v.SetDefault("migrations_directory", "migrations") v.SetDefault("migrations_directory", "migrations")
v.SetDefault("actions.kind", "synchronous") v.SetDefault("actions.kind", "synchronous")
@ -462,6 +508,13 @@ func (ec *ExecutionContext) readConfig() error {
ServerConfig: ServerConfig{ ServerConfig: ServerConfig{
Endpoint: v.GetString("endpoint"), Endpoint: v.GetString("endpoint"),
AdminSecret: adminSecret, AdminSecret: adminSecret,
APIPaths: &ServerAPIPaths{
Query: v.GetString("api_paths.query"),
GraphQL: v.GetString("api_paths.graphql"),
Config: v.GetString("api_paths.config"),
PGDump: v.GetString("api_paths.pg_dump"),
Version: v.GetString("api_paths.version"),
},
}, },
MetadataDirectory: v.GetString("metadata_directory"), MetadataDirectory: v.GetString("metadata_directory"),
MigrationsDirectory: v.GetString("migrations_directory"), MigrationsDirectory: v.GetString("migrations_directory"),

View File

@ -12,6 +12,7 @@ import (
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
"github.com/hasura/graphql-engine/cli" "github.com/hasura/graphql-engine/cli"
"github.com/hasura/graphql-engine/cli/metadata/actions/types"
"github.com/hasura/graphql-engine/cli/migrate/database/hasuradb" "github.com/hasura/graphql-engine/cli/migrate/database/hasuradb"
"github.com/hasura/graphql-engine/cli/migrate/source" "github.com/hasura/graphql-engine/cli/migrate/source"
"github.com/hasura/graphql-engine/cli/migrate/source/file" "github.com/hasura/graphql-engine/cli/migrate/source/file"
@ -319,7 +320,23 @@ func newScriptsUpdateConfigV2Cmd(ec *cli.ExecutionContext) *cobra.Command {
return errors.Wrap(err, "cannot write metadata") return errors.Wrap(err, "cannot write metadata")
} }
ec.Spin("Writing new config file...") ec.Spin("Writing new config file...")
err = ec.WriteConfig(nil) // Read the config from config.yaml
cfgByt, err := ioutil.ReadFile(ec.ConfigFile)
if err != nil {
return errors.Wrap(err, "cannot read config file")
}
var cfg cli.Config
err = yaml.Unmarshal(cfgByt, &cfg)
if err != nil {
return errors.Wrap(err, "cannot parse config file")
}
cfg.Version = cli.V2
cfg.MetadataDirectory = ec.Viper.GetString("metadata_directory")
cfg.ActionConfig = &types.ActionExecutionConfig{
Kind: ec.Viper.GetString("actions.kind"),
HandlerWebhookBaseURL: ec.Viper.GetString("actions.handler_webhook_baseurl"),
}
err = ec.WriteConfig(&cfg)
if err != nil { if err != nil {
return errors.Wrap(err, "cannot write config file") return errors.Wrap(err, "cannot write config file")
} }

View File

@ -40,9 +40,9 @@ var (
type Config struct { type Config struct {
MigrationsTable string MigrationsTable string
SettingsTable string SettingsTable string
v1URL *nurl.URL queryURL *nurl.URL
graphqlURL *nurl.URL graphqlURL *nurl.URL
schemDumpURL *nurl.URL pgDumpURL *nurl.URL
Headers map[string]string Headers map[string]string
isCMD bool isCMD bool
Plugins types.MetadataPlugins Plugins types.MetadataPlugins
@ -115,20 +115,20 @@ func (h *HasuraDB) Open(url string, isCMD bool, logger *log.Logger) (database.Dr
config := &Config{ config := &Config{
MigrationsTable: DefaultMigrationsTable, MigrationsTable: DefaultMigrationsTable,
SettingsTable: DefaultSettingsTable, SettingsTable: DefaultSettingsTable,
v1URL: &nurl.URL{ queryURL: &nurl.URL{
Scheme: scheme, Scheme: scheme,
Host: hurl.Host, Host: hurl.Host,
Path: path.Join(hurl.Path, "v1/query"), Path: path.Join(hurl.Path, params.Get("query")),
}, },
graphqlURL: &nurl.URL{ graphqlURL: &nurl.URL{
Scheme: scheme, Scheme: scheme,
Host: hurl.Host, Host: hurl.Host,
Path: path.Join(hurl.Path, "v1/graphql"), Path: path.Join(hurl.Path, params.Get("graphql")),
}, },
schemDumpURL: &nurl.URL{ pgDumpURL: &nurl.URL{
Scheme: scheme, Scheme: scheme,
Host: hurl.Host, Host: hurl.Host,
Path: path.Join(hurl.Path, "v1alpha1/pg_dump"), Path: path.Join(hurl.Path, params.Get("pg_dump")),
}, },
isCMD: isCMD, isCMD: isCMD,
Headers: headers, Headers: headers,
@ -417,7 +417,7 @@ func (h *HasuraDB) ensureVersionTable() error {
func (h *HasuraDB) sendv1Query(m interface{}) (resp *http.Response, body []byte, err error) { func (h *HasuraDB) sendv1Query(m interface{}) (resp *http.Response, body []byte, err error) {
request := gorequest.New() request := gorequest.New()
request = request.Post(h.config.v1URL.String()).Send(m) request = request.Post(h.config.queryURL.String()).Send(m)
for headerName, headerValue := range h.config.Headers { for headerName, headerValue := range h.config.Headers {
request.Set(headerName, headerValue) request.Set(headerName, headerValue)
} }
@ -455,7 +455,7 @@ func (h *HasuraDB) sendv1GraphQL(query interface{}) (resp *http.Response, body [
func (h *HasuraDB) sendSchemaDumpQuery(m interface{}) (resp *http.Response, body []byte, err error) { func (h *HasuraDB) sendSchemaDumpQuery(m interface{}) (resp *http.Response, body []byte, err error) {
request := gorequest.New() request := gorequest.New()
request = request.Post(h.config.schemDumpURL.String()).Send(m) request = request.Post(h.config.pgDumpURL.String()).Send(m)
for headerName, headerValue := range h.config.Headers { for headerName, headerValue := range h.config.Headers {
request.Set(headerName, headerValue) request.Set(headerName, headerValue)

View File

@ -121,7 +121,7 @@ func FilterCustomQuery(u *nurl.URL) *nurl.URL {
} }
func NewMigrate(ec *cli.ExecutionContext, isCmd bool) (*Migrate, error) { func NewMigrate(ec *cli.ExecutionContext, isCmd bool) (*Migrate, error) {
dbURL := GetDataPath(ec.Config.ServerConfig.ParsedEndpoint, GetAdminSecretHeaderName(ec.Version), ec.Config.ServerConfig.AdminSecret) dbURL := GetDataPath(ec)
fileURL := GetFilePath(ec.MigrationDir) fileURL := GetFilePath(ec.MigrationDir)
t, err := New(fileURL.String(), dbURL.String(), isCmd, int(ec.Config.Version), ec.Logger) t, err := New(fileURL.String(), dbURL.String(), isCmd, int(ec.Config.Version), ec.Logger)
if err != nil { if err != nil {
@ -132,13 +132,15 @@ func NewMigrate(ec *cli.ExecutionContext, isCmd bool) (*Migrate, error) {
return t, nil return t, nil
} }
func GetDataPath(url *nurl.URL, adminSecretHeader, adminSecretValue string) *nurl.URL { func GetDataPath(ec *cli.ExecutionContext) *nurl.URL {
url := ec.Config.ServerConfig.ParsedEndpoint
host := &nurl.URL{ host := &nurl.URL{
Scheme: "hasuradb", Scheme: "hasuradb",
Host: url.Host, Host: url.Host,
Path: url.Path, Path: url.Path,
RawQuery: ec.Config.ServerConfig.APIPaths.GetQueryParams().Encode(),
} }
q := url.Query() q := host.Query()
// Set sslmode in query // Set sslmode in query
switch scheme := url.Scheme; scheme { switch scheme := url.Scheme; scheme {
case "https": case "https":
@ -146,8 +148,8 @@ func GetDataPath(url *nurl.URL, adminSecretHeader, adminSecretValue string) *nur
default: default:
q.Set("sslmode", "disable") q.Set("sslmode", "disable")
} }
if adminSecretValue != "" { if GetAdminSecretHeaderName(ec.Version) != "" {
q.Add("headers", fmt.Sprintf("%s:%s", adminSecretHeader, adminSecretValue)) q.Add("headers", fmt.Sprintf("%s:%s", GetAdminSecretHeaderName(ec.Version), ec.Config.ServerConfig.AdminSecret))
} }
host.RawQuery = q.Encode() host.RawQuery = q.Encode()
return host return host

View File

@ -1,10 +1,8 @@
package version package version
import ( import (
"fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url"
yaml "github.com/ghodss/yaml" yaml "github.com/ghodss/yaml"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -16,12 +14,7 @@ type serverVersionResponse struct {
// FetchServerVersion reads the version from server. // FetchServerVersion reads the version from server.
func FetchServerVersion(endpoint string) (version string, err error) { func FetchServerVersion(endpoint string) (version string, err error) {
ep, err := url.Parse(endpoint) response, err := http.Get(endpoint)
if err != nil {
return "", errors.Wrap(err, "cannot parse endpoint as a valid url")
}
versionEndpoint := fmt.Sprintf("%s/v1/version", ep.String())
response, err := http.Get(versionEndpoint)
if err != nil { if err != nil {
return "", errors.Wrap(err, "failed making version api call") return "", errors.Wrap(err, "failed making version api call")
} }
@ -30,7 +23,7 @@ func FetchServerVersion(endpoint string) (version string, err error) {
case http.StatusNotFound: case http.StatusNotFound:
return "", nil return "", nil
default: default:
return "", errors.Errorf("GET %s failed - [%d]", versionEndpoint, response.StatusCode) return "", errors.Errorf("GET %s failed - [%d]", endpoint, response.StatusCode)
} }
} else { } else {
defer response.Body.Close() defer response.Body.Close()