/
ec2info.go
147 lines (126 loc) · 3.37 KB
/
ec2info.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package aws
import (
"log"
"net/http"
"os"
"strconv"
"sync"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
)
var describerClient InstanceDescriber
var (
co ClientOptions
coInit sync.Once
sdkSession *session.Session
sdkSessionInit sync.Once
)
// ClientOptions -
type ClientOptions struct {
Timeout time.Duration
}
// Ec2Info -
type Ec2Info struct {
describer func() InstanceDescriber
metaClient *Ec2Meta
cache map[string]interface{}
}
// InstanceDescriber - A subset of ec2iface.EC2API that we can use to call EC2.DescribeInstances
type InstanceDescriber interface {
DescribeInstances(*ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error)
}
// GetClientOptions - Centralised reading of AWS_TIMEOUT
// ... but cannot use in vault/auth.go as different strconv.Atoi error handling
func GetClientOptions() ClientOptions {
coInit.Do(func() {
timeout := os.Getenv("AWS_TIMEOUT")
if timeout != "" {
t, err := strconv.Atoi(timeout)
if err != nil {
log.Fatalf("Invalid AWS_TIMEOUT value '%s' - must be an integer\n", timeout)
}
co.Timeout = time.Duration(t) * time.Millisecond
}
})
return co
}
// SDKSession -
func SDKSession() *session.Session {
sdkSessionInit.Do(func() {
options := GetClientOptions()
timeout := options.Timeout
if timeout == 0 {
timeout = 500 * time.Millisecond
}
config := aws.NewConfig()
config = config.WithHTTPClient(&http.Client{Timeout: timeout})
// Waiting for https://github.com/aws/aws-sdk-go/issues/1103
metaClient := NewEc2Meta(options)
metaRegion := metaClient.Region()
_, default1 := os.LookupEnv("AWS_REGION")
_, default2 := os.LookupEnv("AWS_DEFAULT_REGION")
if metaRegion != "unknown" && !default1 && !default2 {
config = config.WithRegion(metaRegion)
}
sdkSession = session.Must(session.NewSessionWithOptions(session.Options{
Config: *config,
SharedConfigState: session.SharedConfigEnable,
}))
})
return sdkSession
}
// NewEc2Info -
func NewEc2Info(options ClientOptions) *Ec2Info {
metaClient := NewEc2Meta(options)
return &Ec2Info{
describer: func() InstanceDescriber {
if describerClient == nil {
describerClient = ec2.New(SDKSession())
}
return describerClient
},
metaClient: metaClient,
cache: make(map[string]interface{}),
}
}
// Tag -
func (e *Ec2Info) Tag(tag string, def ...string) string {
output := e.describeInstance()
if output == nil {
return returnDefault(def)
}
if len(output.Reservations) > 0 &&
len(output.Reservations[0].Instances) > 0 &&
len(output.Reservations[0].Instances[0].Tags) > 0 {
for _, v := range output.Reservations[0].Instances[0].Tags {
if *v.Key == tag {
return *v.Value
}
}
}
return returnDefault(def)
}
func (e *Ec2Info) describeInstance() (output *ec2.DescribeInstancesOutput) {
// cache the InstanceDescriber here
e.describer()
if e.metaClient.nonAWS {
return nil
}
if cached, ok := e.cache["DescribeInstances"]; ok {
output = cached.(*ec2.DescribeInstancesOutput)
} else {
instanceID := e.metaClient.Meta("instance-id")
input := &ec2.DescribeInstancesInput{
InstanceIds: aws.StringSlice([]string{instanceID}),
}
var err error
output, err = e.describer().DescribeInstances(input)
if err != nil {
return nil
}
e.cache["DescribeInstances"] = output
}
return
}