Commit b8507bda authored by John Jarvis's avatar John Jarvis

Merge branch 'jarv/refactor2' into 'master'

Jarv/refactor2

See merge request gl-infra/oncall-robot-assistant!14
parents aec6a895 9eeb8d4b
Pipeline #16485290 passed with stage
in 1 minute and 21 seconds
......@@ -10,39 +10,57 @@ import (
"gopkg.in/yaml.v2"
)
// WeeklyOps for performance graphs
type WeeklyOps struct {
Name string `yaml:"name"`
Url string `yaml:"url"`
URL string `yaml:"url"`
}
// PagerDutySchedule for pd rotations
type PagerDutySchedule struct {
Name string `yaml:"name"`
Id string `yaml:"id"`
ID string `yaml:"id"`
}
// PagerDuty for PD schedules
type PagerDuty struct {
Token string `yaml:"token"`
ServiceId string `yaml:"service_id"`
ServiceID string `yaml:"service_id"`
Schedules []PagerDutySchedule `yaml:"schedules"`
PrimarySchedule string `yaml:"primary"`
SecondarySchedule string `yaml:"secondary"`
}
type GitLab struct {
Token string `yaml: "token"`
Id 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"`
}
// Parses the yaml configuration from a reader
// ParseConfig parses the yaml configuration from a reader
func ParseConfig(r io.Reader) (Config, error) {
configBytes, err := ioutil.ReadAll(r)
config := Config{}
......@@ -56,17 +74,20 @@ func ParseConfig(r io.Reader) (Config, error) {
return config, nil
}
// readConfig will attempt to read the different configuration
// ReadConfig will attempt to read the different configuration
// parameters from a yaml formatted file.
func ReadConfig(f string) (*Config, error) {
var cfg Config
if _, err := os.Stat(f); os.IsNotExist(err) {
return nil, errors.New(err.Error())
}
if content, err := ioutil.ReadFile(f); err != nil {
content, err := ioutil.ReadFile(f)
if err != nil {
return nil, errors.New(err.Error())
}
err = yaml.Unmarshal(content, &cfg)
if err != nil {
return nil, errors.New(err.Error())
} else {
yaml.Unmarshal(content, &cfg)
}
return &cfg, nil
}
......@@ -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{}) {
......
......@@ -3,29 +3,30 @@ package main
import (
"flag"
"fmt"
"gitlab.com/gl-infra/oncall-robot-assistant/config"
oncall "gitlab.com/gl-infra/oncall-robot-assistant/oncall"
"log"
"os"
"path"
"time"
"gitlab.com/gl-infra/oncall-robot-assistant/config"
oncall "gitlab.com/gl-infra/oncall-robot-assistant/oncall"
)
const settings_fname = ".oncall-settings.yaml"
const settingsFname = ".oncall-settings.yaml"
func main() {
var filepath string
if _, err := os.Stat(path.Join(".", settings_fname)); err == nil {
filepath = path.Join(".", settings_fname)
if _, err := os.Stat(path.Join(".", settingsFname)); err == nil {
filepath = path.Join(".", settingsFname)
}
if _, err := os.Stat(path.Join(os.Getenv("HOME"), settings_fname)); err == nil {
filepath = path.Join(os.Getenv("HOME"), settings_fname)
if _, err := os.Stat(path.Join(os.Getenv("HOME"), settingsFname)); err == nil {
filepath = path.Join(os.Getenv("HOME"), settingsFname)
}
if _, err := os.Stat(filepath); os.IsNotExist(err) {
log.Fatal(fmt.Sprintf("ERROR: Unable to locate %s", settings_fname))
log.Fatal(fmt.Sprintf("ERROR: Unable to locate %s", settingsFname))
}
cfgFile := flag.String("config", filepath, "the configuration file")
......@@ -35,8 +36,11 @@ func main() {
if err != nil {
log.Fatalln(err)
}
opts := oncall.OnCallReportOptions{}
report := oncall.NewOnCallReport(config, opts)
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: " +
time.Now().UTC().AddDate(0, 0, -7).Format("2006-01-02") +
" - " + time.Now().UTC().Format("2006-01-02")
......
# Copy this file to .settings.yaml
# and change the pagerduty and gitlab
# tokens
# Copy this file to ~/.oncall-settings.yaml
# and update api_tokens below
api_tokens:
gitlab: "*** GITLAB API TOKEN GOES HERE ***"
gitlab_dev: "*** GITLAB DEV API TOKEN GOES HERE ***"
# 1Password "oncall-robot grafana api key"
grafana: "*** GRAFANA API KEY GOES HERE ***"
# 1Password "oncall-robot pager duty api key"
pager_duty: "*** PD API TOKEN GOES HERE ***"
# Projects
projects:
infrastructure:
id: 1304532
report_project:
# Where to create the issue
# oncall_robot
id: 3950829
# Number of days to look back for the
# oncall report
day_offset: 7
# Project ID of the oncall-robot-assistant
# which is where the report is staged as
# an issue (currently the same as infrastructure)
project_id: 1304532
# Label used for oncall
oncall_label: oncall
# Milestone name for counting closed oncall
# issues
milestone: WoW
# API key for grafana
# 1Password oncall-robot grafana api key
grafana_key: "*** GRAFANA API KEY GOES HERE ***"
# Pagerduty configuration
pagerduty:
token: "*** PD API TOKEN GOES HERE ***"
service_id: PATDFCE
schedules:
- name: AMA
id: PKN8L5Q
- name: EU
id: PWDTHYI
primary:
secondary:
# GitLab API configuration
gitlab:
token: "*** GL API TOKEN GOES HERE ***"
# Project id of the infrastructure project
id: 1304532
# To add weekly ops graphs:
# * clicking "share" for the graph to add
......@@ -56,3 +53,6 @@ weekly_ops:
url: https://performance.gitlab.net/render/dashboard-solo/db/fleet-overview?refresh=5m&orgId=1&var-environment=prd&panelId=43&width=1000&height=500&tz=UTC
- name: NFS timeouts
url: https://performance.gitlab.net/render/dashboard-solo/db/triage-overview?refresh=1m&orgId=1&panelId=18&width=1000&height=500&tz=UTC
# Look for merge requests
# for the following projects
......@@ -2,54 +2,73 @@ package oncall
import (
"fmt"
"log"
"time"
gitlab "github.com/xanzy/go-gitlab"
config "gitlab.com/gl-infra/oncall-robot-assistant/config"
)
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
func getMergeRequestsByProject(config *config.Config) []*gitlab.MergeRequest {
git := gitlab.NewClient(nil, config.APITokens.GitLab)
opts := gitlab.ListProjectMergeRequestsOptions{}
dayInterval := time.Now().UTC().AddDate(0, 0, -config.DayOffset)
opts.CreatedAfter = &dayInterval
if issues, _, err := git.Issues.ListProjectIssues(config.GitLab.Id, &opts); err != nil {
if mrs, _, err := git.MergeRequests.ListProjectMergeRequests(config.Projects.Infrastructure.ID, &opts); err != nil {
panic(err)
} else {
return issues
return mrs
}
}
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
if issues, _, err := git.Issues.ListProjectIssues(config.GitLab.Id, &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.Id, &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) {
......
This diff is collapsed.
package oncall
import (
"time"
pagerduty "github.com/PagerDuty/go-pagerduty"
config "gitlab.com/gl-infra/oncall-robot-assistant/config"
"time"
)
// GetOncallPersons returns the team members who were on call
......@@ -12,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 {
......@@ -21,9 +22,9 @@ 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.ServiceIDs = []string{config.PagerDuty.ServiceID}
opts.Since = nowPdDateWithOffset(-config.DayOffset)
if incs, err := client.ListIncidents(opts); err != nil {
......
package oncall
// WeeklyOpsGraph weekly ops grpah
type WeeklyOpsGraph struct {
Name string
Url string
URL string
}
type OnCallTeamMember struct {
// TeamMember info for a team member
type TeamMember struct {
Schedule string
User string
}
/* Stats for oncall related
issues */
// 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
type Issue struct {
Summary string
Url string
URL string
CreatedAt string
Assignee string
}
// Incident for PD incident
type Incident struct {
Summary string
Url string
URL string
CreatedAt string
}
//TemplateData for report generation
type TemplateData struct {
WeeklyOpsGraphs []WeeklyOpsGraph
OnCallTeamMembers []OnCallTeamMember
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
}
......@@ -2,7 +2,7 @@
| Schedule | Username |
| -------- | -------- |
{{- range .OnCallTeamMembers}}
{{- range .TeamMembers}}
| {{.Schedule}} | {{.User}} |
{{- end}}
......@@ -13,36 +13,40 @@
| ------ | ------- |
{{- range .Incidents }}
| [{{.CreatedAt}}]({{.Url}}) | {{.Summary}} |
| [{{.CreatedAt}}]({{.URL}}) | {{.Summary}} |
{{- 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 }}
| [{{.CreatedAt}}]({{.Url}}) | {{.Assignee }} | {{.Summary}} |
{{- range .OnCallIssues }}
| [{{.CreatedAt}}]({{.URL}}) | {{.Assignee }} | {{.Summary}} |
{{- end }}
## Weekly Ops
{{- range .WeeklyOpsGraphs}}
### {{ .Name }}
![]({{.Url}})
![]({{.URL}})
{{- 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