From 19ac43cedc85cdd154714812268576398d559277 Mon Sep 17 00:00:00 2001 From: "Chen.Zhidong" Date: Thu, 15 Mar 2018 11:31:33 +0800 Subject: [PATCH 01/23] Fix Querying Endpoints and Show endpoint details --- openstack/identity/v3/endpoints/doc.go | 7 +++ openstack/identity/v3/endpoints/requests.go | 6 +++ openstack/identity/v3/endpoints/results.go | 15 ++++++ .../v3/endpoints/testing/requests_test.go | 48 +++++++++++++++++++ 4 files changed, 76 insertions(+) diff --git a/openstack/identity/v3/endpoints/doc.go b/openstack/identity/v3/endpoints/doc.go index 380a3f38e..41d6ae6c8 100644 --- a/openstack/identity/v3/endpoints/doc.go +++ b/openstack/identity/v3/endpoints/doc.go @@ -65,5 +65,12 @@ Example to Delete an Endpoint if err != nil { panic(err) } + +Example to Get an Endpoint + endpointID := "ad59deeec5154d1fa0dcff518596f499" + endpoint, err := endpoints.Get(identityClient, endpointID).ExtractErr() + if err != nil { + panic(err) + } */ package endpoints diff --git a/openstack/identity/v3/endpoints/requests.go b/openstack/identity/v3/endpoints/requests.go index 54f101de9..df717c59f 100644 --- a/openstack/identity/v3/endpoints/requests.go +++ b/openstack/identity/v3/endpoints/requests.go @@ -137,3 +137,9 @@ func Delete(client *golangsdk.ServiceClient, endpointID string) (r DeleteResult) _, r.Err = client.Delete(endpointURL(client, endpointID), nil) return } + +// Get retrieves details on a single Endpoint, by endpointID +func Get(client *golangsdk.ServiceClient, endpointID string) (r GetResult) { + _, r.Err = client.Get(endpointURL(client, endpointID), &r.Body, nil) + return +} diff --git a/openstack/identity/v3/endpoints/results.go b/openstack/identity/v3/endpoints/results.go index fccf7350d..bdfe2859d 100644 --- a/openstack/identity/v3/endpoints/results.go +++ b/openstack/identity/v3/endpoints/results.go @@ -37,6 +37,12 @@ type DeleteResult struct { golangsdk.ErrResult } +// GetResult is the response from a Get operation. Call its Extract +// method to interpret it as an Endpoint. +type GetResult struct { + commonResult +} + // Endpoint describes the entry point for another service's API. type Endpoint struct { // ID is the unique ID of the endpoint. @@ -57,6 +63,15 @@ type Endpoint struct { // URL is the url of the Endpoint. URL string `json:"url"` + + // RegionID is the ID of the region the Endpoint is located in. + RegionID string `json:"region_id"` + + // Enabled is the availablity of the Endpoint. + Enabled bool `json:"enabled"` + + // Links is the links of the Endpoint + Links map[string]string `json:"links"` } // EndpointPage is a single page of Endpoint results. diff --git a/openstack/identity/v3/endpoints/testing/requests_test.go b/openstack/identity/v3/endpoints/testing/requests_test.go index 3783d9fa7..475a3505b 100644 --- a/openstack/identity/v3/endpoints/testing/requests_test.go +++ b/openstack/identity/v3/endpoints/testing/requests_test.go @@ -212,3 +212,51 @@ func TestDeleteEndpoint(t *testing.T) { res := endpoints.Delete(client.ServiceClient(), "34") th.AssertNoErr(t, res.Err) } + +func TestGetEnpoint(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/endpoints/12", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + fmt.Fprintf(w, ` + { + "endpoint": { + "id": "12", + "interface": "public", + "links": { + "self": "https://localhost:5000/v3/endpoints/12" + }, + "name": "renamed", + "region": "somewhere-else", + "service_id": "asdfasdfasdfasdf", + "url": "https://1.2.3.4:9000/", + "region_id": "qwerqwerqwer", + "enabled": true + } + } + `) + }) + + actual, err := endpoints.Get(client.ServiceClient(), "12").Extract() + if err != nil { + t.Fatalf("Unexpected error from Get: %v", err) + } + + expected := &endpoints.Endpoint{ + ID: "12", + Availability: golangsdk.AvailabilityPublic, + Name: "renamed", + Region: "somewhere-else", + ServiceID: "asdfasdfasdfasdf", + URL: "https://1.2.3.4:9000/", + RegionID: "qwerqwerqwer", + Enabled: true, + Links: map[string]string{ + "self": "https://localhost:5000/v3/endpoints/12", + }, + } + th.AssertDeepEquals(t, expected, actual) +} From bed22c46052dd89e0e5004f8bee1580b3a77eee6 Mon Sep 17 00:00:00 2001 From: "Chen.Zhidong" Date: Thu, 15 Mar 2018 13:54:13 +0800 Subject: [PATCH 02/23] Fix Region struct --- openstack/identity/v3/regions/doc.go | 8 ++++++++ openstack/identity/v3/regions/results.go | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/openstack/identity/v3/regions/doc.go b/openstack/identity/v3/regions/doc.go index a37b05a54..5e98bdcbe 100644 --- a/openstack/identity/v3/regions/doc.go +++ b/openstack/identity/v3/regions/doc.go @@ -59,5 +59,13 @@ Example to Delete a Region if err != nil { panic(err) } + +Example to Get a Region + + regionID := "TestRegion" + region, err := regions.Get(identityClient, regionID).Extract() + if err != nil { + panic(err) + } */ package regions diff --git a/openstack/identity/v3/regions/results.go b/openstack/identity/v3/regions/results.go index aa66f5252..70aa553e2 100644 --- a/openstack/identity/v3/regions/results.go +++ b/openstack/identity/v3/regions/results.go @@ -24,6 +24,12 @@ type Region struct { // ParentRegionID is the ID of the parent region. ParentRegionID string `json:"parent_region_id"` + + // Locales is the names of the region + Locales map[string]interface{} `json:"locales"` + + // Type is the type of the region + Type string `json:"type"` } func (r *Region) UnmarshalJSON(b []byte) error { From 047f2a8c24109c7613beeafc1fc7fdf68f48fa14 Mon Sep 17 00:00:00 2001 From: "Chen.Zhidong" Date: Thu, 15 Mar 2018 13:57:45 +0800 Subject: [PATCH 03/23] Use interface instead of string for Endpoint Links --- openstack/identity/v3/endpoints/results.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/identity/v3/endpoints/results.go b/openstack/identity/v3/endpoints/results.go index bdfe2859d..b88ecf7b3 100644 --- a/openstack/identity/v3/endpoints/results.go +++ b/openstack/identity/v3/endpoints/results.go @@ -71,7 +71,7 @@ type Endpoint struct { Enabled bool `json:"enabled"` // Links is the links of the Endpoint - Links map[string]string `json:"links"` + Links map[string]interface{} `json:"links"` } // EndpointPage is a single page of Endpoint results. From b53c2fa67b4a5fbef3a37be4f602894519ea50ee Mon Sep 17 00:00:00 2001 From: "Chen.Zhidong" Date: Thu, 15 Mar 2018 14:01:23 +0800 Subject: [PATCH 04/23] Fix role struct --- openstack/identity/v3/roles/results.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/openstack/identity/v3/roles/results.go b/openstack/identity/v3/roles/results.go index 2e4cec087..deaf9fbd0 100644 --- a/openstack/identity/v3/roles/results.go +++ b/openstack/identity/v3/roles/results.go @@ -24,6 +24,21 @@ type Role struct { // Extra is a collection of miscellaneous key/values. Extra map[string]interface{} `json:"-"` + + // Type is type of the role. + Type string `json:"type"` + + // DisplayName is the displayed name of the role + DisplayName string `json:"display_name"` + + // Catalog is the catelog of the role. + Catalog string `json:"catalog"` + + // Policy contains detail policies of the role + Policy map[string]interface{} `json:"policy"` + + // Description is the description of the role + Description string `json:"description"` } func (r *Role) UnmarshalJSON(b []byte) error { From 7e1ddc528e9a89b14bd98efd66da6e94957604bb Mon Sep 17 00:00:00 2001 From: "Chen.Zhidong" Date: Thu, 15 Mar 2018 14:12:43 +0800 Subject: [PATCH 05/23] Service List and Get --- openstack/identity/v3/services/doc.go | 8 + openstack/identity/v3/services/requests.go | 3 - openstack/identity/v3/services/results.go | 6 + .../identity/v3/services/testing/fixtures.go | 148 ++++++++---------- .../v3/services/testing/requests_test.go | 14 +- 5 files changed, 84 insertions(+), 95 deletions(-) diff --git a/openstack/identity/v3/services/doc.go b/openstack/identity/v3/services/doc.go index 81702359a..8cddd0dff 100644 --- a/openstack/identity/v3/services/doc.go +++ b/openstack/identity/v3/services/doc.go @@ -62,5 +62,13 @@ Example to Delete a Service panic(err) } +Example to Get a service + + serviceID := "3c7bbe9a6ecb453ca1789586291380ed" + service, err := services.Get(identityClient, serviceID).Extract() + if err != nil { + panic(err) + } + */ package services diff --git a/openstack/identity/v3/services/requests.go b/openstack/identity/v3/services/requests.go index cf3fae6be..fb3017647 100644 --- a/openstack/identity/v3/services/requests.go +++ b/openstack/identity/v3/services/requests.go @@ -64,9 +64,6 @@ type ListOptsBuilder interface { type ListOpts struct { // ServiceType filter the response by a type of service. ServiceType string `q:"type"` - - // Name filters the response by a service name. - Name string `q:"name"` } // ToServiceListMap builds a list query from the list options. diff --git a/openstack/identity/v3/services/results.go b/openstack/identity/v3/services/results.go index f49f00ef6..2db066316 100644 --- a/openstack/identity/v3/services/results.go +++ b/openstack/identity/v3/services/results.go @@ -62,6 +62,12 @@ type Service struct { // Extra is a collection of miscellaneous key/values. Extra map[string]interface{} `json:"-"` + + // Description is the description of the service. + Description string `json:"description"` + + // Name is the name of the service. + Name string `json:"name"` } func (r *Service) UnmarshalJSON(b []byte) error { diff --git a/openstack/identity/v3/services/testing/fixtures.go b/openstack/identity/v3/services/testing/fixtures.go index e89eb3669..b2cec25a7 100644 --- a/openstack/identity/v3/services/testing/fixtures.go +++ b/openstack/identity/v3/services/testing/fixtures.go @@ -12,58 +12,52 @@ import ( // ListOutput provides a single page of Service results. const ListOutput = ` -{ - "links": { - "next": null, - "previous": null - }, - "services": [ - { - "id": "1234", - "links": { - "self": "https://example.com/identity/v3/services/1234" - }, - "type": "identity", - "enabled": false, - "extra": { - "name": "service-one", - "description": "Service One" - } - }, - { - "id": "9876", - "links": { - "self": "https://example.com/identity/v3/services/9876" - }, - "type": "compute", - "enabled": false, - "extra": { - "name": "service-two", - "description": "Service Two", - "email": "service@example.com" - } +{ + "services": [ + { + "name": "service-one", + "links": { + "self": "https://iamcore_links.com/v3/services/053d21d488d1463c818132d9d08fb617" + }, + "enabled": true, + "type": "compute", + "id": "053d21d488d1463c818132d9d08fb617", + "description": "Service One" + }, + { + "name": "service-two", + "links": { + "self": "https://iamcore_links.com/v3/services/c2474183dca7453bbd73123a0b78feae" + }, + "enabled": true, + "type": "compute", + "id": "c2474183dca7453bbd73123a0b78feae", + "description": "Service Two" } - ] + ], + "links": { + "self": "https://iamcore_links.com/v3/services?type=compute", + "previous": null, + "next": null + } } + ` // GetOutput provides a Get result. const GetOutput = ` -{ - "service": { - "id": "9876", - "links": { - "self": "https://example.com/identity/v3/services/9876" - }, - "type": "compute", - "enabled": false, - "extra": { - "name": "service-two", - "description": "Service Two", - "email": "service@example.com" - } - } +{ + "service": { + "enabled": true, + "type": "compute", + "name": "nova", + "links": { + "self": "10.10.10.10/v3/services/5a4ed456d228428c800ed2b67b4363a7" + }, + "id": "5a4ed456d228428c800ed2b67b4363a7" + } } + ` // CreateRequest provides the input to a Create request. @@ -71,7 +65,6 @@ const CreateRequest = ` { "service": { "description": "Service Two", - "email": "service@example.com", "name": "service-two", "type": "compute" } @@ -92,63 +85,52 @@ const UpdateRequest = ` const UpdateOutput = ` { "service": { - "id": "9876", - "links": { - "self": "https://example.com/identity/v3/services/9876" - }, - "type": "compute2", - "enabled": false, - "extra": { - "name": "service-two", - "description": "Service Two Updated", - "email": "service@example.com" - } + "name": "service-two", + "links": { + "self": "https://iamcore_links.com/v3/services/c2474183dca7453bbd73123a0b78feae" + }, + "enabled": true, + "type": "compute2", + "id": "c2474183dca7453bbd73123a0b78feae", + "description": "Service Two Updated" } } ` // FirstService is the first service in the List request. var FirstService = services.Service{ - ID: "1234", + ID: "053d21d488d1463c818132d9d08fb617", Links: map[string]interface{}{ - "self": "https://example.com/identity/v3/services/1234", - }, - Type: "identity", - Enabled: false, - Extra: map[string]interface{}{ - "name": "service-one", - "description": "Service One", + "self": "https://iamcore_links.com/v3/services/053d21d488d1463c818132d9d08fb617", }, + Type: "compute", + Enabled: true, + Name: "service-one", + Description: "Service One", } // SecondService is the second service in the List request. var SecondService = services.Service{ - ID: "9876", + ID: "c2474183dca7453bbd73123a0b78feae", Links: map[string]interface{}{ - "self": "https://example.com/identity/v3/services/9876", + "self": "https://iamcore_links.com/v3/services/c2474183dca7453bbd73123a0b78feae", }, Type: "compute", - Enabled: false, - Extra: map[string]interface{}{ - "name": "service-two", - "description": "Service Two", - "email": "service@example.com", - }, + Enabled: true, + Name: "service-two", + Description: "Service Two", } // SecondServiceUpdated is the SecondService should look after an Update. var SecondServiceUpdated = services.Service{ - ID: "9876", + ID: "c2474183dca7453bbd73123a0b78feae", Links: map[string]interface{}{ - "self": "https://example.com/identity/v3/services/9876", + "self": "https://iamcore_links.com/v3/services/c2474183dca7453bbd73123a0b78feae", }, Type: "compute2", - Enabled: false, - Extra: map[string]interface{}{ - "name": "service-two", - "description": "Service Two Updated", - "email": "service@example.com", - }, + Enabled: true, + Name: "service-two", + Description: "Service Two Updated", } // ExpectedServicesSlice is the slice of services to be returned from ListOutput. @@ -171,7 +153,7 @@ func HandleListServicesSuccessfully(t *testing.T) { // HandleGetServiceSuccessfully creates an HTTP handler at `/services` on the // test handler mux that responds with a single service. func HandleGetServiceSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/services/9876", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/services/c2474183dca7453bbd73123a0b78feae", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) @@ -198,7 +180,7 @@ func HandleCreateServiceSuccessfully(t *testing.T) { // HandleUpdateServiceSuccessfully creates an HTTP handler at `/services` on the // test handler mux that tests service update. func HandleUpdateServiceSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/services/9876", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/services/c2474183dca7453bbd73123a0b78feae", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, UpdateRequest) diff --git a/openstack/identity/v3/services/testing/requests_test.go b/openstack/identity/v3/services/testing/requests_test.go index 9777c7497..7564bdaa5 100644 --- a/openstack/identity/v3/services/testing/requests_test.go +++ b/openstack/identity/v3/services/testing/requests_test.go @@ -46,7 +46,7 @@ func TestListServices(t *testing.T) { return true, nil }) th.AssertNoErr(t, err) - th.CheckEquals(t, count, 1) + th.CheckEquals(t, count, 2) } func TestListServicesAllPages(t *testing.T) { @@ -59,8 +59,6 @@ func TestListServicesAllPages(t *testing.T) { actual, err := services.ExtractServices(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedServicesSlice, actual) - th.AssertEquals(t, ExpectedServicesSlice[0].Extra["name"], "service-one") - th.AssertEquals(t, ExpectedServicesSlice[1].Extra["email"], "service@example.com") } func TestGetSuccessful(t *testing.T) { @@ -68,11 +66,10 @@ func TestGetSuccessful(t *testing.T) { defer th.TeardownHTTP() HandleGetServiceSuccessfully(t) - actual, err := services.Get(client.ServiceClient(), "9876").Extract() + actual, err := services.Get(client.ServiceClient(), "c2474183dca7453bbd73123a0b78feae").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondService, *actual) - th.AssertEquals(t, SecondService.Extra["email"], "service@example.com") } func TestUpdateSuccessful(t *testing.T) { @@ -86,22 +83,21 @@ func TestUpdateSuccessful(t *testing.T) { "description": "Service Two Updated", }, } - actual, err := services.Update(client.ServiceClient(), "9876", updateOpts).Extract() + actual, err := services.Update(client.ServiceClient(), "c2474183dca7453bbd73123a0b78feae", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondServiceUpdated, *actual) - th.AssertEquals(t, SecondServiceUpdated.Extra["description"], "Service Two Updated") } func TestDeleteSuccessful(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/services/12345", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/services/053d21d488d1463c818132d9d08fb617", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) - res := services.Delete(client.ServiceClient(), "12345") + res := services.Delete(client.ServiceClient(), "053d21d488d1463c818132d9d08fb617") th.AssertNoErr(t, res.Err) } From 945d5771e2e6a45d64d8159fbef443239f05a4ae Mon Sep 17 00:00:00 2001 From: Huitse Tai Date: Thu, 15 Mar 2018 20:16:30 +0800 Subject: [PATCH 06/23] add sdk function users.UpdatePasswd and its testing codes --- .../openstack/identity/v3/users_test.go | 9 +++++ openstack/identity/v3/users/doc.go | 14 ++++++++ openstack/identity/v3/users/requests.go | 34 +++++++++++++++++++ openstack/identity/v3/users/results.go | 6 ++++ .../identity/v3/users/testing/fixtures.go | 26 ++++++++++++++ .../v3/users/testing/requests_test.go | 13 +++++++ openstack/identity/v3/users/urls.go | 4 +++ 7 files changed, 106 insertions(+) diff --git a/acceptance/openstack/identity/v3/users_test.go b/acceptance/openstack/identity/v3/users_test.go index 40b0aae85..3879da6cb 100644 --- a/acceptance/openstack/identity/v3/users_test.go +++ b/acceptance/openstack/identity/v3/users_test.go @@ -121,6 +121,15 @@ func TestUserCRUD(t *testing.T) { tools.PrintResource(t, newUser) tools.PrintResource(t, newUser.Extra) + + updatePasswdOpts := users.UpdatePasswdOpts{ + OriginalPassword: "foobar", + Password: "barfoo", + } + err = users.UpdatePasswd(client, user.ID, updatePasswdOpts).ExtractErr() + if err != nil { + t.Fatalf("Unable to update user password: %v", err) + } } func TestUsersListGroups(t *testing.T) { diff --git a/openstack/identity/v3/users/doc.go b/openstack/identity/v3/users/doc.go index 2b3b2e390..b8040724a 100644 --- a/openstack/identity/v3/users/doc.go +++ b/openstack/identity/v3/users/doc.go @@ -119,5 +119,19 @@ Example to List Users in a Group fmt.Printf("%+v\n", user) } +Example to Update User Password + + userID := "0fe36e73809d46aeae6705c39077b1b3" + + opts := users.UpdatePasswdOpts { + OriginalPassword: "old-user-password", + Password: "new-user-password", + } + + err := users.UpdatePasswd(identityClient, userID, opts).ExtractErr() + if err != nil { + panic(err) + } + */ package users diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index dea3a9852..ec585cac9 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -240,3 +240,37 @@ func ListInGroup(client *golangsdk.ServiceClient, groupID string, opts ListOptsB return UserPage{pagination.LinkedPageBase{PageResult: r}} }) } + +// UpdatePasswdOptsBuilder is the interface for password updating parameters +type UpdatePasswdOptsBuilder interface { + ToPasswdUpdateMap() (map[string]interface{}, error) +} + +// UpdatePasswdOpts provides options used to update user password +type UpdatePasswdOpts struct { + OriginalPassword string `json:"original_password"` + Password string `json:"password"` +} + +// ToPasswdUpdateMap formats a UpdatePasswdOpts into an http request +func (opts UpdatePasswdOpts) ToPasswdUpdateMap() ( + map[string]interface{}, error) { + return golangsdk.BuildRequestBody(opts, "user") +} + +// UpdatePasswd update password by user itself +func UpdatePasswd(client *golangsdk.ServiceClient, + userID string, opts UpdatePasswdOptsBuilder) (r UpdatePasswdResult) { + + b, err := opts.ToPasswdUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(updatePasswdURL(client, userID), &b, &r.Body, + &golangsdk.RequestOpts{ + OkCodes: []int{204}, + }, + ) + return +} diff --git a/openstack/identity/v3/users/results.go b/openstack/identity/v3/users/results.go index d3e434b97..102ab5f6c 100644 --- a/openstack/identity/v3/users/results.go +++ b/openstack/identity/v3/users/results.go @@ -147,3 +147,9 @@ func (r userResult) Extract() (*User, error) { err := r.ExtractInto(&s) return s.User, err } + +// UpdatePasswordResult is the response from updating password operation. Call +// its ExtractErr to determine the http response status. +type UpdatePasswdResult struct { + golangsdk.ErrResult +} diff --git a/openstack/identity/v3/users/testing/fixtures.go b/openstack/identity/v3/users/testing/fixtures.go index 7b5067dc7..71ada0fad 100644 --- a/openstack/identity/v3/users/testing/fixtures.go +++ b/openstack/identity/v3/users/testing/fixtures.go @@ -231,6 +231,17 @@ const ListProjectsOutput = ` } ` +// UpdatePasswdReUpdatePasswdRequest provides a demo update password request +// body. +const UpdatePasswdRequest = ` +{ + "user": { + "original_password": "secretsecret", + "password": "notthatsecret" + } +} +` + // FirstUser is the first user in the List request. var nilTime time.Time var FirstUser = users.User{ @@ -475,3 +486,18 @@ func HandleListInGroupSuccessfully(t *testing.T) { fmt.Fprintf(w, ListOutput) }) } + +// HandleUpdateUserPasswdSuccessfully creates an HTTP handler at +// "/users/{user_id}/password" on the test handler mux. +func HandleUpdateUserPasswdSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/users/9fe1d3", + func(w http.ResponseWriter, r *http.Request) { + + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestJSONRequest(t, r, UpdatePasswdRequest) + + w.WriteHeader(http.StatusNoContent) + }, + ) +} diff --git a/openstack/identity/v3/users/testing/requests_test.go b/openstack/identity/v3/users/testing/requests_test.go index bbef95fc6..f6abdfd4e 100644 --- a/openstack/identity/v3/users/testing/requests_test.go +++ b/openstack/identity/v3/users/testing/requests_test.go @@ -175,3 +175,16 @@ func TestListInGroup(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedUsersSlice, actual) } + +func TestUpdateUserPasswd(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateUserPasswdSuccessfully(t) + + opts := users.UpdatePasswdOpts{ + OriginalPassword: "secretsecret", + Password: "notthatsecret", + } + res := users.UpdatePasswd(client.ServiceClient(), "9fe1d3", opts) + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/identity/v3/users/urls.go b/openstack/identity/v3/users/urls.go index d3ae819c6..cd7b89b62 100644 --- a/openstack/identity/v3/users/urls.go +++ b/openstack/identity/v3/users/urls.go @@ -30,6 +30,10 @@ func listProjectsURL(client *golangsdk.ServiceClient, userID string) string { return client.ServiceURL("users", userID, "projects") } +func updatePasswdURL(client *golangsdk.ServiceClient, userID string) string { + return client.ServiceURL("users", userID, "password") +} + func listInGroupURL(client *golangsdk.ServiceClient, groupID string) string { return client.ServiceURL("groups", groupID, "users") } From 81d878211958e4f4c2027f2b3d20a76a2db6a1e6 Mon Sep 17 00:00:00 2001 From: Huitse Tai Date: Thu, 15 Mar 2018 21:10:05 +0800 Subject: [PATCH 07/23] fix unit test of TestUpdateUserPasswd --- openstack/identity/v3/users/requests.go | 2 +- openstack/identity/v3/users/testing/fixtures.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index ec585cac9..4f8f31cea 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -267,7 +267,7 @@ func UpdatePasswd(client *golangsdk.ServiceClient, r.Err = err return } - _, r.Err = client.Post(updatePasswdURL(client, userID), &b, &r.Body, + _, r.Err = client.Post(updatePasswdURL(client, userID), &b, nil, &golangsdk.RequestOpts{ OkCodes: []int{204}, }, diff --git a/openstack/identity/v3/users/testing/fixtures.go b/openstack/identity/v3/users/testing/fixtures.go index 71ada0fad..2e92d33c3 100644 --- a/openstack/identity/v3/users/testing/fixtures.go +++ b/openstack/identity/v3/users/testing/fixtures.go @@ -490,7 +490,7 @@ func HandleListInGroupSuccessfully(t *testing.T) { // HandleUpdateUserPasswdSuccessfully creates an HTTP handler at // "/users/{user_id}/password" on the test handler mux. func HandleUpdateUserPasswdSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/users/9fe1d3", + th.Mux.HandleFunc("/users/9fe1d3/password", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") From dd6c0369895eead4a62de251045e0e820fb299a1 Mon Sep 17 00:00:00 2001 From: Huitse Tai Date: Thu, 15 Mar 2018 21:40:18 +0800 Subject: [PATCH 08/23] add HEAD http method to ServiceClient objects --- service_client.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/service_client.go b/service_client.go index df6eaa23f..98d6c7b75 100644 --- a/service_client.go +++ b/service_client.go @@ -72,6 +72,17 @@ func (client *ServiceClient) Get(url string, JSONResponse interface{}, opts *Req return client.Request("GET", url, opts) } +// Head calls "Request" with the "HEAD" HTTP method. +func (client *ServiceClient) Head( + url string, opts *RequestOpts) (*http.Response, error) { + + if opts == nil { + opts = new(RequestOpts) + } + client.initReqOpts(url, nil, nil, opts) + return client.Request("HEAD", url, opts) +} + // Post calls `Request` with the "POST" HTTP verb. func (client *ServiceClient) Post(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { if opts == nil { From c8aa5ec176d6fe9bc6c8968978079b96c753674c Mon Sep 17 00:00:00 2001 From: Huitse Tai Date: Thu, 15 Mar 2018 22:00:46 +0800 Subject: [PATCH 09/23] add users.CheckGroupUser function to check if the user is in a group --- openstack/identity/v3/users/doc.go | 10 ++++++++++ openstack/identity/v3/users/requests.go | 12 ++++++++++++ openstack/identity/v3/users/results.go | 6 ++++++ openstack/identity/v3/users/testing/fixtures.go | 13 +++++++++++++ .../identity/v3/users/testing/requests_test.go | 9 +++++++++ openstack/identity/v3/users/urls.go | 6 ++++++ 6 files changed, 56 insertions(+) diff --git a/openstack/identity/v3/users/doc.go b/openstack/identity/v3/users/doc.go index b8040724a..d4032c61e 100644 --- a/openstack/identity/v3/users/doc.go +++ b/openstack/identity/v3/users/doc.go @@ -133,5 +133,15 @@ Example to Update User Password panic(err) } +Example to Check User in a Group + + groupID := "bede500ee1124ae9b0006ff859758b3a" + userID := "0fe36e73809d46aeae6705c39077b1b3" + + err := users.CheckGroupUser(identityClient, groupID, userID).ExtractErr() + if err != nil { + panic(err) + } + */ package users diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index 4f8f31cea..737d98d1c 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -274,3 +274,15 @@ func UpdatePasswd(client *golangsdk.ServiceClient, ) return } + +// CheckGroupUser check if the user does exist in the group +func CheckGroupUser(client *golangsdk.ServiceClient, + groupID string, userID string) (r CheckGroupUserResult) { + + _, r.Err = client.Head(operateOnGroupUserURL(client, groupID, userID), + &golangsdk.RequestOpts{ + OkCodes: []int{204}, + }, + ) + return +} diff --git a/openstack/identity/v3/users/results.go b/openstack/identity/v3/users/results.go index 102ab5f6c..631c15691 100644 --- a/openstack/identity/v3/users/results.go +++ b/openstack/identity/v3/users/results.go @@ -153,3 +153,9 @@ func (r userResult) Extract() (*User, error) { type UpdatePasswdResult struct { golangsdk.ErrResult } + +// CheckGroupUserResult is the response from checking the user is existed in +// the group. +type CheckGroupUserResult struct { + golangsdk.ErrResult +} diff --git a/openstack/identity/v3/users/testing/fixtures.go b/openstack/identity/v3/users/testing/fixtures.go index 2e92d33c3..06d452dee 100644 --- a/openstack/identity/v3/users/testing/fixtures.go +++ b/openstack/identity/v3/users/testing/fixtures.go @@ -501,3 +501,16 @@ func HandleUpdateUserPasswdSuccessfully(t *testing.T) { }, ) } + +// HandleCheckGroupUserSuccessfully creates an HTTP handler at +// "/groups/{group_id}/users/{user_id}" on the test handler mux. +func HandleCheckGroupUserSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/groups/ea167b/users/9fe1d3", + func(w http.ResponseWriter, r *http.Request) { + + th.TestMethod(t, r, "HEAD") + + w.WriteHeader(http.StatusNoContent) + }, + ) +} diff --git a/openstack/identity/v3/users/testing/requests_test.go b/openstack/identity/v3/users/testing/requests_test.go index f6abdfd4e..932886a87 100644 --- a/openstack/identity/v3/users/testing/requests_test.go +++ b/openstack/identity/v3/users/testing/requests_test.go @@ -188,3 +188,12 @@ func TestUpdateUserPasswd(t *testing.T) { res := users.UpdatePasswd(client.ServiceClient(), "9fe1d3", opts) th.AssertNoErr(t, res.Err) } + +func TestCheckGroupUser(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCheckGroupUserSuccessfully(t) + + res := users.CheckGroupUser(client.ServiceClient(), "ea167b", "9fe1d3") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/identity/v3/users/urls.go b/openstack/identity/v3/users/urls.go index cd7b89b62..148547c75 100644 --- a/openstack/identity/v3/users/urls.go +++ b/openstack/identity/v3/users/urls.go @@ -37,3 +37,9 @@ func updatePasswdURL(client *golangsdk.ServiceClient, userID string) string { func listInGroupURL(client *golangsdk.ServiceClient, groupID string) string { return client.ServiceURL("groups", groupID, "users") } + +func operateOnGroupUserURL(client *golangsdk.ServiceClient, + groupID string, userID string) string { + + return client.ServiceURL("groups", groupID, "users", userID) +} From f3c2f4155ff8cb885a1d1c44d66a3cf2a4fda3e0 Mon Sep 17 00:00:00 2001 From: Huitse Tai Date: Thu, 15 Mar 2018 22:13:24 +0800 Subject: [PATCH 10/23] add users.DeleteGroupUser function to remove a user from a group --- openstack/identity/v3/users/doc.go | 10 ++++++++++ openstack/identity/v3/users/requests.go | 12 ++++++++++++ openstack/identity/v3/users/results.go | 5 +++++ openstack/identity/v3/users/testing/fixtures.go | 13 +++++++++++++ .../identity/v3/users/testing/requests_test.go | 9 +++++++++ 5 files changed, 49 insertions(+) diff --git a/openstack/identity/v3/users/doc.go b/openstack/identity/v3/users/doc.go index d4032c61e..f729b51bf 100644 --- a/openstack/identity/v3/users/doc.go +++ b/openstack/identity/v3/users/doc.go @@ -143,5 +143,15 @@ Example to Check User in a Group panic(err) } +Example to Delete User from a Group + + groupID := "bede500ee1124ae9b0006ff859758b3a" + userID := "0fe36e73809d46aeae6705c39077b1b3" + + err := users.DeleteGroupUser(identityClient, groupID, userID).ExtractErr() + if err != nil { + panic(err) + } + */ package users diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index 737d98d1c..7b6a88c7c 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -286,3 +286,15 @@ func CheckGroupUser(client *golangsdk.ServiceClient, ) return } + +// DeleteGroupUser deletes a user from a group. +func DeleteGroupUser(client *golangsdk.ServiceClient, + groupID string, userID string) (r DeleteGroupUserResult) { + + _, r.Err = client.Delete(operateOnGroupUserURL(client, groupID, userID), + &golangsdk.RequestOpts{ + OkCodes: []int{204}, + }, + ) + return +} diff --git a/openstack/identity/v3/users/results.go b/openstack/identity/v3/users/results.go index 631c15691..7247122c3 100644 --- a/openstack/identity/v3/users/results.go +++ b/openstack/identity/v3/users/results.go @@ -159,3 +159,8 @@ type UpdatePasswdResult struct { type CheckGroupUserResult struct { golangsdk.ErrResult } + +// DeleteGroupUserResult is the response of deleting the user from a group. +type DeleteGroupUserResult struct { + golangsdk.ErrResult +} diff --git a/openstack/identity/v3/users/testing/fixtures.go b/openstack/identity/v3/users/testing/fixtures.go index 06d452dee..c4eac1581 100644 --- a/openstack/identity/v3/users/testing/fixtures.go +++ b/openstack/identity/v3/users/testing/fixtures.go @@ -514,3 +514,16 @@ func HandleCheckGroupUserSuccessfully(t *testing.T) { }, ) } + +// HandleDeleteGroupUserSuccessfully creates an HTTP handler at +// "/groups/{group_id}/users/{user_id}" on the test handler mux. +func HandleDeleteGroupUserSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/groups/ea167b/users/9fe1d3", + func(w http.ResponseWriter, r *http.Request) { + + th.TestMethod(t, r, "DELETE") + + w.WriteHeader(http.StatusNoContent) + }, + ) +} diff --git a/openstack/identity/v3/users/testing/requests_test.go b/openstack/identity/v3/users/testing/requests_test.go index 932886a87..e5855ed10 100644 --- a/openstack/identity/v3/users/testing/requests_test.go +++ b/openstack/identity/v3/users/testing/requests_test.go @@ -197,3 +197,12 @@ func TestCheckGroupUser(t *testing.T) { res := users.CheckGroupUser(client.ServiceClient(), "ea167b", "9fe1d3") th.AssertNoErr(t, res.Err) } + +func TestDeleteGroupUser(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteGroupUserSuccessfully(t) + + res := users.DeleteGroupUser(client.ServiceClient(), "ea167b", "9fe1d3") + th.AssertNoErr(t, res.Err) +} From 92c9334ab0ad53cdf8510c7b1c2465990fb2f115 Mon Sep 17 00:00:00 2001 From: Huitse Tai Date: Fri, 16 Mar 2018 00:01:22 +0800 Subject: [PATCH 11/23] add users.AddUserToGroup function to add a user to a group --- .../openstack/identity/v3/group_users_test.go | 76 +++++++++++++++++++ openstack/identity/v3/users/doc.go | 10 +++ openstack/identity/v3/users/requests.go | 15 ++++ openstack/identity/v3/users/results.go | 5 ++ .../identity/v3/users/testing/fixtures.go | 13 ++++ .../v3/users/testing/requests_test.go | 9 +++ 6 files changed, 128 insertions(+) create mode 100644 acceptance/openstack/identity/v3/group_users_test.go diff --git a/acceptance/openstack/identity/v3/group_users_test.go b/acceptance/openstack/identity/v3/group_users_test.go new file mode 100644 index 000000000..435f560da --- /dev/null +++ b/acceptance/openstack/identity/v3/group_users_test.go @@ -0,0 +1,76 @@ +// +build acceptance + +package v3 + +import ( + "testing" + + "github.com/huaweicloud/golangsdk/acceptance/clients" + "github.com/huaweicloud/golangsdk/acceptance/tools" + "github.com/huaweicloud/golangsdk/openstack/identity/v3/groups" + "github.com/huaweicloud/golangsdk/openstack/identity/v3/users" +) + +func TestGroupUsersCRUD(t *testing.T) { + + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + + // Create Group in the default domain + createGroupOpts := groups.CreateOpts{ + Name: "testgroup", + DomainID: "default", + Extra: map[string]interface{}{ + "email": "testgroup@example.com", + }, + } + group, err := CreateGroup(t, client, &createGroupOpts) + if err != nil { + t.Fatalf("Unable to create group: %v", err) + } + defer DeleteGroup(t, client, group.ID) + + tools.PrintResource(t, group) + tools.PrintResource(t, group.Extra) + + // Create a test user + createUserOpts := users.CreateOpts{ + Password: "foobar", + DomainID: "default", + Options: map[users.Option]interface{}{ + users.IgnorePasswordExpiry: true, + users.MultiFactorAuthRules: []interface{}{ + []string{"password", "totp"}, + []string{"password", "custom-auth-method"}, + }, + }, + Extra: map[string]interface{}{ + "email": "jsmith@example.com", + }, + } + user, err := CreateUser(t, client, &createOpts) + if err != nil { + t.Fatalf("Unable to create user: %v", err) + } + defer DeleteUser(t, client, user.ID) + + tools.PrintResource(t, user) + tools.PrintResource(t, user.Extra) + + err = users.AddUserToGroup(client, group.ID, user.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to add user to group: %v", err) + } + + err = users.CheckGroupUser(client, group.ID, user.ID).ExtractErr() + if err != nil { + t.Fatalf("Didn't successfully add user to group") + } + + err = users.DeleteGroupUser(client, group.ID, user.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to remove user from group: %v", err) + } +} diff --git a/openstack/identity/v3/users/doc.go b/openstack/identity/v3/users/doc.go index f729b51bf..c41511ab4 100644 --- a/openstack/identity/v3/users/doc.go +++ b/openstack/identity/v3/users/doc.go @@ -153,5 +153,15 @@ Example to Delete User from a Group panic(err) } +Example to Add a User to a Group + + groupID := "bede500ee1124ae9b0006ff859758b3a" + userID := "0fe36e73809d46aeae6705c39077b1b3" + + err := users.AddUserToGroup(identityClient, groupID, userID).ExtractErr() + if err != nil { + panic(err) + } + */ package users diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index 7b6a88c7c..7afee92e9 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -1,6 +1,8 @@ package users import ( + "net/http" + "github.com/huaweicloud/golangsdk" "github.com/huaweicloud/golangsdk/openstack/identity/v3/groups" "github.com/huaweicloud/golangsdk/openstack/identity/v3/projects" @@ -298,3 +300,16 @@ func DeleteGroupUser(client *golangsdk.ServiceClient, ) return } + +// AddUserToGroup add a user to a group +func AddUserToGroup(client *golangsdk.ServiceClient, + groupID, userID string) (r AddUserToGroupResult) { + + _, r.Err = client.Put(operateOnGroupUserURL(client, groupID, userID), + nil, nil, + &golangsdk.RequestOpts{ + OkCodes: []int{http.StatusNoContent}, + }, + ) + return +} diff --git a/openstack/identity/v3/users/results.go b/openstack/identity/v3/users/results.go index 7247122c3..c6855d137 100644 --- a/openstack/identity/v3/users/results.go +++ b/openstack/identity/v3/users/results.go @@ -164,3 +164,8 @@ type CheckGroupUserResult struct { type DeleteGroupUserResult struct { golangsdk.ErrResult } + +// AddUserToGroupResult is the response of adding a user to a group. +type AddUserToGroupResult struct { + golangsdk.ErrResult +} diff --git a/openstack/identity/v3/users/testing/fixtures.go b/openstack/identity/v3/users/testing/fixtures.go index c4eac1581..362924721 100644 --- a/openstack/identity/v3/users/testing/fixtures.go +++ b/openstack/identity/v3/users/testing/fixtures.go @@ -527,3 +527,16 @@ func HandleDeleteGroupUserSuccessfully(t *testing.T) { }, ) } + +// HandleAddUserToGroupSuccessfully creates an HTTP handler at +// "/groups/{group_id}/users/{user_id}" on the test handler mux. +func HandleAddUserToGroupSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/groups/ea167b/users/9fe1d3", + func(w http.ResponseWriter, r *http.Request) { + + th.TestMethod(t, r, "PUT") + + w.WriteHeader(http.StatusNoContent) + }, + ) +} diff --git a/openstack/identity/v3/users/testing/requests_test.go b/openstack/identity/v3/users/testing/requests_test.go index e5855ed10..09a29fb50 100644 --- a/openstack/identity/v3/users/testing/requests_test.go +++ b/openstack/identity/v3/users/testing/requests_test.go @@ -206,3 +206,12 @@ func TestDeleteGroupUser(t *testing.T) { res := users.DeleteGroupUser(client.ServiceClient(), "ea167b", "9fe1d3") th.AssertNoErr(t, res.Err) } + +func TestAddUserToGroup(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAddUserToGroupSuccessfully(t) + + res := users.AddUserToGroup(client.ServiceClient(), "ea167b", "9fe1d3") + th.AssertNoErr(t, res.Err) +} From 2975e5ae3f814b1ae6f4dee1ed6da5998bf613c2 Mon Sep 17 00:00:00 2001 From: Huitse Tai Date: Thu, 22 Mar 2018 15:04:32 +0800 Subject: [PATCH 12/23] Add two method to check roles of special target roles.CheckRoleOf to check if the role existed in a special target roles.ListRolesOf to list roles of special target --- .../openstack/identity/v3/roles_test.go | 48 +++++++ openstack/identity/v3/roles/doc.go | 38 ++++++ openstack/identity/v3/roles/requests.go | 120 ++++++++++++++++++ openstack/identity/v3/roles/results.go | 6 + .../identity/v3/roles/testing/fixtures.go | 88 +++++++++++++ .../v3/roles/testing/requests_test.go | 62 +++++++++ openstack/identity/v3/roles/urls.go | 20 +++ 7 files changed, 382 insertions(+) diff --git a/acceptance/openstack/identity/v3/roles_test.go b/acceptance/openstack/identity/v3/roles_test.go index 24a8bff2f..2b1aa3f2e 100644 --- a/acceptance/openstack/identity/v3/roles_test.go +++ b/acceptance/openstack/identity/v3/roles_test.go @@ -268,6 +268,30 @@ func TestRoleAssignToGroupOnDomain(t *testing.T) { for _, roleAssignment := range allRoleAssignments { tools.PrintResource(t, roleAssignment) } + + err = roles.CheckRoleOf(client, role.ID, roles.CheckRoleOfOpts{ + GroupID: group.ID, + DomainID: domain.ID, + }).ExtractErr() + if err != nil { + t.Fatalf("Unable to check role of domain: %v", err) + } + + allPages, err := roles.ListRolesOf(client, roles.CheckRoleOfOpts{ + GroupID: group.ID, + DomainID: domain.ID, + }).AllPages() + if err != nil { + t.Fatalf("Unable to list roles of domain: %v", err) + } + allRoles, err := roles.ExtractRoles(allPages) + if err != nil { + t.Fatalf("Unable to extract roles: %v", err) + } + + for _, role := range allRoles { + tools.PrintResource(t, role) + } } func TestRoleAssignToGroupOnProject(t *testing.T) { @@ -325,4 +349,28 @@ func TestRoleAssignToGroupOnProject(t *testing.T) { for _, roleAssignment := range allRoleAssignments { tools.PrintResource(t, roleAssignment) } + + err = roles.CheckRoleOf(client, role.ID, roles.CheckRoleOfOpts{ + GroupID: group.ID, + ProjectID: project.ID, + }).ExtractErr() + if err != nil { + t.Fatalf("Unable to check role of project: %v", err) + } + + allPages, err := roles.ListRolesOf(client, roles.CheckRoleOfOpts{ + GroupID: group.ID, + ProjectID: project.ID, + }).AllPages() + if err != nil { + t.Fatalf("Unable to list roles of project: %v", err) + } + allRoles, err := roles.ExtractRoles(allPages) + if err != nil { + t.Fatalf("Unable to extract roles: %v", err) + } + + for _, role := range allRoles { + tools.PrintResource(t, role) + } } diff --git a/openstack/identity/v3/roles/doc.go b/openstack/identity/v3/roles/doc.go index 2886a872d..f290e5492 100644 --- a/openstack/identity/v3/roles/doc.go +++ b/openstack/identity/v3/roles/doc.go @@ -105,6 +105,44 @@ Example to Unassign a Role From a User in a Project ProjectID: projectID, }).ExtractErr() + if err != nil { + panic(err) + } + +Example to List Role of a User in a Project + + projectID := "a99e9b4e620e4db09a2dfb6e42a01e66" + userID := "9df1a02f5eb2416a9781e8b0c022d3ae" + roleID := "9fe2ff9ee4384b1894a90878d3e92bab" + + allPages, err := roles.ListRolesOf(identityClient, roles.ListRolesOfOpts{ + UserID: userID, + ProjectID: projectID, + }).AllPages() + if err != nil { + panic(err) + } + + allRoles, err := roles.ExtractRoles(allPages) + if err != nil { + panic(err) + } + + for _, role := range allRoles { + fmt.Printf("%+v\n", role) + } + +Example to Check a Role Of a User in a Project + + projectID := "a99e9b4e620e4db09a2dfb6e42a01e66" + userID := "9df1a02f5eb2416a9781e8b0c022d3ae" + roleID := "9fe2ff9ee4384b1894a90878d3e92bab" + + err := roles.CheckRoleOf(identityClient, roleID, roles.CheckRoleOfOpts{ + UserID: userID, + ProjectID: projectID, + }).ExtractErr() + if err != nil { panic(err) } diff --git a/openstack/identity/v3/roles/requests.go b/openstack/identity/v3/roles/requests.go index 80f944da3..8ae2c795d 100644 --- a/openstack/identity/v3/roles/requests.go +++ b/openstack/identity/v3/roles/requests.go @@ -312,3 +312,123 @@ func Unassign(client *golangsdk.ServiceClient, roleID string, opts UnassignOpts) }) return } + +// ListRolesOfOpts provides options to list roles +type ListRolesOfOpts struct { + // UserID is the ID of a user to unassign a role + // Note: exactly one of UserID or GroupID must be provided + UserID string `xor:"GroupID"` + + // GroupID is the ID of a group to unassign a role + // Note: exactly one of UserID or GroupID must be provided + GroupID string `xor:"UserID"` + + // ProjectID is the ID of a project to unassign a role on + // Note: exactly one of ProjectID or DomainID must be provided + ProjectID string `xor:"DomainID"` + + // DomainID is the ID of a domain to unassign a role on + // Note: exactly one of ProjectID or DomainID must be provided + DomainID string `xor:"ProjectID"` +} + +// GetRolesFromGroup list all roles assigned to the group +func ListRolesOf( + client *golangsdk.ServiceClient, opts ListRolesOfOpts) pagination.Pager { + + // Check xor conditions + _, err := golangsdk.BuildRequestBody(opts, "") + if err != nil { + return pagination.Pager{Err: err} + } + + // Get corresponding URL + var targetID string + var targetType string + if opts.ProjectID != "" { + targetID = opts.ProjectID + targetType = "projects" + } else { + targetID = opts.DomainID + targetType = "domains" + } + + var actorID string + var actorType string + if opts.UserID != "" { + actorID = opts.UserID + actorType = "users" + } else { + actorID = opts.GroupID + actorType = "groups" + } + + return pagination.NewPager( + client, + listRolesOfURL(client, targetType, targetID, actorType, actorID), + func(r pagination.PageResult) pagination.Page { + return RolePage{pagination.LinkedPageBase{PageResult: r}} + }, + ) +} + +// CheckRoleOfOpts provides options to check role existed +type CheckRoleOfOpts struct { + // UserID is the ID of a user to unassign a role + // Note: exactly one of UserID or GroupID must be provided + UserID string `xor:"GroupID"` + + // GroupID is the ID of a group to unassign a role + // Note: exactly one of UserID or GroupID must be provided + GroupID string `xor:"UserID"` + + // ProjectID is the ID of a project to unassign a role on + // Note: exactly one of ProjectID or DomainID must be provided + ProjectID string `xor:"DomainID"` + + // DomainID is the ID of a domain to unassign a role on + // Note: exactly one of ProjectID or DomainID must be provided + DomainID string `xor:"ProjectID"` +} + +// CheckRoleOf check a role existed in a group of a domain or project +func CheckRoleOf(client *golangsdk.ServiceClient, + roleID string, opts CheckRoleOfOpts) (r CheckRoleOfResult) { + + // Check xor conditions + _, err := golangsdk.BuildRequestBody(opts, "") + if err != nil { + r.Err = err + return + } + + // Get corresponding URL + var targetID string + var targetType string + if opts.ProjectID != "" { + targetID = opts.ProjectID + targetType = "projects" + } else { + targetID = opts.DomainID + targetType = "domains" + } + + var actorID string + var actorType string + if opts.UserID != "" { + actorID = opts.UserID + actorType = "users" + } else { + actorID = opts.GroupID + actorType = "groups" + } + + _, r.Err = client.Head( + checkRoleOfURL(client, + targetType, targetID, actorType, actorID, roleID), + &golangsdk.RequestOpts{ + OkCodes: []int{204}, + }, + ) + return +} diff --git a/openstack/identity/v3/roles/results.go b/openstack/identity/v3/roles/results.go index deaf9fbd0..20ce10b99 100644 --- a/openstack/identity/v3/roles/results.go +++ b/openstack/identity/v3/roles/results.go @@ -227,3 +227,9 @@ type AssignmentResult struct { type UnassignmentResult struct { golangsdk.ErrResult } + +// CheckRoleOfResult is the response of checking a role existed in a group +// of a domain or project request. +type CheckRoleOfResult struct { + golangsdk.ErrResult +} diff --git a/openstack/identity/v3/roles/testing/fixtures.go b/openstack/identity/v3/roles/testing/fixtures.go index ce80c1344..0e6d52eff 100644 --- a/openstack/identity/v3/roles/testing/fixtures.go +++ b/openstack/identity/v3/roles/testing/fixtures.go @@ -331,3 +331,91 @@ func HandleListRoleAssignmentsSuccessfully(t *testing.T) { fmt.Fprintf(w, ListAssignmentOutput) }) } + +// HandleCheckRoleOfSuccessfully creates an HTTP handler for CheckRoleOf api +func HandleCheckRoleOfSuccessfully(t *testing.T) { + + th.Mux.HandleFunc( + "/domains/{domain_id}/groups/{group_id}/roles/{role_id}", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "HEAD") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) + + th.Mux.HandleFunc( + "/domains/{domain_id}/users/{user_id}/roles/{role_id}", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "HEAD") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) + + th.Mux.HandleFunc( + "/projects/{project_id}/groups/{group_id}/roles/{role_id}", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "HEAD") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) + + th.Mux.HandleFunc( + "/projects/{project_id}/users/{user_id}/roles/{role_id}", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "HEAD") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) +} + +// HandleListRolesOfSuccessfully creates an HTTP handler for ListRolesOf api +func HandleListRolesOfSuccessfully(t *testing.T) { + + th.Mux.HandleFunc( + "/domains/{domain_id}/groups/{group_id}/roles", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListOutput) + }) + + th.Mux.HandleFunc( + "/domains/{domain_id}/users/{user_id}/roles", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListOutput) + }) + + th.Mux.HandleFunc( + "/projects/{project_id}/groups/{group_id}/roles", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListOutput) + }) + + th.Mux.HandleFunc( + "/projects/{project_id}/users/{user_id}/roles", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListOutput) + }) +} diff --git a/openstack/identity/v3/roles/testing/requests_test.go b/openstack/identity/v3/roles/testing/requests_test.go index 318d36883..f3bb2b54b 100644 --- a/openstack/identity/v3/roles/testing/requests_test.go +++ b/openstack/identity/v3/roles/testing/requests_test.go @@ -174,3 +174,65 @@ func TestUnassign(t *testing.T) { }).ExtractErr() th.AssertNoErr(t, err) } + +func TestCheckRoleOf(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCheckRoleOfSuccessfully(t) + + err := roles.CheckRoleOf(client.ServiceClient(), "{role_id}", + roles.CheckRoleOfOpts{ + UserID: "{user_id}", + ProjectID: "{project_id}", + }).ExtractErr() + th.AssertNoErr(t, err) + + err = roles.CheckRoleOf(client.ServiceClient(), "{role_id}", + roles.CheckRoleOfOpts{ + GroupID: "{group_id}", + ProjectID: "{project_id}", + }).ExtractErr() + th.AssertNoErr(t, err) + + err = roles.CheckRoleOf(client.ServiceClient(), "{role_id}", + roles.CheckRoleOfOpts{ + UserID: "{user_id}", + DomainID: "{domain_id}", + }).ExtractErr() + th.AssertNoErr(t, err) + + err = roles.CheckRoleOf(client.ServiceClient(), "{role_id}", + roles.CheckRoleOfOpts{ + GroupID: "{group_id}", + DomainID: "{domain_id}", + }).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestListRolesOf(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListRolesOfSuccessfully(t) + + count := 0 + err := roles.ListRolesOf( + client.ServiceClient(), + roles.ListRolesOfOpts{ + UserID: "{user_id}", + ProjectID: "{project_id}", + }, + ).EachPage(func(page pagination.Page) (bool, error) { + + count++ + + actual, err := roles.ExtractRoles(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedRolesSlice, actual) + + return true, nil + }) + + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} diff --git a/openstack/identity/v3/roles/urls.go b/openstack/identity/v3/roles/urls.go index e7975d8f9..dca23dc25 100644 --- a/openstack/identity/v3/roles/urls.go +++ b/openstack/identity/v3/roles/urls.go @@ -33,3 +33,23 @@ func listAssignmentsURL(client *golangsdk.ServiceClient) string { func assignURL(client *golangsdk.ServiceClient, targetType, targetID, actorType, actorID, roleID string) string { return client.ServiceURL(targetType, targetID, actorType, actorID, rolePath, roleID) } + +func checkRoleOfURL(client *golangsdk.ServiceClient, + targetType, targetID, actorType, actorID, roleID string) string { + + return client.ServiceURL( + targetType, targetID, + actorType, actorID, + "roles", roleID, + ) +} + +func listRolesOfURL(client *golangsdk.ServiceClient, + targetType, targetID, actorType, actorID string) string { + + return client.ServiceURL( + targetType, targetID, + actorType, actorID, + "roles", + ) +} From 104a16f4adf32896c500fd4bf5bf136f2b8c86cc Mon Sep 17 00:00:00 2001 From: "Chen.Zhidong" Date: Tue, 27 Mar 2018 22:21:19 +0800 Subject: [PATCH 13/23] Fix domain struct --- openstack/identity/v3/domains/results.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openstack/identity/v3/domains/results.go b/openstack/identity/v3/domains/results.go index 5d84e8ca0..e6d4e8130 100644 --- a/openstack/identity/v3/domains/results.go +++ b/openstack/identity/v3/domains/results.go @@ -21,6 +21,9 @@ type Domain struct { // Name is the name of the domain. Name string `json:"name"` + + // EnterpriseName is the name of company user. + EnterpriseName string `json:"enterpriseName"` } type domainResult struct { From 8018a69e186c743bd24e46994d38a8a801115689 Mon Sep 17 00:00:00 2001 From: "Chen.Zhidong" Date: Tue, 27 Mar 2018 22:21:54 +0800 Subject: [PATCH 14/23] Fix endpoints fixture content --- .../identity/v3/endpoints/testing/requests_test.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/openstack/identity/v3/endpoints/testing/requests_test.go b/openstack/identity/v3/endpoints/testing/requests_test.go index 475a3505b..7715ce925 100644 --- a/openstack/identity/v3/endpoints/testing/requests_test.go +++ b/openstack/identity/v3/endpoints/testing/requests_test.go @@ -65,6 +65,9 @@ func TestCreateSuccessful(t *testing.T) { Region: "underground", ServiceID: "asdfasdfasdfasdf", URL: "https://1.2.3.4:9000/", + Links: map[string]interface{}{ + "self": "https://localhost:5000/v3/endpoints/12", + }, } th.AssertDeepEquals(t, expected, actual) @@ -130,6 +133,9 @@ func TestListEndpoints(t *testing.T) { Region: "underground", ServiceID: "asdfasdfasdfasdf", URL: "https://1.2.3.4:9000/", + Links: map[string]interface{}{ + "self": "https://localhost:5000/v3/endpoints/12", + }, }, { ID: "13", @@ -138,6 +144,9 @@ func TestListEndpoints(t *testing.T) { Region: "underground", ServiceID: "asdfasdfasdfasdf", URL: "https://1.2.3.4:9001/", + Links: map[string]interface{}{ + "self": "https://localhost:5000/v3/endpoints/13", + }, }, } th.AssertDeepEquals(t, expected, actual) @@ -194,6 +203,9 @@ func TestUpdateEndpoint(t *testing.T) { Region: "somewhere-else", ServiceID: "asdfasdfasdfasdf", URL: "https://1.2.3.4:9000/", + Links: map[string]interface{}{ + "self": "https://localhost:5000/v3/endpoints/12", + }, } th.AssertDeepEquals(t, expected, actual) } @@ -254,7 +266,7 @@ func TestGetEnpoint(t *testing.T) { URL: "https://1.2.3.4:9000/", RegionID: "qwerqwerqwer", Enabled: true, - Links: map[string]string{ + Links: map[string]interface{}{ "self": "https://localhost:5000/v3/endpoints/12", }, } From 15a489bd1bb3f9dd2c3abacf695d46b179b96da5 Mon Sep 17 00:00:00 2001 From: "Chen.Zhidong" Date: Tue, 27 Mar 2018 22:22:11 +0800 Subject: [PATCH 15/23] Fix service struct --- openstack/identity/v3/services/results.go | 2 +- .../identity/v3/services/testing/fixtures.go | 70 +++++++++++++------ .../v3/services/testing/requests_test.go | 2 +- 3 files changed, 52 insertions(+), 22 deletions(-) diff --git a/openstack/identity/v3/services/results.go b/openstack/identity/v3/services/results.go index 2db066316..acbfc0e7f 100644 --- a/openstack/identity/v3/services/results.go +++ b/openstack/identity/v3/services/results.go @@ -61,7 +61,7 @@ type Service struct { Links map[string]interface{} `json:"links"` // Extra is a collection of miscellaneous key/values. - Extra map[string]interface{} `json:"-"` + Extra map[string]interface{} `json:"-,omitempty"` // Description is the description of the service. Description string `json:"description"` diff --git a/openstack/identity/v3/services/testing/fixtures.go b/openstack/identity/v3/services/testing/fixtures.go index b2cec25a7..547cd937d 100644 --- a/openstack/identity/v3/services/testing/fixtures.go +++ b/openstack/identity/v3/services/testing/fixtures.go @@ -22,7 +22,11 @@ const ListOutput = ` "enabled": true, "type": "compute", "id": "053d21d488d1463c818132d9d08fb617", - "description": "Service One" + "description": "Service One", + "extra": { + "name": "service-one", + "description": "Service One" + } }, { "name": "service-two", @@ -32,7 +36,11 @@ const ListOutput = ` "enabled": true, "type": "compute", "id": "c2474183dca7453bbd73123a0b78feae", - "description": "Service Two" + "description": "Service Two", + "extra": { + "name": "service-two", + "description": "Service Two" + } } ], "links": { @@ -48,13 +56,18 @@ const ListOutput = ` const GetOutput = ` { "service": { - "enabled": true, - "type": "compute", - "name": "nova", - "links": { - "self": "10.10.10.10/v3/services/5a4ed456d228428c800ed2b67b4363a7" - }, - "id": "5a4ed456d228428c800ed2b67b4363a7" + "name": "service-two", + "links": { + "self": "https://iamcore_links.com/v3/services/c2474183dca7453bbd73123a0b78feae" + }, + "enabled": true, + "type": "compute", + "id": "c2474183dca7453bbd73123a0b78feae", + "description": "Service Two", + "extra": { + "name": "service-two", + "description": "Service Two" + } } } @@ -66,7 +79,8 @@ const CreateRequest = ` "service": { "description": "Service Two", "name": "service-two", - "type": "compute" + "type": "compute", + "email": "service@example.com" } } ` @@ -92,7 +106,11 @@ const UpdateOutput = ` "enabled": true, "type": "compute2", "id": "c2474183dca7453bbd73123a0b78feae", - "description": "Service Two Updated" + "description": "Service Two Updated", + "extra": { + "name": "service-two", + "description": "Service Two Updated" + } } } ` @@ -103,10 +121,14 @@ var FirstService = services.Service{ Links: map[string]interface{}{ "self": "https://iamcore_links.com/v3/services/053d21d488d1463c818132d9d08fb617", }, - Type: "compute", - Enabled: true, - Name: "service-one", + Type: "compute", + Enabled: true, + Name: "service-one", Description: "Service One", + Extra: map[string]interface{}{ + "name": "service-one", + "description": "Service One", + }, } // SecondService is the second service in the List request. @@ -115,10 +137,14 @@ var SecondService = services.Service{ Links: map[string]interface{}{ "self": "https://iamcore_links.com/v3/services/c2474183dca7453bbd73123a0b78feae", }, - Type: "compute", - Enabled: true, - Name: "service-two", + Type: "compute", + Enabled: true, + Name: "service-two", Description: "Service Two", + Extra: map[string]interface{}{ + "name": "service-two", + "description": "Service Two", + }, } // SecondServiceUpdated is the SecondService should look after an Update. @@ -127,10 +153,14 @@ var SecondServiceUpdated = services.Service{ Links: map[string]interface{}{ "self": "https://iamcore_links.com/v3/services/c2474183dca7453bbd73123a0b78feae", }, - Type: "compute2", - Enabled: true, - Name: "service-two", + Type: "compute2", + Enabled: true, + Name: "service-two", Description: "Service Two Updated", + Extra: map[string]interface{}{ + "name": "service-two", + "description": "Service Two Updated", + }, } // ExpectedServicesSlice is the slice of services to be returned from ListOutput. diff --git a/openstack/identity/v3/services/testing/requests_test.go b/openstack/identity/v3/services/testing/requests_test.go index 7564bdaa5..3722da40e 100644 --- a/openstack/identity/v3/services/testing/requests_test.go +++ b/openstack/identity/v3/services/testing/requests_test.go @@ -46,7 +46,7 @@ func TestListServices(t *testing.T) { return true, nil }) th.AssertNoErr(t, err) - th.CheckEquals(t, count, 2) + th.CheckEquals(t, count, 1) } func TestListServicesAllPages(t *testing.T) { From 2e84fe7c246cf8a9411619687b1fe699d7144cc4 Mon Sep 17 00:00:00 2001 From: "Chen.Zhidong" Date: Wed, 28 Mar 2018 23:26:15 +0800 Subject: [PATCH 16/23] Fix acceptance of identity --- acceptance/openstack/client_test.go | 2 ++ acceptance/openstack/identity/v2/tenant_test.go | 4 ++-- acceptance/openstack/identity/v3/endpoint_test.go | 8 ++++---- acceptance/openstack/identity/v3/group_users_test.go | 2 +- acceptance/openstack/identity/v3/projects_test.go | 8 ++++---- acceptance/openstack/identity/v3/roles_test.go | 4 ++-- acceptance/openstack/identity/v3/service_test.go | 2 +- acceptance/openstack/identity/v3/token_test.go | 2 +- script/acceptancetest | 2 +- 9 files changed, 18 insertions(+), 16 deletions(-) diff --git a/acceptance/openstack/client_test.go b/acceptance/openstack/client_test.go index 895b23666..a5350ae21 100644 --- a/acceptance/openstack/client_test.go +++ b/acceptance/openstack/client_test.go @@ -18,6 +18,8 @@ func TestAuthenticatedClient(t *testing.T) { t.Fatalf("Unable to acquire credentials: %v", err) } + t.Logf("AuthOptionsFromEnv: %+v", ao) + client, err := openstack.AuthenticatedClient(ao) if err != nil { t.Fatalf("Unable to authenticate: %v", err) diff --git a/acceptance/openstack/identity/v2/tenant_test.go b/acceptance/openstack/identity/v2/tenant_test.go index caea3b516..45a2683cd 100644 --- a/acceptance/openstack/identity/v2/tenant_test.go +++ b/acceptance/openstack/identity/v2/tenant_test.go @@ -13,7 +13,7 @@ import ( func TestTenantsList(t *testing.T) { client, err := clients.NewIdentityV2Client() if err != nil { - t.Fatalf("Unable to obtain an identity client: %v") + t.Fatalf("Unable to obtain an identity client: %v", err) } allPages, err := tenants.List(client, nil).AllPages() @@ -34,7 +34,7 @@ func TestTenantsList(t *testing.T) { func TestTenantsCRUD(t *testing.T) { client, err := clients.NewIdentityV2AdminClient() if err != nil { - t.Fatalf("Unable to obtain an identity client: %v") + t.Fatalf("Unable to obtain an identity client: %v", err) } tenant, err := CreateTenant(t, client, nil) diff --git a/acceptance/openstack/identity/v3/endpoint_test.go b/acceptance/openstack/identity/v3/endpoint_test.go index ded3b4013..8a65a292d 100644 --- a/acceptance/openstack/identity/v3/endpoint_test.go +++ b/acceptance/openstack/identity/v3/endpoint_test.go @@ -15,7 +15,7 @@ import ( func TestEndpointsList(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { - t.Fatalf("Unable to obtain an identity client: %v") + t.Fatalf("Unable to obtain an identity client: %v", err) } allPages, err := endpoints.List(client, nil).AllPages() @@ -36,7 +36,7 @@ func TestEndpointsList(t *testing.T) { func TestEndpointsNavigateCatalog(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { - t.Fatalf("Unable to obtain an identity client: %v") + t.Fatalf("Unable to obtain an identity client: %v", err) } // Discover the service we're interested in. @@ -51,7 +51,7 @@ func TestEndpointsNavigateCatalog(t *testing.T) { allServices, err := services.ExtractServices(allPages) if err != nil { - t.Fatalf("Unable to extract service: %v") + t.Fatalf("Unable to extract service: %v", err) } if len(allServices) != 1 { @@ -74,7 +74,7 @@ func TestEndpointsNavigateCatalog(t *testing.T) { allEndpoints, err := endpoints.ExtractEndpoints(allPages) if err != nil { - t.Fatalf("Unable to extract endpoint: %v") + t.Fatalf("Unable to extract endpoint: %v", err) } if len(allEndpoints) != 1 { diff --git a/acceptance/openstack/identity/v3/group_users_test.go b/acceptance/openstack/identity/v3/group_users_test.go index 435f560da..1a23b06bc 100644 --- a/acceptance/openstack/identity/v3/group_users_test.go +++ b/acceptance/openstack/identity/v3/group_users_test.go @@ -50,7 +50,7 @@ func TestGroupUsersCRUD(t *testing.T) { "email": "jsmith@example.com", }, } - user, err := CreateUser(t, client, &createOpts) + user, err := CreateUser(t, client, &createUserOpts) if err != nil { t.Fatalf("Unable to create user: %v", err) } diff --git a/acceptance/openstack/identity/v3/projects_test.go b/acceptance/openstack/identity/v3/projects_test.go index 545059b9e..af3f032ff 100644 --- a/acceptance/openstack/identity/v3/projects_test.go +++ b/acceptance/openstack/identity/v3/projects_test.go @@ -64,7 +64,7 @@ func TestProjectsGet(t *testing.T) { func TestProjectsCRUD(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { - t.Fatalf("Unable to obtain an identity client: %v") + t.Fatalf("Unable to obtain an identity client: %v", err) } project, err := CreateProject(t, client, nil) @@ -91,7 +91,7 @@ func TestProjectsCRUD(t *testing.T) { func TestProjectsDomain(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { - t.Fatalf("Unable to obtain an identity client: %v") + t.Fatalf("Unable to obtain an identity client: %v", err) } var iTrue = true @@ -126,14 +126,14 @@ func TestProjectsDomain(t *testing.T) { _, err = projects.Update(client, projectDomain.ID, updateOpts).Extract() if err != nil { - t.Fatalf("Unable to disable domain: %v") + t.Fatalf("Unable to disable domain: %v", err) } } func TestProjectsNested(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { - t.Fatalf("Unable to obtain an identity client: %v") + t.Fatalf("Unable to obtain an identity client: %v", err) } projectMain, err := CreateProject(t, client, nil) diff --git a/acceptance/openstack/identity/v3/roles_test.go b/acceptance/openstack/identity/v3/roles_test.go index 2b1aa3f2e..43196151e 100644 --- a/acceptance/openstack/identity/v3/roles_test.go +++ b/acceptance/openstack/identity/v3/roles_test.go @@ -277,7 +277,7 @@ func TestRoleAssignToGroupOnDomain(t *testing.T) { t.Fatalf("Unable to check role of domain: %v", err) } - allPages, err := roles.ListRolesOf(client, roles.CheckRoleOfOpts{ + allPages, err = roles.ListRolesOf(client, roles.ListRolesOfOpts{ GroupID: group.ID, DomainID: domain.ID, }).AllPages() @@ -358,7 +358,7 @@ func TestRoleAssignToGroupOnProject(t *testing.T) { t.Fatalf("Unable to check role of project: %v", err) } - allPages, err := roles.ListRolesOf(client, roles.CheckRoleOfOpts{ + allPages, err = roles.ListRolesOf(client, roles.ListRolesOfOpts{ GroupID: group.ID, ProjectID: project.ID, }).AllPages() diff --git a/acceptance/openstack/identity/v3/service_test.go b/acceptance/openstack/identity/v3/service_test.go index 2eaf0595b..4c0f845e1 100644 --- a/acceptance/openstack/identity/v3/service_test.go +++ b/acceptance/openstack/identity/v3/service_test.go @@ -13,7 +13,7 @@ import ( func TestServicesList(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { - t.Fatalf("Unable to obtain an identity client: %v") + t.Fatalf("Unable to obtain an identity client: %v", err) } listOpts := services.ListOpts{ diff --git a/acceptance/openstack/identity/v3/token_test.go b/acceptance/openstack/identity/v3/token_test.go index a32553d0e..9bd3ae28e 100644 --- a/acceptance/openstack/identity/v3/token_test.go +++ b/acceptance/openstack/identity/v3/token_test.go @@ -14,7 +14,7 @@ import ( func TestGetToken(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { - t.Fatalf("Unable to obtain an identity client: %v") + t.Fatalf("Unable to obtain an identity client: %v", err) } ao, err := openstack.AuthOptionsFromEnv() diff --git a/script/acceptancetest b/script/acceptancetest index 5ed5b6d2f..1b534f290 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -2,4 +2,4 @@ # # Run the acceptance tests. -exec go test -p=1 github.com/huaweicloud/golangsdk/acceptance/... $@ +exec go test -tags 'acceptance' -p=1 github.com/huaweicloud/golangsdk/acceptance/... $@ From 8edf8c4d824b293ab777cc41e82f2cad87e028fd Mon Sep 17 00:00:00 2001 From: "Chen.Zhidong" Date: Thu, 29 Mar 2018 01:06:09 +0800 Subject: [PATCH 17/23] HuaweiCloud use object instead of object-store for ObjectStorage's type --- openstack/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/client.go b/openstack/client.go index 90e917331..e26cad49b 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -287,7 +287,7 @@ func initClientOpts(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts, // NewObjectStorageV1 creates a ServiceClient that may be used with the v1 // object storage package. func NewObjectStorageV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) { - return initClientOpts(client, eo, "object-store") + return initClientOpts(client, eo, "object") } // NewComputeV2 creates a ServiceClient that may be used with the v2 compute From 3a42c8f77f98c1303e4fd0696a0bc1701d1b6298 Mon Sep 17 00:00:00 2001 From: "Chen.Zhidong" Date: Thu, 29 Mar 2018 01:20:15 +0800 Subject: [PATCH 18/23] Fix domains url path --- openstack/identity/v3/domains/urls.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openstack/identity/v3/domains/urls.go b/openstack/identity/v3/domains/urls.go index fad3eee89..4e202ccbc 100644 --- a/openstack/identity/v3/domains/urls.go +++ b/openstack/identity/v3/domains/urls.go @@ -3,21 +3,21 @@ package domains import "github.com/huaweicloud/golangsdk" func listURL(client *golangsdk.ServiceClient) string { - return client.ServiceURL("domains") + return client.ServiceURL("auth/domains") } func getURL(client *golangsdk.ServiceClient, domainID string) string { - return client.ServiceURL("domains", domainID) + return client.ServiceURL("auth/domains", domainID) } func createURL(client *golangsdk.ServiceClient) string { - return client.ServiceURL("domains") + return client.ServiceURL("auth/domains") } func deleteURL(client *golangsdk.ServiceClient, domainID string) string { - return client.ServiceURL("domains", domainID) + return client.ServiceURL("auth/domains", domainID) } func updateURL(client *golangsdk.ServiceClient, domainID string) string { - return client.ServiceURL("domains", domainID) + return client.ServiceURL("auth/domains", domainID) } From 3faecf13fdacc9b4bf319abce850bca3a75aad6b Mon Sep 17 00:00:00 2001 From: Huitse Tai Date: Tue, 3 Apr 2018 13:27:07 +0800 Subject: [PATCH 19/23] Fix the unit test for domains url path change --- openstack/identity/v3/domains/testing/fixtures.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openstack/identity/v3/domains/testing/fixtures.go b/openstack/identity/v3/domains/testing/fixtures.go index 60d904325..cc89617a0 100644 --- a/openstack/identity/v3/domains/testing/fixtures.go +++ b/openstack/identity/v3/domains/testing/fixtures.go @@ -125,7 +125,7 @@ var ExpectedDomainsSlice = []domains.Domain{FirstDomain, SecondDomain} // HandleListDomainsSuccessfully creates an HTTP handler at `/domains` on the // test handler mux that responds with a list of two domains. func HandleListDomainsSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/domains", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/auth/domains", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) @@ -139,7 +139,7 @@ func HandleListDomainsSuccessfully(t *testing.T) { // HandleGetDomainSuccessfully creates an HTTP handler at `/domains` on the // test handler mux that responds with a single domain. func HandleGetDomainSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/domains/9fe1d3", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/auth/domains/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) @@ -153,7 +153,7 @@ func HandleGetDomainSuccessfully(t *testing.T) { // HandleCreateDomainSuccessfully creates an HTTP handler at `/domains` on the // test handler mux that tests domain creation. func HandleCreateDomainSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/domains", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/auth/domains", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, CreateRequest) @@ -166,7 +166,7 @@ func HandleCreateDomainSuccessfully(t *testing.T) { // HandleDeleteDomainSuccessfully creates an HTTP handler at `/domains` on the // test handler mux that tests domain deletion. func HandleDeleteDomainSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/domains/9fe1d3", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/auth/domains/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) @@ -177,7 +177,7 @@ func HandleDeleteDomainSuccessfully(t *testing.T) { // HandleUpdateDomainSuccessfully creates an HTTP handler at `/domains` on the // test handler mux that tests domain update. func HandleUpdateDomainSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/domains/9fe1d3", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/auth/domains/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, UpdateRequest) From 5a0a09a50cf794d93f41d92296c6b32abd82a173 Mon Sep 17 00:00:00 2001 From: Huitse Tai Date: Tue, 3 Apr 2018 17:44:36 +0800 Subject: [PATCH 20/23] complete integration test for all api functions --- .../openstack/identity/v3/endpoint_test.go | 28 ++++++++++++++ .../openstack/identity/v3/group_users_test.go | 9 +++++ .../openstack/identity/v3/service_test.go | 38 +++++++++++++++++++ 3 files changed, 75 insertions(+) diff --git a/acceptance/openstack/identity/v3/endpoint_test.go b/acceptance/openstack/identity/v3/endpoint_test.go index 8a65a292d..8ac65e77c 100644 --- a/acceptance/openstack/identity/v3/endpoint_test.go +++ b/acceptance/openstack/identity/v3/endpoint_test.go @@ -33,6 +33,34 @@ func TestEndpointsList(t *testing.T) { } } +func TestEndpointsGet(t *testing.T) { + + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + + allPages, err := endpoints.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list endpoints: %v", err) + } + + allEndpoints, err := endpoints.ExtractEndpoints(allPages) + if err != nil { + t.Fatalf("Unable to extract endpoints: %v", err) + } + + if len(allEndpoints) > 0 { + endpoint := allEndpoints[0] + p, err := endpoints.Get(client, endpoint.ID).Extract() + if err != nil { + t.Fatalf("Unable to get endpoint: %v", err) + } + + tools.PrintResource(t, p) + } +} + func TestEndpointsNavigateCatalog(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { diff --git a/acceptance/openstack/identity/v3/group_users_test.go b/acceptance/openstack/identity/v3/group_users_test.go index 1a23b06bc..27482b9c8 100644 --- a/acceptance/openstack/identity/v3/group_users_test.go +++ b/acceptance/openstack/identity/v3/group_users_test.go @@ -64,6 +64,15 @@ func TestGroupUsersCRUD(t *testing.T) { t.Fatalf("Unable to add user to group: %v", err) } + allPages, err := users.ListInGroup(client, group.ID, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list users: %v", err) + } + _, err = users.ExtractUsers(allPages) + if err != nil { + t.Fatalf("Unable to extract users: %v", err) + } + err = users.CheckGroupUser(client, group.ID, user.ID).ExtractErr() if err != nil { t.Fatalf("Didn't successfully add user to group") diff --git a/acceptance/openstack/identity/v3/service_test.go b/acceptance/openstack/identity/v3/service_test.go index 4c0f845e1..13d60f370 100644 --- a/acceptance/openstack/identity/v3/service_test.go +++ b/acceptance/openstack/identity/v3/service_test.go @@ -36,6 +36,39 @@ func TestServicesList(t *testing.T) { } +func TestServicesGet(t *testing.T) { + + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + + listOpts := services.ListOpts{ + ServiceType: "identity", + } + + allPages, err := services.List(client, listOpts).AllPages() + if err != nil { + t.Fatalf("Unable to list services: %v", err) + } + + allServices, err := services.ExtractServices(allPages) + if err != nil { + t.Fatalf("Unable to extract services: %v", err) + } + + if len(allServices) > 0 { + + service := allServices[0] + p, err := services.Get(client, service.ID).Extract() + if err != nil { + t.Fatalf("Unable to get service: %v", err) + } + + tools.PrintResource(t, p) + } +} + func TestServicesCRUD(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { @@ -56,6 +89,11 @@ func TestServicesCRUD(t *testing.T) { } defer DeleteService(t, client, service.ID) + service, err = services.Get(client, service.ID).Extract() + if err != nil { + t.Fatalf("Unable to read service: %v", err) + } + tools.PrintResource(t, service) tools.PrintResource(t, service.Extra) From f4fc6dd2da04363ffdbbfb19f0d743df2c64c55d Mon Sep 17 00:00:00 2001 From: Huitse Tai Date: Tue, 3 Apr 2018 17:53:07 +0800 Subject: [PATCH 21/23] fix acceptance test TestGetToken --- acceptance/openstack/identity/v3/token_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/openstack/identity/v3/token_test.go b/acceptance/openstack/identity/v3/token_test.go index 9bd3ae28e..979840191 100644 --- a/acceptance/openstack/identity/v3/token_test.go +++ b/acceptance/openstack/identity/v3/token_test.go @@ -25,7 +25,7 @@ func TestGetToken(t *testing.T) { authOptions := tokens.AuthOptions{ Username: ao.Username, Password: ao.Password, - DomainName: "default", + DomainName: ao.DomainName, } token, err := tokens.Create(client, &authOptions).Extract() From eafe97290baa9e2f1b132bc21f9250233c2ac07c Mon Sep 17 00:00:00 2001 From: "Chen.Zhidong" Date: Sun, 8 Apr 2018 18:25:28 +0800 Subject: [PATCH 22/23] Fix useless omitempty --- openstack/identity/v3/services/results.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/identity/v3/services/results.go b/openstack/identity/v3/services/results.go index acbfc0e7f..2db066316 100644 --- a/openstack/identity/v3/services/results.go +++ b/openstack/identity/v3/services/results.go @@ -61,7 +61,7 @@ type Service struct { Links map[string]interface{} `json:"links"` // Extra is a collection of miscellaneous key/values. - Extra map[string]interface{} `json:"-,omitempty"` + Extra map[string]interface{} `json:"-"` // Description is the description of the service. Description string `json:"description"` From 6dacc4aed29fee35b062143a9b7c72af473026f2 Mon Sep 17 00:00:00 2001 From: "Chen.Zhidong" Date: Mon, 9 Apr 2018 11:45:42 +0800 Subject: [PATCH 23/23] Revert to object-store as object-store and object are different --- openstack/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/client.go b/openstack/client.go index e26cad49b..90e917331 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -287,7 +287,7 @@ func initClientOpts(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts, // NewObjectStorageV1 creates a ServiceClient that may be used with the v1 // object storage package. func NewObjectStorageV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) { - return initClientOpts(client, eo, "object") + return initClientOpts(client, eo, "object-store") } // NewComputeV2 creates a ServiceClient that may be used with the v2 compute