Skip to content

Commit e45e188

Browse files
committed
more coverage + more endpoints implemented, up to 65.7% coverage now
1 parent 0d5209a commit e45e188

File tree

4 files changed

+170
-14
lines changed

4 files changed

+170
-14
lines changed

app.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
// App encapsulates all of the parameters necessary for starting up
1111
// an aws mock metadata server. These can either be set via command line or directly.
1212
type App struct {
13+
AmiID string
1314
AvailabilityZone string
1415
AppPort string
1516
Hostname string
@@ -36,14 +37,15 @@ func main() {
3637
}
3738

3839
func (app *App) addFlags(fs *pflag.FlagSet) {
39-
fs.StringVar(&app.AvailabilityZone, "availability-zone", app.AvailabilityZone, "Availability zone")
40-
fs.StringVar(&app.AppPort, "app-port", app.AppPort, "Http port")
41-
fs.StringVar(&app.Hostname, "hostname", app.Hostname, "ec2 instance hostname")
42-
fs.StringVar(&app.InstanceID, "instance-id", app.InstanceID, "ec2 instance id")
43-
fs.StringVar(&app.PrivateIp, "private-ip", app.PrivateIp, "Private ip")
44-
fs.StringVar(&app.RoleArn, "role-arn", app.RoleArn, "IAM role Arn")
45-
fs.StringVar(&app.RoleName, "role-name", app.RoleName, "IAM role name")
40+
fs.StringVar(&app.AmiID, "ami-id", app.AmiID, "EC2 Instance AMI ID")
41+
fs.StringVar(&app.AvailabilityZone, "availability-zone", app.AvailabilityZone, "Availability Zone")
42+
fs.StringVar(&app.AppPort, "app-port", app.AppPort, "HTTP Port")
43+
fs.StringVar(&app.Hostname, "hostname", app.Hostname, "EC2 Instance Hostname")
44+
fs.StringVar(&app.InstanceID, "instance-id", app.InstanceID, "EC2 instance id")
45+
fs.StringVar(&app.PrivateIp, "private-ip", app.PrivateIp, "Private IP")
46+
fs.StringVar(&app.RoleArn, "role-arn", app.RoleArn, "IAM Role ARN")
47+
fs.StringVar(&app.RoleName, "role-name", app.RoleName, "IAM Role Name")
4648
fs.BoolVar(&app.Verbose, "verbose", false, "Verbose")
47-
fs.StringVar(&app.VpcID, "vpc-id", app.VpcID, "VPC id")
49+
fs.StringVar(&app.VpcID, "vpc-id", app.VpcID, "VPC ID")
4850
fs.BoolVar(&app.NoSchemeHostRedirects, "no-scheme-host-redirects", app.NoSchemeHostRedirects, "Disable the scheme://host prefix in Location redirect headers")
4951
}

main_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ func TestMain(m *testing.M) {
1515
// Setup the test API
1616
app := &App{}
1717
// Mock parameters
18+
app.AmiID = "ami-asdfasdf"
1819
app.AvailabilityZone = "us-east-1a"
1920
// AppPort not required
2021
app.Hostname = "testhostname"

server.go

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,38 @@ func (app *App) versionSubRouter(sr *mux.Router, version string) {
7373
ii.Handle("", appHandler(app.trailingSlashRedirect))
7474
ii.Handle("/", appHandler(app.instanceIdentityHandler))
7575
ii.Handle("/document", appHandler(app.instanceIdentityDocumentHandler))
76-
// TODO: implement
77-
//ii.Handle("/pkcs7", appHandler(app.instanceIdentityHandler))
78-
//ii.Handle("/signature", appHandler(app.instanceIdentityHandler))
76+
ii.Handle("/document/", appHandler(app.instanceIdentityDocumentHandler))
77+
ii.Handle("/pkcs7", appHandler(app.instanceIdentityPkcs7Handler))
78+
ii.Handle("/pkcs7/", appHandler(app.instanceIdentityPkcs7Handler))
79+
ii.Handle("/signature", appHandler(app.instanceIdentitySignatureHandler))
80+
ii.Handle("/signature/", appHandler(app.instanceIdentitySignatureHandler))
7981

8082
m := sr.PathPrefix("/meta-data").Subrouter()
8183
m.Handle("", appHandler(app.trailingSlashRedirect))
8284
m.Handle("/", appHandler(app.metaDataHandler))
85+
m.Handle("/ami-id", appHandler(app.amiIdHandler))
86+
m.Handle("/ami-id/", appHandler(app.amiIdHandler))
87+
m.Handle("/ami-launch-index", appHandler(app.amiLaunchIndexHandler))
88+
m.Handle("/ami-launch-index/", appHandler(app.amiLaunchIndexHandler))
89+
m.Handle("/ami-manifest-path", appHandler(app.amiManifestPathHandler))
90+
m.Handle("/ami-manifest-path/", appHandler(app.amiManifestPathHandler))
91+
92+
bdm := m.PathPrefix("/block-device-mapping").Subrouter()
93+
bdm.Handle("", appHandler(app.trailingSlashRedirect))
94+
bdm.Handle("/", appHandler(app.blockDeviceMappingHandler))
95+
bdm.Handle("/ami", appHandler(app.blockDeviceMappingAmiHandler))
96+
bdm.Handle("/ami/", appHandler(app.blockDeviceMappingAmiHandler))
97+
bdm.Handle("/root", appHandler(app.blockDeviceMappingRootHandler))
98+
bdm.Handle("/root/", appHandler(app.blockDeviceMappingRootHandler))
99+
100+
m.Handle("/hostname", appHandler(app.hostnameHandler))
101+
m.Handle("/hostname/", appHandler(app.hostnameHandler))
83102
m.Handle("/instance-id", appHandler(app.instanceIDHandler))
103+
m.Handle("/instance-id/", appHandler(app.instanceIDHandler))
84104
m.Handle("/local-hostname", appHandler(app.localHostnameHandler))
105+
m.Handle("/local-hostname/", appHandler(app.localHostnameHandler))
85106
m.Handle("/local-ipv4", appHandler(app.privateIpHandler))
107+
m.Handle("/local-ipv4/", appHandler(app.privateIpHandler))
86108

87109
p := m.PathPrefix("/placement").Subrouter()
88110
p.Handle("/availability-zone", appHandler(app.availabilityZoneHandler))
@@ -100,6 +122,7 @@ func (app *App) versionSubRouter(sr *mux.Router, version string) {
100122
d.Handle("/{path:.*}", appHandler(app.notFoundHandler))
101123
ii.Handle("/{path:.*}", appHandler(app.notFoundHandler))
102124
m.Handle("/{path:.*}", appHandler(app.notFoundHandler))
125+
bdm.Handle("/{path:.*}", appHandler(app.notFoundHandler))
103126
p.Handle("/{path:.*}", appHandler(app.notFoundHandler))
104127
i.Handle("/{path:.*}", appHandler(app.notFoundHandler))
105128
n.Handle("/{path:.*}", appHandler(app.notFoundHandler))
@@ -135,6 +158,7 @@ signature
135158
`)
136159
}
137160

161+
// NOTE: order of keys here differs from real metadata service, in theory most (proper) JSON parsers should be fine with it though...
138162
type InstanceIdentityDocument struct {
139163
AvailabilityZone string `json:"availabilityZone"`
140164
Region string `json:"region"`
@@ -157,13 +181,13 @@ func (app *App) instanceIdentityDocumentHandler(w http.ResponseWriter, r *http.R
157181
AvailabilityZone: app.AvailabilityZone,
158182
Region: app.AvailabilityZone[:len(app.AvailabilityZone)-1],
159183
DevpayProductCodes: nil,
160-
PrivateIp: "127.0.0.1",
184+
PrivateIp: app.PrivateIp,
161185
Version: "2010-08-31",
162-
InstanceId: "i-wxyz1234",
186+
InstanceId: app.InstanceID,
163187
BillingProducts: nil,
164188
InstanceType: "t2.micro",
165189
AccountId: "1234567890",
166-
ImageId: "ami-123456",
190+
ImageId: app.AmiID,
167191
PendingTime: "2016-04-15T12:14:15Z",
168192
Architecture: "x86_64",
169193
KernelId: nil,
@@ -175,7 +199,20 @@ func (app *App) instanceIdentityDocumentHandler(w http.ResponseWriter, r *http.R
175199
}
176200
}
177201

202+
func (app *App) instanceIdentityPkcs7Handler(w http.ResponseWriter, r *http.Request) {
203+
// TODO: adjust output to suit
204+
write(w, `
205+
`)
206+
}
207+
208+
func (app *App) instanceIdentitySignatureHandler(w http.ResponseWriter, r *http.Request) {
209+
// TODO: adjust output to suit
210+
write(w, `
211+
`)
212+
}
213+
178214
func (app *App) metaDataHandler(w http.ResponseWriter, r *http.Request) {
215+
// TODO: if IAM Role/Instance Profile is disabled, don't add iam/ to the list (same behavior as real metadata service)
179216
write(w, `ami-id
180217
ami-launch-index
181218
ami-manifest-path
@@ -199,6 +236,36 @@ security-groups
199236
services/`)
200237
}
201238

239+
func (app *App) amiIdHandler(w http.ResponseWriter, r *http.Request) {
240+
write(w, app.AmiID)
241+
}
242+
243+
func (app *App) amiLaunchIndexHandler(w http.ResponseWriter, r *http.Request) {
244+
write(w, "0")
245+
}
246+
247+
func (app *App) amiManifestPathHandler(w http.ResponseWriter, r *http.Request) {
248+
write(w, "(unknown)")
249+
}
250+
251+
func (app *App) blockDeviceMappingHandler(w http.ResponseWriter, r *http.Request) {
252+
// Not exposing any extra volumes for now, this is pretty standard for an EBS backed EC2 instance.
253+
write(w, `ami
254+
root`)
255+
}
256+
257+
func (app *App) blockDeviceMappingAmiHandler(w http.ResponseWriter, r *http.Request) {
258+
write(w, "/dev/xvda")
259+
}
260+
261+
func (app *App) blockDeviceMappingRootHandler(w http.ResponseWriter, r *http.Request) {
262+
write(w, "/dev/xvda")
263+
}
264+
265+
func (app *App) hostnameHandler(w http.ResponseWriter, r *http.Request) {
266+
write(w, app.Hostname)
267+
}
268+
202269
func (app *App) instanceIDHandler(w http.ResponseWriter, r *http.Request) {
203270
write(w, app.InstanceID)
204271
}

server_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,42 @@ signature
105105
doBodyTest(t, "/latest/dynamic/instance-identity/", expected_body)
106106
}
107107

108+
func TestLatestDynamicInstanceIdentityDocument(t *testing.T) {
109+
expected_body := `{
110+
"instanceId" : "i-asdfasdf",
111+
"billingProducts" : null,
112+
"imageId" : "ami-asdfasdf",
113+
"architecture" : "x86_64",
114+
"pendingTime" : "",
115+
"instanceType" : "t2.micro",
116+
"accountId" : "",
117+
"kernelId" : null,
118+
"ramdiskId" : null,
119+
"region" : "us-east-1",
120+
"version" : "2010-08-31",
121+
"availabilityZone" : "us-east-1a",
122+
"devpayProductCodes" : null,
123+
"privateIp" : "10.20.30.40"
124+
}`
125+
126+
doBodyTest(t, "/latest/dynamic/instance-identity/document", expected_body)
127+
doBodyTest(t, "/latest/dynamic/instance-identity/document/", expected_body)
128+
}
129+
130+
func TestLatestDynamicInstanceIdentityPkcs7(t *testing.T) {
131+
expected_body := `TODO-correct-output`
132+
133+
doBodyTest(t, "/latest/dynamic/instance-identity/pkcs7", expected_body)
134+
doBodyTest(t, "/latest/dynamic/instance-identity/pkcs7/", expected_body)
135+
}
136+
137+
func TestLatestDynamicInstanceIdentitySignature(t *testing.T) {
138+
expected_body := `TODO-correct-output`
139+
140+
doBodyTest(t, "/latest/dynamic/instance-identity/signature", expected_body)
141+
doBodyTest(t, "/latest/dynamic/instance-identity/signature/", expected_body)
142+
}
143+
108144
func TestLatestMetaData(t *testing.T) {
109145
// NOTE: iam/ only appears if there is an IAM Instance Profile attached to the instance. assuming available for simulation purposes for now.
110146
expected_body := `ami-id
@@ -133,6 +169,56 @@ services/`
133169
doBodyTest(t, "/latest/meta-data/", expected_body)
134170
}
135171

172+
func TestLatestMetaDataAmiId(t *testing.T) {
173+
expected_body := `ami-asdfasdf`
174+
175+
doBodyTest(t, "/latest/meta-data/ami-id", expected_body)
176+
doBodyTest(t, "/latest/meta-data/ami-id/", expected_body)
177+
}
178+
179+
func TestLatestMetaDataAmiLaunchIndex(t *testing.T) {
180+
expected_body := `0`
181+
182+
doBodyTest(t, "/latest/meta-data/ami-launch-index", expected_body)
183+
doBodyTest(t, "/latest/meta-data/ami-launch-index/", expected_body)
184+
}
185+
186+
func TestLatestMetaDataAmiManifestPath(t *testing.T) {
187+
expected_body := `(unknown)`
188+
189+
doBodyTest(t, "/latest/meta-data/ami-manifest-path", expected_body)
190+
doBodyTest(t, "/latest/meta-data/ami-manifest-path/", expected_body)
191+
}
192+
193+
func TestLatestMetaDataBlockDeviceMapping(t *testing.T) {
194+
expected_body := `ami
195+
root`
196+
197+
doRedirectTest(t, "/latest/meta-data/block-device-mapping", "/latest/meta-data/block-device-mapping/")
198+
doBodyTest(t, "/latest/meta-data/block-device-mapping/", expected_body)
199+
}
200+
201+
func TestLatestMetaDataBlockDeviceMappingAmi(t *testing.T) {
202+
expected_body := `/dev/xvda`
203+
204+
doBodyTest(t, "/latest/meta-data/block-device-mapping/ami", expected_body)
205+
doBodyTest(t, "/latest/meta-data/block-device-mapping/ami/", expected_body)
206+
}
207+
208+
func TestLatestMetaDataBlockDeviceMappingRoot(t *testing.T) {
209+
expected_body := `/dev/xvda`
210+
211+
doBodyTest(t, "/latest/meta-data/block-device-mapping/root", expected_body)
212+
doBodyTest(t, "/latest/meta-data/block-device-mapping/root/", expected_body)
213+
}
214+
215+
func TestLatestMetaDataHostname(t *testing.T) {
216+
expected_body := `testhostname`
217+
218+
doBodyTest(t, "/latest/meta-data/hostname", expected_body)
219+
doBodyTest(t, "/latest/meta-data/hostname/", expected_body)
220+
}
221+
136222
func TestLatestUserData(t *testing.T) {
137223
// TODO: /latest/user-data returns a 404 if none exists... or if one exists, will return it?
138224
// should we expose this in the API? not implemented right now. could be useful...

0 commit comments

Comments
 (0)