зеркало из https://github.com/github/vitess-gh.git
web/vtctld: Port schema manager to new UI.
This commit is contained in:
Родитель
34e4d370bb
Коммит
800c699a67
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -19,8 +19,9 @@
|
|||
</div>
|
||||
|
||||
<md-content ng-if="result.$resolved" class="md-padding">
|
||||
<h2 ng-bind="result.Error === undefined || result.Error ? 'Action Failed' : 'Action Succeeded'"></h2>
|
||||
<p ng-bind="result.Output"></p>
|
||||
<h2 ng-if="result.Error">Action Failed</h2>
|
||||
<h2 ng-if="!result.Error && !result.Output">Action Succeeded</h2>
|
||||
<pre ng-bind="result.Output"></pre>
|
||||
</md-content>
|
||||
|
||||
</md-dialog-content>
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -76,3 +76,7 @@ md-toolbar h1 {
|
|||
font-size: 1.5em;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
textarea.code {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
});
|
||||
|
|
|
@ -69,6 +69,7 @@
|
|||
<script src="./shard.js"></script>
|
||||
<script src="./topo.js"></script>
|
||||
<script src="./actions.js"></script>
|
||||
<script src="./schema.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1 +1,20 @@
|
|||
<md-content class="md-padding">
|
||||
|
||||
<md-tabs md-border-bottom md-center-tabs="false">
|
||||
<md-tab label="Apply Schema">
|
||||
|
||||
<md-select placeholder="Select Keyspace" ng-model="schemaChange.Keyspace">
|
||||
<md-option ng-repeat="keyspace in keyspaces" value="{{keyspace}}">{{keyspace}}</md-option>
|
||||
</md-select>
|
||||
|
||||
<md-input-container flex>
|
||||
<label>Schema Change SQL</label>
|
||||
<textarea ng-model="schemaChange.SQL" class="code"></textarea>
|
||||
</md-input-container>
|
||||
|
||||
<md-button class="md-primary" ng-click="submitSchema($event)">Submit</md-button>
|
||||
|
||||
</md-tab>
|
||||
</md-tabs>
|
||||
|
||||
</md-content>
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
};
|
||||
});
|
Загрузка…
Ссылка в новой задаче