mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-10-05 06:18:04 +03:00
cli: Support migrations for Cockroach DB in CLI Console
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/7550 GitOrigin-RevId: eb9d6c8dae1c1f7980df45f5080f8d7cab8791c9
This commit is contained in:
parent
0dfeea2c5a
commit
094b5e6db2
@ -38,15 +38,17 @@ type CatalogStateOperations interface {
|
||||
type SourceKind string
|
||||
|
||||
const (
|
||||
SourceKindPG SourceKind = "postgres"
|
||||
SourceKindMSSQL SourceKind = "mssql"
|
||||
SourceKindCitus SourceKind = "citus"
|
||||
SourceKindPG SourceKind = "postgres"
|
||||
SourceKindMSSQL SourceKind = "mssql"
|
||||
SourceKindCitus SourceKind = "citus"
|
||||
SourceKindCockroach SourceKind = "cockroach"
|
||||
)
|
||||
|
||||
type V2Query interface {
|
||||
PGSourceOps
|
||||
MSSQLSourceOps
|
||||
CitusSourceOps
|
||||
CockroachSourceOps
|
||||
Send(requestBody interface{}) (httpcResponse *httpc.Response, body io.Reader, error error)
|
||||
Bulk([]RequestBody) (io.Reader, error)
|
||||
}
|
||||
|
@ -40,3 +40,11 @@ type CitusSourceOps interface {
|
||||
type CitusRunSQLInput PGRunSQLInput
|
||||
|
||||
type CitusRunSQLOutput PGRunSQLOutput
|
||||
|
||||
type CockroachSourceOps interface {
|
||||
CockroachRunSQL(input CockroachRunSQLInput) (response *CockroachRunSQLOutput, err error)
|
||||
}
|
||||
|
||||
type CockroachRunSQLInput PGRunSQLInput
|
||||
|
||||
type CockroachRunSQLOutput PGRunSQLOutput
|
||||
|
32
cli/internal/hasura/sourceops/cockroach/cockroach.go
Normal file
32
cli/internal/hasura/sourceops/cockroach/cockroach.go
Normal file
@ -0,0 +1,32 @@
|
||||
package cockroach
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/hasura/graphql-engine/cli/v2/internal/errors"
|
||||
"github.com/hasura/graphql-engine/cli/v2/internal/httpc"
|
||||
)
|
||||
|
||||
type SourceOps struct {
|
||||
*httpc.Client
|
||||
path string
|
||||
}
|
||||
|
||||
func New(client *httpc.Client, path string) *SourceOps {
|
||||
return &SourceOps{client, path}
|
||||
}
|
||||
|
||||
func (d *SourceOps) send(body interface{}, responseBodyWriter io.Writer) (*httpc.Response, error) {
|
||||
var op errors.Op = "cockroach.SourceOps.send"
|
||||
req, err := d.NewRequest(http.MethodPost, d.path, body)
|
||||
if err != nil {
|
||||
return nil, errors.E(op, err)
|
||||
}
|
||||
resp, err := d.LockAndDo(context.Background(), req, responseBodyWriter)
|
||||
if err != nil {
|
||||
return nil, errors.E(op, err)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
36
cli/internal/hasura/sourceops/cockroach/run_sql.go
Normal file
36
cli/internal/hasura/sourceops/cockroach/run_sql.go
Normal file
@ -0,0 +1,36 @@
|
||||
package cockroach
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/hasura/graphql-engine/cli/v2/internal/errors"
|
||||
"github.com/hasura/graphql-engine/cli/v2/internal/hasura"
|
||||
)
|
||||
|
||||
func (s *SourceOps) CockroachRunSQL(input hasura.CockroachRunSQLInput) (*hasura.CockroachRunSQLOutput, error) {
|
||||
var op errors.Op = "cockroach.SourceOps.CockroachRunSQL"
|
||||
body := hasura.RequestBody{
|
||||
Type: "cockroach_run_sql",
|
||||
Args: input,
|
||||
}
|
||||
var b = new(bytes.Buffer)
|
||||
resp, err := s.send(body, b)
|
||||
if err != nil {
|
||||
return nil, errors.E(op, err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
if b.Len() > 0 {
|
||||
return nil, errors.E(op, errors.KindHasuraAPI, b.String())
|
||||
} else {
|
||||
return nil, errors.E(op, errors.KindHasuraAPI, fmt.Errorf("cockroach_run_sql api request failed %d", resp.StatusCode))
|
||||
}
|
||||
}
|
||||
parsedResp := new(hasura.CockroachRunSQLOutput)
|
||||
if err = json.NewDecoder(b).Decode(parsedResp); err != nil {
|
||||
return nil, errors.E(op, err)
|
||||
}
|
||||
return parsedResp, nil
|
||||
}
|
68
cli/internal/hasura/sourceops/cockroach/run_sql_test.go
Normal file
68
cli/internal/hasura/sourceops/cockroach/run_sql_test.go
Normal file
@ -0,0 +1,68 @@
|
||||
package cockroach
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/hasura/graphql-engine/cli/v2/internal/hasura"
|
||||
"github.com/hasura/graphql-engine/cli/v2/internal/httpc"
|
||||
"github.com/hasura/graphql-engine/cli/v2/internal/testutil"
|
||||
)
|
||||
|
||||
func TestHasuraDatabaseOperations_RunSQL(t *testing.T) {
|
||||
port, source, teardown := testutil.StartHasuraWithCockroachSource(t, testutil.HasuraDockerImage)
|
||||
defer teardown()
|
||||
type fields struct {
|
||||
httpClient *httpc.Client
|
||||
path string
|
||||
}
|
||||
type args struct {
|
||||
input hasura.CockroachRunSQLInput
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want *hasura.CockroachRunSQLOutput
|
||||
wantErr bool
|
||||
assertErr require.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
"can send a run_sql request",
|
||||
fields{
|
||||
httpClient: testutil.NewHttpcClient(t, port, nil),
|
||||
path: "v2/query",
|
||||
},
|
||||
args{
|
||||
input: hasura.CockroachRunSQLInput{
|
||||
SQL: "CREATE TABLE users2();",
|
||||
Source: source,
|
||||
},
|
||||
},
|
||||
&hasura.CockroachRunSQLOutput{
|
||||
ResultType: hasura.CommandOK,
|
||||
Result: nil,
|
||||
},
|
||||
false,
|
||||
require.NoError,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
test := func() {
|
||||
h := &SourceOps{
|
||||
Client: tt.fields.httpClient,
|
||||
path: tt.fields.path,
|
||||
}
|
||||
got, err := h.CockroachRunSQL(tt.args.input)
|
||||
tt.assertErr(t, err)
|
||||
if !tt.wantErr {
|
||||
assert.Equal(t, tt.want, got)
|
||||
}
|
||||
}
|
||||
test()
|
||||
})
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/hasura/graphql-engine/cli/v2/internal/hasura/sourceops/citus"
|
||||
"github.com/hasura/graphql-engine/cli/v2/internal/hasura/sourceops/cockroach"
|
||||
"github.com/hasura/graphql-engine/cli/v2/internal/hasura/sourceops/mssql"
|
||||
"github.com/hasura/graphql-engine/cli/v2/internal/hasura/sourceops/postgres"
|
||||
|
||||
@ -20,16 +21,18 @@ type Client struct {
|
||||
hasura.PGSourceOps
|
||||
hasura.MSSQLSourceOps
|
||||
hasura.CitusSourceOps
|
||||
hasura.CockroachSourceOps
|
||||
path string
|
||||
}
|
||||
|
||||
func New(c *httpc.Client, path string) *Client {
|
||||
client := &Client{
|
||||
Client: c,
|
||||
PGSourceOps: postgres.New(c, path),
|
||||
MSSQLSourceOps: mssql.New(c, path),
|
||||
CitusSourceOps: citus.New(c, path),
|
||||
path: path,
|
||||
Client: c,
|
||||
PGSourceOps: postgres.New(c, path),
|
||||
MSSQLSourceOps: mssql.New(c, path),
|
||||
CitusSourceOps: citus.New(c, path),
|
||||
CockroachSourceOps: cockroach.New(c, path),
|
||||
path: path,
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
@ -348,6 +348,11 @@ func AddDatabaseToHasura(t TestingT, hgeEndpoint, sourceName, databaseKind strin
|
||||
return connectionStringMSSQL, teardownMSSQL
|
||||
|
||||
}
|
||||
if databaseKind == "cockroach" {
|
||||
connectionString, teardown := StartCockroachContainer(t)
|
||||
AddCockroachSourceToHasura(t, hgeEndpoint, connectionString, sourceName)
|
||||
return connectionString, teardown
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
@ -555,3 +560,115 @@ func AddCitusSourceToHasura(t TestingT, hasuraEndpoint, connectionString, source
|
||||
t.Fatalf("cannot add citus source to hasura: %s", string(body))
|
||||
}
|
||||
}
|
||||
|
||||
func StartHasuraWithCockroachSource(t TestingT, image string) (hasuraPort, sourceName string, teardown func()) {
|
||||
hasuraPort, hasuraTeardown := StartHasuraWithMetadataDatabase(t, image)
|
||||
sourceName = randomdata.SillyName()
|
||||
connectionStr, cocTeardown := StartCockroachContainer(t)
|
||||
|
||||
teardown = func() {
|
||||
hasuraTeardown()
|
||||
cocTeardown()
|
||||
}
|
||||
hasuraEndpoint := fmt.Sprintf("%s:%s", BaseURL, hasuraPort)
|
||||
AddCockroachSourceToHasura(t, hasuraEndpoint, connectionStr, sourceName)
|
||||
return hasuraPort, sourceName, teardown
|
||||
}
|
||||
|
||||
func StartCockroachContainer(t TestingT) (connectionString string, teardown func()) {
|
||||
user := "root"
|
||||
database := "defaultdb"
|
||||
pool, err := dockertest.NewPool("")
|
||||
if err != nil {
|
||||
t.Fatalf("Could not connect to Docker: %s", err)
|
||||
}
|
||||
uniqueName := getUniqueName(t)
|
||||
containerOpts := &dockertest.RunOptions{
|
||||
Name: fmt.Sprintf("%s-%s", uniqueName, "cockroach"),
|
||||
Repository: "cockroachdb/cockroach-unstable",
|
||||
Tag: "v22.2.0-beta.4",
|
||||
Env: []string{
|
||||
fmt.Sprintf("COCKROACH_USER=%s", user),
|
||||
fmt.Sprintf("COCKROACH_DATABASE=%s", database),
|
||||
},
|
||||
Cmd: []string{
|
||||
"start-single-node",
|
||||
"--insecure",
|
||||
"--accept-sql-without-tls",
|
||||
},
|
||||
ExposedPorts: []string{"26257",
|
||||
"8080", // port for cockroach console
|
||||
},
|
||||
Auth: getDockerAuthConfig(t),
|
||||
}
|
||||
container, err := pool.RunWithOptions(containerOpts)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not start CockroachDB container: %s", err)
|
||||
}
|
||||
var db *sql.DB
|
||||
if err = pool.Retry(func() error {
|
||||
var err error
|
||||
connectionString = fmt.Sprintf("postgresql://%s@%s:%s/%s?sslmode=disable", user, "0.0.0.0", container.GetPort("26257/tcp"), database)
|
||||
db, err = sql.Open("postgres", connectionString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return db.Ping()
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
teardown = func() {
|
||||
if err = pool.Purge(container); err != nil {
|
||||
t.Fatalf("Could not purge CockroachDB container: %s", err)
|
||||
}
|
||||
}
|
||||
connectionString = fmt.Sprintf("postgresql://%s@%s:%s/%s?sslmode=disable", user, DockerSwitchIP, container.GetPort("26257/tcp"), database)
|
||||
return connectionString, teardown
|
||||
}
|
||||
|
||||
func AddCockroachSourceToHasura(t TestingT, hasuraEndpoint, connectionString, sourceName string) {
|
||||
addSourceToHasura(t, hasuraEndpoint, connectionString, sourceName, "cockroach_add_source")
|
||||
}
|
||||
|
||||
func addSourceToHasura(t TestingT, hasuraEndpoint, connectionString, sourceName, requestType string) {
|
||||
url := fmt.Sprintf("%s/v1/metadata", hasuraEndpoint)
|
||||
body := fmt.Sprintf(`
|
||||
{
|
||||
"type": "%s",
|
||||
"args": {
|
||||
"name": "%s",
|
||||
"configuration": {
|
||||
"connection_info": {
|
||||
"database_url": "%s"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`, requestType, sourceName, connectionString)
|
||||
|
||||
fmt.Println(connectionString)
|
||||
fmt.Println(hasuraEndpoint)
|
||||
|
||||
req, err := http.NewRequest("POST", url, strings.NewReader(body))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
adminSecret := os.Getenv("HASURA_GRAPHQL_TEST_ADMIN_SECRET")
|
||||
if adminSecret != "" {
|
||||
req.Header.Set("x-hasura-admin-secret", adminSecret)
|
||||
}
|
||||
|
||||
r, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if r.StatusCode != http.StatusOK {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer r.Body.Close()
|
||||
t.Fatalf("cannot add %s source to hasura: %s", sourceName, string(body))
|
||||
}
|
||||
}
|
||||
|
@ -156,6 +156,8 @@ func MigrateAPI(c *gin.Context) {
|
||||
sourceKind = hasura.SourceKindMSSQL
|
||||
case "citus":
|
||||
sourceKind = hasura.SourceKindCitus
|
||||
case "cockroach":
|
||||
sourceKind = hasura.SourceKindCockroach
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, &Response{Code: "request_parse_error", Message: fmt.Sprintf("cannot determine database kind for '%v'", sourceName)})
|
||||
return
|
||||
|
@ -10,16 +10,13 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/hasura/graphql-engine/cli/v2/internal/statestore"
|
||||
|
||||
"github.com/hasura/graphql-engine/cli/v2/internal/statestore/settings"
|
||||
|
||||
"github.com/hasura/graphql-engine/cli/v2/internal/errors"
|
||||
"github.com/hasura/graphql-engine/cli/v2/internal/hasura"
|
||||
"github.com/hasura/graphql-engine/cli/v2/internal/statestore"
|
||||
"github.com/hasura/graphql-engine/cli/v2/internal/statestore/settings"
|
||||
"github.com/hasura/graphql-engine/cli/v2/migrate/database"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
|
||||
"github.com/hasura/graphql-engine/cli/v2/migrate/database"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@ -42,6 +39,7 @@ var (
|
||||
"select", "insert", "update", "delete", "count", "run_sql", "bulk",
|
||||
"mssql_select", "mssql_insert", "mssql_update", "mssql_delete", "mssql_count", "mssql_run_sql",
|
||||
"citus_select", "citus_insert", "citus_update", "citus_delete", "citus_count", "citus_run_sql",
|
||||
"cockroach_run_sql",
|
||||
}
|
||||
queryTypesMap = func() map[string]bool {
|
||||
var m = map[string]bool{}
|
||||
@ -80,6 +78,7 @@ type HasuraDB struct {
|
||||
pgSourceOps hasura.PGSourceOps
|
||||
mssqlSourceOps hasura.MSSQLSourceOps
|
||||
citusSourceOps hasura.CitusSourceOps
|
||||
cockroachSourceOps hasura.CockroachSourceOps
|
||||
genericQueryRequest hasura.GenericSend
|
||||
hasuraClient *hasura.Client
|
||||
migrationsStateStore statestore.MigrationsStateStore
|
||||
@ -264,6 +263,11 @@ func (h *HasuraDB) Run(migration io.Reader, fileType, fileName string) error {
|
||||
if err != nil {
|
||||
return errors.E(op, err)
|
||||
}
|
||||
case hasura.SourceKindCockroach:
|
||||
_, err := h.cockroachSourceOps.CockroachRunSQL(hasura.CockroachRunSQLInput(sqlInput))
|
||||
if err != nil {
|
||||
return errors.E(op, err)
|
||||
}
|
||||
|
||||
default:
|
||||
return errors.E(op, fmt.Errorf("unsupported source kind, source name: %v kind: %v", h.hasuraOpts.SourceName, h.hasuraOpts.SourceKind))
|
||||
@ -284,6 +288,7 @@ func (h *HasuraDB) Run(migration io.Reader, fileType, fileName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// responsible for deciding which request to send to v1/metadata and which to send to v2/query
|
||||
func sendMetadataMigrations(hasuradb *HasuraDB, requests []interface{}) error {
|
||||
var op errors.Op = "hasuradb.sendMetadataMigrations"
|
||||
var metadataRequests []interface{}
|
||||
|
@ -218,7 +218,7 @@ func GetFilePath(dir string) *nurl.URL {
|
||||
|
||||
func IsMigrationsSupported(kind hasura.SourceKind) bool {
|
||||
switch kind {
|
||||
case hasura.SourceKindMSSQL, hasura.SourceKindPG, hasura.SourceKindCitus:
|
||||
case hasura.SourceKindMSSQL, hasura.SourceKindPG, hasura.SourceKindCitus, hasura.SourceKindCockroach:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
Loading…
Reference in New Issue
Block a user