// NewSchemaInfo creates a new SchemaInfo. func NewSchemaInfo( queryCacheSize int, statsPrefix string, endpoints map[string]string, reloadTime time.Duration, idleTimeout time.Duration, enablePublishStats bool, queryServiceStats *QueryServiceStats) *SchemaInfo { si := &SchemaInfo{ queries: cache.NewLRUCache(int64(queryCacheSize)), connPool: NewConnPool("", 2, idleTimeout, enablePublishStats, queryServiceStats), ticks: timer.NewTimer(reloadTime), endpoints: endpoints, reloadTime: reloadTime, } if enablePublishStats { stats.Publish(statsPrefix+"QueryCacheLength", stats.IntFunc(si.queries.Length)) stats.Publish(statsPrefix+"QueryCacheSize", stats.IntFunc(si.queries.Size)) stats.Publish(statsPrefix+"QueryCacheCapacity", stats.IntFunc(si.queries.Capacity)) stats.Publish(statsPrefix+"QueryCacheOldest", stats.StringFunc(func() string { return fmt.Sprintf("%v", si.queries.Oldest()) })) stats.Publish(statsPrefix+"SchemaReloadTime", stats.DurationFunc(si.ticks.Interval)) _ = stats.NewMultiCountersFunc(statsPrefix+"RowcacheStats", []string{"Table", "Stats"}, si.getRowcacheStats) _ = stats.NewMultiCountersFunc(statsPrefix+"RowcacheInvalidations", []string{"Table"}, si.getRowcacheInvalidations) _ = stats.NewMultiCountersFunc(statsPrefix+"QueryCounts", []string{"Table", "Plan"}, si.getQueryCount) _ = stats.NewMultiCountersFunc(statsPrefix+"QueryTimesNs", []string{"Table", "Plan"}, si.getQueryTime) _ = stats.NewMultiCountersFunc(statsPrefix+"QueryRowCounts", []string{"Table", "Plan"}, si.getQueryRowCount) _ = stats.NewMultiCountersFunc(statsPrefix+"QueryErrorCounts", []string{"Table", "Plan"}, si.getQueryErrorCount) } for _, ep := range endpoints { http.Handle(ep, si) } return si }
func NewSchemaInfo(queryCacheSize int, reloadTime time.Duration, idleTimeout time.Duration, sensitiveMode bool) *SchemaInfo { si := &SchemaInfo{ queryCacheSize: queryCacheSize, queries: cache.NewLRUCache(int64(queryCacheSize)), rules: NewQueryRules(), connPool: NewConnectionPool("", 2, idleTimeout), reloadTime: reloadTime, ticks: timer.NewTimer(reloadTime), sensitiveMode: sensitiveMode, } stats.Publish("QueryCacheLength", stats.IntFunc(si.queries.Length)) stats.Publish("QueryCacheSize", stats.IntFunc(si.queries.Size)) stats.Publish("QueryCacheCapacity", stats.IntFunc(si.queries.Capacity)) stats.Publish("QueryCacheOldest", stats.StringFunc(func() string { return fmt.Sprintf("%v", si.queries.Oldest()) })) stats.Publish("SchemaReloadTime", stats.DurationFunc(func() time.Duration { return si.reloadTime })) stats.Publish("TableStats", stats.NewMatrixFunc("Table", "Stats", si.getTableStats)) stats.Publish("TableInvalidations", stats.CountersFunc(si.getTableInvalidations)) stats.Publish("QueryCounts", stats.NewMatrixFunc("Table", "Plan", si.getQueryCount)) stats.Publish("QueryTimesNs", stats.NewMatrixFunc("Table", "Plan", si.getQueryTime)) stats.Publish("QueryRowCounts", stats.NewMatrixFunc("Table", "Plan", si.getQueryRowCount)) stats.Publish("QueryErrorCounts", stats.NewMatrixFunc("Table", "Plan", si.getQueryErrorCount)) // query_plans cannot be shown in sensitive mode if !si.sensitiveMode { http.Handle("/debug/query_plans", si) } http.Handle("/debug/query_stats", si) http.Handle("/debug/table_stats", si) http.Handle("/debug/schema", si) return si }
// NewMemcacheStats creates a new MemcacheStats. // main, slabs and items specify the categories of stats that need to be exported. func NewMemcacheStats( statsPrefix string, refreshFreq time.Duration, flags int64, queryServiceStats *QueryServiceStats, statsFunc RetrieveCacheStats) *MemcacheStats { memstats := &MemcacheStats{ ticks: timer.NewTimer(refreshFreq), statsPrefix: statsPrefix, statsFunc: statsFunc, main: make(map[string]string), slabs: make(map[string]map[string]int64), items: make(map[string]map[string]int64), queryServiceStats: queryServiceStats, flags: flags, } if flags&enableMain > 0 { memstats.publishMainStats() } if flags&enableSlabs > 0 { memstats.publishSlabsStats() } if flags*enableItems > 0 { memstats.publishItemsStats() } return memstats }
func (agent *ActionAgent) initHealthCheck() { if !agent.IsRunningHealthCheck() { log.Infof("No target_tablet_type specified, disabling any health check") return } tt, err := topoproto.ParseTabletType(*targetTabletType) if err != nil { log.Fatalf("Invalid target tablet type %v: %v", *targetTabletType, err) } log.Infof("Starting periodic health check every %v with target_tablet_type=%v", *healthCheckInterval, *targetTabletType) t := timer.NewTimer(*healthCheckInterval) servenv.OnTermSync(func() { // When we enter lameduck mode, we want to not call // the health check any more. After this returns, we // are guaranteed to not call it. log.Info("Stopping periodic health check timer") t.Stop() // Now we can finish up and force ourselves to not healthy. agent.terminateHealthChecks(tt) }) t.Start(func() { agent.runHealthCheck(tt) }) t.Trigger() }
func NewSchemaInfo(queryCacheSize int, reloadTime time.Duration, idleTimeout time.Duration) *SchemaInfo { si := &SchemaInfo{ queries: cache.NewLRUCache(int64(queryCacheSize)), rules: NewQueryRules(), connPool: dbconnpool.NewConnectionPool("", 2, idleTimeout), ticks: timer.NewTimer(reloadTime), } stats.Publish("QueryCacheLength", stats.IntFunc(si.queries.Length)) stats.Publish("QueryCacheSize", stats.IntFunc(si.queries.Size)) stats.Publish("QueryCacheCapacity", stats.IntFunc(si.queries.Capacity)) stats.Publish("QueryCacheOldest", stats.StringFunc(func() string { return fmt.Sprintf("%v", si.queries.Oldest()) })) stats.Publish("SchemaReloadTime", stats.DurationFunc(si.ticks.Interval)) _ = stats.NewMultiCountersFunc("TableStats", []string{"Table", "Stats"}, si.getTableStats) _ = stats.NewMultiCountersFunc("TableInvalidations", []string{"Table"}, si.getTableInvalidations) _ = stats.NewMultiCountersFunc("QueryCounts", []string{"Table", "Plan"}, si.getQueryCount) _ = stats.NewMultiCountersFunc("QueryTimesNs", []string{"Table", "Plan"}, si.getQueryTime) _ = stats.NewMultiCountersFunc("QueryRowCounts", []string{"Table", "Plan"}, si.getQueryRowCount) _ = stats.NewMultiCountersFunc("QueryErrorCounts", []string{"Table", "Plan"}, si.getQueryErrorCount) http.Handle("/debug/query_plans", si) http.Handle("/debug/query_stats", si) http.Handle("/debug/table_stats", si) http.Handle("/debug/schema", si) return si }
// NewTxPool creates a new TxPool. It's not operational until it's Open'd. func NewTxPool( name string, txStatsPrefix string, capacity int, timeout time.Duration, idleTimeout time.Duration, enablePublishStats bool, qStats *QueryServiceStats, checker MySQLChecker) *TxPool { txStatsName := "" if enablePublishStats { txStatsName = txStatsPrefix + "Transactions" } axp := &TxPool{ pool: NewConnPool(name, capacity, idleTimeout, enablePublishStats, qStats, checker), activePool: pools.NewNumbered(), lastID: sync2.NewAtomicInt64(time.Now().UnixNano()), timeout: sync2.NewAtomicDuration(timeout), ticks: timer.NewTimer(timeout / 10), txStats: stats.NewTimings(txStatsName), checker: checker, queryServiceStats: qStats, } // Careful: pool also exports name+"xxx" vars, // but we know it doesn't export Timeout. if enablePublishStats { stats.Publish(name+"Timeout", stats.DurationFunc(axp.timeout.Get)) } return axp }
func NewActivePool(queryTimeout, idleTimeout time.Duration) *ActivePool { return &ActivePool{ pool: pools.NewNumbered(), timeout: sync2.AtomicDuration(queryTimeout), connPool: NewConnectionPool(1, idleTimeout), ticks: timer.NewTimer(queryTimeout / 10), } }
func NewActiveTxPool(timeout time.Duration) *ActiveTxPool { return &ActiveTxPool{ pool: pools.NewNumbered(), lastId: sync2.AtomicInt64(time.Now().UnixNano()), timeout: sync2.AtomicDuration(timeout), ticks: timer.NewTimer(timeout / 10), txStats: stats.NewTimings("Transactions"), } }
func newLongPollingManager(m *Manager) *longPollingManager { lpm := &longPollingManager{ manager: m, connections: make(map[int]*longPollingConnection), } t := timer.NewTimer(dropTimeout) t.Start(func() { lpm.checkStaleConnections() }) return lpm }
func NewSchemaInfo(queryCacheSize int, reloadTime time.Duration, idleTimeout time.Duration) *SchemaInfo { si := &SchemaInfo{ queryCacheSize: queryCacheSize, queries: cache.NewLRUCache(uint64(queryCacheSize)), rules: NewQueryRules(), connPool: NewConnectionPool(2, idleTimeout), reloadTime: reloadTime, ticks: timer.NewTimer(reloadTime), } http.Handle("/debug/query_plans", si) http.Handle("/debug/query_stats", si) http.Handle("/debug/table_stats", si) return si }
func NewActivePool(name string, queryTimeout time.Duration, connKiller *ConnectionKiller) *ActivePool { ap := &ActivePool{ pool: pools.NewNumbered(), timeout: sync2.AtomicDuration(queryTimeout), ticks: timer.NewTimer(queryTimeout / 10), connKiller: connKiller, } stats.Publish(name+"Size", stats.IntFunc(ap.pool.Size)) stats.Publish( name+"Timeout", stats.DurationFunc(func() time.Duration { return ap.timeout.Get() }), ) return ap }
func NewActiveTxPool(name string, timeout time.Duration) *ActiveTxPool { axp := &ActiveTxPool{ pool: pools.NewNumbered(), lastId: sync2.AtomicInt64(time.Now().UnixNano()), timeout: sync2.AtomicDuration(timeout), ticks: timer.NewTimer(timeout / 10), txStats: stats.NewTimings("Transactions"), } stats.Publish(name+"Size", stats.IntFunc(axp.pool.Size)) stats.Publish( name+"Timeout", stats.DurationFunc(func() time.Duration { return axp.timeout.Get() }), ) return axp }
func NewTxPool(name string, capacity int, timeout, poolTimeout, idleTimeout time.Duration) *TxPool { axp := &TxPool{ pool: dbconnpool.NewConnectionPool(name, capacity, idleTimeout), activePool: pools.NewNumbered(), lastId: sync2.AtomicInt64(time.Now().UnixNano()), timeout: sync2.AtomicDuration(timeout), poolTimeout: sync2.AtomicDuration(poolTimeout), ticks: timer.NewTimer(timeout / 10), txStats: stats.NewTimings("Transactions"), } // Careful: pool also exports name+"xxx" vars, // but we know it doesn't export Timeout. stats.Publish(name+"Timeout", stats.DurationFunc(axp.timeout.Get)) stats.Publish(name+"PoolTimeout", stats.DurationFunc(axp.poolTimeout.Get)) return axp }
// NewMemcacheStats creates a new MemcacheStats based on given CachePool. // main, slabs and items specify the categories of stats that need to be exported. func NewMemcacheStats(cachePool *CachePool, main, slabs, items bool) *MemcacheStats { s := &MemcacheStats{ cachePool: cachePool, ticks: timer.NewTimer(10 * time.Second), } if main { s.publishMainStats() } if slabs { s.publishSlabsStats() } if items { s.publishItemsStats() } return s }
// initHealthCheck will start the health check background go routine, // and configure the healthcheck shutdown. It is only run by NewActionAgent // for real vttablet agents (not by tests, nor vtcombo). func (agent *ActionAgent) initHealthCheck() { registerReplicationReporter(agent) log.Infof("Starting periodic health check every %v", *healthCheckInterval) t := timer.NewTimer(*healthCheckInterval) servenv.OnTermSync(func() { // When we enter lameduck mode, we want to not call // the health check any more. After this returns, we // are guaranteed to not call it. log.Info("Stopping periodic health check timer") t.Stop() // Now we can finish up and force ourselves to not healthy. agent.terminateHealthChecks() }) t.Start(func() { agent.runHealthCheck() }) t.Trigger() }
func (agent *ActionAgent) initHeathCheck() { if !agent.IsRunningHealthCheck() { log.Infof("No target_tablet_type specified, disabling any health check") return } log.Infof("Starting periodic health check every %v with target_tablet_type=%v", *healthCheckInterval, *targetTabletType) t := timer.NewTimer(*healthCheckInterval) servenv.OnTerm(func() { // When we enter lameduck mode, we want to not call // the health check any more. After this returns, we // are guaranteed to not call it. log.Info("Stopping periodic health check timer") t.Stop() // Now we can finish up and force ourselves to not healthy. agent.terminateHealthChecks(topo.TabletType(*targetTabletType), *lockTimeout) }) t.Start(func() { agent.runHealthCheck(topo.TabletType(*targetTabletType), *lockTimeout) }) }
func initSchema() { // Start schema manager service if needed. if *schemaChangeDir != "" { interval := 60 if *schemaChangeCheckInterval > 0 { interval = *schemaChangeCheckInterval } timer := timer.NewTimer(time.Duration(interval) * time.Second) controllerFactory, err := schemamanager.GetControllerFactory(*schemaChangeController) if err != nil { log.Fatalf("unable to get a controller factory, error: %v", err) } timer.Start(func() { controller, err := controllerFactory(map[string]string{ schemamanager.SchemaChangeDirName: *schemaChangeDir, schemamanager.SchemaChangeUser: *schemaChangeUser, }) if err != nil { log.Errorf("failed to get controller, error: %v", err) return } ctx := context.Background() err = schemamanager.Run( ctx, controller, schemamanager.NewTabletExecutor( tmclient.NewTabletManagerClient(), ts), ) if err != nil { log.Errorf("Schema change failed, error: %v", err) } }) servenv.OnClose(func() { timer.Stop() }) } }
func main() { flag.Parse() servenv.Init() defer servenv.Close() ts = topo.GetServer() defer topo.CloseServers() actionRepo = NewActionRepository(ts) // keyspace actions actionRepo.RegisterKeyspaceAction("ValidateKeyspace", func(ctx context.Context, wr *wrangler.Wrangler, keyspace string, r *http.Request) (string, error) { return "", wr.ValidateKeyspace(ctx, keyspace, false) }) actionRepo.RegisterKeyspaceAction("ValidateSchemaKeyspace", func(ctx context.Context, wr *wrangler.Wrangler, keyspace string, r *http.Request) (string, error) { return "", wr.ValidateSchemaKeyspace(ctx, keyspace, nil, false) }) actionRepo.RegisterKeyspaceAction("ValidateVersionKeyspace", func(ctx context.Context, wr *wrangler.Wrangler, keyspace string, r *http.Request) (string, error) { return "", wr.ValidateVersionKeyspace(ctx, keyspace) }) actionRepo.RegisterKeyspaceAction("ValidatePermissionsKeyspace", func(ctx context.Context, wr *wrangler.Wrangler, keyspace string, r *http.Request) (string, error) { return "", wr.ValidatePermissionsKeyspace(ctx, keyspace) }) // shard actions actionRepo.RegisterShardAction("ValidateShard", func(ctx context.Context, wr *wrangler.Wrangler, keyspace, shard string, r *http.Request) (string, error) { return "", wr.ValidateShard(ctx, keyspace, shard, false) }) actionRepo.RegisterShardAction("ValidateSchemaShard", func(ctx context.Context, wr *wrangler.Wrangler, keyspace, shard string, r *http.Request) (string, error) { return "", wr.ValidateSchemaShard(ctx, keyspace, shard, nil, false) }) actionRepo.RegisterShardAction("ValidateVersionShard", func(ctx context.Context, wr *wrangler.Wrangler, keyspace, shard string, r *http.Request) (string, error) { return "", wr.ValidateVersionShard(ctx, keyspace, shard) }) actionRepo.RegisterShardAction("ValidatePermissionsShard", func(ctx context.Context, wr *wrangler.Wrangler, keyspace, shard string, r *http.Request) (string, error) { return "", wr.ValidatePermissionsShard(ctx, keyspace, shard) }) // tablet actions actionRepo.RegisterTabletAction("Ping", "", func(ctx context.Context, wr *wrangler.Wrangler, tabletAlias *topodatapb.TabletAlias, r *http.Request) (string, error) { ti, err := wr.TopoServer().GetTablet(ctx, tabletAlias) if err != nil { return "", err } return "", wr.TabletManagerClient().Ping(ctx, ti) }) actionRepo.RegisterTabletAction("RefreshState", acl.ADMIN, func(ctx context.Context, wr *wrangler.Wrangler, tabletAlias *topodatapb.TabletAlias, r *http.Request) (string, error) { ti, err := wr.TopoServer().GetTablet(ctx, tabletAlias) if err != nil { return "", err } return "", wr.TabletManagerClient().RefreshState(ctx, ti) }) actionRepo.RegisterTabletAction("DeleteTablet", acl.ADMIN, func(ctx context.Context, wr *wrangler.Wrangler, tabletAlias *topodatapb.TabletAlias, r *http.Request) (string, error) { return "", wr.DeleteTablet(ctx, tabletAlias, false, false) }) actionRepo.RegisterTabletAction("ReloadSchema", acl.ADMIN, func(ctx context.Context, wr *wrangler.Wrangler, tabletAlias *topodatapb.TabletAlias, r *http.Request) (string, error) { return "", wr.ReloadSchema(ctx, tabletAlias) }) // Anything unrecognized gets redirected to the main app page. http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, appPrefix, http.StatusFound) }) // Serve the static files for the vtctld web app. http.HandleFunc(appPrefix, func(w http.ResponseWriter, r *http.Request) { // Strip the prefix. parts := strings.SplitN(r.URL.Path, "/", 3) if len(parts) != 3 { http.NotFound(w, r) return } rest := parts[2] if rest == "" { rest = "index.html" } http.ServeFile(w, r, path.Join(*webDir, rest)) }) // Serve the REST API for the vtctld web app. initAPI(context.Background(), ts, actionRepo) // redirects for explorers http.HandleFunc("/explorers/redirect", func(w http.ResponseWriter, r *http.Request) { if explorer == nil { http.Error(w, "no explorer configured", http.StatusInternalServerError) return } if err := r.ParseForm(); err != nil { httpErrorf(w, r, "cannot parse form: %s", err) return } target, err := handleExplorerRedirect(context.Background(), ts, r) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } http.Redirect(w, r, target, http.StatusFound) }) // Start schema manager service. if *schemaChangeDir != "" { interval := 60 if *schemaChangeCheckInterval > 0 { interval = *schemaChangeCheckInterval } timer := timer.NewTimer(time.Duration(interval) * time.Second) controllerFactory, err := schemamanager.GetControllerFactory(*schemaChangeController) if err != nil { log.Fatalf("unable to get a controller factory, error: %v", err) } timer.Start(func() { controller, err := controllerFactory(map[string]string{ schemamanager.SchemaChangeDirName: *schemaChangeDir, schemamanager.SchemaChangeUser: *schemaChangeUser, }) if err != nil { log.Errorf("failed to get controller, error: %v", err) return } ctx := context.Background() err = schemamanager.Run( ctx, controller, schemamanager.NewTabletExecutor( tmclient.NewTabletManagerClient(), ts), ) if err != nil { log.Errorf("Schema change failed, error: %v", err) } }) servenv.OnClose(func() { timer.Stop() }) } servenv.RunDefault() }
func main() { flag.Parse() servenv.Init() defer servenv.Close() templateLoader = NewTemplateLoader(*templateDir, *debug) ts = topo.GetServer() defer topo.CloseServers() actionRepo = NewActionRepository(ts) // keyspace actions actionRepo.RegisterKeyspaceAction("ValidateKeyspace", func(ctx context.Context, wr *wrangler.Wrangler, keyspace string, r *http.Request) (string, error) { return "", wr.ValidateKeyspace(ctx, keyspace, false) }) actionRepo.RegisterKeyspaceAction("ValidateSchemaKeyspace", func(ctx context.Context, wr *wrangler.Wrangler, keyspace string, r *http.Request) (string, error) { return "", wr.ValidateSchemaKeyspace(ctx, keyspace, nil, false) }) actionRepo.RegisterKeyspaceAction("ValidateVersionKeyspace", func(ctx context.Context, wr *wrangler.Wrangler, keyspace string, r *http.Request) (string, error) { return "", wr.ValidateVersionKeyspace(ctx, keyspace) }) actionRepo.RegisterKeyspaceAction("ValidatePermissionsKeyspace", func(ctx context.Context, wr *wrangler.Wrangler, keyspace string, r *http.Request) (string, error) { return "", wr.ValidatePermissionsKeyspace(ctx, keyspace) }) // shard actions actionRepo.RegisterShardAction("ValidateShard", func(ctx context.Context, wr *wrangler.Wrangler, keyspace, shard string, r *http.Request) (string, error) { return "", wr.ValidateShard(ctx, keyspace, shard, false) }) actionRepo.RegisterShardAction("ValidateSchemaShard", func(ctx context.Context, wr *wrangler.Wrangler, keyspace, shard string, r *http.Request) (string, error) { return "", wr.ValidateSchemaShard(ctx, keyspace, shard, nil, false) }) actionRepo.RegisterShardAction("ValidateVersionShard", func(ctx context.Context, wr *wrangler.Wrangler, keyspace, shard string, r *http.Request) (string, error) { return "", wr.ValidateVersionShard(ctx, keyspace, shard) }) actionRepo.RegisterShardAction("ValidatePermissionsShard", func(ctx context.Context, wr *wrangler.Wrangler, keyspace, shard string, r *http.Request) (string, error) { return "", wr.ValidatePermissionsShard(ctx, keyspace, shard) }) // tablet actions actionRepo.RegisterTabletAction("Ping", "", func(ctx context.Context, wr *wrangler.Wrangler, tabletAlias *pb.TabletAlias, r *http.Request) (string, error) { ti, err := wr.TopoServer().GetTablet(ctx, tabletAlias) if err != nil { return "", err } return "", wr.TabletManagerClient().Ping(ctx, ti) }) actionRepo.RegisterTabletAction("RefreshState", acl.ADMIN, func(ctx context.Context, wr *wrangler.Wrangler, tabletAlias *pb.TabletAlias, r *http.Request) (string, error) { ti, err := wr.TopoServer().GetTablet(ctx, tabletAlias) if err != nil { return "", err } return "", wr.TabletManagerClient().RefreshState(ctx, ti) }) actionRepo.RegisterTabletAction("DeleteTablet", acl.ADMIN, func(ctx context.Context, wr *wrangler.Wrangler, tabletAlias *pb.TabletAlias, r *http.Request) (string, error) { return "", wr.DeleteTablet(ctx, tabletAlias, false, false) }) actionRepo.RegisterTabletAction("ReloadSchema", acl.ADMIN, func(ctx context.Context, wr *wrangler.Wrangler, tabletAlias *pb.TabletAlias, r *http.Request) (string, error) { return "", wr.ReloadSchema(ctx, tabletAlias) }) // keyspace actions http.HandleFunc("/keyspace_actions", func(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { httpErrorf(w, r, "cannot parse form: %s", err) return } action := r.FormValue("action") if action == "" { http.Error(w, "no action provided", http.StatusBadRequest) return } keyspace := r.FormValue("keyspace") if keyspace == "" { http.Error(w, "no keyspace provided", http.StatusBadRequest) return } ctx := context.Background() result := actionRepo.ApplyKeyspaceAction(ctx, action, keyspace, r) templateLoader.ServeTemplate("action.html", result, w, r) }) // shard actions http.HandleFunc("/shard_actions", func(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { httpErrorf(w, r, "cannot parse form: %s", err) return } action := r.FormValue("action") if action == "" { http.Error(w, "no action provided", http.StatusBadRequest) return } keyspace := r.FormValue("keyspace") if keyspace == "" { http.Error(w, "no keyspace provided", http.StatusBadRequest) return } shard := r.FormValue("shard") if shard == "" { http.Error(w, "no shard provided", http.StatusBadRequest) return } ctx := context.Background() result := actionRepo.ApplyShardAction(ctx, action, keyspace, shard, r) templateLoader.ServeTemplate("action.html", result, w, r) }) // tablet actions http.HandleFunc("/tablet_actions", func(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { httpErrorf(w, r, "cannot parse form: %s", err) return } action := r.FormValue("action") if action == "" { http.Error(w, "no action provided", http.StatusBadRequest) return } alias := r.FormValue("alias") if alias == "" { http.Error(w, "no alias provided", http.StatusBadRequest) return } tabletAlias, err := topoproto.ParseTabletAlias(alias) if err != nil { http.Error(w, "bad alias provided", http.StatusBadRequest) return } ctx := context.Background() result := actionRepo.ApplyTabletAction(ctx, action, tabletAlias, r) templateLoader.ServeTemplate("action.html", result, w, r) }) // topology server http.HandleFunc("/dbtopo", func(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { httpErrorf(w, r, "cannot parse form: %s", err) return } result := DbTopologyResult{} ctx := context.TODO() topology, err := topotools.DbTopology(ctx, ts) if err == nil && modifyDbTopology != nil { err = modifyDbTopology(ctx, ts, topology) } if err != nil { result.Error = err.Error() } else { result.Topology = topology } templateLoader.ServeTemplate("dbtopo.html", result, w, r) }) // serving graph http.HandleFunc("/serving_graph/", func(w http.ResponseWriter, r *http.Request) { ctx := context.Background() parts := strings.Split(r.URL.Path, "/") cell := parts[len(parts)-1] if cell == "" { cells, err := ts.GetKnownCells(ctx) if err != nil { httpErrorf(w, r, "cannot get known cells: %v", err) return } templateLoader.ServeTemplate("serving_graph_cells.html", cells, w, r) return } servingGraph := topotools.DbServingGraph(ctx, ts, cell) if modifyDbServingGraph != nil { modifyDbServingGraph(ctx, ts, servingGraph) } templateLoader.ServeTemplate("serving_graph.html", servingGraph, w, r) }) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/app/", http.StatusFound) }) http.HandleFunc("/content/", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, *templateDir+r.URL.Path[8:]) }) // Serve the static files for the vtctld web app. http.HandleFunc("/app/", func(w http.ResponseWriter, r *http.Request) { // Strip the prefix. parts := strings.SplitN(r.URL.Path, "/", 3) if len(parts) != 3 { http.NotFound(w, r) return } rest := parts[2] if rest == "" { rest = "index.html" } http.ServeFile(w, r, path.Join(*webDir, rest)) }) // Serve the REST API for the vtctld web app. initAPI(context.Background(), ts, actionRepo) // vschema viewer http.HandleFunc("/vschema", func(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { httpErrorf(w, r, "cannot parse form: %s", err) return } var data struct { Error error Input, Output string } ctx := context.Background() switch r.Method { case "POST": data.Input = r.FormValue("vschema") data.Error = ts.SaveVSchema(ctx, data.Input) } vschema, err := ts.GetVSchema(ctx) if err != nil { if data.Error == nil { data.Error = fmt.Errorf("Error fetching schema: %s", err) } } data.Output = vschema templateLoader.ServeTemplate("vschema.html", data, w, r) }) // redirects for explorers http.HandleFunc("/explorers/redirect", func(w http.ResponseWriter, r *http.Request) { if explorer == nil { http.Error(w, "no explorer configured", http.StatusInternalServerError) return } if err := r.ParseForm(); err != nil { httpErrorf(w, r, "cannot parse form: %s", err) return } target, err := handleExplorerRedirect(r) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } http.Redirect(w, r, target, http.StatusFound) }) // serve some data knownCellsCache := newKnownCellsCache(ts) http.HandleFunc("/json/KnownCells", func(w http.ResponseWriter, r *http.Request) { ctx := context.Background() result, err := knownCellsCache.Get(ctx) if err != nil { httpErrorf(w, r, "error getting known cells: %v", err) return } w.Write(result) }) keyspacesCache := newKeyspacesCache(ts) http.HandleFunc("/json/Keyspaces", func(w http.ResponseWriter, r *http.Request) { ctx := context.Background() result, err := keyspacesCache.Get(ctx) if err != nil { httpErrorf(w, r, "error getting keyspaces: %v", err) return } w.Write(result) }) keyspaceCache := newKeyspaceCache(ts) http.HandleFunc("/json/Keyspace", func(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { httpErrorf(w, r, "cannot parse form: %s", err) return } keyspace := r.FormValue("keyspace") if keyspace == "" { http.Error(w, "no keyspace provided", http.StatusBadRequest) return } ctx := context.Background() result, err := keyspaceCache.Get(ctx, keyspace) if err != nil { httpErrorf(w, r, "error getting keyspace: %v", err) return } w.Write(result) }) shardNamesCache := newShardNamesCache(ts) http.HandleFunc("/json/ShardNames", func(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { httpErrorf(w, r, "cannot parse form: %s", err) return } keyspace := r.FormValue("keyspace") if keyspace == "" { http.Error(w, "no keyspace provided", http.StatusBadRequest) return } ctx := context.Background() result, err := shardNamesCache.Get(ctx, keyspace) if err != nil { httpErrorf(w, r, "error getting shardNames: %v", err) return } w.Write(result) }) shardCache := newShardCache(ts) http.HandleFunc("/json/Shard", func(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { httpErrorf(w, r, "cannot parse form: %s", err) return } keyspace := r.FormValue("keyspace") if keyspace == "" { http.Error(w, "no keyspace provided", http.StatusBadRequest) return } shard := r.FormValue("shard") if shard == "" { http.Error(w, "no shard provided", http.StatusBadRequest) return } ctx := context.Background() result, err := shardCache.Get(ctx, keyspace+"/"+shard) if err != nil { httpErrorf(w, r, "error getting shard: %v", err) return } w.Write(result) }) cellShardTabletsCache := newCellShardTabletsCache(ts) http.HandleFunc("/json/CellShardTablets", func(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { httpErrorf(w, r, "cannot parse form: %s", err) return } cell := r.FormValue("cell") if cell == "" { http.Error(w, "no cell provided", http.StatusBadRequest) return } keyspace := r.FormValue("keyspace") if keyspace == "" { http.Error(w, "no keyspace provided", http.StatusBadRequest) return } shard := r.FormValue("shard") if shard == "" { http.Error(w, "no shard provided", http.StatusBadRequest) return } ctx := context.Background() result, err := cellShardTabletsCache.Get(ctx, cell+"/"+keyspace+"/"+shard) if err != nil { httpErrorf(w, r, "error getting shard: %v", err) return } w.Write(result) }) // flush all data and will force a full client reload http.HandleFunc("/json/flush", func(w http.ResponseWriter, r *http.Request) { knownCellsCache.Flush() keyspacesCache.Flush() keyspaceCache.Flush() shardNamesCache.Flush() shardCache.Flush() cellShardTabletsCache.Flush() }) http.HandleFunc("/json/schema-manager", func(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { httpErrorf(w, r, "cannot parse form: %s", err) return } sqlStr := r.FormValue("data") keyspace := r.FormValue("keyspace") executor := schemamanager.NewTabletExecutor( tmclient.NewTabletManagerClient(), ts) ctx := context.Background() schemamanager.Run( ctx, schemamanager.NewUIController(sqlStr, keyspace, w), executor, ) }) if *schemaChangeDir != "" { interval := 60 if *schemaChangeCheckInterval > 0 { interval = *schemaChangeCheckInterval } timer := timer.NewTimer(time.Duration(interval) * time.Second) controllerFactory, err := schemamanager.GetControllerFactory(*schemaChangeController) if err != nil { log.Fatalf("unable to get a controller factory, error: %v", err) } timer.Start(func() { controller, err := controllerFactory(map[string]string{ schemamanager.SchemaChangeDirName: *schemaChangeDir, schemamanager.SchemaChangeUser: *schemaChangeUser, }) if err != nil { log.Errorf("failed to get controller, error: %v", err) return } ctx := context.Background() err = schemamanager.Run( ctx, controller, schemamanager.NewTabletExecutor( tmclient.NewTabletManagerClient(), ts), ) if err != nil { log.Errorf("Schema change failed, error: %v", err) } }) servenv.OnClose(func() { timer.Stop() }) } servenv.RunDefault() }