Skip to content

Commit

Permalink
Add CRUD operations for Org user (#210)
Browse files Browse the repository at this point in the history
* Update AdminOrg structure to include roles and users
* Define the new type User
* Define auxiliary types RoleReference, RightReference
* Add AdminOrg methods GetRole, FetchUserByHref
* Add AdminOrg methods FetchUserByName, FetchUserById, FetchUserByNameOrId 
* Add AdminOrg methods CreateUser, CreateUserSimple
* Add OrgUser methods GetRoleName, Update, Enable, Disable
* Add OrgUser methods Delete
* Add OrgUser methods ChangePassword, Unlock, TakeOwnership
* Add tests for org user retrieval, creation, update, deletion
* Make sure that System Admin and Org Admin can run these functions

Implement recommendations from Issue #211
  • Loading branch information
dataclouder authored Jul 5, 2019
1 parent 8d15560 commit ed123fe
Show file tree
Hide file tree
Showing 8 changed files with 913 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Added load balancer server pool [#205](https://github.com/vmware/go-vcloud-director/pull/205)
* Added functions for refreshing, getting and update Org VDC [#206](https://github.com/vmware/go-vcloud-director/pull/206)
* Added VDC meta data create/get/delete functions [#203](https://github.com/vmware/go-vcloud-director/pull/203)
* Added org user create/delete/update functions [#18](https://github.com/vmware/go-vcloud-director/issues/18)

## 2.2.0 (May 15, 2019)

Expand Down
80 changes: 79 additions & 1 deletion govcd/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import (
"bytes"
"encoding/xml"
"fmt"

"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"reflect"
"strings"

Expand All @@ -36,6 +36,80 @@ type Client struct {
MaxRetryTimeout int
}

// General purpose error to be used whenever an entity is not found from a "GET" request
// Allows a simpler checking of the call result
// such as
// if err == ErrorEntityNotFound {
// // do what is needed in case of not found
// }
var ErrorEntityNotFound = fmt.Errorf("entity not found")

// Triggers for debugging functions that show requests and responses
var debugShowRequestEnabled = os.Getenv("GOVCD_SHOW_REQ") != ""
var debugShowResponseEnabled = os.Getenv("GOVCD_SHOW_RESP") != ""

// Enables the debugging hook to show requests as they are processed.
func enableDebugShowRequest() {
debugShowRequestEnabled = true
}

// Disables the debugging hook to show requests as they are processed.
func disableDebugShowRequest() {
debugShowRequestEnabled = false
_ = os.Setenv("GOVCD_SHOW_REQ", "")
}

// Enables the debugging hook to show responses as they are processed.
func enableDebugShowResponse() {
debugShowResponseEnabled = true
}

// Disables the debugging hook to show responses as they are processed.
func disableDebugShowResponse() {
debugShowResponseEnabled = false
_ = os.Setenv("GOVCD_SHOW_RESP", "")
}

// On-the-fly debug hook. If either debugShowRequestEnabled or the environment
// variable "GOVCD_SHOW_REQ" are enabled, this function will show the contents
// of the request as it is being processed.
func debugShowRequest(req *http.Request, payload string) {
if debugShowRequestEnabled {
header := "[\n"
for key, value := range req.Header {
header += fmt.Sprintf("\t%s => %s\n", key, value)
}
header += "]\n"
fmt.Printf("method: %s\n", req.Method)
fmt.Printf("host: %s\n", req.Host)
fmt.Printf("length: %d\n", req.ContentLength)
fmt.Printf("URL: %s\n", req.URL.String())
fmt.Printf("header: %s\n", header)
fmt.Printf("payload: %s\n", payload)
}
}

// On-the-fly debug hook. If either debugShowResponseEnabled or the environment
// variable "GOVCD_SHOW_RESP" are enabled, this function will show the contents
// of the response as it is being processed.
func debugShowResponse(resp *http.Response, body []byte) {
if debugShowResponseEnabled {
fmt.Printf("status: %d - %s \n", resp.StatusCode, resp.Status)
fmt.Printf("length: %d\n", resp.ContentLength)
fmt.Printf("header: %v\n", resp.Header)
fmt.Printf("body: %s\n", body)
}
}

// Convenience function, similar to os.IsNotExist that checks whether a given error
// is a "Not found" error, such as
// if isNotFound(err) {
// // do what is needed in case of not found
// }
func IsNotFound(err error) bool {
return err != nil && err == ErrorEntityNotFound
}

// Function allow to pass complex values params which shouldn't be encoded like for queries. e.g. /query?filter=(name=foo)
func (cli *Client) NewRequestWitNotEncodedParams(params map[string]string, notEncodedParams map[string]string, method string, reqUrl url.URL, body io.Reader) *http.Request {
reqValues := url.Values{}
Expand Down Expand Up @@ -86,6 +160,8 @@ func (cli *Client) NewRequestWitNotEncodedParams(params map[string]string, notEn
}
}
util.ProcessRequestOutput(util.FuncNameCallStack(), method, reqUrl.String(), payload, req)

debugShowRequest(req, payload)
}
return req

Expand Down Expand Up @@ -119,6 +195,7 @@ func decodeBody(resp *http.Response, out interface{}) error {
return err
}

debugShowResponse(resp, body)
// Unmarshal the XML.
if err = xml.Unmarshal(body, &out); err != nil {
return err
Expand Down Expand Up @@ -238,6 +315,7 @@ func (client *Client) ExecuteRequestWithoutResponse(pathURL, requestType, conten
// log response explicitly because decodeBody() was not triggered
util.ProcessResponseOutput(util.FuncNameCallStack(), resp, fmt.Sprintf("%s", resp.Body))

debugShowResponse(resp, []byte("SKIPPED RESPONSE"))
err = resp.Body.Close()
if err != nil {
return fmt.Errorf("error closing response body: %s", err)
Expand Down
24 changes: 23 additions & 1 deletion govcd/api_vcd_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// +build api functional catalog vapp gateway network org query extnetwork task vm vdc system disk lbServerPool lbServiceMonitor ALL
// +build api functional catalog vapp gateway network org query extnetwork task vm vdc system disk lbServerPool lbServiceMonitor user ALL

/*
* Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License.
Expand Down Expand Up @@ -545,6 +545,28 @@ func (vcd *TestVCD) removeLeftoverEntities(entity CleanupEntity) {
vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
}
return
case "user":
if entity.Parent == "" {
vcd.infoCleanup("removeLeftoverEntries: [ERROR] No ORG provided for user '%s'\n", entity.Name)
return
}
org, err := GetAdminOrgByName(vcd.client, entity.Parent)
if org == (AdminOrg{}) || err != nil {
vcd.infoCleanup(notFoundMsg, "org", entity.Parent)
return
}
user, err := org.FetchUserByName(entity.Name, true)
if err != nil {
vcd.infoCleanup(notFoundMsg, "user", entity.Name)
return
}
err = user.Delete(true)
if err == nil {
vcd.infoCleanup(removedMsg, entity.EntityType, entity.Name, entity.CreatedBy)
} else {
vcd.infoCleanup(notDeletedMsg, entity.EntityType, entity.Name, err)
}
return
case "vdc":
if entity.Parent == "" {
vcd.infoCleanup("removeLeftoverEntries: [ERROR] No ORG provided for VDC '%s'\n", entity.Name)
Expand Down
17 changes: 17 additions & 0 deletions govcd/monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ func prettyVapp(vapp types.VApp) string {
return ""
}

// Returns an OrgUser structure as JSON
func prettyUser(user types.User) string {
byteBuf, err := json.MarshalIndent(user, " ", " ")
if err == nil {
return fmt.Sprintf("%s\n", string(byteBuf))
}
return ""
}

// Returns a VDC structure as JSON
func prettyVdc(vdc types.Vdc) string {
byteBuf, err := json.MarshalIndent(vdc, " ", " ")
Expand Down Expand Up @@ -202,6 +211,14 @@ func LogVdc(vdc types.Vdc) {
out("log", prettyVdc(vdc))
}

func ShowUser(user types.User) {
out("screen", prettyUser(user))
}

func LogUser(user types.User) {
out("log", prettyUser(user))
}

func ShowDisk(disk types.Disk) {
out("screen", prettyDisk(disk))
}
Expand Down
Loading

0 comments on commit ed123fe

Please sign in to comment.