Skip to content

Commit

Permalink
OSD- 8119, OSD 8453,OSD 8311 -Oncall complete implementation with tes…
Browse files Browse the repository at this point in the history
…ts (#53)

* two seperate functions added

* next-oncall, pagination of all teams fixed

* mod cleaned

* teamsOncall test implemented

* tests fully implemented for pdcli oncall

* Refactored oncall to fetch the user data

* reverted go back to 1.16 in mod

Co-authored-by: Supreeth Basabattini <[email protected]>
  • Loading branch information
MitaliBhalla and Supreeth Basabattini authored Oct 26, 2021
1 parent 1bc9af8 commit c30bbc2
Show file tree
Hide file tree
Showing 6 changed files with 394 additions and 59 deletions.
147 changes: 90 additions & 57 deletions cmd/pdcli/oncall/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,38 +14,49 @@ limitations under the License.
package oncall

import (
"fmt"
"strings"
"time"

"github.com/PagerDuty/go-pagerduty"
"github.com/openshift/pagerduty-short-circuiter/pkg/client"
"github.com/openshift/pagerduty-short-circuiter/pkg/constants"
"github.com/openshift/pagerduty-short-circuiter/pkg/output"
"github.com/openshift/pagerduty-short-circuiter/pkg/pdcli"
"github.com/spf13/cobra"
)

var options struct {
allTeams bool
nextOncall bool
}

var Cmd = &cobra.Command{
Use: "oncall",
Short: "oncall to the PagerDuty CLI",
Long: "Running the pdcli oncall command will display the current primary and secondary oncall SRE",
Args: cobra.NoArgs,
RunE: OnCall,
RunE: oncallHandler,
}

type User struct {
EscalationPolicy string
OncallRole string
Name string
Start string
End string
func init() {

// Shows who is on-call in all teams
Cmd.Flags().BoolVarP(
&options.allTeams,
"all",
"a",
false,
"Show who is on-call in all teams",
)

// Next oncall
Cmd.Flags().BoolVar(
&options.nextOncall,
"next-oncall",
false,
"Show the current user's next oncall schedule",
)
}

//Oncall implements the fetching of current roles and names of users
func OnCall(cmd *cobra.Command, args []string) error {
// oncallHandler is the main handler for pdcli oncall.
func oncallHandler(cmd *cobra.Command, args []string) (err error) {

var callOpts pagerduty.ListOnCallOptions
callOpts.ScheduleIDs = []string{constants.PrimaryScheduleID, constants.SecondaryScheduleID, constants.OncallManager}
var onCallUsers []pdcli.OncallUser

// Establish a secure connection with the PagerDuty API
client, err := client.NewClient().Connect()
Expand All @@ -54,64 +65,86 @@ func OnCall(cmd *cobra.Command, args []string) error {
return err
}

oncallListing, err := client.ListOnCalls(callOpts)
switch {
case options.allTeams:
// Fetch oncall data from all teams
onCallUsers, err = pdcli.AllTeamsOncall(client)

if err != nil {
return err
if err != nil {
return err
}

}
printOncalls(onCallUsers)

//oncallData stores struct objects for each Escalation Policy
var oncallData []User
case options.nextOncall:
onCallUsers, err = pdcli.UserNextOncallSchedule(client)

//OnCalls array contains all information about the API object
for _, y := range oncallListing.OnCalls {
if err != nil {
return err
}

timeConversionStart := timeConversion(y.Start)
timeConversionEnd := timeConversion(y.End)
printOncalls(onCallUsers)

temp := User{}
temp.EscalationPolicy = y.EscalationPolicy.Summary
temp.OncallRole = y.Schedule.Summary
temp.Name = y.User.Summary
temp.Start = timeConversionStart
temp.End = timeConversionEnd
oncallData = append(oncallData, temp)
default:
// Fetch oncall data from Platform-SRE team
onCallUsers, err = pdcli.TeamSREOnCall(client)

}
if err != nil {
return err
}

printOncalls(oncallData)
printOncalls(onCallUsers)
}

return nil
}

//timeConversion converts timestamp into time and date
func timeConversion(s string) string {
//printOncalls prints data in a tabular form.
func printOncalls(oncallData []pdcli.OncallUser) {

// Initialize table writer
table := output.NewTable(false)

timeString := s
timeConverted, err := time.Parse(time.RFC3339, timeString)
for _, v := range oncallData {

if err != nil {
fmt.Println(err)
var data []string

if v.EscalationPolicy != "" {
data = append(data, v.EscalationPolicy)
} else {
data = append(data, "N/A")
}

if v.Name != "" {
data = append(data, v.Name)
} else {
data = append(data, "N/A")
}

if v.OncallRole != "" {
data = append(data, v.OncallRole)
} else {
data = append(data, "N/A")
}

if v.Start != "" {
data = append(data, v.Start)
} else {
data = append(data, "N/A")
}

if v.End != "" {
data = append(data, v.End)
} else {
data = append(data, "N/A")
}

table.AddRow(data)
}
finalTimeString := timeConverted.String()
finalTimeString = strings.ReplaceAll(finalTimeString, " +0000 UTC", " UTC")

return finalTimeString

}
headers := []string{"Escalation Policy", "Name", "Oncall Role", "From", "To"}

//printOncalls prints data in a tabular form
func printOncalls(oncallData []User) {
var printData []string
table := output.NewTable(false)
headers := []string{"Escalation Policy", "Oncall Role", "Name", "Start", "End"}
table.SetHeaders(headers)
for _, v := range oncallData {
printData = []string{v.EscalationPolicy, v.OncallRole, v.Name, v.Start, v.End}
table.AddRow(printData)
}

table.SetData()
table.Print()
}
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
Expand Down Expand Up @@ -507,6 +508,7 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
Expand Down
3 changes: 2 additions & 1 deletion pkg/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,6 @@ const (
SecondaryScheduleID = "P4TU2IT"
OncallIDWeekend = "P7CC7UN"
OncallManager = "P1WFZIG"
OncallId = "PA4586M"
OncallID = "PA4586M"
InvestigatorID = "PWQAANA"
)
1 change: 1 addition & 0 deletions pkg/output/tableoutput.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func NewTable(mergeCol bool) *table {
table.writer.SetHeaderLine(false)
table.writer.SetAutoFormatHeaders(true)
table.writer.SetAlignment(tablewriter.ALIGN_LEFT)
table.writer.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.writer.SetRowSeparator("")
table.writer.SetCenterSeparator("")
table.writer.SetColumnSeparator("")
Expand Down
158 changes: 158 additions & 0 deletions pkg/pdcli/oncallHandlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package pdcli

import (
"sort"
"time"

"github.com/PagerDuty/go-pagerduty"
"github.com/openshift/pagerduty-short-circuiter/pkg/client"
"github.com/openshift/pagerduty-short-circuiter/pkg/constants"
"github.com/openshift/pagerduty-short-circuiter/pkg/utils"
)

type OncallUser struct {
EscalationPolicy string
OncallRole string
Name string
Start string
End string
}

//TeamSREOnCall fetches the current roles and names of on-call users.
func TeamSREOnCall(c client.PagerDutyClient) ([]OncallUser, error) {
var callOpts pagerduty.ListOnCallOptions
var oncallData []OncallUser

callOpts.ScheduleIDs = []string{
constants.PrimaryScheduleID,
constants.SecondaryScheduleID,
constants.OncallManager,
constants.OncallIDWeekend,
constants.InvestigatorID,
}

// Fetch the oncall data from pagerduty API
oncallListing, err := c.ListOnCalls(callOpts)

if err != nil {
return nil, err
}

// OnCalls array contains all information about the API object
for _, y := range oncallListing.OnCalls {

timeConversionStart, err := utils.FormatTimestamp(y.Start)

if err != nil {
return nil, err
}

timeConversionEnd, err := utils.FormatTimestamp(y.End)

if err != nil {
return nil, err
}

// Parse the oncall data to OncallUser object
temp := OncallUser{}
temp.EscalationPolicy = y.EscalationPolicy.Summary
temp.OncallRole = y.Schedule.Summary
temp.Name = y.User.Summary
temp.Start = timeConversionStart
temp.End = timeConversionEnd
oncallData = append(oncallData, temp)
}

return oncallData, err
}

// AllTeamsOncall displays the oncall data of all Red Hat PagerDuty teams.
func AllTeamsOncall(c client.PagerDutyClient) ([]OncallUser, error) {
var callOpts pagerduty.ListOnCallOptions
var oncallData []OncallUser

offset := []uint{0, 100, 200, 300, 400, 500, 600}

callOpts.Limit = 100
callOpts.Offset = 0

for _, o := range offset {
callOpts.Limit = 100
callOpts.Offset = o
callOpts.Earliest = true

// Fetch the oncall data from pagerduty API
oncallListing, err := c.ListOnCalls(callOpts)

if err != nil {
return nil, err
}

// Parse oncall data
for _, y := range oncallListing.OnCalls {
temp := OncallUser{}
temp.EscalationPolicy = y.EscalationPolicy.Summary
temp.OncallRole = y.Schedule.Summary
temp.Name = y.User.Summary
temp.Start = y.Start
temp.End = y.End
oncallData = append(oncallData, temp)
}
}

// Sort by escalation policy
sort.SliceStable(oncallData, func(i, j int) bool {
return oncallData[i].EscalationPolicy < oncallData[j].EscalationPolicy
})

return oncallData, nil
}

// UserNextOncallSchedule displays the current user's
// next oncall schedule.
func UserNextOncallSchedule(c client.PagerDutyClient) ([]OncallUser, error) {
var callOpts pagerduty.ListOnCallOptions
var nextOncallData []OncallUser

callOpts.Until = time.Now().AddDate(0, 3, 0).String()

user, err := c.GetCurrentUser(pagerduty.GetCurrentUserOptions{})

if err != nil {
return nil, err
}

callOpts.UserIDs = append(callOpts.UserIDs, user.ID)

// Fetch the oncall data from pagerduty API
onCallOncallUser, err := c.ListOnCalls(callOpts)

if err != nil {
return nil, err
}

for _, y := range onCallOncallUser.OnCalls {

start, err := utils.FormatTimestamp(y.Start)

if err != nil {
return nil, err
}

end, err := utils.FormatTimestamp(y.End)

if err != nil {
return nil, err
}

temp := OncallUser{}
temp.EscalationPolicy = y.EscalationPolicy.Summary
temp.OncallRole = y.Schedule.Summary
temp.Name = y.User.Summary
temp.Start = start
temp.End = end
nextOncallData = append(nextOncallData, temp)
}

return nextOncallData, nil
}
Loading

0 comments on commit c30bbc2

Please sign in to comment.