Complete project pivot (#55)

Complete project pivot
This commit is contained in:
2022-01-12 08:57:21 +01:00
committed by GitHub
parent 70aa5332a9
commit bd581db472
46 changed files with 2638 additions and 1396 deletions

14
.gitignore vendored
View File

@@ -1,20 +1,16 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
speedrun
^speedrun
TODO
.vscode
.idea
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
dist/
*.crt
*.key
*.csr
*.srl

52
Makefile Normal file
View File

@@ -0,0 +1,52 @@
GITCOMMIT=$(shell git rev-parse --short HEAD 2>/dev/null)
DATE=$(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
VERSION=$(shell git branch --show-current)
DIST=dist
LINUX_PATH=$(DIST)/linux
DARWIN_PATH=$(DIST)/darwin
all: requirements speedrun-linux speedrun-darwin portal-linux
clean:
rm -rf dist
requirements:
@go mod tidy -compat=1.17
@go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
@go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
speedrun-linux-amd64: requirements
GOOS=linux GOARCH=amd64 go build -o $(DIST)/speedrun-linux-amd64 -ldflags "-X github.com/speedrunsh/speedrun/cmd/speedrun/cli.version=$(VERSION) -X github.com/speedrunsh/speedrun/cmd/speedrun/cli.commit=$(GITCOMMIT) -X github.com/speedrunsh/speedrun/cmd/speedrun/cli.date=$(DATE)" ./cmd/speedrun
speedrun-linux-arm64: requirements
GOOS=linux GOARCH=arm64 go build -o $(DIST)/speedrun-linux-arm64 -ldflags "-X github.com/speedrunsh/speedrun/cmd/speedrun/cli.version=$(VERSION) -X github.com/speedrunsh/speedrun/cmd/speedrun/cli.commit=$(GITCOMMIT) -X github.com/speedrunsh/speedrun/cmd/speedrun/cli.date=$(DATE)" ./cmd/speedrun
speedrun-linux: speedrun-linux-amd64 speedrun-linux-arm64
speedrun-darwin-amd64: requirements
GOOS=darwin GOARCH=amd64 go build -o $(DIST)/speedrun-darwin-amd64 -ldflags "-X github.com/speedrunsh/speedrun/cmd/speedrun/cli.version=$(VERSION) -X github.com/speedrunsh/speedrun/cmd/speedrun/cli.commit=$(GITCOMMIT) -X github.com/speedrunsh/speedrun/cmd/speedrun/cli.date=$(DATE)" ./cmd/speedrun
speedrun-darwin-arm64: requirements
GOOS=darwin GOARCH=arm64 go build -o $(DIST)/speedrun-darwin-arm64 -ldflags "-X github.com/speedrunsh/speedrun/cmd/speedrun/cli.version=$(VERSION) -X github.com/speedrunsh/speedrun/cmd/speedrun/cli.commit=$(GITCOMMIT) -X github.com/speedrunsh/speedrun/cmd/speedrun/cli.date=$(DATE)" ./cmd/speedrun
speedrun-darwin: speedrun-darwin-amd64 speedrun-darwin-arm64
speedrun: speedrun-linux speedrun-darwin
portal-linux-amd64: requirements
GOOS=linux GOARCH=amd64 go build -o $(DIST)/portal-linux-amd64 ./cmd/portal
# zip $(DIST)/portal-linux-amd64.zip $(DIST)/portal-linux-amd64
portal-linux-arm64: requirements
GOOS=linux GOARCH=arm64 go build -o $(DIST)/portal-linux-arm64 ./cmd/portal
# zip $(DIST)/portal-linux-arm64.zip $(DIST)/portal-linux-arm64
portal-linux: portal-linux-amd64 portal-linux-arm64
portal: portal-linux
dev: speedrun-darwin-arm64 portal-linux-amd64
proto:
protoc --go_out=paths=source_relative:. --go-grpc_out=paths=source_relative:. proto/portal/portal.proto

View File

@@ -1,77 +1,80 @@
<p align="center">
<a rel="nofollow">
<img src="docs/logo.png?raw=true" width="200" style="max-width:100%;">
<img src="assets/logo.png?raw=true" width="200" style="max-width:100%;">
</a>
</p>
# Speedrun
[![license](https://img.shields.io/badge/license-MPL2-blue.svg)](https://github.com/dpogorzelski/speedrun/blob/master/LICENSE)
[![Go Report Card](https://goreportcard.com/badge/github.com/dpogorzelski/speedrun)](https://goreportcard.com/report/github.com/dpogorzelski/speedrun)
[![Go](https://github.com/dpogorzelski/speedrun/actions/workflows/go.yml/badge.svg)](https://github.com/dpogorzelski/speedrun/actions/workflows/go.yml)
Speedrun is an action execution framework that works at scale.
Speedrun helps you control your compute fleet with minimal effort.
It allows you to run any action/command across any number of servers, projects and cloud vendors with ease and fast (currently GCP only but AWS and Azure will be supported as well), example:
Example (stop nginx across 3k machines):
```bash
speedrun run systemctl stop nginx
speedrun service stop nginx
```
to stop nginx across 3k machines.
No hassles with setting up and maintaining a server with agents as speedrun has none. Single self-contained binary. Speedrun leverages SSH as transport with tight cloud vendor integration to take the burden of mundane things like key generation and propagation/revocation away from the user.
Server targeting (`--target`) is made as intuitive as possible, currently based on [gcloud topic filters](https://cloud.google.com/sdk/gcloud/reference/topic/filters) but in the future will be replaced by a generic selection mechanism to provide a seamless experience across different cloud vendors.
Features:
* native cloud integration (currently Google Cloud only, AWS and Azure coming up!)
* stateless and agentless
* stateless
* serverless
* idempotent
* no complex configuration required
* single self-contained binary
* can run against any number of servers and projects seamlessly
* a plugin system is in the plan to allow anyone to integrate execution modules that will wrap complex functionality instead of running raw shell commands
* server discovery via native cloud integration (currently Google Cloud only, AWS and Azure coming up!)
* extensible (plugin system is in the works)
## Installation
#### Homebrew (MacOS, Linux)
```bash
brew install dpogorzelski/tap/speedrun
```
#### MacOS, Linux, Windows
Download the precompiled binaries from here: [Releases](https://github.com/dpogorzelski/speedrun/releases)
#### Manual (MacOS, Linux, Windows)
Download the precompiled binary from here: [Releases](https://github.com/dpogorzelski/speedrun/releases)
## Usage
#### Quickstart
On a server:
`sudo ./portal --insecure`
On your machine:
```bash
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/serviceaccount.json
speedrun init
speedrun key new
speedrun key authorize
speedrun run whoami
speedrun run whoami --insecure
```
## Architecture
* picture first here
* speedrun client
* portal
* protocols
* service discovery
* language definition [expr/Language-Definition.md at master · antonmedv/expr · GitHub](https://github.com/antonmedv/expr/blob/master/docs/Language-Definition.md)
## Examples
Use 1000 concurrent SSH workers
Stop Nginx on VMs that have a label `role` with value `nginx` and a label named `project` with value `someproject`
```bash
speedrun run "uname -r" --concurrency 1000
speedrun service stop nginx --target "labels.role == 'nginx' and labels.project == 'someproject'r"
```
Stop Nginx on VMs matching the target selector
Run arbitrary shell command on the target machines. Ignore Portal's certificate and connect via private IP address.
```bash
speedrun run sudo systemctl stop nginx --target "labels.env=staging AND labels.app=foobar"
```
Ignore SSH fingerprint mismatch and connect via private IP addresses
```bash
speedrun run "ls -la" --target "labels.env != prod" --ignore-fingerprint --concurrency 1000 --use-private-ip
speedrun run "ls -la" --target "labels.env != 'prod'" --insecure --use-private-ip
```
Use a different config file
@@ -82,7 +85,11 @@ speedrun run whoami -c /path/to/config.toml
## Configuration
Using certain flags repeteadly can be annoying, it's possible to persist their behavior via config file. Default config file is located at `~/.speedrun/config.toml` and can be re-initialized to it's default form via `speedrun init`.
Instead of supplying certain flags repeatedly you can persist their behavior in the config file. Default config file is located at `~/.speedrun/config.toml` and default settings can be fetched via `speedrun init --print`.
#### Run portal as a systemd unit
#### Use self signed certificates during testing
## Contributing

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -1,8 +0,0 @@
#!/usr/bin/env bash
set -e
GITCOMMIT=$(git rev-parse --short HEAD 2>/dev/null)
DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
VERSION=$(git branch --show-current)
go build -ldflags "-X speedrun/cmd.version=$VERSION -X speedrun/cmd.commit=$GITCOMMIT -X speedrun/cmd.date=$DATE"

View File

@@ -1,55 +0,0 @@
package cloud
import (
"context"
"encoding/json"
"fmt"
"golang.org/x/oauth2/google"
"google.golang.org/api/compute/v1"
"google.golang.org/api/oslogin/v1"
)
type GCPClient struct {
gce *compute.Service
oslogin *oslogin.Service
client_email string
Project string
}
func NewGCPClient(project string) (*GCPClient, error) {
var err error
ctx := context.Background()
credentials, err := google.FindDefaultCredentials(ctx, compute.ComputeScope)
if err != nil {
err = fmt.Errorf("couldn't fetch default client credentials: %v", err)
return nil, err
}
var jsonCreds map[string]interface{}
err = json.Unmarshal(credentials.JSON, &jsonCreds)
if err != nil {
err = fmt.Errorf("couldn't decode default client credentials json: %v", err)
return nil, err
}
gce, err := compute.NewService(ctx)
if err != nil {
err = fmt.Errorf("couldn't initialize GCP client: %v", err)
return nil, err
}
osc, err := oslogin.NewService(ctx)
if err != nil {
return nil, err
}
c := &GCPClient{
gce: gce,
oslogin: osc,
client_email: jsonCreds["client_email"].(string),
Project: project,
}
return c, nil
}

View File

@@ -1,38 +0,0 @@
package cloud
import (
"context"
"google.golang.org/api/compute/v1"
)
// GetInstances returns a list of external IP addresses used for the SHH connection
func (c *GCPClient) GetInstances(filter string, usePrivateIP bool) ([]Instance, error) {
instances := []Instance{}
listCall := c.gce.Instances.AggregatedList(c.Project).Fields("nextPageToken", "items(Name,NetworkInterfaces)")
var ctx context.Context
listCall.Filter(filter).Pages(ctx, func(list *compute.InstanceAggregatedList) error {
for _, item := range list.Items {
for _, instance := range item.Instances {
i := &Instance{
Name: instance.Name,
}
if usePrivateIP {
i.Address = instance.NetworkInterfaces[0].NetworkIP
} else {
i.Address = instance.NetworkInterfaces[0].AccessConfigs[0].NatIP
}
instances = append(instances, *i)
}
}
return nil
})
_, err := listCall.Do()
if err != nil {
return nil, err
}
return instances, nil
}

View File

@@ -1,186 +0,0 @@
package cloud
import (
"fmt"
"sort"
"speedrun/key"
"strings"
"google.golang.org/api/compute/v1"
)
// addKeyToMetadataP updates SSH key entires in the project metadata
func (c *GCPClient) AddKeyToMetadata(key *key.Key) error {
projectData, err := c.gce.Projects.Get(c.Project).Do()
if err != nil {
return err
}
item, err := formatSSHKey(key)
if err != nil {
return err
}
hasKey, same, i := hasItem(projectData.CommonInstanceMetadata, item)
var items []*compute.MetadataItems
if hasKey && same {
return nil
} else if hasKey && !same {
items = updateMetadata(projectData.CommonInstanceMetadata, item, i)
} else if !hasKey {
items = appendToMetadata(projectData.CommonInstanceMetadata, item)
}
metadata := compute.Metadata{
Fingerprint: projectData.CommonInstanceMetadata.Fingerprint,
Items: items,
}
setMetadata := c.gce.Projects.SetCommonInstanceMetadata(c.Project, &metadata)
_, err = setMetadata.Do()
if err != nil {
return err
}
return nil
}
// removeKeyFromMetadata removes user's ssh public key from the project metadata
func (c *GCPClient) RemoveKeyFromMetadata(key *key.Key) error {
projectData, err := c.gce.Projects.Get(c.Project).Do()
if err != nil {
return err
}
item, err := formatSSHKey(key)
if err != nil {
return err
}
hasKey, same, i := hasItem(projectData.CommonInstanceMetadata, item)
var items []*compute.MetadataItems
if hasKey && same {
removeFromMetadata(projectData.CommonInstanceMetadata, item, i)
} else {
return nil
}
metadata := compute.Metadata{
Fingerprint: projectData.CommonInstanceMetadata.Fingerprint,
Items: items,
}
setMetadata := c.gce.Projects.SetCommonInstanceMetadata(c.Project, &metadata)
_, err = setMetadata.Do()
if err != nil {
return err
}
return nil
}
// Extracts username, algorithm and comment from a metadata SSH key item
func parseMetadataitem(key string) (string, string, string) {
t := strings.Split(key, " ")
head, comment := t[0], t[len(t)-1]
username := strings.Split(head, ":")[0]
algo := strings.Split(head, ":")[1]
return username, algo, comment
}
// Verifies if a metadata item already exists for a given user/cipher/comment combination.
// If true it also returns the index number at which the existing item can be found otherwise index is -1.
func hasItem(md *compute.Metadata, x string) (bool, bool, int) {
flatMD := flattenMetadata(md)
if flatMD["ssh-keys"] == nil {
return false, false, -1
}
items := strings.Split(flatMD["ssh-keys"].(string), "\n")
username, algo, comment := parseMetadataitem(x)
for i, e := range items {
header := fmt.Sprintf("%s:%s", username, algo)
if x == e {
return true, true, i
} else if strings.HasPrefix(e, header) && strings.HasSuffix(e, comment) {
return true, false, i
}
}
return false, false, -1
}
// formatSSHKey formats public key item according to GCP guidelines
func formatSSHKey(key *key.Key) (string, error) {
authorizedKey, err := key.MarshalAuthorizedKey()
if err != nil {
return "", err
}
v := fmt.Sprintf("%s:%s %s", key.User, authorizedKey, key.Comment)
return v, nil
}
func appendToMetadata(md *compute.Metadata, item string) []*compute.MetadataItems {
var items []string
flatMD := flattenMetadata(md)
if flatMD["ssh-keys"] == nil {
items = append(items, item)
flatMD["ssh-keys"] = strings.Join(items, "\n")
return expandComputeMetadata(flatMD)
}
items = strings.Split(flatMD["ssh-keys"].(string), "\n")
items = append(items, item)
flatMD["ssh-keys"] = strings.Join(items, "\n")
return expandComputeMetadata(flatMD)
}
func removeFromMetadata(md *compute.Metadata, item string, i int) []*compute.MetadataItems {
var items []string
flatMD := flattenMetadata(md)
items = strings.Split(flatMD["ssh-keys"].(string), "\n")
copy(items[i:], items[i+1:])
items[len(items)-1] = ""
items = items[:len(items)-1]
flatMD["ssh-keys"] = strings.Join(items, "\n")
return expandComputeMetadata(flatMD)
}
func updateMetadata(md *compute.Metadata, item string, i int) []*compute.MetadataItems {
var items []string
flatMD := flattenMetadata(md)
items = strings.Split(flatMD["ssh-keys"].(string), "\n")
items[i] = item
flatMD["ssh-keys"] = strings.Join(items, "\n")
return expandComputeMetadata(flatMD)
}
func expandComputeMetadata(m map[string]interface{}) []*compute.MetadataItems {
metadata := make([]*compute.MetadataItems, len(m))
var keys []string
for key := range m {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
v := m[key].(string)
metadata = append(metadata, &compute.MetadataItems{
Key: key,
Value: &v,
})
}
return metadata
}
func flattenMetadata(metadata *compute.Metadata) map[string]interface{} {
metadataMap := make(map[string]interface{})
for _, item := range metadata.Items {
metadataMap[item.Key] = *item.Value
}
return metadataMap
}

View File

@@ -1,62 +0,0 @@
package cloud
import (
"crypto/sha256"
"fmt"
"speedrun/key"
"github.com/apex/log"
"google.golang.org/api/oslogin/v1"
)
func (c *GCPClient) AddUserKey(key *key.Key) error {
parent := fmt.Sprintf("users/%s", c.client_email)
authorizedKey, err := key.MarshalAuthorizedKey()
if err != nil {
return err
}
sshPublicKey := &oslogin.SshPublicKey{
Key: string(authorizedKey),
}
_, err = c.oslogin.Users.ImportSshPublicKey(parent, sshPublicKey).Do()
return err
}
func (c *GCPClient) RemoveUserKey(key *key.Key) error {
authorizedKey, err := key.MarshalAuthorizedKey()
if err != nil {
return err
}
name := fmt.Sprintf("users/%s/sshPublicKeys/%x", c.client_email, sha256.Sum256([]byte(authorizedKey)))
_, err = c.oslogin.Users.SshPublicKeys.Delete(name).Do()
return err
}
func (c *GCPClient) ListUserKeys() error {
parent := fmt.Sprintf("users/%s", c.client_email)
profile, err := c.oslogin.Users.GetLoginProfile(parent).Do()
if err != nil {
return err
}
for _, k := range profile.SshPublicKeys {
log.Info(k.Key)
}
return nil
}
func (c *GCPClient) GetSAUsername() (string, error) {
parent := fmt.Sprintf("users/%s", c.client_email)
profile, err := c.oslogin.Users.GetLoginProfile(parent).Do()
if err != nil {
return "", err
}
return profile.PosixAccounts[0].Username, nil
}

View File

@@ -1,6 +0,0 @@
package cloud
type Instance struct {
Address string
Name string
}

View File

@@ -1,15 +0,0 @@
package cmd
import (
"speedrun/config"
"github.com/spf13/cobra"
)
var initCmd = &cobra.Command{
Use: "init",
Short: "Initialize speedrun",
RunE: func(cmd *cobra.Command, args []string) error {
return config.Create()
},
}

View File

@@ -1,181 +0,0 @@
package cmd
import (
"path/filepath"
"speedrun/cloud"
"speedrun/key"
"github.com/apex/log"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var keyCmd = &cobra.Command{
Use: "key",
Short: "Manage ssh keys",
TraverseChildren: true,
}
var newKeyCmd = &cobra.Command{
Use: "new",
Short: "Create a new ssh key",
PreRun: func(cmd *cobra.Command, args []string) {
initConfig()
},
RunE: newKey,
}
var authorizeKeyCmd = &cobra.Command{
Use: "authorize",
Short: "Authorize key for ssh access",
Example: " speedrun key authorize",
PreRun: func(cmd *cobra.Command, args []string) {
initConfig()
},
RunE: authorizeKey,
}
var revokeKeyCmd = &cobra.Command{
Use: "revoke",
Short: "Revoke ssh key",
Example: " speedrun key revoke",
PreRun: func(cmd *cobra.Command, args []string) {
initConfig()
},
RunE: revokeKey,
}
var listKeysCmd = &cobra.Command{
Use: "list",
Short: "List OS Login keys",
Example: " speedrun key list",
PreRun: func(cmd *cobra.Command, args []string) {
initConfig()
},
RunE: listKeys,
}
func init() {
keyCmd.AddCommand(newKeyCmd)
keyCmd.AddCommand(authorizeKeyCmd)
keyCmd.AddCommand(revokeKeyCmd)
keyCmd.AddCommand(listKeysCmd)
authorizeKeyCmd.Flags().Bool("use-oslogin", false, "Authorize the key via OS Login rather than metadata")
viper.BindPFlag("gcp.use-oslogin", authorizeKeyCmd.Flags().Lookup("use-oslogin"))
}
func determineKeyFilePath() (string, error) {
log.Debug("Determining private key path")
home, err := homedir.Dir()
if err != nil {
return "", err
}
path := filepath.Join(home, ".speedrun/privatekey")
return path, nil
}
func newKey(cmd *cobra.Command, args []string) error {
k, err := key.New()
if err != nil {
return err
}
path, err := determineKeyFilePath()
if err != nil {
return err
}
err = k.Write(path)
if err != nil {
return err
}
log.Info("Private key created")
return nil
}
func authorizeKey(cmd *cobra.Command, args []string) error {
project := viper.GetString("gcp.projectid")
useOSlogin := viper.GetBool("gcp.use-oslogin")
gcpClient, err := cloud.NewGCPClient(project)
if err != nil {
return err
}
path, err := determineKeyFilePath()
if err != nil {
return err
}
k, err := key.Read(path)
if err != nil {
return err
}
if useOSlogin {
gcpClient.AddUserKey(k)
if err != nil {
return err
}
log.Info("Authorized key via OS Login")
} else {
gcpClient.AddKeyToMetadata(k)
if err != nil {
return err
}
log.Info("Authorized key in the project metadata")
}
return nil
}
func revokeKey(cmd *cobra.Command, args []string) error {
project := viper.GetString("gcp.projectid")
gcpClient, err := cloud.NewGCPClient(project)
if err != nil {
return err
}
path, err := determineKeyFilePath()
if err != nil {
return err
}
k, err := key.Read(path)
if err != nil {
return err
}
log.Info("Revoking public key")
err = gcpClient.RemoveKeyFromMetadata(k)
if err != nil {
return err
}
err = gcpClient.RemoveUserKey(k)
if err != nil {
return err
}
return nil
}
func listKeys(cmd *cobra.Command, args []string) error {
project := viper.GetString("gcp.projectid")
gcpClient, err := cloud.NewGCPClient(project)
if err != nil {
return err
}
log.Info("Fetching OS Login keys")
err = gcpClient.ListUserKeys()
if err != nil {
return err
}
return nil
}

59
cmd/portal/cli/init.go Normal file
View File

@@ -0,0 +1,59 @@
package cli
import (
"fmt"
"os"
"path/filepath"
"github.com/apex/log"
"github.com/pelletier/go-toml"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func init() {
initCmd.Flags().BoolP("print", "p", false, "Print default config to stdout")
}
var initCmd = &cobra.Command{
Use: "init",
Short: "Initialize portal",
RunE: func(cmd *cobra.Command, args []string) error {
print, err := cmd.Flags().GetBool("print")
if err != nil {
return err
}
if print {
c := viper.AllSettings()
bs, err := toml.Marshal(c)
if err != nil {
return fmt.Errorf("unable to marshal config: %v", err)
}
fmt.Println(string(bs))
return nil
}
dir := filepath.Dir(cfgFile)
if _, err := os.Stat(dir); os.IsNotExist(err) {
err = os.Mkdir(dir, 0755)
if err != nil {
return err
}
}
err = viper.SafeWriteConfigAs(cfgFile)
if err != nil {
if _, ok := err.(viper.ConfigFileAlreadyExistsError); !ok {
return fmt.Errorf("couldn't save config at \"%s\" (%s)", viper.ConfigFileUsed(), err)
}
} else {
log.Infof("Your config was saved at \"%s\"", viper.ConfigFileUsed())
return nil
}
log.Infof("Config already exists at \"%s\", no changes applied", viper.ConfigFileUsed())
return nil
},
}

128
cmd/portal/cli/root.go Normal file
View File

@@ -0,0 +1,128 @@
package cli
import (
"context"
"crypto/tls"
"fmt"
"os"
"path/filepath"
"github.com/apex/log"
jsonhandler "github.com/apex/log/handlers/json"
texthandler "github.com/apex/log/handlers/text"
"github.com/speedrunsh/speedrun/pkg/common/cryptoutil"
"github.com/speedrunsh/speedrun/pkg/portal"
portalpb "github.com/speedrunsh/speedrun/proto/portal"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"storj.io/drpc/drpcmux"
"storj.io/drpc/drpcserver"
)
var cfgFile string
var version string
var commit string
var date string
func Execute() {
var rootCmd = &cobra.Command{
Use: "portal",
Short: "Control your compute fleet at scale",
Version: fmt.Sprintf("%s, commit: %s, date: %s", version, commit, date),
SilenceUsage: true,
SilenceErrors: true,
RunE: func(cmd *cobra.Command, args []string) error {
insecure := viper.GetBool("tls.insecure")
caPath := viper.GetString("tls.ca")
certPath := viper.GetString("tls.cert")
keyPath := viper.GetString("tls.key")
m := drpcmux.New()
var err error
err = portalpb.DRPCRegisterPortal(m, &portal.Server{})
if err != nil {
return fmt.Errorf("could not register DRPC server: %v", err)
}
s := drpcserver.New(m)
var tlsConfig *tls.Config
if insecure {
log.Warn("Using insecure TLS configuration, this should be avoided in production environments")
tlsConfig, err = cryptoutil.InsecureTLSConfig()
} else {
tlsConfig, err = cryptoutil.ServerTLSConfig(caPath, certPath, keyPath)
}
if err != nil {
return fmt.Errorf("could not initialize TLS config: %v", err)
}
port := viper.GetInt("port")
ip := viper.GetString("address")
addr := fmt.Sprintf("%s:%d", ip, port)
lis, err := tls.Listen("tcp", addr, tlsConfig)
if err != nil {
return fmt.Errorf("could not create TCP socket: %v", err)
}
defer lis.Close()
ctx := context.Background()
log.Infof("Starting portal on %s", addr)
return s.Serve(ctx, lis)
},
}
dir := "/etc/portal"
configPath := filepath.Join(dir, "config.toml")
cobra.OnInitialize(initConfig)
rootCmd.AddCommand(initCmd)
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", configPath, "config file")
rootCmd.PersistentFlags().StringP("loglevel", "l", "info", "Log level")
rootCmd.PersistentFlags().BoolP("json", "j", false, "Output logs in JSON format")
rootCmd.Flags().IntP("port", "p", 1337, "Port to listen on for connections")
rootCmd.Flags().StringP("address", "a", "0.0.0.0", "Address to listen on for connections")
rootCmd.Flags().Bool("insecure", false, "Skip client certificate verification")
rootCmd.Flags().String("ca", "ca.crt", "Path to the CA cert")
rootCmd.Flags().String("cert", "portal.crt", "Path to the server cert")
rootCmd.Flags().String("key", "portal.key", "Path to the server key")
viper.BindPFlag("tls.insecure", rootCmd.Flags().Lookup("insecure"))
viper.BindPFlag("tls.ca", rootCmd.Flags().Lookup("ca"))
viper.BindPFlag("tls.cert", rootCmd.Flags().Lookup("cert"))
viper.BindPFlag("tls.key", rootCmd.Flags().Lookup("key"))
viper.BindPFlag("logging.loglevel", rootCmd.PersistentFlags().Lookup("loglevel"))
viper.BindPFlag("logging.json", rootCmd.PersistentFlags().Lookup("json"))
viper.BindPFlag("port", rootCmd.Flags().Lookup("port"))
viper.BindPFlag("address", rootCmd.Flags().Lookup("address"))
rootCmd.DisableSuggestions = false
if err := rootCmd.Execute(); err != nil {
log.Error(err.Error())
}
}
func initConfig() {
viper.SetConfigFile(cfgFile)
viper.AutomaticEnv()
json := viper.GetBool("logging.json")
if json {
handler := jsonhandler.New(os.Stdout)
log.SetHandler(handler)
} else {
handler := texthandler.New(os.Stdout)
log.SetHandler(handler)
}
if err := viper.ReadInConfig(); err != nil {
log.Warnf("Couldn't read config at \"%s\", starting with default settings", viper.ConfigFileUsed())
}
lvl, err := log.ParseLevel(viper.GetString("logging.loglevel"))
if err != nil {
log.Fatalf("couldn't parse log level: %s (%s)", err, lvl)
return
}
log.SetLevel(lvl)
}

9
cmd/portal/main.go Normal file
View File

@@ -0,0 +1,9 @@
package main
import (
"github.com/speedrunsh/speedrun/cmd/portal/cli"
)
func main() {
cli.Execute()
}

View File

@@ -1,72 +0,0 @@
package cmd
import (
"fmt"
"path/filepath"
"github.com/apex/log"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var cfgFile string
var version string
var commit string
var date string
//Execute runs the root command
func Execute() {
// cobra.OnInitialize(initConfig)
var rootCmd = &cobra.Command{
Use: "speedrun",
Short: "Cloud first command execution",
Version: fmt.Sprintf("%s, commit: %s, date: %s", version, commit, date),
SilenceUsage: true,
SilenceErrors: true,
}
rootCmd.AddCommand(initCmd)
rootCmd.AddCommand(keyCmd)
rootCmd.AddCommand(runCmd)
home, err := homedir.Dir()
if err != nil {
log.Fatal(err.Error())
}
dir := filepath.Join(home, ".speedrun")
path := filepath.Join(dir, "config.toml")
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", path, "config file")
rootCmd.PersistentFlags().StringP("loglevel", "l", "info", "Log level")
viper.BindPFlag("loglevel", rootCmd.PersistentFlags().Lookup("loglevel"))
if err := rootCmd.Execute(); err != nil {
log.Fatal(err.Error())
}
}
func initConfig() {
dir, file := filepath.Split(cfgFile)
viper.SetConfigName(file)
viper.SetConfigType("toml")
viper.AddConfigPath(dir)
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
if err != nil {
log.Error(err.Error())
log.Fatal("Run `speedrun init` first")
}
} else {
log.Fatal(err.Error())
}
}
lvl, err := log.ParseLevel(viper.GetString("loglevel"))
if err != nil {
log.Fatalf("Couldn't parse log level: %s", err)
return
}
log.SetLevel(lvl)
}

View File

@@ -1,101 +0,0 @@
package cmd
import (
"speedrun/cloud"
"speedrun/key"
"speedrun/marathon"
"strings"
"time"
"github.com/apex/log"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var runCmd = &cobra.Command{
Use: "run <command to run>",
Short: "Run command on remote servers",
Example: " speedrun run whoami\n speedrun run whoami --only-failures --target \"labels.foo = bar AND labels.environment = staging\"",
Args: cobra.MinimumNArgs(1),
PreRun: func(cmd *cobra.Command, args []string) {
initConfig()
},
RunE: run,
}
func init() {
runCmd.Flags().StringP("target", "t", "", "Fetch instances that match the target selection criteria")
runCmd.Flags().String("projectid", "", "Override GCP project id")
runCmd.Flags().Bool("only-failures", false, "Print only failures and errors")
runCmd.Flags().Bool("ignore-fingerprint", false, "Ignore host's fingerprint mismatch")
runCmd.Flags().Duration("timeout", time.Duration(10*time.Second), "SSH connection timeout")
runCmd.Flags().Int("concurrency", 100, "Number of maximum concurrent SSH workers")
runCmd.Flags().Bool("use-private-ip", false, "Connect to private IPs instead of public ones")
runCmd.Flags().Bool("use-oslogin", false, "Authenticate via OS Login")
viper.BindPFlag("gcp.projectid", runCmd.Flags().Lookup("projectid"))
viper.BindPFlag("gcp.use-oslogin", runCmd.Flags().Lookup("use-oslogin"))
viper.BindPFlag("ssh.timeout", runCmd.Flags().Lookup("timeout"))
viper.BindPFlag("ssh.ignore-fingerprint", runCmd.Flags().Lookup("ignore-fingerprint"))
viper.BindPFlag("ssh.only-failures", runCmd.Flags().Lookup("only-failures"))
viper.BindPFlag("ssh.concurrency", runCmd.Flags().Lookup("concurrency"))
viper.BindPFlag("ssh.use-private-ip", runCmd.Flags().Lookup("use-private-ip"))
}
func run(cmd *cobra.Command, args []string) error {
command := strings.Join(args, " ")
project := viper.GetString("gcp.projectid")
timeout := viper.GetDuration("ssh.timeout")
ignoreFingerprint := viper.GetBool("ssh.ignore-fingerprint")
onlyFailures := viper.GetBool("ssh.only-failures")
concurrency := viper.GetInt("ssh.concurrency")
usePrivateIP := viper.GetBool("ssh.use-private-ip")
useOSlogin := viper.GetBool("gcp.use-oslogin")
target, err := cmd.Flags().GetString("target")
if err != nil {
return err
}
gcpClient, err := cloud.NewGCPClient(project)
if err != nil {
return err
}
path, err := determineKeyFilePath()
if err != nil {
return err
}
k, err := key.Read(path)
if err != nil {
return err
}
log.Info("Fetching instance list")
instances, err := gcpClient.GetInstances(target, usePrivateIP)
if err != nil {
return err
}
if len(instances) == 0 {
log.Warn("No instances found")
return nil
}
if useOSlogin {
user, err := gcpClient.GetSAUsername()
if err != nil {
return err
}
k.User = user
}
m := marathon.New(command, timeout, concurrency)
err = m.Run(instances, k, ignoreFingerprint)
if err != nil {
return err
}
m.PrintResult(onlyFailures)
return nil
}

61
cmd/speedrun/cli/init.go Normal file
View File

@@ -0,0 +1,61 @@
package cli
import (
"fmt"
"os"
"path/filepath"
"github.com/apex/log"
"github.com/pelletier/go-toml"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func init() {
initCmd.SetUsageTemplate(usage)
initCmd.Flags().BoolP("print", "p", false, "Print default config to stdout")
}
var initCmd = &cobra.Command{
Use: "init",
Short: "Initialize speedrun",
RunE: func(cmd *cobra.Command, args []string) error {
viper.SetDefault("gcp.projectid", "")
print, err := cmd.Flags().GetBool("print")
if err != nil {
return err
}
if print {
c := viper.AllSettings()
bs, err := toml.Marshal(c)
if err != nil {
return fmt.Errorf("unable to marshal config: %v", err)
}
fmt.Println(string(bs))
return nil
}
dir := filepath.Dir(cfgFile)
if _, err := os.Stat(dir); os.IsNotExist(err) {
err = os.Mkdir(dir, 0755)
if err != nil {
return err
}
}
err = viper.SafeWriteConfigAs(cfgFile)
if err != nil {
if _, ok := err.(viper.ConfigFileAlreadyExistsError); !ok {
return fmt.Errorf("couldn't save config at \"%s\" (%s)", viper.ConfigFileUsed(), err)
}
} else {
log.Infof("Your config was saved at \"%s\"", viper.ConfigFileUsed())
return nil
}
log.Infof("Config already exists at \"%s\", no changes applied", viper.ConfigFileUsed())
return nil
},
}

97
cmd/speedrun/cli/root.go Normal file
View File

@@ -0,0 +1,97 @@
package cli
import (
_ "embed"
"fmt"
"os"
"path/filepath"
"github.com/apex/log"
jsonhandler "github.com/apex/log/handlers/json"
texthandler "github.com/apex/log/handlers/text"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var cfgFile string
var version string
var commit string
var date string
//go:embed templates/root.tmpl
var rootUsage string
//go:embed templates/usage.tmpl
var usage string
func Execute() {
var rootCmd = &cobra.Command{
Use: "speedrun",
Short: "Control your compute fleet at scale",
Version: fmt.Sprintf("%s, commit: %s, date: %s", version, commit, date),
SilenceUsage: true,
SilenceErrors: true,
}
cobra.OnInitialize(initConfig)
rootCmd.SetUsageTemplate(rootUsage)
rootCmd.AddCommand(initCmd, runCmd, serviceCmd)
home, err := homedir.Dir()
if err != nil {
log.Fatal(err.Error())
}
dir := filepath.Join(home, ".speedrun")
configPath := filepath.Join(dir, "config.toml")
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", configPath, "config file")
rootCmd.PersistentFlags().StringP("loglevel", "l", "info", "Log level")
rootCmd.PersistentFlags().BoolP("json", "j", false, "Output logs in JSON format")
rootCmd.PersistentFlags().StringP("target", "t", "", "Fetch instances that match the target selection criteria")
rootCmd.PersistentFlags().Bool("insecure", false, "Skip server certificate verification")
rootCmd.PersistentFlags().String("ca", "ca.crt", "Path to the CA cert")
rootCmd.PersistentFlags().String("cert", "cert.crt", "Path to the client cert")
rootCmd.PersistentFlags().String("key", "key.key", "Path to the client key")
rootCmd.PersistentFlags().Bool("use-private-ip", false, "Connect to private IPs instead of public ones")
viper.BindPFlag("logging.loglevel", rootCmd.PersistentFlags().Lookup("loglevel"))
viper.BindPFlag("logging.json", rootCmd.PersistentFlags().Lookup("json"))
viper.BindPFlag("tls.insecure", rootCmd.PersistentFlags().Lookup("insecure"))
viper.BindPFlag("tls.ca", rootCmd.PersistentFlags().Lookup("ca"))
viper.BindPFlag("tls.cert", rootCmd.PersistentFlags().Lookup("cert"))
viper.BindPFlag("tls.key", rootCmd.PersistentFlags().Lookup("key"))
viper.BindPFlag("portal.use-private-ip", rootCmd.PersistentFlags().Lookup("use-private-ip"))
rootCmd.DisableSuggestions = false
if err := rootCmd.Execute(); err != nil {
log.Error(err.Error())
}
}
func initConfig() {
viper.SetConfigFile(cfgFile)
viper.AutomaticEnv()
json := viper.GetBool("logging.json")
if json {
handler := jsonhandler.New(os.Stdout)
log.SetHandler(handler)
} else {
handler := texthandler.New(os.Stdout)
log.SetHandler(handler)
}
if err := viper.ReadInConfig(); err != nil {
log.Warnf("Couldn't read config at \"%s\", starting with default settings", viper.ConfigFileUsed())
}
lvl, err := log.ParseLevel(viper.GetString("logging.loglevel"))
if err != nil {
log.Fatalf("couldn't parse log level: %s (%s)", err, lvl)
return
}
log.SetLevel(lvl)
}

86
cmd/speedrun/cli/run.go Normal file
View File

@@ -0,0 +1,86 @@
package cli
import (
"context"
"crypto/tls"
"fmt"
"strings"
"time"
"github.com/alitto/pond"
"github.com/speedrunsh/speedrun/pkg/speedrun/cloud"
portalpb "github.com/speedrunsh/speedrun/proto/portal"
"storj.io/drpc/drpcconn"
"github.com/apex/log"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var runCmd = &cobra.Command{
Use: "run <command to run>",
Short: "Run a shell command on remote servers",
Example: " speedrun run whoami\n speedrun run whoami --target \"labels.foo = bar AND labels.environment = staging\"",
Args: cobra.MinimumNArgs(1),
RunE: run,
}
func init() {
runCmd.SetUsageTemplate(usage)
}
func run(cmd *cobra.Command, args []string) error {
command := strings.Join(args, " ")
s := strings.Split(command, " ")
usePrivateIP := viper.GetBool("portal.use-private-ip")
tlsConfig, err := cloud.SetupTLS()
if err != nil {
return err
}
target, err := cmd.Flags().GetString("target")
if err != nil {
return err
}
instances, err := cloud.GetInstances(target)
if err != nil {
return err
}
pool := pond.New(1000, 10000)
for _, p := range instances {
instance := p
pool.Submit(func() {
fields := log.Fields{
"host": instance.Name,
"address": instance.GetAddress(usePrivateIP),
}
log := log.WithFields(fields)
addr := fmt.Sprintf("%s:%d", instance.GetAddress(usePrivateIP), 1337)
rawconn, err := tls.Dial("tcp", addr, tlsConfig)
if err != nil {
log.Error(err.Error())
return
}
conn := drpcconn.New(rawconn)
defer conn.Close()
c := portalpb.NewDRPCPortalClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
r, err := c.RunCommand(ctx, &portalpb.CommandRequest{Name: s[0], Args: s[1:]})
if err != nil {
log.Error(err.Error())
return
}
log.WithField("state", r.GetState()).Info(r.GetMessage())
})
}
pool.StopAndWait()
return nil
}

142
cmd/speedrun/cli/service.go Normal file
View File

@@ -0,0 +1,142 @@
package cli
import (
"context"
"crypto/tls"
"fmt"
"strings"
"time"
"github.com/alitto/pond"
"github.com/apex/log"
"github.com/speedrunsh/speedrun/pkg/speedrun/cloud"
portalpb "github.com/speedrunsh/speedrun/proto/portal"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"storj.io/drpc/drpcconn"
)
var serviceCmd = &cobra.Command{
Use: "service",
Short: "Manage services",
TraverseChildren: true,
}
var restartCmd = &cobra.Command{
Use: "restart <servicename>",
Short: "Restart a service",
Example: " speedrun service restart nginx",
Args: cobra.MinimumNArgs(1),
RunE: action,
}
var startCmd = &cobra.Command{
Use: "start <servicename>",
Short: "Start a service",
Example: " speedrun service start nginx",
Args: cobra.MinimumNArgs(1),
RunE: action,
}
var stopCmd = &cobra.Command{
Use: "stop <servicename>",
Short: "Stop a service",
Example: " speedrun service stop nginx",
Args: cobra.MinimumNArgs(1),
RunE: action,
}
var statusCmd = &cobra.Command{
Use: "status <servicename>",
Short: "Return the status of the service",
Example: " speedrun service status nginx",
Args: cobra.MinimumNArgs(1),
RunE: action,
}
func init() {
serviceCmd.SetUsageTemplate(usage)
serviceCmd.AddCommand(restartCmd)
serviceCmd.AddCommand(startCmd)
serviceCmd.AddCommand(stopCmd)
serviceCmd.AddCommand(statusCmd)
}
func action(cmd *cobra.Command, args []string) error {
usePrivateIP := viper.GetBool("portal.use-private-ip")
tlsConfig, err := cloud.SetupTLS()
if err != nil {
return err
}
target, err := cmd.Flags().GetString("target")
if err != nil {
return err
}
portals, err := cloud.GetInstances(target)
if err != nil {
return err
}
pool := pond.New(1000, 10000)
for _, p := range portals {
portal := p
pool.Submit(func() {
fields := log.Fields{
"host": portal.Name,
"address": portal.GetAddress(usePrivateIP),
}
log := log.WithFields(fields)
addr := fmt.Sprintf("%s:%d", portal.GetAddress(usePrivateIP), 1337)
rawconn, err := tls.Dial("tcp", addr, tlsConfig)
if err != nil {
log.Error(err.Error())
return
}
conn := drpcconn.New(rawconn)
defer conn.Close()
c := portalpb.NewDRPCPortalClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
switch cmd.Name() {
case "restart":
r, err := c.ServiceRestart(ctx, &portalpb.ServiceRequest{Name: strings.Join(args, " ")})
if err != nil {
log.Error(err.Error())
return
}
log.WithField("state", r.GetState()).Info(r.GetMessage())
case "start":
r, err := c.ServiceStart(ctx, &portalpb.ServiceRequest{Name: strings.Join(args, " ")})
if err != nil {
log.Error(err.Error())
return
}
log.WithField("state", r.GetState()).Info(r.GetMessage())
case "stop":
r, err := c.ServiceStop(ctx, &portalpb.ServiceRequest{Name: strings.Join(args, " ")})
if err != nil {
log.Error(err.Error())
return
}
log.WithField("state", r.GetState()).Info(r.GetMessage())
case "status":
r, err := c.ServiceStatus(ctx, &portalpb.ServiceRequest{Name: strings.Join(args, " ")})
if err != nil {
log.Error(err.Error())
return
}
log.WithField("state", r.GetState()).Infof("Loadstate: \"%s\", Activestate: \"%s\", Substate: \"%s\"", r.GetLoadstate(), r.GetActivestate(), r.GetSubstate())
}
})
}
pool.StopAndWait()
return nil
}

View File

@@ -0,0 +1,18 @@
Usage:{{if .Runnable}}
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
{{.CommandPath}} [command]{{end}}
Core Commands:{{range .Commands}}{{if (or (eq .Name "help") (eq .Name "init") (eq .Name "key") (eq .Name "portal") (eq .Name "completion"))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}
Action Commands:{{range .Commands}}{{if (or (eq .Name "run") (eq .Name "exec") (eq .Name "service"))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}
{{if .HasAvailableLocalFlags}}
Flags:
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
Global Flags:
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}

View File

@@ -0,0 +1,23 @@
Usage:{{if .Runnable}}
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
Aliases:
{{.NameAndAliases}}{{end}}{{if .HasExample}}
Examples:
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}
Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
Flags:
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
Global Flags:
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}

9
cmd/speedrun/main.go Normal file
View File

@@ -0,0 +1,9 @@
package main
import (
"github.com/speedrunsh/speedrun/cmd/speedrun/cli"
)
func main() {
cli.Execute()
}

View File

@@ -1,35 +0,0 @@
package colors
import (
"github.com/jwalton/gchalk"
)
// Green returns a green string
func Green(s string) string {
return gchalk.Green(s)
}
// Yellow returns a green string
func Yellow(s string) string {
return gchalk.Yellow(s)
}
// Red returns a green string
func Red(s string) string {
return gchalk.Red(s)
}
// Blue returns a blue string
func Blue(s string) string {
return gchalk.Blue(s)
}
// White returns a white string
func White(s string) string {
return gchalk.White(s)
}
// Purple returns a purple string
func Purple(s string) string {
return gchalk.RGB(196, 16, 224)(s)
}

View File

@@ -1,77 +0,0 @@
package config
import (
"errors"
"os"
"path/filepath"
"regexp"
"github.com/apex/log"
"github.com/manifoldco/promptui"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/viper"
)
//Create generates a new config
func Create() error {
viper.SetDefault("loglevel", "info")
project := promptui.Prompt{
Label: "Project id",
Validate: func(input string) error {
match, err := regexp.MatchString("^[a-z][a-z0-9-]{6,30}", input)
if err != nil {
return err
}
if !match {
return errors.New("invalid projectid")
}
return nil
},
}
prompt := promptui.Select{
Label: "Pick a cloud provider",
Items: []string{"Google Cloud"},
HideHelp: true,
}
_, result, err := prompt.Run()
if err != nil {
log.Fatal(err.Error())
}
switch result {
case "Google Cloud":
result, err := project.Run()
if err != nil {
log.Fatal(err.Error())
}
viper.SetDefault("gcp.projectid", result)
}
if err != nil {
log.Fatal(err.Error())
}
home, err := homedir.Dir()
if err != nil {
return err
}
dir := filepath.Join(home, ".speedrun")
path := filepath.Join(dir, "config.toml")
if _, err := os.Stat(dir); os.IsNotExist(err) {
err = os.Mkdir(dir, 0755)
if err != nil {
return err
}
}
err = viper.WriteConfigAs(path)
if err != nil {
return err
}
log.Infof("Your config was saved at: %s", path)
return nil
}

74
go.mod
View File

@@ -1,61 +1,55 @@
module speedrun
module github.com/speedrunsh/speedrun
go 1.17
require (
github.com/alitto/pond v1.5.1
github.com/alitto/pond v1.7.0
github.com/apex/log v1.9.0
github.com/cheggaaa/pb/v3 v3.0.8
github.com/jwalton/gchalk v1.0.3
github.com/manifoldco/promptui v0.8.0
github.com/melbahja/goph v1.2.1
github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a
github.com/mitchellh/go-homedir v1.1.0
github.com/spf13/cobra v1.2.1
github.com/spf13/viper v1.8.1
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
google.golang.org/api v0.52.0
github.com/spf13/cobra v1.3.0
github.com/spf13/viper v1.10.1
google.golang.org/api v0.65.0
)
require (
cloud.google.com/go v0.89.0 // indirect
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
github.com/fatih/color v1.12.0 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/googleapis/gax-go/v2 v2.0.5 // indirect
github.com/googleapis/gax-go/v2 v2.1.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/juju/ansiterm v0.0.0-20210706145210-9283cdf370b5 // indirect
github.com/jwalton/go-supportscolor v1.0.0 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/lunixbochs/vtclean v1.0.0 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.13 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/pelletier/go-toml v1.9.3 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/pelletier/go-toml v1.9.4
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/sftp v1.13.2 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/spf13/afero v1.6.0 // indirect
github.com/spf13/cast v1.4.0 // indirect
github.com/spf13/afero v1.8.0 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
go.opencensus.io v0.23.0 // indirect
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 // indirect
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect
golang.org/x/text v0.3.6 // indirect
golang.org/x/net v0.0.0-20220111093109-d55c255bac03 // indirect
golang.org/x/sys v0.0.0-20220111092808-5a964db01320 // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20210729151513-df9385d47c1b // indirect
google.golang.org/grpc v1.39.0 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
google.golang.org/grpc v1.43.0 // indirect
gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
require (
cloud.google.com/go/compute v1.0.0 // indirect
github.com/godbus/dbus/v5 v5.0.6 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/zeebo/errs v1.2.2 // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
google.golang.org/genproto v0.0.0-20220111164026-67b88f271998 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
)
require (
github.com/antonmedv/expr v1.9.0
github.com/coreos/go-systemd/v22 v22.3.2
github.com/mitchellh/go-homedir v1.1.0
google.golang.org/protobuf v1.27.1
storj.io/drpc v0.0.26
)

377
go.sum
View File

@@ -3,6 +3,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
@@ -15,25 +16,32 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
cloud.google.com/go v0.88.0/go.mod h1:dnKwfYbP9hQhefiUvpbcAyoGSHUrOxR20JVElLiUvEY=
cloud.google.com/go v0.89.0 h1:ZT4GU+y59fC95Mfdn2RtxuzN2gc69dzlVevQK8Ykyqs=
cloud.google.com/go v0.89.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM=
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
cloud.google.com/go/compute v1.0.0 h1:SJYBzih8Jj9EUm6IDirxKG0I0AGWduhtb6BmdqWarw4=
cloud.google.com/go/compute v1.0.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
@@ -43,15 +51,22 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
github.com/alitto/pond v1.5.1 h1:HwOA8M/QB8lpkqzj8lnCEvC+44lHbPrrD5qHaJ9HRYI=
github.com/alitto/pond v1.5.1/go.mod h1:LPLCPu5q4FAhvZsJB9OsNcXE+pUMu7lwf7fzPZH0HPs=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alitto/pond v1.7.0 h1:o/V4qQmhT6EOXkg1PdCz2tF9X3RjMTU6hhlHq1k2t1g=
github.com/alitto/pond v1.7.0/go.mod h1:/TYBaKCXvQ8Qiy3TfOoKeu6K7AVAhMpvu4itSpGdws0=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antonmedv/expr v1.9.0 h1:j4HI3NHEdgDnN9p6oI6Ndr0G5QryMY0FNxT4ONrFDGU=
github.com/antonmedv/expr v1.9.0/go.mod h1:5qsM3oLGDND7sDmQGDXHkYfkjYMUX14qsgqmHhwGEk8=
github.com/apex/log v1.9.0 h1:FHtw/xuaM8AgmvDDTI9fiwoAL25Sq2cxojnZICUU8l0=
github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA=
github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo=
@@ -59,28 +74,42 @@ github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy
github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cheggaaa/pb/v3 v3.0.8 h1:bC8oemdChbke2FHIIGy9mn4DPJ2caZYQnfbRqwmdCoA=
github.com/cheggaaa/pb/v3 v3.0.8/go.mod h1:UICbiLec/XO6Hw6k+BHEtHeQFzzBH4i2/qk/ow1EJTA=
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -91,20 +120,31 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro=
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -169,163 +209,185 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210715191844-86eeefc3e471/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU=
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=
github.com/juju/ansiterm v0.0.0-20210706145210-9283cdf370b5 h1:Q5klzs6BL5FkassBX65t+KkG0XjYcjxEm+GNcQAsuaw=
github.com/juju/ansiterm v0.0.0-20210706145210-9283cdf370b5/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=
github.com/jwalton/gchalk v1.0.3 h1:Eo/Ig+8efrpVpNI1ZoiJ+fLFHGXH7Q7+pBzOpG4Mib4=
github.com/jwalton/gchalk v1.0.3/go.mod h1:kmvsubrIhnHSklat2ZWNj7zlLs3SS2wGNgsBVPtill4=
github.com/jwalton/go-supportscolor v1.0.0 h1:Do3OE2y/iUibg79+QhkRE6G2evYKEv2bwi6sGs8Nd7s=
github.com/jwalton/go-supportscolor v1.0.0/go.mod h1:hFVUAZV2cWg+WFFC4v8pT2X/S2qUUBYMioBD9AINXGs=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/manifoldco/promptui v0.8.0 h1:R95mMF+McvXZQ7j1g8ucVZE1gLP3Sv6j9vlF9kyRqQo=
github.com/manifoldco/promptui v0.8.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/melbahja/goph v1.2.1 h1:msPxbLxf1PnbxRQGhv9mVqm7T16tPo3wqLWah0hJhKQ=
github.com/melbahja/goph v1.2.1/go.mod h1:y+wS4c0UtZOLSwNz6ktaGiyUeYZBeT1e8PiSz+YK77o=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a h1:eU8j/ClY2Ty3qdHnn0TyW3ivFoPC/0F1gQZz8yTxbbE=
github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a/go.mod h1:v8eSC2SMp9/7FTKUncp7fH9IwPfw+ysMObcEz5FWheQ=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pkg/sftp v1.12.0/go.mod h1:fUqqXB5vEgVCZ131L+9say31RAri6aF6KDViawhxKK8=
github.com/pkg/sftp v1.13.2 h1:taJnKntsWgU+qae21Rx52lIwndAdKrj0mfUNQsz1z4Q=
github.com/pkg/sftp v1.13.2/go.mod h1:LzqnAvaD5TWeNBsZpfKxSYn1MbjWwOsCIAFFJbpIsK8=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/rivo/tview v0.0.0-20200219210816-cd38d7432498/go.mod h1:6lkG1x+13OShEf0EaOCaTQYyB7d5nSbb181KtjlS+84=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
github.com/sanity-io/litter v1.2.0/go.mod h1:JF6pZUFgu2Q0sBZ+HSV35P8TVPI1TTzEwyu9FXAw2W4=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs=
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.4.0 h1:WhlbjwB9EGCc8W5Rxdkus+wmH2ASRwwTJk6tgHKwdqQ=
github.com/spf13/cast v1.4.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/afero v1.8.0 h1:5MmtuhAgYeU6qpa7w7bP0dv6MBYuup0vekhSpSkoq60=
github.com/spf13/afero v1.8.0/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0=
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
github.com/spf13/viper v1.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk=
github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -342,14 +404,19 @@ github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj
github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0=
github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao=
github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/errs v1.2.2 h1:5NFypMTuSdoySVTqlNs1dEoU21QVamMQJxW/Fii5O7g=
github.com/zeebo/errs v1.2.2/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -362,19 +429,19 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -410,11 +477,12 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -422,9 +490,11 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -443,13 +513,17 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 h1:a8jGStKg0XqKDlKqjLrXn0ioF5MH36pT7Z0BRTqLhbk=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220111093109-d55c255bac03 h1:0FB83qp0AzVJm+0wcIlauAjJ+tNdh7jLuacRYCIVv7s=
golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -461,11 +535,13 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 h1:3B43BWw0xEBsLZ/NO1VALz6fppU3481pik+2Ksv45z8=
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -479,26 +555,32 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -514,10 +596,11 @@ golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -529,14 +612,22 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY=
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -544,8 +635,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -555,7 +647,6 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
@@ -563,9 +654,9 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -598,6 +689,7 @@ golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
@@ -630,14 +722,20 @@ google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34q
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
google.golang.org/api v0.52.0 h1:m5FLEd6dp5CU1F0tMWyqDi2XjchviIz8ntzOSz7w8As=
google.golang.org/api v0.52.0/go.mod h1:Him/adpjt0sxtkWViy0b6xyKW/SD71CwdJ7HqJo7SrU=
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU=
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw=
google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
google.golang.org/api v0.65.0 h1:MTW9c+LIBAbwoS1Gb+YV7NjFBt2f7GtAS5hIzh2NjgQ=
google.golang.org/api v0.65.0/go.mod h1:ArYhxgGadlWmqO1IqVujw6Cs8IdD33bTmzKo2Sh+cbg=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -681,7 +779,9 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
@@ -693,11 +793,26 @@ google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxH
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210721163202-f1cecdd8b78a/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210729151513-df9385d47c1b h1:4xoALQmXxqVdDdLimpPyPeDdsJzo+nFTJw9euAMpqgM=
google.golang.org/genproto v0.0.0-20210729151513-df9385d47c1b/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220111164026-67b88f271998 h1:g/x+MYjJYDEP3OBCYYmwIbt4x6k3gryb+ohyOR7PXfI=
google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -717,12 +832,16 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.39.0 h1:Klz8I9kdtkIN6EpHHUOMLCYhTn/2WAe5a0s1hcBkdTI=
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM=
google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -738,18 +857,22 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
@@ -767,3 +890,5 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
storj.io/drpc v0.0.26 h1:T6jJzjby7QUa/2XHR1qMxTCENpDHEw4/o+kfDfZQqQI=
storj.io/drpc v0.0.26/go.mod h1:ofQUDPQbbIymRDKE0tms48k8bLP5Y+dsI9CbXGv3gko=

17
init/portal.service Normal file
View File

@@ -0,0 +1,17 @@
[Unit]
Description=Portal
Documentation=https://speedrun.sh
Wants=network-online.target
After=network-online.target
[Service]
ExecReload=/bin/kill -HUP $MAINPID
ExecStart=/usr/local/bin/portal
KillMode=process
KillSignal=SIGINT
Restart=on-failure
RestartSec=2
TasksMax=infinity
[Install]
WantedBy=multi-user.target

View File

@@ -1,124 +0,0 @@
package key
import (
"bytes"
"crypto/ed25519"
"crypto/rand"
"encoding/base64"
"encoding/gob"
"encoding/pem"
"io/ioutil"
"os/user"
"path/filepath"
"strings"
"github.com/apex/log"
"github.com/melbahja/goph"
"github.com/mikesmitty/edkey"
"golang.org/x/crypto/ssh"
)
const Comment = "speedrun"
type Key struct {
User string
Comment string
Key []byte
}
func New() (*Key, error) {
key := &Key{}
_, rawKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, err
}
pemKey := &pem.Block{
Type: "OPENSSH PRIVATE KEY",
Bytes: edkey.MarshalED25519PrivateKey(rawKey),
}
privateKey := pem.EncodeToMemory(pemKey)
key.Key = privateKey
user, err := user.Current()
if err != nil {
return nil, err
}
key.User = user.Username
key.Comment = Comment
return key, nil
}
func Read(path string) (*Key, error) {
cleanPath := filepath.Clean(path)
absPath, err := filepath.Abs(cleanPath)
if err != nil {
return nil, err
}
file, err := ioutil.ReadFile(absPath)
if err != nil {
return nil, err
}
b64Result, err := base64.StdEncoding.DecodeString(string(file))
if err != nil {
return nil, err
}
key := &Key{}
buf := bytes.NewBuffer(b64Result)
enc := gob.NewDecoder(buf)
err = enc.Decode(key)
if err != nil {
return nil, err
}
return key, nil
}
func (k *Key) Write(path string) error {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(k)
if err != nil {
return err
}
b64Result := base64.StdEncoding.EncodeToString(buf.Bytes())
if err != nil {
return err
}
log.Debugf("Writing priviate key to %s", path)
err = ioutil.WriteFile(path, []byte(b64Result), 0600)
if err != nil {
return err
}
return nil
}
func (k *Key) MarshalAuthorizedKey() (string, error) {
privKey, err := ssh.ParsePrivateKey(k.Key)
if err != nil {
return "", err
}
authorizedKey := ssh.MarshalAuthorizedKey(privKey.PublicKey())
trimmedKey := strings.TrimSuffix(string(authorizedKey), "\n")
return trimmedKey, nil
}
func (k *Key) GetAuth() (goph.Auth, error) {
privKey, err := ssh.ParsePrivateKey(k.Key)
if err != nil {
return nil, err
}
return goph.Auth{
ssh.PublicKeys(privKey),
}, nil
}

16
main.go
View File

@@ -1,16 +0,0 @@
package main
import (
"os"
"speedrun/cmd"
"github.com/apex/log"
loghandler "github.com/apex/log/handlers/cli"
)
func main() {
h := loghandler.New(os.Stdout)
h.Padding = 0
log.SetHandler(h)
cmd.Execute()
}

View File

@@ -1,190 +0,0 @@
package marathon
import (
"fmt"
"net"
"os"
"path/filepath"
"speedrun/cloud"
"speedrun/colors"
"speedrun/key"
"sync"
"time"
"github.com/alitto/pond"
"github.com/apex/log"
"github.com/cheggaaa/pb/v3"
"github.com/melbahja/goph"
"github.com/mitchellh/go-homedir"
"github.com/spf13/viper"
"golang.org/x/crypto/ssh"
)
// Marathon represents the instance of the execution of a command against a number of target servers
type Marathon struct {
sync.Mutex
errors map[string]error
failures map[string]string
successes map[string]string
Command string
Timeout time.Duration
Concurrency int
}
// New creates a new instance of the Marathon type
func New(command string, timeout time.Duration, concurrency int) *Marathon {
r := Marathon{
errors: make(map[string]error),
failures: make(map[string]string),
successes: make(map[string]string),
Command: command,
Timeout: timeout,
Concurrency: concurrency,
}
return &r
}
// Run runs a given command on servers in the addresses list
func (m *Marathon) Run(instances []cloud.Instance, key *key.Key, ignoreFingerprint bool) error {
auth, err := key.GetAuth()
if err != nil {
return err
}
err = checkHostsFile()
if err != nil {
return err
}
cb := verifyHost
if ignoreFingerprint {
cb = ssh.InsecureIgnoreHostKey()
}
pool := pond.New(m.Concurrency, 10000)
bar := pb.New(len(instances))
lvl, err := log.ParseLevel(viper.GetString("loglevel"))
if err != nil {
return fmt.Errorf("couldn't parse log level: %s", err)
}
if lvl > 0 {
bar.SetMaxWidth(1)
bar.SetTemplateString(fmt.Sprintf("%s Running [%s]: {{counters . }}", colors.Blue("•"), colors.Purple(m.Command)))
bar.Start()
}
for _, i := range instances {
instance := i
log.Debugf("Adding %s to the queue", instance.Name)
pool.Submit(func() {
var client *goph.Client
var err error
client, err = goph.NewConn(&goph.Config{
User: key.User,
Addr: instance.Address,
Port: 22,
Auth: auth,
Callback: cb,
Timeout: m.Timeout,
})
if err != nil {
log.WithField("host", instance.Name).Debugf("Error encountered while trying to connect: %s", err)
m.Lock()
bar.Increment()
m.errors[instance.Name] = err
m.Unlock()
return
}
defer client.Close()
out, err := client.Run(m.Command)
if err != nil {
m.Lock()
bar.Increment()
m.failures[instance.Name] = formatOutput(string(out))
m.Unlock()
return
}
m.Lock()
bar.Increment()
m.successes[instance.Name] = formatOutput(string(out))
m.Unlock()
})
}
pool.StopAndWait()
bar.Finish()
return nil
}
// VerifyHost chekcks that the remote host's fingerprint matches the know one to avoid MITM.
// If the host is new the fingerprint is added to known hostss file
func verifyHost(host string, remote net.Addr, key ssh.PublicKey) error {
home, err := homedir.Dir()
if err != nil {
return err
}
knownhosts := filepath.Join(home, ".speedrun", "known_hosts")
hostFound, err := goph.CheckKnownHost(host, remote, key, knownhosts)
if hostFound && err != nil {
log.Debugf("Host fingerprint known")
return err
}
if !hostFound && err != nil {
if err.Error() == "knownhosts: key is unknown" {
log.Debugf("Adding host %s to ~/.speedrun/known_hosts", host)
return goph.AddKnownHost(host, remote, key, knownhosts)
}
return err
}
if hostFound {
log.Debugf("Host %s is already known", host)
return nil
}
return nil
}
func checkHostsFile() error {
home, err := homedir.Dir()
if err != nil {
return err
}
knownhosts := filepath.Join(home, ".speedrun", "known_hosts")
if _, err := os.Stat(knownhosts); os.IsNotExist(err) {
_, err = os.Create(knownhosts)
if err != nil {
return err
}
}
return nil
}
// PrintResult prints the results of the ssh command run
func (m *Marathon) PrintResult(failures bool) {
if !failures {
for host, msg := range m.successes {
fmt.Printf(" %s:\n%s\n", colors.Green(host), colors.White(msg))
}
}
for host, msg := range m.failures {
fmt.Printf(" %s:\n%s\n", colors.Yellow(host), colors.White(msg))
}
for host, msg := range m.errors {
fmt.Printf(" %s:\n %s\n\n", colors.Red(host), colors.White(msg.Error()))
}
fmt.Printf("%s: %d\n%s: %d\n%s: %d\n", colors.Green("Success"), len(m.successes), colors.Yellow("Failure"), len(m.failures), colors.Red("Error"), len(m.errors))
}

View File

@@ -1,17 +0,0 @@
package marathon
import (
"fmt"
"strings"
)
func formatOutput(body string) string {
f := []string{}
for _, line := range strings.Split(body, "\n") {
line = fmt.Sprintf(" %s", line)
f = append(f, line)
}
f = append(f[:len(f)-1], "")
return strings.Join(f, "\n")
}

View File

@@ -0,0 +1,90 @@
package cryptoutil
import (
"crypto/ed25519"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"io/ioutil"
"math/big"
)
func InsecureTLSConfig() (*tls.Config, error) {
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, err
}
template := x509.Certificate{SerialNumber: big.NewInt(1)}
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey, privateKey)
if err != nil {
return nil, err
}
bytes, err := x509.MarshalPKCS8PrivateKey(privateKey)
if err != nil {
return nil, err
}
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: bytes})
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
if err != nil {
return nil, err
}
return &tls.Config{
MinVersion: tls.VersionTLS13,
CurvePreferences: []tls.CurveID{tls.X25519},
CipherSuites: []uint16{tls.TLS_CHACHA20_POLY1305_SHA256},
Certificates: []tls.Certificate{tlsCert},
InsecureSkipVerify: true,
}, nil
}
func ClientTLSConfig(caPath, certPath, keyPath string) (*tls.Config, error) {
caCert, err := ioutil.ReadFile(caPath)
if err != nil {
return nil, err
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
return nil, err
}
return &tls.Config{
MinVersion: tls.VersionTLS13,
CurvePreferences: []tls.CurveID{tls.X25519},
CipherSuites: []uint16{tls.TLS_CHACHA20_POLY1305_SHA256},
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
}, nil
}
func ServerTLSConfig(caPath, certPath, keyPath string) (*tls.Config, error) {
caCert, err := ioutil.ReadFile(caPath)
if err != nil {
return nil, err
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
return nil, err
}
return &tls.Config{
MinVersion: tls.VersionTLS13,
CurvePreferences: []tls.CurveID{tls.X25519},
CipherSuites: []uint16{tls.TLS_CHACHA20_POLY1305_SHA256},
Certificates: []tls.Certificate{cert},
ClientCAs: caCertPool,
ClientAuth: tls.RequireAndVerifyClientCert,
}, nil
}

28
pkg/portal/command.go Normal file
View File

@@ -0,0 +1,28 @@
package portal
import (
"context"
"os/exec"
"strings"
"github.com/apex/log"
"github.com/speedrunsh/speedrun/proto/portal"
)
func (s *Server) RunCommand(ctx context.Context, in *portal.CommandRequest) (*portal.CommandResponse, error) {
fields := log.Fields{
"context": "command",
}
log := log.WithFields(fields)
log.Debugf("Received command: %s %s", in.GetName(), in.GetArgs())
cmd := exec.Command(in.GetName(), in.GetArgs()...)
stdout, err := cmd.CombinedOutput()
if err != nil {
log.Error(err.Error())
return nil, err
}
return &portal.CommandResponse{Message: strings.TrimSpace(string(stdout))}, nil
}

7
pkg/portal/server.go Normal file
View File

@@ -0,0 +1,7 @@
package portal
import "github.com/speedrunsh/speedrun/proto/portal"
type Server struct {
portal.DRPCPortalUnimplementedServer
}

158
pkg/portal/service.go Normal file
View File

@@ -0,0 +1,158 @@
package portal
import (
"context"
"fmt"
"strings"
"github.com/apex/log"
"github.com/coreos/go-systemd/v22/dbus"
"github.com/speedrunsh/speedrun/proto/portal"
)
func (s *Server) ServiceRestart(ctx context.Context, service *portal.ServiceRequest) (*portal.ServiceResponse, error) {
fields := log.Fields{
"context": "service",
"command": "restart",
"name": service.GetName(),
}
log := log.WithFields(fields)
log.Debug("Received service restart request")
conn, err := dbus.NewWithContext(ctx)
if err != nil {
log.Error(err.Error())
return nil, err
}
defer conn.Close()
responseChan := make(chan string, 1)
serviceName := fmt.Sprintf("%s.service", service.GetName())
_, err = conn.RestartUnitContext(ctx, serviceName, "replace", responseChan)
if err != nil {
log.Error(err.Error())
return nil, err
}
res := <-responseChan
log.Debugf("Service restart result: %v", res)
return &portal.ServiceResponse{State: portal.State_CHANGED, Message: strings.Title(res)}, nil
}
func (s *Server) ServiceStop(ctx context.Context, service *portal.ServiceRequest) (*portal.ServiceResponse, error) {
fields := log.Fields{
"context": "service",
"command": "stop",
"name": service.GetName(),
}
log := log.WithFields(fields)
log.Debug("Received service stop request")
conn, err := dbus.NewWithContext(ctx)
if err != nil {
log.Error(err.Error())
return nil, err
}
defer conn.Close()
responseChan := make(chan string, 1)
serviceName := fmt.Sprintf("%s.service", service.GetName())
list, err := conn.ListUnitsByNamesContext(ctx, []string{serviceName})
if err != nil {
log.Error(err.Error())
return nil, err
}
log.Debugf("Fetched service list by name: %v", list)
if list[0].ActiveState == "inactive" {
return &portal.ServiceResponse{State: portal.State_UNCHANGED, Message: "Service already stopped"}, nil
}
_, err = conn.StopUnitContext(ctx, serviceName, "replace", responseChan)
if err != nil {
log.Error(err.Error())
return nil, err
}
res := <-responseChan
log.Debugf("Service stop result: %v", res)
return &portal.ServiceResponse{State: portal.State_CHANGED, Message: strings.Title(res)}, nil
}
func (s *Server) ServiceStart(ctx context.Context, service *portal.ServiceRequest) (*portal.ServiceResponse, error) {
fields := log.Fields{
"context": "service",
"command": "start",
"name": service.GetName(),
}
log := log.WithFields(fields)
log.Debug("Received service start request")
conn, err := dbus.NewWithContext(ctx)
if err != nil {
log.Error(err.Error())
return nil, err
}
defer conn.Close()
responseChan := make(chan string, 1)
serviceName := fmt.Sprintf("%s.service", service.GetName())
list, err := conn.ListUnitsByNamesContext(ctx, []string{serviceName})
if err != nil {
log.Error(err.Error())
return nil, err
}
log.Debugf("Fetched service list by name: %v", list)
if list[0].ActiveState == "active" {
return &portal.ServiceResponse{State: portal.State_UNCHANGED, Message: "Service already running"}, nil
}
_, err = conn.StartUnitContext(ctx, serviceName, "replace", responseChan)
if err != nil {
log.Error(err.Error())
return nil, err
}
res := <-responseChan
log.Debugf("Service start result: %v", res)
return &portal.ServiceResponse{State: portal.State_CHANGED, Message: strings.Title(res)}, nil
}
func (s *Server) ServiceStatus(ctx context.Context, service *portal.ServiceRequest) (*portal.ServiceStatusResponse, error) {
fields := log.Fields{
"context": "service",
"command": "status",
"name": service.GetName(),
}
log := log.WithFields(fields)
log.Debug("Received service status request")
conn, err := dbus.NewWithContext(ctx)
if err != nil {
log.Error(err.Error())
return nil, err
}
defer conn.Close()
serviceName := fmt.Sprintf("%s.service", service.GetName())
res, err := conn.ListUnitsByNamesContext(ctx, []string{serviceName})
if err != nil {
log.Error(err.Error())
return nil, err
}
log.Debugf("Fetched service list by name: %v", res[0])
if res[0].LoadState == "not-found" {
log.Error("service not found")
return nil, fmt.Errorf("service not found")
}
return &portal.ServiceStatusResponse{
State: portal.State_UNCHANGED,
Activestate: res[0].ActiveState,
Loadstate: res[0].LoadState,
Substate: res[0].SubState,
}, nil
}

View File

@@ -0,0 +1,53 @@
package cloud
import (
"context"
"fmt"
"google.golang.org/api/compute/v1"
)
type GoogleClient struct {
*compute.Service
}
func NewGCPClient() (*GoogleClient, error) {
var err error
ctx := context.Background()
gce, err := compute.NewService(ctx)
if err != nil {
err = fmt.Errorf("couldn't initialize GCP client: %v", err)
return nil, err
}
return &GoogleClient{gce}, nil
}
// GetInstances returns a list of external IP addresses used for the SHH connection
func (c *GoogleClient) GetInstances(project string) ([]Instance, error) {
instances := []Instance{}
listCall := c.Instances.AggregatedList(project).Fields("nextPageToken", "items(Name,NetworkInterfaces,Labels)")
var ctx context.Context
listCall.Pages(ctx, func(list *compute.InstanceAggregatedList) error {
for _, item := range list.Items {
for _, instance := range item.Instances {
i := Instance{
Name: instance.Name,
PrivateAddress: instance.NetworkInterfaces[0].NetworkIP,
PublicAddress: instance.NetworkInterfaces[0].AccessConfigs[0].NatIP,
Labels: instance.Labels,
}
instances = append(instances, i)
}
}
return nil
})
_, err := listCall.Do()
if err != nil {
return nil, err
}
return instances, nil
}

View File

@@ -0,0 +1,93 @@
package cloud
import (
"crypto/tls"
"fmt"
"github.com/antonmedv/expr"
"github.com/apex/log"
"github.com/speedrunsh/speedrun/pkg/common/cryptoutil"
"github.com/spf13/viper"
)
type Instance struct {
PublicAddress string
PrivateAddress string
Name string
Labels map[string]string
}
func (i Instance) GetAddress(private bool) string {
if private {
return i.PrivateAddress
}
return i.PublicAddress
}
func GetInstances(target string) ([]Instance, error) {
project := viper.GetString("gcp.projectid")
gcpClient, err := NewGCPClient()
if err != nil {
return nil, err
}
log.Info("Fetching instance list")
instances, err := gcpClient.GetInstances(project)
if err != nil {
return nil, err
}
subset, err := filter(instances, target)
if err != nil {
return nil, err
}
if len(subset) == 0 {
return nil, fmt.Errorf("no instances found")
}
return subset, nil
}
func SetupTLS() (*tls.Config, error) {
insecure := viper.GetBool("tls.insecure")
caPath := viper.GetString("tls.ca")
certPath := viper.GetString("tls.cert")
keyPath := viper.GetString("tls.key")
if insecure {
log.Warn("Using insecure TLS configuration, this should be avoided in production environments")
return cryptoutil.InsecureTLSConfig()
} else {
return cryptoutil.ClientTLSConfig(caPath, certPath, keyPath)
}
}
func filter(instnces []Instance, target string) ([]Instance, error) {
var subset []Instance
program, err := expr.Compile(target, expr.Env(Instance{}), expr.AsBool())
if err != nil {
return nil, err
}
for _, instance := range instnces {
output, err := expr.Run(program, instance)
if err != nil {
continue
}
match, ok := output.(bool)
if !ok {
continue
}
if match {
subset = append(subset, instance)
}
}
return subset, nil
}

10
proto/Makefile Normal file
View File

@@ -0,0 +1,10 @@
.PHONY: requirements portal
all: requirements portal
requirements:
@go mod download
@go install storj.io/drpc/cmd/protoc-gen-go-drpc@latest
portal:
protoc --go_out=paths=source_relative:. --go-drpc_out=paths=source_relative:. portal/*.proto

687
proto/portal/portal.pb.go Normal file
View File

@@ -0,0 +1,687 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.17.3
// source: portal/portal.proto
package portal
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type State int32
const (
State_UNKNOWN State = 0
State_CHANGED State = 1
State_UNCHANGED State = 2
)
// Enum value maps for State.
var (
State_name = map[int32]string{
0: "UNKNOWN",
1: "CHANGED",
2: "UNCHANGED",
}
State_value = map[string]int32{
"UNKNOWN": 0,
"CHANGED": 1,
"UNCHANGED": 2,
}
)
func (x State) Enum() *State {
p := new(State)
*p = x
return p
}
func (x State) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (State) Descriptor() protoreflect.EnumDescriptor {
return file_portal_portal_proto_enumTypes[0].Descriptor()
}
func (State) Type() protoreflect.EnumType {
return &file_portal_portal_proto_enumTypes[0]
}
func (x State) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use State.Descriptor instead.
func (State) EnumDescriptor() ([]byte, []int) {
return file_portal_portal_proto_rawDescGZIP(), []int{0}
}
type CommandRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Args []string `protobuf:"bytes,2,rep,name=args,proto3" json:"args,omitempty"`
}
func (x *CommandRequest) Reset() {
*x = CommandRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_portal_portal_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CommandRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CommandRequest) ProtoMessage() {}
func (x *CommandRequest) ProtoReflect() protoreflect.Message {
mi := &file_portal_portal_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CommandRequest.ProtoReflect.Descriptor instead.
func (*CommandRequest) Descriptor() ([]byte, []int) {
return file_portal_portal_proto_rawDescGZIP(), []int{0}
}
func (x *CommandRequest) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *CommandRequest) GetArgs() []string {
if x != nil {
return x.Args
}
return nil
}
type CommandResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
State State `protobuf:"varint,1,opt,name=state,proto3,enum=portal.State" json:"state,omitempty"`
Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
}
func (x *CommandResponse) Reset() {
*x = CommandResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_portal_portal_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CommandResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CommandResponse) ProtoMessage() {}
func (x *CommandResponse) ProtoReflect() protoreflect.Message {
mi := &file_portal_portal_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CommandResponse.ProtoReflect.Descriptor instead.
func (*CommandResponse) Descriptor() ([]byte, []int) {
return file_portal_portal_proto_rawDescGZIP(), []int{1}
}
func (x *CommandResponse) GetState() State {
if x != nil {
return x.State
}
return State_UNKNOWN
}
func (x *CommandResponse) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
type ServiceRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
}
func (x *ServiceRequest) Reset() {
*x = ServiceRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_portal_portal_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ServiceRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ServiceRequest) ProtoMessage() {}
func (x *ServiceRequest) ProtoReflect() protoreflect.Message {
mi := &file_portal_portal_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ServiceRequest.ProtoReflect.Descriptor instead.
func (*ServiceRequest) Descriptor() ([]byte, []int) {
return file_portal_portal_proto_rawDescGZIP(), []int{2}
}
func (x *ServiceRequest) GetName() string {
if x != nil {
return x.Name
}
return ""
}
type ServiceResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
State State `protobuf:"varint,1,opt,name=state,proto3,enum=portal.State" json:"state,omitempty"`
Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
}
func (x *ServiceResponse) Reset() {
*x = ServiceResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_portal_portal_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ServiceResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ServiceResponse) ProtoMessage() {}
func (x *ServiceResponse) ProtoReflect() protoreflect.Message {
mi := &file_portal_portal_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ServiceResponse.ProtoReflect.Descriptor instead.
func (*ServiceResponse) Descriptor() ([]byte, []int) {
return file_portal_portal_proto_rawDescGZIP(), []int{3}
}
func (x *ServiceResponse) GetState() State {
if x != nil {
return x.State
}
return State_UNKNOWN
}
func (x *ServiceResponse) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
type ServiceStatusResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
State State `protobuf:"varint,1,opt,name=state,proto3,enum=portal.State" json:"state,omitempty"`
Loadstate string `protobuf:"bytes,2,opt,name=loadstate,proto3" json:"loadstate,omitempty"`
Activestate string `protobuf:"bytes,3,opt,name=activestate,proto3" json:"activestate,omitempty"`
Substate string `protobuf:"bytes,4,opt,name=substate,proto3" json:"substate,omitempty"`
}
func (x *ServiceStatusResponse) Reset() {
*x = ServiceStatusResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_portal_portal_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ServiceStatusResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ServiceStatusResponse) ProtoMessage() {}
func (x *ServiceStatusResponse) ProtoReflect() protoreflect.Message {
mi := &file_portal_portal_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ServiceStatusResponse.ProtoReflect.Descriptor instead.
func (*ServiceStatusResponse) Descriptor() ([]byte, []int) {
return file_portal_portal_proto_rawDescGZIP(), []int{4}
}
func (x *ServiceStatusResponse) GetState() State {
if x != nil {
return x.State
}
return State_UNKNOWN
}
func (x *ServiceStatusResponse) GetLoadstate() string {
if x != nil {
return x.Loadstate
}
return ""
}
func (x *ServiceStatusResponse) GetActivestate() string {
if x != nil {
return x.Activestate
}
return ""
}
func (x *ServiceStatusResponse) GetSubstate() string {
if x != nil {
return x.Substate
}
return ""
}
type CPUusageRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *CPUusageRequest) Reset() {
*x = CPUusageRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_portal_portal_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CPUusageRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CPUusageRequest) ProtoMessage() {}
func (x *CPUusageRequest) ProtoReflect() protoreflect.Message {
mi := &file_portal_portal_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CPUusageRequest.ProtoReflect.Descriptor instead.
func (*CPUusageRequest) Descriptor() ([]byte, []int) {
return file_portal_portal_proto_rawDescGZIP(), []int{5}
}
type CPUusageResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Loadavg1 int32 `protobuf:"varint,1,opt,name=loadavg1,proto3" json:"loadavg1,omitempty"`
Loadavg5 int32 `protobuf:"varint,2,opt,name=loadavg5,proto3" json:"loadavg5,omitempty"`
Loadavg15 int32 `protobuf:"varint,3,opt,name=loadavg15,proto3" json:"loadavg15,omitempty"`
}
func (x *CPUusageResponse) Reset() {
*x = CPUusageResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_portal_portal_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CPUusageResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CPUusageResponse) ProtoMessage() {}
func (x *CPUusageResponse) ProtoReflect() protoreflect.Message {
mi := &file_portal_portal_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CPUusageResponse.ProtoReflect.Descriptor instead.
func (*CPUusageResponse) Descriptor() ([]byte, []int) {
return file_portal_portal_proto_rawDescGZIP(), []int{6}
}
func (x *CPUusageResponse) GetLoadavg1() int32 {
if x != nil {
return x.Loadavg1
}
return 0
}
func (x *CPUusageResponse) GetLoadavg5() int32 {
if x != nil {
return x.Loadavg5
}
return 0
}
func (x *CPUusageResponse) GetLoadavg15() int32 {
if x != nil {
return x.Loadavg15
}
return 0
}
var File_portal_portal_proto protoreflect.FileDescriptor
var file_portal_portal_proto_rawDesc = []byte{
0x0a, 0x13, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x22, 0x38, 0x0a,
0x0e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
0x09, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x22, 0x50, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x6d, 0x61,
0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x73, 0x74,
0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x70, 0x6f, 0x72, 0x74,
0x61, 0x6c, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12,
0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x24, 0x0a, 0x0e, 0x53, 0x65, 0x72,
0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22,
0x50, 0x0a, 0x0f, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0e, 0x32, 0x0d, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65,
0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x22, 0x98, 0x01, 0x0a, 0x15, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61,
0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x73,
0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x70, 0x6f, 0x72,
0x74, 0x61, 0x6c, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65,
0x12, 0x1c, 0x0a, 0x09, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x20,
0x0a, 0x0b, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x74, 0x61, 0x74, 0x65,
0x12, 0x1a, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01,
0x28, 0x09, 0x52, 0x08, 0x73, 0x75, 0x62, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x11, 0x0a, 0x0f,
0x43, 0x50, 0x55, 0x75, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22,
0x68, 0x0a, 0x10, 0x43, 0x50, 0x55, 0x75, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x61, 0x64, 0x61, 0x76, 0x67, 0x31, 0x18,
0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x6c, 0x6f, 0x61, 0x64, 0x61, 0x76, 0x67, 0x31, 0x12,
0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x61, 0x64, 0x61, 0x76, 0x67, 0x35, 0x18, 0x02, 0x20, 0x01, 0x28,
0x05, 0x52, 0x08, 0x6c, 0x6f, 0x61, 0x64, 0x61, 0x76, 0x67, 0x35, 0x12, 0x1c, 0x0a, 0x09, 0x6c,
0x6f, 0x61, 0x64, 0x61, 0x76, 0x67, 0x31, 0x35, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09,
0x6c, 0x6f, 0x61, 0x64, 0x61, 0x76, 0x67, 0x31, 0x35, 0x2a, 0x30, 0x0a, 0x05, 0x53, 0x74, 0x61,
0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12,
0x0b, 0x0a, 0x07, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09,
0x55, 0x4e, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x44, 0x10, 0x02, 0x32, 0x9e, 0x03, 0x0a, 0x06,
0x50, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x12, 0x43, 0x0a, 0x0e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x16, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61,
0x6c, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x17, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0c, 0x53,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x16, 0x2e, 0x70, 0x6f,
0x72, 0x74, 0x61, 0x6c, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x53, 0x65, 0x72,
0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x40,
0x0a, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x16, 0x2e,
0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x53,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
0x12, 0x48, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75,
0x73, 0x12, 0x16, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x70, 0x6f, 0x72, 0x74,
0x61, 0x6c, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x0a, 0x52, 0x75,
0x6e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x16, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61,
0x6c, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x17, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x08, 0x43,
0x50, 0x55, 0x75, 0x73, 0x61, 0x67, 0x65, 0x12, 0x17, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c,
0x2e, 0x43, 0x50, 0x55, 0x75, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x18, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x43, 0x50, 0x55, 0x75, 0x73, 0x61,
0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x2d, 0x5a, 0x2b,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x70, 0x65, 0x65, 0x64,
0x72, 0x75, 0x6e, 0x73, 0x68, 0x2f, 0x73, 0x70, 0x65, 0x65, 0x64, 0x72, 0x75, 0x6e, 0x2f, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var (
file_portal_portal_proto_rawDescOnce sync.Once
file_portal_portal_proto_rawDescData = file_portal_portal_proto_rawDesc
)
func file_portal_portal_proto_rawDescGZIP() []byte {
file_portal_portal_proto_rawDescOnce.Do(func() {
file_portal_portal_proto_rawDescData = protoimpl.X.CompressGZIP(file_portal_portal_proto_rawDescData)
})
return file_portal_portal_proto_rawDescData
}
var file_portal_portal_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_portal_portal_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_portal_portal_proto_goTypes = []interface{}{
(State)(0), // 0: portal.State
(*CommandRequest)(nil), // 1: portal.CommandRequest
(*CommandResponse)(nil), // 2: portal.CommandResponse
(*ServiceRequest)(nil), // 3: portal.ServiceRequest
(*ServiceResponse)(nil), // 4: portal.ServiceResponse
(*ServiceStatusResponse)(nil), // 5: portal.ServiceStatusResponse
(*CPUusageRequest)(nil), // 6: portal.CPUusageRequest
(*CPUusageResponse)(nil), // 7: portal.CPUusageResponse
}
var file_portal_portal_proto_depIdxs = []int32{
0, // 0: portal.CommandResponse.state:type_name -> portal.State
0, // 1: portal.ServiceResponse.state:type_name -> portal.State
0, // 2: portal.ServiceStatusResponse.state:type_name -> portal.State
3, // 3: portal.Portal.ServiceRestart:input_type -> portal.ServiceRequest
3, // 4: portal.Portal.ServiceStart:input_type -> portal.ServiceRequest
3, // 5: portal.Portal.ServiceStop:input_type -> portal.ServiceRequest
3, // 6: portal.Portal.ServiceStatus:input_type -> portal.ServiceRequest
1, // 7: portal.Portal.RunCommand:input_type -> portal.CommandRequest
6, // 8: portal.Portal.CPUusage:input_type -> portal.CPUusageRequest
4, // 9: portal.Portal.ServiceRestart:output_type -> portal.ServiceResponse
4, // 10: portal.Portal.ServiceStart:output_type -> portal.ServiceResponse
4, // 11: portal.Portal.ServiceStop:output_type -> portal.ServiceResponse
5, // 12: portal.Portal.ServiceStatus:output_type -> portal.ServiceStatusResponse
2, // 13: portal.Portal.RunCommand:output_type -> portal.CommandResponse
7, // 14: portal.Portal.CPUusage:output_type -> portal.CPUusageResponse
9, // [9:15] is the sub-list for method output_type
3, // [3:9] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
}
func init() { file_portal_portal_proto_init() }
func file_portal_portal_proto_init() {
if File_portal_portal_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_portal_portal_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CommandRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_portal_portal_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CommandResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_portal_portal_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ServiceRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_portal_portal_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ServiceResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_portal_portal_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ServiceStatusResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_portal_portal_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CPUusageRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_portal_portal_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CPUusageResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_portal_portal_proto_rawDesc,
NumEnums: 1,
NumMessages: 7,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_portal_portal_proto_goTypes,
DependencyIndexes: file_portal_portal_proto_depIdxs,
EnumInfos: file_portal_portal_proto_enumTypes,
MessageInfos: file_portal_portal_proto_msgTypes,
}.Build()
File_portal_portal_proto = out.File
file_portal_portal_proto_rawDesc = nil
file_portal_portal_proto_goTypes = nil
file_portal_portal_proto_depIdxs = nil
}

56
proto/portal/portal.proto Normal file
View File

@@ -0,0 +1,56 @@
syntax = "proto3";
option go_package = "github.com/speedrunsh/speedrun/proto/portal";
package portal;
enum State {
UNKNOWN = 0;
CHANGED = 1;
UNCHANGED = 2;
}
message CommandRequest {
string name = 1;
repeated string args = 2;
}
message CommandResponse {
State state = 1;
string message = 2;
}
message ServiceRequest {
string name = 1;
}
message ServiceResponse {
State state = 1;
string message = 2;
}
message ServiceStatusResponse {
State state = 1;
string loadstate = 2;
string activestate = 3;
string substate = 4;
}
message CPUusageRequest {}
message CPUusageResponse {
int32 loadavg1 = 1;
int32 loadavg5 = 2;
int32 loadavg15 = 3;
}
service Portal {
rpc ServiceRestart(ServiceRequest) returns (ServiceResponse) {}
rpc ServiceStart(ServiceRequest) returns (ServiceResponse) {}
rpc ServiceStop(ServiceRequest) returns (ServiceResponse) {}
rpc ServiceStatus(ServiceRequest) returns (ServiceStatusResponse) {}
rpc RunCommand(CommandRequest) returns (CommandResponse) {}
rpc CPUusage(CPUusageRequest) returns (CPUusageResponse) {
} // --target group1 --target group2
// rpc CPUProfile(CPUProfileRequest) returns (CPUProfileResponse) {}
// rpc MemProfile(MemProfileRequest) returns (MemProfileResponse) {}
// rpc EnsureMountedDisk()
}

View File

@@ -0,0 +1,311 @@
// Code generated by protoc-gen-go-drpc. DO NOT EDIT.
// protoc-gen-go-drpc version: v0.0.26
// source: portal/portal.proto
package portal
import (
context "context"
errors "errors"
protojson "google.golang.org/protobuf/encoding/protojson"
proto "google.golang.org/protobuf/proto"
drpc "storj.io/drpc"
drpcerr "storj.io/drpc/drpcerr"
)
type drpcEncoding_File_portal_portal_proto struct{}
func (drpcEncoding_File_portal_portal_proto) Marshal(msg drpc.Message) ([]byte, error) {
return proto.Marshal(msg.(proto.Message))
}
func (drpcEncoding_File_portal_portal_proto) MarshalAppend(buf []byte, msg drpc.Message) ([]byte, error) {
return proto.MarshalOptions{}.MarshalAppend(buf, msg.(proto.Message))
}
func (drpcEncoding_File_portal_portal_proto) Unmarshal(buf []byte, msg drpc.Message) error {
return proto.Unmarshal(buf, msg.(proto.Message))
}
func (drpcEncoding_File_portal_portal_proto) JSONMarshal(msg drpc.Message) ([]byte, error) {
return protojson.Marshal(msg.(proto.Message))
}
func (drpcEncoding_File_portal_portal_proto) JSONUnmarshal(buf []byte, msg drpc.Message) error {
return protojson.Unmarshal(buf, msg.(proto.Message))
}
type DRPCPortalClient interface {
DRPCConn() drpc.Conn
ServiceRestart(ctx context.Context, in *ServiceRequest) (*ServiceResponse, error)
ServiceStart(ctx context.Context, in *ServiceRequest) (*ServiceResponse, error)
ServiceStop(ctx context.Context, in *ServiceRequest) (*ServiceResponse, error)
ServiceStatus(ctx context.Context, in *ServiceRequest) (*ServiceStatusResponse, error)
RunCommand(ctx context.Context, in *CommandRequest) (*CommandResponse, error)
CPUusage(ctx context.Context, in *CPUusageRequest) (*CPUusageResponse, error)
}
type drpcPortalClient struct {
cc drpc.Conn
}
func NewDRPCPortalClient(cc drpc.Conn) DRPCPortalClient {
return &drpcPortalClient{cc}
}
func (c *drpcPortalClient) DRPCConn() drpc.Conn { return c.cc }
func (c *drpcPortalClient) ServiceRestart(ctx context.Context, in *ServiceRequest) (*ServiceResponse, error) {
out := new(ServiceResponse)
err := c.cc.Invoke(ctx, "/portal.Portal/ServiceRestart", drpcEncoding_File_portal_portal_proto{}, in, out)
if err != nil {
return nil, err
}
return out, nil
}
func (c *drpcPortalClient) ServiceStart(ctx context.Context, in *ServiceRequest) (*ServiceResponse, error) {
out := new(ServiceResponse)
err := c.cc.Invoke(ctx, "/portal.Portal/ServiceStart", drpcEncoding_File_portal_portal_proto{}, in, out)
if err != nil {
return nil, err
}
return out, nil
}
func (c *drpcPortalClient) ServiceStop(ctx context.Context, in *ServiceRequest) (*ServiceResponse, error) {
out := new(ServiceResponse)
err := c.cc.Invoke(ctx, "/portal.Portal/ServiceStop", drpcEncoding_File_portal_portal_proto{}, in, out)
if err != nil {
return nil, err
}
return out, nil
}
func (c *drpcPortalClient) ServiceStatus(ctx context.Context, in *ServiceRequest) (*ServiceStatusResponse, error) {
out := new(ServiceStatusResponse)
err := c.cc.Invoke(ctx, "/portal.Portal/ServiceStatus", drpcEncoding_File_portal_portal_proto{}, in, out)
if err != nil {
return nil, err
}
return out, nil
}
func (c *drpcPortalClient) RunCommand(ctx context.Context, in *CommandRequest) (*CommandResponse, error) {
out := new(CommandResponse)
err := c.cc.Invoke(ctx, "/portal.Portal/RunCommand", drpcEncoding_File_portal_portal_proto{}, in, out)
if err != nil {
return nil, err
}
return out, nil
}
func (c *drpcPortalClient) CPUusage(ctx context.Context, in *CPUusageRequest) (*CPUusageResponse, error) {
out := new(CPUusageResponse)
err := c.cc.Invoke(ctx, "/portal.Portal/CPUusage", drpcEncoding_File_portal_portal_proto{}, in, out)
if err != nil {
return nil, err
}
return out, nil
}
type DRPCPortalServer interface {
ServiceRestart(context.Context, *ServiceRequest) (*ServiceResponse, error)
ServiceStart(context.Context, *ServiceRequest) (*ServiceResponse, error)
ServiceStop(context.Context, *ServiceRequest) (*ServiceResponse, error)
ServiceStatus(context.Context, *ServiceRequest) (*ServiceStatusResponse, error)
RunCommand(context.Context, *CommandRequest) (*CommandResponse, error)
CPUusage(context.Context, *CPUusageRequest) (*CPUusageResponse, error)
}
type DRPCPortalUnimplementedServer struct{}
func (s *DRPCPortalUnimplementedServer) ServiceRestart(context.Context, *ServiceRequest) (*ServiceResponse, error) {
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
}
func (s *DRPCPortalUnimplementedServer) ServiceStart(context.Context, *ServiceRequest) (*ServiceResponse, error) {
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
}
func (s *DRPCPortalUnimplementedServer) ServiceStop(context.Context, *ServiceRequest) (*ServiceResponse, error) {
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
}
func (s *DRPCPortalUnimplementedServer) ServiceStatus(context.Context, *ServiceRequest) (*ServiceStatusResponse, error) {
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
}
func (s *DRPCPortalUnimplementedServer) RunCommand(context.Context, *CommandRequest) (*CommandResponse, error) {
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
}
func (s *DRPCPortalUnimplementedServer) CPUusage(context.Context, *CPUusageRequest) (*CPUusageResponse, error) {
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
}
type DRPCPortalDescription struct{}
func (DRPCPortalDescription) NumMethods() int { return 6 }
func (DRPCPortalDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver, interface{}, bool) {
switch n {
case 0:
return "/portal.Portal/ServiceRestart", drpcEncoding_File_portal_portal_proto{},
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
return srv.(DRPCPortalServer).
ServiceRestart(
ctx,
in1.(*ServiceRequest),
)
}, DRPCPortalServer.ServiceRestart, true
case 1:
return "/portal.Portal/ServiceStart", drpcEncoding_File_portal_portal_proto{},
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
return srv.(DRPCPortalServer).
ServiceStart(
ctx,
in1.(*ServiceRequest),
)
}, DRPCPortalServer.ServiceStart, true
case 2:
return "/portal.Portal/ServiceStop", drpcEncoding_File_portal_portal_proto{},
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
return srv.(DRPCPortalServer).
ServiceStop(
ctx,
in1.(*ServiceRequest),
)
}, DRPCPortalServer.ServiceStop, true
case 3:
return "/portal.Portal/ServiceStatus", drpcEncoding_File_portal_portal_proto{},
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
return srv.(DRPCPortalServer).
ServiceStatus(
ctx,
in1.(*ServiceRequest),
)
}, DRPCPortalServer.ServiceStatus, true
case 4:
return "/portal.Portal/RunCommand", drpcEncoding_File_portal_portal_proto{},
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
return srv.(DRPCPortalServer).
RunCommand(
ctx,
in1.(*CommandRequest),
)
}, DRPCPortalServer.RunCommand, true
case 5:
return "/portal.Portal/CPUusage", drpcEncoding_File_portal_portal_proto{},
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
return srv.(DRPCPortalServer).
CPUusage(
ctx,
in1.(*CPUusageRequest),
)
}, DRPCPortalServer.CPUusage, true
default:
return "", nil, nil, nil, false
}
}
func DRPCRegisterPortal(mux drpc.Mux, impl DRPCPortalServer) error {
return mux.Register(impl, DRPCPortalDescription{})
}
type DRPCPortal_ServiceRestartStream interface {
drpc.Stream
SendAndClose(*ServiceResponse) error
}
type drpcPortal_ServiceRestartStream struct {
drpc.Stream
}
func (x *drpcPortal_ServiceRestartStream) SendAndClose(m *ServiceResponse) error {
if err := x.MsgSend(m, drpcEncoding_File_portal_portal_proto{}); err != nil {
return err
}
return x.CloseSend()
}
type DRPCPortal_ServiceStartStream interface {
drpc.Stream
SendAndClose(*ServiceResponse) error
}
type drpcPortal_ServiceStartStream struct {
drpc.Stream
}
func (x *drpcPortal_ServiceStartStream) SendAndClose(m *ServiceResponse) error {
if err := x.MsgSend(m, drpcEncoding_File_portal_portal_proto{}); err != nil {
return err
}
return x.CloseSend()
}
type DRPCPortal_ServiceStopStream interface {
drpc.Stream
SendAndClose(*ServiceResponse) error
}
type drpcPortal_ServiceStopStream struct {
drpc.Stream
}
func (x *drpcPortal_ServiceStopStream) SendAndClose(m *ServiceResponse) error {
if err := x.MsgSend(m, drpcEncoding_File_portal_portal_proto{}); err != nil {
return err
}
return x.CloseSend()
}
type DRPCPortal_ServiceStatusStream interface {
drpc.Stream
SendAndClose(*ServiceStatusResponse) error
}
type drpcPortal_ServiceStatusStream struct {
drpc.Stream
}
func (x *drpcPortal_ServiceStatusStream) SendAndClose(m *ServiceStatusResponse) error {
if err := x.MsgSend(m, drpcEncoding_File_portal_portal_proto{}); err != nil {
return err
}
return x.CloseSend()
}
type DRPCPortal_RunCommandStream interface {
drpc.Stream
SendAndClose(*CommandResponse) error
}
type drpcPortal_RunCommandStream struct {
drpc.Stream
}
func (x *drpcPortal_RunCommandStream) SendAndClose(m *CommandResponse) error {
if err := x.MsgSend(m, drpcEncoding_File_portal_portal_proto{}); err != nil {
return err
}
return x.CloseSend()
}
type DRPCPortal_CPUusageStream interface {
drpc.Stream
SendAndClose(*CPUusageResponse) error
}
type drpcPortal_CPUusageStream struct {
drpc.Stream
}
func (x *drpcPortal_CPUusageStream) SendAndClose(m *CPUusageResponse) error {
if err := x.MsgSend(m, drpcEncoding_File_portal_portal_proto{}); err != nil {
return err
}
return x.CloseSend()
}

23
scripts/generate_fake_ca.sh Executable file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
set -e
openssl ecparam -name secp384r1 -genkey -noout -out ca.key
openssl req -new -x509 -key ca.key -out ca.crt -days 365 -config <(
cat <<-EOF
[req]
distinguished_name = req_distinguished_name
default_md = sha512
req_extensions = v3_req
prompt = no
[req_distinguished_name]
C = SE
O = Speedrun
CN = Fake CA
[v3_req]
subjectKeyIdentifier=hash
basicConstraints=critical,CA:TRUE
keyUsage=critical,keyCertSign,cRLSign
EOF
) -extensions v3_req

46
scripts/generate_portal_cert.sh Executable file
View File

@@ -0,0 +1,46 @@
#!/bin/bash
set -e
openssl ecparam -name secp384r1 -genkey -noout -out portal.key
openssl req -new -key portal.key -out portal.csr -config <(
cat <<-EOF
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
C = SE
O = Speedrun
CN = portal
[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
IP.1 = 34.74.21.99
EOF
)
openssl x509 -req -in portal.csr -CA ca.crt -CAkey ca.key -out portal.crt -days 365 -sha512 -CAcreateserial -extensions v3_req -extfile <(
cat <<-EOF
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
C = SE
O = Speedrun
CN = portal
[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
IP.1 = 34.74.21.99
EOF
)
rm portal.csr

View File

@@ -0,0 +1,40 @@
#!/bin/bash
set -e
openssl ecparam -name secp384r1 -genkey -noout -out speedrun.key
openssl req -new -key speedrun.key -out speedrun.csr -config <(
cat <<-EOF
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
C = SE
O = Speedrun
CN = speedrun
[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = clientAuth
EOF
)
openssl x509 -req -in speedrun.csr -CA ca.crt -CAkey ca.key -out speedrun.crt -days 365 -sha512 -CAcreateserial -extensions v3_req -extfile <(
cat <<-EOF
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
C = SE
O = Speedrun
CN = speedrun
[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = clientAuth
EOF
)
rm speedrun.csr