Skip to content

Commit

Permalink
Completely rework the system to pull statues
Browse files Browse the repository at this point in the history
The CLI command now provides one status at a time, based on the name
provided. A future PR will reinstate multiple statues in some form or
fashion.

The status handling itself has been moved into a module Go package. This
will make it easier to add new status pages and status page providers in
the future.
  • Loading branch information
FelicianoTech committed Sep 20, 2024
1 parent e98edc4 commit 6d8280c
Show file tree
Hide file tree
Showing 13 changed files with 334 additions and 125 deletions.
143 changes: 18 additions & 125 deletions arc/cmd/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,148 +2,41 @@ package cmd

import (
"fmt"
"time"

"github.com/hubci/arc/arc/statuses"
"github.com/spf13/cobra"
)

type spPage struct {
ID string `json:"id"`
Name string `json:"name"`
URL string `url:"url"`
TimeZone string `json:"time_zone"`
UpdatedAt time.Time `json:"updated_at"`
}

type spStatus struct {
Indicator string `json:"indicator"`
Description string `json:"description"`
}

// StatusPage.io Response
type spResponse struct {
Page *spPage `json:"page"`
Status *spStatus `json:"status"`
}

type sStatus struct {
Updated time.Time `json:"updated"`
Status string `json:"status"`
StatusCode int `json:"status_code"`
}

type sResult struct {
StatusOverall *sStatus `json:"status_overall"`
}

// Status.io Response
type sResponse struct {
Result *sResult `json:"result"`
}

