func joinMonitorsAndHosts(client *mkr.Client, alerts []*mkr.Alert) []*alertSet { hostsJSON, err := client.FindHosts(&mkr.FindHostsParam{ Statuses: []string{"working", "standby", "poweroff", "maintenance"}, }) logger.DieIf(err) hosts := map[string]*mkr.Host{} for _, host := range hostsJSON { hosts[host.ID] = host } monitorsJSON, err := client.FindMonitors() logger.DieIf(err) monitors := map[string]*mkr.Monitor{} for _, monitor := range monitorsJSON { monitors[monitor.ID] = monitor } alertSets := []*alertSet{} for _, alert := range alerts { alertSets = append( alertSets, &alertSet{Alert: alert, Host: hosts[alert.HostID], Monitor: monitors[alert.MonitorID]}, ) } return alertSets }
func doCreate(c *cli.Context) { conffile := c.GlobalString("conf") argHostName := c.Args().Get(0) optRoleFullnames := c.StringSlice("roleFullname") optStatus := c.String("status") if argHostName == "" { cli.ShowCommandHelp(c, "create") os.Exit(1) } client := newMackerel(conffile) hostID, err := client.CreateHost(&mkr.CreateHostParam{ Name: argHostName, RoleFullnames: optRoleFullnames, }) logger.DieIf(err) logger.Log("created", hostID) if optStatus != "" { err := client.UpdateHostStatus(hostID, optStatus) logger.DieIf(err) logger.Log("updated", fmt.Sprintf("%s %s", hostID, optStatus)) } }
func doThrow(c *cli.Context) error { conffile := c.GlobalString("conf") optHostID := c.String("host") optService := c.String("service") var metricValues []*(mkr.MetricValue) scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { line := scanner.Text() // name, value, timestamp // ex.) tcp.CLOSING 0 1397031808 items := strings.Fields(line) if len(items) != 3 { continue } value, err := strconv.ParseFloat(items[1], 64) if err != nil { logger.Log("warning", fmt.Sprintf("Failed to parse values: %s", err)) continue } time, err := strconv.ParseInt(items[2], 10, 64) if err != nil { logger.Log("warning", fmt.Sprintf("Failed to parse values: %s", err)) continue } metricValue := &mkr.MetricValue{ Name: items[0], Value: value, Time: time, } metricValues = append(metricValues, metricValue) } logger.ErrorIf(scanner.Err()) client := newMackerel(conffile) if optHostID != "" { err := client.PostHostMetricValuesByHostID(optHostID, metricValues) logger.DieIf(err) for _, metric := range metricValues { logger.Log("thrown", fmt.Sprintf("%s '%s\t%f\t%d'", optHostID, metric.Name, metric.Value, metric.Time)) } } else if optService != "" { err := client.PostServiceMetricValues(optService, metricValues) logger.DieIf(err) for _, metric := range metricValues { logger.Log("thrown", fmt.Sprintf("%s '%s\t%f\t%d'", optService, metric.Name, metric.Value, metric.Time)) } } else { cli.ShowCommandHelp(c, "throw") os.Exit(1) } return nil }
func doUpdate(c *cli.Context) { conffile := c.GlobalString("conf") argHostIDs := c.Args() optName := c.String("name") optStatus := c.String("status") optRoleFullnames := c.StringSlice("roleFullname") if len(argHostIDs) < 1 { argHostIDs = make([]string, 1) if argHostIDs[0] = LoadHostIDFromConfig(conffile); argHostIDs[0] == "" { cli.ShowCommandHelp(c, "update") os.Exit(1) } } needUpdateHostStatus := optStatus != "" needUpdateHost := (optName != "" || len(optRoleFullnames) > 0) if !needUpdateHostStatus && !needUpdateHost { cli.ShowCommandHelp(c, "update") os.Exit(1) } client := newMackerel(conffile) var wg sync.WaitGroup for _, hostID := range argHostIDs { wg.Add(1) go func(hostID string) { defer wg.Done() if needUpdateHostStatus { err := client.UpdateHostStatus(hostID, optStatus) logger.DieIf(err) } if needUpdateHost { _, err := client.UpdateHost(hostID, &mkr.UpdateHostParam{ Name: optName, RoleFullnames: optRoleFullnames, }) logger.DieIf(err) } logger.Log("updated", hostID) }(hostID) } wg.Wait() }
func doStatus(c *cli.Context) { conffile := c.GlobalString("conf") argHostID := c.Args().Get(0) isVerbose := c.Bool("verbose") if argHostID == "" { if argHostID = LoadHostIDFromConfig(conffile); argHostID == "" { cli.ShowCommandHelp(c, "status") os.Exit(1) } } host, err := newMackerel(conffile).FindHost(argHostID) logger.DieIf(err) if isVerbose { PrettyPrintJSON(host) } else { format := &HostFormat{ ID: host.ID, Name: host.Name, Status: host.Status, RoleFullnames: host.GetRoleFullnames(), IsRetired: host.IsRetired, CreatedAt: host.DateStringFromCreatedAt(), IPAddresses: host.IPAddresses(), } PrettyPrintJSON(format) } }
func doRetire(c *cli.Context) { conffile := c.GlobalString("conf") argHostIDs := c.Args() if len(argHostIDs) < 1 { argHostIDs = make([]string, 1) if argHostIDs[0] = LoadHostIDFromConfig(conffile); argHostIDs[0] == "" { cli.ShowCommandHelp(c, "retire") os.Exit(1) } } client := newMackerel(conffile) var wg sync.WaitGroup for _, hostID := range argHostIDs { wg.Add(1) go func(hostID string) { defer wg.Done() err := client.RetireHost(hostID) logger.DieIf(err) logger.Log("retired", hostID) }(hostID) } wg.Wait() }
func doRetire(c *cli.Context) error { conffile := c.GlobalString("conf") force := c.Bool("force") argHostIDs := c.Args() if len(argHostIDs) < 1 { argHostIDs = make([]string, 1) if argHostIDs[0] = LoadHostIDFromConfig(conffile); argHostIDs[0] == "" { cli.ShowCommandHelp(c, "retire") os.Exit(1) } } if !force && !prompter.YN("Retire following hosts.\n "+strings.Join(argHostIDs, "\n ")+"\nAre you sure?", true) { logger.Log("", "retirement is canceled.") return nil } client := newMackerel(conffile) for _, hostID := range argHostIDs { err := client.RetireHost(hostID) logger.DieIf(err) logger.Log("retired", hostID) } return nil }
func doAlertsRetrieve(c *cli.Context) error { conffile := c.GlobalString("conf") client := newMackerel(conffile) alerts, err := client.FindAlerts() logger.DieIf(err) PrettyPrintJSON(alerts) return nil }
func doMonitorsList(c *cli.Context) error { conffile := c.GlobalString("conf") monitors, err := newMackerel(conffile).FindMonitors() logger.DieIf(err) PrettyPrintJSON(monitors) return nil }
func checkMonitorsDiff(c *cli.Context) monitorDiff { conffile := c.GlobalString("conf") filePath := c.String("file-path") var monitorDiff monitorDiff monitorsRemote, err := newMackerel(conffile).FindMonitors() logger.DieIf(err) flagNameUniquenessRemote, err := validateRules(monitorsRemote, "remote rules") logger.DieIf(err) monitorsLocal, err := monitorLoadRules(filePath) logger.DieIf(err) flagNameUniquenessLocal, err := validateRules(monitorsLocal, "local rules") logger.DieIf(err) flagNameUniqueness := flagNameUniquenessLocal && flagNameUniquenessRemote for _, remote := range monitorsRemote { found := false for i, local := range monitorsLocal { diff, isSame := isSameMonitor(remote, local, flagNameUniqueness) if isSame || diff != "" { monitorsLocal[i] = nil found = true if diff != "" { monitorDiff.diff = append(monitorDiff.diff, &monitorDiffPair{remote, local}) } break } } if found == false { monitorDiff.onlyRemote = append(monitorDiff.onlyRemote, remote) } } for _, local := range monitorsLocal { if local != nil { monitorDiff.onlyLocal = append(monitorDiff.onlyLocal, local) } } return monitorDiff }
func doHosts(c *cli.Context) error { conffile := c.GlobalString("conf") isVerbose := c.Bool("verbose") hosts, err := newMackerel(conffile).FindHosts(&mkr.FindHostsParam{ Name: c.String("name"), Service: c.String("service"), Roles: c.StringSlice("role"), Statuses: c.StringSlice("status"), }) logger.DieIf(err) format := c.String("format") if format != "" { t := template.Must(template.New("format").Parse(format)) err := t.Execute(os.Stdout, hosts) logger.DieIf(err) } else if isVerbose { PrettyPrintJSON(hosts) } else { var hostsFormat []*HostFormat for _, host := range hosts { format := &HostFormat{ ID: host.ID, Name: host.Name, DisplayName: host.DisplayName, Status: host.Status, RoleFullnames: host.GetRoleFullnames(), IsRetired: host.IsRetired, CreatedAt: host.DateStringFromCreatedAt(), IPAddresses: host.IPAddresses(), } hostsFormat = append(hostsFormat, format) } PrettyPrintJSON(hostsFormat) } return nil }
func doMonitorsPush(c *cli.Context) error { monitorDiff := checkMonitorsDiff(c) isDryRun := c.Bool("dry-run") isVerbose := c.Bool("verbose") conffile := c.GlobalString("conf") client := newMackerel(conffile) if isVerbose { client.Verbose = true } for _, m := range monitorDiff.onlyLocal { logger.Log("info", "Create a new rule.") fmt.Println(stringifyMonitor(m, "")) if !isDryRun { _, err := client.CreateMonitor(m) logger.DieIf(err) } } for _, m := range monitorDiff.onlyRemote { logger.Log("info", "Delete a rule.") fmt.Println(stringifyMonitor(m, "")) if !isDryRun { _, err := client.DeleteMonitor(m.ID) logger.DieIf(err) } } for _, d := range monitorDiff.diff { logger.Log("info", "Update a rule.") fmt.Println(stringifyMonitor(d.local, "")) if !isDryRun { _, err := client.UpdateMonitor(d.remote.ID, d.local) logger.DieIf(err) } } return nil }
func doFetch(c *cli.Context) { conffile := c.GlobalString("conf") argHostIDs := c.Args() optMetricNames := c.StringSlice("name") if len(argHostIDs) < 1 || len(optMetricNames) < 1 { cli.ShowCommandHelp(c, "fetch") os.Exit(1) } metricValues, err := newMackerel(conffile).FetchLatestMetricValues(argHostIDs, optMetricNames) logger.DieIf(err) PrettyPrintJSON(metricValues) }
func newMackerel(conffile string) *mkr.Client { apiKey := LoadApikeyFromEnvOrConfig(conffile) if apiKey == "" { logger.Log("error", ` Not set MACKEREL_APIKEY environment variable. (Try "export MACKEREL_APIKEY='<Your apikey>'") `) os.Exit(1) } if os.Getenv("DEBUG") != "" { mackerel, err := mkr.NewClientWithOptions(apiKey, "https://mackerel.io/api/v0", true) logger.DieIf(err) return mackerel } return mkr.NewClient(apiKey) }
func doMonitorsPull(c *cli.Context) error { conffile := c.GlobalString("conf") isVerbose := c.Bool("verbose") filePath := c.String("file-path") monitors, err := newMackerel(conffile).FindMonitors() logger.DieIf(err) monitorSaveRules(monitors, filePath) if isVerbose { PrettyPrintJSON(monitors) } if filePath == "" { filePath = "monitors.json" } logger.Log("info", fmt.Sprintf("Monitor rules are saved to '%s' (%d rules).", filePath, len(monitors))) return nil }
func doAlertsList(c *cli.Context) error { conffile := c.GlobalString("conf") filterServices := c.StringSlice("service") filterStatuses := c.StringSlice("host-status") client := newMackerel(conffile) alerts, err := client.FindAlerts() logger.DieIf(err) joinedAlerts := joinMonitorsAndHosts(client, alerts) for _, joinAlert := range joinedAlerts { if len(filterServices) > 0 { found := false for _, filterService := range filterServices { if joinAlert.Host != nil { if _, ok := joinAlert.Host.Roles[filterService]; ok { found = true } } else if joinAlert.Monitor.Service == filterService { found = true } } if !found { continue } } if len(filterStatuses) > 0 { found := false for _, filterStatus := range filterStatuses { if joinAlert.Host != nil && joinAlert.Host.Status == filterStatus { found = true } } if !found { continue } } fmt.Println(formatJoinedAlert(joinAlert, c.BoolT("color"))) } return nil }
func doAlertsClose(c *cli.Context) error { conffile := c.GlobalString("conf") isVerbose := c.Bool("verbose") argAlertIDs := c.Args() reason := c.String("reason") if len(argAlertIDs) < 1 { cli.ShowCommandHelp(c, "alerts") os.Exit(1) } client := newMackerel(conffile) for _, alertID := range argAlertIDs { alert, err := client.CloseAlert(alertID, reason) logger.DieIf(err) logger.Log("Alert closed", alertID) if isVerbose == true { PrettyPrintJSON(alert) } } return nil }
// PrettyPrintJSON output indented json via stdout. func PrettyPrintJSON(src interface{}) { data, err := json.MarshalIndent(src, "", " ") logger.DieIf(err) fmt.Fprintln(os.Stdout, string(data)) }
func doGenerateDashboards(c *cli.Context) error { conffile := c.GlobalString("conf") isStdout := c.Bool("print") argFilePath := c.Args() if len(argFilePath) < 1 { cli.ShowCommandHelp(c, "generate") return cli.NewExitError("specify a yaml file.", 1) } buf, err := ioutil.ReadFile(argFilePath[0]) logger.DieIf(err) yml := graphsConfig{} err = yaml.Unmarshal(buf, &yml) logger.DieIf(err) client := newMackerel(conffile) org, err := client.GetOrg() logger.DieIf(err) if yml.ConfigVersion == "" { return cli.NewExitError("config_version is required in yaml.", 1) } if yml.ConfigVersion != "0.9" { return cli.NewExitError(fmt.Sprintf("config_version %s is not suport.", yml.ConfigVersion), 1) } if yml.Title == "" { return cli.NewExitError("title is required in yaml.", 1) } if yml.URLPath == "" { return cli.NewExitError("url_path is required in yaml.", 1) } if yml.Format == "" { yml.Format = "iframe" } if yml.Format != "iframe" && yml.Format != "image" { return cli.NewExitError("graph_type should be 'iframe' or 'image'.", 1) } if yml.Height == 0 { yml.Height = 200 } if yml.Width == 0 { yml.Width = 400 } if yml.HostGraphFormat != nil && yml.GraphFormat != nil { return cli.NewExitError("you cannot specify both 'graphs' and host_graphs'.", 1) } var markdown string for _, h := range yml.HostGraphFormat { mdf := generateHostGraphsMarkdownFactory(h, yml.Format, yml.Height, yml.Width) markdown += mdf.generate(org.Name) } for _, g := range yml.GraphFormat { mdf, err := generateGraphsMarkdownFactory(g, yml.Format, yml.Height, yml.Width) if err != nil { return err } markdown += mdf.generate(org.Name) } if isStdout { fmt.Println(markdown) } else { updateDashboard := &mackerel.Dashboard{ Title: yml.Title, BodyMarkDown: markdown, URLPath: yml.URLPath, } dashboards, fetchError := client.FindDashboards() logger.DieIf(fetchError) dashboardID := "" for _, ds := range dashboards { if ds.URLPath == yml.URLPath { dashboardID = ds.ID } } if dashboardID == "" { _, createError := client.CreateDashboard(updateDashboard) logger.DieIf(createError) } else { _, updateError := client.UpdateDashboard(dashboardID, updateDashboard) logger.DieIf(updateError) } } return nil }
// JSONMarshalIndent call json.MarshalIndent and replace encoded angle brackets func JSONMarshalIndent(src interface{}, prefix, indent string) string { dataRaw, err := json.MarshalIndent(src, prefix, indent) logger.DieIf(err) return replaceAngleBrackets(string(dataRaw)) }
func doUpdate(c *cli.Context) error { conffile := c.GlobalString("conf") argHostIDs := c.Args() optName := c.String("name") optDisplayName := c.String("displayName") optStatus := c.String("status") optRoleFullnames := c.StringSlice("roleFullname") overwriteRoles := c.Bool("overwriteRoles") if len(argHostIDs) < 1 { argHostIDs = make([]string, 1) if argHostIDs[0] = LoadHostIDFromConfig(conffile); argHostIDs[0] == "" { cli.ShowCommandHelp(c, "update") os.Exit(1) } } needUpdateHostStatus := optStatus != "" needUpdateRolesInHostUpdate := !overwriteRoles && len(optRoleFullnames) > 0 needUpdateHost := (optName != "" || optDisplayName != "" || overwriteRoles || needUpdateRolesInHostUpdate) if !needUpdateHostStatus && !needUpdateHost { logger.Log("update", "at least one argumet is required.") cli.ShowCommandHelp(c, "update") os.Exit(1) } client := newMackerel(conffile) for _, hostID := range argHostIDs { if needUpdateHostStatus { err := client.UpdateHostStatus(hostID, optStatus) logger.DieIf(err) } if overwriteRoles { err := client.UpdateHostRoleFullnames(hostID, optRoleFullnames) logger.DieIf(err) } if needUpdateHost { host, err := client.FindHost(hostID) logger.DieIf(err) meta := host.Meta name := "" if optName == "" { name = host.Name } else { name = optName } displayname := "" if optDisplayName == "" { displayname = host.DisplayName } else { displayname = optDisplayName } param := &mkr.UpdateHostParam{ Name: name, DisplayName: displayname, Meta: meta, } if needUpdateRolesInHostUpdate { param.RoleFullnames = optRoleFullnames } _, err = client.UpdateHost(hostID, param) logger.DieIf(err) } logger.Log("updated", hostID) } return nil }