Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export VERSION=0.1.24
export VERSION=0.1.25

.PHONY : install
install:
Expand Down
118 changes: 83 additions & 35 deletions base/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const DefaultBaseURL = "https://api.ucloud.cn/"
const DefaultProfile = "default"

//Version 版本号
const Version = "0.1.24"
const Version = "0.1.25"

//ConfigIns 配置实例, 程序加载时生成
var ConfigIns = &AggConfig{
Expand Down Expand Up @@ -79,14 +79,15 @@ type GlobalFlag struct {

//CLIConfig cli_config element
type CLIConfig struct {
ProjectID string `json:"project_id"`
Region string `json:"region"`
Zone string `json:"zone"`
BaseURL string `json:"base_url"`
Timeout int `json:"timeout_sec"`
Profile string `json:"profile"`
Active bool `json:"active"` //是否生效
MaxRetryTimes *int `json:"max_retry_times"`
ProjectID string `json:"project_id"`
Region string `json:"region"`
Zone string `json:"zone"`
BaseURL string `json:"base_url"`
Timeout int `json:"timeout_sec"`
Profile string `json:"profile"`
Active bool `json:"active"` //是否生效
MaxRetryTimes *int `json:"max_retry_times"`
AgreeUploadLog bool `json:"agree_upload_log"`
}

//CredentialConfig credential element
Expand All @@ -98,16 +99,17 @@ type CredentialConfig struct {

//AggConfig 聚合配置 config+credential
type AggConfig struct {
Profile string `json:"profile"`
Active bool `json:"active"`
ProjectID string `json:"project_id"`
Region string `json:"region"`
Zone string `json:"zone"`
BaseURL string `json:"base_url"`
Timeout int `json:"timeout_sec"`
PublicKey string `json:"public_key"`
PrivateKey string `json:"private_key"`
MaxRetryTimes *int `json:"max_retry_times"`
Profile string `json:"profile"`
Active bool `json:"active"`
ProjectID string `json:"project_id"`
Region string `json:"region"`
Zone string `json:"zone"`
BaseURL string `json:"base_url"`
Timeout int `json:"timeout_sec"`
PublicKey string `json:"public_key"`
PrivateKey string `json:"private_key"`
MaxRetryTimes *int `json:"max_retry_times"`
AgreeUploadLog bool `json:"agree_upload_log"`
}

//ConfigPublicKey 输入公钥
Expand Down Expand Up @@ -136,6 +138,22 @@ func (p *AggConfig) ConfigPrivateKey() error {
return nil
}

//ConfigUploadLog agree upload log or not
func (p *AggConfig) ConfigUploadLog() error {
var input string
fmt.Print("Do you agree to upload log in local file ~/.ucloud/cli.log to help ucloud-cli get better(yes/no):")
_, err := fmt.Scanf("%s\n", &input)
if err != nil {
HandleError(err)
return err
}

if str := strings.ToLower(input); str == "y" || str == "ye" || str == "yes" {
p.AgreeUploadLog = true
}
return nil
}

//GetClientConfig 用来生成sdkClient
func (p *AggConfig) GetClientConfig(isDebug bool) *sdk.Config {
clientConfig := &sdk.Config{
Expand Down Expand Up @@ -169,6 +187,7 @@ func (p *AggConfig) copyToCLIConfig(target *CLIConfig) {
target.Zone = p.Zone
target.Active = p.Active
target.MaxRetryTimes = p.MaxRetryTimes
target.AgreeUploadLog = p.AgreeUploadLog
}

func (p *AggConfig) copyToCredentialConfig(target *CredentialConfig) {
Expand Down Expand Up @@ -217,7 +236,7 @@ func NewAggConfigManager(cfgFile, credFile *os.File) (*AggConfigManager, error)
//Append config to list, override if already exist the same profile
func (p *AggConfigManager) Append(config *AggConfig) error {
if _, ok := p.configs[config.Profile]; ok {
return fmt.Errorf("profile %s exists already", config.Profile)
return fmt.Errorf("profile [%s] exists already", config.Profile)
}

if config.Active && config.Profile != p.activeProfile {
Expand Down Expand Up @@ -279,16 +298,17 @@ func (p *AggConfigManager) Load() error {
}

p.configs[profile] = &AggConfig{
PrivateKey: cred.PrivateKey,
PublicKey: cred.PublicKey,
Profile: config.Profile,
ProjectID: config.ProjectID,
Region: config.Region,
Zone: config.Zone,
BaseURL: config.BaseURL,
Timeout: config.Timeout,
Active: config.Active,
MaxRetryTimes: config.MaxRetryTimes,
PrivateKey: cred.PrivateKey,
PublicKey: cred.PublicKey,
Profile: config.Profile,
ProjectID: config.ProjectID,
Region: config.Region,
Zone: config.Zone,
BaseURL: config.BaseURL,
Timeout: config.Timeout,
Active: config.Active,
MaxRetryTimes: config.MaxRetryTimes,
AgreeUploadLog: config.AgreeUploadLog,
}
}

Expand Down Expand Up @@ -465,6 +485,37 @@ func LoadUserInfo() (*uaccount.UserInfo, error) {
return &user, nil
}

//GetUserInfo from local file and remote api
func GetUserInfo() (*uaccount.UserInfo, error) {
user, err := LoadUserInfo()
if err == nil {
return user, nil
}

req := BizClient.NewGetUserInfoRequest()
resp, err := BizClient.GetUserInfo(req)

if err != nil {
return nil, err
}

if len(resp.DataSet) == 1 {
user = &resp.DataSet[0]
bytes, err := json.Marshal(user)
if err != nil {
return nil, err
}
fileFullPath := GetConfigDir() + "/user.json"
err = ioutil.WriteFile(fileFullPath, bytes, 0600)
if err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf("GetUserInfo DataSet length: %d", len(resp.DataSet))
}
return user, nil
}

//OldConfig 0.1.7以及之前版本的配置struct
type OldConfig struct {
PublicKey string `json:"public_key"`
Expand Down Expand Up @@ -585,18 +636,15 @@ func InitConfig() {
HandleError(err)
}
} else {
var ok bool
ins, ok = AggConfigListIns.GetAggConfigByProfile(Global.Profile)
if !ok {
LogError("Profile %s does not exist", Global.Profile)
}
ins, _ = AggConfigListIns.GetAggConfigByProfile(Global.Profile)
}

if ins != nil {
ConfigIns = ins
} else {
ins = ConfigIns
}
logCmd()

bc, err := GetBizClient(ConfigIns)
if err != nil {
Expand Down
117 changes: 114 additions & 3 deletions base/log.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
package base

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
"runtime"
"strings"
"sync"
"time"

uuid "github.com/satori/go.uuid"
log "github.com/sirupsen/logrus"

"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/version"
)

const DefaultDasURL = "https://das-rpt.ucloud.cn/log"

//Logger 日志
var logger *log.Logger
var mu sync.Mutex
var out = Cxt.GetWriter()
var tracer = Tracer{DefaultDasURL}

func initConfigDir() {
if _, err := os.Stat(GetLogFileDir()); os.IsNotExist(err) {
Expand All @@ -23,6 +34,7 @@ func initConfigDir() {
}
}
}

func initLog() error {
initConfigDir()
file, err := os.OpenFile(GetLogFilePath(), os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644)
Expand All @@ -33,10 +45,21 @@ func initLog() error {
logger.SetNoLock()
logger.AddHook(NewLogRotateHook(file))
logger.SetOutput(file)
LogInfo(fmt.Sprintf("command: %s", strings.Join(os.Args, " ")))

return nil
}

func logCmd() {
args := make([]string, len(os.Args))
copy(args, os.Args)
for idx, arg := range args {
if strings.Contains(arg, "password") && idx <= len(args)-2 {
args[idx+1] = strings.Repeat("*", 8)
}
}
LogInfo(fmt.Sprintf("command: %s", strings.Join(args, " ")))
}

//GetLogger return point of logger
func GetLogger() *log.Logger {
return logger
Expand All @@ -54,21 +77,47 @@ func GetLogFilePath() string {

//LogInfo 记录日志
func LogInfo(logs ...string) {
_, ok := os.LookupEnv("COMP_LINE")
if ok {
return
}
mu.Lock()
defer mu.Unlock()
goID := curGoroutineID()
for _, line := range logs {
logger.WithField("GoroutineID", goID).Info(line)
logger.WithField("goroutine_id", goID).Info(line)
}
if ConfigIns.AgreeUploadLog {
UploadLogs(logs, "info", goID)
}
}

//LogError 记录日志
func LogError(logs ...string) {
_, ok := os.LookupEnv("COMP_LINE")
if ok {
return
}
mu.Lock()
defer mu.Unlock()
goID := curGoroutineID()
for _, line := range logs {
logger.WithField("GoroutineID", goID).Error(line)
logger.WithField("goroutine_id", goID).Error(line)
fmt.Fprintln(out, line)
}
if ConfigIns.AgreeUploadLog {
UploadLogs(logs, "error", goID)
}
}

//UploadLogs send logs to das server
func UploadLogs(logs []string, level string, goID int64) {
var lines []string
for _, log := range logs {
line := fmt.Sprintf("time=%s level=%s goroutine_id=%d msg=%s", time.Now().Format(time.RFC3339Nano), level, goID, log)
lines = append(lines, line)
}
tracer.Send(lines)
}

//LogRotateHook rotate log file
Expand Down Expand Up @@ -143,3 +192,65 @@ func ToQueryMap(req request.Common) map[string]string {
delete(reqMap, "Password")
return reqMap
}

//Tracer upload log to server if allowed
type Tracer struct {
DasUrl string
}

func (t Tracer) wrapLogs(log []string) ([]byte, error) {
dataSet := make([]map[string]interface{}, 0)
dataItem := map[string]interface{}{
"level": "info",
"topic": "api",
"log": log,
}
dataSet = append(dataSet, dataItem)
reqUUID := uuid.NewV4()
sessionID := uuid.NewV4()
user, err := GetUserInfo()
if err != nil {
return nil, err
}
payload := map[string]interface{}{
"aid": "iywtleaa",
"uuid": reqUUID,
"sid": sessionID,
"ds": dataSet,
"cs": map[string]interface{}{
"uname": user.UserEmail,
},
}
marshaled, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("cannot to marshal log: %s", err)
}
return marshaled, nil
}

//Send logs to server
func (t Tracer) Send(logs []string) error {
body, err := t.wrapLogs(logs)
if err != nil {
return err
}
for i := 0; i < len(body); i++ {
body[i] = ^body[i]
}

client := &http.Client{}
ua := fmt.Sprintf("GO/%s GO-SDK/%s %s", runtime.Version(), version.Version, ClientConfig.UserAgent)
req, err := http.NewRequest("POST", t.DasUrl, bytes.NewReader(body))
req.Header.Add("Origin", "https://sdk.ucloud.cn")
req.Header.Add("User-Agent", ua)
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("send logs failed: status %d %s", resp.StatusCode, resp.Status)
}

return nil
}
4 changes: 2 additions & 2 deletions base/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func GetConfigDir() string {
//HandleBizError 处理RetCode != 0 的业务异常
func HandleBizError(resp response.Common) error {
format := "Something wrong. RetCode:%d. Message:%s\n"
Cxt.Printf(format, resp.GetRetCode(), resp.GetMessage())
LogError(fmt.Sprintf(format, resp.GetRetCode(), resp.GetMessage()))
return fmt.Errorf(format, resp.GetRetCode(), resp.GetMessage())
}

Expand Down Expand Up @@ -625,7 +625,7 @@ func Confirm(yes bool, text string) bool {
}
sure, err := ux.Prompt(text)
if err != nil {
Cxt.Println(err)
LogError(err.Error())
return false
}
return sure
Expand Down
Loading