var (
cciFl bool

statusCmd = &cobra.Command{
Use: "status",
Short: "Provides the status page results for various DevOps services",
Use: "status <name>",
Short: "Provides the status page result for the provided name",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {

var cciResp *spResponse
var cfResp *spResponse
var ghResp *spResponse
var gitlabResp *sResponse
var linodeResp *spResponse
var doResp *spResponse
var dockerResp *sResponse

cciURL := "https://status.circleci.com/api/v2/status.json"
cfURL := "https://www.cloudflarestatus.com/api/v2/status.json"
ghURL := "https://www.githubstatus.com/api/v2/status.json"
gitlabURL := "https://status.gitlab.com/1.0/status/5b36dc6502d06804c08349f7"
linodeURL := "https://status.linode.com/api/v2/status.json"
doURL := "https://status.digitalocean.com/api/v2/status.json"
dockerURL := "https://status.docker.com/1.0/status/533c6539221ae15e3f000031"

client := New()

errCCI := client.getJSON(cciURL, &cciResp)
errCF := client.getJSON(cfURL, &cfResp)
errGH := client.getJSON(ghURL, &ghResp)
errGitlab := client.getJSON(gitlabURL, &gitlabResp)
errLinode := client.getJSON(linodeURL, &linodeResp)
errDO := client.getJSON(doURL, &doResp)
errDocker := client.getJSON(dockerURL, &dockerResp)

if errCCI != nil {
cciResp.Status.Indicator = "can't connect"
}

if errCF != nil {
cfResp.Status.Indicator = "can't connect"
}

if errGH != nil {
ghResp.Status.Indicator = "can't connect"
}

if errGitlab != nil {
gitlabResp.Result.StatusOverall.Status = "can't connect"
}

if errLinode != nil {
linodeResp.Status.Indicator = "can't connect"
}

if errDO != nil {
doResp.Status.Indicator = "can't connect"
statusPage, err := statuses.Page(args[0])
if err != nil {
fmt.Println(err)
return
}

if errDocker != nil {
dockerResp.Result.StatusOverall.Status = "can't connect"
}

var cciTabs = ""
if cciResp.Status.Indicator == "none" {
cciResp.Status.Indicator = ""
cciTabs = "\t"
}

var cfTabs = ""
if cfResp.Status.Indicator == "none" {
cfResp.Status.Indicator = ""
cfTabs = "\t"
}

var ghTabs = ""
if ghResp.Status.Indicator == "none" {
ghResp.Status.Indicator = ""
ghTabs = "\t"
}
client := New()

var linodeTabs = ""
if linodeResp.Status.Indicator == "none" {
linodeResp.Status.Indicator = ""
linodeTabs = "\t"
err = statusPage.Fetch(client.c)
if err != nil {
fmt.Println(err)
return
}

var doTabs = ""
if doResp.Status.Indicator == "none" {
doResp.Status.Indicator = ""
doTabs = "\t"
status, err := statusPage.Status()
if err != nil {
fmt.Println(err)
return
}

fmt.Println("Reporting status page results...")
fmt.Println("")
fmt.Printf("CircleCI:\t%s%s\t%s\n", cciResp.Status.Indicator, cciTabs, cciResp.Status.Description)
fmt.Printf("GitHub:\t\t%s%s\t%s\n", ghResp.Status.Indicator, ghTabs, ghResp.Status.Description)
fmt.Printf("Gitlab:\t\t\t\t%s\n", gitlabResp.Result.StatusOverall.Status)
fmt.Printf("Docker:\t\t\t\t%s\n", dockerResp.Result.StatusOverall.Status)
if !cciFl {
fmt.Printf("Cloudflare:\t\t%s%s\t%s\n", cfResp.Status.Indicator, cfTabs, cfResp.Status.Description)
fmt.Printf("Linode:\t\t%s%s\t%s\n", linodeResp.Status.Indicator, linodeTabs, linodeResp.Status.Description)
fmt.Printf("DigitalOcean:\t%s%s\t%s\n", doResp.Status.Indicator, doTabs, doResp.Status.Description)
}
fmt.Println(status)
},
}
)
Expand Down
8 changes: 8 additions & 0 deletions arc/statuses/circleci.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package statuses

/*
* Register the CircleCI status page.
*/
func init() {
RegisterStatusPageIOPage("circleci", "https://status.circleci.com/api/v2/status.json")
}
8 changes: 8 additions & 0 deletions arc/statuses/cloudflare.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package statuses

/*
* Register the CircleCI status page.
*/
func init() {
RegisterStatusPageIOPage("cloudflare", "https://www.cloudflarestatus.com/api/v2/status.json")
}
8 changes: 8 additions & 0 deletions arc/statuses/digitalocean.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package statuses

/*
* Register the CircleCI status page.
*/
func init() {
RegisterStatusPageIOPage("digitalocean", "https://status.digitalocean.com/api/v2/status.json")
}
8 changes: 8 additions & 0 deletions arc/statuses/docker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package statuses

/*
* Register the Docker (company) status page.
*/
func init() {
RegisterStatusIOPage("docker", "https://www.dockerstatus.com/1.0/status/533c6539221ae15e3f000031")
}
25 changes: 25 additions & 0 deletions arc/statuses/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package statuses

import "fmt"

/*
* NameTakenError occurs when a status page is registered with a name that is
* already registered.
*/
type NameTakenError struct {
Name string
}

/*
* Error returns the string representation of NameTakenError.
*/
func (e *NameTakenError) Error() string {
return fmt.Sprintf("Failed to register the name %s. It has already been taken.", e.Name)
}

/*
* newNameTakenError creates a new NameTakenError.
*/
func newNameTakenError(name string) *NameTakenError {
return &NameTakenError{Name: name}
}
8 changes: 8 additions & 0 deletions arc/statuses/github.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package statuses

/*
* Register the CircleCI status page.
*/
func init() {
RegisterStatusPageIOPage("github", "https://www.githubstatus.com/api/v2/status.json")
}
8 changes: 8 additions & 0 deletions arc/statuses/gitlab.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package statuses

/*
* Register the Docker (company) status page.
*/
func init() {
RegisterStatusIOPage("gitlab", "https://status.gitlab.com/1.0/status/5b36dc6502d06804c08349f7")
}
8 changes: 8 additions & 0 deletions arc/statuses/linode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package statuses

/*
* Register the CircleCI status page.
*/
func init() {
RegisterStatusPageIOPage("linode", "https://status.linode.com/api/v2/status.json")
}
48 changes: 48 additions & 0 deletions arc/statuses/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package statuses

import (
"fmt"
"net/url"
)

// This is how the package keeps track of all the status pages that it knows
// about.
var registeredPages map[string]StatusPage = map[string]StatusPage{}

/*
* GetPageURL returns a status page's API URL.
*/
func Page(name string) (StatusPage, error) {

sp, ok := registeredPages[name]
if !ok {
return nil, fmt.Errorf("Not a registered page.")
}

return sp, nil
}

/*
* registerPage adds a new status page to the list for a specific company /
* provider.
*
* name - should be a unique slug
*/
func registerPage(provider StatusPage) error {

name := provider.Name()

// If the name has already been used.
if _, ok := registeredPages[name]; ok {
return newNameTakenError(name)
}

// If the URL provided is no good.
if _, err := url.Parse(provider.URL()); err != nil {
return err
}

registeredPages[name] = provider

return nil
}
75 changes: 75 additions & 0 deletions arc/statuses/status_io.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package statuses

import (
"fmt"
"net/http"
"time"
)

/*
* This file defines the structs and other components needed to represent a
* status page hosted by Status.io.
*/

/*
* statusIOStatus represents the individual component status as well as most
* of the overall status.
*/
type StatusIOStatus struct {
Status string `json:"status"`
StatusCode int `json:"status_code"`
}

/*
* statusIOReponse represents the JSON response from the StatusPage.io API.
*/
type statusIOResponse struct {
Result struct {
StatusOverall struct {
Updated time.Time `json:"updated"`
*StatusIOStatus
} `json:"status_overall"`
} `json:"result"`
}

/*
* statusIOProvider represents the way to connect to the API and retrieve a
* response.
*/
type statusIOProvider struct {
name string
apiURL string
data *statusIOResponse
}

/*
* Fetch pulls in a response from the API.
*/
func (p *statusIOProvider) Fetch(c *http.Client) error {

return fetchJSON(c, p.apiURL, &p.data)
}

func (p *statusIOProvider) Name() string {
return p.name
}

func (p *statusIOProvider) URL() string {
return p.apiURL
}

func (p *statusIOProvider) Status() (string, error) {

if p.data == nil {
return "", fmt.Errorf("Data hasn't been retrieved.")
}

return p.data.Result.StatusOverall.Status, nil
}

/*
* RegisterStatusIOPage creates a new provider.
*/
func RegisterStatusIOPage(name, apiURL string) error {
return registerPage(&statusIOProvider{name: name, apiURL: apiURL})
}
Loading

0 comments on commit 6d8280c

Please sign in to comment.