1package ec2metadata 2 3import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "strconv" 8 "strings" 9 "time" 10 11 "github.com/aws/aws-sdk-go/aws" 12 "github.com/aws/aws-sdk-go/aws/awserr" 13 "github.com/aws/aws-sdk-go/aws/request" 14 "github.com/aws/aws-sdk-go/internal/sdkuri" 15) 16 17// getToken uses the duration to return a token for EC2 metadata service, 18// or an error if the request failed. 19func (c *EC2Metadata) getToken(ctx aws.Context, duration time.Duration) (tokenOutput, error) { 20 op := &request.Operation{ 21 Name: "GetToken", 22 HTTPMethod: "PUT", 23 HTTPPath: "/api/token", 24 } 25 26 var output tokenOutput 27 req := c.NewRequest(op, nil, &output) 28 req.SetContext(ctx) 29 30 // remove the fetch token handler from the request handlers to avoid infinite recursion 31 req.Handlers.Sign.RemoveByName(fetchTokenHandlerName) 32 33 // Swap the unmarshalMetadataHandler with unmarshalTokenHandler on this request. 34 req.Handlers.Unmarshal.Swap(unmarshalMetadataHandlerName, unmarshalTokenHandler) 35 36 ttl := strconv.FormatInt(int64(duration/time.Second), 10) 37 req.HTTPRequest.Header.Set(ttlHeader, ttl) 38 39 err := req.Send() 40 41 // Errors with bad request status should be returned. 42 if err != nil { 43 err = awserr.NewRequestFailure( 44 awserr.New(req.HTTPResponse.Status, http.StatusText(req.HTTPResponse.StatusCode), err), 45 req.HTTPResponse.StatusCode, req.RequestID) 46 } 47 48 return output, err 49} 50 51// GetMetadata uses the path provided to request information from the EC2 52// instance metadata service. The content will be returned as a string, or 53// error if the request failed. 54func (c *EC2Metadata) GetMetadata(p string) (string, error) { 55 return c.GetMetadataWithContext(aws.BackgroundContext(), p) 56} 57 58// GetMetadataWithContext uses the path provided to request information from the EC2 59// instance metadata service. The content will be returned as a string, or 60// error if the request failed. 61func (c *EC2Metadata) GetMetadataWithContext(ctx aws.Context, p string) (string, error) { 62 op := &request.Operation{ 63 Name: "GetMetadata", 64 HTTPMethod: "GET", 65 HTTPPath: sdkuri.PathJoin("/meta-data", p), 66 } 67 output := &metadataOutput{} 68 69 req := c.NewRequest(op, nil, output) 70 71 req.SetContext(ctx) 72 73 err := req.Send() 74 return output.Content, err 75} 76 77// GetUserData returns the userdata that was configured for the service. If 78// there is no user-data setup for the EC2 instance a "NotFoundError" error 79// code will be returned. 80func (c *EC2Metadata) GetUserData() (string, error) { 81 return c.GetUserDataWithContext(aws.BackgroundContext()) 82} 83 84// GetUserDataWithContext returns the userdata that was configured for the service. If 85// there is no user-data setup for the EC2 instance a "NotFoundError" error 86// code will be returned. 87func (c *EC2Metadata) GetUserDataWithContext(ctx aws.Context) (string, error) { 88 op := &request.Operation{ 89 Name: "GetUserData", 90 HTTPMethod: "GET", 91 HTTPPath: "/user-data", 92 } 93 94 output := &metadataOutput{} 95 req := c.NewRequest(op, nil, output) 96 req.SetContext(ctx) 97 98 err := req.Send() 99 return output.Content, err 100} 101 102// GetDynamicData uses the path provided to request information from the EC2 103// instance metadata service for dynamic data. The content will be returned 104// as a string, or error if the request failed. 105func (c *EC2Metadata) GetDynamicData(p string) (string, error) { 106 return c.GetDynamicDataWithContext(aws.BackgroundContext(), p) 107} 108 109// GetDynamicDataWithContext uses the path provided to request information from the EC2 110// instance metadata service for dynamic data. The content will be returned 111// as a string, or error if the request failed. 112func (c *EC2Metadata) GetDynamicDataWithContext(ctx aws.Context, p string) (string, error) { 113 op := &request.Operation{ 114 Name: "GetDynamicData", 115 HTTPMethod: "GET", 116 HTTPPath: sdkuri.PathJoin("/dynamic", p), 117 } 118 119 output := &metadataOutput{} 120 req := c.NewRequest(op, nil, output) 121 req.SetContext(ctx) 122 123 err := req.Send() 124 return output.Content, err 125} 126 127// GetInstanceIdentityDocument retrieves an identity document describing an 128// instance. Error is returned if the request fails or is unable to parse 129// the response. 130func (c *EC2Metadata) GetInstanceIdentityDocument() (EC2InstanceIdentityDocument, error) { 131 return c.GetInstanceIdentityDocumentWithContext(aws.BackgroundContext()) 132} 133 134// GetInstanceIdentityDocumentWithContext retrieves an identity document describing an 135// instance. Error is returned if the request fails or is unable to parse 136// the response. 137func (c *EC2Metadata) GetInstanceIdentityDocumentWithContext(ctx aws.Context) (EC2InstanceIdentityDocument, error) { 138 resp, err := c.GetDynamicDataWithContext(ctx, "instance-identity/document") 139 if err != nil { 140 return EC2InstanceIdentityDocument{}, 141 awserr.New("EC2MetadataRequestError", 142 "failed to get EC2 instance identity document", err) 143 } 144 145 doc := EC2InstanceIdentityDocument{} 146 if err := json.NewDecoder(strings.NewReader(resp)).Decode(&doc); err != nil { 147 return EC2InstanceIdentityDocument{}, 148 awserr.New(request.ErrCodeSerialization, 149 "failed to decode EC2 instance identity document", err) 150 } 151 152 return doc, nil 153} 154 155// IAMInfo retrieves IAM info from the metadata API 156func (c *EC2Metadata) IAMInfo() (EC2IAMInfo, error) { 157 return c.IAMInfoWithContext(aws.BackgroundContext()) 158} 159 160// IAMInfoWithContext retrieves IAM info from the metadata API 161func (c *EC2Metadata) IAMInfoWithContext(ctx aws.Context) (EC2IAMInfo, error) { 162 resp, err := c.GetMetadataWithContext(ctx, "iam/info") 163 if err != nil { 164 return EC2IAMInfo{}, 165 awserr.New("EC2MetadataRequestError", 166 "failed to get EC2 IAM info", err) 167 } 168 169 info := EC2IAMInfo{} 170 if err := json.NewDecoder(strings.NewReader(resp)).Decode(&info); err != nil { 171 return EC2IAMInfo{}, 172 awserr.New(request.ErrCodeSerialization, 173 "failed to decode EC2 IAM info", err) 174 } 175 176 if info.Code != "Success" { 177 errMsg := fmt.Sprintf("failed to get EC2 IAM Info (%s)", info.Code) 178 return EC2IAMInfo{}, 179 awserr.New("EC2MetadataError", errMsg, nil) 180 } 181 182 return info, nil 183} 184 185// Region returns the region the instance is running in. 186func (c *EC2Metadata) Region() (string, error) { 187 return c.RegionWithContext(aws.BackgroundContext()) 188} 189 190// RegionWithContext returns the region the instance is running in. 191func (c *EC2Metadata) RegionWithContext(ctx aws.Context) (string, error) { 192 ec2InstanceIdentityDocument, err := c.GetInstanceIdentityDocumentWithContext(ctx) 193 if err != nil { 194 return "", err 195 } 196 // extract region from the ec2InstanceIdentityDocument 197 region := ec2InstanceIdentityDocument.Region 198 if len(region) == 0 { 199 return "", awserr.New("EC2MetadataError", "invalid region received for ec2metadata instance", nil) 200 } 201 // returns region 202 return region, nil 203} 204 205// Available returns if the application has access to the EC2 Metadata service. 206// Can be used to determine if application is running within an EC2 Instance and 207// the metadata service is available. 208func (c *EC2Metadata) Available() bool { 209 return c.AvailableWithContext(aws.BackgroundContext()) 210} 211 212// AvailableWithContext returns if the application has access to the EC2 Metadata service. 213// Can be used to determine if application is running within an EC2 Instance and 214// the metadata service is available. 215func (c *EC2Metadata) AvailableWithContext(ctx aws.Context) bool { 216 if _, err := c.GetMetadataWithContext(ctx, "instance-id"); err != nil { 217 return false 218 } 219 220 return true 221} 222 223// An EC2IAMInfo provides the shape for unmarshaling 224// an IAM info from the metadata API 225type EC2IAMInfo struct { 226 Code string 227 LastUpdated time.Time 228 InstanceProfileArn string 229 InstanceProfileID string 230} 231 232// An EC2InstanceIdentityDocument provides the shape for unmarshaling 233// an instance identity document 234type EC2InstanceIdentityDocument struct { 235 DevpayProductCodes []string `json:"devpayProductCodes"` 236 MarketplaceProductCodes []string `json:"marketplaceProductCodes"` 237 AvailabilityZone string `json:"availabilityZone"` 238 PrivateIP string `json:"privateIp"` 239 Version string `json:"version"` 240 Region string `json:"region"` 241 InstanceID string `json:"instanceId"` 242 BillingProducts []string `json:"billingProducts"` 243 InstanceType string `json:"instanceType"` 244 AccountID string `json:"accountId"` 245 PendingTime time.Time `json:"pendingTime"` 246 ImageID string `json:"imageId"` 247 KernelID string `json:"kernelId"` 248 RamdiskID string `json:"ramdiskId"` 249 Architecture string `json:"architecture"` 250} 251