Add auth package
Includes AuthOpts struct and AuthRef interface, plus an Identity v2 password auth implementation. Note: the examples work, the objectstore_test is broken, will be fixed along with additional session and auth tests. Change-Id: I77b07c92586c37e855b466e18dea133a4a938aaa
This commit is contained in:
parent
ef7386d2c4
commit
9de84b3c5d
26
README.md
26
README.md
@ -3,18 +3,32 @@ OpenStack Golang Client
|
||||
|
||||
NOTE(dtroyer) Apr 2015: This repo is under heavy revision as it is being revived.
|
||||
|
||||
stackforge/golang-client is yet another implementation of [OpenStack]
|
||||
`stackforge/golang-client` is an implementation of [OpenStack]
|
||||
(http://www.openstack.org/) API client in [Go language](http://golang.org).
|
||||
The code follows OpenStack licensing and borrows its infrastructure for code
|
||||
hosting. It currently implements [Identity Service v2]
|
||||
The code follows OpenStack licensing and uses the Stackforge infrastructure
|
||||
for hosting. It currently implements [Identity Service v2]
|
||||
(http://docs.openstack.org/api/openstack-identity-service/2.0/content/)
|
||||
and [Object Storage v1]
|
||||
(http://docs.openstack.org/api/openstack-object-storage/1.0/content/).
|
||||
Some API calls are not implemented initially, but the intention is to expand
|
||||
the lib over time (where pragmatic).
|
||||
|
||||
The initial focus is on building a solid core REST Session and OpenStack
|
||||
authentication on which to build the usual API interfaces. The architecture
|
||||
if the `Session` and authentication is similar to that used in the current
|
||||
Python Keystone client library: The `Session` object contains the HTTP
|
||||
interface methods and an authentication object that provides access to
|
||||
the auth token and service catalog.
|
||||
|
||||
Current State
|
||||
-------------
|
||||
Code maturity is considered experimental.
|
||||
|
||||
* The new Session object is functional and used by most of the code now.
|
||||
* The examples work.
|
||||
* The image tests work.
|
||||
* The obejct store tests do not work.
|
||||
* identity/v2/auth.go is now unused, will be kept around for a short time
|
||||
for easier reference.
|
||||
|
||||
Installation
|
||||
------------
|
||||
Use `go get git.openstack.org/stackforge/golang-client.git`. Or alternatively,
|
||||
@ -53,7 +67,7 @@ Apache v2.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
The code repository borrows OpenStack StackForge infrastructure.
|
||||
The code repository utilizes the OpenStack StackForge infrastructure.
|
||||
Please use the [recommended workflow]
|
||||
(http://docs.openstack.org/infra/manual/developers.html#development-workflow). If you are not a member yet,
|
||||
please consider joining as an [OpenStack contributor]
|
||||
|
@ -16,8 +16,10 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.openstack.org/stackforge/golang-client.git/identity/v2"
|
||||
// "git.openstack.org/stackforge/golang-client.git/identity/v2"
|
||||
"time"
|
||||
|
||||
"git.openstack.org/stackforge/golang-client.git/openstack"
|
||||
)
|
||||
|
||||
// Authentication examples.
|
||||
@ -26,42 +28,51 @@ func main() {
|
||||
|
||||
// Authenticate with just a username and password. The returned token is
|
||||
// unscoped to a tenant.
|
||||
auth, err := identity.AuthUserName(config.Host,
|
||||
config.Username,
|
||||
config.Password)
|
||||
creds := openstack.AuthOpts{
|
||||
AuthUrl: config.Host,
|
||||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
}
|
||||
auth, err := openstack.DoAuthRequest(creds)
|
||||
if err != nil {
|
||||
fmt.Println("There was an error authenticating:", err)
|
||||
fmt.Println("Error authenticating username/password:", err)
|
||||
return
|
||||
}
|
||||
if !auth.Access.Token.Expires.After(time.Now()) {
|
||||
if !auth.GetExpiration().After(time.Now()) {
|
||||
fmt.Println("There was an error. The auth token has an invalid expiration.")
|
||||
return
|
||||
}
|
||||
|
||||
// Authenticate with a username, password, tenant name.
|
||||
auth, err = identity.AuthUserNameTenantName(config.Host,
|
||||
config.Username,
|
||||
config.Password,
|
||||
config.ProjectName)
|
||||
// Authenticate with a project name, username, password.
|
||||
creds = openstack.AuthOpts{
|
||||
AuthUrl: config.Host,
|
||||
Project: config.ProjectName,
|
||||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
}
|
||||
auth, err = openstack.DoAuthRequest(creds)
|
||||
if err != nil {
|
||||
fmt.Println("There was an error authenticating:", err)
|
||||
fmt.Println("Error authenticating project/username/password:", err)
|
||||
return
|
||||
}
|
||||
if !auth.Access.Token.Expires.After(time.Now()) {
|
||||
if !auth.GetExpiration().After(time.Now()) {
|
||||
fmt.Println("There was an error. The auth token has an invalid expiration.")
|
||||
return
|
||||
}
|
||||
|
||||
// Authenticate with a username, password, tenant id.
|
||||
auth, err = identity.AuthUserNameTenantId(config.Host,
|
||||
config.Username,
|
||||
config.Password,
|
||||
config.ProjectID)
|
||||
// Authenticate with a project id, username, password.
|
||||
creds = openstack.AuthOpts{
|
||||
AuthUrl: config.Host,
|
||||
Project: config.ProjectID,
|
||||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
}
|
||||
auth, err = openstack.DoAuthRequest(creds)
|
||||
if err != nil {
|
||||
fmt.Println("There was an error authenticating:", err)
|
||||
fmt.Println("Error authenticating project/username/password:", err)
|
||||
return
|
||||
}
|
||||
if !auth.Access.Token.Expires.After(time.Now()) {
|
||||
if !auth.GetExpiration().After(time.Now()) {
|
||||
fmt.Println("There was an error. The auth token has an invalid expiration.")
|
||||
return
|
||||
}
|
||||
|
@ -18,10 +18,12 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.openstack.org/stackforge/golang-client.git/identity/v2"
|
||||
"git.openstack.org/stackforge/golang-client.git/objectstorage/v1"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.openstack.org/stackforge/golang-client.git/objectstorage/v1"
|
||||
"git.openstack.org/stackforge/golang-client.git/openstack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -29,45 +31,52 @@ func main() {
|
||||
|
||||
// Before working with object storage we need to authenticate with a project
|
||||
// that has active object storage.
|
||||
auth, err := identity.AuthUserNameTenantName(config.Host,
|
||||
config.Username,
|
||||
config.Password,
|
||||
config.ProjectName)
|
||||
// Authenticate with a project name, username, password.
|
||||
creds := openstack.AuthOpts{
|
||||
AuthUrl: config.Host,
|
||||
Project: config.ProjectName,
|
||||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
}
|
||||
auth, err := openstack.DoAuthRequest(creds)
|
||||
if err != nil {
|
||||
panicString := fmt.Sprint("There was an error authenticating:", err)
|
||||
panic(panicString)
|
||||
}
|
||||
if !auth.Access.Token.Expires.After(time.Now()) {
|
||||
if !auth.GetExpiration().After(time.Now()) {
|
||||
panic("There was an error. The auth token has an invalid expiration.")
|
||||
}
|
||||
|
||||
// Find the endpoint for object storage.
|
||||
url := ""
|
||||
for _, svc := range auth.Access.ServiceCatalog {
|
||||
if svc.Type == "object-store" {
|
||||
url = svc.Endpoints[0].PublicURL + "/"
|
||||
break
|
||||
}
|
||||
}
|
||||
if url == "" {
|
||||
url, err := auth.GetEndpoint("object-store", "")
|
||||
if url == "" || err != nil {
|
||||
panic("object-store url not found during authentication")
|
||||
}
|
||||
|
||||
hdr, err := objectstorage.GetAccountMeta(url, auth.Access.Token.Id)
|
||||
// Make a new client with these creds
|
||||
sess, err := openstack.NewSession(nil, auth, nil)
|
||||
if err != nil {
|
||||
panicString := fmt.Sprint("Error crating new Session:", err)
|
||||
panic(panicString)
|
||||
}
|
||||
|
||||
hdr, err := objectstorage.GetAccountMeta(sess, url)
|
||||
if err != nil {
|
||||
panicString := fmt.Sprint("There was an error getting account metadata:", err)
|
||||
panic(panicString)
|
||||
}
|
||||
_ = hdr
|
||||
|
||||
// Create a new container.
|
||||
if err = objectstorage.PutContainer(url+config.Container, auth.Access.Token.Id,
|
||||
"X-Log-Retention", "true"); err != nil {
|
||||
var headers http.Header = http.Header{}
|
||||
headers.Add("X-Log-Retention", "true")
|
||||
if err = objectstorage.PutContainer(sess, url+"/"+config.Container, headers); err != nil {
|
||||
panicString := fmt.Sprint("PutContainer Error:", err)
|
||||
panic(panicString)
|
||||
}
|
||||
|
||||
// Get a list of all the containers at the selected endoint.
|
||||
containersJson, err := objectstorage.ListContainers(0, "", url, auth.Access.Token.Id)
|
||||
containersJson, err := objectstorage.ListContainers(sess, 0, "", url)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -93,12 +102,13 @@ func main() {
|
||||
}
|
||||
|
||||
// Set and Get container metadata.
|
||||
if err = objectstorage.SetContainerMeta(url+config.Container, auth.Access.Token.Id,
|
||||
"X-Container-Meta-fubar", "false"); err != nil {
|
||||
headers = http.Header{}
|
||||
headers.Add("X-Container-Meta-fubar", "false")
|
||||
if err = objectstorage.SetContainerMeta(sess, url+"/"+config.Container, headers); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
hdr, err = objectstorage.GetContainerMeta(url+config.Container, auth.Access.Token.Id)
|
||||
hdr, err = objectstorage.GetContainerMeta(sess, url+"/"+config.Container)
|
||||
if err != nil {
|
||||
panicString := fmt.Sprint("GetContainerMeta Error:", err)
|
||||
panic(panicString)
|
||||
@ -115,13 +125,14 @@ func main() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
headers = http.Header{}
|
||||
headers.Add("X-Container-Meta-fubar", "false")
|
||||
object := config.Container + "/" + srcFile
|
||||
if err = objectstorage.PutObject(&fContent, url+object, auth.Access.Token.Id,
|
||||
"X-Object-Meta-fubar", "false"); err != nil {
|
||||
if err = objectstorage.PutObject(sess, &fContent, url+"/"+object, headers); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
objectsJson, err := objectstorage.ListObjects(0, "", "", "", "",
|
||||
url+config.Container, auth.Access.Token.Id)
|
||||
objectsJson, err := objectstorage.ListObjects(sess, 0, "", "", "", "",
|
||||
url+"/"+config.Container)
|
||||
|
||||
type objectType struct {
|
||||
Name, Hash, Content_type, Last_modified string
|
||||
@ -143,12 +154,13 @@ func main() {
|
||||
}
|
||||
|
||||
// Manage object metadata
|
||||
if err = objectstorage.SetObjectMeta(url+object, auth.Access.Token.Id,
|
||||
"X-Object-Meta-fubar", "true"); err != nil {
|
||||
headers = http.Header{}
|
||||
headers.Add("X-Object-Meta-fubar", "true")
|
||||
if err = objectstorage.SetObjectMeta(sess, url+"/"+object, headers); err != nil {
|
||||
panicString := fmt.Sprint("SetObjectMeta Error:", err)
|
||||
panic(panicString)
|
||||
}
|
||||
hdr, err = objectstorage.GetObjectMeta(url+object, auth.Access.Token.Id)
|
||||
hdr, err = objectstorage.GetObjectMeta(sess, url+"/"+object)
|
||||
if err != nil {
|
||||
panicString := fmt.Sprint("GetObjectMeta Error:", err)
|
||||
panic(panicString)
|
||||
@ -159,7 +171,7 @@ func main() {
|
||||
}
|
||||
|
||||
// Retrieve an object and check that it is the same as what as uploaded.
|
||||
_, body, err := objectstorage.GetObject(url+object, auth.Access.Token.Id)
|
||||
_, body, err := objectstorage.GetObject(sess, url+"/"+object)
|
||||
if err != nil {
|
||||
panicString := fmt.Sprint("GetObject Error:", err)
|
||||
panic(panicString)
|
||||
@ -170,24 +182,23 @@ func main() {
|
||||
}
|
||||
|
||||
// Duplication (Copy) an existing object.
|
||||
if err = objectstorage.CopyObject(url+object, "/"+object+".dup", auth.Access.Token.Id); err != nil {
|
||||
if err = objectstorage.CopyObject(sess, url+"/"+object, "/"+object+".dup"); err != nil {
|
||||
panicString := fmt.Sprint("CopyObject Error:", err)
|
||||
panic(panicString)
|
||||
}
|
||||
|
||||
// Delete the objects.
|
||||
if err = objectstorage.DeleteObject(url+object, auth.Access.Token.Id); err != nil {
|
||||
if err = objectstorage.DeleteObject(sess, url+"/"+object); err != nil {
|
||||
panicString := fmt.Sprint("DeleteObject Error:", err)
|
||||
panic(panicString)
|
||||
}
|
||||
if err = objectstorage.DeleteObject(url+object+".dup", auth.Access.Token.Id); err != nil {
|
||||
if err = objectstorage.DeleteObject(sess, url+"/"+object+".dup"); err != nil {
|
||||
panicString := fmt.Sprint("DeleteObject Error:", err)
|
||||
panic(panicString)
|
||||
}
|
||||
|
||||
// Delete the container that was previously created.
|
||||
if err = objectstorage.DeleteContainer(url+config.Container,
|
||||
auth.Access.Token.Id); err != nil {
|
||||
if err = objectstorage.DeleteContainer(sess, url+"/"+config.Container); err != nil {
|
||||
panicString := fmt.Sprint("DeleteContainer Error:", err)
|
||||
panic(panicString)
|
||||
}
|
||||
|
@ -16,10 +16,11 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.openstack.org/stackforge/golang-client.git/identity/v2"
|
||||
"git.openstack.org/stackforge/golang-client.git/image/v1"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.openstack.org/stackforge/golang-client.git/image/v1"
|
||||
"git.openstack.org/stackforge/golang-client.git/openstack"
|
||||
)
|
||||
|
||||
// Image examples.
|
||||
@ -27,34 +28,39 @@ func main() {
|
||||
config := getConfig()
|
||||
|
||||
// Authenticate with a username, password, tenant id.
|
||||
auth, err := identity.AuthUserNameTenantName(config.Host,
|
||||
config.Username,
|
||||
config.Password,
|
||||
config.ProjectName)
|
||||
creds := openstack.AuthOpts{
|
||||
AuthUrl: config.Host,
|
||||
Project: config.ProjectName,
|
||||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
}
|
||||
auth, err := openstack.DoAuthRequest(creds)
|
||||
if err != nil {
|
||||
panicString := fmt.Sprint("There was an error authenticating:", err)
|
||||
panic(panicString)
|
||||
}
|
||||
if !auth.Access.Token.Expires.After(time.Now()) {
|
||||
if !auth.GetExpiration().After(time.Now()) {
|
||||
panic("There was an error. The auth token has an invalid expiration.")
|
||||
}
|
||||
|
||||
// Find the endpoint for the image service.
|
||||
url := ""
|
||||
for _, svc := range auth.Access.ServiceCatalog {
|
||||
if svc.Type == "image" {
|
||||
for _, ep := range svc.Endpoints {
|
||||
url = ep.PublicURL + "/v1"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if url == "" {
|
||||
url, err := auth.GetEndpoint("image", "")
|
||||
if url == "" || err != nil {
|
||||
panic("v1 image service url not found during authentication")
|
||||
}
|
||||
|
||||
imageService := image.Service{TokenID: auth.Access.Token.Id, Client: *http.DefaultClient, URL: url}
|
||||
// Make a new client with these creds
|
||||
sess, err := openstack.NewSession(nil, auth, nil)
|
||||
if err != nil {
|
||||
panicString := fmt.Sprint("Error crating new Session:", err)
|
||||
panic(panicString)
|
||||
}
|
||||
|
||||
imageService := image.Service{
|
||||
Session: *sess,
|
||||
Client: *http.DefaultClient,
|
||||
URL: url + "/v1", // We're forcing Image v1 for now
|
||||
}
|
||||
imagesDetails, err := imageService.ImagesDetail()
|
||||
if err != nil {
|
||||
panicString := fmt.Sprint("Cannot access images:", err)
|
||||
|
@ -24,7 +24,9 @@ package image
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
@ -38,8 +40,8 @@ import (
|
||||
// imageService := image.ImageService{Client: *http.DefaultClient, TokenId: tokenId, Url: "http://imageservicelocation"}
|
||||
// images:= imageService.Images()
|
||||
type Service struct {
|
||||
Session openstack.Session
|
||||
Client http.Client
|
||||
TokenID string
|
||||
URL string
|
||||
}
|
||||
|
||||
@ -150,19 +152,22 @@ func (imageService Service) queryImages(includeDetails bool, imagesResponseConta
|
||||
}
|
||||
|
||||
var headers http.Header = http.Header{}
|
||||
headers.Set("X-Auth-Token", imageService.TokenID)
|
||||
headers.Set("Accept", "application/json")
|
||||
resp, err := openstack.Get(reqURL.String(), nil, &headers)
|
||||
resp, err := imageService.Session.Get(reqURL.String(), nil, &headers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = util.CheckHTTPResponseStatusCode(resp.Resp)
|
||||
err = util.CheckHTTPResponseStatusCode(resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = json.Unmarshal(resp.Body, &imagesResponseContainer); err != nil {
|
||||
rbody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return errors.New("aaa")
|
||||
}
|
||||
if err = json.Unmarshal(rbody, &imagesResponseContainer); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"git.openstack.org/stackforge/golang-client.git/image/v1"
|
||||
"git.openstack.org/stackforge/golang-client.git/openstack"
|
||||
"git.openstack.org/stackforge/golang-client.git/testUtil"
|
||||
"git.openstack.org/stackforge/golang-client.git/util"
|
||||
)
|
||||
@ -159,7 +160,18 @@ func testImageServiceAction(t *testing.T, uriEndsWith string, testData string, i
|
||||
apiServer := testUtil.CreateGetJSONTestRequestServer(t, tokn, testData, anon)
|
||||
defer apiServer.Close()
|
||||
|
||||
imageService := image.Service{TokenID: tokn, Client: *http.DefaultClient, URL: apiServer.URL}
|
||||
auth := openstack.AuthToken{
|
||||
Access: openstack.AccessType{
|
||||
Token: openstack.Token{
|
||||
ID: tokn,
|
||||
},
|
||||
},
|
||||
}
|
||||
sess, _ := openstack.NewSession(http.DefaultClient, auth, nil)
|
||||
imageService := image.Service{
|
||||
Session: *sess,
|
||||
URL: apiServer.URL,
|
||||
}
|
||||
imageServiceAction(&imageService)
|
||||
}
|
||||
|
||||
|
@ -31,65 +31,65 @@ var zeroByte = &([]byte{}) //pointer to empty []byte
|
||||
//"limit" and "marker" corresponds to the API's "limit" and "marker".
|
||||
//"url" can be regular storage or cdn-enabled storage URL.
|
||||
//It returns []byte which then needs to be unmarshalled to decode the JSON.
|
||||
func ListContainers(limit int64, marker, url, token string) ([]byte, error) {
|
||||
return ListObjects(limit, marker, "", "", "", url, token)
|
||||
func ListContainers(session *openstack.Session, limit int64, marker, url string) ([]byte, error) {
|
||||
return ListObjects(session, limit, marker, "", "", "", url)
|
||||
}
|
||||
|
||||
//GetAccountMeta calls the OpenStack retrieve account metadata API using
|
||||
//previously obtained token.
|
||||
func GetAccountMeta(url, token string) (http.Header, error) {
|
||||
return GetObjectMeta(url, token)
|
||||
func GetAccountMeta(session *openstack.Session, url string) (http.Header, error) {
|
||||
return GetObjectMeta(session, url)
|
||||
}
|
||||
|
||||
//DeleteContainer calls the OpenStack delete container API using
|
||||
//previously obtained token.
|
||||
func DeleteContainer(url, token string) error {
|
||||
return DeleteObject(url, token)
|
||||
func DeleteContainer(session *openstack.Session, url string) error {
|
||||
return DeleteObject(session, url)
|
||||
}
|
||||
|
||||
//GetContainerMeta calls the OpenStack retrieve object metadata API
|
||||
//using previously obtained token.
|
||||
//url can be regular storage or CDN-enabled storage URL.
|
||||
func GetContainerMeta(url, token string) (http.Header, error) {
|
||||
return GetObjectMeta(url, token)
|
||||
func GetContainerMeta(session *openstack.Session, url string) (http.Header, error) {
|
||||
return GetObjectMeta(session, url)
|
||||
}
|
||||
|
||||
//SetContainerMeta calls the OpenStack API to create / update meta data
|
||||
//for container using previously obtained token.
|
||||
//url can be regular storage or CDN-enabled storage URL.
|
||||
func SetContainerMeta(url string, token string, s ...string) (err error) {
|
||||
return SetObjectMeta(url, token, s...)
|
||||
func SetContainerMeta(session *openstack.Session, url string, headers http.Header) (err error) {
|
||||
return SetObjectMeta(session, url, headers)
|
||||
}
|
||||
|
||||
//PutContainer calls the OpenStack API to create / update
|
||||
//container using previously obtained token.
|
||||
func PutContainer(url, token string, s ...string) error {
|
||||
return PutObject(zeroByte, url, token, s...)
|
||||
func PutContainer(session *openstack.Session, url string, headers http.Header) error {
|
||||
return PutObject(session, zeroByte, url, headers)
|
||||
}
|
||||
|
||||
//ListObjects calls the OpenStack list object API using previously
|
||||
//obtained token. "Limit", "marker", "prefix", "path", "delim" corresponds
|
||||
//to the API's "limit", "marker", "prefix", "path", and "delimiter".
|
||||
func ListObjects(limit int64,
|
||||
marker, prefix, path, delim, conURL, token string) ([]byte, error) {
|
||||
var query = "?format=json"
|
||||
func ListObjects(session *openstack.Session, limit int64,
|
||||
marker, prefix, path, delim, conURL string) ([]byte, error) {
|
||||
var query url.Values = url.Values{}
|
||||
query.Add("format", "json")
|
||||
if limit > 0 {
|
||||
query += "&limit=" + strconv.FormatInt(limit, 10)
|
||||
query.Add("limit", strconv.FormatInt(limit, 10))
|
||||
}
|
||||
if marker != "" {
|
||||
query += "&marker=" + url.QueryEscape(marker)
|
||||
query.Add("marker", url.QueryEscape(marker))
|
||||
}
|
||||
if prefix != "" {
|
||||
query += "&prefix=" + url.QueryEscape(prefix)
|
||||
query.Add("prefix", url.QueryEscape(prefix))
|
||||
}
|
||||
if path != "" {
|
||||
query += "&path=" + url.QueryEscape(path)
|
||||
query.Add("path", url.QueryEscape(path))
|
||||
}
|
||||
if delim != "" {
|
||||
query += "&delimiter=" + url.QueryEscape(delim)
|
||||
query.Add("delimiter", url.QueryEscape(delim))
|
||||
}
|
||||
resp, err := util.CallAPI("GET", conURL+query, zeroByte,
|
||||
"X-Auth-Token", token)
|
||||
resp, err := session.Get(conURL, &query, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -107,23 +107,21 @@ func ListObjects(limit int64,
|
||||
//PutObject calls the OpenStack create object API using previously
|
||||
//obtained token.
|
||||
//url can be regular storage or CDN-enabled storage URL.
|
||||
func PutObject(fContent *[]byte, url, token string, s ...string) (err error) {
|
||||
var headers http.Header = http.Header{}
|
||||
headers.Set("X-Auth-Token", token)
|
||||
resp, err := openstack.Put(url, nil, &headers, fContent)
|
||||
func PutObject(session *openstack.Session, fContent *[]byte, url string, headers http.Header) (err error) {
|
||||
resp, err := session.Put(url, nil, &headers, fContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return util.CheckHTTPResponseStatusCode(resp.Resp)
|
||||
return util.CheckHTTPResponseStatusCode(resp)
|
||||
}
|
||||
|
||||
//CopyObject calls the OpenStack copy object API using previously obtained
|
||||
//token. Note from API doc: "The destination container must exist before
|
||||
//attempting the copy."
|
||||
func CopyObject(srcURL, destURL, token string) (err error) {
|
||||
resp, err := util.CallAPI("COPY", srcURL, zeroByte,
|
||||
"X-Auth-Token", token,
|
||||
"Destination", destURL)
|
||||
func CopyObject(session *openstack.Session, srcURL, destURL string) (err error) {
|
||||
var headers http.Header = http.Header{}
|
||||
headers.Add("Destination", destURL)
|
||||
resp, err := session.Request("COPY", srcURL, nil, &headers, zeroByte)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -138,8 +136,8 @@ func CopyObject(srcURL, destURL, token string) (err error) {
|
||||
//from the non-current container to the current." .. "If you want to completely
|
||||
//remove an object and you have five total versions of it, you must DELETE it
|
||||
//five times."
|
||||
func DeleteObject(url, token string) (err error) {
|
||||
resp, err := util.CallAPI("DELETE", url, zeroByte, "X-Auth-Token", token)
|
||||
func DeleteObject(session *openstack.Session, url string) (err error) {
|
||||
resp, err := session.Delete(url, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -148,10 +146,9 @@ func DeleteObject(url, token string) (err error) {
|
||||
|
||||
//SetObjectMeta calls the OpenStack API to create/update meta data for
|
||||
//object using previously obtained token.
|
||||
func SetObjectMeta(url string, token string, s ...string) (err error) {
|
||||
s = append(s, "X-Auth-Token")
|
||||
s = append(s, token)
|
||||
resp, err := util.CallAPI("POST", url, zeroByte, s...)
|
||||
func SetObjectMeta(session *openstack.Session, url string, headers http.Header) (err error) {
|
||||
// headers.Add("X-Auth-Token", token)
|
||||
resp, err := session.Post(url, nil, &headers, zeroByte)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -160,8 +157,8 @@ func SetObjectMeta(url string, token string, s ...string) (err error) {
|
||||
|
||||
//GetObjectMeta calls the OpenStack retrieve object metadata API using
|
||||
//previously obtained token.
|
||||
func GetObjectMeta(url, token string) (http.Header, error) {
|
||||
resp, err := util.CallAPI("HEAD", url, zeroByte, "X-Auth-Token", token)
|
||||
func GetObjectMeta(session *openstack.Session, url string) (http.Header, error) {
|
||||
resp, err := session.Head(url, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -175,8 +172,8 @@ func GetObjectMeta(url, token string) (http.Header, error) {
|
||||
//Since this implementation of GetObject retrieves header info, it
|
||||
//effectively executes GetObjectMeta also in addition to getting the
|
||||
//object content.
|
||||
func GetObject(url, token string) (http.Header, []byte, error) {
|
||||
resp, err := util.CallAPI("GET", url, zeroByte, "X-Auth-Token", token)
|
||||
func GetObject(session *openstack.Session, url string) (http.Header, []byte, error) {
|
||||
resp, err := session.Get(url, nil, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -2,3 +2,12 @@ openstack
|
||||
=========
|
||||
|
||||
`openstack` is the API to an OpenStack cloud.
|
||||
|
||||
* `session.go` - A Session object that encapsulates the HTTP REST handler
|
||||
and authentication and logging
|
||||
|
||||
* `auth.go` - The basic authentication interface
|
||||
|
||||
* `auth-password.go` - Implements password authentication (v2 only at present)
|
||||
|
||||
* `auth-token.go` - The returned token objects
|
||||
|
81
openstack/auth-password.go
Normal file
81
openstack/auth-password.go
Normal file
@ -0,0 +1,81 @@
|
||||
// auth-password - Username/Password Authentication
|
||||
// Copyright 2015 Dean Troyer
|
||||
//
|
||||
// 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 openstack
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
// "strings"
|
||||
)
|
||||
|
||||
// The token request structure for Identity v2
|
||||
|
||||
type PasswordCredentials struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type OSAuth struct {
|
||||
PasswordCredentials `json:"passwordCredentials"`
|
||||
Project string `json:"tenantName"`
|
||||
}
|
||||
|
||||
type UserPassV2 struct {
|
||||
OSAuth `json:"auth"`
|
||||
AuthUrl string `json:"-"`
|
||||
}
|
||||
|
||||
func NewUserPassV2(ao AuthOpts) (upv2 *UserPassV2, err error) {
|
||||
// Validate incoming values
|
||||
if ao.AuthUrl == "" {
|
||||
err = errors.New("AuthUrl required")
|
||||
return nil, err
|
||||
}
|
||||
if ao.Username == "" {
|
||||
err = errors.New("Username required")
|
||||
return nil, err
|
||||
}
|
||||
if ao.Password == "" {
|
||||
err = errors.New("Password required")
|
||||
return nil, err
|
||||
}
|
||||
upv2 = &UserPassV2{
|
||||
AuthUrl: ao.AuthUrl,
|
||||
OSAuth: OSAuth{
|
||||
PasswordCredentials: PasswordCredentials{
|
||||
Username: ao.Username,
|
||||
Password: ao.Password,
|
||||
},
|
||||
Project: ao.Project,
|
||||
},
|
||||
}
|
||||
return upv2, nil
|
||||
}
|
||||
|
||||
// Produce JSON output
|
||||
func (s *UserPassV2) JSON() []byte {
|
||||
reqAuth, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
// Return an empty structure
|
||||
reqAuth = []byte{'{', '}'}
|
||||
}
|
||||
return reqAuth
|
||||
}
|
||||
|
||||
// func (self *UserPassV2) AuthUserPassV2(opts interface{}) (AuthRef, error) {
|
||||
// auth, err := self.GetAuthRef()
|
||||
// return AuthRef(auth), err
|
||||
// }
|
81
openstack/auth-token.go
Normal file
81
openstack/auth-token.go
Normal file
@ -0,0 +1,81 @@
|
||||
// auth-token - Token Authentication
|
||||
// Copyright 2015 Dean Troyer
|
||||
//
|
||||
// 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 openstack
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Identity Response Types
|
||||
|
||||
type AccessType struct {
|
||||
Token Token `json:"token"`
|
||||
User interface{} `json:"id"`
|
||||
ServiceCatalog []ServiceCatalogEntry `json:"servicecatalog"`
|
||||
}
|
||||
|
||||
type AuthToken struct {
|
||||
Access AccessType `json:"access"`
|
||||
}
|
||||
|
||||
type Token struct {
|
||||
ID string `json:"id"`
|
||||
Expires time.Time `json:"expires"`
|
||||
Project struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
} `json:"tenant"`
|
||||
}
|
||||
|
||||
type ServiceCatalogEntry struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Endpoints []ServiceEndpoint `json:"endpoints"`
|
||||
// Endpoints []map[string]string `json:"endpoints"`
|
||||
}
|
||||
|
||||
type ServiceEndpoint struct {
|
||||
Type string `json:"type"`
|
||||
Region string `json:"region"`
|
||||
PublicURL string `json:"publicurl"`
|
||||
AdminURL string `json:"adminurl"`
|
||||
InternalURL string `json:"internalurl"`
|
||||
VersionID string `json:"versionid"`
|
||||
}
|
||||
|
||||
func (s AuthToken) GetToken() string {
|
||||
return s.Access.Token.ID
|
||||
}
|
||||
|
||||
func (s AuthToken) GetExpiration() time.Time {
|
||||
return s.Access.Token.Expires
|
||||
}
|
||||
|
||||
func (s AuthToken) GetEndpoint(serviceType string, regionName string) (string, error) {
|
||||
|
||||
// Parse service catalog
|
||||
for _, v := range s.Access.ServiceCatalog {
|
||||
if v.Type == serviceType {
|
||||
for _, r := range v.Endpoints {
|
||||
if regionName == "" || r.Region == regionName {
|
||||
return r.PublicURL, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", errors.New("err: endpoint not found")
|
||||
}
|
99
openstack/auth.go
Normal file
99
openstack/auth.go
Normal file
@ -0,0 +1,99 @@
|
||||
// auth - Authentication interface
|
||||
// Copyright 2015 Dean Troyer
|
||||
//
|
||||
// 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 openstack
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AuthRef is the returned authentication object, maybe v2 or v3
|
||||
type AuthRef interface {
|
||||
GetToken() string
|
||||
GetExpiration() time.Time
|
||||
GetEndpoint(string, string) (string, error)
|
||||
}
|
||||
|
||||
// AuthOpts is the set of credentials used to authenticate to OpenStack
|
||||
type AuthOpts struct {
|
||||
// AuthUrl is always required
|
||||
AuthUrl string
|
||||
|
||||
// Domain is ignored for v2 and required for v3 auth
|
||||
Domain string
|
||||
|
||||
// Project is optional to get an unscoped token but required for
|
||||
// a scoped token, which is required to do pretty much everything
|
||||
// except list projects
|
||||
Project string
|
||||
|
||||
// Username is required for password auth
|
||||
Username string
|
||||
|
||||
// Password is required for password auth
|
||||
Password string
|
||||
|
||||
// Token is required for Toekn auth
|
||||
Token string
|
||||
}
|
||||
|
||||
func (s *AuthOpts) GetAuthType() (string, error) {
|
||||
var auth_type string
|
||||
if s.AuthUrl != "" && s.Token != "" {
|
||||
auth_type = "token"
|
||||
} else if s.Username != "" {
|
||||
auth_type = "password"
|
||||
}
|
||||
return auth_type, nil
|
||||
}
|
||||
|
||||
// Basic auth call
|
||||
// These args should be an interface??
|
||||
func DoAuthRequest(authopts AuthOpts) (AuthRef, error) {
|
||||
// url string, body []byte)
|
||||
var auth = AuthToken{}
|
||||
|
||||
auth_mod, err := NewUserPassV2(authopts)
|
||||
if err != nil {
|
||||
err = errors.New("Failed to get auth options")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
path := auth_mod.AuthUrl + "/tokens"
|
||||
body := auth_mod.JSON()
|
||||
resp, err := Post(path, nil, nil, &body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contentType := strings.ToLower(resp.Header.Get("Content-Type"))
|
||||
if strings.Contains(contentType, "json") != true {
|
||||
return nil, errors.New("err: header Content-Type is not JSON")
|
||||
}
|
||||
|
||||
rbody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, errors.New("aaa")
|
||||
}
|
||||
if err = json.Unmarshal(rbody, &auth); err != nil {
|
||||
return nil, errors.New("bbb")
|
||||
}
|
||||
|
||||
return auth, nil
|
||||
}
|
@ -19,12 +19,11 @@ import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
// "io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var Debug = new(bool)
|
||||
@ -34,46 +33,28 @@ type Response struct {
|
||||
Body []byte
|
||||
}
|
||||
|
||||
type TokenInterface interface {
|
||||
GetTokenId() string
|
||||
}
|
||||
|
||||
type Token struct {
|
||||
Expires string
|
||||
Id string
|
||||
Project struct {
|
||||
Id string
|
||||
Name string
|
||||
}
|
||||
}
|
||||
|
||||
func (t Token) GetTokenId() string {
|
||||
return t.Id
|
||||
}
|
||||
|
||||
// Generic callback to get a token from the auth plugin
|
||||
type AuthFunc func(s *Session, opts interface{}) (TokenInterface, error)
|
||||
type AuthFunc func(s *Session, opts interface{}) (AuthRef, error)
|
||||
|
||||
type Session struct {
|
||||
httpClient *http.Client
|
||||
endpoint string
|
||||
authenticate AuthFunc
|
||||
Token TokenInterface
|
||||
Headers http.Header
|
||||
// ServCat map[string]ServiceEndpoint
|
||||
httpClient *http.Client
|
||||
AuthToken AuthRef
|
||||
Headers http.Header
|
||||
}
|
||||
|
||||
func NewSession(af AuthFunc, endpoint string, tls *tls.Config) (session *Session, err error) {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: tls,
|
||||
DisableCompression: true,
|
||||
func NewSession(hclient *http.Client, auth AuthRef, tls *tls.Config) (session *Session, err error) {
|
||||
if hclient == nil {
|
||||
// Only build a transport if we're also building the client
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: tls,
|
||||
DisableCompression: true,
|
||||
}
|
||||
hclient = &http.Client{Transport: tr}
|
||||
}
|
||||
session = &Session{
|
||||
// TODO(dtroyer): httpClient needs to be able to be passed in, or set externally
|
||||
httpClient: &http.Client{Transport: tr},
|
||||
endpoint: strings.TrimRight(endpoint, "/"),
|
||||
authenticate: af,
|
||||
Headers: http.Header{},
|
||||
httpClient: hclient,
|
||||
AuthToken: auth,
|
||||
Headers: http.Header{},
|
||||
}
|
||||
return session, nil
|
||||
}
|
||||
@ -83,47 +64,35 @@ func (s *Session) NewRequest(method, url string, headers *http.Header, body io.R
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// add token, get one if needed
|
||||
if s.Token == nil && s.authenticate != nil {
|
||||
var tok TokenInterface
|
||||
tok, err = s.authenticate(s, nil)
|
||||
if err != nil {
|
||||
// (re-)auth failure!!
|
||||
return nil, err
|
||||
}
|
||||
s.Token = tok
|
||||
}
|
||||
if headers != nil {
|
||||
req.Header = *headers
|
||||
}
|
||||
if s.Token != nil {
|
||||
req.Header.Add("X-Auth-Token", s.Token.GetTokenId())
|
||||
if s.AuthToken != nil {
|
||||
req.Header.Add("X-Auth-Token", s.AuthToken.GetToken())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Session) Do(req *http.Request) (*Response, error) {
|
||||
if *Debug {
|
||||
d, _ := httputil.DumpRequestOut(req, true)
|
||||
log.Printf(">>>>>>>>>> REQUEST:\n", string(d))
|
||||
}
|
||||
|
||||
func (s *Session) Do(req *http.Request) (*http.Response, error) {
|
||||
// Add session headers
|
||||
for k := range s.Headers {
|
||||
req.Header.Set(k, s.Headers.Get(k))
|
||||
}
|
||||
|
||||
hresp, err := s.httpClient.Do(req)
|
||||
if *Debug {
|
||||
d, _ := httputil.DumpRequestOut(req, true)
|
||||
log.Printf(">>>>>>>>>> REQUEST:\n", string(d))
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if *Debug {
|
||||
dr, _ := httputil.DumpResponse(hresp, true)
|
||||
dr, _ := httputil.DumpResponse(resp, true)
|
||||
log.Printf("<<<<<<<<<< RESULT:\n", string(dr))
|
||||
}
|
||||
|
||||
resp := new(Response)
|
||||
resp.Resp = hresp
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
@ -134,7 +103,7 @@ func (s *Session) Request(
|
||||
params *url.Values,
|
||||
headers *http.Header,
|
||||
body *[]byte,
|
||||
) (resp *Response, err error) {
|
||||
) (resp *http.Response, err error) {
|
||||
// add params to url here
|
||||
if params != nil {
|
||||
url = url + "?" + params.Encode()
|
||||
@ -151,35 +120,42 @@ func (s *Session) Request(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
resp, err = s.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// do we need to parse this in this func? yes...
|
||||
defer resp.Resp.Body.Close()
|
||||
|
||||
resp.Body, err = ioutil.ReadAll(resp.Resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *Session) Delete(
|
||||
url string,
|
||||
params *url.Values,
|
||||
headers *http.Header) (resp *http.Response, err error) {
|
||||
return s.Request("DELETE", url, params, headers, nil)
|
||||
}
|
||||
|
||||
func (s *Session) Get(
|
||||
url string,
|
||||
params *url.Values,
|
||||
headers *http.Header) (resp *Response, err error) {
|
||||
headers *http.Header) (resp *http.Response, err error) {
|
||||
return s.Request("GET", url, params, headers, nil)
|
||||
}
|
||||
|
||||
func (s *Session) Head(
|
||||
url string,
|
||||
params *url.Values,
|
||||
headers *http.Header) (resp *http.Response, err error) {
|
||||
return s.Request("HEAD", url, params, headers, nil)
|
||||
}
|
||||
|
||||
func (s *Session) Post(
|
||||
url string,
|
||||
params *url.Values,
|
||||
headers *http.Header,
|
||||
body *[]byte) (resp *Response, err error) {
|
||||
body *[]byte) (resp *http.Response, err error) {
|
||||
return s.Request("POST", url, params, headers, body)
|
||||
}
|
||||
|
||||
@ -187,7 +163,7 @@ func (s *Session) Put(
|
||||
url string,
|
||||
params *url.Values,
|
||||
headers *http.Header,
|
||||
body *[]byte) (resp *Response, err error) {
|
||||
body *[]byte) (resp *http.Response, err error) {
|
||||
return s.Request("PUT", url, params, headers, body)
|
||||
}
|
||||
|
||||
@ -195,8 +171,8 @@ func (s *Session) Put(
|
||||
func Get(
|
||||
url string,
|
||||
params *url.Values,
|
||||
headers *http.Header) (resp *Response, err error) {
|
||||
s, _ := NewSession(nil, "", nil)
|
||||
headers *http.Header) (resp *http.Response, err error) {
|
||||
s, _ := NewSession(nil, nil, nil)
|
||||
return s.Get(url, params, headers)
|
||||
}
|
||||
|
||||
@ -205,8 +181,8 @@ func Post(
|
||||
url string,
|
||||
params *url.Values,
|
||||
headers *http.Header,
|
||||
body *[]byte) (resp *Response, err error) {
|
||||
s, _ := NewSession(nil, "", nil)
|
||||
body *[]byte) (resp *http.Response, err error) {
|
||||
s, _ := NewSession(nil, nil, nil)
|
||||
return s.Post(url, params, headers, body)
|
||||
}
|
||||
|
||||
@ -215,7 +191,7 @@ func Put(
|
||||
url string,
|
||||
params *url.Values,
|
||||
headers *http.Header,
|
||||
body *[]byte) (resp *Response, err error) {
|
||||
s, _ := NewSession(nil, "", nil)
|
||||
body *[]byte) (resp *http.Response, err error) {
|
||||
s, _ := NewSession(nil, nil, nil)
|
||||
return s.Put(url, params, headers, body)
|
||||
}
|
||||
|
@ -13,48 +13,47 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
package openstack_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"testing"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"git.openstack.org/stackforge/golang-client.git/openstack"
|
||||
"git.openstack.org/stackforge/golang-client.git/testUtil"
|
||||
"git.openstack.org/stackforge/golang-client.git/openstack"
|
||||
"git.openstack.org/stackforge/golang-client.git/testUtil"
|
||||
)
|
||||
|
||||
type TestStruct struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func TestSessionGet(t *testing.T) {
|
||||
tokn := "eaaafd18-0fed-4b3a-81b4-663c99ec1cbb"
|
||||
var apiServer = testUtil.CreateGetJsonTestServer(
|
||||
t,
|
||||
tokn,
|
||||
`{"id":"id1","name":"Chris"}`,
|
||||
nil,
|
||||
)
|
||||
expected := TestStruct{ID: "id1", Name: "Chris"}
|
||||
actual := TestStruct{}
|
||||
tokn := "eaaafd18-0fed-4b3a-81b4-663c99ec1cbb"
|
||||
var apiServer = testUtil.CreateGetJsonTestServer(
|
||||
t,
|
||||
tokn,
|
||||
`{"id":"id1","name":"Chris"}`,
|
||||
nil,
|
||||
)
|
||||
expected := TestStruct{ID: "id1", Name: "Chris"}
|
||||
actual := TestStruct{}
|
||||
|
||||
s, _ := openstack.NewSession(nil, "", nil)
|
||||
var headers http.Header = http.Header{}
|
||||
headers.Set("X-Auth-Token", tokn)
|
||||
headers.Set("Accept", "application/json")
|
||||
headers.Set("Etag", "md5hash-blahblah")
|
||||
resp, err := s.Get(apiServer.URL, nil, &headers)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
testUtil.IsNil(t, err)
|
||||
s, _ := openstack.NewSession(nil, "", nil)
|
||||
var headers http.Header = http.Header{}
|
||||
headers.Set("X-Auth-Token", tokn)
|
||||
headers.Set("Accept", "application/json")
|
||||
headers.Set("Etag", "md5hash-blahblah")
|
||||
resp, err := s.Get(apiServer.URL, nil, &headers)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
testUtil.IsNil(t, err)
|
||||
|
||||
if err = json.Unmarshal(resp.Body, &actual); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err = json.Unmarshal(resp.Body, &actual); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
testUtil.Equals(t, expected, actual)
|
||||
testUtil.Equals(t, expected, actual)
|
||||
}
|
||||
|
@ -61,24 +61,24 @@ func IsNil(tb testing.TB, act interface{}) {
|
||||
// JSON Get requests. Takes a token, JSON payload, and a verification function
|
||||
// to do additional validation
|
||||
func CreateGetJsonTestServer(
|
||||
t *testing.T,
|
||||
expectedAuthToken string,
|
||||
jsonResponsePayload string,
|
||||
verifyRequest func(*http.Request)) *httptest.Server {
|
||||
return httptest.NewServer(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
headerValuesEqual(t, r, "X-Auth-Token", expectedAuthToken)
|
||||
headerValuesEqual(t, r, "Accept", "application/json")
|
||||
// verifyRequest(r)
|
||||
if r.Method == "GET" {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(jsonResponsePayload))
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
t *testing.T,
|
||||
expectedAuthToken string,
|
||||
jsonResponsePayload string,
|
||||
verifyRequest func(*http.Request)) *httptest.Server {
|
||||
return httptest.NewServer(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
headerValuesEqual(t, r, "X-Auth-Token", expectedAuthToken)
|
||||
headerValuesEqual(t, r, "Accept", "application/json")
|
||||
// verifyRequest(r)
|
||||
if r.Method == "GET" {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(jsonResponsePayload))
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
t.Error(errors.New("Failed: r.Method == GET"))
|
||||
}))
|
||||
t.Error(errors.New("Failed: r.Method == GET"))
|
||||
}))
|
||||
}
|
||||
|
||||
// CreateGetJSONTestRequestServer creates a httptest.Server that can be used to test GetJson requests. Just specify the token,
|
||||
|
Loading…
x
Reference in New Issue
Block a user