From 800c699a67cacd8bcc54da02214c9eb04a3de593 Mon Sep 17 00:00:00 2001 From: Anthony Yeh Date: Wed, 22 Jul 2015 17:29:43 -0700 Subject: [PATCH] web/vtctld: Port schema manager to new UI. --- go/cmd/vtctld/api.go | 35 ++++++++++++++++++++++++++++++++++- web/vtctld/action-dialog.html | 5 +++-- web/vtctld/actions.js | 8 +++++++- web/vtctld/app.css | 4 ++++ web/vtctld/app.js | 8 ++++---- web/vtctld/index.html | 1 + web/vtctld/schema.html | 19 +++++++++++++++++++ web/vtctld/schema.js | 33 +++++++++++++++++++++++++++++++++ 8 files changed, 105 insertions(+), 8 deletions(-) create mode 100644 web/vtctld/schema.js diff --git a/go/cmd/vtctld/api.go b/go/cmd/vtctld/api.go index f60f5da415..c4b5332046 100644 --- a/go/cmd/vtctld/api.go +++ b/go/cmd/vtctld/api.go @@ -4,17 +4,24 @@ import ( "encoding/json" "errors" "fmt" + "io/ioutil" "net/http" "reflect" "strings" + "github.com/youtube/vitess/go/vt/schemamanager" + "github.com/youtube/vitess/go/vt/tabletmanager/tmclient" "github.com/youtube/vitess/go/vt/topo" "golang.org/x/net/context" ) // This file implements a REST-style API for the vtctld web interface. -const apiPrefix = "/api/" +const ( + apiPrefix = "/api/" + + jsonContentType = "application/json; charset=utf-8" +) func handleCollection(collection string, getFunc func(*http.Request) (interface{}, error)) { http.HandleFunc(apiPrefix+collection+"/", func(w http.ResponseWriter, r *http.Request) { @@ -31,6 +38,7 @@ func handleCollection(collection string, getFunc func(*http.Request) (interface{ // JSON marshals a nil slice as "null", but we prefer "[]". if val := reflect.ValueOf(obj); val.Kind() == reflect.Slice && val.IsNil() { + w.Header().Set("Content-Type", jsonContentType) w.Write([]byte("[]")) return } @@ -41,6 +49,7 @@ func handleCollection(collection string, getFunc func(*http.Request) (interface{ httpErrorf(w, r, "json error: %v", err) return } + w.Header().Set("Content-Type", jsonContentType) w.Write(data) }) } @@ -60,6 +69,14 @@ func getItemPath(url string) string { return parts[1] } +func unmarshalRequest(r *http.Request, v interface{}) error { + data, err := ioutil.ReadAll(r.Body) + if err != nil { + return err + } + return json.Unmarshal(data, v) +} + func initAPI(ctx context.Context, ts topo.Server, actions *ActionRepository) { tabletHealthCache := newTabletHealthCache(ts) @@ -206,4 +223,20 @@ func initAPI(ctx context.Context, ts topo.Server, actions *ActionRepository) { ep, _, err := ts.GetEndPoints(ctx, parts[0], parts[1], parts[2], topo.TabletType(parts[3])) return ep, err }) + + // Schema Change + http.HandleFunc(apiPrefix+"schema/apply", func(w http.ResponseWriter, r *http.Request) { + req := struct{ Keyspace, SQL string }{} + if err := unmarshalRequest(r, &req); err != nil { + httpErrorf(w, r, "can't unmarshal request: %v", err) + return + } + + executor := schemamanager.NewTabletExecutor( + tmclient.NewTabletManagerClient(), + ts) + + schemamanager.Run(ctx, + schemamanager.NewUIController(req.SQL, req.Keyspace, w), executor) + }) } diff --git a/web/vtctld/action-dialog.html b/web/vtctld/action-dialog.html index c22a9d39a3..7c582d8ee2 100644 --- a/web/vtctld/action-dialog.html +++ b/web/vtctld/action-dialog.html @@ -19,8 +19,9 @@ -

-

+

Action Failed

+

Action Succeeded

+

 
diff --git a/web/vtctld/actions.js b/web/vtctld/actions.js index 5e4365985d..439592a95b 100644 --- a/web/vtctld/actions.js +++ b/web/vtctld/actions.js @@ -51,7 +51,7 @@ app.factory('actions', function($mdDialog, keyspaces, shards, tablets) { keyspace: keyspace, shard: shard, action: action.name - }, ''); + }, ''); showResult(ev, action, result); }); }; @@ -66,6 +66,12 @@ app.factory('actions', function($mdDialog, keyspaces, shards, tablets) { }); }; + svc.applyFunc = function(ev, action, func) { + confirm(ev, action, function() { + showResult(ev, action, func()); + }); + }; + svc.label = function(action) { return action.confirm ? action.title + '...' : action.title; }; diff --git a/web/vtctld/app.css b/web/vtctld/app.css index ff82be860d..9313f0e7fd 100644 --- a/web/vtctld/app.css +++ b/web/vtctld/app.css @@ -76,3 +76,7 @@ md-toolbar h1 { font-size: 1.5em; font-weight: 900; } + +textarea.code { + font-family: monospace; +} diff --git a/web/vtctld/app.js b/web/vtctld/app.js index b951b2e2ff..4ab24edc6c 100644 --- a/web/vtctld/app.js +++ b/web/vtctld/app.js @@ -68,7 +68,10 @@ app.controller('AppCtrl', function($scope, $mdSidenav, $route, $location, $scope.routes = routes; $scope.refreshRoute = function() { - $route.current.locals.$scope.refreshData(); + if ($route.current && $route.current.locals.$scope.refreshData) + $route.current.locals.$scope.refreshData(); + else + $route.reload(); }; $scope.toggleNav = function() { $mdSidenav('left').toggle(); } @@ -85,6 +88,3 @@ app.controller('AppCtrl', function($scope, $mdSidenav, $route, $location, $location.path(path); }; }); - -app.controller('SchemaCtrl', function() { -}); diff --git a/web/vtctld/index.html b/web/vtctld/index.html index 8df86f252c..0e38050336 100644 --- a/web/vtctld/index.html +++ b/web/vtctld/index.html @@ -69,6 +69,7 @@ + diff --git a/web/vtctld/schema.html b/web/vtctld/schema.html index 8b13789179..dc86b08582 100644 --- a/web/vtctld/schema.html +++ b/web/vtctld/schema.html @@ -1 +1,20 @@ + + + + + + {{keyspace}} + + + + + + + + Submit + + + + + diff --git a/web/vtctld/schema.js b/web/vtctld/schema.js new file mode 100644 index 0000000000..2d3752d99a --- /dev/null +++ b/web/vtctld/schema.js @@ -0,0 +1,33 @@ +app.controller('SchemaCtrl', function($scope, $http, $mdDialog, + actions, keyspaces) { + $scope.schemaChange = {Keyspace: '', SQL: ''}; + + $scope.refreshData = function() { + $scope.keyspaces = keyspaces.query(); + }; + $scope.refreshData(); + + $scope.submitSchema = function(ev) { + var action = { + title: 'Apply Schema', + confirm: 'This will execute the provided SQL on all shards in the keyspace.' + }; + actions.applyFunc(ev, action, function() { + var result = {$resolved: false}; + + $http.post('/api/schema/apply', $scope.schemaChange) + .success(function(data) { + result.$resolved = true; + result.Output = data; + result.Error = false; + }) + .error(function(data) { + result.$resolved = true; + result.Output = data; + result.Error = true; + }); + + return result; + }); + }; +});