637 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			637 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
| //
 | |
| // goamz - Go packages to interact with the Amazon Web Services.
 | |
| //
 | |
| //   https://wiki.ubuntu.com/goamz
 | |
| //
 | |
| // Copyright (c) 2011 Canonical Ltd.
 | |
| //
 | |
| // Written by Gustavo Niemeyer <gustavo.niemeyer@canonical.com>
 | |
| //
 | |
| package aws
 | |
| 
 | |
| import (
 | |
| 	"encoding/json"
 | |
| 	"encoding/xml"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"io/ioutil"
 | |
| 	"net"
 | |
| 	"net/http"
 | |
| 	"net/url"
 | |
| 	"os"
 | |
| 	"os/user"
 | |
| 	"path"
 | |
| 	"regexp"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| // Regular expressions for INI files
 | |
| var (
 | |
| 	iniSectionRegexp = regexp.MustCompile(`^\s*\[([^\[\]]+)\]\s*$`)
 | |
| 	iniSettingRegexp = regexp.MustCompile(`^\s*(.+?)\s*=\s*(.*\S)\s*$`)
 | |
| )
 | |
| 
 | |
| // Defines the valid signers
 | |
| const (
 | |
| 	V2Signature      = iota
 | |
| 	V4Signature      = iota
 | |
| 	Route53Signature = iota
 | |
| )
 | |
| 
 | |
| // Defines the service endpoint and correct Signer implementation to use
 | |
| // to sign requests for this endpoint
 | |
| type ServiceInfo struct {
 | |
| 	Endpoint string
 | |
| 	Signer   uint
 | |
| }
 | |
| 
 | |
| // Region defines the URLs where AWS services may be accessed.
 | |
| //
 | |
| // See http://goo.gl/d8BP1 for more details.
 | |
| type Region struct {
 | |
| 	Name                   string // the canonical name of this region.
 | |
| 	EC2Endpoint            ServiceInfo
 | |
| 	S3Endpoint             string
 | |
| 	S3BucketEndpoint       string // Not needed by AWS S3. Use ${bucket} for bucket name.
 | |
| 	S3LocationConstraint   bool   // true if this region requires a LocationConstraint declaration.
 | |
| 	S3LowercaseBucket      bool   // true if the region requires bucket names to be lower case.
 | |
| 	SDBEndpoint            string
 | |
| 	SNSEndpoint            string
 | |
| 	SQSEndpoint            string
 | |
| 	SESEndpoint            string
 | |
| 	IAMEndpoint            string
 | |
| 	ELBEndpoint            string
 | |
| 	KMSEndpoint            string
 | |
| 	DynamoDBEndpoint       string
 | |
| 	CloudWatchServicepoint ServiceInfo
 | |
| 	AutoScalingEndpoint    string
 | |
| 	RDSEndpoint            ServiceInfo
 | |
| 	KinesisEndpoint        string
 | |
| 	STSEndpoint            string
 | |
| 	CloudFormationEndpoint string
 | |
| 	ElastiCacheEndpoint    string
 | |
| }
 | |
| 
 | |
| var Regions = map[string]Region{
 | |
| 	APNortheast.Name:  APNortheast,
 | |
| 	APNortheast2.Name: APNortheast2,
 | |
| 	APSoutheast.Name:  APSoutheast,
 | |
| 	APSoutheast2.Name: APSoutheast2,
 | |
| 	EUCentral.Name:    EUCentral,
 | |
| 	EUWest.Name:       EUWest,
 | |
| 	USEast.Name:       USEast,
 | |
| 	USWest.Name:       USWest,
 | |
| 	USWest2.Name:      USWest2,
 | |
| 	USGovWest.Name:    USGovWest,
 | |
| 	SAEast.Name:       SAEast,
 | |
| 	CNNorth1.Name:     CNNorth1,
 | |
| }
 | |
| 
 | |
| // Designates a signer interface suitable for signing AWS requests, params
 | |
| // should be appropriately encoded for the request before signing.
 | |
| //
 | |
| // A signer should be initialized with Auth and the appropriate endpoint.
 | |
| type Signer interface {
 | |
| 	Sign(method, path string, params map[string]string)
 | |
| }
 | |
| 
 | |
| // An AWS Service interface with the API to query the AWS service
 | |
| //
 | |
| // Supplied as an easy way to mock out service calls during testing.
 | |
| type AWSService interface {
 | |
| 	// Queries the AWS service at a given method/path with the params and
 | |
| 	// returns an http.Response and error
 | |
| 	Query(method, path string, params map[string]string) (*http.Response, error)
 | |
| 	// Builds an error given an XML payload in the http.Response, can be used
 | |
| 	// to process an error if the status code is not 200 for example.
 | |
| 	BuildError(r *http.Response) error
 | |
| }
 | |
| 
 | |
| // Implements a Server Query/Post API to easily query AWS services and build
 | |
| // errors when desired
 | |
| type Service struct {
 | |
| 	service ServiceInfo
 | |
| 	signer  Signer
 | |
| }
 | |
| 
 | |
| // Create a base set of params for an action
 | |
| func MakeParams(action string) map[string]string {
 | |
| 	params := make(map[string]string)
 | |
| 	params["Action"] = action
 | |
| 	return params
 | |
| }
 | |
| 
 | |
| // Create a new AWS server to handle making requests
 | |
| func NewService(auth Auth, service ServiceInfo) (s *Service, err error) {
 | |
| 	var signer Signer
 | |
| 	switch service.Signer {
 | |
| 	case V2Signature:
 | |
| 		signer, err = NewV2Signer(auth, service)
 | |
| 	// case V4Signature:
 | |
| 	// 	signer, err = NewV4Signer(auth, service, Regions["eu-west-1"])
 | |
| 	default:
 | |
| 		err = fmt.Errorf("Unsupported signer for service")
 | |
| 	}
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	s = &Service{service: service, signer: signer}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func (s *Service) Query(method, path string, params map[string]string) (resp *http.Response, err error) {
 | |
| 	params["Timestamp"] = time.Now().UTC().Format(time.RFC3339)
 | |
| 	u, err := url.Parse(s.service.Endpoint)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	u.Path = path
 | |
| 
 | |
| 	s.signer.Sign(method, path, params)
 | |
| 	if method == "GET" {
 | |
| 		u.RawQuery = multimap(params).Encode()
 | |
| 		resp, err = http.Get(u.String())
 | |
| 	} else if method == "POST" {
 | |
| 		resp, err = http.PostForm(u.String(), multimap(params))
 | |
| 	}
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func (s *Service) BuildError(r *http.Response) error {
 | |
| 	errors := ErrorResponse{}
 | |
| 	xml.NewDecoder(r.Body).Decode(&errors)
 | |
| 	var err Error
 | |
| 	err = errors.Errors
 | |
| 	err.RequestId = errors.RequestId
 | |
| 	err.StatusCode = r.StatusCode
 | |
| 	if err.Message == "" {
 | |
| 		err.Message = r.Status
 | |
| 	}
 | |
| 	return &err
 | |
| }
 | |
| 
 | |
| type ServiceError interface {
 | |
| 	error
 | |
| 	ErrorCode() string
 | |
| }
 | |
| 
 | |
| type ErrorResponse struct {
 | |
| 	Errors    Error  `xml:"Error"`
 | |
| 	RequestId string // A unique ID for tracking the request
 | |
| }
 | |
| 
 | |
| type Error struct {
 | |
| 	StatusCode int
 | |
| 	Type       string
 | |
| 	Code       string
 | |
| 	Message    string
 | |
| 	RequestId  string
 | |
| }
 | |
| 
 | |
| func (err *Error) Error() string {
 | |
| 	return fmt.Sprintf("Type: %s, Code: %s, Message: %s",
 | |
| 		err.Type, err.Code, err.Message,
 | |
| 	)
 | |
| }
 | |
| 
 | |
| func (err *Error) ErrorCode() string {
 | |
| 	return err.Code
 | |
| }
 | |
| 
 | |
| type Auth struct {
 | |
| 	AccessKey, SecretKey string
 | |
| 	token                string
 | |
| 	expiration           time.Time
 | |
| }
 | |
| 
 | |
| func (a *Auth) Token() string {
 | |
| 	if a.token == "" {
 | |
| 		return ""
 | |
| 	}
 | |
| 	if time.Since(a.expiration) >= -30*time.Second { //in an ideal world this should be zero assuming the instance is synching it's clock
 | |
| 		auth, err := GetAuth("", "", "", time.Time{})
 | |
| 		if err == nil {
 | |
| 			*a = auth
 | |
| 		}
 | |
| 	}
 | |
| 	return a.token
 | |
| }
 | |
| 
 | |
| func (a *Auth) Expiration() time.Time {
 | |
| 	return a.expiration
 | |
| }
 | |
| 
 | |
| // To be used with other APIs that return auth credentials such as STS
 | |
| func NewAuth(accessKey, secretKey, token string, expiration time.Time) *Auth {
 | |
| 	return &Auth{
 | |
| 		AccessKey:  accessKey,
 | |
| 		SecretKey:  secretKey,
 | |
| 		token:      token,
 | |
| 		expiration: expiration,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // ResponseMetadata
 | |
| type ResponseMetadata struct {
 | |
| 	RequestId string // A unique ID for tracking the request
 | |
| }
 | |
| 
 | |
| type BaseResponse struct {
 | |
| 	ResponseMetadata ResponseMetadata
 | |
| }
 | |
| 
 | |
| var unreserved = make([]bool, 128)
 | |
| var hex = "0123456789ABCDEF"
 | |
| 
 | |
| func init() {
 | |
| 	// RFC3986
 | |
| 	u := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890-_.~"
 | |
| 	for _, c := range u {
 | |
| 		unreserved[c] = true
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func multimap(p map[string]string) url.Values {
 | |
| 	q := make(url.Values, len(p))
 | |
| 	for k, v := range p {
 | |
| 		q[k] = []string{v}
 | |
| 	}
 | |
| 	return q
 | |
| }
 | |
| 
 | |
| type credentials struct {
 | |
| 	Code            string
 | |
| 	LastUpdated     string
 | |
| 	Type            string
 | |
| 	AccessKeyId     string
 | |
| 	SecretAccessKey string
 | |
| 	Token           string
 | |
| 	Expiration      string
 | |
| }
 | |
| 
 | |
| // GetMetaData retrieves instance metadata about the current machine.
 | |
| //
 | |
| // See http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AESDG-chapter-instancedata.html for more details.
 | |
| func GetMetaData(path string) (contents []byte, err error) {
 | |
| 	c := http.Client{
 | |
| 		Transport: &http.Transport{
 | |
| 			Dial: func(netw, addr string) (net.Conn, error) {
 | |
| 				deadline := time.Now().Add(5 * time.Second)
 | |
| 				c, err := net.DialTimeout(netw, addr, time.Second*2)
 | |
| 				if err != nil {
 | |
| 					return nil, err
 | |
| 				}
 | |
| 				c.SetDeadline(deadline)
 | |
| 				return c, nil
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	url := "http://169.254.169.254/latest/meta-data/" + path
 | |
| 
 | |
| 	resp, err := c.Get(url)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	defer resp.Body.Close()
 | |
| 
 | |
| 	if resp.StatusCode != 200 {
 | |
| 		err = fmt.Errorf("Code %d returned for url %s", resp.StatusCode, url)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	body, err := ioutil.ReadAll(resp.Body)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	return []byte(body), err
 | |
| }
 | |
| 
 | |
| func GetRegion(regionName string) (region Region) {
 | |
| 	region = Regions[regionName]
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // GetInstanceCredentials creates an Auth based on the instance's role credentials.
 | |
| // If the running instance is not in EC2 or does not have a valid IAM role, an error will be returned.
 | |
| // For more info about setting up IAM roles, see http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html
 | |
| func GetInstanceCredentials() (cred credentials, err error) {
 | |
| 	credentialPath := "iam/security-credentials/"
 | |
| 
 | |
| 	// Get the instance role
 | |
| 	role, err := GetMetaData(credentialPath)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Get the instance role credentials
 | |
| 	credentialJSON, err := GetMetaData(credentialPath + string(role))
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	err = json.Unmarshal([]byte(credentialJSON), &cred)
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // GetAuth creates an Auth based on either passed in credentials,
 | |
| // environment information or instance based role credentials.
 | |
| func GetAuth(accessKey string, secretKey, token string, expiration time.Time) (auth Auth, err error) {
 | |
| 	// First try passed in credentials
 | |
| 	if accessKey != "" && secretKey != "" {
 | |
| 		return Auth{accessKey, secretKey, token, expiration}, nil
 | |
| 	}
 | |
| 
 | |
| 	// Next try to get auth from the environment
 | |
| 	auth, err = EnvAuth()
 | |
| 	if err == nil {
 | |
| 		// Found auth, return
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Next try getting auth from the instance role
 | |
| 	cred, err := GetInstanceCredentials()
 | |
| 	if err == nil {
 | |
| 		// Found auth, return
 | |
| 		auth.AccessKey = cred.AccessKeyId
 | |
| 		auth.SecretKey = cred.SecretAccessKey
 | |
| 		auth.token = cred.Token
 | |
| 		exptdate, err := time.Parse("2006-01-02T15:04:05Z", cred.Expiration)
 | |
| 		if err != nil {
 | |
| 			err = fmt.Errorf("Error Parsing expiration date: cred.Expiration :%s , error: %s \n", cred.Expiration, err)
 | |
| 		}
 | |
| 		auth.expiration = exptdate
 | |
| 		return auth, err
 | |
| 	}
 | |
| 
 | |
| 	// Next try getting auth from the credentials file
 | |
| 	auth, err = CredentialFileAuth("", "", time.Minute*5)
 | |
| 	if err == nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	//err = errors.New("No valid AWS authentication found")
 | |
| 	err = fmt.Errorf("No valid AWS authentication found: %s", err)
 | |
| 	return auth, err
 | |
| }
 | |
| 
 | |
| // EnvAuth creates an Auth based on environment information.
 | |
| // The AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment
 | |
| // variables are used.
 | |
| func EnvAuth() (auth Auth, err error) {
 | |
| 	auth.AccessKey = os.Getenv("AWS_ACCESS_KEY_ID")
 | |
| 	if auth.AccessKey == "" {
 | |
| 		auth.AccessKey = os.Getenv("AWS_ACCESS_KEY")
 | |
| 	}
 | |
| 
 | |
| 	auth.SecretKey = os.Getenv("AWS_SECRET_ACCESS_KEY")
 | |
| 	if auth.SecretKey == "" {
 | |
| 		auth.SecretKey = os.Getenv("AWS_SECRET_KEY")
 | |
| 	}
 | |
| 	if auth.AccessKey == "" {
 | |
| 		err = errors.New("AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY not found in environment")
 | |
| 	}
 | |
| 	if auth.SecretKey == "" {
 | |
| 		err = errors.New("AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY not found in environment")
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // CredentialFileAuth creates and Auth based on a credentials file. The file
 | |
| // contains various authentication profiles for use with AWS.
 | |
| //
 | |
| // The credentials file, which is used by other AWS SDKs, is documented at
 | |
| // http://blogs.aws.amazon.com/security/post/Tx3D6U6WSFGOK2H/A-New-and-Standardized-Way-to-Manage-Credentials-in-the-AWS-SDKs
 | |
| func CredentialFileAuth(filePath string, profile string, expiration time.Duration) (auth Auth, err error) {
 | |
| 	if profile == "" {
 | |
| 		profile = os.Getenv("AWS_DEFAULT_PROFILE")
 | |
| 		if profile == "" {
 | |
| 			profile = os.Getenv("AWS_PROFILE")
 | |
| 			if profile == "" {
 | |
| 				profile = "default"
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if filePath == "" {
 | |
| 		u, err := user.Current()
 | |
| 		if err != nil {
 | |
| 			return auth, err
 | |
| 		}
 | |
| 
 | |
| 		filePath = path.Join(u.HomeDir, ".aws", "credentials")
 | |
| 	}
 | |
| 
 | |
| 	// read the file, then parse the INI
 | |
| 	contents, err := ioutil.ReadFile(filePath)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	profiles := parseINI(string(contents))
 | |
| 	profileData, ok := profiles[profile]
 | |
| 
 | |
| 	if !ok {
 | |
| 		err = errors.New("The credentials file did not contain the profile")
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	keyId, ok := profileData["aws_access_key_id"]
 | |
| 	if !ok {
 | |
| 		err = errors.New("The credentials file did not contain required attribute aws_access_key_id")
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	secretKey, ok := profileData["aws_secret_access_key"]
 | |
| 	if !ok {
 | |
| 		err = errors.New("The credentials file did not contain required attribute aws_secret_access_key")
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	auth.AccessKey = keyId
 | |
| 	auth.SecretKey = secretKey
 | |
| 
 | |
| 	if token, ok := profileData["aws_session_token"]; ok {
 | |
| 		auth.token = token
 | |
| 	}
 | |
| 
 | |
| 	auth.expiration = time.Now().Add(expiration)
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // parseINI takes the contents of a credentials file and returns a map, whose keys
 | |
| // are the various profiles, and whose values are maps of the settings for the
 | |
| // profiles
 | |
| func parseINI(fileContents string) map[string]map[string]string {
 | |
| 	profiles := make(map[string]map[string]string)
 | |
| 
 | |
| 	lines := strings.Split(fileContents, "\n")
 | |
| 
 | |
| 	var currentSection map[string]string
 | |
| 	for _, line := range lines {
 | |
| 		// remove comments, which start with a semi-colon
 | |
| 		if split := strings.Split(line, ";"); len(split) > 1 {
 | |
| 			line = split[0]
 | |
| 		}
 | |
| 
 | |
| 		// check if the line is the start of a profile.
 | |
| 		//
 | |
| 		// for example:
 | |
| 		//     [default]
 | |
| 		//
 | |
| 		// otherwise, check for the proper setting
 | |
| 		//     property=value
 | |
| 		if sectMatch := iniSectionRegexp.FindStringSubmatch(line); len(sectMatch) == 2 {
 | |
| 			currentSection = make(map[string]string)
 | |
| 			profiles[sectMatch[1]] = currentSection
 | |
| 		} else if setMatch := iniSettingRegexp.FindStringSubmatch(line); len(setMatch) == 3 && currentSection != nil {
 | |
| 			currentSection[setMatch[1]] = setMatch[2]
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return profiles
 | |
| }
 | |
| 
 | |
| // Encode takes a string and URI-encodes it in a way suitable
 | |
| // to be used in AWS signatures.
 | |
| func Encode(s string) string {
 | |
| 	encode := false
 | |
| 	for i := 0; i != len(s); i++ {
 | |
| 		c := s[i]
 | |
| 		if c > 127 || !unreserved[c] {
 | |
| 			encode = true
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 	if !encode {
 | |
| 		return s
 | |
| 	}
 | |
| 	e := make([]byte, len(s)*3)
 | |
| 	ei := 0
 | |
| 	for i := 0; i != len(s); i++ {
 | |
| 		c := s[i]
 | |
| 		if c > 127 || !unreserved[c] {
 | |
| 			e[ei] = '%'
 | |
| 			e[ei+1] = hex[c>>4]
 | |
| 			e[ei+2] = hex[c&0xF]
 | |
| 			ei += 3
 | |
| 		} else {
 | |
| 			e[ei] = c
 | |
| 			ei += 1
 | |
| 		}
 | |
| 	}
 | |
| 	return string(e[:ei])
 | |
| }
 | |
| 
 | |
| func dialTimeout(network, addr string) (net.Conn, error) {
 | |
| 	return net.DialTimeout(network, addr, time.Duration(2*time.Second))
 | |
| }
 | |
| 
 | |
| func AvailabilityZone() string {
 | |
| 	transport := http.Transport{Dial: dialTimeout}
 | |
| 	client := http.Client{
 | |
| 		Transport: &transport,
 | |
| 	}
 | |
| 	resp, err := client.Get("http://169.254.169.254/latest/meta-data/placement/availability-zone")
 | |
| 	if err != nil {
 | |
| 		return "unknown"
 | |
| 	} else {
 | |
| 		defer resp.Body.Close()
 | |
| 		body, err := ioutil.ReadAll(resp.Body)
 | |
| 		if err != nil {
 | |
| 			return "unknown"
 | |
| 		} else {
 | |
| 			return string(body)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func InstanceRegion() string {
 | |
| 	az := AvailabilityZone()
 | |
| 	if az == "unknown" {
 | |
| 		return az
 | |
| 	} else {
 | |
| 		region := az[:len(az)-1]
 | |
| 		return region
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func InstanceId() string {
 | |
| 	transport := http.Transport{Dial: dialTimeout}
 | |
| 	client := http.Client{
 | |
| 		Transport: &transport,
 | |
| 	}
 | |
| 	resp, err := client.Get("http://169.254.169.254/latest/meta-data/instance-id")
 | |
| 	if err != nil {
 | |
| 		return "unknown"
 | |
| 	} else {
 | |
| 		defer resp.Body.Close()
 | |
| 		body, err := ioutil.ReadAll(resp.Body)
 | |
| 		if err != nil {
 | |
| 			return "unknown"
 | |
| 		} else {
 | |
| 			return string(body)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func InstanceType() string {
 | |
| 	transport := http.Transport{Dial: dialTimeout}
 | |
| 	client := http.Client{
 | |
| 		Transport: &transport,
 | |
| 	}
 | |
| 	resp, err := client.Get("http://169.254.169.254/latest/meta-data/instance-type")
 | |
| 	if err != nil {
 | |
| 		return "unknown"
 | |
| 	} else {
 | |
| 		defer resp.Body.Close()
 | |
| 		body, err := ioutil.ReadAll(resp.Body)
 | |
| 		if err != nil {
 | |
| 			return "unknown"
 | |
| 		} else {
 | |
| 			return string(body)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func ServerLocalIp() string {
 | |
| 	transport := http.Transport{Dial: dialTimeout}
 | |
| 	client := http.Client{
 | |
| 		Transport: &transport,
 | |
| 	}
 | |
| 	resp, err := client.Get("http://169.254.169.254/latest/meta-data/local-ipv4")
 | |
| 	if err != nil {
 | |
| 		return "127.0.0.1"
 | |
| 	} else {
 | |
| 		defer resp.Body.Close()
 | |
| 		body, err := ioutil.ReadAll(resp.Body)
 | |
| 		if err != nil {
 | |
| 			return "127.0.0.1"
 | |
| 		} else {
 | |
| 			return string(body)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func ServerPublicIp() string {
 | |
| 	transport := http.Transport{Dial: dialTimeout}
 | |
| 	client := http.Client{
 | |
| 		Transport: &transport,
 | |
| 	}
 | |
| 	resp, err := client.Get("http://169.254.169.254/latest/meta-data/public-ipv4")
 | |
| 	if err != nil {
 | |
| 		return "127.0.0.1"
 | |
| 	} else {
 | |
| 		defer resp.Body.Close()
 | |
| 		body, err := ioutil.ReadAll(resp.Body)
 | |
| 		if err != nil {
 | |
| 			return "127.0.0.1"
 | |
| 		} else {
 | |
| 			return string(body)
 | |
| 		}
 | |
| 	}
 | |
| }
 |