pkg/resource.go (228 lines of code) (raw):
package main
import (
"encoding/json"
"io/ioutil"
"net/http"
"net/url"
"regexp"
"strings"
sls "github.com/aliyun/aliyun-log-go-sdk"
"gitlab.alibaba-inc.com/rapt/go-security-utils/network"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter"
)
func newResourceHandler(ds *SlsDatasource) backend.CallResourceHandler {
mux := http.NewServeMux()
// register route
mux.HandleFunc("/api/gotoSLS", ds.gotoSLS)
mux.HandleFunc("/api/version", ds.serveVersion)
mux.HandleFunc("/api/getLogstoreList", ds.getLogstoreList)
return httpadapter.New(mux)
}
func (ds *SlsDatasource) serveVersion(w http.ResponseWriter, r *http.Request) {
// Handle query request...
}
type ListLogstoresData struct {
Project string
TelemetryType string
}
func (ds *SlsDatasource) getLogstoreList(w http.ResponseWriter, r *http.Request) {
response := map[string]interface{}{
"data": []map[string]interface{}{}, // 定义 data 为一个任意类型的对象数组
"res": nil,
"message": "",
}
config, err := LoadSettings(httpadapter.PluginConfigFromContext(r.Context()))
if err != nil {
response["message"] = err.Error()
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
provider := sls.NewStaticCredentialsProvider(config.AccessKeyId, config.AccessKeySecret, "")
client := sls.CreateNormalInterfaceV2(config.Endpoint, provider)
client.SetUserAgent("grafana-go")
if config.Region != "" {
client.SetAuthVersion(sls.AuthV4)
client.SetRegion(config.Region)
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response["message"] = err.Error()
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// 解析request JSON 数据
var data ListLogstoresData
if err := json.Unmarshal(body, &data); err != nil {
response["message"] = err.Error()
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
log.DefaultLogger.Debug("getBODY", "body", body, "bodyData", data)
project, err := client.GetProject(data.Project)
if err != nil {
response["message"] = err.Error()
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// 拿当前 Project 的信息
list, err := project.ListLogStoreV2(0, 500, data.TelemetryType)
if err != nil {
response["message"] = err.Error()
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
response["data"] = list
response["res"] = list
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(response)
log.DefaultLogger.Debug("get logstore success.")
}
type Data struct {
Encoding string `json:"encoding"`
Logstore string `json:"logstore"`
Type string `json:"type"`
}
func (ds *SlsDatasource) gotoSLS(w http.ResponseWriter, r *http.Request) {
response := map[string]interface{}{
"message": "",
"err": "",
"url": "",
// "policy": "",
}
config, err := LoadSettings(httpadapter.PluginConfigFromContext(r.Context()))
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
ak := config.AccessKeyId
sk := config.AccessKeySecret
arn := config.RoleArn
prj := config.Project
logstore := config.LogStore
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// 解析request JSON 数据
var data Data
if err := json.Unmarshal(body, &data); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
logstoreType := "/logsearch/"
if data.Type == "metricsql" || data.Type == "metricstore" {
logstoreType = "/metric/"
}
if data.Logstore != "" {
logstore = data.Logstore
}
pattern := `^acs:ram::\d+:role\/[^\/]+$`
regex, err := regexp.Compile(pattern)
if err != nil {
return
}
normalJump := false
if len(arn) == 0 {
normalJump = true
} else {
if !regex.MatchString(arn) {
response["err"] = "regexCheckError"
response["message"] = "roleArn 不符合格式,请检查。"
normalJump = true
}
}
if !normalJump {
roleName := strings.Split(arn, "/")[1]
_, err2 := roleCheck(ak, sk, roleName)
if err2 != nil {
response["err"] = "roleCheckError"
response["message"] = err2.Error()
// http.Error(w, err2.Error(), http.StatusBadRequest)
// return
normalJump = true
}
// response["policy"] = p
}
if !normalJump {
client := NewClient(ak, sk, arn, "default")
stsResp, err := client.AssumeRole(900)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
log.DefaultLogger.Error(err.Error())
// response["err"] = err.Error()
// response["message"] = err.Error()
// w.Header().Set("Content-Type", "application/json")
// w.WriteHeader(http.StatusInternalServerError)
// json.NewEncoder(w).Encode(response)
return
}
id := stsResp.Credentials.AccessKeyId
secret := stsResp.Credentials.AccessKeySecret
token := stsResp.Credentials.SecurityToken
// 使用STS Token换取控制台Signin Token
SigninResp, err := getSigninToken(id, secret, token)
if err != nil {
panic(err)
}
signinToken := SigninResp.SigninToken
// 生成登录链接
loginUrl := "http://www.aliyun.com"
// destination := "http://sls4service.console.aliyun.com"
destination := "http://sls4service.console.aliyun.com/lognext/project/" + prj + logstoreType + logstore + "?isShare=true&hideTopbar=true&hideSidebar=true&ignoreTabLocalStorage=true&" + data.Encoding
url, err := genSigninUrl(signinToken, loginUrl, destination)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
log.DefaultLogger.Error(err.Error())
return
}
response["url"] = url
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(response)
log.DefaultLogger.Debug("Goto SLS with STS success.", url)
return
}
url := "https://sls.console.aliyun.com/lognext/project/" + prj + logstoreType + logstore + "?" + data.Encoding
response["url"] = url
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(response)
log.DefaultLogger.Debug("Goto SLS with Normal jump success.", url)
}
func getSigninToken(id string, secret string, token string) (*SigninResponse, error) {
urlStr := "http://signin.aliyun.com/federation?Action=GetSigninToken"
urlStr += "&AccessKeyId=" + id
urlStr += "&AccessKeySecret=" + secret
urlStr += "&SecurityToken=" + url.QueryEscape(token)
urlStr += "&TicketType=mini"
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.DialContext = network.DefaultNetworkFilter.FilterHttpDialContext(transport.DialContext)
client := &http.Client{
Transport: transport,
}
res, err := client.Get(urlStr)
if err != nil {
return nil, err
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
// fmt.Println("SigninToken json:", string(body))
resp := SigninResponse{}
err = json.Unmarshal(body, &resp)
if err != nil {
return nil, err
}
return &resp, nil
}
func genSigninUrl(signinToken string, loginUrl string, destination string) (string, error) {
urlStr := "http://signin.aliyun.com/federation?Action=Login"
urlStr += "&LoginUrl=" + url.QueryEscape(loginUrl)
urlStr += "&Destination=" + url.QueryEscape(destination)
urlStr += "&SigninToken=" + url.QueryEscape(signinToken)
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
res, err := client.Get(urlStr)
if err != nil {
return "", err
}
location, err := res.Location()
if err != nil {
return "", err
}
locationUrl := location.String()
return locationUrl, nil
}
type SigninResponse struct {
SigninToken string
}