@@ -17,6 +17,7 @@ import (
17
17
"sync"
18
18
"time"
19
19
20
+ "v.io/v23/logging"
20
21
"v.io/x/ref/lib/stats"
21
22
"v.io/x/ref/runtime/internal/cloudvm/cloudpaths"
22
23
)
@@ -56,65 +57,88 @@ const (
56
57
)
57
58
58
59
var (
59
- onceAWS sync.Once
60
- onAWS bool
60
+ onceAWS sync.Once
61
+ onAWS bool
62
+ onIMDSv2 bool
61
63
)
62
64
63
65
// OnAWS returns true if this process is running on Amazon Web Services.
64
66
// If true, the the stats variables AWSAccountIDStatName and GCPRegionStatName
65
67
// are set.
66
- func OnAWS (ctx context.Context , timeout time.Duration ) bool {
68
+ func OnAWS (ctx context.Context , logger logging. Logger , timeout time.Duration ) bool {
67
69
onceAWS .Do (func () {
68
- onAWS = awsInit (ctx , timeout )
70
+ onAWS , onIMDSv2 = awsInit (ctx , logger , timeout )
71
+ logger .VI (1 ).Infof ("OnAWS: onAWS: %v, onIMDSv2: %v" , onAWS , onIMDSv2 )
69
72
})
70
73
return onAWS
71
74
}
72
75
73
76
// AWSPublicAddrs returns the current public IP of this AWS instance.
77
+ // Must be called after OnAWS.
74
78
func AWSPublicAddrs (ctx context.Context , timeout time.Duration ) ([]net.Addr , error ) {
75
- return awsGetAddr (ctx , awsExternalURL (), timeout )
79
+ return awsGetAddr (ctx , onIMDSv2 , awsExternalURL (), timeout )
76
80
}
77
81
78
82
// AWSPrivateAddrs returns the current private Addrs of this AWS instance.
83
+ // Must be called after OnAWS.
79
84
func AWSPrivateAddrs (ctx context.Context , timeout time.Duration ) ([]net.Addr , error ) {
80
- return awsGetAddr (ctx , awsInternalURL (), timeout )
85
+ return awsGetAddr (ctx , onIMDSv2 , awsInternalURL (), timeout )
81
86
}
82
87
83
- func awsGet (ctx context.Context , url string , timeout time.Duration ) ([]byte , error ) {
88
+ func awsGet (ctx context.Context , imdsv2 bool , url string , timeout time.Duration ) ([]byte , error ) {
84
89
client := & http.Client {Timeout : timeout }
85
- token , err := awsSetIMDSv2Token (ctx , awsTokenURL (), timeout )
86
- if err != nil {
87
- return nil , err
90
+ var token string
91
+ var err error
92
+ if imdsv2 {
93
+ token , err = awsSetIMDSv2Token (ctx , awsTokenURL (), timeout )
94
+ if err != nil {
95
+ return nil , err
96
+ }
88
97
}
89
98
req , err := http .NewRequestWithContext (ctx , "GET" , url , nil )
90
- req .Header .Add ("X-aws-ec2-metadata-token" , token )
91
99
if err != nil {
92
100
return nil , err
93
101
}
102
+ if len (token ) > 0 {
103
+ req .Header .Add ("X-aws-ec2-metadata-token" , token )
104
+ }
94
105
resp , err := client .Do (req )
95
106
if err != nil {
96
107
return nil , err
97
108
}
98
109
defer resp .Body .Close ()
99
110
if resp .StatusCode != 200 {
100
- return nil , err
111
+ return nil , fmt . Errorf ( "HTTP Error: %v %v" , url , resp . StatusCode )
101
112
}
102
113
if server := resp .Header ["Server" ]; len (server ) != 1 || server [0 ] != "EC2ws" {
103
114
return nil , fmt .Errorf ("wrong headers" )
104
115
}
105
116
return ioutil .ReadAll (resp .Body )
106
117
}
107
118
108
- // awsInit returns true if it can access AWS project metadata. It also
119
+ // awsInit returns true if it can access AWS project metadata and the version
120
+ // of the metadata service it was able to access. It also
109
121
// creates two stats variables with the account ID and zone.
110
- func awsInit (ctx context.Context , timeout time.Duration ) bool {
111
- body , err := awsGet (ctx , awsIdentityDocURL (), timeout )
122
+ func awsInit (ctx context.Context , logger logging.Logger , timeout time.Duration ) (bool , bool ) {
123
+ v2 := false
124
+ // Try the v1 service first since it should always work unless v2
125
+ // is specifically configured (and hence v1 is disabled), in which
126
+ // case the expectation is that it fails fast with a 4xx HTTP error.
127
+ body , err := awsGet (ctx , false , awsIdentityDocURL (), timeout )
112
128
if err != nil {
113
- return false
129
+ logger .VI (1 ).Infof ("failed to access v1 metadata service: %v" , err )
130
+ // can't access v1, try v2.
131
+ body , err = awsGet (ctx , true , awsIdentityDocURL (), timeout )
132
+ if err != nil {
133
+ logger .VI (1 ).Infof ("failed to access v2 metadata service: %v" , err )
134
+ return false , false
135
+ }
136
+ v2 = true
114
137
}
115
138
doc := map [string ]interface {}{}
116
139
if err := json .Unmarshal (body , & doc ); err != nil {
117
- return false
140
+ logger .VI (1 ).Infof ("failed to unmarshal metadata service response: %s: %v" , body , err )
141
+ return false , false
118
142
}
119
143
found := 0
120
144
for _ , v := range []struct {
@@ -130,11 +154,11 @@ func awsInit(ctx context.Context, timeout time.Duration) bool {
130
154
}
131
155
}
132
156
}
133
- return found == 2
157
+ return found == 2 , v2
134
158
}
135
159
136
- func awsGetAddr (ctx context.Context , url string , timeout time.Duration ) ([]net.Addr , error ) {
137
- body , err := awsGet (ctx , url , timeout )
160
+ func awsGetAddr (ctx context.Context , imdsv2 bool , url string , timeout time.Duration ) ([]net.Addr , error ) {
161
+ body , err := awsGet (ctx , imdsv2 , url , timeout )
138
162
if err != nil {
139
163
return nil , err
140
164
}
0 commit comments