internal/ccm/ccm.go (146 lines of code) (raw):

//go:build ccm // +build ccm /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package ccm import ( "bufio" "bytes" "errors" "fmt" "os/exec" "runtime" "strings" ) func execCmd(args ...string) (*bytes.Buffer, error) { execName := "ccm" if runtime.GOOS == "windows" { args = append([]string{"/c", execName}, args...) execName = "cmd.exe" } cmd := exec.Command(execName, args...) stdout := &bytes.Buffer{} cmd.Stdout = stdout cmd.Stderr = &bytes.Buffer{} if err := cmd.Run(); err != nil { return nil, errors.New(cmd.Stderr.(*bytes.Buffer).String()) } return stdout, nil } func AllUp() error { status, err := Status() if err != nil { return err } for _, host := range status { if !host.State.IsUp() { if err := NodeUp(host.Name); err != nil { return err } } } return nil } func NodeUp(node string) error { args := []string{node, "start", "--wait-for-binary-proto"} if runtime.GOOS == "windows" { args = append(args, "--quiet-windows") } _, err := execCmd(args...) return err } func NodeDown(node string) error { _, err := execCmd(node, "stop") return err } type Host struct { State NodeState Addr string Name string } type NodeState int func (n NodeState) String() string { if n == NodeStateUp { return "UP" } else if n == NodeStateDown { return "DOWN" } else { return fmt.Sprintf("UNKNOWN_STATE_%d", n) } } func (n NodeState) IsUp() bool { return n == NodeStateUp } const ( NodeStateUp NodeState = iota NodeStateDown ) func Status() (map[string]Host, error) { // TODO: parse into struct to manipulate out, err := execCmd("status", "-v") if err != nil { return nil, err } const ( stateCluster = iota stateCommas stateNode stateOption ) nodes := make(map[string]Host) // didnt really want to write a full state machine parser state := stateCluster sc := bufio.NewScanner(out) var host Host for sc.Scan() { switch state { case stateCluster: text := sc.Text() if !strings.HasPrefix(text, "Cluster:") { return nil, fmt.Errorf("expected 'Cluster:' got %q", text) } state = stateCommas case stateCommas: text := sc.Text() if !strings.HasPrefix(text, "-") { return nil, fmt.Errorf("expected commas got %q", text) } state = stateNode case stateNode: // assume nodes start with node text := sc.Text() if !strings.HasPrefix(text, "node") { return nil, fmt.Errorf("expected 'node' got %q", text) } line := strings.Split(text, ":") host.Name = line[0] nodeState := strings.TrimSpace(line[1]) switch nodeState { case "UP": host.State = NodeStateUp case "DOWN": host.State = NodeStateDown default: return nil, fmt.Errorf("unknown node state from ccm: %q", nodeState) } state = stateOption case stateOption: text := sc.Text() if text == "" { state = stateNode nodes[host.Name] = host host = Host{} continue } line := strings.Split(strings.TrimSpace(text), "=") k, v := line[0], line[1] if k == "binary" { // could check errors // ('127.0.0.1', 9042) v = v[2:] // ('' if i := strings.IndexByte(v, '\''); i < 0 { return nil, fmt.Errorf("invalid binary v=%q", v) } else { host.Addr = v[:i] // dont need port } } default: return nil, fmt.Errorf("unexpected state: %q", state) } } if err := sc.Err(); err != nil { return nil, fmt.Errorf("unable to parse ccm status: %v", err) } return nodes, nil }