diff --git a/LICENSE b/LICENSE index ad410e11..5c304d1a 100644 --- a/LICENSE +++ b/LICENSE @@ -198,4 +198,4 @@ Apache License 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. \ No newline at end of file + limitations under the License. diff --git a/README.md b/README.md index 478825cb..74fb7f1a 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,21 @@ This is a Library that you can use to write tools to interact with the chef serv go test -v github.com/go-chef/chef examples::chefapi_tester kitchen verify +## Chef Server URL + +The API calls expect the client configuration to be set up with the appropriate base URL. Most of the API calls are intended to be made relative to a base URL that specifies a chef server organization. The specified URL will look something like "https://chef-server.example/organizations/orgname". The association, license, organization and user endpoints use the base URL without a specified organization similar to "https://chef-server.example". If the StetURL variable in the client config is set to false, the default, the global URL will be computed from a URL that specifies the organization. The default makes it possible to make global calls and calls for one organization using the same client configuration.If you want to call the API for multiple organizations new clients need to be created for each organization. + ## SSL - If you run into an SSL verification problem when trying to connect to a ssl server with self signed certs setup your config object with `SkipSSL: true` +If you run into an SSL verification problem when trying to connect to a chef server with self signed certs you can setup your config object with `SkipSSL: true`. +You may also add self signed certificates and missing root CAs to the ROOTCAS field in the chef client cfg. See the testapi/testapi.go file for example code. ## Usage +Typically using this api client follows this pattern: + +* Create a client structure using NewClient. Specify the chef server URL and an organization. +* Make api calls using client.EndPoint.method function calls. Some calls take parameters and some require JSON bodies. The functions generally turn the JSON returnedfrom the chef server into golang structures. + This example is setting up a basic client that you can use to interact with all the service endpoints (clients, nodes, cookbooks, etc. At [@chefapi](https://docs.chef.io/api_chef_server.html)) More usage examples can be found in the [examples](examples) directory. diff --git a/acl.go b/acl.go index f0ced798..063ba2fb 100644 --- a/acl.go +++ b/acl.go @@ -34,7 +34,7 @@ func NewACL(acltype string, actors, groups ACLitem) (acl *ACL) { // Chef API docs: lol func (a *ACLService) Get(subkind string, name string) (acl ACL, err error) { url := fmt.Sprintf("%s/%s/_acl", subkind, name) - err = a.client.magicRequestDecoder("GET", url, nil, &acl) + err = a.client.magicRequestDecoder("GET", url, UseOrg, nil, &acl) return } @@ -48,6 +48,6 @@ func (a *ACLService) Put(subkind, name string, acltype string, item *ACL) (err e return } - err = a.client.magicRequestDecoder("PUT", url, body, nil) + err = a.client.magicRequestDecoder("PUT", url, UseOrg, body, nil) return } diff --git a/association.go b/association.go index 996b255a..1e8873d7 100644 --- a/association.go +++ b/association.go @@ -76,7 +76,7 @@ type OrgUser struct { // ListInvites gets a list of the pending invitations for an organization. func (e *AssociationService) ListInvites() (invitelist []Invite, err error) { - err = e.client.magicRequestDecoder("GET", "association_requests", nil, &invitelist) + err = e.client.magicRequestDecoder("GET", "association_requests", UseGlobal, nil, &invitelist) return } @@ -86,20 +86,20 @@ func (e *AssociationService) Invite(invite Request) (data Association, err error if err != nil { return } - err = e.client.magicRequestDecoder("POST", "association_requests/", body, &data) + err = e.client.magicRequestDecoder("POST", "association_requests/", UseGlobal, body, &data) return } // DeleteInvite removes a pending invitation to an organization func (e *AssociationService) DeleteInvite(id string) (rescind RescindInvite, err error) { - err = e.client.magicRequestDecoder("DELETE", "association_requests/"+id, nil, &rescind) + err = e.client.magicRequestDecoder("DELETE", "association_requests/"+id, UseGlobal, nil, &rescind) return } // InviteID Finds an invitation id for a user func (e *AssociationService) InviteId(user string) (id string, err error) { var invitelist []Invite - err = e.client.magicRequestDecoder("GET", "association_requests", nil, &invitelist) + err = e.client.magicRequestDecoder("GET", "association_requests", UseGlobal, nil, &invitelist) if err != nil { return } @@ -122,13 +122,13 @@ func (e *AssociationService) AcceptInvite(id string) (data string, err error) { if err != nil { return } - err = e.client.magicRequestDecoder("PUT", "association_requests/"+id, body, &data) + err = e.client.magicRequestDecoder("PUT", "association_requests/"+id, UseGlobal, body, &data) return } // List gets a list of the users in an organization func (e *AssociationService) List() (data []OrgUserListEntry, err error) { - err = e.client.magicRequestDecoder("GET", "users", nil, &data) + err = e.client.magicRequestDecoder("GET", "users", UseGlobal, nil, &data) return } @@ -138,18 +138,18 @@ func (e *AssociationService) Add(addme AddNow) (err error) { if err != nil { return } - err = e.client.magicRequestDecoder("POST", "users", body, nil) + err = e.client.magicRequestDecoder("POST", "users", UseGlobal, body, nil) return } // Get the details of a user in an organization func (e *AssociationService) Get(name string) (data OrgUser, err error) { - err = e.client.magicRequestDecoder("GET", "users/"+name, nil, &data) + err = e.client.magicRequestDecoder("GET", "users/"+name, UseGlobal, nil, &data) return } // Delete removes a user from an organization func (e *AssociationService) Delete(name string) (data OrgUser, err error) { - err = e.client.magicRequestDecoder("DELETE", "users/"+name, nil, &data) + err = e.client.magicRequestDecoder("DELETE", "users/"+name, UseGlobal, nil, &data) return } diff --git a/authenticate.go b/authenticate.go index 48da0f97..ed57fa23 100644 --- a/authenticate.go +++ b/authenticate.go @@ -15,6 +15,6 @@ type Authenticate struct { // https://docs.chef.io/api_chef_server.html#authenticate-user func (e *AuthenticateUserService) Authenticate(authenticate_request Authenticate) (err error) { body, err := JSONReader(authenticate_request) - err = e.client.magicRequestDecoder("POST", "authenticate_user", body, nil) + err = e.client.magicRequestDecoder("POST", "authenticate_user", UseOrg, body, nil) return } diff --git a/client.go b/client.go index 5eb775b2..07b895ac 100644 --- a/client.go +++ b/client.go @@ -47,7 +47,7 @@ func (c ApiClientListResult) String() (out string) { // // Chef API docs: https://docs.chef.io/api_chef_server/#get-11 func (e *ApiClientService) List() (data ApiClientListResult, err error) { - err = e.client.magicRequestDecoder("GET", "clients", nil, &data) + err = e.client.magicRequestDecoder("GET", "clients", UseOrg, nil, &data) return } @@ -59,7 +59,7 @@ func (e *ApiClientService) Create(client ApiNewClient) (data *ApiClientCreateRes if err != nil { return } - err = e.client.magicRequestDecoder("POST", "clients", body, &data) + err = e.client.magicRequestDecoder("POST", "clients", UseOrg, body, &data) return } @@ -68,7 +68,7 @@ func (e *ApiClientService) Create(client ApiNewClient) (data *ApiClientCreateRes // Chef API docs: https://docs.chef.io/api_chef_server.html#clients-name func (e *ApiClientService) Delete(name string) (err error) { url := fmt.Sprintf("clients/%s", name) - err = e.client.magicRequestDecoder("DELETE", url, nil, nil) + err = e.client.magicRequestDecoder("DELETE", url, UseOrg, nil, nil) return } @@ -77,7 +77,7 @@ func (e *ApiClientService) Delete(name string) (err error) { // Chef API docs: https://docs.chef.io/api_chef_server.html#clients-name func (e *ApiClientService) Get(name string) (client ApiClient, err error) { url := fmt.Sprintf("clients/%s", name) - err = e.client.magicRequestDecoder("GET", url, nil, &client) + err = e.client.magicRequestDecoder("GET", url, UseOrg, nil, &client) return } @@ -90,7 +90,7 @@ func (e *ApiClientService) Update(name string, client ApiNewClient) (data *ApiCl if err != nil { return } - err = e.client.magicRequestDecoder("PUT", url, body, &data) + err = e.client.magicRequestDecoder("PUT", url, UseOrg, body, &data) return } @@ -99,7 +99,7 @@ func (e *ApiClientService) Update(name string, client ApiNewClient) (data *ApiCl // Chef API docs: https://docs.chef.io/api_chef_server.html#clients-client-keys func (e *ApiClientService) ListKeys(name string) (data []KeyItem, err error) { url := fmt.Sprintf("clients/%s/keys", name) - err = e.client.magicRequestDecoder("GET", url, nil, &data) + err = e.client.magicRequestDecoder("GET", url, UseOrg, nil, &data) return } @@ -115,7 +115,7 @@ func (e *ApiClientService) ListKeys(name string) (data []KeyItem, err error) { func (e *ApiClientService) AddKey(name string, keyadd AccessKey) (key KeyItem, err error) { url := fmt.Sprintf("clients/%s/keys", name) body, err := JSONReader(keyadd) - err = e.client.magicRequestDecoder("POST", url, body, &key) + err = e.client.magicRequestDecoder("POST", url, UseOrg, body, &key) return } @@ -129,7 +129,7 @@ func (e *ApiClientService) AddKey(name string, keyadd AccessKey) (key KeyItem, e // Chef API docs: https://docs.chef.io/api_chef_server/#clientskeys func (e *ApiClientService) DeleteKey(name string, keyname string) (key AccessKey, err error) { url := fmt.Sprintf("clients/%s/keys/%s", name, keyname) - err = e.client.magicRequestDecoder("DELETE", url, nil, &key) + err = e.client.magicRequestDecoder("DELETE", url, UseOrg, nil, &key) return } @@ -138,7 +138,7 @@ func (e *ApiClientService) DeleteKey(name string, keyname string) (key AccessKey // Chef API docs: https://docs.chef.io/api_chef_server.html#clients-client-keys-key func (e *ApiClientService) GetKey(name string, keyname string) (key AccessKey, err error) { url := fmt.Sprintf("clients/%s/keys/%s", name, keyname) - err = e.client.magicRequestDecoder("GET", url, nil, &key) + err = e.client.magicRequestDecoder("GET", url, UseOrg, nil, &key) return } @@ -153,6 +153,6 @@ func (e *ApiClientService) GetKey(name string, keyname string) (key AccessKey, e func (e *ApiClientService) UpdateKey(name string, keyname string, keyupd AccessKey) (key AccessKey, err error) { url := fmt.Sprintf("clients/%s/keys/%s", name, keyname) body, err := JSONReader(keyupd) - err = e.client.magicRequestDecoder("PUT", url, body, &key) + err = e.client.magicRequestDecoder("PUT", url, UseOrg, body, &key) return } diff --git a/container.go b/container.go index 63daa72a..547f3d75 100644 --- a/container.go +++ b/container.go @@ -33,7 +33,7 @@ func (c ContainerListResult) String() (out string) { // // Chef API docs: https://docs.chef.io/api_chef_server/containers func (e *ContainerService) List() (data ContainerListResult, err error) { - err = e.client.magicRequestDecoder("GET", "containers", nil, &data) + err = e.client.magicRequestDecoder("GET", "containers", UseOrg, nil, &data) return } @@ -45,7 +45,7 @@ func (e *ContainerService) Create(container Container) (data *ContainerCreateRes if err != nil { return } - err = e.client.magicRequestDecoder("POST", "containers", body, &data) + err = e.client.magicRequestDecoder("POST", "containers", UseOrg, body, &data) return } @@ -54,7 +54,7 @@ func (e *ContainerService) Create(container Container) (data *ContainerCreateRes // Chef API docs: https://docs.chef.io/api_chef_server.html#container func (e *ContainerService) Delete(name string) (err error) { url := fmt.Sprintf("containers/%s", name) - err = e.client.magicRequestDecoder("DELETE", url, nil, nil) + err = e.client.magicRequestDecoder("DELETE", url, UseOrg, nil, nil) return } @@ -63,6 +63,6 @@ func (e *ContainerService) Delete(name string) (err error) { // Chef API docs: https://docs.chef.io/api_chef_server.html#containers func (e *ContainerService) Get(name string) (container Container, err error) { url := fmt.Sprintf("containers/%s", name) - err = e.client.magicRequestDecoder("GET", url, nil, &container) + err = e.client.magicRequestDecoder("GET", url, UseOrg, nil, &container) return } diff --git a/cookbook.go b/cookbook.go index d6108bcf..3544b270 100644 --- a/cookbook.go +++ b/cookbook.go @@ -112,18 +112,18 @@ func versionParams(path, numVersions string) string { return path } -// Get retruns a CookbookVersion for a specific cookbook +// Get returns a CookbookVersion for a specific cookbook // GET /cookbooks/name func (c *CookbookService) Get(name string) (data CookbookVersion, err error) { path := fmt.Sprintf("cookbooks/%s", name) - err = c.client.magicRequestDecoder("GET", path, nil, &data) + err = c.client.magicRequestDecoder("GET", path, UseOrg, nil, &data) return } // GetAvailable returns the versions of a coookbook available on a server func (c *CookbookService) GetAvailableVersions(name, numVersions string) (data CookbookListResult, err error) { path := versionParams(fmt.Sprintf("cookbooks/%s", name), numVersions) - err = c.client.magicRequestDecoder("GET", path, nil, &data) + err = c.client.magicRequestDecoder("GET", path, UseOrg, nil, &data) return } @@ -133,7 +133,7 @@ func (c *CookbookService) GetAvailableVersions(name, numVersions string) (data C // Chef API docs: https://docs.chef.io/api_chef_server.html#cookbooks-name-version func (c *CookbookService) GetVersion(name, version string) (data Cookbook, err error) { url := fmt.Sprintf("cookbooks/%s/%s", name, version) - err = c.client.magicRequestDecoder("GET", url, nil, &data) + err = c.client.magicRequestDecoder("GET", url, UseOrg, nil, &data) return } @@ -141,7 +141,7 @@ func (c *CookbookService) GetVersion(name, version string) (data Cookbook, err e // Chef API docs: https://docs.chef.io/api_chef_server.html#cookbooks-name func (c *CookbookService) ListAvailableVersions(numVersions string) (data CookbookListResult, err error) { path := versionParams("cookbooks", numVersions) - err = c.client.magicRequestDecoder("GET", path, nil, &data) + err = c.client.magicRequestDecoder("GET", path, UseOrg, nil, &data) return } @@ -149,7 +149,7 @@ func (c *CookbookService) ListAvailableVersions(numVersions string) (data Cookbo // Chef API docs: https://docs.chef.io/api_chef_server.html#cookbooks-recipes func (c *CookbookService) ListAllRecipes() (data CookbookRecipesResult, err error) { path := "cookbooks/_recipes" - err = c.client.magicRequestDecoder("GET", path, nil, &data) + err = c.client.magicRequestDecoder("GET", path, UseOrg, nil, &data) return } @@ -161,6 +161,6 @@ func (c *CookbookService) List() (CookbookListResult, error) { // DeleteVersion removes a version of a cook from a server func (c *CookbookService) Delete(name, version string) (err error) { path := fmt.Sprintf("cookbooks/%s/%s", name, version) - err = c.client.magicRequestDecoder("DELETE", path, nil, nil) + err = c.client.magicRequestDecoder("DELETE", path, UseOrg, nil, nil) return } diff --git a/cookbook_artifacts.go b/cookbook_artifacts.go index d64db9e4..fe354063 100644 --- a/cookbook_artifacts.go +++ b/cookbook_artifacts.go @@ -67,7 +67,7 @@ type CBAMeta struct { // List lists the Cookbook_Artifacts in the Chef server. // GET /cookbook_artifacts func (c *CBAService) List() (data CBAGetResponse, err error) { - err = c.client.magicRequestDecoder("GET", "cookbook_artifacts", nil, &data) + err = c.client.magicRequestDecoder("GET", "cookbook_artifacts", UseOrg, nil, &data) return } @@ -75,7 +75,7 @@ func (c *CBAService) List() (data CBAGetResponse, err error) { // GET /cookbook_artifacts/name func (c *CBAService) Get(name string) (data CBAGetResponse, err error) { path := fmt.Sprintf("cookbook_artifacts/%s", name) - err = c.client.magicRequestDecoder("GET", path, nil, &data) + err = c.client.magicRequestDecoder("GET", path, UseOrg, nil, &data) return } @@ -83,6 +83,6 @@ func (c *CBAService) Get(name string) (data CBAGetResponse, err error) { // GET /cookbook_artifact/foo/1ef062de1bc4cb14e4a78fb739e104eb9508473e func (c *CBAService) GetVersion(name, id string) (data CBADetail, err error) { url := fmt.Sprintf("cookbook_artifacts/%s/%s", name, id) - err = c.client.magicRequestDecoder("GET", url, nil, &data) + err = c.client.magicRequestDecoder("GET", url, UseOrg, nil, &data) return } diff --git a/cookbook_download.go b/cookbook_download.go index 909a98e3..23cd5d72 100644 --- a/cookbook_download.go +++ b/cookbook_download.go @@ -99,7 +99,7 @@ func (c *CookbookService) downloadCookbookFile(item CookbookItem, localPath stri return nil } - request, err := c.client.NewRequest("GET", item.Url, nil) + request, err := c.client.NewRequest("GET", item.Url, false, nil) if err != nil { return err } diff --git a/databag.go b/databag.go index f4d95c79..73813282 100644 --- a/databag.go +++ b/databag.go @@ -37,7 +37,7 @@ func (d DataBagListResult) String() (out string) { // Chef API Docs: https://docs.chef.io/api_chef_server/#get-19 func (d *DataBagService) List() (data *DataBagListResult, err error) { path := fmt.Sprintf("data") - err = d.client.magicRequestDecoder("GET", path, nil, &data) + err = d.client.magicRequestDecoder("GET", path, UseOrg, nil, &data) return } @@ -49,7 +49,7 @@ func (d *DataBagService) Create(databag *DataBag) (result *DataBagCreateResult, return } - err = d.client.magicRequestDecoder("POST", "data", body, &result) + err = d.client.magicRequestDecoder("POST", "data", UseOrg, body, &result) return } @@ -57,7 +57,7 @@ func (d *DataBagService) Create(databag *DataBag) (result *DataBagCreateResult, // Chef API Docs: https://docs.chef.io/api_chef_server/#delete-7 func (d *DataBagService) Delete(name string) (result *DataBag, err error) { path := fmt.Sprintf("data/%s", name) - err = d.client.magicRequestDecoder("DELETE", path, nil, &result) + err = d.client.magicRequestDecoder("DELETE", path, UseOrg, nil, &result) return } @@ -65,7 +65,7 @@ func (d *DataBagService) Delete(name string) (result *DataBag, err error) { // Chef API Docs: https://docs.chef.io/api_chef_server/#get-20 func (d *DataBagService) ListItems(name string) (data *DataBagListResult, err error) { path := fmt.Sprintf("data/%s", name) - err = d.client.magicRequestDecoder("GET", path, nil, &data) + err = d.client.magicRequestDecoder("GET", path, UseOrg, nil, &data) return } @@ -77,14 +77,14 @@ func (d *DataBagService) CreateItem(databagName string, databagItem DataBagItem) return } path := fmt.Sprintf("data/%s", databagName) - return d.client.magicRequestDecoder("POST", path, body, nil) + return d.client.magicRequestDecoder("POST", path, UseOrg, body, nil) } // DeleteItem deletes an item from a data bag // Chef API Docs: https://docs.chef.io/api_chef_server/#delete-8 func (d *DataBagService) DeleteItem(databagName string, databagItem string) (err error) { path := fmt.Sprintf("data/%s/%s", databagName, databagItem) - err = d.client.magicRequestDecoder("DELETE", path, nil, nil) + err = d.client.magicRequestDecoder("DELETE", path, UseOrg, nil, nil) return } @@ -92,7 +92,7 @@ func (d *DataBagService) DeleteItem(databagName string, databagItem string) (err // Chef API Docs: https://docs.chef.io/api_chef_server/#get-21 func (d *DataBagService) GetItem(databagName string, databagItem string) (item DataBagItem, err error) { path := fmt.Sprintf("data/%s/%s", databagName, databagItem) - err = d.client.magicRequestDecoder("GET", path, nil, &item) + err = d.client.magicRequestDecoder("GET", path, UseOrg, nil, &item) return } @@ -104,5 +104,5 @@ func (d *DataBagService) UpdateItem(databagName string, databagItemId string, da return } path := fmt.Sprintf("data/%s/%s", databagName, databagItemId) - return d.client.magicRequestDecoder("PUT", path, body, nil) + return d.client.magicRequestDecoder("PUT", path, UseOrg, body, nil) } diff --git a/environment.go b/environment.go index 6bd19b9d..0b2af137 100644 --- a/environment.go +++ b/environment.go @@ -48,7 +48,7 @@ func (e EnvironmentResult) String() (out string) { // // Chef API docs: https://docs.chef.io/api_chef_server.html#environments func (e *EnvironmentService) List() (data *EnvironmentResult, err error) { - err = e.client.magicRequestDecoder("GET", "environments", nil, &data) + err = e.client.magicRequestDecoder("GET", "environments", UseOrg, nil, &data) return } @@ -61,7 +61,7 @@ func (e *EnvironmentService) Create(environment *Environment) (data *Environment return } - err = e.client.magicRequestDecoder("POST", "environments", body, &data) + err = e.client.magicRequestDecoder("POST", "environments", UseOrg, body, &data) return } @@ -70,7 +70,7 @@ func (e *EnvironmentService) Create(environment *Environment) (data *Environment // Chef API docs: https://docs.chef.io/api_chef_server/#delete-9 func (e *EnvironmentService) Delete(name string) (data *Environment, err error) { path := fmt.Sprintf("environments/%s", name) - err = e.client.magicRequestDecoder("DELETE", path, nil, &data) + err = e.client.magicRequestDecoder("DELETE", path, UseOrg, nil, &data) return } @@ -79,7 +79,7 @@ func (e *EnvironmentService) Delete(name string) (data *Environment, err error) // Chef API docs: https://docs.chef.io/api_chef_server.html#environments-name func (e *EnvironmentService) Get(name string) (data *Environment, err error) { path := fmt.Sprintf("environments/%s", name) - err = e.client.magicRequestDecoder("GET", path, nil, &data) + err = e.client.magicRequestDecoder("GET", path, UseOrg, nil, &data) return } @@ -94,7 +94,7 @@ func (e *EnvironmentService) Put(environment *Environment) (data *Environment, e return } - err = e.client.magicRequestDecoder("PUT", path, body, &data) + err = e.client.magicRequestDecoder("PUT", path, UseOrg, body, &data) return } @@ -103,7 +103,7 @@ func (e *EnvironmentService) Put(environment *Environment) (data *Environment, e // Chef API docs: https://docs.chef.io/api_chef_server.html#environments-name-cookbooks func (e *EnvironmentService) ListCookbooks(name string, numVersions string) (data EnvironmentCookbookResult, err error) { path := versionParams(fmt.Sprintf("environments/%s/cookbooks", name), numVersions) - err = e.client.magicRequestDecoder("GET", path, nil, &data) + err = e.client.magicRequestDecoder("GET", path, UseOrg, nil, &data) return } diff --git a/group.go b/group.go index 2ae23610..a7df559c 100644 --- a/group.go +++ b/group.go @@ -36,7 +36,7 @@ type GroupResult struct { // // Chef API docs: https://docs.chef.io/api_chef_server.html#groups func (e *GroupService) List() (grouplist map[string]string, err error) { - err = e.client.magicRequestDecoder("GET", "groups", nil, &grouplist) + err = e.client.magicRequestDecoder("GET", "groups", UseOrg, nil, &grouplist) return } @@ -45,7 +45,7 @@ func (e *GroupService) List() (grouplist map[string]string, err error) { // Chef API docs: http://docs.opscode.com/api_chef_server.html#id28 func (e *GroupService) Get(name string) (group Group, err error) { url := fmt.Sprintf("groups/%s", name) - err = e.client.magicRequestDecoder("GET", url, nil, &group) + err = e.client.magicRequestDecoder("GET", url, UseOrg, nil, &group) return } @@ -58,7 +58,7 @@ func (e *GroupService) Create(group Group) (data *GroupResult, err error) { return } - err = e.client.magicRequestDecoder("POST", "groups", body, &data) + err = e.client.magicRequestDecoder("POST", "groups", UseOrg, body, &data) return } @@ -72,7 +72,7 @@ func (e *GroupService) Update(g GroupUpdate) (group GroupUpdate, err error) { return } - err = e.client.magicRequestDecoder("PUT", url, body, &group) + err = e.client.magicRequestDecoder("PUT", url, UseOrg, body, &group) return } @@ -80,6 +80,6 @@ func (e *GroupService) Update(g GroupUpdate) (group GroupUpdate, err error) { // // Chef API docs: https://docs.chef.io/api_chef_server.html#groups func (e *GroupService) Delete(name string) (err error) { - err = e.client.magicRequestDecoder("DELETE", "groups/"+name, nil, nil) + err = e.client.magicRequestDecoder("DELETE", "groups/"+name, UseOrg, nil, nil) return } diff --git a/http.go b/http.go index 0c395281..c36a7182 100644 --- a/http.go +++ b/http.go @@ -23,6 +23,10 @@ import ( // ChefVersion that we pretend to emulate const ChefVersion = "14.0.0" +// Values to indicate which base url to use +const UseGlobal = true +const UseOrg = false + // Body wraps io.Reader and adds methods for calculating hashes and detecting content type Body struct { io.Reader @@ -38,9 +42,10 @@ type AuthConfig struct { // Client is vessel for public methods used against the chef-server type Client struct { - Auth *AuthConfig - BaseURL *url.URL - client *http.Client + Auth *AuthConfig + BaseURL *url.URL + GlobalBaseURL *url.URL + client *http.Client ACLs *ACLService Associations *AssociationService @@ -91,6 +96,12 @@ type Config struct { // Authentication Protocol Version AuthenticationVersion string + + // Base URL handling + // StetBaseURL the default of false implies + // A / will be added to the end of the specified BaseURL if it is not there + // The GlobalBaseURL will be calculated and used for global end points + StetBaseURL bool } /* @@ -205,8 +216,16 @@ func NewClient(cfg *Config) (*Client, error) { return nil, err } + if !cfg.StetBaseURL { + cfg.BaseURL = urlSlash(cfg.BaseURL) + } baseUrl, _ := url.Parse(cfg.BaseURL) + globalBaseUrl := baseUrl + if !cfg.StetBaseURL { + globalBaseUrl = urlBase(globalBaseUrl) + } + tlsConfig := &tls.Config{InsecureSkipVerify: cfg.SkipSSL} if cfg.RootCAs != nil { tlsConfig.RootCAs = cfg.RootCAs @@ -231,7 +250,8 @@ func NewClient(cfg *Config) (*Client, error) { Transport: tr, Timeout: time.Duration(cfg.Timeout) * time.Second, }, - BaseURL: baseUrl, + BaseURL: baseUrl, + GlobalBaseURL: globalBaseUrl, } c.ACLs = &ACLService{client: c} c.AuthenticateUser = &AuthenticateUserService{client: c} @@ -271,8 +291,8 @@ func (cfg *Config) VerifyVersion() (err error) { // basicRequestDecoder performs a request on an endpoint, and decodes the response into the passed in Type // basicRequestDecoder is the same code as magic RequestDecoder with the addition of a generated Authentication: Basic header // to the http request -func (c *Client) basicRequestDecoder(method, path string, body io.Reader, v interface{}, user string, password string) error { - req, err := c.NewRequest(method, path, body) +func (c *Client) basicRequestDecoder(method, path string, globalUrl bool, body io.Reader, v interface{}, user string, password string) error { + req, err := c.NewRequest(method, path, globalUrl, body) if err != nil { return err } @@ -292,8 +312,8 @@ func (c *Client) basicRequestDecoder(method, path string, body io.Reader, v inte } // magicRequestDecoder performs a request on an endpoint, and decodes the response into the passed in Type -func (c *Client) magicRequestDecoder(method, path string, body io.Reader, v interface{}) error { - req, err := c.NewRequest(method, path, body) +func (c *Client) magicRequestDecoder(method, path string, globalUrl bool, body io.Reader, v interface{}) error { + req, err := c.NewRequest(method, path, globalUrl, body) if err != nil { return err } @@ -311,12 +331,17 @@ func (c *Client) magicRequestDecoder(method, path string, body io.Reader, v inte } // NewRequest returns a signed request suitable for the chef server -func (c *Client) NewRequest(method string, requestUrl string, body io.Reader) (*http.Request, error) { +func (c *Client) NewRequest(method string, requestUrl string, globalUrl bool, body io.Reader) (*http.Request, error) { relativeUrl, err := url.Parse(requestUrl) if err != nil { return nil, err } - u := c.BaseURL.ResolveReference(relativeUrl) + u, _ := url.Parse("") + if globalUrl { + u = c.GlobalBaseURL.ResolveReference(relativeUrl) + } else { + u = c.BaseURL.ResolveReference(relativeUrl) + } // NewRequest uses a new value object of body req, err := http.NewRequest(method, u.String(), body) @@ -602,3 +627,18 @@ func PrivateKeyFromString(key []byte) (*rsa.PrivateKey, error) { } return rsaKey, nil } + +// urlSlash Make sure the specified URL ends in a forward slash +func urlSlash(baseURL string) string { + baseURL = strings.TrimSpace(baseURL) + if len(baseURL) == 0 || string(baseURL[len(baseURL)-1:]) != "/" { + baseURL = baseURL + "/" + } + return baseURL +} + +// urlBase Extract the base chef server URL +func urlBase(baseUrl *url.URL) *url.URL { + rootUrl, _ := url.Parse("/") + return baseUrl.ResolveReference(rootUrl) +} diff --git a/http_test.go b/http_test.go index da05d0e2..f48aabd6 100644 --- a/http_test.go +++ b/http_test.go @@ -13,6 +13,7 @@ import ( "math/big" "net/http" "net/http/httptest" + "net/url" "reflect" "regexp" "strconv" @@ -206,7 +207,7 @@ func TestSignRequestNoBody(t *testing.T) { setup() defer teardown() ac, err := makeAuthConfig() - request, err := client.NewRequest("GET", requestURL, nil) + request, err := client.NewRequest("GET", requestURL, false, nil) err = ac.SignRequest(request) if err != nil { @@ -238,7 +239,7 @@ func TestSignRequestBody(t *testing.T) { // nopCloser came from https://groups.google.com/d/msg/golang-nuts/J-Y4LtdGNSw/wDSYbHWIKj0J // yay for sharing requestBody := strings.NewReader("somecoolbodytext") - request, err := client.NewRequest("GET", requestURL, requestBody) + request, err := client.NewRequest("GET", requestURL, false, requestBody) err = ac.SignRequest(request) if err != nil { @@ -339,7 +340,7 @@ func TestRequest(t *testing.T) { setup() defer teardown() - request, err := client.NewRequest("GET", server.URL, nil) + request, err := client.NewRequest("GET", server.URL, false, nil) err = ac.SignRequest(request) if err != nil { @@ -372,7 +373,7 @@ func TestRequestToEndpoint(t *testing.T) { defer server.Close() requestBody := strings.NewReader("somecoolbodytext") - request, err := client.NewRequest("GET", server.URL+"/clients", requestBody) + request, err := client.NewRequest("GET", server.URL+"/clients", false, requestBody) err = ac.SignRequest(request) if err != nil { @@ -414,7 +415,7 @@ func TestTLSValidation(t *testing.T) { BaseURL: server.URL, }) - request, err := chefClient.NewRequest("GET", server.URL, nil) + request, err := chefClient.NewRequest("GET", server.URL, false, nil) err = ac.SignRequest(request) if err != nil { t.Fatal("failed to generate RequestHeaders") @@ -436,7 +437,7 @@ func TestTLSValidation(t *testing.T) { RootCAs: certPool, }) - request, err = chefClient.NewRequest("GET", server.URL, nil) + request, err = chefClient.NewRequest("GET", server.URL, false, nil) err = ac.SignRequest(request) if err != nil { t.Fatal("failed to generate RequestHeaders") @@ -606,7 +607,7 @@ func TestNewClient(t *testing.T) { t.Error("Built a client from a bad key string") } - // TODO: Test the value of Authentication assisgned + // TODO: Test the value of Authentication assigned } func TestNewRequest(t *testing.T) { @@ -616,7 +617,7 @@ func TestNewRequest(t *testing.T) { c, _ := NewClient(cfg) defer server.Close() - request, err := c.NewRequest("GET", server.URL, nil) + request, err := c.NewRequest("GET", server.URL, false, nil) if err != nil { t.Error("HRRRM! we tried to make a request but it failed :`( ", err) } @@ -627,13 +628,13 @@ func TestNewRequest(t *testing.T) { } // This should fail because we've got an invalid URI - _, err = c.NewRequest("GET", "%gh&%ij", nil) + _, err = c.NewRequest("GET", "%gh&%ij", false, nil) if err == nil { t.Error("This terrible request thing should fail and it didn't") } // This should fail because there is no TOODLES! method :D - request, err = c.NewRequest("TOODLES!", "", nil) + request, err = c.NewRequest("TOODLES!", "", false, nil) _, err = c.Do(request, nil) if err == nil { t.Error("This terrible request thing should fail and it didn't") @@ -649,7 +650,7 @@ func TestDo_badjson(t *testing.T) { }) stupidData := struct{}{} - request, err := client.NewRequest("GET", "hashrocket", nil) + request, err := client.NewRequest("GET", "hashrocket", false, nil) _, err = client.Do(request, &stupidData) if err == nil { t.Error(err) @@ -669,7 +670,7 @@ func TestDoText(t *testing.T) { }) var getdata string - request, err := client.NewRequest("GET", "hashrocket", nil) + request, err := client.NewRequest("GET", "hashrocket", false, nil) _, err = client.Do(request, &getdata) if err != nil { t.Error(err) @@ -691,7 +692,7 @@ func TestDoJSON(t *testing.T) { getdata := map[string]string{} wantdata := map[string]string{"key": "value"} - request, err := client.NewRequest("GET", "hashrocket", nil) + request, err := client.NewRequest("GET", "hashrocket", false, nil) _, err = client.Do(request, &getdata) if err != nil { t.Error(err) @@ -714,7 +715,7 @@ func TestDoDefaultParse(t *testing.T) { getdata := map[string]string{} wantdata := map[string]string{"key": "value"} - request, err := client.NewRequest("GET", "hashrocket", nil) + request, err := client.NewRequest("GET", "hashrocket", false, nil) _, err = client.Do(request, &getdata) if err != nil { t.Error(err) @@ -727,7 +728,7 @@ func TestDoDefaultParse(t *testing.T) { func TestBasicAuthHeader(t *testing.T) { setup() defer teardown() - req, _ := client.NewRequest("GET", "http://dummy", nil) + req, _ := client.NewRequest("GET", "http://dummy", false, nil) basicAuthHeader(req, "stduser", "stdpassword") basicHeader := req.Header.Get("Authorization") if basicHeader != "Basic c3RkdXNlcjpzdGRwYXNzd29yZA==" { @@ -741,3 +742,42 @@ func TestBasicAuth(t *testing.T) { t.Error("BasicAuth credentials not calculated properly") } } + +func TestUrlSlash(t *testing.T) { + if urlSlash("") != "/" { + t.Errorf("urlSlash expected / got %+v\n", urlSlash("/")) + } + if urlSlash("/") != "/" { + t.Errorf("urlSlash expected / got %+v\n", urlSlash("/")) + } + if urlSlash("https://stuff") != "https://stuff/" { + t.Errorf("urlSlash expected https://stuff/ got %+v\n", urlSlash("https://stuff")) + } + if urlSlash("https://stuff/") != "https://stuff/" { + t.Errorf("urlSlash expected https://stuff/ got %+v\n", urlSlash("https://stuff/")) + } + if urlSlash("https://stuff:8443") != "https://stuff:8443/" { + t.Errorf("urlSlash expected https://stuff:8443/ got %+v\n", urlSlash("https://stuff:8443")) + } + if urlSlash("https://stuff/org") != "https://stuff/org/" { + t.Errorf("urlSlash expected https://stuff/org/ got %+v\n", urlSlash("https://stuff/org")) + } +} + +func TestUrlBase(t *testing.T) { + baseUrl, _ := url.Parse("https://stuff/org/") + globalBaseUrl := urlBase(baseUrl).String() + if globalBaseUrl != "https://stuff/" { + t.Errorf("urlBase expected https://stuff/ got %+v\n", globalBaseUrl) + } + baseUrl, _ = url.Parse("https://stuff:8443/org/mine/") + globalBaseUrl = urlBase(baseUrl).String() + if globalBaseUrl != "https://stuff:8443/" { + t.Errorf("urlBase expected https://stuff:8443/ got %+v\n", globalBaseUrl) + } + baseUrl, _ = url.Parse("https://stuff:8443/org/mine") + globalBaseUrl = urlBase(baseUrl).String() + if globalBaseUrl != "https://stuff:8443/" { + t.Errorf("urlBase expected https://stuff:8443/ got %+v\n", globalBaseUrl) + } +} diff --git a/license.go b/license.go index 0a1315b0..69a05b47 100644 --- a/license.go +++ b/license.go @@ -16,6 +16,6 @@ type License struct { // // https://docs.chef.io/api_chef_server/#license func (e *LicenseService) Get() (data License, err error) { - err = e.client.magicRequestDecoder("GET", "license", nil, &data) + err = e.client.magicRequestDecoder("GET", "license", UseGlobal, nil, &data) return } diff --git a/node.go b/node.go index e4bb1809..3ed31aae 100644 --- a/node.go +++ b/node.go @@ -41,7 +41,7 @@ func NewNode(name string) (node Node) { // // Chef API docs: https://docs.chef.io/api_chef_server.html#nodes func (e *NodeService) List() (data map[string]string, err error) { - err = e.client.magicRequestDecoder("GET", "nodes", nil, &data) + err = e.client.magicRequestDecoder("GET", "nodes", UseOrg, nil, &data) return } @@ -50,7 +50,7 @@ func (e *NodeService) List() (data map[string]string, err error) { // Chef API docs: https://docs.chef.io/api_chef_server.html#nodes-name func (e *NodeService) Get(name string) (node Node, err error) { url := fmt.Sprintf("nodes/%s", name) - err = e.client.magicRequestDecoder("GET", url, nil, &node) + err = e.client.magicRequestDecoder("GET", url, UseOrg, nil, &node) return } @@ -59,7 +59,7 @@ func (e *NodeService) Get(name string) (node Node, err error) { // Chef API docs: https://docs.chef.io/api_chef_server.html#nodes-name func (e *NodeService) Head(name string) (err error) { url := fmt.Sprintf("nodes/%s", name) - err = e.client.magicRequestDecoder("HEAD", url, nil, nil) + err = e.client.magicRequestDecoder("HEAD", url, UseOrg, nil, nil) return } @@ -72,7 +72,7 @@ func (e *NodeService) Post(node Node) (data *NodeResult, err error) { return } - err = e.client.magicRequestDecoder("POST", "nodes", body, &data) + err = e.client.magicRequestDecoder("POST", "nodes", UseOrg, body, &data) return } @@ -87,7 +87,7 @@ func (e *NodeService) Put(n Node) (node Node, err error) { return } - err = e.client.magicRequestDecoder("PUT", url, body, &node) + err = e.client.magicRequestDecoder("PUT", url, UseOrg, body, &node) return } @@ -95,6 +95,6 @@ func (e *NodeService) Put(n Node) (node Node, err error) { // // Chef API docs: https://docs.chef.io/api_chef_server.html#nodes-name func (e *NodeService) Delete(name string) (err error) { - err = e.client.magicRequestDecoder("DELETE", "nodes/"+name, nil, nil) + err = e.client.magicRequestDecoder("DELETE", "nodes/"+name, UseOrg, nil, nil) return } diff --git a/organization.go b/organization.go index 1c79a9ee..d727f17c 100644 --- a/organization.go +++ b/organization.go @@ -23,7 +23,7 @@ type OrganizationResult struct { // // Chef API docs: https://docs.chef.io/api_chef_server.html#organizations func (e *OrganizationService) List() (organizationlist map[string]string, err error) { - err = e.client.magicRequestDecoder("GET", "organizations", nil, &organizationlist) + err = e.client.magicRequestDecoder("GET", "organizations", UseGlobal, nil, &organizationlist) return } @@ -32,7 +32,7 @@ func (e *OrganizationService) List() (organizationlist map[string]string, err er // Chef API docs: http://docs.opscode.com/api_chef_server.html#id28 func (e *OrganizationService) Get(name string) (organization Organization, err error) { url := fmt.Sprintf("organizations/%s", name) - err = e.client.magicRequestDecoder("GET", url, nil, &organization) + err = e.client.magicRequestDecoder("GET", url, UseGlobal, nil, &organization) return } @@ -46,7 +46,7 @@ func (e *OrganizationService) Create(organization Organization) (data Organizati } var orglist map[string]string - err = e.client.magicRequestDecoder("POST", "organizations", body, &orglist) + err = e.client.magicRequestDecoder("POST", "organizations", UseGlobal, body, &orglist) data.ClientName = orglist["clientname"] data.PrivateKey = orglist["private_key"] data.Uri = orglist["uri"] @@ -63,7 +63,7 @@ func (e *OrganizationService) Update(g Organization) (organization Organization, return } - err = e.client.magicRequestDecoder("PUT", url, body, &organization) + err = e.client.magicRequestDecoder("PUT", url, UseGlobal, body, &organization) return } @@ -71,6 +71,6 @@ func (e *OrganizationService) Update(g Organization) (organization Organization, // // Chef API docs: https://docs.chef.io/api_chef_server.html#organizations func (e *OrganizationService) Delete(name string) (err error) { - err = e.client.magicRequestDecoder("DELETE", "organizations/"+name, nil, nil) + err = e.client.magicRequestDecoder("DELETE", "organizations/"+name, UseGlobal, nil, nil) return } diff --git a/policy.go b/policy.go index 77d5ee49..64c4d0a4 100644 --- a/policy.go +++ b/policy.go @@ -68,7 +68,7 @@ type SolutionDep struct { // Chef API docs: https://docs.chef.io/api_chef_server/#policies // GET /policies func (c *PolicyService) List() (data PoliciesGetResponse, err error) { - err = c.client.magicRequestDecoder("GET", "policies", nil, &data) + err = c.client.magicRequestDecoder("GET", "policies", UseOrg, nil, &data) return } @@ -76,7 +76,7 @@ func (c *PolicyService) List() (data PoliciesGetResponse, err error) { // GET /policies/name func (c *PolicyService) Get(name string) (data PolicyGetResponse, err error) { path := fmt.Sprintf("policies/%s", name) - err = c.client.magicRequestDecoder("GET", path, nil, &data) + err = c.client.magicRequestDecoder("GET", path, UseOrg, nil, &data) return } @@ -84,7 +84,7 @@ func (c *PolicyService) Get(name string) (data PolicyGetResponse, err error) { // DELETE /policies/name func (c *PolicyService) Delete(policyName string) (data PolicyGetResponse, err error) { path := fmt.Sprintf("policies/%s", policyName) - err = c.client.magicRequestDecoder("DELETE", path, nil, &data) + err = c.client.magicRequestDecoder("DELETE", path, UseOrg, nil, &data) return } @@ -92,7 +92,7 @@ func (c *PolicyService) Delete(policyName string) (data PolicyGetResponse, err e // GET /policies//revisions/ func (c *PolicyService) GetRevisionDetails(policyName string, revisionID string) (data RevisionDetailsResponse, err error) { path := fmt.Sprintf("policies/%s/revisions/%s", policyName, revisionID) - err = c.client.magicRequestDecoder("GET", path, nil, &data) + err = c.client.magicRequestDecoder("GET", path, UseOrg, nil, &data) return } @@ -100,7 +100,7 @@ func (c *PolicyService) GetRevisionDetails(policyName string, revisionID string) // GET /policies//revisions/ func (c *PolicyService) DeleteRevision(policyName string, revisionID string) (data RevisionDetailsResponse, err error) { path := fmt.Sprintf("policies/%s/revisions/%s", policyName, revisionID) - err = c.client.magicRequestDecoder("DELETE", path, nil, &data) + err = c.client.magicRequestDecoder("DELETE", path, UseOrg, nil, &data) return } diff --git a/policy_group.go b/policy_group.go index 900d3fea..02a363f1 100644 --- a/policy_group.go +++ b/policy_group.go @@ -22,7 +22,7 @@ type Revision map[string]string // List lists the policy groups in the Chef server. // Chef API docs: https://docs.chef.io/api_chef_server/#policy_groups func (e *PolicyGroupService) List() (data PolicyGroupGetResponse, err error) { - err = e.client.magicRequestDecoder("GET", "policy_groups", nil, &data) + err = e.client.magicRequestDecoder("GET", "policy_groups", UseOrg, nil, &data) return } @@ -31,7 +31,7 @@ func (e *PolicyGroupService) List() (data PolicyGroupGetResponse, err error) { // Chef API docs: https://docs.chef.io/api_chef_server/#policy_groups func (e *PolicyGroupService) Get(policyGroupName string) (data PolicyGroup, err error) { url := fmt.Sprintf("policy_groups/%s", policyGroupName) - err = e.client.magicRequestDecoder("GET", url, nil, &data) + err = e.client.magicRequestDecoder("GET", url, UseOrg, nil, &data) return } @@ -40,7 +40,7 @@ func (e *PolicyGroupService) Get(policyGroupName string) (data PolicyGroup, err // Chef API docs: https://docs.chef.io/api_chef_server/#policy_groups func (e *PolicyGroupService) Delete(policyGroupName string) (data PolicyGroup, err error) { url := fmt.Sprintf("policy_groups/%s", policyGroupName) - err = e.client.magicRequestDecoder("DELETE", url, nil, &data) + err = e.client.magicRequestDecoder("DELETE", url, UseOrg, nil, &data) return } @@ -49,7 +49,7 @@ func (e *PolicyGroupService) Delete(policyGroupName string) (data PolicyGroup, e // Chef API docs: https://docs.chef.io/api_chef_server/#policy_groups func (e *PolicyGroupService) GetPolicy(policyGroupName string, policyName string) (data RevisionDetailsResponse, err error) { url := fmt.Sprintf("policy_groups/%s/policies/%s", policyGroupName, policyName) - err = e.client.magicRequestDecoder("GET", url, nil, &data) + err = e.client.magicRequestDecoder("GET", url, UseOrg, nil, &data) return } @@ -58,7 +58,7 @@ func (e *PolicyGroupService) GetPolicy(policyGroupName string, policyName string // Chef API docs: https://docs.chef.io/api_chef_server/#policy_groups func (e *PolicyGroupService) DeletePolicy(policyGroupName string, policyName string) (data RevisionDetailsResponse, err error) { url := fmt.Sprintf("policy_groups/%s/policies/%s", policyGroupName, policyName) - err = e.client.magicRequestDecoder("DELETE", url, nil, &data) + err = e.client.magicRequestDecoder("DELETE", url, UseOrg, nil, &data) return } diff --git a/principal.go b/principal.go index 45706860..3e0da591 100644 --- a/principal.go +++ b/principal.go @@ -24,6 +24,6 @@ type Principals struct { // Chef API docs: https://docs.chef.io/api_chef_server.html#principals-name func (e *PrincipalService) Get(name string) (principal Principal, err error) { url := fmt.Sprintf("principals/%s", name) - err = e.client.magicRequestDecoder("GET", url, nil, &principal) + err = e.client.magicRequestDecoder("GET", url, UseOrg, nil, &principal) return } diff --git a/required_recipe.go b/required_recipe.go index 271542ef..4489be99 100644 --- a/required_recipe.go +++ b/required_recipe.go @@ -4,7 +4,7 @@ type RequiredRecipeService struct { client *Client } -// RequireRecipe the text of the required recipe. +// RequiredRecipe the text of the required recipe. type RequiredRecipe string // RequiredRecipe gets the optional required_runlist value. @@ -14,7 +14,7 @@ type RequiredRecipe string // 404 required_recipe enabled = false func (e *RequiredRecipeService) Get() (data RequiredRecipe, err error) { var getdata string - err = e.client.magicRequestDecoder("GET", "required_recipe", nil, &getdata) + err = e.client.magicRequestDecoder("GET", "required_recipe", UseOrg, nil, &getdata) data = RequiredRecipe(getdata) return } diff --git a/role.go b/role.go index 696bdcf8..f6b2bf8c 100644 --- a/role.go +++ b/role.go @@ -36,7 +36,7 @@ func (e RoleCreateResult) String() (out string) { // // Chef API docs: https://docs.chef.io/api_chef_server.html#roles func (e *RoleService) List() (data *RoleListResult, err error) { - err = e.client.magicRequestDecoder("GET", "roles", nil, &data) + err = e.client.magicRequestDecoder("GET", "roles", UseOrg, nil, &data) return } @@ -50,7 +50,7 @@ func (e *RoleService) Create(role *Role) (data *RoleCreateResult, err error) { } // BUG(fujiN): This is now both a *response* decoder and handles upload.. gettin smelly - err = e.client.magicRequestDecoder("POST", "roles", body, &data) + err = e.client.magicRequestDecoder("POST", "roles", UseOrg, body, &data) return } @@ -60,7 +60,7 @@ func (e *RoleService) Create(role *Role) (data *RoleCreateResult, err error) { // Chef API docs: https://docs.chef.io/api_chef_server.html#roles-name func (e *RoleService) Delete(name string) (err error) { path := fmt.Sprintf("roles/%s", name) - err = e.client.magicRequestDecoder("DELETE", path, nil, nil) + err = e.client.magicRequestDecoder("DELETE", path, UseOrg, nil, nil) return } @@ -69,7 +69,7 @@ func (e *RoleService) Delete(name string) (err error) { // Chef API docs: https://docs.chef.io/api_chef_server.html#roles-name func (e *RoleService) Get(name string) (data *Role, err error) { path := fmt.Sprintf("roles/%s", name) - err = e.client.magicRequestDecoder("GET", path, nil, &data) + err = e.client.magicRequestDecoder("GET", path, UseOrg, nil, &data) return } @@ -84,7 +84,7 @@ func (e *RoleService) Put(role *Role) (data *Role, err error) { return } - err = e.client.magicRequestDecoder("PUT", path, body, &data) + err = e.client.magicRequestDecoder("PUT", path, UseOrg, body, &data) return } @@ -93,7 +93,7 @@ func (e *RoleService) Put(role *Role) (data *Role, err error) { // Chef API docs: https://docs.chef.io/api_chef_server.html#roles-name-environments func (e *RoleService) GetEnvironments(role string) (data RoleEnvironmentsResult, err error) { path := fmt.Sprintf("roles/%s/environments", role) - err = e.client.magicRequestDecoder("GET", path, nil, &data) + err = e.client.magicRequestDecoder("GET", path, UseOrg, nil, &data) return } @@ -102,6 +102,6 @@ func (e *RoleService) GetEnvironments(role string) (data RoleEnvironmentsResult, // Chef API docs: https://docs.chef.io/api_chef_server.html#roles-name-environments-name func (e *RoleService) GetEnvironmentRunlist(role string, environment string) (data EnvRunList, err error) { path := fmt.Sprintf("roles/%s/environments/%s", role, environment) - err = e.client.magicRequestDecoder("GET", path, nil, &data) + err = e.client.magicRequestDecoder("GET", path, UseOrg, nil, &data) return } diff --git a/sandbox.go b/sandbox.go index 6726ce28..28b0dbbf 100644 --- a/sandbox.go +++ b/sandbox.go @@ -51,7 +51,7 @@ func (s SandboxService) Post(sums []string) (data SandboxPostResponse, err error return } - err = s.client.magicRequestDecoder("POST", "sandboxes", body, &data) + err = s.client.magicRequestDecoder("POST", "sandboxes", UseOrg, body, &data) return } @@ -65,6 +65,6 @@ func (s SandboxService) Put(id string) (box Sandbox, err error) { return box, fmt.Errorf("must supply sandbox id to PUT request.") } - err = s.client.magicRequestDecoder("PUT", "sandboxes/"+id, body, &box) + err = s.client.magicRequestDecoder("PUT", "sandboxes/"+id, UseOrg, body, &box) return } diff --git a/search.go b/search.go index f09f0c70..7fdb6b30 100644 --- a/search.go +++ b/search.go @@ -50,7 +50,7 @@ func (e SearchService) PageSize(setting int) { // Do will execute the search query on the client func (q SearchQuery) Do(client *Client) (res SearchResult, err error) { fullUrl := fmt.Sprintf("search/%s", q) - err = client.magicRequestDecoder("GET", fullUrl, nil, &res) + err = client.magicRequestDecoder("GET", fullUrl, UseOrg, nil, &res) return } @@ -64,7 +64,7 @@ func (q SearchQuery) DoPartial(client *Client, params map[string]interface{}) (r return } - err = client.magicRequestDecoder("POST", fullUrl, body, &res) + err = client.magicRequestDecoder("POST", fullUrl, UseOrg, body, &res) return } @@ -144,7 +144,7 @@ func (e SearchService) PartialExec(idx, statement string, params map[string]inte return } - err = e.client.magicRequestDecoder("POST", fullUrl, body, &res) + err = e.client.magicRequestDecoder("POST", fullUrl, UseOrg, body, &res) if err != nil { return } @@ -163,7 +163,7 @@ func (e SearchService) PartialExec(idx, statement string, params map[string]inte return } fullUrl := fmt.Sprintf("search/%s", query) - err = e.client.magicRequestDecoder("POST", fullUrl, body, &paged_res) + err = e.client.magicRequestDecoder("POST", fullUrl, UseOrg, body, &paged_res) if err != nil { fmt.Printf("Partial search error %+v\n", err) return @@ -178,6 +178,6 @@ func (e SearchService) PartialExec(idx, statement string, params map[string]inte // // Chef API docs: http://docs.opscode.com/api_chef_server.html#id25 func (e SearchService) Indexes() (data map[string]string, err error) { - err = e.client.magicRequestDecoder("GET", "search", nil, &data) + err = e.client.magicRequestDecoder("GET", "search", UseOrg, nil, &data) return } diff --git a/stats.go b/stats.go index 9b6d1b2a..75975b93 100644 --- a/stats.go +++ b/stats.go @@ -16,6 +16,6 @@ type Stats []map[string]interface{} // out data will force JSON output. func (e *StatsService) Get(user string, password string) (data Stats, err error) { format := "json" - err = e.client.basicRequestDecoder("GET", "_stats?format="+format, nil, &data, user, password) + err = e.client.basicRequestDecoder("GET", "_stats?format="+format, true, nil, &data, user, password) return } diff --git a/status.go b/status.go index 83f24c4f..d9c7da7b 100644 --- a/status.go +++ b/status.go @@ -15,6 +15,6 @@ type Status struct { // // https://docs.chef.io/api_chef_server/#_status func (e *StatusService) Get() (data Status, err error) { - err = e.client.magicRequestDecoder("GET", "_status", nil, &data) + err = e.client.magicRequestDecoder("GET", "_status", UseOrg, nil, &data) return } diff --git a/test_chef_server/recipes/workstation.rb b/test_chef_server/recipes/workstation.rb index 54173486..fe51fd18 100644 --- a/test_chef_server/recipes/workstation.rb +++ b/test_chef_server/recipes/workstation.rb @@ -11,7 +11,7 @@ command 'ntpdate time.nist.gov' end -file '/etc/chef/accepted_licenses/chef_infra_server' do +file '/etc/chef/accepted_licenses/chef_workstation' do content "--- id: chef-workstation name: Chef Workstation @@ -24,5 +24,4 @@ execute 'Install chef workstation' do command 'curl --silent --show-error https://omnitruck.chef.io/install.sh | sudo -E bash -s -- -c stable -P chef-workstation' - not_if 'chef --version' end diff --git a/test_chef_server/test/integration/default/inspec/client1.3_spec.rb b/test_chef_server/test/integration/default/inspec/client1.3_spec.rb new file mode 100644 index 00000000..18a7c3c2 --- /dev/null +++ b/test_chef_server/test/integration/default/inspec/client1.3_spec.rb @@ -0,0 +1,23 @@ +# Inspec tests for the client chef api go module +# + +describe command('/go/src/testapi/bin/client1.3') do + its('stderr') { should match(%r{^Couldn't recreate client client1. POST https://testhost/organizations/test/clients: 409}) } + its('stderr') { should match(/^Couldn't recreate client client1. Code 409/) } + its('stderr') { should match(/^Couldn't recreate client client1. Msg Client already exists/) } + its('stderr') { should match(/^Couldn't recreate client client1. Text \{"error":\["Client already exists"\]\}/) } + its('stderr') { should_not match(/no such file|cannot find|not used|undefined/) } + its('stdout') { should match(%r{^List initial clients test-validator => https://testhost/organizations/test/clients/test-validator}) } + its('stdout') { should match(/^Define client1 \{Name:client1 ClientName: Validator:false Admin:false CreateKey:true\}/) } + its('stdout') { should match(%r{^Added client1 &\{Uri:https://testhost/organizations/test/clients/client1 ChefKey:\{Name:default PublicKey:-----BEGIN PUBLIC KEY-----MIIB.*ExpirationDate:infinity Uri:.*PrivateKey:-----BEGIN RSA PRIVATE KEY}) } + its('stdout') { should match(%r{^Added client2 &\{Uri:https://testhost/organizations/test/clients/client2 ChefKey:\{Name: PublicKey: ExpirationDate: Uri: PrivateKey:\}\}}) } + # TODO: are OrgName and Uri part of the get output + its('stdout') { should match(/Get client1 \{Name:client1 ClientName:client1 OrgName:test Validator:false JsonClass:Chef::ApiClient ChefType:client\}/) } + its('stdout') { should match(/Get client2 \{Name:client2 ClientName:client2 OrgName:test Validator:true JsonClass:Chef::ApiClient ChefType:client\}/) } + # TODO: are orgname and uri part of the output + its('stdout') { should match(/Update client1 &\{Name:clientchanged ClientName:clientchanged OrgName: Validator:true JsonClass:Chef::ApiClient ChefType:client\}/) } + its('stdout') { should match(/Update client2 &\{Name:client2 ClientName:client2 OrgName: Validator:true JsonClass:Chef::ApiClient ChefType:client\}/) } + its('stdout') { should match(/^Delete client1 /) } + its('stdout') { should match(/^Delete client2 /) } + its('stdout') { should match(%r{^List clients after cleanup test-validator => https://testhost/organizations/test/clients/test-validator}) } +end diff --git a/test_chef_server/test/integration/default/inspec/user_fix_spec.rb b/test_chef_server/test/integration/default/inspec/user_fix_spec.rb new file mode 100644 index 00000000..955a0279 --- /dev/null +++ b/test_chef_server/test/integration/default/inspec/user_fix_spec.rb @@ -0,0 +1,34 @@ +# Inspec tests for the user chef api go module +# + +describe command('/go/src/testapi/bin/user_fix') do + its('stderr') { should match(%r{^Issue creating user POST https://testhost/users: 409}) } + its('stderr') { should match(%r{^Issue creating user err: POST https://testhost/users: 409}) } + its('stderr') { should match(/^Issue creating user code: 409/) } + its('stderr') { should match(/^Issue creating user method: POST/) } + its('stderr') { should match(%r{^Issue creating user url: https://testhost/users}) } + its('stderr') { should_not match(/error|no such file|cannot find|not used|undefined/) } + its('stdout') { should match(%r{^List initial users map\[(?=.*pivotal:https://testhost/users/pivotal).*\] EndInitialList}) } + # might want a multi line match here to test for expirationdate, key uri and privatekey + its('stdout') { should match(%r{^Add usr1 \{Uri:https://testhost/users/usr1 ChefKey:\{Name:default PublicKey:-----BEGIN}) } + its('stdout') { should match(%r{^Add usr2 \{Uri:https://testhost/users/usr2 ChefKey:\{Name:default PublicKey:-----BEGIN}) } + its('stdout') { should match(%r{^Add usr3 \{Uri:https://testhost/users/usr3 ChefKey:\{Name: PublicKey: ExpirationDate: Uri: PrivateKey:\}\}}) } + its('stdout') { should match(%r{^Filter users map\[usr1:https://testhost/users/usr1\]}) } + its('stdout') { should match(/^Verbose out map\[(?=.*pivotal:)/) } + its('stdout') { should match(/^Get usr1 \{(?=.*UserName:usr1)(?=.*DisplayName:User1 Fullname)(?=.*Email:user1@domain.io)(?=.*ExternalAuthenticationUid:)(?=.*FirstName:user1)(?=.*LastName:fullname)(?=.*MiddleName:)(?=.*Password:)(?=.*PublicKey:)(?=.*RecoveryAuthenticationEnabled:false).*/) } + its('stdout') { should match(/^Pivotal user \{(?=.*UserName:pivotal)(?=.*DisplayName:Chef Server Superuser)(?=.*Email:root@localhost.localdomain)(?=.*ExternalAuthenticationUid:)(?=.*FirstName:Chef)(?=.*LastName:Server)(?=.*MiddleName:)(?=.*Password:)(?=.*PublicKey:)/) } + its('stdout') { should match(%r{^List after adding map\[(?=.*pivotal:https://testhost/users/pivotal)(?=.*usr1:https://testhost/users/usr1).*\] EndAddList}) } + its('stdout') { should match(/^Get usr1 \{(?=.*UserName:usr1)(?=.*DisplayName:User1 Fullname)(?=.*Email:user1@domain.io)(?=.*ExternalAuthenticationUid:)(?=.*FirstName:user1)(?=.*LastName:fullname)(?=.*MiddleName:)(?=.*Password:)(?=.*PublicKey:)/) } + its('stdout') { should match(%r{^List after adding map\[(?=.*pivotal:https://testhost/users/pivotal)(?=.*usr1:https://testhost/users/usr1).*\] EndAddList}) } + # TODO: - update and create new private key + # TODO - is admin a thing + its('stdout') { should match(%r{^Update usr1 partial update \{Uri:https://testhost/users/usr1 ChefKey:\{}) } + its('stdout') { should match(/^Get usr1 after partial update \{(UserName:usr1)(?=.*DisplayName:usr1)(?=.*Email:myuser@samp.com)(?=.*ExternalAuthenticationUid:)(?=.*FirstName:user1)(?=.*LastName:fullname)(?=.*MiddleName:)(?=.*Password:)(?=.*PublicKey:)(?=.*RecoveryAuthenticationEnabled:false).*\}/) } + its('stdout') { should match(%r{^Update usr1 full update \{Uri:https://testhost/users/usr1 ChefKey:\{Name: PublicKey: ExpirationDate: Uri: PrivateKey:\}}) } + its('stdout') { should match(/^Get usr1 after full update \{(UserName:usr1)(?=.*DisplayName:usr1)(?=.*Email:myuser@samp.com)(?=.*ExternalAuthenticationUid:)(?=.*FirstName:user)(?=.*LastName:name)(?=.*MiddleName:mid)(?=.*Password:)(?=.*PublicKey:)(?=.*RecoveryAuthenticationEnabled:false).*\}/) } + its('stdout') { should match(%r{^Update usr1 rename \{Uri:https://testhost/users/usr1new ChefKey:\{.*\}}) } + its('stdout') { should match(/^Get usr1 after rename \{(UserName:usr1new)(?=.*DisplayName:usr1)(?=.*Email:myuser@samp.com)(?=.*ExternalAuthenticationUid:)(?=.*FirstName:user)(?=.*LastName:name)(?=.*MiddleName:mid Password:)(?=.*PublicKey:)(?=.*RecoveryAuthenticationEnabled:false).*\}/) } + its('stdout') { should match(%r{^Delete usr1 DELETE https://testhost/users/usr1: 404}) } + its('stdout') { should match(/^Delete usr1new /) } + its('stdout') { should match(%r{^List after cleanup map\[(?=.*pivotal:https://testhost/users/pivotal).*\] EndCleanupList}) } +end diff --git a/testapi/bin/client b/testapi/bin/client index 0b1e6cea..a2815dfe 100755 --- a/testapi/bin/client +++ b/testapi/bin/client @@ -6,4 +6,4 @@ BASE=$(dirname $0) . ${BASE}/setup . ${BASE}/creds -go run ${BASE}/../testcase/testcase.go client ${CHEFUSER} ${KEYFILE} ${CHEFORGANIZATIONURL} ${SSLBYPASS} '1.3' +go run ${BASE}/../testcase/testcase.go client ${CHEFUSER} ${KEYFILE} ${CHEFORGANIZATIONURL} ${SSLBYPASS} '1.0' diff --git a/testapi/bin/client1.3 b/testapi/bin/client1.3 new file mode 100755 index 00000000..0b1e6cea --- /dev/null +++ b/testapi/bin/client1.3 @@ -0,0 +1,9 @@ +#!/bin/bash + +# Client testing + +BASE=$(dirname $0) + +. ${BASE}/setup +. ${BASE}/creds +go run ${BASE}/../testcase/testcase.go client ${CHEFUSER} ${KEYFILE} ${CHEFORGANIZATIONURL} ${SSLBYPASS} '1.3' diff --git a/testapi/bin/user b/testapi/bin/user index 41bc7b9c..6d345e50 100755 --- a/testapi/bin/user +++ b/testapi/bin/user @@ -6,4 +6,5 @@ BASE=$(dirname $0) . ${BASE}/setup . ${BASE}/creds +# Use the organization end point and let the GlobalBaseUrl code figure out the right URL go run ${BASE}/../testcase/testcase.go user ${CHEFUSER} ${KEYFILE} ${CHEFGLOBALURL} ${SSLBYPASS} diff --git a/testapi/bin/user_fix b/testapi/bin/user_fix new file mode 100755 index 00000000..3fbde01c --- /dev/null +++ b/testapi/bin/user_fix @@ -0,0 +1,10 @@ +#!/bin/bash + +# Group testing + +BASE=$(dirname $0) + +. ${BASE}/setup +. ${BASE}/creds +# Use the organization end point and let the GlobalBaseUrl code figure out the right URL +go run ${BASE}/../testcase/testcase.go user ${CHEFUSER} ${KEYFILE} ${CHEFORGANIZATIONURL} ${SSLBYPASS} "1.3" false diff --git a/testapi/sandbox.go b/testapi/sandbox.go index 93aebb99..dc563809 100644 --- a/testapi/sandbox.go +++ b/testapi/sandbox.go @@ -46,7 +46,7 @@ func Sandbox() { } // If you were writing this in your own tool you could just use the FH and let the Reader interface suck out the content instead of doing the convert. fmt.Printf("\nUploading: %s ---> %v\n\n", hash, item) - req, err := client.NewRequest("PUT", item.Url, bytes.NewReader(files[hash])) + req, err := client.NewRequest("PUT", item.Url, false, bytes.NewReader(files[hash])) // TODO: headers = { "content-type" => "application/x-binary", "content-md5" => checksum64, "accept" => "application/json" } if err != nil { fmt.Fprintln(os.Stderr, "Issue this shouldn't happen:", err) diff --git a/testapi/testapi.go b/testapi/testapi.go index 57443d46..aa1fadff 100644 --- a/testapi/testapi.go +++ b/testapi/testapi.go @@ -28,14 +28,18 @@ func Client() *chef.Client { if len(os.Args) > 6 { version = os.Args[6] } + stetUrl := true + if len(os.Args) > 7 && os.Args[7] == "false" { + stetUrl = false + } // Create a client for access - return buildClient(user, keyfile, chefurl, skipssl, version) + return buildClient(user, keyfile, chefurl, skipssl, version, stetUrl) } // buildClient creates a connection to a chef server using the chef api. // goiardi uses port 4545 by default, chef-zero uses 8889, chef-server uses 443 -func buildClient(user string, keyfile string, baseurl string, skipssl bool, version string) *chef.Client { +func buildClient(user string, keyfile string, baseurl string, skipssl bool, version string, stetUrl bool) *chef.Client { key := clientKey(keyfile) client, err := chef.NewClient(&chef.Config{ Name: user, @@ -44,6 +48,7 @@ func buildClient(user string, keyfile string, baseurl string, skipssl bool, vers SkipSSL: skipssl, RootCAs: chefCerts(), AuthenticationVersion: version, + StetBaseURL: stetUrl, }) if err != nil { diff --git a/universe.go b/universe.go index 279f6951..7a24f378 100644 --- a/universe.go +++ b/universe.go @@ -24,7 +24,7 @@ type UniverseVersion struct { // https://docs.chef.io/api_chef_server.html#universe func (e *UniverseService) Get() (universe Universe, err error) { var data map[string]interface{} - err = e.client.magicRequestDecoder("GET", "universe", nil, &data) + err = e.client.magicRequestDecoder("GET", "universe", UseOrg, nil, &data) unpackUniverse(&universe, &data) return } diff --git a/updated_since.go b/updated_since.go index 644d2247..552a9f61 100644 --- a/updated_since.go +++ b/updated_since.go @@ -23,7 +23,7 @@ type UpdatedSince struct { // Calls will always return 404 not found errors func (e UpdatedSinceService) Get(sequenceId int64) (updated []UpdatedSince, err error) { url := "updated_since?seq=" + strconv.FormatInt(sequenceId, 10) - err = e.client.magicRequestDecoder("GET", url, nil, &updated) + err = e.client.magicRequestDecoder("GET", url, UseOrg, nil, &updated) if err != nil { err = errors.New("Update_since is a deprecated endpoint and always returns 404.") } diff --git a/user.go b/user.go index 3db95f2a..37597263 100644 --- a/user.go +++ b/user.go @@ -43,7 +43,7 @@ func (e *UserService) List(filters ...string) (userlist map[string]string, err e if len(filters) > 0 { url += "?" + strings.Join(filters, "&") } - err = e.client.magicRequestDecoder("GET", url, nil, &userlist) + err = e.client.magicRequestDecoder("GET", url, UseGlobal, nil, &userlist) return } @@ -56,7 +56,7 @@ func (e *UserService) VerboseList(filters ...string) (userlist map[string]UserVe if len(filters) > 0 { url += "?" + strings.Join(filters, "&") } - err = e.client.magicRequestDecoder("GET", url, nil, &userlist) + err = e.client.magicRequestDecoder("GET", url, UseGlobal, nil, &userlist) return } @@ -75,7 +75,7 @@ func (e *UserService) Create(user User) (data UserResult, err error) { return } - err = e.client.magicRequestDecoder("POST", "users", body, &data) + err = e.client.magicRequestDecoder("POST", "users", UseGlobal, body, &data) return } @@ -88,7 +88,7 @@ func (e *UserService) Create(user User) (data UserResult, err error) { // // Chef API docs: https://docs.chef.io/api_chef_server.html#users-name func (e *UserService) Delete(name string) (err error) { - err = e.client.magicRequestDecoder("DELETE", "users/"+name, nil, nil) + err = e.client.magicRequestDecoder("DELETE", "users/"+name, UseGlobal, nil, nil) return } @@ -102,7 +102,7 @@ func (e *UserService) Delete(name string) (err error) { // Chef API docs: https://docs.chef.io/api_chef_server.html#users-name func (e *UserService) Get(name string) (user User, err error) { url := fmt.Sprintf("users/%s", name) - err = e.client.magicRequestDecoder("GET", url, nil, &user) + err = e.client.magicRequestDecoder("GET", url, UseGlobal, nil, &user) return } @@ -118,7 +118,7 @@ func (e *UserService) Get(name string) (user User, err error) { func (e *UserService) Update(name string, user User) (userUpdate UserResult, err error) { url := fmt.Sprintf("users/%s", name) body, err := JSONReader(user) - err = e.client.magicRequestDecoder("PUT", url, body, &userUpdate) + err = e.client.magicRequestDecoder("PUT", url, UseGlobal, body, &userUpdate) return } @@ -132,7 +132,7 @@ func (e *UserService) Update(name string, user User) (userUpdate UserResult, err // Chef API docs: https://docs.chef.io/api_chef_server/#usersuserkeys func (e *UserService) ListKeys(name string) (userkeys []KeyItem, err error) { url := fmt.Sprintf("users/%s/keys", name) - err = e.client.magicRequestDecoder("GET", url, nil, &userkeys) + err = e.client.magicRequestDecoder("GET", url, UseGlobal, nil, &userkeys) return } @@ -148,7 +148,7 @@ func (e *UserService) ListKeys(name string) (userkeys []KeyItem, err error) { func (e *UserService) AddKey(name string, keyadd AccessKey) (key KeyItem, err error) { url := fmt.Sprintf("users/%s/keys", name) body, err := JSONReader(keyadd) - err = e.client.magicRequestDecoder("POST", url, body, &key) + err = e.client.magicRequestDecoder("POST", url, UseGlobal, body, &key) return } @@ -162,7 +162,7 @@ func (e *UserService) AddKey(name string, keyadd AccessKey) (key KeyItem, err er // Chef API docs: https://docs.chef.io/api_chef_server/#usersuserkeys func (e *UserService) DeleteKey(name string, keyname string) (key AccessKey, err error) { url := fmt.Sprintf("users/%s/keys/%s", name, keyname) - err = e.client.magicRequestDecoder("DELETE", url, nil, &key) + err = e.client.magicRequestDecoder("DELETE", url, UseGlobal, nil, &key) return } @@ -176,7 +176,7 @@ func (e *UserService) DeleteKey(name string, keyname string) (key AccessKey, err // Chef API docs: https://docs.chef.io/api_chef_server/#usersuserkeys func (e *UserService) GetKey(name string, keyname string) (key AccessKey, err error) { url := fmt.Sprintf("users/%s/keys/%s", name, keyname) - err = e.client.magicRequestDecoder("GET", url, nil, &key) + err = e.client.magicRequestDecoder("GET", url, UseGlobal, nil, &key) return } @@ -191,6 +191,6 @@ func (e *UserService) GetKey(name string, keyname string) (key AccessKey, err er func (e *UserService) UpdateKey(username string, keyname string, keyUp AccessKey) (userkey AccessKey, err error) { url := fmt.Sprintf("users/%s/keys/%s", username, keyname) body, err := JSONReader(keyUp) - err = e.client.magicRequestDecoder("PUT", url, body, &userkey) + err = e.client.magicRequestDecoder("PUT", url, UseGlobal, body, &userkey) return }