-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0ca076f
commit 5116327
Showing
11 changed files
with
1,072 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
// Use IntelliSense to learn about possible attributes. | ||
// Hover to view descriptions of existing attributes. | ||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||
"version": "0.2.0", | ||
"configurations": [ | ||
{ | ||
"name": "Launch Package", | ||
"type": "go", | ||
"request": "launch", | ||
"mode": "auto", | ||
"program": "${workspaceFolder}/main.go", | ||
"args": [ "credentials", "--apiKey", "rec8DNOFisd19B944", "--apiToken", "edBfHBhyDsUzeTnJLjAHhXvwqUAFjU" ] | ||
} | ||
] | ||
} |
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,48 @@ | ||
# tensordock-cli | ||
|
||
A CLI client for https://tensordock.com | ||
|
||
## Installation | ||
|
||
Grab a build from [Releases](releases) | ||
|
||
## Building | ||
|
||
``` | ||
go build | ||
``` | ||
|
||
## Usage | ||
|
||
### Configuration | ||
|
||
|
||
``` | ||
$ tensordock-cli config --apiKey <YOUR_API_KEY> --apiToken <YOUR_API_TOKEN> [--serviceUrl <SERVICE_URL>] | ||
``` | ||
|
||
Note: Go to https://console.tensordock.com/api to get your API key and token | ||
|
||
### List Servers | ||
|
||
```sh | ||
$ tensordock-cli servers list | ||
``` | ||
|
||
### Get server info | ||
|
||
```sh | ||
$ tensordock-cli servers info --server <serverId> | ||
``` | ||
|
||
### Start/Stop Server | ||
|
||
```sh | ||
$ tensordock-cli servers <start|stop> --server <serverId> | ||
``` | ||
|
||
### Get billing info | ||
|
||
```sh | ||
$ tensordock-cli billing | ||
``` |
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 api | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
"net/url" | ||
"strings" | ||
) | ||
|
||
type Response struct { | ||
Success bool `json:"success"` | ||
} | ||
|
||
type ServersGetSingleResponse struct { | ||
Response | ||
Server Server `json:"server"` | ||
} | ||
|
||
type ServersListResponse struct { | ||
Response | ||
Servers map[string]Server `json:"servers"` | ||
} | ||
|
||
type BillingDetailsResponse struct { | ||
Response | ||
BillingDetails | ||
} | ||
|
||
type Client struct { | ||
BaseUrl string | ||
ApiKey string | ||
ApiToken string | ||
} | ||
|
||
func (client *Client) get(path string, params map[string]string) (*json.RawMessage, error) { | ||
query := url.Values{} | ||
query.Add("api_key", client.ApiKey) | ||
query.Add("api_token", client.ApiToken) | ||
for key, elem := range params { | ||
query.Add(key, elem) | ||
} | ||
|
||
url := fmt.Sprintf("%v/%v?%v", client.BaseUrl, path, query.Encode()) | ||
req, err := http.NewRequest("GET", url, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
res, err := http.DefaultClient.Do(req) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer res.Body.Close() | ||
|
||
bytes, _ := ioutil.ReadAll(res.Body) | ||
if res.StatusCode == 200 && strings.HasPrefix(res.Header.Get("Content-Type"), "text/html") { | ||
bytes, err := json.Marshal(Response{Success: false}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
msg := json.RawMessage(bytes) | ||
return &msg, nil | ||
} | ||
|
||
msg := json.RawMessage(bytes) | ||
return &msg, nil | ||
} | ||
|
||
func (client *Client) ListServers() (*ServersListResponse, error) { | ||
raw, err := client.get("list", nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var res ServersListResponse | ||
if err := json.Unmarshal(*raw, &res); err != nil { | ||
return nil, err | ||
} | ||
|
||
return &res, nil | ||
} | ||
|
||
func (client *Client) StopServer(server string) (*Response, error) { | ||
raw, err := client.get("stop/single", map[string]string{"server": server}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var res Response | ||
if err := json.Unmarshal(*raw, &res); err != nil { | ||
return nil, err | ||
} | ||
|
||
return &res, nil | ||
} | ||
|
||
func (client *Client) StartServer(server string) (*Response, error) { | ||
raw, err := client.get("start/single", map[string]string{"server": server}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var res Response | ||
if err := json.Unmarshal(*raw, &res); err != nil { | ||
return nil, err | ||
} | ||
|
||
return &res, nil | ||
} | ||
|
||
func (client *Client) GetServer(server string) (*ServersGetSingleResponse, error) { | ||
raw, err := client.get("get/single", map[string]string{"server": server}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var res ServersGetSingleResponse | ||
if err := json.Unmarshal(*raw, &res); err != nil { | ||
return nil, err | ||
} | ||
|
||
return &res, nil | ||
} | ||
|
||
func (client *Client) GetBillingDetails() (*BillingDetailsResponse, error) { | ||
raw, err := client.get("billing", nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var res BillingDetailsResponse | ||
if err := json.Unmarshal(*raw, &res); err != nil { | ||
return nil, err | ||
} | ||
|
||
return &res, nil | ||
} | ||
|
||
func NewClient(baseUrl string, apiKey string, apiToken string) *Client { | ||
return &Client{baseUrl, apiKey, apiToken} | ||
} |
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,32 @@ | ||
package api | ||
|
||
type Cost struct { | ||
Charged float32 `json:"charged"` | ||
HourOff float32 `json:"hour_off"` | ||
HourOn float32 `json:"hour_on"` | ||
MinutesOff float32 `json:"minutes_off"` | ||
MinutesOn float32 `json:"minutes_on"` | ||
} | ||
|
||
type Server struct { | ||
Cost Cost `json:"cost"` | ||
CPUModel string `json:"cpu_model"` | ||
GPUCount int `json:"gpu_count"` | ||
GPUModel string `json:"gpu_model"` | ||
Id string `json:"id"` | ||
Ip string `json:"ip"` | ||
Links map[string]map[string]string `json:"links"` | ||
Location string `json:"location"` | ||
Name string `json:"name"` | ||
Ram int `json:"ram"` | ||
Status string `json:"status"` | ||
Storage int `json:"storage"` | ||
StorageClass string `json:"storage_class"` | ||
Type string `json:"type"` | ||
VCPUs int `json:"vcpus"` | ||
} | ||
|
||
type BillingDetails struct { | ||
Balance float32 `json:"balance"` | ||
HourlySpendingRate float32 `json:"hourly_spending_rate"` | ||
} |
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,36 @@ | ||
package commands | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
"github.com/spf13/cobra" | ||
) | ||
|
||
var ( | ||
billingCmd = &cobra.Command{ | ||
Use: "billing", | ||
Short: "Manage billing", | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
res, err := client.GetBillingDetails() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if !res.Success { | ||
log.Printf("api call failed") | ||
return nil | ||
} | ||
|
||
fmt.Printf(`Balance: %v | ||
Hourly Spending Rate: %v`, | ||
res.Balance, | ||
res.HourlySpendingRate) | ||
return nil | ||
}, | ||
} | ||
) | ||
|
||
func init() { | ||
rootCmd.AddCommand(billingCmd) | ||
} |
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,44 @@ | ||
package commands | ||
|
||
import ( | ||
"log" | ||
|
||
"github.com/spf13/cobra" | ||
"github.com/spf13/viper" | ||
) | ||
|
||
var ( | ||
configCmd = &cobra.Command{ | ||
Use: "config", | ||
Short: "Set API token/key", | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
apiKey, err := cmd.Flags().GetString("apiKey") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
apiToken, err := cmd.Flags().GetString("apiToken") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
viper.Set("apiKey", apiKey) | ||
viper.Set("apiToken", apiToken) | ||
viper.WriteConfig() | ||
|
||
return nil | ||
}, | ||
PostRun: func(cmd *cobra.Command, args []string) { | ||
log.Print("config updated") | ||
}, | ||
} | ||
) | ||
|
||
func init() { | ||
configCmd.Flags().String("apiKey", "", "API Key") | ||
configCmd.MarkFlagRequired("apiKey") | ||
configCmd.Flags().String("apiToken", "", "API Token") | ||
configCmd.MarkFlagRequired("apiToken") | ||
configCmd.Flags().String("serviceUrl", "https://console.tensordock.com/api", "Service URL") | ||
rootCmd.AddCommand(configCmd) | ||
} |
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,67 @@ | ||
package commands | ||
|
||
import ( | ||
"log" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/caguiclajmg/tensordock-cli/api" | ||
"github.com/spf13/cobra" | ||
"github.com/spf13/viper" | ||
) | ||
|
||
var ( | ||
cfgFile string | ||
client *api.Client | ||
|
||
rootCmd = &cobra.Command{ | ||
Use: "tensordock-cli", | ||
Short: "A brief description of your application", | ||
SilenceUsage: true, | ||
} | ||
) | ||
|
||
func Execute() { | ||
err := rootCmd.Execute() | ||
if err != nil { | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
func init() { | ||
cobra.OnInitialize(initConfig) | ||
|
||
pflags := rootCmd.PersistentFlags() | ||
pflags.StringVar(&cfgFile, "config", "", "config file (default is $HOME/.tensordock.yml)") | ||
pflags.String("apiKey", "", "API key") | ||
pflags.String("apiToken", "", "API token") | ||
|
||
viper.BindPFlag("apiKey", pflags.Lookup("apiKey")) | ||
viper.BindPFlag("apiToken", pflags.Lookup("apiToken")) | ||
} | ||
|
||
func initConfig() { | ||
if cfgFile != "" { | ||
viper.SetConfigFile(cfgFile) | ||
} else { | ||
home, err := os.UserHomeDir() | ||
cobra.CheckErr(err) | ||
|
||
viper.SetConfigFile(filepath.Join(home, ".tensordock.yml")) | ||
} | ||
|
||
viper.SetDefault("serviceUrl", "https://console.tensordock.com/api") | ||
|
||
err := viper.ReadInConfig() | ||
if err != nil { | ||
log.Fatalf("error: %v", err) | ||
} | ||
|
||
viper.AutomaticEnv() | ||
|
||
serviceUrl := viper.GetString("serviceUrl") | ||
apiKey := viper.GetString("apiKey") | ||
apiToken := viper.GetString("apiToken") | ||
|
||
client = api.NewClient(serviceUrl, apiKey, apiToken) | ||
} |
Oops, something went wrong.