diff --git a/dgraph/cmd/alpha/admin.go b/dgraph/cmd/alpha/admin.go index e608ff473a3..271d12d4769 100644 --- a/dgraph/cmd/alpha/admin.go +++ b/dgraph/cmd/alpha/admin.go @@ -18,17 +18,11 @@ package alpha import ( "bytes" - "context" "fmt" - "io/ioutil" "net" "net/http" - "strconv" - "github.com/dgraph-io/dgraph/posting" - "github.com/dgraph-io/dgraph/worker" "github.com/dgraph-io/dgraph/x" - "github.com/golang/glog" ) // handlerInit does some standard checks. Returns false if something is wrong. @@ -46,114 +40,6 @@ func handlerInit(w http.ResponseWriter, r *http.Request, allowedMethods map[stri return true } -func drainingHandler(w http.ResponseWriter, r *http.Request) { - switch r.Method { - case http.MethodPut, http.MethodPost: - enableStr := r.URL.Query().Get("enable") - - enable, err := strconv.ParseBool(enableStr) - if err != nil { - x.SetStatus(w, x.ErrorInvalidRequest, - "Found invalid value for the enable parameter") - return - } - - x.UpdateDrainingMode(enable) - _, err = w.Write([]byte(fmt.Sprintf(`{"code": "Success",`+ - `"message": "draining mode has been set to %v"}`, enable))) - if err != nil { - glog.Errorf("Failed to write response: %v", err) - } - default: - w.WriteHeader(http.StatusMethodNotAllowed) - } -} - -func shutDownHandler(w http.ResponseWriter, r *http.Request) { - if !handlerInit(w, r, map[string]bool{ - http.MethodGet: true, - }) { - return - } - - close(worker.ShutdownCh) - w.Header().Set("Content-Type", "application/json") - x.Check2(w.Write([]byte(`{"code": "Success", "message": "Server is shutting down"}`))) -} - -func exportHandler(w http.ResponseWriter, r *http.Request) { - if !handlerInit(w, r, map[string]bool{ - http.MethodGet: true, - }) { - return - } - if err := r.ParseForm(); err != nil { - x.SetHttpStatus(w, http.StatusBadRequest, "Parse of export request failed.") - return - } - - format := worker.DefaultExportFormat - if vals, ok := r.Form["format"]; ok { - if len(vals) > 1 { - x.SetHttpStatus(w, http.StatusBadRequest, - "Only one export format may be specified.") - return - } - format = worker.NormalizeExportFormat(vals[0]) - if format == "" { - x.SetHttpStatus(w, http.StatusBadRequest, "Invalid export format.") - return - } - } - if err := worker.ExportOverNetwork(context.Background(), format); err != nil { - x.SetStatus(w, err.Error(), "Export failed.") - return - } - w.Header().Set("Content-Type", "application/json") - x.Check2(w.Write([]byte(`{"code": "Success", "message": "Export completed."}`))) -} - -func memoryLimitHandler(w http.ResponseWriter, r *http.Request) { - switch r.Method { - case http.MethodGet: - memoryLimitGetHandler(w, r) - case http.MethodPut: - memoryLimitPutHandler(w, r) - default: - w.WriteHeader(http.StatusMethodNotAllowed) - } -} - -func memoryLimitPutHandler(w http.ResponseWriter, r *http.Request) { - body, err := ioutil.ReadAll(r.Body) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - memoryMB, err := strconv.ParseFloat(string(body), 64) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - if err := worker.UpdateLruMb(memoryMB); err != nil { - w.WriteHeader(http.StatusBadRequest) - fmt.Fprint(w, err.Error()) - return - } - w.WriteHeader(http.StatusOK) -} - -func memoryLimitGetHandler(w http.ResponseWriter, r *http.Request) { - posting.Config.Lock() - memoryMB := posting.Config.AllottedMemory - posting.Config.Unlock() - - if _, err := fmt.Fprintln(w, memoryMB); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } -} - func ipInIPWhitelistRanges(ipString string) bool { ip := net.ParseIP(ipString) diff --git a/dgraph/cmd/alpha/admin_backup.go b/dgraph/cmd/alpha/admin_backup.go deleted file mode 100644 index 8919e0940da..00000000000 --- a/dgraph/cmd/alpha/admin_backup.go +++ /dev/null @@ -1,64 +0,0 @@ -// +build !oss - -/* - * Copyright 2018 Dgraph Labs, Inc. and Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package alpha - -import ( - "context" - "net/http" - - "github.com/dgraph-io/dgraph/protos/pb" - "github.com/dgraph-io/dgraph/worker" - "github.com/dgraph-io/dgraph/x" -) - -func init() { - http.HandleFunc("/admin/backup", backupHandler) -} - -// backupHandler handles backup requests coming from the HTTP endpoint. -func backupHandler(w http.ResponseWriter, r *http.Request) { - if !handlerInit(w, r, map[string]bool{ - http.MethodPost: true, - }) { - return - } - - destination := r.FormValue("destination") - accessKey := r.FormValue("access_key") - secretKey := r.FormValue("secret_key") - sessionToken := r.FormValue("session_token") - anonymous := r.FormValue("anonymous") == "true" - forceFull := r.FormValue("force_full") == "true" - - req := pb.BackupRequest{ - Destination: destination, - AccessKey: accessKey, - SecretKey: secretKey, - SessionToken: sessionToken, - Anonymous: anonymous, - } - - if err := worker.ProcessBackupRequest(context.Background(), &req, forceFull); err != nil { - x.SetStatus(w, err.Error(), "Backup failed.") - return - } - - w.Header().Set("Content-Type", "application/json") - x.Check2(w.Write([]byte(`{"code": "Success", "message": "Backup completed."}`))) -} diff --git a/dgraph/cmd/alpha/http_test.go b/dgraph/cmd/alpha/http_test.go index ff74c842e88..1befd09d203 100644 --- a/dgraph/cmd/alpha/http_test.go +++ b/dgraph/cmd/alpha/http_test.go @@ -736,14 +736,29 @@ func TestHealth(t *testing.T) { } func setDrainingMode(t *testing.T, enable bool) { - url := fmt.Sprintf("%s/admin/draining?enable=%v", addr, enable) - req, err := http.NewRequest("POST", url, nil) - require.NoError(t, err, "Error while creating post request for %s", url) - client := &http.Client{} - resp, err := client.Do(req) - require.NoError(t, err, "Error while sending post request to %s", url) - status := resp.StatusCode - require.Equal(t, http.StatusOK, status, "Unexpected status code: %v", status) + drainingRequest := `mutation drain($enable: Boolean) { + draining(input: {enable: $enable}) { + response { + code + } + } + }` + adminUrl := fmt.Sprintf("%s/admin", addr) + params := testutil.GraphQLParams{ + Query: drainingRequest, + Variables: map[string]interface{}{"enable": enable}, + } + b, err := json.Marshal(params) + require.NoError(t, err) + + resp, err := http.Post(adminUrl, "application/json", bytes.NewBuffer(b)) + require.NoError(t, err) + + defer resp.Body.Close() + b, err = ioutil.ReadAll(resp.Body) + require.NoError(t, err) + require.JSONEq(t, `{"data":{"draining":{"response":{"code":"Success"}}}}`, + string(b)) } func TestDrainingMode(t *testing.T) { diff --git a/dgraph/cmd/alpha/run.go b/dgraph/cmd/alpha/run.go index bfa405720d3..0ec94074dde 100644 --- a/dgraph/cmd/alpha/run.go +++ b/dgraph/cmd/alpha/run.go @@ -447,11 +447,6 @@ func setupServer(closer *y.Closer) { // TODO: Figure out what this is for? http.HandleFunc("/debug/store", storeStatsHandler) - http.HandleFunc("/admin/shutdown", shutDownHandler) - http.HandleFunc("/admin/draining", drainingHandler) - http.HandleFunc("/admin/export", exportHandler) - http.HandleFunc("/admin/config/lru_mb", memoryLimitHandler) - introspection := Alpha.Conf.GetBool("graphql_introspection") mainServer, adminServer := admin.NewServers(introspection, closer) http.Handle("/graphql", mainServer.HTTPHandler()) diff --git a/dgraph/cmd/bulk/systest/test-bulk-schema.sh b/dgraph/cmd/bulk/systest/test-bulk-schema.sh index 52ee675f7da..12868deb5eb 100755 --- a/dgraph/cmd/bulk/systest/test-bulk-schema.sh +++ b/dgraph/cmd/bulk/systest/test-bulk-schema.sh @@ -118,7 +118,7 @@ function QuerySchema function DoExport { INFO "running export" - docker exec alpha1 curl -Ss localhost:$HTTP_PORT/admin/export &>/dev/null + docker exec alpha1 curl -Ss -H "Content-Type: application/json" localhost:$HTTP_PORT/admin -XPOST -d '{ "query": "mutation { export(input: {format: \"rdf\"}) { response { code message } }}" }' &>/dev/null sleep 2 docker cp alpha1:/data/alpha1/export . sleep 1 diff --git a/ee/backup/tests/filesystem/backup_test.go b/ee/backup/tests/filesystem/backup_test.go index be3b37077a8..478a789ff27 100644 --- a/ee/backup/tests/filesystem/backup_test.go +++ b/ee/backup/tests/filesystem/backup_test.go @@ -16,12 +16,13 @@ package main import ( + "bytes" "context" + "encoding/json" "fmt" "io/ioutil" "math" "net/http" - "net/url" "os" "path/filepath" "strings" @@ -210,15 +211,27 @@ func runBackup(t *testing.T, numExpectedFiles, numExpectedDirs int) []string { func runBackupInternal(t *testing.T, forceFull bool, numExpectedFiles, numExpectedDirs int) []string { - forceFullStr := "false" - if forceFull { - forceFullStr = "true" + backupRequest := `mutation backup($dst: String!, $ff: Boolean!) { + backup(input: {destination: $dst, forceFull: $ff}) { + response { + code + message + } + } + }` + + adminUrl := "http://localhost:8180/admin" + params := testutil.GraphQLParams{ + Query: backupRequest, + Variables: map[string]interface{}{ + "dst": alphaBackupDir, + "ff": forceFull, + }, } + b, err := json.Marshal(params) + require.NoError(t, err) - resp, err := http.PostForm("http://localhost:8180/admin/backup", url.Values{ - "destination": []string{alphaBackupDir}, - "force_full": []string{forceFullStr}, - }) + resp, err := http.Post(adminUrl, "application/json", bytes.NewBuffer(b)) require.NoError(t, err) defer resp.Body.Close() buf, err := ioutil.ReadAll(resp.Body) diff --git a/ee/backup/tests/minio-large/backup_test.go b/ee/backup/tests/minio-large/backup_test.go index 32e540039d5..0403c21a936 100644 --- a/ee/backup/tests/minio-large/backup_test.go +++ b/ee/backup/tests/minio-large/backup_test.go @@ -16,12 +16,13 @@ package main import ( + "bytes" "context" + "encoding/json" "fmt" "io/ioutil" "math" "net/http" - "net/url" "os" "strings" "testing" @@ -139,9 +140,26 @@ func addTriples(t *testing.T, dg *dgo.Dgraph, numTriples int) { } func runBackup(t *testing.T) { - resp, err := http.PostForm("http://localhost:8180/admin/backup", url.Values{ - "destination": []string{backupDestination}, - }) + backupRequest := `mutation backup($dst: String!) { + backup(input: {destination: $dst}) { + response { + code + message + } + } + }` + + adminUrl := "http://localhost:8180/admin" + params := testutil.GraphQLParams{ + Query: backupRequest, + Variables: map[string]interface{}{ + "dst": backupDestination, + }, + } + b, err := json.Marshal(params) + require.NoError(t, err) + + resp, err := http.Post(adminUrl, "application/json", bytes.NewBuffer(b)) require.NoError(t, err) defer resp.Body.Close() buf, err := ioutil.ReadAll(resp.Body) diff --git a/ee/backup/tests/minio/backup_test.go b/ee/backup/tests/minio/backup_test.go index b8872299c09..7274ce292a0 100644 --- a/ee/backup/tests/minio/backup_test.go +++ b/ee/backup/tests/minio/backup_test.go @@ -16,12 +16,13 @@ package main import ( + "bytes" "context" + "encoding/json" "fmt" "io/ioutil" "math" "net/http" - "net/url" "os" "path/filepath" "strings" @@ -216,17 +217,29 @@ func runBackup(t *testing.T, numExpectedFiles, numExpectedDirs int) []string { func runBackupInternal(t *testing.T, forceFull bool, numExpectedFiles, numExpectedDirs int) []string { - forceFullStr := "false" - if forceFull { - forceFullStr = "true" + + backupRequest := `mutation backup($dst: String!, $ff: Boolean!) { + backup(input: {destination: $dst, forceFull: $ff}) { + response { + code + message + } + } + }` + + adminUrl := "http://localhost:8180/admin" + params := testutil.GraphQLParams{ + Query: backupRequest, + Variables: map[string]interface{}{ + "dst": backupDst, + "ff": forceFull, + }, } + b, err := json.Marshal(params) + require.NoError(t, err) - resp, err := http.PostForm("http://localhost:8180/admin/backup", url.Values{ - "destination": []string{backupDst}, - "force_full": []string{forceFullStr}, - }) + resp, err := http.Post(adminUrl, "application/json", bytes.NewBuffer(b)) require.NoError(t, err) - defer resp.Body.Close() buf, err := ioutil.ReadAll(resp.Body) require.NoError(t, err) require.Contains(t, string(buf), "Backup completed.") diff --git a/systest/cluster_setup_test.go b/systest/cluster_setup_test.go index 3a58f3348dd..532f0fa9601 100644 --- a/systest/cluster_setup_test.go +++ b/systest/cluster_setup_test.go @@ -17,6 +17,8 @@ package main import ( + "bytes" + "encoding/json" "fmt" "io/ioutil" "net/http" @@ -29,6 +31,7 @@ import ( "github.com/dgraph-io/dgo/v2" "github.com/dgraph-io/dgo/v2/protos/api" + "github.com/dgraph-io/dgraph/testutil" "github.com/dgraph-io/dgraph/x" "github.com/golang/glog" "github.com/pkg/errors" @@ -167,11 +170,20 @@ type matchExport struct { func matchExportCount(opts matchExport) error { // Now try and export data from second server. - resp, err := http.Get(fmt.Sprintf("http://localhost:%d/admin/export", opts.port)) + adminUrl := fmt.Sprintf("http://localhost:%d/admin", opts.port) + params := testutil.GraphQLParams{ + Query: testutil.ExportRequest, + } + b, err := json.Marshal(params) if err != nil { return err } - b, err := ioutil.ReadAll(resp.Body) + resp, err := http.Post(adminUrl, "application/json", bytes.NewBuffer(b)) + if err != nil { + return err + } + + b, err = ioutil.ReadAll(resp.Body) if err != nil { return err } diff --git a/systest/loader/loader_test.go b/systest/loader/loader_test.go index 75d9adc35c2..5a57f999385 100644 --- a/systest/loader/loader_test.go +++ b/systest/loader/loader_test.go @@ -17,6 +17,8 @@ package main import ( + "bytes" + "encoding/json" "fmt" "io/ioutil" "net/http" @@ -61,12 +63,40 @@ func TestLoaderXidmap(t *testing.T) { liveCmd.Stderr = os.Stdout require.NoError(t, liveCmd.Run()) - resp, err := http.Get(fmt.Sprintf("http://%s/admin/export", testutil.SockAddrHttp)) + exportRequest := `mutation { + export(input: {format: "rdf"}) { + response { + code + message + } + } + }` + + adminUrl := "http://" + testutil.SockAddrHttp + "/admin" + params := testutil.GraphQLParams{ + Query: exportRequest, + Variables: map[string]interface{}{"format": "json"}, + } + b, err := json.Marshal(params) require.NoError(t, err) - b, _ := ioutil.ReadAll(resp.Body) - expected := `{"code": "Success", "message": "Export completed."}` - require.Equal(t, expected, string(b)) + resp, err := http.Post(adminUrl, "application/json", bytes.NewBuffer(b)) + require.NoError(t, err) + defer resp.Body.Close() + + b, err = ioutil.ReadAll(resp.Body) + require.NoError(t, err) + expected := `{ + "data": { + "export": { + "response": { + "code": "Success", + "message": "Export completed." + } + } + } + }` + require.JSONEq(t, expected, string(b)) require.NoError(t, copyExportFiles(tmpDir)) diff --git a/testutil/graphql.go b/testutil/graphql.go index 90dc8cf9e52..55f2db695c1 100644 --- a/testutil/graphql.go +++ b/testutil/graphql.go @@ -25,6 +25,15 @@ import ( "github.com/stretchr/testify/require" ) +const ExportRequest = `mutation { + export(input: {format: "json"}) { + response { + code + message + } + } +}` + type GraphQLParams struct { Query string `json:"query"` Variables map[string]interface{} `json:"variables"` diff --git a/worker/export_test.go b/worker/export_test.go index 95de00917bd..bb20c181838 100644 --- a/worker/export_test.go +++ b/worker/export_test.go @@ -21,6 +21,7 @@ import ( "bytes" "compress/gzip" "context" + "encoding/json" "io/ioutil" "math" "net/http" @@ -334,26 +335,53 @@ func TestExportJson(t *testing.T) { checkExportSchema(t, schemaFileList) } +const exportRequest = `mutation export($format: String!) { + export(input: {format: $format}) { + response { + code + } + } +}` + func TestExportFormat(t *testing.T) { tmpdir, err := ioutil.TempDir("", "export") require.NoError(t, err) defer os.RemoveAll(tmpdir) - resp, err := http.Get("http://" + testutil.SockAddrHttp + "/admin/export?format=json") + adminUrl := "http://" + testutil.SockAddrHttp + "/admin" + params := testutil.GraphQLParams{ + Query: exportRequest, + Variables: map[string]interface{}{"format": "json"}, + } + b, err := json.Marshal(params) + require.NoError(t, err) + + resp, err := http.Post(adminUrl, "application/json", bytes.NewBuffer(b)) require.NoError(t, err) - require.Equal(t, resp.StatusCode, http.StatusOK) + testutil.RequireNoGraphQLErrors(t, resp) - resp, err = http.Get("http://" + testutil.SockAddrHttp + "/admin/export?format=rdf") + params.Variables["format"] = "rdf" + b, err = json.Marshal(params) + require.NoError(t, err) + + resp, err = http.Post(adminUrl, "application/json", bytes.NewBuffer(b)) + require.NoError(t, err) + testutil.RequireNoGraphQLErrors(t, resp) + + params.Variables["format"] = "xml" + b, err = json.Marshal(params) + require.NoError(t, err) + resp, err = http.Post(adminUrl, "application/json", bytes.NewBuffer(b)) require.NoError(t, err) - require.Equal(t, resp.StatusCode, http.StatusOK) - resp, err = http.Get("http://" + testutil.SockAddrHttp + "/admin/export?format=xml") + defer resp.Body.Close() + b, err = ioutil.ReadAll(resp.Body) require.NoError(t, err) - require.NotEqual(t, resp.StatusCode, http.StatusOK) - resp, err = http.Get("http://" + testutil.SockAddrHttp + "/admin/export?output=rdf") + var result *testutil.GraphQLResponse + err = json.Unmarshal(b, &result) require.NoError(t, err) - require.Equal(t, resp.StatusCode, http.StatusOK) + require.NotNil(t, result.Errors) } type skv struct {