Skip to content

Commit

Permalink
Completely rework the system to pull statues (#97)
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 authored Sep 20, 2024
1 parent 57a9f80 commit ef9776d
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://www.dockerstatus.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 ef9776d

Please sign in to comment.