Commit 29785279 authored by John Jarvis's avatar John Jarvis

Refactor report, better issue reporting and remove the WoW.

parent a79bb23b
Pipeline #16485254 passed with stage
in 1 minute and 40 seconds
......@@ -24,29 +24,40 @@ type PagerDutySchedule struct {
// PagerDuty for PD schedules
type PagerDuty struct {
Token string `yaml:"token"`
ServiceID string `yaml:"service_id"`
Schedules []PagerDutySchedule `yaml:"schedules"`
PrimarySchedule string `yaml:"primary"`
SecondarySchedule string `yaml:"secondary"`
}
// GitLab Project for infrastructure
type GitLab struct {
Token string `yaml:"token"`
ProjectID int `yaml:"project_id"`
// Project has data for a GitLab project
type Project struct {
ID int `yaml:"id"`
}
// Projects for project ids
type Projects struct {
Infrastructure Project `yaml:"infrastructure"`
ReportProject Project `yaml:"report_project"`
}
// APITokens for api tokens
type APITokens struct {
GitLab string `yaml:"gitlab"`
GitLabDev string `yaml:"gitlab_dev"`
Grafana string `yaml:"grafana"`
PagerDuty string `yaml:"pager_duty"`
}
// Config for holding report settings
type Config struct {
Milestone string `yaml:"milestone"`
PagerDuty PagerDuty `yaml:"pagerduty"`
ProjectID int `yaml:"project_id"`
GitLab GitLab `yaml:"gitlab"`
APITokens APITokens `yaml:"api_tokens"`
Projects Projects `yaml:"projects"`
DayOffset int `yaml:"day_offset"`
OncallLabel string `yaml:"oncall_label"`
PagerDuty PagerDuty `yaml:"pagerduty"`
WeeklyOps []WeeklyOps `yaml:"weekly_ops"`
GrafanaKey string `yaml:"grafana_key"`
}
// ParseConfig parses the yaml configuration from a reader
......
......@@ -41,8 +41,8 @@ func TestParseConfigCorrectly(t *testing.T) {
if err != nil {
t.Fatalf("failed to parse the configuration: %s", err)
}
assertEquals(t, "gitlab token", "*** GL API TOKEN GOES HERE ***", c.GitLab.Token)
assertEquals(t, "pagerduty token", "*** PD API TOKEN GOES HERE ***", c.PagerDuty.Token)
assertEquals(t, "gitlab token", "*** GITLAB API TOKEN GOES HERE ***", c.APITokens.GitLab)
assertEquals(t, "pagerduty token", "*** PD API TOKEN GOES HERE ***", c.APITokens.PagerDuty)
}
func assertEquals(t *testing.T, name string, expected, actual interface{}) {
......
......@@ -36,6 +36,9 @@ func main() {
if err != nil {
log.Fatalln(err)
}
if config.Projects.Infrastructure.ID == 0 {
log.Fatalln("ERROR: It looks like you have an old settings file, please update using oncall-settings-example.yaml")
}
opts := oncall.ReportOptions{}
report := oncall.NewReport(config, opts)
title := "OnCall report for period: " +
......
......@@ -13,7 +13,9 @@ api_tokens:
projects:
infrastructure:
id: 1304532
oncall_robot:
report_project:
# Where to create the issue
# oncall_robot
id: 3950829
# Number of days to look back for the
......
......@@ -10,12 +10,12 @@ import (
)
func getMergeRequestsByProject(config *config.Config) []*gitlab.MergeRequest {
git := gitlab.NewClient(nil, config.GitLab.Token)
git := gitlab.NewClient(nil, config.APITokens.GitLab)
opts := gitlab.ListProjectMergeRequestsOptions{}
dayInterval := time.Now().UTC().AddDate(0, 0, -config.DayOffset)
opts.CreatedAfter = &dayInterval
if mrs, _, err := git.MergeRequests.ListProjectMergeRequests(config.GitLab.ProjectID, &opts); err != nil {
if mrs, _, err := git.MergeRequests.ListProjectMergeRequests(config.Projects.Infrastructure.ID, &opts); err != nil {
panic(err)
} else {
return mrs
......@@ -23,49 +23,52 @@ func getMergeRequestsByProject(config *config.Config) []*gitlab.MergeRequest {
}
func getIssuesClosedDuringMilestone(config *config.Config) []*gitlab.Issue {
git := gitlab.NewClient(nil, config.GitLab.Token)
labels := []string{config.OncallLabel}
opts := gitlab.ListProjectIssuesOptions{}
opts.Labels = labels
state := "closed"
opts.State = &state
opts.Milestone = &config.Milestone
dayInterval := time.Now().UTC().AddDate(0, 0, -config.DayOffset)
opts.CreatedAfter = &dayInterval
if issues, _, err := git.Issues.ListProjectIssues(config.GitLab.ProjectID, &opts); err != nil {
panic(err)
} else {
return issues
}
}
func getIssuesOpenedDuringShift(config *config.Config) []*gitlab.Issue {
git := gitlab.NewClient(nil, config.GitLab.Token)
labels := []string{config.OncallLabel}
git := gitlab.NewClient(nil, config.APITokens.GitLab)
var opts gitlab.ListProjectIssuesOptions
opts.Labels = labels
opts.ListOptions.PerPage = 100
dayInterval := time.Now().UTC().AddDate(0, 0, -config.DayOffset)
opts.CreatedAfter = &dayInterval
log.Println("Project ID", config.GitLab.ProjectID)
if issues, _, err := git.Issues.ListProjectIssues(config.GitLab.ProjectID, &opts); err != nil {
panic(err)
} else {
return issues
allIssues := make([]*gitlab.Issue, 0)
for {
issues, r, err := git.Issues.ListProjectIssues(config.Projects.Infrastructure.ID, &opts)
if err != nil {
panic(err)
}
for _, issue := range issues {
allIssues = append(allIssues, issue)
}
if r.NextPage == 0 {
break
}
log.Println("Setting next page to ", r.NextPage)
opts.ListOptions.Page = r.NextPage
}
return allIssues
}
func getIssuesOpenAll(config *config.Config) []*gitlab.Issue {
git := gitlab.NewClient(nil, config.GitLab.Token)
labels := []string{config.OncallLabel}
git := gitlab.NewClient(nil, config.APITokens.GitLab)
var opts gitlab.ListProjectIssuesOptions
opts.Labels = labels
opts.ListOptions.PerPage = 100
state := "opened"
opts.State = &state
if issues, _, err := git.Issues.ListProjectIssues(config.GitLab.ProjectID, &opts); err != nil {
panic(err)
} else {
return issues
allIssues := make([]*gitlab.Issue, 0)
for {
issues, r, err := git.Issues.ListProjectIssues(config.Projects.Infrastructure.ID, &opts)
if err != nil {
panic(err)
}
for _, issue := range issues {
allIssues = append(allIssues, issue)
}
if r.NextPage == 0 {
break
}
log.Println("Getting next page from response", r.NextPage)
opts.ListOptions.Page = r.NextPage
}
return allIssues
}
func filterIssuesByLabel(label string, issues []*gitlab.Issue) (filteredIssues []*gitlab.Issue) {
......
......@@ -61,30 +61,28 @@ func (r *Report) CreateReportIssue(title string) { // *gitlab.Issue {
opts.Title = &title
opts.Description = &desc
log.Println("Creating issue ...")
/*
start := time.Now()
issue, _, err := r.Clients.GitLab.Issues.CreateIssue(r.Config.ProjectID, &opts)
if err != nil {
log.Fatalf("Unable to create issue in GitLab project %d: %s", r.Config.ProjectID, err.Error())
}
secs := time.Since(start).Seconds()
log.Printf(" Created issue %s in %.2f seconds\n", issue.WebURL, secs)
return issue
*/
log.Println(desc)
start := time.Now()
issue, _, err := r.Clients.GitLab.Issues.CreateIssue(r.Config.Projects.ReportProject.ID, &opts)
if err != nil {
log.Fatalf("Unable to create issue in GitLab project %d: %s", r.Config.Projects.ReportProject.ID, err.Error())
}
secs := time.Since(start).Seconds()
log.Printf(" Created issue %s in %.2f seconds\n", issue.WebURL, secs)
}
// Upload graphs to the gitlab project
func (r *Report) uploadGraphs(graphs <-chan GraphData) (opsGraphs []WeeklyOpsGraph) {
log.Println("Uploading graphs to GitLab project ...")
start := time.Now()
git := gitlab.NewClient(nil, r.Config.GitLab.Token)
git := gitlab.NewClient(nil, r.Config.APITokens.GitLab)
for graph := range graphs {
defer os.Remove(graph.Fname)
opsGraph := WeeklyOpsGraph{}
log.Println(" uploading", graph.Name)
pf, _, err := git.Projects.UploadFile(r.Config.ProjectID, graph.Fname)
pf, _, err := git.Projects.UploadFile(r.Config.Projects.ReportProject.ID, graph.Fname)
if err != nil {
log.Fatalf("Unable to upload graphs to project id %d : %s", r.Config.ProjectID, err.Error())
log.Fatalf("Unable to upload graphs to project id %d : %s", r.Config.Projects.ReportProject.ID, err.Error())
}
opsGraph.URL = pf.URL
opsGraph.Name = graph.Name
......@@ -104,7 +102,7 @@ func (r *Report) downloadGraphs() (graphs <-chan GraphData) {
ch := make(chan GraphData, numGraphs)
wg.Add(numGraphs)
for _, graph := range weeklyOpsGraphs(r.Config) {
go makeRequest(graph.URL, graph.Name, ch, &wg, r.Config.GrafanaKey)
go makeRequest(graph.URL, graph.Name, ch, &wg, r.Config.APITokens.Grafana)
}
wg.Wait()
close(ch)
......@@ -136,25 +134,24 @@ func (r *Report) generateTemplate(weeklyOpsGraphs []WeeklyOpsGraph) string {
inc.CreatedAt = p.CreatedAt
templateData.Incidents = append(templateData.Incidents, inc)
}
oncallIssues := getIssuesOpenedDuringShift(r.Config)
closedIssues := getIssuesClosedDuringMilestone(r.Config)
shiftIssues := getIssuesOpenedDuringShift(r.Config)
allOpenIssues := getIssuesOpenAll(r.Config)
// Issue stats
templateData.IssuesOpenedDuringShift.Count = len(oncallIssues)
templateData.IssuesOpenedDuringShift.AccessRequest = len(filterIssuesByLabel("access request", oncallIssues))
templateData.IssuesOpenedDuringShift.Critical = len(filterIssuesByLabel("critical", oncallIssues))
templateData.IssuesClosedDuringMilestone.Count = len(closedIssues)
templateData.IssuesClosedDuringMilestone.AccessRequest = len(filterIssuesByLabel("access request", closedIssues))
templateData.IssuesClosedDuringMilestone.Critical = len(filterIssuesByLabel("critical", closedIssues))
templateData.IssuesOpenedDuringShift.Count = len(shiftIssues)
templateData.IssuesOpenedDuringShift.OnCall = len(filterIssuesByLabel("oncall", shiftIssues))
templateData.IssuesOpenedDuringShift.AccessRequest = len(filterIssuesByLabel("access request", shiftIssues))
templateData.IssuesOpenedDuringShift.Critical = len(filterIssuesByLabel("critical", shiftIssues))
templateData.IssuesOpenedDuringShift.CorrectiveAction = len(filterIssuesByLabel("corrective action", shiftIssues))
templateData.IssuesOpenAll.Count = len(allOpenIssues)
templateData.IssuesOpenAll.OnCall = len(filterIssuesByLabel("oncall", allOpenIssues))
templateData.IssuesOpenAll.AccessRequest = len(filterIssuesByLabel("access request", allOpenIssues))
templateData.IssuesOpenAll.Critical = len(filterIssuesByLabel("critical", allOpenIssues))
templateData.IssuesOpenAll.CorrectiveAction = len(filterIssuesByLabel("corrective action", allOpenIssues))
// Populate list of issues for the template
for _, p := range allOpenIssues {
// Populate list of issues for the template - oncall issues
for _, p := range filterIssuesByLabel("oncall", allOpenIssues) {
issue := Issue{}
created := p.CreatedAt.Format(time.RFC822)
if p.Assignee.Username == "" {
......@@ -164,7 +161,7 @@ func (r *Report) generateTemplate(weeklyOpsGraphs []WeeklyOpsGraph) string {
issue.Summary = p.Title
issue.URL = p.WebURL
issue.Assignee = p.Assignee.Username
templateData.Issues = append(templateData.Issues, issue)
templateData.OnCallIssues = append(templateData.OnCallIssues, issue)
}
// Populate weekly ops graphs for template using uploaded files
for _, graph := range weeklyOpsGraphs {
......@@ -192,7 +189,7 @@ func (r *Report) generateTemplate(weeklyOpsGraphs []WeeklyOpsGraph) string {
func newReportClients(opts ReportOptions, config *config.Config) *ReportClients {
tc := ReportClients{
GitLab: gitlab.NewClient(nil, config.GitLab.Token),
GitLab: gitlab.NewClient(nil, config.APITokens.GitLab),
}
return &tc
}
......
......@@ -13,7 +13,7 @@ func getOncallPersons(config *config.Config, schedule string) []pagerduty.User {
var options pagerduty.ListOnCallUsersOptions
options.Since = nowPdDateWithOffset(-config.DayOffset)
options.Until = nowPdDate()
client := pagerduty.NewClient(config.PagerDuty.Token)
client := pagerduty.NewClient(config.APITokens.PagerDuty)
if oncall, err := client.ListOnCallUsers(schedule, options); err != nil {
panic(err)
} else {
......@@ -22,7 +22,7 @@ func getOncallPersons(config *config.Config, schedule string) []pagerduty.User {
}
func getOncallIncidents(config *config.Config) []pagerduty.Incident {
client := pagerduty.NewClient(config.PagerDuty.Token)
client := pagerduty.NewClient(config.APITokens.PagerDuty)
var opts pagerduty.ListIncidentsOptions
opts.ServiceIDs = []string{config.PagerDuty.ServiceID}
opts.Since = nowPdDateWithOffset(-config.DayOffset)
......
......@@ -14,9 +14,12 @@ type TeamMember struct {
// IssueStats for report issues
type IssueStats struct {
Count int
AccessRequest int
Critical int
Count int
OnCall int
AccessRequest int
Critical int
Outage int
CorrectiveAction int
}
// Issue for oncall labeled issues
......@@ -36,12 +39,14 @@ type Incident struct {
//TemplateData for report generation
type TemplateData struct {
WeeklyOpsGraphs []WeeklyOpsGraph
TeamMembers []TeamMember
Incidents []Incident
Issues []Issue
IncidentCount int
IssuesOpenAll IssueStats
IssuesOpenedDuringShift IssueStats
IssuesClosedDuringMilestone IssueStats
WeeklyOpsGraphs []WeeklyOpsGraph
TeamMembers []TeamMember
Incidents []Incident
OnCallIssues []Issue
CriticalIssues []Issue
OutageIssues []Issue
CorrectiveActionIssues []Issue
IncidentCount int
IssuesOpenAll IssueStats
IssuesOpenedDuringShift IssueStats
}
......@@ -17,25 +17,29 @@
{{- end }}
## Issues
### Stats for the last oncall period
* Total number of oncall issues opened in the last on call shift: **{{.IssuesOpenedDuringShift.Count}}**
* Access Request: **{{.IssuesOpenedDuringShift.AccessRequest}}**
* Critical: **{{.IssuesOpenedDuringShift.Critical}}**
* Total number of oncall issues closed in this milestone: **{{.IssuesClosedDuringMilestone.Count}}**
* Access Request: **{{.IssuesClosedDuringMilestone.AccessRequest}}**
* Critical: **{{.IssuesClosedDuringMilestone.Critical}}**
### 7 Day OnCall Issue Stats
## Open OnCall Issues
* Oncall issues : **{{.IssuesOpenedDuringShift.OnCall}}**
* Access Request : **{{.IssuesOpenedDuringShift.AccessRequest}}**
* Critical : **{{.IssuesOpenedDuringShift.Critical}}**
* Outage : **{{.IssuesOpenedDuringShift.Outage}}**
* Corrective Action : **{{.IssuesOpenedDuringShift.CorrectiveAction}}**
* Total number of open oncall issues: **{{.IssuesOpenAll.Count}}**
* Access Request: **{{.IssuesOpenAll.AccessRequest}}**
* Critical: **{{.IssuesOpenAll.Critical}}**
### Open OnCall Issue Stats
* [Oncall issues](https://gitlab.com/gitlab-com/infrastructure/issues?scope=all&utf8=%E2%9C%93&state=opened&label_name[]=oncall) : **{{.IssuesOpenAll.OnCall}}**
* [Access Request](https://gitlab.com/gitlab-com/infrastructure/issues?scope=all&utf8=%E2%9C%93&state=opened&label_name[]=access%20request) : **{{.IssuesOpenAll.AccessRequest}}**
* [Critical](https://gitlab.com/gitlab-com/infrastructure/issues?scope=all&utf8=%E2%9C%93&state=opened&label_name[]=critical) : **{{.IssuesOpenAll.Critical}}**
* [Outage](https://gitlab.com/gitlab-com/infrastructure/issues?scope=all&utf8=%E2%9C%93&state=opened&label_name[]=outage) : **{{.IssuesOpenAll.Outage}}**
* [Corrective Action](https://gitlab.com/gitlab-com/infrastructure/issues?scope=all&utf8=%E2%9C%93&state=opened&label_name[]=corrective%20action) : **{{.IssuesOpenAll.CorrectiveAction}}**
### Open Oncall Issues
| Created | Assignee | Summary |
| ------ | ---------| ------- |
{{- range .Issues }}
{{- range .OnCallIssues }}
| [{{.CreatedAt}}]({{.URL}}) | {{.Assignee }} | {{.Summary}} |
{{- end }}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment