1
// Copyright 2013 Canonical Ltd. This software is licensed under the
2
// GNU Lesser General Public License version 3 (see the file COPYING).
13
. "launchpad.net/gocheck"
20
// x509DispatcherFixture records the current x509 dispatcher before a test,
21
// and restores it after. This gives your test the freedom to replace the
22
// dispatcher with test doubles, using any of the rig*Dispatcher functions.
23
// Call the fixture's SetUpTest/TearDownTest methods before/after your test,
24
// or if you have no other setup/teardown methods, just embed the fixture in
26
type x509DispatcherFixture struct {
27
oldDispatcher func(*x509Session, *X509Request) (*x509Response, error)
30
func (suite *x509DispatcherFixture) SetUpTest(c *C) {
31
// Record the original X509 dispatcher. Will be restored at the end of
33
suite.oldDispatcher = _X509Dispatcher
36
func (suite *x509DispatcherFixture) TearDownTest(c *C) {
37
// Restore old dispatcher.
38
_X509Dispatcher = suite.oldDispatcher
41
type x509SessionSuite struct {
45
var _ = Suite(&x509SessionSuite{})
47
// Create a cert and pem file in a temporary dir in /tmp and return the
48
// names of the files. The caller is responsible for cleaning up the files.
49
func makeX509Certificate() (string, string) {
50
// Code is shamelessly stolen from
51
// http://golang.org/src/pkg/crypto/tls/generate_cert.go
52
priv, err := rsa.GenerateKey(rand.Reader, 1024)
54
panic(fmt.Errorf("Failed to generate rsa key: %v", err))
57
// Create a template for x509.CreateCertificate.
59
template := x509.Certificate{
60
SerialNumber: new(big.Int).SetInt64(0),
62
CommonName: "localhost",
63
Organization: []string{"Bogocorp"},
65
NotBefore: now.Add(-5 * time.Minute).UTC(),
66
NotAfter: now.AddDate(1, 0, 0).UTC(), // valid for 1 year.
67
SubjectKeyId: []byte{1, 2, 3, 4},
68
KeyUsage: x509.KeyUsageKeyEncipherment |
69
x509.KeyUsageDigitalSignature,
72
// Create the certificate itself.
73
derBytes, err := x509.CreateCertificate(
74
rand.Reader, &template, &template, &priv.PublicKey, priv)
76
panic(fmt.Errorf("Failed to generate x509 certificate: %v", err))
79
// Write the certificate file out.
80
dirname := os.TempDir() + "/" + MakeRandomString(10)
81
os.Mkdir(dirname, 0700)
82
certFile := dirname + "/cert.pem"
83
certOut, err := os.Create(certFile)
85
panic(fmt.Errorf("Failed to create %s: %v", certFile, err))
87
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
90
// Write the key file out.
91
keyFile := dirname + "/key.pem"
92
keyOut, err := os.OpenFile(
93
keyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
95
panic(fmt.Errorf("Failed to create %s: %v", keyFile, err))
100
Type: "RSA PRIVATE KEY",
101
Bytes: x509.MarshalPKCS1PrivateKey(priv)})
104
return certFile, keyFile
107
func (suite *x509SessionSuite) TestNewX509SessionCreation(c *C) {
108
_, err := newX509Session("subscriptionid", "azure.pem")
112
func (suite *x509SessionSuite) TestComposeURLComposesURLWithRelativePath(c *C) {
113
const subscriptionID = "subscriptionid"
114
const path = "foo/bar"
115
session, err := newX509Session(subscriptionID, "cert.pem")
118
url := session.composeURL(path)
120
c.Check(url, Matches, AZURE_URL+subscriptionID+"/"+path)
123
func (suite *x509SessionSuite) TestComposeURLRejectsAbsolutePath(c *C) {
126
c.Assert(err, NotNil)
127
c.Check(err, ErrorMatches, ".*absolute.*path.*")
129
session, err := newX509Session("subscriptionid", "cert.pem")
132
// This panics because we're passing an absolute path.
133
session.composeURL("/foo")
136
func (suite *x509SessionSuite) TestGetServerErrorProducesServerError(c *C) {
138
status := http.StatusNotFound
139
session, err := newX509Session("subscriptionid", "azure.pem")
142
err = session.getServerError(status, []byte{}, msg)
143
c.Assert(err, NotNil)
145
c.Check(err, ErrorMatches, ".*"+msg+".*")
146
serverError := err.(*ServerError)
147
c.Check(serverError.StatusCode(), Equals, status)
150
func (suite *x509SessionSuite) TestGetServerErrorLikes20x(c *C) {
153
http.StatusNoContent,
155
session, err := newX509Session("subscriptionid", "azure.pem")
158
for _, status := range goodCodes {
159
c.Check(session.getServerError(status, []byte{}, ""), IsNil)
163
func (suite *x509SessionSuite) TestGetServerReturnsErrorsForFailures(c *C) {
165
http.StatusSwitchingProtocols,
166
http.StatusBadRequest,
167
http.StatusPaymentRequired,
168
http.StatusForbidden,
170
http.StatusInternalServerError,
171
http.StatusNotImplemented,
173
session, err := newX509Session("subscriptionid", "azure.pem")
176
for _, status := range badCodes {
177
c.Check(session.getServerError(status, []byte{}, ""), NotNil)
181
func (suite *x509SessionSuite) TestGetIssuesRequest(c *C) {
182
subscriptionID := "subscriptionID"
184
session, err := newX509Session(subscriptionID, "cert.pem")
186
// Record incoming requests, and have them return a given reply.
187
fixedResponse := x509Response{
188
StatusCode: http.StatusOK,
189
Body: []byte("Response body"),
191
rigFixedResponseDispatcher(&fixedResponse)
192
recordedRequests := make([]*X509Request, 0)
193
rigRecordingDispatcher(&recordedRequests)
195
receivedResponse, err := session.get(uri)
198
c.Assert(len(recordedRequests), Equals, 1)
199
request := recordedRequests[0]
200
c.Check(request.URL, Equals, AZURE_URL+subscriptionID+"/"+uri)
201
c.Check(request.Method, Equals, "GET")
202
c.Check(*receivedResponse, DeepEquals, fixedResponse)
205
func (suite *x509SessionSuite) TestGetReportsClientSideError(c *C) {
206
session, err := newX509Session("subscriptionid", "cert.pem")
207
msg := "could not dispatch request"
208
rigFailingDispatcher(fmt.Errorf(msg))
210
body, err := session.get("flop")
211
c.Assert(err, NotNil)
214
c.Check(err, ErrorMatches, ".*"+msg+".*")
217
func (suite *x509SessionSuite) TestGetReportsServerSideError(c *C) {
218
session, err := newX509Session("subscriptionid", "cert.pem")
219
fixedResponse := x509Response{
220
StatusCode: http.StatusForbidden,
221
Body: []byte("Body"),
223
rigFixedResponseDispatcher(&fixedResponse)
225
response, err := session.get("fail")
226
c.Assert(err, NotNil)
228
serverError := err.(*ServerError)
229
c.Check(serverError.StatusCode(), Equals, fixedResponse.StatusCode)
230
c.Check(*response, DeepEquals, fixedResponse)
233
func (suite *x509SessionSuite) TestPostIssuesRequest(c *C) {
234
subscriptionID := "subscriptionID"
236
requestBody := []byte("Request body")
237
requestContentType := "bogusContentType"
238
session, err := newX509Session(subscriptionID, "cert.pem")
240
// Record incoming requests, and have them return a given reply.
241
fixedResponse := x509Response{
242
StatusCode: http.StatusOK,
243
Body: []byte("Response body"),
245
rigFixedResponseDispatcher(&fixedResponse)
246
recordedRequests := make([]*X509Request, 0)
247
rigRecordingDispatcher(&recordedRequests)
249
receivedResponse, err := session.post(uri, requestBody, requestContentType)
252
c.Assert(len(recordedRequests), Equals, 1)
253
request := recordedRequests[0]
254
c.Check(request.URL, Equals, AZURE_URL+subscriptionID+"/"+uri)
255
c.Check(request.Method, Equals, "POST")
256
c.Check(request.ContentType, Equals, requestContentType)
257
c.Check(request.Payload, DeepEquals, requestBody)
258
c.Check(*receivedResponse, DeepEquals, fixedResponse)
261
func (suite *x509SessionSuite) TestPostReportsClientSideError(c *C) {
262
session, err := newX509Session("subscriptionid", "cert.pem")
263
msg := "could not dispatch request"
264
rigFailingDispatcher(fmt.Errorf(msg))
266
body, err := session.post("flop", []byte("body"), "contentType")
267
c.Assert(err, NotNil)
270
c.Check(err, ErrorMatches, ".*"+msg+".*")
273
func (suite *x509SessionSuite) TestPostReportsServerSideError(c *C) {
274
session, err := newX509Session("subscriptionid", "cert.pem")
275
fixedResponse := x509Response{
276
StatusCode: http.StatusForbidden,
277
Body: []byte("Body"),
279
rigFixedResponseDispatcher(&fixedResponse)
281
reponse, err := session.post("fail", []byte("request body"), "contentType")
282
c.Assert(err, NotNil)
284
serverError := err.(*ServerError)
285
c.Check(serverError.StatusCode(), Equals, fixedResponse.StatusCode)
286
c.Check(*reponse, DeepEquals, fixedResponse)
289
func (suite *x509SessionSuite) TestDeleteIssuesRequest(c *C) {
290
subscriptionID := "subscriptionID"
292
session, err := newX509Session(subscriptionID, "cert.pem")
294
// Record incoming requests, and have them return a given reply.
295
fixedResponse := x509Response{StatusCode: http.StatusOK}
296
rigFixedResponseDispatcher(&fixedResponse)
297
recordedRequests := make([]*X509Request, 0)
298
rigRecordingDispatcher(&recordedRequests)
300
response, err := session.delete(uri)
303
c.Check(*response, DeepEquals, fixedResponse)
304
c.Assert(len(recordedRequests), Equals, 1)
305
request := recordedRequests[0]
306
c.Check(request.URL, Equals, AZURE_URL+subscriptionID+"/"+uri)
307
c.Check(request.Method, Equals, "DELETE")
310
func (suite *x509SessionSuite) TestPutIssuesRequest(c *C) {
311
subscriptionID := "subscriptionID"
313
requestBody := []byte("Request body")
314
session, err := newX509Session(subscriptionID, "cert.pem")
316
// Record incoming requests, and have them return a given reply.
317
fixedResponse := x509Response{
318
StatusCode: http.StatusOK,
320
rigFixedResponseDispatcher(&fixedResponse)
321
recordedRequests := make([]*X509Request, 0)
322
rigRecordingDispatcher(&recordedRequests)
324
_, err = session.put(uri, requestBody)
327
c.Assert(len(recordedRequests), Equals, 1)
328
request := recordedRequests[0]
329
c.Check(request.URL, Equals, AZURE_URL+subscriptionID+"/"+uri)
330
c.Check(request.Method, Equals, "PUT")
331
c.Check(request.Payload, DeepEquals, requestBody)