Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

paasta-metastatus #57

Open
wants to merge 40 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
e03353b
go-swagger paasta api
May 3, 2020
8693980
configstore for access to paasta system and instance configs in json …
Nov 25, 2019
3352d39
sync go.mod with master
Jan 17, 2020
0851442
add more descriptive error messages to configstore/store.go
Jan 17, 2020
2220ecb
wrap map reads in lock/unlock
Jan 20, 2020
7716c6b
use sync.Map
Jan 24, 2020
19c762d
Adding support to get Yaml for k8s object (#45)
AshishKhatkar Mar 2, 2020
8e0003f
Use golang 1.12
Bronek Mar 5, 2020
90cf486
Refactor test options
Bronek Mar 5, 2020
53dcb69
Refactoring of Client access in Harness
Bronek Mar 6, 2020
fac838d
Upgrade controller-runtime package to v0.2.2
Bronek Mar 6, 2020
6d09865
configstore for access to paasta system and instance configs in json …
Nov 25, 2019
0d92a0d
sync go.mod with master
Jan 17, 2020
873aae1
fix tests
Apr 23, 2020
eabe1b6
fix more tests
Apr 23, 2020
9264633
add package docstring, add error context where missing
Apr 23, 2020
4deaee4
implement config stores with support for key hints
Nov 25, 2019
3ebbdb2
fix tests in pkg/config
Nov 26, 2019
269ae79
fix tests
Nov 27, 2019
3af160f
bring back old pkg/config, move new stuff to pkg/config_store
Nov 29, 2019
4a4f17b
add paasta api client
Dec 4, 2019
9efa7f5
add make gen-paasta-api target, regenerate paasta-api
Apr 23, 2020
bc04f19
update gen-paasta-api target to delete old files and to use current u…
Apr 23, 2020
81cb5af
remove old config_store, destructure config values
Apr 23, 2020
90b8a44
fix config loading, add basic cluster status func and call it in para…
Apr 24, 2020
2539fac
print dashboards
Apr 24, 2020
f3c1f3a
send api requests for metastatus
Apr 24, 2020
fc681db
groupings is a list
Apr 24, 2020
5334635
sync with configstore branch
Apr 24, 2020
6975a02
add return value to indicate missing key
Apr 27, 2020
aaa669e
load from all configs when matching file doesn't contain the key
Apr 27, 2020
66c3bd0
create context once and pass it around
Apr 27, 2020
e099eef
make testable, add tests, fix bugs
May 2, 2020
50ea198
sync with master
May 3, 2020
47e7e06
fix go.mod/sum
May 4, 2020
6e117bb
Merge branch 'master' into u/maksym/paasta-metastatus
Nov 17, 2020
0ded11b
tidy
Nov 17, 2020
6e86572
Merge branch 'master' into u/maksym/paasta-metastatus
Nov 17, 2020
0389029
use new paastaapi
Nov 17, 2020
9f77d7b
reduce diff
Nov 17, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
dist
bin
.idea/*
/paasta-metastatus
/paasta_go
260 changes: 260 additions & 0 deletions cmd/paasta-metastatus/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
package main

import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"sync"
"time"

flag "github.com/spf13/pflag"

"github.com/Yelp/paasta-tools-go/pkg/cli"
"github.com/Yelp/paasta-tools-go/pkg/configstore"
"github.com/logrusorgru/aurora"

"github.com/Yelp/paasta-tools-go/pkg/paastaapi"
)

// PaastaMetastatusOptions ...
type PaastaMetastatusOptions struct {
cli.PaastaOptions
cli.CSIOptions
AutoscalingInfo bool
Groupings []string
}

// Setup ...
func (o *PaastaMetastatusOptions) Setup() {
(&o.PaastaOptions).Setup()
(&o.CSIOptions).Setup()
flag.BoolVar(&o.AutoscalingInfo, "autoscaling-info", false, "")
flag.StringArrayVarP(&o.Groupings, "groupings", "g", []string{"region"}, "")
}

func parseFlags(opts *PaastaMetastatusOptions) error {
opts.Setup()
flag.CommandLine.SortFlags = false
flag.Parse()
return nil
}

func writeDashboards(
cluster string, dashboards map[string]interface{}, sb *strings.Builder,
) {
if dashboards == nil {
sb.WriteString(aurora.Red("No dashboards configured!\n").String())
} else {
spacing := 0
for svc := range dashboards {
svcLen := len(svc)
if svcLen > spacing {
spacing = svcLen
}
}
spacing++
for svc, dashboard := range dashboards {
spacer := strings.Repeat(" ", spacing-len(svc))
sb.WriteString(fmt.Sprintf(" %s:%s", svc, spacer))
switch d := dashboard.(type) {
case string:
sb.WriteString(aurora.Cyan(d).String())
case []string:
if len(d) > 1 {
for _, url := range d {
sb.WriteString(
fmt.Sprintf("\n %v", aurora.Cyan(url)),
)
}
} else if len(d) == 1 {
sb.WriteString(aurora.Cyan(d[0]).String())
}
}
sb.WriteString("\n")
}
}
}

func buildMetastatusCmdArgs(opts *PaastaMetastatusOptions) ([]string, time.Duration) {
cmdArgs := []string{}
verbosity := 0
timeout := time.Duration(20)

if opts.Verbosity > 0 {
verbosity = opts.Verbosity
timeout = 120
}
if opts.AutoscalingInfo {
cmdArgs = append(cmdArgs, "-a")
if verbosity < 2 {
verbosity = 2
}
timeout = 120
}
if len(opts.Groupings) > 0 {
cmdArgs = append(cmdArgs, "-g")
cmdArgs = append(cmdArgs, opts.Groupings...)
}
if opts.UseMesosCache {
cmdArgs = append(cmdArgs, "--use-mesos-cache")
}
if verbosity > 0 {
cmdArgs = append(cmdArgs, fmt.Sprintf("-%s", strings.Repeat("v", verbosity)))
}

return cmdArgs, timeout
}

type ctxKey int

const (
ctxKeyHTTPClient ctxKey = iota
ctxKeyOut
ctxKeyErr
)

func writeAPIStatus(
ctx context.Context,
cluster, endpoint string,
cmdArgs []string,
sb *strings.Builder,
) error {
url, err := url.Parse(endpoint)
if err != nil {
return fmt.Errorf("Failed to parse API endpoint %v: %v", endpoint, err)
}

config := paastaapi.NewConfiguration()
config.Host = url.Host
config.Scheme = url.Scheme

httpClient := ctx.Value(ctxKeyHTTPClient)
if httpClient != nil {
config.HTTPClient = httpClient.(*http.Client)
}

client := paastaapi.NewAPIClient(config)
mr := client.DefaultApi.Metastatus(ctx).CmdArgs(cmdArgs)
metastatusResp, _, err := mr.Execute()
if err != nil {
return fmt.Errorf("Failed to get metastatus: %s", aurora.Red(err))
}
sb.WriteString(fmt.Sprintf("%s\n", metastatusResp.GetOutput()))
return nil
}

func getClusterStatus(
ctx context.Context,
cluster, endpoint string,
dashboards map[string]interface{},
cmdArgs []string,
) (*strings.Builder, error) {
sb := &strings.Builder{}
sb.WriteString(fmt.Sprintf("Cluster: %v\n", cluster))
writeDashboards(cluster, dashboards, sb)
err := writeAPIStatus(ctx, cluster, endpoint, cmdArgs, sb)
if err != nil {
return sb, fmt.Errorf("Failed to get status for cluster %v: %v", cluster, err)
}
return sb, nil
}

func metastatus(
ctx context.Context,
clusters []string,
apiEndpoints map[string]string,
dashboardLinks map[string]map[string]interface{},
cmdArgs []string,
) (bool, error) {
outf := ctx.Value(ctxKeyOut).(io.Writer)
errf := ctx.Value(ctxKeyErr).(io.Writer)

var wg sync.WaitGroup
var success bool = true
for _, cluster := range clusters {
endpoint, ok := apiEndpoints[cluster]
if !ok {
fmt.Fprintf(errf, "WARN: api endpoint not found for %v\n", cluster)
continue
}
dashboards, _ := dashboardLinks[cluster]
wg.Add(1)
go func(cluster, endpoint string) {
defer wg.Done()
sb, err := getClusterStatus(ctx, cluster, endpoint, dashboards, cmdArgs)
fmt.Fprint(outf, sb)
if err != nil {
fmt.Fprint(errf, err.Error())
}
}(cluster, endpoint)
}
wg.Wait()

return success, nil
}

func main() {
options := &PaastaMetastatusOptions{}
err := parseFlags(options)
if err != nil {
fmt.Println(err)
flag.PrintDefaults()
os.Exit(1)
}
if options.Help {
flag.PrintDefaults()
os.Exit(0)
}

if options.AutoscalingInfo {
if options.Verbosity < 2 {
options.Verbosity = 2
}
}
sysStore := configstore.NewStore(options.SysDir, nil)

apiEndpoints := map[string]string{}
ok, err := sysStore.Load("api_endpoints", &apiEndpoints)
if !ok || err != nil {
fmt.Fprintf(os.Stderr, "Failed to load api_endpoints from configs: found=%v, error=%v", ok, err)
os.Exit(1)
}

dashboardLinks := map[string]map[string]interface{}{}
ok, err = sysStore.Load("dashboard_links", &dashboardLinks)
if !ok || err != nil {
fmt.Fprintf(os.Stderr, "Failed to load dashboard_links from configs: found=%v, error=%v", ok, err)
os.Exit(1)
}

var clusters []string
if options.Cluster != "" {
clusters = []string{options.Cluster}
} else {
ok, err := sysStore.Load("clusters", &clusters)
if !ok || err != nil {
fmt.Fprintf(os.Stderr, "Failed to load clusters from configs: found=%v, error=%v", ok, err)
os.Exit(1)
}
}

cmdArgs, timeout := buildMetastatusCmdArgs(options)
ctx, cancel := context.WithTimeout(context.Background(), timeout*time.Second)
defer cancel()

ctx = context.WithValue(ctx, ctxKeyOut, os.Stdout)
ctx = context.WithValue(ctx, ctxKeyErr, os.Stderr)

success, err := metastatus(ctx, clusters, apiEndpoints, dashboardLinks, cmdArgs)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if !success {
os.Exit(1)
}
}
Loading