mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-11-16 03:10:21 +00:00
251 lines
8.4 KiB
Go
251 lines
8.4 KiB
Go
|
package ec2metadata
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"net/http"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"github.com/aws/aws-sdk-go/aws"
|
||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||
|
"github.com/aws/aws-sdk-go/internal/sdkuri"
|
||
|
)
|
||
|
|
||
|
// getToken uses the duration to return a token for EC2 metadata service,
|
||
|
// or an error if the request failed.
|
||
|
func (c *EC2Metadata) getToken(ctx aws.Context, duration time.Duration) (tokenOutput, error) {
|
||
|
op := &request.Operation{
|
||
|
Name: "GetToken",
|
||
|
HTTPMethod: "PUT",
|
||
|
HTTPPath: "/latest/api/token",
|
||
|
}
|
||
|
|
||
|
var output tokenOutput
|
||
|
req := c.NewRequest(op, nil, &output)
|
||
|
req.SetContext(ctx)
|
||
|
|
||
|
// remove the fetch token handler from the request handlers to avoid infinite recursion
|
||
|
req.Handlers.Sign.RemoveByName(fetchTokenHandlerName)
|
||
|
|
||
|
// Swap the unmarshalMetadataHandler with unmarshalTokenHandler on this request.
|
||
|
req.Handlers.Unmarshal.Swap(unmarshalMetadataHandlerName, unmarshalTokenHandler)
|
||
|
|
||
|
ttl := strconv.FormatInt(int64(duration/time.Second), 10)
|
||
|
req.HTTPRequest.Header.Set(ttlHeader, ttl)
|
||
|
|
||
|
err := req.Send()
|
||
|
|
||
|
// Errors with bad request status should be returned.
|
||
|
if err != nil {
|
||
|
err = awserr.NewRequestFailure(
|
||
|
awserr.New(req.HTTPResponse.Status, http.StatusText(req.HTTPResponse.StatusCode), err),
|
||
|
req.HTTPResponse.StatusCode, req.RequestID)
|
||
|
}
|
||
|
|
||
|
return output, err
|
||
|
}
|
||
|
|
||
|
// GetMetadata uses the path provided to request information from the EC2
|
||
|
// instance metadata service. The content will be returned as a string, or
|
||
|
// error if the request failed.
|
||
|
func (c *EC2Metadata) GetMetadata(p string) (string, error) {
|
||
|
return c.GetMetadataWithContext(aws.BackgroundContext(), p)
|
||
|
}
|
||
|
|
||
|
// GetMetadataWithContext uses the path provided to request information from the EC2
|
||
|
// instance metadata service. The content will be returned as a string, or
|
||
|
// error if the request failed.
|
||
|
func (c *EC2Metadata) GetMetadataWithContext(ctx aws.Context, p string) (string, error) {
|
||
|
op := &request.Operation{
|
||
|
Name: "GetMetadata",
|
||
|
HTTPMethod: "GET",
|
||
|
HTTPPath: sdkuri.PathJoin("/latest/meta-data", p),
|
||
|
}
|
||
|
output := &metadataOutput{}
|
||
|
|
||
|
req := c.NewRequest(op, nil, output)
|
||
|
|
||
|
req.SetContext(ctx)
|
||
|
|
||
|
err := req.Send()
|
||
|
return output.Content, err
|
||
|
}
|
||
|
|
||
|
// GetUserData returns the userdata that was configured for the service. If
|
||
|
// there is no user-data setup for the EC2 instance a "NotFoundError" error
|
||
|
// code will be returned.
|
||
|
func (c *EC2Metadata) GetUserData() (string, error) {
|
||
|
return c.GetUserDataWithContext(aws.BackgroundContext())
|
||
|
}
|
||
|
|
||
|
// GetUserDataWithContext returns the userdata that was configured for the service. If
|
||
|
// there is no user-data setup for the EC2 instance a "NotFoundError" error
|
||
|
// code will be returned.
|
||
|
func (c *EC2Metadata) GetUserDataWithContext(ctx aws.Context) (string, error) {
|
||
|
op := &request.Operation{
|
||
|
Name: "GetUserData",
|
||
|
HTTPMethod: "GET",
|
||
|
HTTPPath: "/latest/user-data",
|
||
|
}
|
||
|
|
||
|
output := &metadataOutput{}
|
||
|
req := c.NewRequest(op, nil, output)
|
||
|
req.SetContext(ctx)
|
||
|
|
||
|
err := req.Send()
|
||
|
return output.Content, err
|
||
|
}
|
||
|
|
||
|
// GetDynamicData uses the path provided to request information from the EC2
|
||
|
// instance metadata service for dynamic data. The content will be returned
|
||
|
// as a string, or error if the request failed.
|
||
|
func (c *EC2Metadata) GetDynamicData(p string) (string, error) {
|
||
|
return c.GetDynamicDataWithContext(aws.BackgroundContext(), p)
|
||
|
}
|
||
|
|
||
|
// GetDynamicDataWithContext uses the path provided to request information from the EC2
|
||
|
// instance metadata service for dynamic data. The content will be returned
|
||
|
// as a string, or error if the request failed.
|
||
|
func (c *EC2Metadata) GetDynamicDataWithContext(ctx aws.Context, p string) (string, error) {
|
||
|
op := &request.Operation{
|
||
|
Name: "GetDynamicData",
|
||
|
HTTPMethod: "GET",
|
||
|
HTTPPath: sdkuri.PathJoin("/latest/dynamic", p),
|
||
|
}
|
||
|
|
||
|
output := &metadataOutput{}
|
||
|
req := c.NewRequest(op, nil, output)
|
||
|
req.SetContext(ctx)
|
||
|
|
||
|
err := req.Send()
|
||
|
return output.Content, err
|
||
|
}
|
||
|
|
||
|
// GetInstanceIdentityDocument retrieves an identity document describing an
|
||
|
// instance. Error is returned if the request fails or is unable to parse
|
||
|
// the response.
|
||
|
func (c *EC2Metadata) GetInstanceIdentityDocument() (EC2InstanceIdentityDocument, error) {
|
||
|
return c.GetInstanceIdentityDocumentWithContext(aws.BackgroundContext())
|
||
|
}
|
||
|
|
||
|
// GetInstanceIdentityDocumentWithContext retrieves an identity document describing an
|
||
|
// instance. Error is returned if the request fails or is unable to parse
|
||
|
// the response.
|
||
|
func (c *EC2Metadata) GetInstanceIdentityDocumentWithContext(ctx aws.Context) (EC2InstanceIdentityDocument, error) {
|
||
|
resp, err := c.GetDynamicDataWithContext(ctx, "instance-identity/document")
|
||
|
if err != nil {
|
||
|
return EC2InstanceIdentityDocument{},
|
||
|
awserr.New("EC2MetadataRequestError",
|
||
|
"failed to get EC2 instance identity document", err)
|
||
|
}
|
||
|
|
||
|
doc := EC2InstanceIdentityDocument{}
|
||
|
if err := json.NewDecoder(strings.NewReader(resp)).Decode(&doc); err != nil {
|
||
|
return EC2InstanceIdentityDocument{},
|
||
|
awserr.New(request.ErrCodeSerialization,
|
||
|
"failed to decode EC2 instance identity document", err)
|
||
|
}
|
||
|
|
||
|
return doc, nil
|
||
|
}
|
||
|
|
||
|
// IAMInfo retrieves IAM info from the metadata API
|
||
|
func (c *EC2Metadata) IAMInfo() (EC2IAMInfo, error) {
|
||
|
return c.IAMInfoWithContext(aws.BackgroundContext())
|
||
|
}
|
||
|
|
||
|
// IAMInfoWithContext retrieves IAM info from the metadata API
|
||
|
func (c *EC2Metadata) IAMInfoWithContext(ctx aws.Context) (EC2IAMInfo, error) {
|
||
|
resp, err := c.GetMetadataWithContext(ctx, "iam/info")
|
||
|
if err != nil {
|
||
|
return EC2IAMInfo{},
|
||
|
awserr.New("EC2MetadataRequestError",
|
||
|
"failed to get EC2 IAM info", err)
|
||
|
}
|
||
|
|
||
|
info := EC2IAMInfo{}
|
||
|
if err := json.NewDecoder(strings.NewReader(resp)).Decode(&info); err != nil {
|
||
|
return EC2IAMInfo{},
|
||
|
awserr.New(request.ErrCodeSerialization,
|
||
|
"failed to decode EC2 IAM info", err)
|
||
|
}
|
||
|
|
||
|
if info.Code != "Success" {
|
||
|
errMsg := fmt.Sprintf("failed to get EC2 IAM Info (%s)", info.Code)
|
||
|
return EC2IAMInfo{},
|
||
|
awserr.New("EC2MetadataError", errMsg, nil)
|
||
|
}
|
||
|
|
||
|
return info, nil
|
||
|
}
|
||
|
|
||
|
// Region returns the region the instance is running in.
|
||
|
func (c *EC2Metadata) Region() (string, error) {
|
||
|
return c.RegionWithContext(aws.BackgroundContext())
|
||
|
}
|
||
|
|
||
|
// RegionWithContext returns the region the instance is running in.
|
||
|
func (c *EC2Metadata) RegionWithContext(ctx aws.Context) (string, error) {
|
||
|
ec2InstanceIdentityDocument, err := c.GetInstanceIdentityDocumentWithContext(ctx)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
// extract region from the ec2InstanceIdentityDocument
|
||
|
region := ec2InstanceIdentityDocument.Region
|
||
|
if len(region) == 0 {
|
||
|
return "", awserr.New("EC2MetadataError", "invalid region received for ec2metadata instance", nil)
|
||
|
}
|
||
|
// returns region
|
||
|
return region, nil
|
||
|
}
|
||
|
|
||
|
// Available returns if the application has access to the EC2 Metadata service.
|
||
|
// Can be used to determine if application is running within an EC2 Instance and
|
||
|
// the metadata service is available.
|
||
|
func (c *EC2Metadata) Available() bool {
|
||
|
return c.AvailableWithContext(aws.BackgroundContext())
|
||
|
}
|
||
|
|
||
|
// AvailableWithContext returns if the application has access to the EC2 Metadata service.
|
||
|
// Can be used to determine if application is running within an EC2 Instance and
|
||
|
// the metadata service is available.
|
||
|
func (c *EC2Metadata) AvailableWithContext(ctx aws.Context) bool {
|
||
|
if _, err := c.GetMetadataWithContext(ctx, "instance-id"); err != nil {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// An EC2IAMInfo provides the shape for unmarshaling
|
||
|
// an IAM info from the metadata API
|
||
|
type EC2IAMInfo struct {
|
||
|
Code string
|
||
|
LastUpdated time.Time
|
||
|
InstanceProfileArn string
|
||
|
InstanceProfileID string
|
||
|
}
|
||
|
|
||
|
// An EC2InstanceIdentityDocument provides the shape for unmarshaling
|
||
|
// an instance identity document
|
||
|
type EC2InstanceIdentityDocument struct {
|
||
|
DevpayProductCodes []string `json:"devpayProductCodes"`
|
||
|
MarketplaceProductCodes []string `json:"marketplaceProductCodes"`
|
||
|
AvailabilityZone string `json:"availabilityZone"`
|
||
|
PrivateIP string `json:"privateIp"`
|
||
|
Version string `json:"version"`
|
||
|
Region string `json:"region"`
|
||
|
InstanceID string `json:"instanceId"`
|
||
|
BillingProducts []string `json:"billingProducts"`
|
||
|
InstanceType string `json:"instanceType"`
|
||
|
AccountID string `json:"accountId"`
|
||
|
PendingTime time.Time `json:"pendingTime"`
|
||
|
ImageID string `json:"imageId"`
|
||
|
KernelID string `json:"kernelId"`
|
||
|
RamdiskID string `json:"ramdiskId"`
|
||
|
Architecture string `json:"architecture"`
|
||
|
}
|