client.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. package metadata
  2. import (
  3. "errors"
  4. "fmt"
  5. "github.com/denverdino/aliyungo/util"
  6. "io"
  7. "io/ioutil"
  8. "net"
  9. "net/http"
  10. "net/url"
  11. "strings"
  12. "time"
  13. )
  14. type Request struct {
  15. }
  16. const (
  17. ENDPOINT = "http://100.100.100.200"
  18. META_VERSION_LATEST = "latest"
  19. RS_TYPE_META_DATA = "meta-data"
  20. RS_TYPE_USER_DATA = "user-data"
  21. DNS_NAMESERVERS = "dns-conf/nameservers"
  22. EIPV4 = "eipv4"
  23. HOSTNAME = "hostname"
  24. IMAGE_ID = "image-id"
  25. INSTANCE_ID = "instance-id"
  26. MAC = "mac"
  27. NETWORK_TYPE = "network-type"
  28. NTP_CONF_SERVERS = "ntp-conf/ntp-servers"
  29. OWNER_ACCOUNT_ID = "owner-account-id"
  30. PRIVATE_IPV4 = "private-ipv4"
  31. REGION = "region-id"
  32. SERIAL_NUMBER = "serial-number"
  33. SOURCE_ADDRESS = "source-address"
  34. VPC_CIDR_BLOCK = "vpc-cidr-block"
  35. VPC_ID = "vpc-id"
  36. VSWITCH_CIDR_BLOCK = "vswitch-cidr-block"
  37. VSWITCH_ID = "vswitch-id"
  38. )
  39. type IMetaDataClient interface {
  40. Version(version string) IMetaDataClient
  41. ResourceType(rtype string) IMetaDataClient
  42. Resource(resource string) IMetaDataClient
  43. Go() ([]string, error)
  44. Url() (string, error)
  45. }
  46. type MetaData struct {
  47. c IMetaDataClient
  48. }
  49. func NewMetaData(client *http.Client) *MetaData {
  50. if client == nil {
  51. client = &http.Client{}
  52. }
  53. return &MetaData{
  54. c: &MetaDataClient{client: client},
  55. }
  56. }
  57. func (m *MetaData) HostName() (string, error) {
  58. hostname, err := m.c.Resource(HOSTNAME).Go()
  59. if err != nil {
  60. return "", err
  61. }
  62. return hostname[0], nil
  63. }
  64. func (m *MetaData) ImageID() (string, error) {
  65. image, err := m.c.Resource(IMAGE_ID).Go()
  66. if err != nil {
  67. return "", err
  68. }
  69. return image[0], err
  70. }
  71. func (m *MetaData) InstanceID() (string, error) {
  72. instanceid, err := m.c.Resource(INSTANCE_ID).Go()
  73. if err != nil {
  74. return "", err
  75. }
  76. return instanceid[0], err
  77. }
  78. func (m *MetaData) Mac() (string, error) {
  79. mac, err := m.c.Resource(MAC).Go()
  80. if err != nil {
  81. return "", err
  82. }
  83. return mac[0], nil
  84. }
  85. func (m *MetaData) NetworkType() (string, error) {
  86. network, err := m.c.Resource(NETWORK_TYPE).Go()
  87. if err != nil {
  88. return "", err
  89. }
  90. return network[0], nil
  91. }
  92. func (m *MetaData) OwnerAccountID() (string, error) {
  93. owner, err := m.c.Resource(OWNER_ACCOUNT_ID).Go()
  94. if err != nil {
  95. return "", err
  96. }
  97. return owner[0], nil
  98. }
  99. func (m *MetaData) PrivateIPv4() (string, error) {
  100. private, err := m.c.Resource(PRIVATE_IPV4).Go()
  101. if err != nil {
  102. return "", err
  103. }
  104. return private[0], nil
  105. }
  106. func (m *MetaData) Region() (string, error) {
  107. region, err := m.c.Resource(REGION).Go()
  108. if err != nil {
  109. return "", err
  110. }
  111. return region[0], nil
  112. }
  113. func (m *MetaData) SerialNumber() (string, error) {
  114. serial, err := m.c.Resource(SERIAL_NUMBER).Go()
  115. if err != nil {
  116. return "", err
  117. }
  118. return serial[0], nil
  119. }
  120. func (m *MetaData) SourceAddress() (string, error) {
  121. source, err := m.c.Resource(SOURCE_ADDRESS).Go()
  122. if err != nil {
  123. return "", err
  124. }
  125. return source[0], nil
  126. }
  127. func (m *MetaData) VpcCIDRBlock() (string, error) {
  128. vpcCIDR, err := m.c.Resource(VPC_CIDR_BLOCK).Go()
  129. if err != nil {
  130. return "", err
  131. }
  132. return vpcCIDR[0], err
  133. }
  134. func (m *MetaData) VpcID() (string, error) {
  135. vpcId, err := m.c.Resource(VPC_ID).Go()
  136. if err != nil {
  137. return "", err
  138. }
  139. return vpcId[0], err
  140. }
  141. func (m *MetaData) VswitchCIDRBlock() (string, error) {
  142. cidr, err := m.c.Resource(VSWITCH_CIDR_BLOCK).Go()
  143. if err != nil {
  144. return "", err
  145. }
  146. return cidr[0], err
  147. }
  148. func (m *MetaData) VswitchID() (string, error) {
  149. vswithcid, err := m.c.Resource(VSWITCH_ID).Go()
  150. if err != nil {
  151. return "", err
  152. }
  153. return vswithcid[0], err
  154. }
  155. func (m *MetaData) EIPv4() (string, error) {
  156. eip, err := m.c.Resource(EIPV4).Go()
  157. if err != nil {
  158. return "", err
  159. }
  160. return eip[0], nil
  161. }
  162. func (m *MetaData) DNSNameServers() ([]string, error) {
  163. data, err := m.c.Resource(DNS_NAMESERVERS).Go()
  164. if err != nil {
  165. return []string{}, err
  166. }
  167. return data, nil
  168. }
  169. func (m *MetaData) NTPConfigServers() ([]string, error) {
  170. data, err := m.c.Resource(NTP_CONF_SERVERS).Go()
  171. if err != nil {
  172. return []string{}, err
  173. }
  174. return data, nil
  175. }
  176. //
  177. type MetaDataClient struct {
  178. version string
  179. resourceType string
  180. resource string
  181. client *http.Client
  182. }
  183. func (vpc *MetaDataClient) Version(version string) IMetaDataClient {
  184. vpc.version = version
  185. return vpc
  186. }
  187. func (vpc *MetaDataClient) ResourceType(rtype string) IMetaDataClient {
  188. vpc.resourceType = rtype
  189. return vpc
  190. }
  191. func (vpc *MetaDataClient) Resource(resource string) IMetaDataClient {
  192. vpc.resource = resource
  193. return vpc
  194. }
  195. var retry = util.AttemptStrategy{
  196. Min: 5,
  197. Total: 5 * time.Second,
  198. Delay: 200 * time.Millisecond,
  199. }
  200. func (vpc *MetaDataClient) Url() (string, error) {
  201. if vpc.version == "" {
  202. vpc.version = "latest"
  203. }
  204. if vpc.resourceType == "" {
  205. vpc.resourceType = "meta-data"
  206. }
  207. if vpc.resource == "" {
  208. return "", errors.New("the resource you want to visit must not be nil!")
  209. }
  210. return fmt.Sprintf("%s/%s/%s/%s", ENDPOINT, vpc.version, vpc.resourceType, vpc.resource), nil
  211. }
  212. func (vpc *MetaDataClient) Go() (resu []string, err error) {
  213. for r := retry.Start(); r.Next(); {
  214. resu, err = vpc.send()
  215. if !shouldRetry(err) {
  216. break
  217. }
  218. }
  219. return resu, err
  220. }
  221. func (vpc *MetaDataClient) send() ([]string, error) {
  222. url, err := vpc.Url()
  223. if err != nil {
  224. return []string{}, err
  225. }
  226. requ, err := http.NewRequest(http.MethodGet, url, nil)
  227. if err != nil {
  228. return []string{}, err
  229. }
  230. resp, err := vpc.client.Do(requ)
  231. if err != nil {
  232. return nil, err
  233. }
  234. if resp.StatusCode != 200 {
  235. return nil, err
  236. }
  237. defer resp.Body.Close()
  238. data, err := ioutil.ReadAll(resp.Body)
  239. if err != nil {
  240. return []string{}, err
  241. }
  242. if string(data) == "" {
  243. return []string{""}, nil
  244. }
  245. return strings.Split(string(data), "\n"), nil
  246. }
  247. type TimeoutError interface {
  248. error
  249. Timeout() bool // Is the error a timeout?
  250. }
  251. func shouldRetry(err error) bool {
  252. if err == nil {
  253. return false
  254. }
  255. _, ok := err.(TimeoutError)
  256. if ok {
  257. return true
  258. }
  259. switch err {
  260. case io.ErrUnexpectedEOF, io.EOF:
  261. return true
  262. }
  263. switch e := err.(type) {
  264. case *net.DNSError:
  265. return true
  266. case *net.OpError:
  267. switch e.Op {
  268. case "read", "write":
  269. return true
  270. }
  271. case *url.Error:
  272. // url.Error can be returned either by net/url if a URL cannot be
  273. // parsed, or by net/http if the response is closed before the headers
  274. // are received or parsed correctly. In that later case, e.Op is set to
  275. // the HTTP method name with the first letter uppercased. We don't want
  276. // to retry on POST operations, since those are not idempotent, all the
  277. // other ones should be safe to retry.
  278. switch e.Op {
  279. case "Get", "Put", "Delete", "Head":
  280. return shouldRetry(e.Err)
  281. default:
  282. return false
  283. }
  284. }
  285. return false
  286. }