func (h *CfgGetHandler) ServeHTTP( w http.ResponseWriter, req *http.Request) { // TODO: Might need to scrub auth passwords from this output. cfg := h.mgr.Cfg() indexDefs, indexDefsCAS, indexDefsErr := cbgt.CfgGetIndexDefs(cfg) nodeDefsWanted, nodeDefsWantedCAS, nodeDefsWantedErr := cbgt.CfgGetNodeDefs(cfg, cbgt.NODE_DEFS_WANTED) nodeDefsKnown, nodeDefsKnownCAS, nodeDefsKnownErr := cbgt.CfgGetNodeDefs(cfg, cbgt.NODE_DEFS_KNOWN) planPIndexes, planPIndexesCAS, planPIndexesErr := cbgt.CfgGetPlanPIndexes(cfg) MustEncode(w, RESTCfg{ Status: "ok", IndexDefs: indexDefs, IndexDefsCAS: indexDefsCAS, IndexDefsErr: indexDefsErr, NodeDefsWanted: nodeDefsWanted, NodeDefsWantedCAS: nodeDefsWantedCAS, NodeDefsWantedErr: nodeDefsWantedErr, NodeDefsKnown: nodeDefsKnown, NodeDefsKnownCAS: nodeDefsKnownCAS, NodeDefsKnownErr: nodeDefsKnownErr, PlanPIndexes: planPIndexes, PlanPIndexesCAS: planPIndexesCAS, PlanPIndexesErr: planPIndexesErr, }) }
// WaitForWantedNodes blocks until the nodeDefsWanted in the cfg is // equal to or a superset of the provided wantedNodes, and returns the // "nodes to remove" (actualWantedNodes SET-DIFFERENCE wantedNodes). func WaitForWantedNodes(cfg cbgt.Cfg, wantedNodes []string, cancelCh <-chan struct{}, secs int) ( []string, error) { var nodeDefWantedUUIDs []string for i := 0; i < secs; i++ { select { case <-cancelCh: return nil, ErrCtlCanceled default: } nodeDefsWanted, _, err := cbgt.CfgGetNodeDefs(cfg, cbgt.NODE_DEFS_WANTED) if err != nil { return nil, err } nodeDefWantedUUIDs = nil for _, nodeDef := range nodeDefsWanted.NodeDefs { nodeDefWantedUUIDs = append(nodeDefWantedUUIDs, nodeDef.UUID) } if len(cbgt.StringsRemoveStrings(wantedNodes, nodeDefWantedUUIDs)) <= 0 { return cbgt.StringsRemoveStrings(nodeDefWantedUUIDs, wantedNodes), nil } time.Sleep(1 * time.Second) } return nil, fmt.Errorf("ctl: WaitForWantedNodes"+ " could not attain wantedNodes: %#v,"+ " only reached nodeDefWantedUUIDs: %#v", wantedNodes, nodeDefWantedUUIDs) }
func CurrentMemberNodes(cfg cbgt.Cfg) ([]CtlNode, error) { nodeDefsWanted, _, err := cbgt.CfgGetNodeDefs(cfg, cbgt.NODE_DEFS_WANTED) if err != nil { return nil, err } var memberNodes []CtlNode for _, nodeDef := range nodeDefsWanted.NodeDefs { memberNode := CtlNode{ UUID: nodeDef.UUID, ServiceURL: "http://" + nodeDef.HostPort, } if nodeDef.Extras != "" { // Early versions of ns_server integration had a simple, // non-JSON "host:port" format for nodeDef.Extras, which // we use as default. nsHostPort := nodeDef.Extras var e struct { NsHostPort string `json:"nsHostPort"` } err := json.Unmarshal([]byte(nodeDef.Extras), &e) if err != nil { nsHostPort = e.NsHostPort } memberNode.ManagerURL = "http://" + nsHostPort } memberNodes = append(memberNodes, memberNode) } return memberNodes, nil }
// PlannerSteps helps command-line tools implement the planner steps: // * "unregister" - unregisters nodesRemove from the cfg. // * "planner" - runs the planner to save a new plan into the cfg. // * "failover" - a composite step, comprised of "unregister" and "failover_". // * "failover_" - processes the nodesRemove as nodes to be failover'ed. // // The "NODES-REMOVE-ALL" step overrides the nodesRemove with every // known and wanted node. This can have a lot of impact, and was // meant to be used for cluster cleanup/purging situations. func PlannerSteps(steps map[string]bool, cfg cbgt.Cfg, version, server string, options map[string]string, nodesRemove []string, dryRun bool, plannerFilter cbgt.PlannerFilter) error { if steps != nil && steps["failover"] { steps["unregister"] = true steps["failover_"] = true } if steps != nil && steps["NODES-REMOVE-ALL"] { nodesRemove = nil nodesSeen := map[string]bool{} for _, kind := range []string{ cbgt.NODE_DEFS_WANTED, cbgt.NODE_DEFS_KNOWN, } { nodeDefs, _, err := cbgt.CfgGetNodeDefs(cfg, kind) if err != nil { return err } for _, nodeDef := range nodeDefs.NodeDefs { if !nodesSeen[nodeDef.UUID] { nodesSeen[nodeDef.UUID] = true nodesRemove = append(nodesRemove, nodeDef.UUID) } } } } log.Printf("planner: nodesRemove: %#v", nodesRemove) if steps != nil && steps["unregister"] { log.Printf("planner: step unregister") if !dryRun { err := cbgt.UnregisterNodes(cfg, cbgt.VERSION, nodesRemove) if err != nil { return err } } } if steps != nil && steps["planner"] { log.Printf("planner: step planner") if !dryRun { _, err := cbgt.Plan(cfg, cbgt.VERSION, "", server, options, plannerFilter) if err != nil { return err } } } if steps != nil && steps["failover_"] { log.Printf("planner: step failover_") if !dryRun { _, err := Failover(cfg, cbgt.VERSION, server, options, nodesRemove) if err != nil { return err } } } return nil }
func (h *NsStatusHandler) ServeHTTP( w http.ResponseWriter, req *http.Request) { cfg := h.mgr.Cfg() planPIndexes, _, err := cbgt.CfgGetPlanPIndexes(cfg) if err != nil { rest.ShowError(w, req, "could not retrieve plan pIndexes", 500) return } nodesDefs, _, err := cbgt.CfgGetNodeDefs(cfg, cbgt.NODE_DEFS_WANTED) if err != nil { rest.ShowError(w, req, "could not retrieve node defs (wanted)", 500) return } _, indexDefsMap, err := h.mgr.GetIndexDefs(false) if err != nil { rest.ShowError(w, req, "could not retrieve index defs", 500) return } w.Write(cbgt.JsonOpenBrace) w.Write(statsNamePrefix) w.Write([]byte("status")) w.Write(statsNameSuffix) w.Write([]byte("[")) indexDefNames := make(sort.StringSlice, 0, len(indexDefsMap)) for indexDefName := range indexDefsMap { indexDefNames = append(indexDefNames, indexDefName) } sort.Sort(indexDefNames) for i, indexDefName := range indexDefNames { indexDef := indexDefsMap[indexDefName] if i > 0 { w.Write(cbgt.JsonComma) } rest.MustEncode(w, struct { Completion int `json:"completion"` Hosts []string `json:"hosts"` Status string `json:"status"` Bucket string `json:"bucket"` Name string `json:"name"` }{ Bucket: indexDef.SourceName, Name: indexDefName, Hosts: HostsForIndex(indexDefName, planPIndexes, nodesDefs), // FIXME hard-coded Completion: 100, Status: "Ready", }) } w.Write([]byte("],")) w.Write(statsNamePrefix) w.Write([]byte("code")) w.Write(statsNameSuffix) w.Write([]byte("\"success\"")) w.Write(cbgt.JsonCloseBrace) }
func RunRecentInfoCache(mgr *cbgt.Manager) { cfg := mgr.Cfg() cfgChangedCh := make(chan struct{}, 10) go func() { // Debounce cfg events to feed into the cfgChangedCh. ech := make(chan cbgt.CfgEvent) cfg.Subscribe(cbgt.PLAN_PINDEXES_KEY, ech) for { <-ech // First, wait for a cfg event. debounceTimeCh := time.After(500 * time.Millisecond) DEBOUNCE_LOOP: for { select { case <-ech: // NO-OP when there are more, spammy cfg events. case <-debounceTimeCh: break DEBOUNCE_LOOP } } cfgChangedCh <- struct{}{} } }() tickCh := time.Tick(1 * time.Minute) for { var nodeDefs *cbgt.NodeDefs var planPIndexes *cbgt.PlanPIndexes indexDefs, indexDefsMap, err := mgr.GetIndexDefs(false) if err == nil { nodeDefs, _, err = cbgt.CfgGetNodeDefs(cfg, cbgt.NODE_DEFS_WANTED) if err == nil { planPIndexes, _, err = cbgt.CfgGetPlanPIndexes(cfg) } } rd := &recentInfo{ indexDefs: indexDefs, indexDefsMap: indexDefsMap, nodeDefs: nodeDefs, planPIndexes: planPIndexes, err: err, } runtime.ReadMemStats(&rd.memStats) REUSE_CACHE: for { select { case <-cfgChangedCh: break REUSE_CACHE case <-tickCh: break REUSE_CACHE case recentInfoCh <- rd: if rd.err != nil { break REUSE_CACHE } } } } }