Skip to content

Commit 1006063

Browse files
committed
feature: support metadata v2
Signed-off-by: bingshen.wbs <[email protected]>
1 parent c52b2cc commit 1006063

File tree

3 files changed

+193
-58
lines changed

3 files changed

+193
-58
lines changed

internal/config/config.go

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@ package config
33
import (
44
"encoding/json"
55
"fmt"
6-
"io"
7-
"net/http"
8-
"os"
9-
"strings"
10-
6+
"github.com/AliyunContainerService/alibabacloud-erdma-controller/internal/utils"
117
"k8s.io/utils/ptr"
8+
"os"
129

1310
"github.com/AliyunContainerService/alibabacloud-erdma-controller/internal/types"
1411
ctrl "sigs.k8s.io/controller-runtime"
@@ -29,27 +26,7 @@ var (
2926

3027
func getRegion() (string, error) {
3128
url := regionIDAddr
32-
resp, err := http.Get(url)
33-
if err != nil {
34-
return "", fmt.Errorf("error get url: %s from metaserver. %w", url, err)
35-
}
36-
//nolint:errcheck
37-
defer resp.Body.Close()
38-
39-
if resp.StatusCode == http.StatusNotFound {
40-
return "", fmt.Errorf("error get url: %s from metaserver, code: %v, %v", url, resp.StatusCode, "Not Found")
41-
}
42-
if resp.StatusCode >= http.StatusBadRequest {
43-
return "", fmt.Errorf("error get url: %s from metaserver, code: %v", url, resp.StatusCode)
44-
}
45-
46-
body, err := io.ReadAll(resp.Body)
47-
if err != nil {
48-
return "", err
49-
}
50-
result := strings.Split(string(body), "\n")
51-
trimResult := strings.Trim(result[0], "/")
52-
return trimResult, nil
29+
return utils.GetStrFromMetadata(url)
5330
}
5431

5532
func InitConfig(configPath, credentialPath string) error {

internal/drivers/netdev.go

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,11 @@ package drivers
44

55
import (
66
"fmt"
7-
"io"
8-
"net"
9-
"net/http"
10-
"strings"
11-
127
"github.com/AliyunContainerService/alibabacloud-erdma-controller/internal/types"
8+
"github.com/AliyunContainerService/alibabacloud-erdma-controller/internal/utils"
139
"github.com/samber/lo"
1410
"github.com/vishvananda/netlink"
11+
"net"
1512
)
1613

1714
const (
@@ -237,15 +234,15 @@ func genRoutesForAddr(gateway net.IP, cidr *net.IPNet) ([]*route, error) {
237234
}
238235

239236
func getNetConfFromMetadata(mac string) (*netConf, error) {
240-
addr, err := getStrFromMetadata(fmt.Sprintf(ipUrl, mac))
237+
addr, err := utils.GetStrFromMetadata(fmt.Sprintf(ipUrl, mac))
241238
if err != nil {
242239
return nil, err
243240
}
244241
ip := net.ParseIP(addr)
245242
if ip == nil {
246243
return nil, fmt.Errorf("invalid ip address: %s", addr)
247244
}
248-
cidr, err := getStrFromMetadata(fmt.Sprintf(cidrURL, mac))
245+
cidr, err := utils.GetStrFromMetadata(fmt.Sprintf(cidrURL, mac))
249246
if err != nil {
250247
return nil, err
251248
}
@@ -254,7 +251,7 @@ func getNetConfFromMetadata(mac string) (*netConf, error) {
254251
return nil, fmt.Errorf("invalid cidr: %s", cidr)
255252
}
256253

257-
gw, err := getStrFromMetadata(fmt.Sprintf(gatewayURL, mac))
254+
gw, err := utils.GetStrFromMetadata(fmt.Sprintf(gatewayURL, mac))
258255
if err != nil {
259256
return nil, err
260257
}
@@ -276,27 +273,3 @@ func getNetConfFromMetadata(mac string) (*netConf, error) {
276273
conf.routes = routes
277274
return conf, nil
278275
}
279-
280-
func getStrFromMetadata(url string) (string, error) {
281-
resp, err := http.Get(url)
282-
if err != nil {
283-
return "", fmt.Errorf("error get url: %s from metaserver. %w", url, err)
284-
}
285-
//nolint:errcheck
286-
defer resp.Body.Close()
287-
288-
if resp.StatusCode == http.StatusNotFound {
289-
return "", fmt.Errorf("error get url: %s from metaserver, code: %v, %v", url, resp.StatusCode, "Not Found")
290-
}
291-
if resp.StatusCode >= http.StatusBadRequest {
292-
return "", fmt.Errorf("error get url: %s from metaserver, code: %v", url, resp.StatusCode)
293-
}
294-
295-
body, err := io.ReadAll(resp.Body)
296-
if err != nil {
297-
return "", err
298-
}
299-
result := strings.Split(string(body), "\n")
300-
trimResult := strings.Trim(result[0], "/")
301-
return trimResult, nil
302-
}

internal/utils/metadata.go

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
package utils
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"golang.org/x/sync/singleflight"
7+
"io"
8+
"k8s.io/apimachinery/pkg/util/cache"
9+
"k8s.io/apimachinery/pkg/util/wait"
10+
"net/http"
11+
"strconv"
12+
"strings"
13+
"time"
14+
)
15+
16+
const (
17+
tokenURL = "http://100.100.100.200/latest/api/token"
18+
tokenTimeout = 21600
19+
)
20+
21+
func GetStrFromMetadata(url string) (string, error) {
22+
body, err := getWithToken(url)
23+
if err != nil {
24+
return "", err
25+
}
26+
result := strings.Split(string(body), "\n")
27+
trimResult := strings.Trim(result[0], "/")
28+
return trimResult, nil
29+
}
30+
31+
type Error struct {
32+
URL string
33+
Code string
34+
R error
35+
}
36+
37+
func (e *Error) Error() string {
38+
return fmt.Sprintf("get from metaserver failed code: %s, url: %s, err: %s", e.Code, e.URL, e.R)
39+
}
40+
41+
var (
42+
tokenCache *cache.Expiring
43+
single singleflight.Group
44+
defaultClient *http.Client
45+
)
46+
47+
func getWithToken(url string) ([]byte, error) {
48+
49+
skipRetry := false
50+
retry:
51+
var token string
52+
v, ok := tokenCache.Get(tokenURL)
53+
if !ok {
54+
vv, err, _ := single.Do(tokenURL, func() (interface{}, error) {
55+
out, err := withRetry(tokenURL, [][]string{
56+
{
57+
"X-aliyun-ecs-metadata-token-ttl-seconds", strconv.Itoa(tokenTimeout),
58+
},
59+
})
60+
if err != nil {
61+
return nil, err
62+
}
63+
return string(out), nil
64+
})
65+
if err != nil {
66+
return nil, err
67+
}
68+
69+
token = vv.(string)
70+
71+
tokenCache.Set(tokenURL, token, tokenTimeout*time.Second/2)
72+
} else {
73+
token = v.(string)
74+
}
75+
76+
out, err := withRetry(url, [][]string{
77+
{
78+
"X-aliyun-ecs-metadata-token", token,
79+
},
80+
})
81+
if err != nil {
82+
var typedErr *Error
83+
ok := errors.As(err, &typedErr)
84+
85+
if ok && !skipRetry {
86+
if typedErr.Code == strconv.Itoa(http.StatusUnauthorized) {
87+
skipRetry = true
88+
89+
tokenCache.Delete(tokenURL)
90+
goto retry
91+
}
92+
}
93+
94+
return nil, err
95+
}
96+
97+
return out, err
98+
}
99+
100+
func withRetry(url string, headers [][]string) ([]byte, error) {
101+
var innerErr error
102+
var body []byte
103+
err := wait.ExponentialBackoff(wait.Backoff{
104+
Duration: 500 * time.Millisecond,
105+
Factor: 1.2,
106+
Jitter: 0.1,
107+
Steps: 4,
108+
}, func() (bool, error) {
109+
var err error
110+
111+
method := "GET"
112+
if url == tokenURL {
113+
method = "PUT"
114+
}
115+
req, err := http.NewRequest(method, url, nil)
116+
if err != nil {
117+
innerErr = &Error{
118+
URL: url,
119+
R: err,
120+
}
121+
return false, nil
122+
}
123+
124+
for _, h := range headers {
125+
if len(h) != 2 {
126+
return false, fmt.Errorf("invalid header")
127+
}
128+
req.Header.Set(h[0], h[1])
129+
}
130+
131+
resp, err := defaultClient.Do(req)
132+
if err != nil {
133+
// retryable err
134+
innerErr = &Error{
135+
URL: url,
136+
R: err,
137+
}
138+
return false, nil
139+
}
140+
defer resp.Body.Close()
141+
142+
// retryable err
143+
if resp.StatusCode == http.StatusTooManyRequests ||
144+
resp.StatusCode >= http.StatusInternalServerError {
145+
innerErr = &Error{
146+
URL: url,
147+
Code: strconv.Itoa(resp.StatusCode),
148+
R: nil,
149+
}
150+
return false, nil
151+
}
152+
153+
if resp.StatusCode >= http.StatusBadRequest {
154+
innerErr = &Error{
155+
URL: url,
156+
Code: strconv.Itoa(resp.StatusCode),
157+
R: nil,
158+
}
159+
return false, innerErr
160+
}
161+
162+
body, err = io.ReadAll(resp.Body)
163+
if err != nil {
164+
return false, err
165+
}
166+
return true, nil
167+
})
168+
if err != nil {
169+
if innerErr != nil {
170+
return nil, innerErr
171+
}
172+
return nil, err
173+
}
174+
return body, nil
175+
}
176+
177+
func init() {
178+
tokenCache = cache.NewExpiring()
179+
defaultClient = &http.Client{
180+
Transport: nil,
181+
CheckRedirect: nil,
182+
Jar: nil,
183+
Timeout: 30 * time.Second,
184+
}
185+
}

0 commit comments

Comments
 (0)