<commit_before>package command

import (
  "fmt"
  "encoding/json"
  "os"
  "os/signal"
  "time"

  "github.com/jwaldrip/odin/cli"
  "github.com/hashicorp/consul/api"
)

type CascadeEvent struct {
  Source string `json:"source"`
  Msg string `json:"msg"`
  Ref string `json:"ref"`
}

var Cm = cli.NewSubCommand("cm", "Config management operations", cmRun)

func init() {
  Cm.DefineParams("action")
  Cm.DefineStringFlag("role", "", "filter by role")
  Cm.AliasFlag('r', "role")

  Cm.SetLongDescription(`
Run CM on member systems

Actions:
  roll - ordered synchronous run
  local - run CM locally only
  single <nodename> - run on single remote node
  `)
}

func cmRun(c cli.Command) {
  switch c.Param("action").String() {
  case "local": cmLocal(c)
  case "roll": cmRoll(c)
  case "single": cmSingle(c)
  default: cli.ShowUsage(c)
  }
}

func cmLocal(c cli.Command) {
  client, _ := api.NewClient(api.DefaultConfig())
  agent := client.Agent()

  self, err := agent.Self()

  if err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  }

  services, err := agent.Services()
  if err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  }

  if _, ok := services["cascade"]; !ok {
    fmt.Println("Node not managemed by cascade")
    os.Exit(1)
  }

  fmt.Println("Running CM...")
  _cmDispatch(self["Config"]["NodeName"].(string))
}

func cmRoll(c cli.Command) {
  client, _ := api.NewClient(api.DefaultConfig())
  catalog := client.Catalog()
  session := client.Session()
  kv := client.KV()

  nodes, _, err := catalog.Service("cascade", c.Flag("role").String(), nil)

  if err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  }

  se := &api.SessionEntry{
    Name: "cascade",
    TTL: "250s",
    Behavior: api.SessionBehaviorDelete,
  }

  session_id, _, err := session.Create(se, nil)
  if err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  }
  defer session.Destroy(session_id, nil)

  key := "cascade/roll"
  user := os.Getenv("USER")

  if user == "root" && os.Getenv("SUDO_USER") != "" {
    user = os.Getenv("SUDO_USER")
  }

  value := []byte(user)
  p := &api.KVPair{Key: key, Value: value, Session: session_id}
  if work, _, err := kv.Acquire(p, nil); err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  } else if !work {
    fmt.Println("Failed to acquire roll lock")
    
    pair, _, err := kv.Get(key, nil)
    if err != nil {
      fmt.Println("err: ", err)
    }

    fmt.Println("user:", string(pair.Value[:]), "has the lock")

    os.Exit(1)
  }<commit_msg>check for nil key<commit_after>package command

import (
  "fmt"
  "encoding/json"
  "os"
  "os/signal"
  "time"

  "github.com/jwaldrip/odin/cli"
  "github.com/hashicorp/consul/api"
)

type CascadeEvent struct {
  Source string `json:"source"`
  Msg string `json:"msg"`
  Ref string `json:"ref"`
}

var Cm = cli.NewSubCommand("cm", "Config management operations", cmRun)

func init() {
  Cm.DefineParams("action")
  Cm.DefineStringFlag("role", "", "filter by role")
  Cm.AliasFlag('r', "role")

  Cm.SetLongDescription(`
Run CM on member systems

Actions:
  roll - ordered synchronous run
  local - run CM locally only
  single <nodename> - run on single remote node
  `)
}

func cmRun(c cli.Command) {
  switch c.Param("action").String() {
  case "local": cmLocal(c)
  case "roll": cmRoll(c)
  case "single": cmSingle(c)
  default: cli.ShowUsage(c)
  }
}

func cmLocal(c cli.Command) {
  client, _ := api.NewClient(api.DefaultConfig())
  agent := client.Agent()

  self, err := agent.Self()

  if err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  }

  services, err := agent.Services()
  if err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  }

  if _, ok := services["cascade"]; !ok {
    fmt.Println("Node not managemed by cascade")
    os.Exit(1)
  }

  fmt.Println("Running CM...")
  _cmDispatch(self["Config"]["NodeName"].(string))
}

func cmRoll(c cli.Command) {
  client, _ := api.NewClient(api.DefaultConfig())
  catalog := client.Catalog()
  session := client.Session()
  kv := client.KV()

  nodes, _, err := catalog.Service("cascade", c.Flag("role").String(), nil)

  if err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  }

  se := &api.SessionEntry{
    Name: "cascade",
    TTL: "250s",
    Behavior: api.SessionBehaviorDelete,
  }

  session_id, _, err := session.Create(se, nil)
  if err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  }
  defer session.Destroy(session_id, nil)

  key := "cascade/roll"
  user := os.Getenv("USER")

  if user == "root" && os.Getenv("SUDO_USER") != "" {
    user = os.Getenv("SUDO_USER")
  }

  value := []byte(user)
  p := &api.KVPair{Key: key, Value: value, Session: session_id}
  if work, _, err := kv.Acquire(p, nil); err != nil {
    fmt.Println("err: ", err)
    os.Exit(1)
  } else if !work {
    fmt.Println("Failed to acquire roll lock")
    
    pair, _, err := kv.Get(key, nil)
    if err != nil {
      fmt.Println("err: ", err)
      os.Exit(1)
    }

    if pair.Value != nil {
      fmt.Println("user:", string(pair.Value[:]), "has the lock")
    }
    os.Exit(1)
  }
{'fraction_non_alphanumeric': 0.1798150698406453, 'fraction_numerical': 0.004131418453669093, 'mean_word_length': 3.15359477124183, 'pattern_counts': {'":': 6, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1298465', 'n_tokens_mistral': 1939, 'n_tokens_neox': 1888, 'n_words': 427}