From 476096f44672ab45b84f1e475600d0156c965c23 Mon Sep 17 00:00:00 2001 From: Bob Zoller Date: Mon, 1 Oct 2018 14:27:02 -1000 Subject: [PATCH] use official go-pagerduty client removes uses of the v1 PagerDuty API that is being decommissioned. https://v2.developer.pagerduty.com/docs/v1-rest-api-decommissioning-faq --- glide.lock | 18 ++-- glide.yaml | 6 +- pagerduty/pagerduty.go | 43 ++++------ pagerduty/request.go | 117 -------------------------- pagerduty/schedules.go | 184 ++++++++++++++++++++++------------------- pagerduty/users.go | 34 +++++--- 6 files changed, 150 insertions(+), 252 deletions(-) delete mode 100644 pagerduty/request.go diff --git a/glide.lock b/glide.lock index c578704..90bc19b 100644 --- a/glide.lock +++ b/glide.lock @@ -1,8 +1,12 @@ -hash: 42d77bccba2438ac8a7a42fd2916790dfb87a362908bc346ebf00b4d811b8c27 -updated: 2016-06-01T18:22:28.234527011+01:00 +hash: 66ca75830e4a274ddb9e194585e0a72f49a25645682280f34889e587d716a357 +updated: 2018-10-01T15:23:47.375819612-10:00 imports: - name: github.com/nlopes/slack - version: 67c6939dd382a85ccbbe2f2e133fd245fe424098 + version: ca27004ea682472cfc73a093b72358ae3e68d367 + repo: git@github.com:yosmudge/slack.git + vcs: git +- name: github.com/PagerDuty/go-pagerduty + version: b4a4067bdbde86de1d3fdf15c3c01e76bee9127c - name: github.com/Sirupsen/logrus version: f3cfb454f4c209e6668c95216c4744b8fddb2356 - name: github.com/stretchr/testify @@ -13,12 +17,6 @@ imports: version: c4c3ea71919de159c9e246d7be66deb7f0a39a58 subpackages: - websocket -- name: golang.org/x/sys - version: 076b546753157f758b316e59bcb51e6807c04057 - subpackages: - - unix -- name: gopkg.in/jmcvetta/napping.v3 - version: 5be8267a453ffa5b80c4af7fa4cfc1d04d3cfd5c - name: gopkg.in/yaml.v2 version: a83829b6f1293c91addabc89d0571c246397bbf4 -devImports: [] +testImports: [] diff --git a/glide.yaml b/glide.yaml index 03f8d86..bb5c9d9 100644 --- a/glide.yaml +++ b/glide.yaml @@ -5,8 +5,10 @@ import: - package: gopkg.in/yaml.v2 - package: github.com/stretchr/testify - package: github.com/nlopes/slack - repo: github.com/yosmudge/slack + repo: git@github.com:yosmudge/slack.git + vcs: git - package: golang.org/x/net subpackages: - websocket -- package: gopkg.in/jmcvetta/napping.v3 +- package: github.com/PagerDuty/go-pagerduty + version: ^1.0.4 diff --git a/pagerduty/pagerduty.go b/pagerduty/pagerduty.go index aeecdd0..dbe58da 100644 --- a/pagerduty/pagerduty.go +++ b/pagerduty/pagerduty.go @@ -1,37 +1,30 @@ package pagerduty -import( - "fmt" - "net/http" - "gopkg.in/jmcvetta/napping.v3" +import ( + "github.com/PagerDuty/go-pagerduty" ) -type Api struct{ - key string - org string - http *napping.Session - timezone string +type Api struct { + key string + org string + client *pagerduty.Client + timezone string } // Pagerduty API doesn't provide a sane way of checking for auth // so we just get the schedules at setup time -func New(key string, org string) (*Api, error){ - a := Api{} - a.key = key - a.org = org - a.timezone = "UTC" +func New(key string, org string) (*Api, error) { + a := Api{} + a.key = key + a.org = org + a.timezone = "UTC" - a.http = &napping.Session{ - Header: &http.Header{ - "Authorization": []string{fmt.Sprintf("Token token=%s", a.key)}, - "User-Agent": []string{"PagerBot +https://github.com/yosmudge/pagerbot"}, - }, - } + a.client = pagerduty.NewClient(key) - _, err := a.request("schedules") - if err != nil { - return &a, err - } + _, err := a.client.ListSchedules(pagerduty.ListSchedulesOptions{}) + if err != nil { + return &a, err + } - return &a, nil + return &a, nil } diff --git a/pagerduty/request.go b/pagerduty/request.go deleted file mode 100644 index 906e2aa..0000000 --- a/pagerduty/request.go +++ /dev/null @@ -1,117 +0,0 @@ -package pagerduty - -import( - "fmt" - "sync" - "time" - "net/url" - "encoding/json" - log "github.com/Sirupsen/logrus" - "gopkg.in/jmcvetta/napping.v3" -) - -type response interface{ - Add(interface{}) -} - -type pagination struct{ - Limit int - Offset int - Total int -} - -// Request a list of items (users, schedules etc.) and paginate -// Pagerduty has a weird response format so we make the assumption that the -// data we want is the same key as the URL (I.E. schedules, users etc.) -// There is a super weird JSON hack in here to decode the responses properly -func (a *Api) requestThing(thing string, r interface{}) error{ - var err error - var items []interface{} - - var page int = 0 - var perPage int = 25 - - for{ - q := url.Values{} - q.Set("limit", fmt.Sprintf("%d", perPage)) - q.Set("offset", fmt.Sprintf("%d", page*perPage)) - - path := fmt.Sprintf("%s?%s", thing, q.Encode()) - - var resp *napping.Response - resp, err = a.request(path) - if err != nil { - return err - } - - var pages pagination - resp.Unmarshal(&pages) - - var rawResponse map[string]interface{} - resp.Unmarshal(&rawResponse) - - for _,i := range rawResponse[thing].([]interface{}){ - items = append(items, i) - } - - if len(items) == pages.Total{ - break - } - - page += 1 - } - - itemsEncoded, _ := json.Marshal(&items) - json.Unmarshal(itemsEncoded, &r) - // Told you - - return nil -} - -var requestLock sync.Mutex -const requestPause time.Duration = 2*time.Second -var limiter <-chan time.Time = time.Tick(requestPause) - -// Send request to Pagerduty -// Ensures requests are no more frequent than `requestPause` to permit Goroutines calling without hitting rate limit -func (a *Api) request(path string) (*napping.Response, error){ - requestLock.Lock() - defer func(){ - go func(){ - <- limiter - requestLock.Unlock() - }() - }() - - var resp *napping.Response - var err error - var u url.URL - - u.Host = fmt.Sprintf("%s.pagerduty.com", a.org) - u.Scheme = "https" - - fullPath := fmt.Sprintf("%s/api/v1/%s", u.String(), path) - - resp, err = a.http.Get(fullPath, &url.Values{}, nil, nil) - if err != nil || resp.Status() != 200 { - fields := log.Fields{ - "url": fullPath, - "error": err, - } - var status int - if err == nil{ - fields["status"] = resp.Status() - status = resp.Status() - } - log.WithFields(fields).Warning("Error from Pagerduty") - - return resp, fmt.Errorf("Got error from Pagerduty: %d (%s)", status, err) - } - - log.WithFields(log.Fields{ - "url": fullPath, - "status": resp.Status(), - }).Debug("Pagerduty request") - - return resp, nil -} diff --git a/pagerduty/schedules.go b/pagerduty/schedules.go index cafca24..3e90060 100644 --- a/pagerduty/schedules.go +++ b/pagerduty/schedules.go @@ -1,99 +1,109 @@ package pagerduty -import( - "fmt" - "time" - log "github.com/Sirupsen/logrus" +import ( + "time" + + "github.com/PagerDuty/go-pagerduty" + log "github.com/Sirupsen/logrus" ) type Schedules []Schedule -type Schedule struct{ - Id string - Name string - Timezone string `json:"time_zone"` - CurrentPeriod *CallPeriod - NextPeriod *CallPeriod +type Schedule struct { + Id string + Name string + Timezone string `json:"time_zone"` + CurrentPeriod *CallPeriod + NextPeriod *CallPeriod } -type CallPeriod struct{ - Start time.Time - User string +type CallPeriod struct { + Start time.Time + User string } // Fetch the main schedule list then the details about specific schedules -func (a *Api) Schedules() (Schedules, error){ - var schdList Schedules - - err := a.requestThing("schedules", &schdList) - if err != nil { - return schdList, err - } - - var today string = time.Now().UTC().Format("2006-01-02") - var nextWeek string = time.Now().UTC().Add(time.Hour*24*7).Format("2006-01-02") - - for i,_ := range schdList{ - schd := &schdList[i] - rsp, err := a.request(fmt.Sprintf("schedules/%s/entries?since=%s&until=%s&time_zone=%s&overflow=true", schd.Id, today, nextWeek, a.timezone)) - if err != nil { - return schdList, err - } - - var schdInfo struct{ - Entries []struct{ - User struct{ - Id string - } - Start time.Time - End time.Time - } - } - - rsp.Unmarshal(&schdInfo) - - var activeEntries int - for _,se := range schdInfo.Entries{ - if se.Start.Before(time.Now().UTC()) && se.End.After(time.Now().UTC()){ - if activeEntries == 0{ - p := CallPeriod{} - p.Start = se.Start - p.User = se.User.Id - schd.CurrentPeriod = &p - } - activeEntries += 1 - } - - if se.Start.After(time.Now().UTC()) && (schd.NextPeriod == nil || se.Start.Before(schd.NextPeriod.Start)){ - p := CallPeriod{} - p.Start = se.Start - p.User = se.User.Id - schd.NextPeriod = &p - } - } - - lf := log.Fields{ - "id": schd.Id, - } - - if schd.CurrentPeriod == nil{ - log.WithFields(lf).Warning("No active current period for schedule") - } else { - lf["currentCall"] = schd.CurrentPeriod.User - } - - if schd.NextPeriod == nil{ - log.WithFields(lf).Warning("No active next period for schedule") - } else { - lf["nextCall"] = schd.NextPeriod.User - lf["changeover"] = schd.NextPeriod.Start - } - - if activeEntries > 1{ - log.WithFields(lf).Warning("Multiple active schedules") - } - log.WithFields(lf).Debug("Got schedule entries") - } - - return schdList, nil +func (a *Api) Schedules() (Schedules, error) { + var schdList Schedules + + res, err := a.client.ListSchedules(pagerduty.ListSchedulesOptions{}) + if err != nil { + return schdList, err + } + + var today string = time.Now().UTC().Format("2006-01-02") + var nextWeek string = time.Now().UTC().Add(time.Hour * 24 * 7).Format("2006-01-02") + + for _, bareSchedule := range res.Schedules { + schd := &Schedule{} + + schd.Id = bareSchedule.ID + schd.Name = bareSchedule.Name + schd.Timezone = bareSchedule.TimeZone + + res, err := a.client.GetSchedule(bareSchedule.ID, pagerduty.GetScheduleOptions{ + TimeZone: a.timezone, + Since: today, + Until: nextWeek, + }) + if err != nil { + return schdList, err + } + + var activeEntries int + for _, se := range res.FinalSchedule.RenderedScheduleEntries { + start, err := time.Parse(time.RFC3339Nano, se.Start) + if err != nil { + return schdList, err + } + + end, err := time.Parse(time.RFC3339Nano, se.End) + if err != nil { + return schdList, err + } + + if start.Before(time.Now().UTC()) && end.After(time.Now().UTC()) { + if activeEntries == 0 { + p := CallPeriod{} + p.Start = start + p.User = se.User.ID + schd.CurrentPeriod = &p + } + activeEntries += 1 + } + + if start.After(time.Now().UTC()) && (schd.NextPeriod == nil || start.Before(schd.NextPeriod.Start)) { + p := CallPeriod{} + p.Start = start + p.User = se.User.ID + schd.NextPeriod = &p + } + + schdList = append(schdList, *schd) + } + + lf := log.Fields{ + "id": schd.Id, + } + + if schd.CurrentPeriod == nil { + log.WithFields(lf).Warning("No active current period for schedule") + } else { + lf["currentCall"] = schd.CurrentPeriod.User + } + + if schd.NextPeriod == nil { + log.WithFields(lf).Warning("No active next period for schedule") + } else { + lf["nextCall"] = schd.NextPeriod.User + lf["changeover"] = schd.NextPeriod.Start + } + + if activeEntries > 1 { + log.WithFields(lf).Warning("Multiple active schedules") + } + log.WithFields(lf).Debug("Got schedule entries") + } + + return schdList, nil } diff --git a/pagerduty/users.go b/pagerduty/users.go index ef1c434..64f8422 100644 --- a/pagerduty/users.go +++ b/pagerduty/users.go @@ -1,20 +1,32 @@ package pagerduty +import ( + "github.com/PagerDuty/go-pagerduty" +) + type Users []User -type User struct{ - Id string - Name string - Email string +type User struct { + Id string + Name string + Email string } -func (a *Api) Users() (Users, error){ - var usr Users +func (a *Api) Users() (Users, error) { + var usr Users + + res, err := a.client.ListUsers(pagerduty.ListUsersOptions{}) + if err != nil { + return usr, err + } - err := a.requestThing("users", &usr) - if err != nil { - return usr, err - } + for _, user := range res.Users { + usr = append(usr, User{ + Id: user.ID, + Name: user.Name, + Email: user.Email, + }) + } - return usr, nil + return usr, nil }