14
15
// Openstack provides an Openstack service double implementation.
15
16
type Openstack struct {
16
17
Identity identityservice.IdentityService
17
Nova *novaservice.Nova
18
Swift *swiftservice.Swift
18
// Keystone v3 supports serving both V2 and V3 at the same time
19
// this will intend to emulate that behavior.
20
FallbackIdentity identityservice.IdentityService
21
Nova *novaservice.Nova
22
Swift *swiftservice.Swift
23
// base url of openstack endpoints, might be required to
24
// simmulate response contents such as the ones from
25
// identity discovery.
29
func (openstack *Openstack) AddUser(user, secret, tennant string) *identityservice.UserInfo {
30
uinfo := openstack.Identity.AddUser(user, secret, tennant)
31
if openstack.FallbackIdentity != nil {
32
_ = openstack.FallbackIdentity.AddUser(user, secret, tennant)
21
37
// New creates an instance of a full Openstack service double.
26
42
openstack = Openstack{
27
43
Identity: identityservice.NewKeyPair(),
45
} else if authMode == identity.AuthUserPassV3 {
46
openstack = Openstack{
47
Identity: identityservice.NewV3UserPass(),
48
FallbackIdentity: identityservice.NewUserPass(),
30
51
openstack = Openstack{
31
Identity: identityservice.NewUserPass(),
52
Identity: identityservice.NewUserPass(),
53
FallbackIdentity: identityservice.NewV3UserPass(),
34
userInfo := openstack.Identity.AddUser(cred.User, cred.Secrets, cred.TenantName)
56
userInfo := openstack.AddUser(cred.User, cred.Secrets, cred.TenantName)
35
57
if cred.TenantName == "" {
36
58
panic("Openstack service double requires a tenant to be specified.")
38
openstack.Nova = novaservice.New(cred.URL, "v2", userInfo.TenantId, cred.Region, openstack.Identity)
60
openstack.Nova = novaservice.New(cred.URL, "v2", userInfo.TenantId, cred.Region, openstack.Identity, openstack.FallbackIdentity)
39
61
// Create the swift service using only the region base so we emulate real world deployments.
40
62
regionParts := strings.Split(cred.Region, ".")
41
63
baseRegion := regionParts[len(regionParts)-1]
42
openstack.Swift = swiftservice.New(cred.URL, "v1", userInfo.TenantId, baseRegion, openstack.Identity)
64
openstack.Swift = swiftservice.New(cred.URL, "v1", userInfo.TenantId, baseRegion, openstack.Identity, openstack.FallbackIdentity)
65
openstack.url = cred.URL
43
66
// Create container and add image metadata endpoint so that product-streams URLs are included
44
67
// in the keystone catalog.
45
68
err := openstack.Swift.AddContainer("imagemetadata")
47
70
panic(fmt.Errorf("setting up image metadata container: %v", err))
49
72
url := openstack.Swift.Endpoints()[0].PublicURL
50
serviceDef := identityservice.Service{
73
serviceDef := identityservice.V2Service{
51
74
Name: "simplestreams",
52
75
Type: "product-streams",
53
76
Endpoints: []identityservice.Endpoint{
54
77
{PublicURL: url + "/imagemetadata", Region: cred.Region},
56
openstack.Identity.AddService(serviceDef)
79
service3Def := identityservice.V3Service{
80
Name: "simplestreams",
81
Type: "product-streams",
82
Endpoints: identityservice.NewV3Endpoints("", "", url+"/imagemetadata", cred.Region),
85
openstack.Identity.AddService(identityservice.Service{V2: serviceDef, V3: service3Def})
57
86
// Add public bucket endpoint so that juju-tools URLs are included in the keystone catalog.
58
serviceDef = identityservice.Service{
87
serviceDef = identityservice.V2Service{
60
89
Type: "juju-tools",
61
90
Endpoints: []identityservice.Endpoint{
62
91
{PublicURL: url, Region: cred.Region},
64
openstack.Identity.AddService(serviceDef)
93
service3Def = identityservice.V3Service{
96
Endpoints: identityservice.NewV3Endpoints("", "", url, cred.Region),
99
openstack.Identity.AddService(identityservice.Service{V2: serviceDef, V3: service3Def})
68
103
// SetupHTTP attaches all the needed handlers to provide the HTTP API for the Openstack service..
69
104
func (openstack *Openstack) SetupHTTP(mux *http.ServeMux) {
70
105
openstack.Identity.SetupHTTP(mux)
106
// If there is a FallbackIdentity service also register its urls.
107
if openstack.FallbackIdentity != nil {
108
openstack.FallbackIdentity.SetupHTTP(mux)
71
110
openstack.Nova.SetupHTTP(mux)
72
111
openstack.Swift.SetupHTTP(mux)
113
// Handle root calls to be able to return auth information or fallback
114
// to Nova root handler in case its not an auth info request.
115
mux.Handle("/", openstack)
119
const authInformationBody = `{"versions": {"values": [{"status": "stable", ` +
120
`"updated": "2015-03-30T00:00:00Z", "media-types": [{"base": "application/json", ` +
121
`"type": "application/vnd.openstack.identity-v3+json"}], "id": "v3.4", "links": ` +
122
`[{"href": "%s/v3/", "rel": "self"}]}, {"status": "stable", "updated": ` +
123
`"2014-04-17T00:00:00Z", "media-types": [{"base": "application/json", ` +
124
`"type": "application/vnd.openstack.identity-v2.0+json"}], "id": "v2.0", ` +
125
`"links": [{"href": "%s/v2.0/", "rel": "self"}, {"href": ` +
126
`"http://docs.openstack.org/", "type": "text/html", "rel": "describedby"}]}]}}`
128
func (openstack *Openstack) ServeHTTP(w http.ResponseWriter, r *http.Request) {
129
if r.URL.Path != "/" {
130
openstack.Nova.HandleRoot(w, r)
133
w.Header().Set("Content-Type", "application/json")
134
body := []byte(fmt.Sprintf(authInformationBody, openstack.url, openstack.url))
135
// workaround for https://code.google.com/p/go/issues/detail?id=4454
136
w.Header().Set("Content-Length", strconv.Itoa(len(body)))
137
w.WriteHeader(http.StatusMultipleChoices)