forked from ciao-project/ciao
/
service.go
232 lines (192 loc) · 6.09 KB
/
service.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
// Copyright (c) 2016 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package service
import (
"errors"
"fmt"
"io"
"net/http"
"time"
"github.com/01org/ciao/ciao-image/datastore"
"github.com/01org/ciao/openstack/identity"
"github.com/01org/ciao/openstack/image"
"github.com/01org/ciao/ssntp/uuid"
"github.com/gorilla/mux"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack"
)
// ImageService is the context for the image service implementation.
type ImageService struct {
ds datastore.DataStore
}
// CreateImage will create an empty image in the image datastore.
func (is ImageService) CreateImage(req image.CreateImageRequest) (image.DefaultResponse, error) {
// create an ImageInfo struct and store it in our image
// datastore.
i := datastore.Image{
ID: uuid.Generate().String(),
State: datastore.Created,
Name: req.Name,
CreateTime: time.Now(),
}
err := is.ds.CreateImage(i)
if err != nil {
return image.DefaultResponse{}, err
}
return image.DefaultResponse{
Status: image.Queued,
CreatedAt: i.CreateTime,
Tags: make([]string, 0),
Locations: make([]string, 0),
DiskFormat: image.Raw,
Visibility: i.Visibility(),
Self: fmt.Sprintf("/v2/images/%s", i.ID),
Protected: false,
ID: i.ID,
File: fmt.Sprintf("/v2/images/%s/file", i.ID),
Schema: "/v2/schemas/image",
Name: &i.Name,
}, nil
}
func createImageResponse(img datastore.Image) (image.DefaultResponse, error) {
return image.DefaultResponse{
Status: img.State.Status(),
CreatedAt: img.CreateTime,
Tags: make([]string, 0),
Locations: make([]string, 0),
DiskFormat: image.DiskFormat(img.Type),
Visibility: img.Visibility(),
Self: fmt.Sprintf("/v2/images/%s", img.ID),
Protected: false,
ID: img.ID,
File: fmt.Sprintf("/v2/images/%s/file", img.ID),
Schema: "/v2/schemas/image",
Name: &img.Name,
}, nil
}
// ListImages will return a list of all the images in the datastore.
func (is ImageService) ListImages() ([]image.DefaultResponse, error) {
var response []image.DefaultResponse
images, err := is.ds.GetAllImages()
if err != nil {
return response, err
}
for _, img := range images {
i, _ := createImageResponse(img)
response = append(response, i)
}
return response, nil
}
// UploadImage will upload a raw image data and update its status.
func (is ImageService) UploadImage(imageID string, body io.Reader) (image.UploadImageResponse, error) {
var response image.UploadImageResponse
err := is.ds.UploadImage(imageID, body)
if err != nil {
return response, err
}
response.ImageID = imageID
return response, nil
}
// GetImage will get the raw image data
func (is ImageService) GetImage(imageID string) (image.DefaultResponse, error) {
var response image.DefaultResponse
image, err := is.ds.GetImage(imageID)
if err != nil {
return response, err
}
response, _ = createImageResponse(image)
return response, nil
}
// Config is required to setup the API context for the image service.
type Config struct {
// Port represents the http port that should be used for the service.
Port int
// HTTPSCACert is the path to the http ca cert to use.
HTTPSCACert string
// HTTPSKey is the path to the https cert key.
HTTPSKey string
// DataStore is an interface to a persistent datastore for the image raw data.
RawDataStore datastore.RawDataStore
// MetaDataStore is an interface to a persistent datastore for the image meta data.
MetaDataStore datastore.MetaDataStore
// IdentityEndpoint is the location of the keystone service.
IdentityEndpoint string
// Username is the service username for the image service in keystone.
Username string
// Password is the password for the image service user in keystone.
Password string
}
func getIdentityClient(config Config) (*gophercloud.ServiceClient, error) {
opt := gophercloud.AuthOptions{
IdentityEndpoint: config.IdentityEndpoint + "/v3/",
Username: config.Username,
Password: config.Password,
DomainID: "default",
AllowReauth: true,
}
provider, err := openstack.AuthenticatedClient(opt)
if err != nil {
return nil, err
}
v3client := openstack.NewIdentityV3(provider)
if v3client == nil {
return nil, errors.New("Unable to get keystone V3 client")
}
return v3client, nil
}
// Start will get the Image API endpoints from the OpenStack image api,
// then wrap them in keystone validation. It will then start the https
// service.
func Start(config Config) error {
is := ImageService{ds: &datastore.ImageCache{}}
err := is.ds.Init(config.RawDataStore, config.MetaDataStore)
if err != nil {
return err
}
apiConfig := image.APIConfig{
Port: config.Port,
ImageService: is,
}
// get our routes.
r := image.Routes(apiConfig)
// setup identity for these routes.
validServices := []identity.ValidService{
{ServiceType: "image", ServiceName: "ciao"},
{ServiceType: "image", ServiceName: "glance"},
}
validAdmins := []identity.ValidAdmin{
{Project: "service", Role: "admin"},
{Project: "admin", Role: "admin"},
}
client, err := getIdentityClient(config)
if err != nil {
return err
}
err = r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
h := identity.Handler{
Client: client,
Next: route.GetHandler(),
ValidServices: validServices,
ValidAdmins: validAdmins,
}
route.Handler(h)
return nil
})
if err != nil {
return err
}
// start service.
service := fmt.Sprintf(":%d", config.Port)
return http.ListenAndServeTLS(service, config.HTTPSCACert, config.HTTPSKey, r)
}