You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2018/04/16 02:08:11 UTC
[cloudstack-cloudmonkey] branch master updated: client: implement
login based API calls, simplify config
This is an automated email from the ASF dual-hosted git repository.
rohit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack-cloudmonkey.git
The following commit(s) were added to refs/heads/master by this push:
new d0b58be client: implement login based API calls, simplify config
d0b58be is described below
commit d0b58bee3a7d223c2b521c870c741c0183c65022
Author: Rohit Yadav <ro...@apache.org>
AuthorDate: Mon Apr 16 07:37:41 2018 +0530
client: implement login based API calls, simplify config
Signed-off-by: Rohit Yadav <ro...@apache.org>
---
cmd/api.go | 6 +++-
cmd/login.go | 27 +++++++++++++++---
cmd/network.go | 87 +++++++++++++++++++++++++++++++++++++++++++++++---------
config/config.go | 67 ++++++++++++++++++++++---------------------
4 files changed, 136 insertions(+), 51 deletions(-)
diff --git a/cmd/api.go b/cmd/api.go
index dd4400a..47a0f0f 100644
--- a/cmd/api.go
+++ b/cmd/api.go
@@ -77,7 +77,11 @@ func init() {
return nil
}
- b, _ := NewAPIRequest(r, api.Name, apiArgs)
+ b, err := NewAPIRequest(r, api.Name, apiArgs)
+ if err != nil {
+ return err
+ }
+
response, _ := json.MarshalIndent(b, "", " ")
// Implement various output formats
diff --git a/cmd/login.go b/cmd/login.go
index a2ace48..36b54e8 100644
--- a/cmd/login.go
+++ b/cmd/login.go
@@ -40,33 +40,52 @@ func init() {
return nil
}
+ // username
prompt := promptui.Prompt{
Label: "Username",
Validate: validate,
Default: "",
}
-
username, err := prompt.Run()
if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return nil
}
+ //password
prompt = promptui.Prompt{
Label: "Password",
Validate: validate,
Mask: '*',
}
-
password, err := prompt.Run()
+ if err != nil {
+ fmt.Printf("Prompt failed %v\n", err)
+ return nil
+ }
+ // domain
+ prompt = promptui.Prompt{
+ Label: "Domain",
+ Validate: validate,
+ Default: "/",
+ }
+ domain, err := prompt.Run()
if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return nil
}
- // TODO: implement login based key setup workflow
- fmt.Println("Trying to log in using", username, password)
+ r.Config.ActiveProfile.Username = username
+ r.Config.ActiveProfile.Password = password
+ r.Config.ActiveProfile.Domain = domain
+
+ client, _, err := Login(r)
+ if client == nil || err != nil {
+ fmt.Println("Failed to login, check credentials")
+ } else {
+ fmt.Println("Success!")
+ }
return nil
},
diff --git a/cmd/network.go b/cmd/network.go
index d40b7bd..1a57494 100644
--- a/cmd/network.go
+++ b/cmd/network.go
@@ -21,15 +21,18 @@ import (
"bytes"
"crypto/hmac"
"crypto/sha1"
+ "crypto/tls"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
+ "net/http/cookiejar"
"net/url"
"sort"
"strings"
+ "time"
)
func encodeRequestParams(params url.Values) string {
@@ -56,6 +59,42 @@ func encodeRequestParams(params url.Values) string {
return buf.String()
}
+// Login logs in a user based on provided request and returns http client and session key
+func Login(r *Request) (*http.Client, string, error) {
+ params := make(url.Values)
+ params.Add("command", "login")
+ params.Add("username", r.Config.ActiveProfile.Username)
+ params.Add("password", r.Config.ActiveProfile.Password)
+ params.Add("domain", r.Config.ActiveProfile.Domain)
+ params.Add("response", "json")
+
+ jar, _ := cookiejar.New(nil)
+ client := &http.Client{
+ Jar: jar,
+ Transport: &http.Transport{
+ TLSClientConfig: &tls.Config{InsecureSkipVerify: !r.Config.ActiveProfile.VerifyCert},
+ },
+ }
+
+ sessionKey := ""
+ resp, err := client.PostForm(r.Config.ActiveProfile.URL, params)
+ if resp.StatusCode != http.StatusOK {
+ e := errors.New("failed to log in")
+ if err != nil {
+ e = errors.New("failed to log in due to:" + err.Error())
+ }
+ return client, sessionKey, e
+ }
+
+ for _, cookie := range resp.Cookies() {
+ if cookie.Name == "sessionkey" {
+ sessionKey = cookie.Value
+ break
+ }
+ }
+ return client, sessionKey, nil
+}
+
// NewAPIRequest makes an API request to configured management server
func NewAPIRequest(r *Request, api string, args []string) (map[string]interface{}, error) {
params := make(url.Values)
@@ -66,25 +105,47 @@ func NewAPIRequest(r *Request, api string, args []string) (map[string]interface{
params.Add(parts[0], parts[1])
}
}
+ params.Add("response", "json")
- apiKey := r.Config.Core.ActiveProfile.APIKey
- secretKey := r.Config.Core.ActiveProfile.SecretKey
+ var client *http.Client
+ var encodedParams string
+ var err error
+ if len(r.Config.ActiveProfile.APIKey) > 0 && len(r.Config.ActiveProfile.SecretKey) > 0 {
+ apiKey := r.Config.ActiveProfile.APIKey
+ secretKey := r.Config.ActiveProfile.SecretKey
- if len(apiKey) > 0 {
- params.Add("apiKey", apiKey)
- }
+ if len(apiKey) > 0 {
+ params.Add("apiKey", apiKey)
+ }
- params.Add("response", "json")
- encodedParams := encodeRequestParams(params)
+ client = &http.Client{
+ Transport: &http.Transport{
+ TLSClientConfig: &tls.Config{InsecureSkipVerify: !r.Config.ActiveProfile.VerifyCert},
+ },
+ }
+ encodedParams = encodeRequestParams(params)
- mac := hmac.New(sha1.New, []byte(secretKey))
- mac.Write([]byte(strings.Replace(strings.ToLower(encodedParams), "+", "%20", -1)))
- signature := base64.StdEncoding.EncodeToString(mac.Sum(nil))
- encodedParams = encodedParams + fmt.Sprintf("&signature=%s", url.QueryEscape(signature))
+ mac := hmac.New(sha1.New, []byte(secretKey))
+ mac.Write([]byte(strings.Replace(strings.ToLower(encodedParams), "+", "%20", -1)))
+ signature := base64.StdEncoding.EncodeToString(mac.Sum(nil))
+ encodedParams = encodedParams + fmt.Sprintf("&signature=%s", url.QueryEscape(signature))
+ } else if len(r.Config.ActiveProfile.Username) > 0 && len(r.Config.ActiveProfile.Password) > 0 {
+ var sessionKey string
+ client, sessionKey, err = Login(r)
+ if err != nil {
+ return nil, err
+ }
+ params.Add("sessionkey", sessionKey)
+ encodedParams = encodeRequestParams(params)
+ } else {
+ fmt.Println("Please provide either apikey/secretkey or username/password to make an API call")
+ return nil, errors.New("failed to authenticate to make API call")
+ }
- apiURL := fmt.Sprintf("%s?%s", r.Config.Core.ActiveProfile.URL, encodedParams)
+ apiURL := fmt.Sprintf("%s?%s", r.Config.ActiveProfile.URL, encodedParams)
- response, err := http.Get(apiURL)
+ client.Timeout = time.Duration(time.Duration(r.Config.Core.Timeout) * time.Second)
+ response, err := client.Get(apiURL)
if err != nil {
fmt.Println("Error:", err)
return nil, err
diff --git a/config/config.go b/config/config.go
index 16b2b64..97fa6da 100644
--- a/config/config.go
+++ b/config/config.go
@@ -47,21 +47,21 @@ type ServerProfile struct {
// Core block describes common options for the CLI
type Core struct {
- AsyncBlock bool `ini:"asyncblock"`
- Timeout int `ini:"timeout"`
- Output string `ini:"output"`
- ProfileName string `ini:"profile"`
- ActiveProfile *ServerProfile `ini:"-"`
+ AsyncBlock bool `ini:"asyncblock"`
+ Timeout int `ini:"timeout"`
+ Output string `ini:"output"`
+ ProfileName string `ini:"profile"`
}
// Config describes CLI config file and default options
type Config struct {
- Dir string
- ConfigFile string
- HistoryFile string
- CacheFile string
- LogFile string
- Core *Core
+ Dir string
+ ConfigFile string
+ HistoryFile string
+ CacheFile string
+ LogFile string
+ Core *Core
+ ActiveProfile *ServerProfile
}
func getDefaultConfigDir() string {
@@ -86,15 +86,15 @@ func defaultConfig() *Config {
Timeout: 1800,
Output: JSON,
ProfileName: "local",
- ActiveProfile: &ServerProfile{
- URL: "http://localhost:8080/client/api",
- Username: "admin",
- Password: "password",
- Domain: "/",
- APIKey: "",
- SecretKey: "",
- VerifyCert: false,
- },
+ },
+ ActiveProfile: &ServerProfile{
+ URL: "http://localhost:8080/client/api",
+ Username: "admin",
+ Password: "password",
+ Domain: "/",
+ APIKey: "",
+ SecretKey: "",
+ VerifyCert: false,
},
}
}
@@ -117,7 +117,7 @@ func reloadConfig(cfg *Config) *Config {
defaultConf := defaultConfig()
conf := ini.Empty()
conf.Section(ini.DEFAULT_SECTION).ReflectFrom(defaultConf.Core)
- conf.Section(cfg.Core.ProfileName).ReflectFrom(defaultConf.Core.ActiveProfile)
+ conf.Section(cfg.Core.ProfileName).ReflectFrom(defaultConf.ActiveProfile)
conf.SaveTo(cfg.ConfigFile)
}
@@ -149,16 +149,16 @@ func reloadConfig(cfg *Config) *Config {
profile, err := conf.GetSection(cfg.Core.ProfileName)
if profile == nil {
section, _ := conf.NewSection(cfg.Core.ProfileName)
- section.ReflectFrom(&defaultConfig().Core.ActiveProfile)
+ section.ReflectFrom(&defaultConfig().ActiveProfile)
} else {
// Write
- if cfg.Core.ActiveProfile != nil {
- conf.Section(cfg.Core.ProfileName).ReflectFrom(&cfg.Core.ActiveProfile)
+ if cfg.ActiveProfile != nil {
+ conf.Section(cfg.Core.ProfileName).ReflectFrom(&cfg.ActiveProfile)
}
// Update
profile := new(ServerProfile)
conf.Section(cfg.Core.ProfileName).MapTo(profile)
- cfg.Core.ActiveProfile = profile
+ cfg.ActiveProfile = profile
}
// Save
conf.SaveTo(cfg.ConfigFile)
@@ -187,21 +187,21 @@ func (c *Config) UpdateConfig(key string, value string) {
c.Core.Timeout = intValue
case "profile":
c.Core.ProfileName = value
- c.Core.ActiveProfile = nil
+ c.ActiveProfile = nil
case "url":
- c.Core.ActiveProfile.URL = value
+ c.ActiveProfile.URL = value
case "username":
- c.Core.ActiveProfile.Username = value
+ c.ActiveProfile.Username = value
case "password":
- c.Core.ActiveProfile.Password = value
+ c.ActiveProfile.Password = value
case "domain":
- c.Core.ActiveProfile.Domain = value
+ c.ActiveProfile.Domain = value
case "apikey":
- c.Core.ActiveProfile.APIKey = value
+ c.ActiveProfile.APIKey = value
case "secretkey":
- c.Core.ActiveProfile.SecretKey = value
+ c.ActiveProfile.SecretKey = value
case "verifycert":
- c.Core.ActiveProfile.VerifyCert = value == "true"
+ c.ActiveProfile.VerifyCert = value == "true"
default:
return
}
@@ -213,6 +213,7 @@ func (c *Config) UpdateConfig(key string, value string) {
func NewConfig() *Config {
defaultConf := defaultConfig()
defaultConf.Core = nil
+ defaultConf.ActiveProfile = nil
cfg := reloadConfig(defaultConf)
LoadCache(cfg)
return cfg
--
To stop receiving notification emails like this one, please contact
rohit@apache.org.