Update Request Header Prefixes and Upgrade API Version to 0.6.0 (#5772)

* Update Request Header Prefixes and Upgrade API Version to 0.6.0

- Changed all HTTP request header prefixes from `x-sls-` to `x-log-` to comply with the latest API specifications.
- Adjusted header prefix validation in `signature.go` to support `x-log-` and `x-acs-`.
- Replaced `x-sls-bodyrawsize` with `x-log-bodyrawsize` in multiple files (`log_project.go`, `request.go`, etc.).
- Updated header fields for compression type and signing method (e.g., `x-sls-compresstype` to `x-log-compresstype`).
- Modified authorization logic to replace `SLS` with `LOG`.

* Refactor signature generation logic for improved readability and maintainability

Split the signature generation logic into several independent functions, including header extraction, standardized header construction, and resource string generation. This modular design enhances code clarity, reduces redundant logic, and improves error handling mechanisms. Additionally, comments and code structure have been optimized to ensure easier extension and debugging.

---------

Co-authored-by: 优胜 <zhushaofei.zsf@alibaba-inc.com>
This commit is contained in:
css521 2025-04-11 16:19:46 +08:00 committed by GitHub
parent 8b65fc75c4
commit 3000824ac5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 217 additions and 569 deletions

View File

@ -1,7 +1,7 @@
package alils package alils
const ( const (
version = "0.5.0" // SDK version version = "0.6.0" // SDK version
signatureMethod = "hmac-sha1" // Signature method signatureMethod = "hmac-sha1" // Signature method
// OffsetNewest is the log head offset, i.e. the offset that will be // OffsetNewest is the log head offset, i.e. the offset that will be

View File

@ -39,33 +39,62 @@ func NewLogProject(name, endpoint, AccessKeyID, accessKeySecret string) (p *LogP
return p, nil return p, nil
} }
// ListLogStore returns all logstore names of project p. // handleResponse is a helper function to process HTTP response and handle common error cases
func (p *LogProject) ListLogStore() (storeNames []string, err error) { // Returns response body as []byte and error if any
h := map[string]string{ func handleResponse(r *http.Response, actionDesc string) ([]byte, error) {
"x-sls-bodyrawsize": "0", body, err := io.ReadAll(r.Body)
}
uri := "/logstores"
r, err := request(p, "GET", uri, h, nil)
if err != nil { if err != nil {
return return nil, err
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return
} }
if r.StatusCode != http.StatusOK { if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{} errMsg := &errorMessage{}
err = json.Unmarshal(buf, errMsg) err = json.Unmarshal(body, errMsg)
if err != nil { if err != nil {
err = fmt.Errorf("failed to list logstore") err = fmt.Errorf("failed to %s", actionDesc)
dump, _ := httputil.DumpResponse(r, true) dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump) fmt.Printf("%s\n", dump)
return return nil, err
} }
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) return nil, fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
}
return body, nil
}
// sendRequest is a helper function to send HTTP request with specified method, uri, headers and body
// Returns response body as []byte and error if any
func (p *LogProject) sendRequest(method, uri string, headers map[string]string, body []byte) ([]byte, error) {
r, err := request(p, method, uri, headers, body)
if err != nil {
return nil, err
}
return handleResponse(r, "process request "+method+" "+uri)
}
// createStandardHeaders creates a map with standard headers for requests
func createStandardHeaders(bodyLen int) map[string]string {
if bodyLen <= 0 {
return map[string]string{
"x-log-bodyrawsize": "0",
}
}
return map[string]string{
"x-log-bodyrawsize": fmt.Sprintf("%v", bodyLen),
"Content-Type": "application/json",
"Accept-Encoding": "deflate", // TODO: support lz4
}
}
// ListLogStore returns all logstore names of project p.
func (p *LogProject) ListLogStore() (storeNames []string, err error) {
h := createStandardHeaders(0)
uri := "/logstores"
buf, err := p.sendRequest("GET", uri, h, nil)
if err != nil {
return return
} }
@ -81,39 +110,18 @@ func (p *LogProject) ListLogStore() (storeNames []string, err error) {
} }
storeNames = body.LogStores storeNames = body.LogStores
return return
} }
// GetLogStore returns logstore according by logstore name. // GetLogStore returns logstore according by logstore name.
func (p *LogProject) GetLogStore(name string) (s *LogStore, err error) { func (p *LogProject) GetLogStore(name string) (s *LogStore, err error) {
h := map[string]string{ h := createStandardHeaders(0)
"x-sls-bodyrawsize": "0",
}
r, err := request(p, "GET", "/logstores/"+name, h, nil) buf, err := p.sendRequest("GET", "/logstores/"+name, h, nil)
if err != nil { if err != nil {
return return
} }
buf, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(buf, errMsg)
if err != nil {
err = fmt.Errorf("failed to get logstore")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
s = &LogStore{} s = &LogStore{}
err = json.Unmarshal(buf, s) err = json.Unmarshal(buf, s)
if err != nil { if err != nil {
@ -145,66 +153,15 @@ func (p *LogProject) CreateLogStore(name string, ttl, shardCnt int) (err error)
return return
} }
h := map[string]string{ h := createStandardHeaders(len(body))
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), _, err = p.sendRequest("POST", "/logstores", h, body)
"Content-Type": "application/json",
"Accept-Encoding": "deflate", // TODO: support lz4
}
r, err := request(p, "POST", "/logstores", h, body)
if err != nil {
return
}
body, err = io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(body, errMsg)
if err != nil {
err = fmt.Errorf("failed to create logstore")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
return return
} }
// DeleteLogStore deletes a logstore according by logstore name. // DeleteLogStore deletes a logstore according by logstore name.
func (p *LogProject) DeleteLogStore(name string) (err error) { func (p *LogProject) DeleteLogStore(name string) (err error) {
h := map[string]string{ h := createStandardHeaders(0)
"x-sls-bodyrawsize": "0", _, err = p.sendRequest("DELETE", "/logstores/"+name, h, nil)
}
r, err := request(p, "DELETE", "/logstores/"+name, h, nil)
if err != nil {
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(body, errMsg)
if err != nil {
err = fmt.Errorf("failed to delete logstore")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
return return
} }
@ -228,73 +185,26 @@ func (p *LogProject) UpdateLogStore(name string, ttl, shardCnt int) (err error)
return return
} }
h := map[string]string{ h := createStandardHeaders(len(body))
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), _, err = p.sendRequest("PUT", "/logstores", h, body)
"Content-Type": "application/json",
"Accept-Encoding": "deflate", // TODO: support lz4
}
r, err := request(p, "PUT", "/logstores", h, body)
if err != nil {
return
}
body, err = io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(body, errMsg)
if err != nil {
err = fmt.Errorf("failed to update logstore")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
return return
} }
// ListMachineGroup returns machine group name list and the total number of machine groups. // ListMachineGroup returns machine group name list and the total number of machine groups.
// The offset starts from 0 and the size is the max number of machine groups could be returned. // The offset starts from 0 and the size is the max number of machine groups could be returned.
func (p *LogProject) ListMachineGroup(offset, size int) (m []string, total int, err error) { func (p *LogProject) ListMachineGroup(offset, size int) (m []string, total int, err error) {
h := map[string]string{ h := createStandardHeaders(0)
"x-sls-bodyrawsize": "0",
}
if size <= 0 { if size <= 0 {
size = 500 size = 500
} }
uri := fmt.Sprintf("/machinegroups?offset=%v&size=%v", offset, size) uri := fmt.Sprintf("/machinegroups?offset=%v&size=%v", offset, size)
r, err := request(p, "GET", uri, h, nil) buf, err := p.sendRequest("GET", uri, h, nil)
if err != nil { if err != nil {
return return
} }
buf, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(buf, errMsg)
if err != nil {
err = fmt.Errorf("failed to list machine group")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
type Body struct { type Body struct {
MachineGroups []string MachineGroups []string
Count int Count int
@ -309,39 +219,18 @@ func (p *LogProject) ListMachineGroup(offset, size int) (m []string, total int,
m = body.MachineGroups m = body.MachineGroups
total = body.Total total = body.Total
return return
} }
// GetMachineGroup retruns machine group according by machine group name. // GetMachineGroup retruns machine group according by machine group name.
func (p *LogProject) GetMachineGroup(name string) (m *MachineGroup, err error) { func (p *LogProject) GetMachineGroup(name string) (m *MachineGroup, err error) {
h := map[string]string{ h := createStandardHeaders(0)
"x-sls-bodyrawsize": "0",
}
r, err := request(p, "GET", "/machinegroups/"+name, h, nil) buf, err := p.sendRequest("GET", "/machinegroups/"+name, h, nil)
if err != nil { if err != nil {
return return
} }
buf, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(buf, errMsg)
if err != nil {
err = fmt.Errorf("failed to get machine group:%v", name)
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
m = &MachineGroup{} m = &MachineGroup{}
err = json.Unmarshal(buf, m) err = json.Unmarshal(buf, m)
if err != nil { if err != nil {
@ -358,35 +247,8 @@ func (p *LogProject) CreateMachineGroup(m *MachineGroup) (err error) {
return return
} }
h := map[string]string{ h := createStandardHeaders(len(body))
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), _, err = p.sendRequest("POST", "/machinegroups", h, body)
"Content-Type": "application/json",
"Accept-Encoding": "deflate", // TODO: support lz4
}
r, err := request(p, "POST", "/machinegroups", h, body)
if err != nil {
return
}
body, err = io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(body, errMsg)
if err != nil {
err = fmt.Errorf("failed to create machine group")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
return return
} }
@ -397,104 +259,33 @@ func (p *LogProject) UpdateMachineGroup(m *MachineGroup) (err error) {
return return
} }
h := map[string]string{ h := createStandardHeaders(len(body))
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), _, err = p.sendRequest("PUT", "/machinegroups/"+m.Name, h, body)
"Content-Type": "application/json",
"Accept-Encoding": "deflate", // TODO: support lz4
}
r, err := request(p, "PUT", "/machinegroups/"+m.Name, h, body)
if err != nil {
return
}
body, err = io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(body, errMsg)
if err != nil {
err = fmt.Errorf("failed to update machine group")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
return return
} }
// DeleteMachineGroup deletes machine group according machine group name. // DeleteMachineGroup deletes machine group according machine group name.
func (p *LogProject) DeleteMachineGroup(name string) (err error) { func (p *LogProject) DeleteMachineGroup(name string) (err error) {
h := map[string]string{ h := createStandardHeaders(0)
"x-sls-bodyrawsize": "0", _, err = p.sendRequest("DELETE", "/machinegroups/"+name, h, nil)
}
r, err := request(p, "DELETE", "/machinegroups/"+name, h, nil)
if err != nil {
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(body, errMsg)
if err != nil {
err = fmt.Errorf("failed to delete machine group")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
return return
} }
// ListConfig returns config names list and the total number of configs. // ListConfig returns config names list and the total number of configs.
// The offset starts from 0 and the size is the max number of configs could be returned. // The offset starts from 0 and the size is the max number of configs could be returned.
func (p *LogProject) ListConfig(offset, size int) (cfgNames []string, total int, err error) { func (p *LogProject) ListConfig(offset, size int) (cfgNames []string, total int, err error) {
h := map[string]string{ h := createStandardHeaders(0)
"x-sls-bodyrawsize": "0",
}
if size <= 0 { if size <= 0 {
size = 100 size = 100
} }
uri := fmt.Sprintf("/configs?offset=%v&size=%v", offset, size) uri := fmt.Sprintf("/configs?offset=%v&size=%v", offset, size)
r, err := request(p, "GET", uri, h, nil) buf, err := p.sendRequest("GET", uri, h, nil)
if err != nil { if err != nil {
return return
} }
buf, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(buf, errMsg)
if err != nil {
err = fmt.Errorf("failed to delete machine group")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
type Body struct { type Body struct {
Total int Total int
Configs []string Configs []string
@ -513,33 +304,13 @@ func (p *LogProject) ListConfig(offset, size int) (cfgNames []string, total int,
// GetConfig returns config according by config name. // GetConfig returns config according by config name.
func (p *LogProject) GetConfig(name string) (c *LogConfig, err error) { func (p *LogProject) GetConfig(name string) (c *LogConfig, err error) {
h := map[string]string{ h := createStandardHeaders(0)
"x-sls-bodyrawsize": "0",
}
r, err := request(p, "GET", "/configs/"+name, h, nil) buf, err := p.sendRequest("GET", "/configs/"+name, h, nil)
if err != nil { if err != nil {
return return
} }
buf, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(buf, errMsg)
if err != nil {
err = fmt.Errorf("failed to delete config")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
c = &LogConfig{} c = &LogConfig{}
err = json.Unmarshal(buf, c) err = json.Unmarshal(buf, c)
if err != nil { if err != nil {
@ -556,35 +327,8 @@ func (p *LogProject) UpdateConfig(c *LogConfig) (err error) {
return return
} }
h := map[string]string{ h := createStandardHeaders(len(body))
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), _, err = p.sendRequest("PUT", "/configs/"+c.Name, h, body)
"Content-Type": "application/json",
"Accept-Encoding": "deflate", // TODO: support lz4
}
r, err := request(p, "PUT", "/configs/"+c.Name, h, body)
if err != nil {
return
}
body, err = io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(body, errMsg)
if err != nil {
err = fmt.Errorf("failed to update config")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
return return
} }
@ -595,99 +339,28 @@ func (p *LogProject) CreateConfig(c *LogConfig) (err error) {
return return
} }
h := map[string]string{ h := createStandardHeaders(len(body))
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), _, err = p.sendRequest("POST", "/configs", h, body)
"Content-Type": "application/json",
"Accept-Encoding": "deflate", // TODO: support lz4
}
r, err := request(p, "POST", "/configs", h, body)
if err != nil {
return
}
body, err = io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(body, errMsg)
if err != nil {
err = fmt.Errorf("failed to update config")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
return return
} }
// DeleteConfig deletes a config according by config name. // DeleteConfig deletes a config according by config name.
func (p *LogProject) DeleteConfig(name string) (err error) { func (p *LogProject) DeleteConfig(name string) (err error) {
h := map[string]string{ h := createStandardHeaders(0)
"x-sls-bodyrawsize": "0", _, err = p.sendRequest("DELETE", "/configs/"+name, h, nil)
}
r, err := request(p, "DELETE", "/configs/"+name, h, nil)
if err != nil {
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(body, errMsg)
if err != nil {
err = fmt.Errorf("failed to delete config")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
return return
} }
// GetAppliedMachineGroups returns applied machine group names list according config name. // GetAppliedMachineGroups returns applied machine group names list according config name.
func (p *LogProject) GetAppliedMachineGroups(confName string) (groupNames []string, err error) { func (p *LogProject) GetAppliedMachineGroups(confName string) (groupNames []string, err error) {
h := map[string]string{ h := createStandardHeaders(0)
"x-sls-bodyrawsize": "0",
}
uri := fmt.Sprintf("/configs/%v/machinegroups", confName) uri := fmt.Sprintf("/configs/%v/machinegroups", confName)
r, err := request(p, "GET", uri, h, nil) buf, err := p.sendRequest("GET", uri, h, nil)
if err != nil { if err != nil {
return return
} }
buf, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(buf, errMsg)
if err != nil {
err = fmt.Errorf("failed to get applied machine groups")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
type Body struct { type Body struct {
Count int Count int
Machinegroups []string Machinegroups []string
@ -705,34 +378,14 @@ func (p *LogProject) GetAppliedMachineGroups(confName string) (groupNames []stri
// GetAppliedConfigs returns applied config names list according machine group name groupName. // GetAppliedConfigs returns applied config names list according machine group name groupName.
func (p *LogProject) GetAppliedConfigs(groupName string) (confNames []string, err error) { func (p *LogProject) GetAppliedConfigs(groupName string) (confNames []string, err error) {
h := map[string]string{ h := createStandardHeaders(0)
"x-sls-bodyrawsize": "0",
}
uri := fmt.Sprintf("/machinegroups/%v/configs", groupName) uri := fmt.Sprintf("/machinegroups/%v/configs", groupName)
r, err := request(p, "GET", uri, h, nil) buf, err := p.sendRequest("GET", uri, h, nil)
if err != nil { if err != nil {
return return
} }
buf, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(buf, errMsg)
if err != nil {
err = fmt.Errorf("failed to applied configs")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
type Cfg struct { type Cfg struct {
Count int `json:"count"` Count int `json:"count"`
Configs []string `json:"configs"` Configs []string `json:"configs"`
@ -750,64 +403,18 @@ func (p *LogProject) GetAppliedConfigs(groupName string) (confNames []string, er
// ApplyConfigToMachineGroup applies config to machine group. // ApplyConfigToMachineGroup applies config to machine group.
func (p *LogProject) ApplyConfigToMachineGroup(confName, groupName string) (err error) { func (p *LogProject) ApplyConfigToMachineGroup(confName, groupName string) (err error) {
h := map[string]string{ h := createStandardHeaders(0)
"x-sls-bodyrawsize": "0",
}
uri := fmt.Sprintf("/machinegroups/%v/configs/%v", groupName, confName) uri := fmt.Sprintf("/machinegroups/%v/configs/%v", groupName, confName)
r, err := request(p, "PUT", uri, h, nil) _, err = p.sendRequest("PUT", uri, h, nil)
if err != nil {
return
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(buf, errMsg)
if err != nil {
err = fmt.Errorf("failed to apply config to machine group")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
return return
} }
// RemoveConfigFromMachineGroup removes config from machine group. // RemoveConfigFromMachineGroup removes config from machine group.
func (p *LogProject) RemoveConfigFromMachineGroup(confName, groupName string) (err error) { func (p *LogProject) RemoveConfigFromMachineGroup(confName, groupName string) (err error) {
h := map[string]string{ h := createStandardHeaders(0)
"x-sls-bodyrawsize": "0",
}
uri := fmt.Sprintf("/machinegroups/%v/configs/%v", groupName, confName) uri := fmt.Sprintf("/machinegroups/%v/configs/%v", groupName, confName)
r, err := request(p, "DELETE", uri, h, nil) _, err = p.sendRequest("DELETE", uri, h, nil)
if err != nil {
return
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return
}
if r.StatusCode != http.StatusOK {
errMsg := &errorMessage{}
err = json.Unmarshal(buf, errMsg)
if err != nil {
err = fmt.Errorf("failed to remove config from machine group")
dump, _ := httputil.DumpResponse(r, true)
fmt.Printf("%s\n", dump)
return
}
err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message)
return
}
return return
} }

View File

@ -32,7 +32,7 @@ type Shard struct {
// ListShards returns shard id list of this logstore. // ListShards returns shard id list of this logstore.
func (s *LogStore) ListShards() (shardIDs []int, err error) { func (s *LogStore) ListShards() (shardIDs []int, err error) {
h := map[string]string{ h := map[string]string{
"x-sls-bodyrawsize": "0", "x-log-bodyrawsize": "0",
} }
uri := fmt.Sprintf("/logstores/%v/shards", s.Name) uri := fmt.Sprintf("/logstores/%v/shards", s.Name)
@ -87,8 +87,8 @@ func (s *LogStore) PutLogs(lg *LogGroup) (err error) {
} }
h := map[string]string{ h := map[string]string{
"x-sls-compresstype": "lz4", "x-log-compresstype": "lz4",
"x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), "x-log-bodyrawsize": fmt.Sprintf("%v", len(body)),
"Content-Type": "application/x-protobuf", "Content-Type": "application/x-protobuf",
} }
@ -123,7 +123,7 @@ func (s *LogStore) PutLogs(lg *LogGroup) (err error) {
// For more detail please read: http://gitlab.alibaba-inc.com/sls/doc/blob/master/api/shard.md#logstore // For more detail please read: http://gitlab.alibaba-inc.com/sls/doc/blob/master/api/shard.md#logstore
func (s *LogStore) GetCursor(shardID int, from string) (cursor string, err error) { func (s *LogStore) GetCursor(shardID int, from string) (cursor string, err error) {
h := map[string]string{ h := map[string]string{
"x-sls-bodyrawsize": "0", "x-log-bodyrawsize": "0",
} }
uri := fmt.Sprintf("/logstores/%v/shards/%v?type=cursor&from=%v", uri := fmt.Sprintf("/logstores/%v/shards/%v?type=cursor&from=%v",
@ -172,7 +172,7 @@ func (s *LogStore) GetLogsBytes(shardID int, cursor string,
logGroupMaxCount int) (out []byte, nextCursor string, err error) { logGroupMaxCount int) (out []byte, nextCursor string, err error) {
h := map[string]string{ h := map[string]string{
"x-sls-bodyrawsize": "0", "x-log-bodyrawsize": "0",
"Accept": "application/x-protobuf", "Accept": "application/x-protobuf",
"Accept-Encoding": "lz4", "Accept-Encoding": "lz4",
} }
@ -203,9 +203,9 @@ func (s *LogStore) GetLogsBytes(shardID int, cursor string,
return return
} }
v, ok := r.Header["X-Sls-Compresstype"] v, ok := r.Header["X-Log-Compresstype"]
if !ok || len(v) == 0 { if !ok || len(v) == 0 {
err = fmt.Errorf("can't find 'x-sls-compresstype' header") err = fmt.Errorf("can't find 'x-log-compresstype' header")
return return
} }
if v[0] != "lz4" { if v[0] != "lz4" {
@ -213,16 +213,16 @@ func (s *LogStore) GetLogsBytes(shardID int, cursor string,
return return
} }
v, ok = r.Header["X-Sls-Cursor"] v, ok = r.Header["X-Log-Cursor"]
if !ok || len(v) == 0 { if !ok || len(v) == 0 {
err = fmt.Errorf("can't find 'x-sls-cursor' header") err = fmt.Errorf("can't find 'x-log-cursor' header")
return return
} }
nextCursor = v[0] nextCursor = v[0]
v, ok = r.Header["X-Sls-Bodyrawsize"] v, ok = r.Header["X-Log-Bodyrawsize"]
if !ok || len(v) == 0 { if !ok || len(v) == 0 {
err = fmt.Errorf("can't find 'x-sls-bodyrawsize' header") err = fmt.Errorf("can't find 'x-log-bodyrawsize' header")
return return
} }
bodyRawSize, err := strconv.Atoi(v[0]) bodyRawSize, err := strconv.Atoi(v[0])

View File

@ -45,7 +45,7 @@ type MachineList struct {
// ListMachines returns the machine list of this machine group. // ListMachines returns the machine list of this machine group.
func (m *MachineGroup) ListMachines() (ms []*Machine, total int, err error) { func (m *MachineGroup) ListMachines() (ms []*Machine, total int, err error) {
h := map[string]string{ h := map[string]string{
"x-sls-bodyrawsize": "0", "x-log-bodyrawsize": "0",
} }
uri := fmt.Sprintf("/machinegroups/%v/machines", m.Name) uri := fmt.Sprintf("/machinegroups/%v/machines", m.Name)

View File

@ -11,17 +11,17 @@ import (
func request(project *LogProject, method, uri string, headers map[string]string, func request(project *LogProject, method, uri string, headers map[string]string,
body []byte) (resp *http.Response, err error) { body []byte) (resp *http.Response, err error) {
// The caller should provide 'x-sls-bodyrawsize' header // The caller should provide 'x-log-bodyrawsize' header
if _, ok := headers["x-sls-bodyrawsize"]; !ok { if _, ok := headers["x-log-bodyrawsize"]; !ok {
err = fmt.Errorf("can't find 'x-sls-bodyrawsize' header") err = fmt.Errorf("can't find 'x-log-bodyrawsize' header")
return return
} }
// SLS public request headers // SLS public request headers
headers["Host"] = project.Name + "." + project.Endpoint headers["Host"] = project.Name + "." + project.Endpoint
headers["Date"] = nowRFC1123() headers["Date"] = nowRFC1123()
headers["x-sls-apiversion"] = version headers["x-log-apiversion"] = version
headers["x-sls-signaturemethod"] = signatureMethod headers["x-log-signaturemethod"] = signatureMethod
if body != nil { if body != nil {
bodyMD5 := fmt.Sprintf("%X", md5.Sum(body)) bodyMD5 := fmt.Sprintf("%X", md5.Sum(body))
headers["Content-MD5"] = bodyMD5 headers["Content-MD5"] = bodyMD5
@ -33,12 +33,12 @@ func request(project *LogProject, method, uri string, headers map[string]string,
} }
// Calc Authorization // Calc Authorization
// Authorization = "SLS <AccessKeyID>:<Signature>" // Authorization = "LOG <AccessKeyID>:<Signature>"
digest, err := signature(project, method, uri, headers) digest, err := signature(project, method, uri, headers)
if err != nil { if err != nil {
return return
} }
auth := fmt.Sprintf("SLS %v:%v", project.AccessKeyID, digest) auth := fmt.Sprintf("LOG %v:%v", project.AccessKeyID, digest)
headers["Authorization"] = auth headers["Authorization"] = auth
// Initialize http request // Initialize http request

View File

@ -20,92 +20,133 @@ func nowRFC1123() string {
return time.Now().In(gmtLoc).Format(time.RFC1123) return time.Now().In(gmtLoc).Format(time.RFC1123)
} }
// signature calculates a request's signature digest. // signature calculates a request's signature digest
func signature(project *LogProject, method, uri string, func signature(project *LogProject, method, uri string,
headers map[string]string) (digest string, err error) { headers map[string]string) (digest string, err error) {
var contentMD5, contentType, date, canoHeaders, canoResource string
// 1. Extract basic header information
contentMD5 := getHeaderSafe(headers, "Content-MD5")
contentType := getHeaderSafe(headers, "Content-Type")
date, ok := headers["Date"]
if !ok {
return "", fmt.Errorf("Can't find 'Date' header")
}
// 2. Calculate CanonicalizedSLSHeaders
canoHeaders := buildCanonicalHeaders(headers)
// 3. Calculate CanonicalizedResource
canoResource, err := buildCanonicalResource(uri)
if err != nil {
return "", err
}
// 4. Build the signature string
signStr := buildSignString(method, contentMD5, contentType, date, canoHeaders, canoResource)
// 5. Calculate HMAC-SHA1 signature and encode with Base64
// Signature = base64(hmac-sha1(UTF8-Encoding-Of(SignString)AccessKeySecret))
return calculateHmacSha1(signStr, project.AccessKeySecret)
}
// getHeaderSafe safely retrieves a header value, returns empty string if not exists
func getHeaderSafe(headers map[string]string, key string) string {
if val, ok := headers[key]; ok {
return val
}
return ""
}
// buildCanonicalHeaders constructs normalized SLS headers
func buildCanonicalHeaders(headers map[string]string) string {
slsHeaders := make(map[string]string)
var slsHeaderKeys sort.StringSlice var slsHeaderKeys sort.StringSlice
// Extract headers prefixed with x-log- and x-acs-
for k, v := range headers {
l := strings.TrimSpace(strings.ToLower(k))
if strings.HasPrefix(l, "x-log-") || strings.HasPrefix(l, "x-acs-") {
slsHeaders[l] = strings.TrimSpace(v)
slsHeaderKeys = append(slsHeaderKeys, l)
}
}
// Sort headers alphabetically
sort.Sort(slsHeaderKeys)
// Build the canonical header string
var result strings.Builder
for i, k := range slsHeaderKeys {
result.WriteString(k)
result.WriteString(":")
result.WriteString(slsHeaders[k])
if i+1 < len(slsHeaderKeys) {
result.WriteString("\n")
}
}
return result.String()
}
// buildCanonicalResource constructs a normalized resource string
func buildCanonicalResource(uri string) (string, error) {
u, err := url.Parse(uri)
if err != nil {
return "", err
}
result := u.Path
// Process query parameters
if u.RawQuery != "" {
var keys sort.StringSlice
vals := u.Query()
for k := range vals {
keys = append(keys, k)
}
sort.Sort(keys)
result += "?"
for i, k := range keys {
if i > 0 {
result += "&"
}
for _, v := range vals[k] {
result += k + "=" + v
}
}
}
return result, nil
}
// buildSignString constructs the complete signature string
func buildSignString(method, contentMD5, contentType, date, canoHeaders, canoResource string) string {
// SignString = VERB + "\n" // SignString = VERB + "\n"
// + CONTENT-MD5 + "\n" // + CONTENT-MD5 + "\n"
// + CONTENT-TYPE + "\n" // + CONTENT-TYPE + "\n"
// + DATE + "\n" // + DATE + "\n"
// + CanonicalizedSLSHeaders + "\n" // + CanonicalizedSLSHeaders + "\n"
// + CanonicalizedResource // + CanonicalizedResource
return method + "\n" +
if val, ok := headers["Content-MD5"]; ok {
contentMD5 = val
}
if val, ok := headers["Content-Type"]; ok {
contentType = val
}
date, ok := headers["Date"]
if !ok {
err = fmt.Errorf("Can't find 'Date' header")
return
}
// Calc CanonicalizedSLSHeaders
slsHeaders := make(map[string]string, len(headers))
for k, v := range headers {
l := strings.TrimSpace(strings.ToLower(k))
if strings.HasPrefix(l, "x-sls-") {
slsHeaders[l] = strings.TrimSpace(v)
slsHeaderKeys = append(slsHeaderKeys, l)
}
}
sort.Sort(slsHeaderKeys)
for i, k := range slsHeaderKeys {
canoHeaders += k + ":" + slsHeaders[k]
if i+1 < len(slsHeaderKeys) {
canoHeaders += "\n"
}
}
// Calc CanonicalizedResource
u, err := url.Parse(uri)
if err != nil {
return
}
canoResource += url.QueryEscape(u.Path)
if u.RawQuery != "" {
var keys sort.StringSlice
vals := u.Query()
for k := range vals {
keys = append(keys, k)
}
sort.Sort(keys)
canoResource += "?"
for i, k := range keys {
if i > 0 {
canoResource += "&"
}
for _, v := range vals[k] {
canoResource += k + "=" + v
}
}
}
signStr := method + "\n" +
contentMD5 + "\n" + contentMD5 + "\n" +
contentType + "\n" + contentType + "\n" +
date + "\n" + date + "\n" +
canoHeaders + "\n" + canoHeaders + "\n" +
canoResource canoResource
}
// Signature = base64(hmac-sha1(UTF8-Encoding-Of(SignString)AccessKeySecret))
mac := hmac.New(sha1.New, []byte(project.AccessKeySecret)) // calculateHmacSha1 calculates the HMAC-SHA1 signature and encodes it with Base64
_, err = mac.Write([]byte(signStr)) func calculateHmacSha1(signStr, secret string) (string, error) {
if err != nil { mac := hmac.New(sha1.New, []byte(secret))
return _, err := mac.Write([]byte(signStr))
} if err != nil {
digest = base64.StdEncoding.EncodeToString(mac.Sum(nil)) return "", err
return }
return base64.StdEncoding.EncodeToString(mac.Sum(nil)), nil
} }