func (s monitorSetIgnoringID) contains(x Monitor) (Monitor, bool) { withoutID := x withoutID.ID = nil str, _ := util.Marshal(withoutID, util.JSON) r, ok := s[str] return r, ok }
func (monitor *Monitor) create(client *http.Client) error { repr, err := util.Marshal(monitor, util.JSON) if err != nil { return err } _, err = doHTTP(client, "POST", authedURL(monitorURL), repr) return err }
func (monitor *Monitor) update(client *http.Client, target *Monitor) error { msg := target msg.ID = nil repr, err := util.Marshal(msg, util.JSON) if err != nil { return err } _, err = doHTTP(client, "PUT", authedURL(fmt.Sprintf("%s/%d", monitorURL, *monitor.ID)), repr) return err }
func (s monitorSetIgnoringID) add(x Monitor) { withoutID := x withoutID.ID = nil str, _ := util.Marshal(withoutID, util.JSON) s[str] = x }
func (s monitorSetIgnoringID) remove(x Monitor) { withoutID := x withoutID.ID = nil str, _ := util.Marshal(withoutID, util.JSON) delete(s, str) }
// SyncMonitors creates Datadog metrics from local that aren't in Datadog, // then update metrics in Datadog to metrics from local whose ID matches, // then deletes metrics in Datadog that weren't accounted for in local func SyncMonitors(local, remote []Monitor, client *http.Client, dryRun, verbose bool) error { remoteSetIgnoringID := make(monitorSetIgnoringID) remotesByID := make(map[id]Monitor) localSet := make(monitorSetIgnoringID) var toCreate []Monitor var toUpdate []update for _, r := range remote { remoteSetIgnoringID.add(r) remotesByID[*r.ID] = r } for _, l := range local { if l.ID == nil { if r, ok := remoteSetIgnoringID.contains(l); ok { delete(remotesByID, *r.ID) } else { toCreate = append(toCreate, l) } } else { // l has an ID if r, ok := remotesByID[*l.ID]; ok { delete(remotesByID, *r.ID) if !reflect.DeepEqual(l, r) { toUpdate = append(toUpdate, update{from: l, to: r}) } } else { return fmt.Errorf("no remote alert #%d", *l.ID) } } localSet.add(l) } creations := len(toCreate) updates := len(toUpdate) deletions := len(remotesByID) total := creations + updates + deletions logrus.Infof("%d creations, %d updates, %d deletions", creations, updates, deletions) for i, m := range toCreate { logrus.Infof("CREATE %d/%d/%d: %s", i, creations, total, m.shortDescription()) if !dryRun { if err := m.create(client); err != nil { return err } } if verbose { repr, _ := util.Marshal(m, util.YAML) logrus.Debug(repr) } } for i, u := range toUpdate { logrus.Infof("UPDATE %d/%d/%d: %s", i, updates, total, u.from.shortDescription()) if !dryRun { if err := u.from.update(client, &u.to); err != nil { return err } } if verbose { f, _ := util.Marshal(u.from, util.YAML) t, _ := util.Marshal(u.to, util.YAML) logrus.Debugf("%s\n=>\n%s", f, t) } } idx := 0 for _, m := range remotesByID { logrus.Infof("DELETE %d/%d/%d: %s", idx, deletions, total, m.shortDescription()) idx++ if !dryRun { if err := m.delete(client); err != nil { return err } } if verbose { repr, _ := util.Marshal(m, util.YAML) logrus.Debug(repr) } } return nil }
func main() { logrus.SetLevel(logrus.DebugLevel) var action mode var format util.Format flag.Parse() switch *modeStr { case "pull": action = pull case "push": action = push default: logrus.Fatalf("unsupported mode %v", *modeStr) } switch *formatStr { case "json": format = util.JSON case "yaml": format = util.YAML default: logrus.Fatalf("unsupported format %v", *formatStr) } if *dd.APIKey == "" { var ok bool if *dd.APIKey, ok = os.LookupEnv("DATADOG_API_KEY"); !ok { logrus.Fatal("no API key provided") } } if *dd.AppKey == "" { var ok bool if *dd.AppKey, ok = os.LookupEnv("DATADOG_APP_KEY"); !ok { logrus.Fatal("no application key provided") } } client := &http.Client{} remote, err := dd.GetMonitors(client) if err != nil { logrus.WithError(err).Fatal("could not pull monitors") } remote, err = filteredMonitors(remote, *filter) if err != nil { logrus.WithError(err).Fatal("could not filter remote monitors") } switch action { case pull: var repr string if !*withIds { for i := range remote { remote[i].ID = nil } } repr, err = util.Marshal(remote, format) if err != nil { logrus.WithError(err).Fatal("could not serialize monitors") } fmt.Println(repr) case push: var local []dd.Monitor repr, err := ioutil.ReadAll(os.Stdin) if err != nil { logrus.WithError(err).Fatal("could not read from standard input") } if err = util.Unmarshal(repr, &local, format); err != nil { logrus.WithError(err).Fatal("could not deserialize monitors") } local, err = filteredMonitors(local, *filter) if err != nil { logrus.WithError(err).Fatal("could not filter local monitors") } if err = dd.SyncMonitors(local, remote, client, *dryRun, *verbose); err != nil { logrus.WithError(err).Fatal("could not sync monitors: %v") } } }