-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Clusters: Add command to show cluster details * Clusters: Add clusters flag to create and delete commands * Clusters: Implement delete cluster * Clusters: Implement create cluster * Clusters: Add non-interactive cluster creation logic * Clusters: Add non-interactive cluster creation logic * Clusters: Vendor dependency survey * Cluster delete: change the basic usage text * Clusters endpoint: Add docs * Modify according to the current API changes * clusters: Multiple fixes * Update according to new pricing plan * Separate provider depenedent options into different sets * Display error message in case of cluster failure * Remove deprecated vm_size options Removes `g1-small` and `n1-standard-1` * Add plan VM mappings * Update docs * cluster_details: Annotate response message with error * Remove depracted addons field
- Loading branch information
Showing
806 changed files
with
356,304 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
package cluster | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"strconv" | ||
"strings" | ||
"time" | ||
|
||
"github.com/AlecAivazis/survey" | ||
"github.com/appbaseio/abc/appbase/common" | ||
"github.com/appbaseio/abc/appbase/session" | ||
"github.com/appbaseio/abc/appbase/spinner" | ||
) | ||
|
||
type status struct { | ||
Message string `json:"message"` | ||
Code int `json:"code"` | ||
} | ||
|
||
type cluster struct { | ||
Name string `json:"name"` | ||
ID string `json:"id"` | ||
Status string `json:"status"` | ||
CreatedAt time.Time `json:"created_at"` | ||
Message string `json:"message"` | ||
Provider string `json:"provider"` | ||
} | ||
|
||
type createClusterRespBody struct { | ||
Status status `json:"status"` | ||
Cluster cluster `json:"cluster"` | ||
} | ||
|
||
func decodeResp(resp *http.Response, res *createClusterRespBody) error { | ||
dec := json.NewDecoder(resp.Body) | ||
err := dec.Decode(&res) | ||
if err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
// BuildRequestBody creates a request body to for cluster deployment based on input from flags | ||
func BuildRequestBody(name string, location string, vmSize string, plan string, ssh string, provider string, nodes int, esVersion string, volumeSize int) string { | ||
esBody := "\"elasticsearch\": {\n \"nodes\": " + strconv.Itoa(nodes) + ",\n \"version\": \"" + esVersion + "\",\n \"volume_size\": " + strconv.Itoa(volumeSize) + "\n }" | ||
clusterBody := "\"cluster\": {\n \"name\": \"" + name + "\",\n \"location\": \"" + location + "\",\n \"vm_size\": \"" + vmSize + "\",\n \"ssh_public_key\": \"" + ssh + "\",\n \"pricing_plan\": \"" + plan + "\",\n \"provider\": \"" + provider + "\"\n }" | ||
return "{\n " + esBody + ",\n " + clusterBody + "\n}" | ||
} | ||
|
||
var additionalChoices = []*survey.Question{ | ||
{ | ||
Name: "logstash", | ||
Prompt: &survey.Confirm{Message: "Would you like to provide Logstash options to your cluster deployment?"}, | ||
Validate: survey.Required, | ||
}, | ||
{ | ||
Name: "kibana", | ||
Prompt: &survey.Confirm{Message: "Would you like to provide Kibana options to your cluster deployment?"}, | ||
Validate: survey.Required, | ||
}, | ||
{ | ||
Name: "addons", | ||
Prompt: &survey.Select{ | ||
Message: "How many add-ons would you like to add to your cluster deployment?", | ||
Help: "The following adddons are supported currently: Mirage, DejaVu, Elasticsearch-HQ", | ||
Options: []string{"0", "1", "2", "3"}, | ||
}, | ||
Validate: survey.Required, | ||
}, | ||
} | ||
|
||
// BuildRequestBodyInteractive asks the user questions based on which it constructs the | ||
// request body string to deploy the cluster. | ||
func BuildRequestBodyInteractive() string { | ||
answers := make(map[string]interface{}) | ||
|
||
err := survey.Ask(additionalChoices, &answers) | ||
if err != nil { | ||
fmt.Println(err.Error()) | ||
} | ||
|
||
clustersString, plan := buildClusterObjectString() | ||
respBodyString := "{\n " + buildESObjectString(plan) + " " + clustersString | ||
|
||
if answers["logstash"] == true { | ||
respBodyString = respBodyString + buildLogstashObjectString() | ||
} | ||
if answers["kibana"] == true { | ||
respBodyString = respBodyString + buildKibanaObjectString() | ||
} | ||
if answers["addons"] != "0" { | ||
num, _ := strconv.Atoi(answers["addons"].(string)) | ||
respBodyString = respBodyString + buildAddonsObjectString(num) | ||
} | ||
|
||
idx := strings.LastIndex(respBodyString, ",") | ||
return respBodyString[:idx] + respBodyString[idx+1:] + "}" | ||
} | ||
|
||
// DeployCluster creates a cluster in interactive mode by asking the user | ||
// for the deployment details. | ||
func DeployCluster(body string) error { | ||
payload := strings.NewReader(body) | ||
spinner.Start() | ||
defer spinner.Stop() | ||
|
||
req, err := http.NewRequest("POST", common.AccAPIURL+"/v1/_deploy", payload) | ||
if err != nil { | ||
return err | ||
} | ||
req.Header.Add("Content-Type", "application/json") | ||
|
||
resp, err := session.SendRequest(req) | ||
if err != nil { | ||
return err | ||
} | ||
spinner.Stop() | ||
|
||
if resp.StatusCode != 202 { | ||
defer resp.Body.Close() | ||
var res createClusterRespBody | ||
_ = decodeResp(resp, &res) | ||
return fmt.Errorf("There was an error %s", res.Status.Message) | ||
} | ||
|
||
var res createClusterRespBody | ||
err = decodeResp(resp, &res) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// output | ||
fmt.Printf("ID: %s\n", res.Cluster.ID) | ||
fmt.Printf("Name: %s\n", res.Cluster.Name) | ||
fmt.Printf("Status: %s\n", res.Cluster.Status) | ||
fmt.Printf("Provider: %s\n", res.Cluster.Provider) | ||
fmt.Printf("Created at: %s\n", res.Cluster.CreatedAt) | ||
fmt.Printf("Message: %s\n", res.Cluster.Message) | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package cluster | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
|
||
"github.com/appbaseio/abc/appbase/common" | ||
"github.com/appbaseio/abc/appbase/session" | ||
"github.com/appbaseio/abc/appbase/spinner" | ||
) | ||
|
||
type deleteRespBody struct { | ||
Status statusDetailsBody `json:"status"` | ||
Deployment string `json:"deployment"` | ||
} | ||
|
||
// RunClusterDelete deletes a cluster using the cluster ID | ||
func RunClusterDelete(clusterID string) error { | ||
spinner.Start() | ||
defer spinner.Stop() | ||
|
||
req, err := http.NewRequest("DELETE", common.AccAPIURL+"/v1/_delete/"+clusterID, nil) | ||
if err != nil { | ||
return err | ||
} | ||
resp, err := session.SendRequest(req) | ||
if err != nil { | ||
return err | ||
} | ||
spinner.Stop() | ||
// status code not 200 | ||
if resp.StatusCode != 200 { | ||
defer resp.Body.Close() | ||
bodyBytes, _ := ioutil.ReadAll(resp.Body) | ||
return fmt.Errorf("There was an error %s", string(bodyBytes)) | ||
} | ||
// decode | ||
var res deleteRespBody | ||
dec := json.NewDecoder(resp.Body) | ||
err = dec.Decode(&res) | ||
if err != nil { | ||
return err | ||
} | ||
// output | ||
fmt.Println(res.Status.Message) | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package cluster | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"net/http" | ||
"strconv" | ||
"time" | ||
|
||
"github.com/appbaseio/abc/appbase/common" | ||
"github.com/appbaseio/abc/appbase/session" | ||
"github.com/appbaseio/abc/appbase/spinner" | ||
) | ||
|
||
type clusterDetailsBody struct { | ||
Name string `json:"name"` | ||
ID string `json:"id"` | ||
Status string `json:"status"` | ||
CreatedAt time.Time `json:"created_at"` | ||
PricingPlan string `json:"pricing_plan"` | ||
Region string `json:"region"` | ||
EsVersion string `json:"es_version"` | ||
TotalNodes int `json:"total_nodes"` | ||
DashboardURL string `json:"dashboard_url"` | ||
DashboardUsername string `json:"dashboard_username"` | ||
DashboardPassword string `json:"dashboard_password"` | ||
DashboardHTTPS bool `json:"dashboard_https"` | ||
} | ||
|
||
type kibanaDetailsBody struct{} | ||
|
||
type logstashDetailsBody struct{} | ||
|
||
type esDetailsBody struct { | ||
Name string `json:"name"` | ||
RequiredNodes int `json:"required_nodes"` | ||
ReadyNodes int `json:"ready_nodes"` | ||
Status string `json:"status"` | ||
Username string `json:"username"` | ||
Password string `json:"password"` | ||
URL string `json:"url"` | ||
HTTPS bool `json:"https"` | ||
} | ||
|
||
type deploymentDetailsBody struct { | ||
Elasticsearch esDetailsBody `json:"elasticsearch"` | ||
Logstash logstashDetailsBody `json:"logstash"` | ||
Kibana kibanaDetailsBody `json:"kibana"` | ||
} | ||
|
||
type statusDetailsBody struct { | ||
Message string `json:"message"` | ||
Code int `json:"code"` | ||
} | ||
|
||
type clusterRespBody struct { | ||
Status statusDetailsBody `json:"status"` | ||
Deployment deploymentDetailsBody `json:"deployment"` | ||
Cluster clusterDetailsBody `json:"cluster"` | ||
} | ||
|
||
// ShowClusterDetails shows the cluster details | ||
func ShowClusterDetails(cluster string) error { | ||
spinner.StartText("Loading cluster details") | ||
defer spinner.Stop() | ||
|
||
// get cluster details | ||
req, err := http.NewRequest("GET", common.AccAPIURL+"/v1/_status/"+cluster, nil) | ||
if err != nil { | ||
return err | ||
} | ||
resp, err := session.SendRequest(req) | ||
if err != nil { | ||
return err | ||
} | ||
spinner.Stop() | ||
// decode response | ||
var res clusterRespBody | ||
dec := json.NewDecoder(resp.Body) | ||
err = dec.Decode(&res) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if res.Status.Code != 200 { | ||
errMessage := fmt.Sprintf("Could not fetch cluster details: [error: %s %d]", res.Status.Message, res.Status.Code) | ||
return errors.New(errMessage) | ||
} | ||
|
||
fmt.Println("Cluster Details") | ||
fmt.Println("--------------------------------------------------------") | ||
fmt.Printf("Name: %s\n", res.Cluster.Name) | ||
fmt.Printf("ID: %s\n", res.Cluster.ID) | ||
fmt.Printf("Status: %s\n", res.Cluster.Status) | ||
fmt.Printf("Created at: %s\n", res.Cluster.CreatedAt) | ||
fmt.Printf("Pricing Plan: %s\n", res.Cluster.PricingPlan) | ||
fmt.Printf("Region: %s\n", res.Cluster.Region) | ||
fmt.Printf("ES Version: %s\n", res.Cluster.EsVersion) | ||
fmt.Printf("Total Nodes: %d\n", res.Cluster.TotalNodes) | ||
fmt.Printf("Dashboard URL: %s\n", res.Cluster.DashboardURL) | ||
fmt.Printf("Dashboard Username: %s\n", res.Cluster.DashboardUsername) | ||
fmt.Printf("Dashboard Password: %s\n", res.Cluster.DashboardPassword) | ||
fmt.Printf("Dashboard HTTPS: %s\n", strconv.FormatBool(res.Cluster.DashboardHTTPS)) | ||
|
||
fmt.Println() | ||
|
||
fmt.Println("Deployment Details") | ||
fmt.Println("--------------------------------------------------------") | ||
fmt.Println("ElasticSearch:") | ||
|
||
fmt.Printf("ID: %s\n", cluster) | ||
fmt.Printf("Name: %s\n", res.Deployment.Elasticsearch.Name) | ||
fmt.Printf("Required Nodes: %d\n", res.Deployment.Elasticsearch.RequiredNodes) | ||
fmt.Printf("Ready Nodes: %d\n", res.Deployment.Elasticsearch.ReadyNodes) | ||
fmt.Printf("Status: %s\n", res.Deployment.Elasticsearch.Status) | ||
fmt.Printf("Username: %s\n", res.Deployment.Elasticsearch.Username) | ||
fmt.Printf("Password: %s\n", res.Deployment.Elasticsearch.Password) | ||
fmt.Printf("URL: %s\n", res.Deployment.Elasticsearch.URL) | ||
fmt.Printf("HTTPS: %s\n", strconv.FormatBool(res.Deployment.Elasticsearch.HTTPS)) | ||
|
||
return nil | ||
} |
Oops, something went wrong.