From 1b64e461c2dffb77a289f73d52e28d3917bf21e7 Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Thu, 7 Jul 2016 16:46:31 +0100 Subject: [PATCH] Add Weave Cloud client --- cmd/wcloud/cli.go | 178 +++++++++++++++++++++++++++++++++++++++++++ cmd/wcloud/client.go | 131 +++++++++++++++++++++++++++++++ cmd/wcloud/types.go | 24 ++++++ 3 files changed, 333 insertions(+) create mode 100644 cmd/wcloud/cli.go create mode 100644 cmd/wcloud/client.go create mode 100644 cmd/wcloud/types.go diff --git a/cmd/wcloud/cli.go b/cmd/wcloud/cli.go new file mode 100644 index 0000000000..e9dd1aa399 --- /dev/null +++ b/cmd/wcloud/cli.go @@ -0,0 +1,178 @@ +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" + + "github.com/olekukonko/tablewriter" + "gopkg.in/yaml.v2" +) + +func env(key, def string) string { + if val, ok := os.LookupEnv(key); ok { + return val + } + return def +} + +var ( + token = env("SERVICE_TOKEN", "") + baseURL = env("BASE_URL", "https://cloud.weave.works") +) + +func usage() { + fmt.Println(`Usage: + deploy : Deploy image to your configured env + list List recent deployments + config () Get (or set) the configured env + logs Show lots for the given deployment`) +} + +func main() { + if len(os.Args) <= 1 { + usage() + os.Exit(1) + } + + c := NewClient(token, baseURL) + + switch os.Args[1] { + case "deploy": + deploy(c, os.Args[2:]) + case "list": + list(c, os.Args[2:]) + case "config": + config(c, os.Args[2:]) + case "logs": + logs(c, os.Args[2:]) + case "help": + usage() + default: + usage() + } +} + +func deploy(c Client, args []string) { + if len(args) != 1 { + usage() + return + } + parts := strings.SplitN(args[0], ":", 2) + if len(parts) < 2 { + usage() + return + } + deployment := Deployment{ + ImageName: parts[0], + Version: parts[1], + } + if err := c.Deploy(deployment); err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } +} + +func list(c Client, args []string) { + flags := flag.NewFlagSet("list", flag.ContinueOnError) + page := flags.Int("page", 0, "Zero based index of page to list.") + pagesize := flags.Int("page-size", 10, "Number of results per page") + if err := flags.Parse(args); err != nil { + usage() + return + } + deployments, err := c.GetDeployments(*page, *pagesize) + if err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"Created", "ID", "Image", "Version", "State"}) + table.SetBorder(false) + table.SetColumnSeparator(" ") + for _, deployment := range deployments { + table.Append([]string{ + deployment.CreatedAt.Format(time.RFC822), + deployment.ID, + deployment.ImageName, + deployment.Version, + deployment.State, + }) + } + table.Render() +} + +func loadConfig(filename string) (*Config, error) { + extension := filepath.Ext(filename) + var config Config + buf, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + if extension == ".yaml" || extension == ".yml" { + if err := yaml.Unmarshal(buf, &config); err != nil { + return nil, err + } + } else { + if err := json.NewDecoder(bytes.NewReader(buf)).Decode(&config); err != nil { + return nil, err + } + } + return &config, nil +} + +func config(c Client, args []string) { + if len(args) > 1 { + usage() + return + } + + if len(args) == 1 { + config, err := loadConfig(args[0]) + if err != nil { + fmt.Println("Error reading config:", err) + os.Exit(1) + } + + if err := c.SetConfig(config); err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + } else { + config, err := c.GetConfig() + if err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + + buf, err := yaml.Marshal(config) + if err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + + fmt.Println(string(buf)) + } +} + +func logs(c Client, args []string) { + if len(args) != 1 { + usage() + return + } + + output, err := c.GetLogs(args[0]) + if err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + + fmt.Println(string(output)) +} diff --git a/cmd/wcloud/client.go b/cmd/wcloud/client.go new file mode 100644 index 0000000000..7f21b817e6 --- /dev/null +++ b/cmd/wcloud/client.go @@ -0,0 +1,131 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" +) + +// Client for the deployment service +type Client struct { + token string + baseURL string +} + +// NewClient makes a new Client +func NewClient(token, baseURL string) Client { + return Client{ + token: token, + baseURL: baseURL, + } +} + +func (c Client) newRequest(method, path string, body io.Reader) (*http.Request, error) { + req, err := http.NewRequest(method, c.baseURL+path, body) + if err != nil { + return nil, err + } + req.Header.Add("Authorization", fmt.Sprintf("Scope-Probe token=%s", c.token)) + return req, nil +} + +// Deploy notifies the deployment service about a new deployment +func (c Client) Deploy(deployment Deployment) error { + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(deployment); err != nil { + return err + } + req, err := c.newRequest("POST", "/api/deploy", &buf) + if err != nil { + return err + } + res, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + if res.StatusCode != 204 { + return fmt.Errorf("Error making request: %s", res.Status) + } + return nil +} + +// GetDeployments returns a list of deployments +func (c Client) GetDeployments(page, pagesize int) ([]Deployment, error) { + req, err := c.newRequest("GET", fmt.Sprintf("/api/deploy?page=%d&pagesize=%d", page, pagesize), nil) + if err != nil { + return nil, err + } + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + if res.StatusCode != 200 { + return nil, fmt.Errorf("Error making request: %s", res.Status) + } + var response struct { + Deployments []Deployment `json:"deployments"` + } + if err := json.NewDecoder(res.Body).Decode(&response); err != nil { + return nil, err + } + return response.Deployments, nil +} + +// GetConfig returns the current Config +func (c Client) GetConfig() (*Config, error) { + req, err := c.newRequest("GET", "/api/config/deploy", nil) + if err != nil { + return nil, err + } + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + if res.StatusCode != 200 { + return nil, fmt.Errorf("Error making request: %s", res.Status) + } + var config Config + if err := json.NewDecoder(res.Body).Decode(&config); err != nil { + return nil, err + } + return &config, nil +} + +// SetConfig sets the current Config +func (c Client) SetConfig(config *Config) error { + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(config); err != nil { + return err + } + req, err := c.newRequest("POST", "/api/config/deploy", &buf) + if err != nil { + return err + } + res, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + if res.StatusCode != 204 { + return fmt.Errorf("Error making request: %s", res.Status) + } + return nil +} + +// GetLogs returns the logs for a given deployment. +func (c Client) GetLogs(deployID string) ([]byte, error) { + req, err := c.newRequest("GET", fmt.Sprintf("/api/deploy/%s/log", deployID), nil) + if err != nil { + return nil, err + } + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + if res.StatusCode != 200 { + return nil, fmt.Errorf("Error making request: %s", res.Status) + } + return ioutil.ReadAll(res.Body) +} diff --git a/cmd/wcloud/types.go b/cmd/wcloud/types.go new file mode 100644 index 0000000000..16e6b2622c --- /dev/null +++ b/cmd/wcloud/types.go @@ -0,0 +1,24 @@ +package main + +import ( + "time" +) + +// Deployment describes a deployment +type Deployment struct { + ID string `json:"id"` + CreatedAt time.Time `json:"created_at"` + ImageName string `json:"image_name"` + Version string `json:"version"` + Priority int `json:"priority"` + State string `json:"status"` + LogKey string `json:"-"` +} + +// Config for the deployment system for a user. +type Config struct { + RepoURL string `json:"repo_url" yaml:"repo_url"` + RepoPath string `json:"repo_path" yaml:"repo_path"` + RepoKey string `json:"repo_key" yaml:"repo_key"` + KubeconfigPath string `json:"kubeconfig_path" yaml:"kubeconfig_path"` +}