From ff1b8db8efb715814f1f586d6e253e2ade40e3b7 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Fri, 6 Dec 2019 17:11:23 +0200 Subject: [PATCH 01/10] Add change which allows to pass and override default hard coded api version Signed-off-by: Vaidotas Bauzys --- govcd/api.go | 108 +++++++++++++++++++++++++++++---- govcd/api_vcd.go | 13 ++-- govcd/api_vcd_versions.go | 48 +++++++-------- govcd/api_vcd_versions_test.go | 30 ++++----- govcd/disk_test.go | 2 +- govcd/query.go | 2 +- govcd/upload.go | 2 +- govcd/vapp_test.go | 2 +- govcd/vm_test.go | 2 +- 9 files changed, 146 insertions(+), 63 deletions(-) diff --git a/govcd/api.go b/govcd/api.go index bfcdf8077..f54a54b82 100644 --- a/govcd/api.go +++ b/govcd/api.go @@ -34,6 +34,8 @@ type Client struct { // where vCloud director may take time to respond and retry mechanism is needed. // This must be >0 to avoid instant timeout errors. MaxRetryTimeout int + + supportedVersions SupportedVersions // Versions from /api/versions endpoint } // The header key used by default to set the authorization token. @@ -126,7 +128,7 @@ func ContainsNotFound(err error) bool { } // NewRequestWitNotEncodedParams allows passing complex values params that 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 { +func (cli *Client) NewRequestWitNotEncodedParams(params map[string]string, notEncodedParams map[string]string, method string, reqUrl url.URL, body io.Reader, apiVersion string) *http.Request { reqValues := url.Values{} // Build up our request parameters @@ -152,7 +154,7 @@ func (cli *Client) NewRequestWitNotEncodedParams(params map[string]string, notEn // Add the authorization header req.Header.Add(cli.VCDAuthHeader, cli.VCDToken) // Add the Accept header for VCD - req.Header.Add("Accept", "application/*+xml;version="+cli.APIVersion) + req.Header.Add("Accept", "application/*+xml;version="+apiVersion) } // Avoids passing data if the logging of requests is disabled @@ -185,7 +187,13 @@ func (cli *Client) NewRequestWitNotEncodedParams(params map[string]string, notEn // NewRequest creates a new HTTP request and applies necessary auth headers if // set. func (cli *Client) NewRequest(params map[string]string, method string, reqUrl url.URL, body io.Reader) *http.Request { - return cli.NewRequestWitNotEncodedParams(params, nil, method, reqUrl, body) + return cli.NewRequestWitNotEncodedParams(params, nil, method, reqUrl, body, cli.APIVersion) +} + +// NewRequest creates a new HTTP request and applies necessary auth headers if set. +// Allows to override default request API Version +func (cli *Client) NewRequestWithApiVersion(params map[string]string, method string, reqUrl url.URL, body io.Reader, apiVersion string) *http.Request { + return cli.NewRequestWitNotEncodedParams(params, nil, method, reqUrl, body, apiVersion) } // ParseErr takes an error XML resp, error interface for unmarshaling and returns a single string for @@ -284,12 +292,36 @@ func checkRespWithErrType(resp *http.Response, err, errType error) (*http.Respon // payload - XML struct which will be marshalled and added as body/payload // E.g. client.ExecuteTaskRequest(updateDiskLink.HREF, http.MethodPut, updateDiskLink.Type, "error updating disk: %s", xmlPayload) func (client *Client) ExecuteTaskRequest(pathURL, requestType, contentType, errorMessage string, payload interface{}) (Task, error) { + return client.executeTaskRequest(pathURL, requestType, contentType, errorMessage, payload, client.APIVersion) +} + +// Helper function creates request, runs it, checks response and parses task from response. +// pathURL - request URL +// requestType - HTTP method type +// contentType - value to set for "Content-Type" +// errorMessage - error message to return when error happens +// payload - XML struct which will be marshalled and added as body/payload +// apiVersion - api version which will be used in request +// E.g. client.ExecuteTaskRequest(updateDiskLink.HREF, http.MethodPut, updateDiskLink.Type, "error updating disk: %s", xmlPayload) +func (client *Client) ExecuteTaskRequestWithApiVersion(pathURL, requestType, contentType, errorMessage string, payload interface{}, apiVersion string) (Task, error) { + return client.executeTaskRequest(pathURL, requestType, contentType, errorMessage, payload, apiVersion) +} + +// Helper function creates request, runs it, checks response and parses task from response. +// pathURL - request URL +// requestType - HTTP method type +// contentType - value to set for "Content-Type" +// errorMessage - error message to return when error happens +// payload - XML struct which will be marshalled and added as body/payload +// apiVersion - api version which will be used in request +// E.g. client.ExecuteTaskRequest(updateDiskLink.HREF, http.MethodPut, updateDiskLink.Type, "error updating disk: %s", xmlPayload) +func (client *Client) executeTaskRequest(pathURL, requestType, contentType, errorMessage string, payload interface{}, apiVersion string) (Task, error) { if !isMessageWithPlaceHolder(errorMessage) { return Task{}, fmt.Errorf("error message has to include place holder for error") } - resp, err := executeRequest(pathURL, requestType, contentType, payload, client) + resp, err := executeRequestWithApiVersion(pathURL, requestType, contentType, payload, client, apiVersion) if err != nil { return Task{}, fmt.Errorf(errorMessage, err) } @@ -317,12 +349,36 @@ func (client *Client) ExecuteTaskRequest(pathURL, requestType, contentType, erro // payload - XML struct which will be marshalled and added as body/payload // E.g. client.ExecuteRequestWithoutResponse(catalogItemHREF.String(), http.MethodDelete, "", "error deleting Catalog item: %s", nil) func (client *Client) ExecuteRequestWithoutResponse(pathURL, requestType, contentType, errorMessage string, payload interface{}) error { + return client.executeRequestWithoutResponse(pathURL, requestType, contentType, errorMessage, payload, client.APIVersion) +} + +// Helper function creates request, runs it, checks response and do not expect any values from it. +// pathURL - request URL +// requestType - HTTP method type +// contentType - value to set for "Content-Type" +// errorMessage - error message to return when error happens +// payload - XML struct which will be marshalled and added as body/payload +// apiVersion - api version which will be used in request +// E.g. client.ExecuteRequestWithoutResponse(catalogItemHREF.String(), http.MethodDelete, "", "error deleting Catalog item: %s", nil) +func (client *Client) ExecuteRequestWithoutResponseWithApiVersion(pathURL, requestType, contentType, errorMessage string, payload interface{}, apiVersion string) error { + return client.executeRequestWithoutResponse(pathURL, requestType, contentType, errorMessage, payload, apiVersion) +} + +// Helper function creates request, runs it, checks response and do not expect any values from it. +// pathURL - request URL +// requestType - HTTP method type +// contentType - value to set for "Content-Type" +// errorMessage - error message to return when error happens +// payload - XML struct which will be marshalled and added as body/payload +// apiVersion - api version which will be used in request +// E.g. client.ExecuteRequestWithoutResponse(catalogItemHREF.String(), http.MethodDelete, "", "error deleting Catalog item: %s", nil) +func (client *Client) executeRequestWithoutResponse(pathURL, requestType, contentType, errorMessage string, payload interface{}, apiVersion string) error { if !isMessageWithPlaceHolder(errorMessage) { return fmt.Errorf("error message has to include place holder for error") } - resp, err := executeRequest(pathURL, requestType, contentType, payload, client) + resp, err := executeRequestWithApiVersion(pathURL, requestType, contentType, payload, client, apiVersion) if err != nil { return fmt.Errorf(errorMessage, err) } @@ -350,12 +406,40 @@ func (client *Client) ExecuteRequestWithoutResponse(pathURL, requestType, conten // E.g. unmarshalledAdminOrg := &types.AdminOrg{} // client.ExecuteRequest(adminOrg.AdminOrg.HREF, http.MethodGet, "", "error refreshing organization: %s", nil, unmarshalledAdminOrg) func (client *Client) ExecuteRequest(pathURL, requestType, contentType, errorMessage string, payload, out interface{}) (*http.Response, error) { + return client.executeRequest(pathURL, requestType, contentType, errorMessage, payload, out, client.APIVersion) +} + +// Helper function creates request, runs it, check responses and parses out interface from response. +// pathURL - request URL +// requestType - HTTP method type +// contentType - value to set for "Content-Type" +// errorMessage - error message to return when error happens +// payload - XML struct which will be marshalled and added as body/payload +// out - structure to be used for unmarshalling xml +// apiVersion - api version which will be used in request +// E.g. unmarshalledAdminOrg := &types.AdminOrg{} +// client.ExecuteRequest(adminOrg.AdminOrg.HREF, http.MethodGet, "", "error refreshing organization: %s", nil, unmarshalledAdminOrg) +func (client *Client) ExecuteRequestWithApiVersion(pathURL, requestType, contentType, errorMessage string, payload, out interface{}, apiVersion string) (*http.Response, error) { + return client.executeRequest(pathURL, requestType, contentType, errorMessage, payload, out, apiVersion) +} + +// Helper function creates request, runs it, check responses and parses out interface from response. +// pathURL - request URL +// requestType - HTTP method type +// contentType - value to set for "Content-Type" +// errorMessage - error message to return when error happens +// payload - XML struct which will be marshalled and added as body/payload +// out - structure to be used for unmarshalling xml +// apiVersion - api version which will be used in request +// E.g. unmarshalledAdminOrg := &types.AdminOrg{} +// client.ExecuteRequest(adminOrg.AdminOrg.HREF, http.MethodGet, "", "error refreshing organization: %s", nil, unmarshalledAdminOrg) +func (client *Client) executeRequest(pathURL, requestType, contentType, errorMessage string, payload, out interface{}, apiVersion string) (*http.Response, error) { if !isMessageWithPlaceHolder(errorMessage) { return &http.Response{}, fmt.Errorf("error message has to include place holder for error") } - resp, err := executeRequest(pathURL, requestType, contentType, payload, client) + resp, err := executeRequestWithApiVersion(pathURL, requestType, contentType, payload, client, apiVersion) if err != nil { return resp, fmt.Errorf(errorMessage, err) } @@ -390,7 +474,7 @@ func (client *Client) ExecuteParamRequestWithCustomError(pathURL string, params return &http.Response{}, fmt.Errorf("error message has to include place holder for error") } - resp, err := executeRequestCustomErr(pathURL, params, requestType, contentType, payload, client, errType) + resp, err := executeRequestCustomErr(pathURL, params, requestType, contentType, payload, client, errType, client.APIVersion) if err != nil { return &http.Response{}, fmt.Errorf(errorMessage, err) } @@ -413,12 +497,12 @@ func (client *Client) ExecuteParamRequestWithCustomError(pathURL string, params } // executeRequest does executeRequestCustomErr and checks for vCD errors in API response -func executeRequest(pathURL, requestType, contentType string, payload interface{}, client *Client) (*http.Response, error) { - return executeRequestCustomErr(pathURL, map[string]string{}, requestType, contentType, payload, client, &types.Error{}) +func executeRequestWithApiVersion(pathURL, requestType, contentType string, payload interface{}, client *Client, apiVersion string) (*http.Response, error) { + return executeRequestCustomErr(pathURL, map[string]string{}, requestType, contentType, payload, client, &types.Error{}, apiVersion) } // executeRequestCustomErr performs request and unmarshals API error to errType if not 2xx status was returned -func executeRequestCustomErr(pathURL string, params map[string]string, requestType, contentType string, payload interface{}, client *Client, errType error) (*http.Response, error) { +func executeRequestCustomErr(pathURL string, params map[string]string, requestType, contentType string, payload interface{}, client *Client, errType error, apiVersion string) (*http.Response, error) { url, _ := url.ParseRequestURI(pathURL) var req *http.Request @@ -431,10 +515,10 @@ func executeRequestCustomErr(pathURL string, params map[string]string, requestTy } body := bytes.NewBufferString(xml.Header + string(marshaledXml)) - req = client.NewRequest(params, requestType, *url, body) + req = client.NewRequestWithApiVersion(params, requestType, *url, body, apiVersion) default: - req = client.NewRequest(params, requestType, *url, nil) + req = client.NewRequestWithApiVersion(params, requestType, *url, nil, apiVersion) } if contentType != "" { diff --git a/govcd/api_vcd.go b/govcd/api_vcd.go index 752c0b6e6..8c30f119f 100644 --- a/govcd/api_vcd.go +++ b/govcd/api_vcd.go @@ -21,21 +21,20 @@ import ( type VCDClientOption func(*VCDClient) error type VCDClient struct { - Client Client // Client for the underlying VCD instance - sessionHREF url.URL // HREF for the session API - QueryHREF url.URL // HREF for the query API - Mutex sync.Mutex - supportedVersions SupportedVersions // Versions from /api/versions endpoint + Client Client // Client for the underlying VCD instance + sessionHREF url.URL // HREF for the session API + QueryHREF url.URL // HREF for the query API + Mutex sync.Mutex } func (vcdCli *VCDClient) vcdloginurl() error { - if err := vcdCli.validateAPIVersion(); err != nil { + if err := vcdCli.Client.validateAPIVersion(); err != nil { return fmt.Errorf("could not find valid version for login: %s", err) } // find login address matching the API version var neededVersion VersionInfo - for _, versionInfo := range vcdCli.supportedVersions.VersionInfos { + for _, versionInfo := range vcdCli.Client.supportedVersions.VersionInfos { if versionInfo.Version == vcdCli.Client.APIVersion { neededVersion = versionInfo break diff --git a/govcd/api_vcd_versions.go b/govcd/api_vcd_versions.go index 6e3d398e5..4d1f3c751 100644 --- a/govcd/api_vcd_versions.go +++ b/govcd/api_vcd_versions.go @@ -36,21 +36,21 @@ type SupportedVersions struct { // Format: ">= 27.0, < 32.0", ">= 30.0", "= 27.0" // // vCD version mapping to API version support https://code.vmware.com/doc/preview?id=8072 -func (vcdCli *VCDClient) APIVCDMaxVersionIs(versionConstraint string) bool { - err := vcdCli.vcdFetchSupportedVersions() +func (cli *Client) APIVCDMaxVersionIs(versionConstraint string) bool { + err := cli.vcdFetchSupportedVersions() if err != nil { util.Logger.Printf("[ERROR] could not retrieve supported versions: %s", err) return false } util.Logger.Printf("[TRACE] checking max API version against constraints '%s'", versionConstraint) - maxVersion, err := vcdCli.maxSupportedVersion() + maxVersion, err := cli.maxSupportedVersion() if err != nil { util.Logger.Printf("[ERROR] unable to find max supported version : %s", err) return false } - isSupported, err := vcdCli.apiVersionMatchesConstraint(maxVersion, versionConstraint) + isSupported, err := cli.apiVersionMatchesConstraint(maxVersion, versionConstraint) if err != nil { util.Logger.Printf("[ERROR] unable to find max supported version : %s", err) return false @@ -66,11 +66,11 @@ func (vcdCli *VCDClient) APIVCDMaxVersionIs(versionConstraint string) bool { // Format: ">= 27.0, < 32.0", ">= 30.0", "= 27.0" // // vCD version mapping to API version support https://code.vmware.com/doc/preview?id=8072 -func (vcdCli *VCDClient) APIClientVersionIs(versionConstraint string) bool { +func (cli *Client) APIClientVersionIs(versionConstraint string) bool { util.Logger.Printf("[TRACE] checking current API version against constraints '%s'", versionConstraint) - isSupported, err := vcdCli.apiVersionMatchesConstraint(vcdCli.Client.APIVersion, versionConstraint) + isSupported, err := cli.apiVersionMatchesConstraint(cli.APIVersion, versionConstraint) if err != nil { util.Logger.Printf("[ERROR] unable to find cur supported version : %s", err) return false @@ -82,30 +82,30 @@ func (vcdCli *VCDClient) APIClientVersionIs(versionConstraint string) bool { // vcdFetchSupportedVersions retrieves list of supported versions from // /api/versions endpoint and stores them in VCDClient for future uses. // It only does it once. -func (vcdCli *VCDClient) vcdFetchSupportedVersions() error { +func (cli *Client) vcdFetchSupportedVersions() error { // Only fetch /versions if it is not stored already - numVersions := len(vcdCli.supportedVersions.VersionInfos) + numVersions := len(cli.supportedVersions.VersionInfos) if numVersions > 0 { util.Logger.Printf("[TRACE] skipping fetch of versions because %d are stored", numVersions) return nil } - apiEndpoint := vcdCli.Client.VCDHREF + apiEndpoint := cli.VCDHREF apiEndpoint.Path += "/versions" suppVersions := new(SupportedVersions) - _, err := vcdCli.Client.ExecuteRequest(apiEndpoint.String(), http.MethodGet, + _, err := cli.ExecuteRequest(apiEndpoint.String(), http.MethodGet, "", "error fetching versions: %s", nil, suppVersions) - vcdCli.supportedVersions = *suppVersions + cli.supportedVersions = *suppVersions return err } // maxSupportedVersion parses supported version list and returns the highest version in string format. -func (vcdCli *VCDClient) maxSupportedVersion() (string, error) { - versions := make([]*semver.Version, len(vcdCli.supportedVersions.VersionInfos)) - for index, versionInfo := range vcdCli.supportedVersions.VersionInfos { +func (cli *Client) maxSupportedVersion() (string, error) { + versions := make([]*semver.Version, len(cli.supportedVersions.VersionInfos)) + for index, versionInfo := range cli.supportedVersions.VersionInfos { version, _ := semver.NewVersion(versionInfo.Version) versions[index] = version } @@ -124,15 +124,15 @@ func (vcdCli *VCDClient) maxSupportedVersion() (string, error) { // vcdCheckSupportedVersion checks if there is at least one specified version exactly matching listed ones. // Format example "27.0" -func (vcdCli *VCDClient) vcdCheckSupportedVersion(version string) (bool, error) { - return vcdCli.checkSupportedVersionConstraint(fmt.Sprintf("= %s", version)) +func (cli *Client) vcdCheckSupportedVersion(version string) (bool, error) { + return cli.checkSupportedVersionConstraint(fmt.Sprintf("= %s", version)) } // Checks if there is at least one specified version matching the list returned by vCD. // Constraint format can be in format ">= 27.0, < 32",">= 30" ,"= 27.0". -func (vcdCli *VCDClient) checkSupportedVersionConstraint(versionConstraint string) (bool, error) { - for _, versionInfo := range vcdCli.supportedVersions.VersionInfos { - versionMatch, err := vcdCli.apiVersionMatchesConstraint(versionInfo.Version, versionConstraint) +func (cli *Client) checkSupportedVersionConstraint(versionConstraint string) (bool, error) { + for _, versionInfo := range cli.supportedVersions.VersionInfos { + versionMatch, err := cli.apiVersionMatchesConstraint(versionInfo.Version, versionConstraint) if err != nil { return false, fmt.Errorf("cannot match version: %s", err) } @@ -144,7 +144,7 @@ func (vcdCli *VCDClient) checkSupportedVersionConstraint(versionConstraint strin return false, fmt.Errorf("version %s is not supported", versionConstraint) } -func (vcdCli *VCDClient) apiVersionMatchesConstraint(version, versionConstraint string) (bool, error) { +func (cli *Client) apiVersionMatchesConstraint(version, versionConstraint string) (bool, error) { checkVer, err := semver.NewVersion(version) if err != nil { @@ -165,15 +165,15 @@ func (vcdCli *VCDClient) apiVersionMatchesConstraint(version, versionConstraint } // validateAPIVersion fetches API versions -func (vcdCli *VCDClient) validateAPIVersion() error { - err := vcdCli.vcdFetchSupportedVersions() +func (cli *Client) validateAPIVersion() error { + err := cli.vcdFetchSupportedVersions() if err != nil { return fmt.Errorf("could not retrieve supported versions: %s", err) } // Check if version is supported - if ok, err := vcdCli.vcdCheckSupportedVersion(vcdCli.Client.APIVersion); !ok || err != nil { - return fmt.Errorf("API version %s is not supported: %s", vcdCli.Client.APIVersion, err) + if ok, err := cli.vcdCheckSupportedVersion(cli.APIVersion); !ok || err != nil { + return fmt.Errorf("API version %s is not supported: %s", cli.APIVersion, err) } return nil diff --git a/govcd/api_vcd_versions_test.go b/govcd/api_vcd_versions_test.go index fa4d98c7d..4b323a1ff 100644 --- a/govcd/api_vcd_versions_test.go +++ b/govcd/api_vcd_versions_test.go @@ -19,9 +19,9 @@ func (vcd *TestVCD) Test_APIVCDMaxVersionIs_Unauthenticated(check *C) { vcdClient, err := GetTestVCDFromYaml(config) check.Assert(err, IsNil) - versionCheck := vcdClient.APIVCDMaxVersionIs(">= 27.0") + versionCheck := vcdClient.Client.APIVCDMaxVersionIs(">= 27.0") check.Assert(versionCheck, Equals, true) - check.Assert(vcdClient.supportedVersions.VersionInfos, Not(Equals), 0) + check.Assert(vcdClient.Client.supportedVersions.VersionInfos, Not(Equals), 0) } func (vcd *TestVCD) Test_APIClientVersionIs_Unauthenticated(check *C) { @@ -31,16 +31,16 @@ func (vcd *TestVCD) Test_APIClientVersionIs_Unauthenticated(check *C) { vcdClient, err := GetTestVCDFromYaml(config) check.Assert(err, IsNil) - versionCheck := vcdClient.APIClientVersionIs(">= 27.0") + versionCheck := vcdClient.Client.APIClientVersionIs(">= 27.0") check.Assert(versionCheck, Equals, true) - check.Assert(vcdClient.supportedVersions.VersionInfos, Not(Equals), 0) + check.Assert(vcdClient.Client.supportedVersions.VersionInfos, Not(Equals), 0) } // Test_APIVCDMaxVersionIs uses already authenticated vcdClient (in SetupSuite) func (vcd *TestVCD) Test_APIVCDMaxVersionIs(check *C) { // Minimum supported vCD 8.20 introduced API version 27.0 - versionCheck := vcd.client.APIVCDMaxVersionIs(">= 27.0") + versionCheck := vcd.client.Client.APIVCDMaxVersionIs(">= 27.0") check.Assert(versionCheck, Equals, true) mockVcd := getMockVcdWithAPIVersion("27.0") @@ -60,7 +60,7 @@ func (vcd *TestVCD) Test_APIVCDMaxVersionIs(check *C) { } for _, tt := range versionTests { - versionCheck := mockVcd.APIVCDMaxVersionIs(tt.version) + versionCheck := mockVcd.Client.APIVCDMaxVersionIs(tt.version) check.Assert(versionCheck, tt.boolChecker, tt.isSupported) } } @@ -69,10 +69,10 @@ func (vcd *TestVCD) Test_APIVCDMaxVersionIs(check *C) { func (vcd *TestVCD) Test_APIClientVersionIs(check *C) { // Check with currently set version - versionCheck := vcd.client.APIClientVersionIs(fmt.Sprintf("= %s", vcd.client.Client.APIVersion)) + versionCheck := vcd.client.Client.APIClientVersionIs(fmt.Sprintf("= %s", vcd.client.Client.APIVersion)) check.Assert(versionCheck, Equals, true) - versionCheck = vcd.client.APIClientVersionIs(">= 27.0") + versionCheck = vcd.client.Client.APIClientVersionIs(">= 27.0") check.Assert(versionCheck, Equals, true) mockVcd := getMockVcdWithAPIVersion("27.0") @@ -92,7 +92,7 @@ func (vcd *TestVCD) Test_APIClientVersionIs(check *C) { } for _, tt := range versionTests { - versionCheck := mockVcd.APIClientVersionIs(tt.version) + versionCheck := mockVcd.Client.APIClientVersionIs(tt.version) check.Assert(versionCheck, tt.boolChecker, tt.isSupported) } } @@ -107,7 +107,7 @@ func (vcd *TestVCD) Test_validateAPIVersion(check *C) { vcdClient, err := GetTestVCDFromYaml(config, WithAPIVersion(unsupportedVersion)) check.Assert(err, IsNil) - err = vcdClient.validateAPIVersion() + err = vcdClient.Client.validateAPIVersion() check.Assert(err, ErrorMatches, "API version .* is not supported: version = .* is not supported") } @@ -115,11 +115,11 @@ func getMockVcdWithAPIVersion(version string) *VCDClient { return &VCDClient{ Client: Client{ APIVersion: version, - }, - supportedVersions: SupportedVersions{ - VersionInfos{ - VersionInfo{ - Version: version, + supportedVersions: SupportedVersions{ + VersionInfos{ + VersionInfo{ + Version: version, + }, }, }, }, diff --git a/govcd/disk_test.go b/govcd/disk_test.go index 9df5ce4f2..1800d62d4 100644 --- a/govcd/disk_test.go +++ b/govcd/disk_test.go @@ -601,7 +601,7 @@ func (vcd *TestVCD) Test_QueryDisk(check *C) { check.Assert(diskRecord.Disk.SizeB, Equals, int64(diskCreateParamsDisk.Size)) // vCD version >= 9.5. Earlier versions don't return Description - if vcd.client.APIVCDMaxVersionIs(">= 31.0") { + if vcd.client.Client.APIVCDMaxVersionIs(">= 31.0") { check.Assert(diskRecord.Disk.Description, Equals, diskCreateParamsDisk.Description) } else { fmt.Printf("%s: skipping disk description check (not available in vCD < 9.5) \n", check.TestName()) diff --git a/govcd/query.go b/govcd/query.go index 564c2b8b9..7552461cd 100644 --- a/govcd/query.go +++ b/govcd/query.go @@ -45,7 +45,7 @@ func (client *Client) QueryWithNotEncodedParams(params map[string]string, notEnc queryUlr := client.VCDHREF queryUlr.Path += "/query" - req := client.NewRequestWitNotEncodedParams(params, notEncodedParams, http.MethodGet, queryUlr, nil) + req := client.NewRequestWitNotEncodedParams(params, notEncodedParams, http.MethodGet, queryUlr, nil, client.APIVersion) req.Header.Add("Accept", "vnd.vmware.vcloud.org+xml;version="+client.APIVersion) return getResult(client, req) diff --git a/govcd/upload.go b/govcd/upload.go index d937b551c..d5cb5aa3f 100644 --- a/govcd/upload.go +++ b/govcd/upload.go @@ -119,7 +119,7 @@ func newFileUploadRequest(client *Client, requestUrl string, filePart []byte, of return nil, fmt.Errorf("error decoding vdc response: %s", err) } - uploadReq := client.NewRequestWitNotEncodedParams(nil, nil, http.MethodPut, *parsedRequestURL, bytes.NewReader(filePart)) + uploadReq := client.NewRequestWitNotEncodedParams(nil, nil, http.MethodPut, *parsedRequestURL, bytes.NewReader(filePart), client.APIVersion) uploadReq.ContentLength = filePartSize uploadReq.Header.Set("Content-Length", strconv.FormatInt(uploadReq.ContentLength, 10)) diff --git a/govcd/vapp_test.go b/govcd/vapp_test.go index ba5a4c706..0f31f2fbc 100644 --- a/govcd/vapp_test.go +++ b/govcd/vapp_test.go @@ -376,7 +376,7 @@ func (vcd *TestVCD) Test_AddAndRemoveIsolatedNetwork(check *C) { } // vCD 8.20 does not support sending guestVlanAllowed - if vcd.client.APIVCDMaxVersionIs("> 27.0") { + if vcd.client.Client.APIVCDMaxVersionIs("> 27.0") { vappNetworkSettings.GuestVLANAllowed = &guestVlanAllowed } else { fmt.Printf("Skipping GuestVLANAllowed parameter as it is not supported on vCD 8.20") diff --git a/govcd/vm_test.go b/govcd/vm_test.go index 2aa067290..bb435b642 100644 --- a/govcd/vm_test.go +++ b/govcd/vm_test.go @@ -454,7 +454,7 @@ func (vcd *TestVCD) Test_InsertOrEjectMedia(check *C) { } // Skipping this test due to a bug in vCD. VM refresh status returns old state, though eject task is finished. - if vcd.client.APIVCDMaxVersionIs(">= 32.0, <= 33.0") { + if vcd.client.Client.APIVCDMaxVersionIs(">= 32.0, <= 33.0") { check.Skip("Skipping test because this vCD version has a bug") } From 1666e87e61a57f991da8c52b6a4bd97aef5b5c37 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 9 Dec 2019 10:35:36 +0200 Subject: [PATCH 02/10] Add back function to be back compatible Signed-off-by: Vaidotas Bauzys --- govcd/api.go | 13 +++++++++---- govcd/query.go | 2 +- govcd/upload.go | 2 +- util/logging.go | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/govcd/api.go b/govcd/api.go index f54a54b82..50cba14ff 100644 --- a/govcd/api.go +++ b/govcd/api.go @@ -127,8 +127,13 @@ func ContainsNotFound(err error) bool { return err != nil && strings.Contains(err.Error(), ErrorEntityNotFound.Error()) } -// NewRequestWitNotEncodedParams allows passing complex values params that 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, apiVersion string) *http.Request { +// NewRequestWitNotEncodedParamsWithApi allows passing complex values params that 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 { + return cli.NewRequestWitNotEncodedParamsWithApiVersion(params, notEncodedParams, method, reqUrl, body, cli.APIVersion) +} + +// NewRequestWitNotEncodedParamsWithApiVersion allows passing complex values params that shouldn't be encoded like for queries. e.g. /query?filter=name=foo +func (cli *Client) NewRequestWitNotEncodedParamsWithApiVersion(params map[string]string, notEncodedParams map[string]string, method string, reqUrl url.URL, body io.Reader, apiVersion string) *http.Request { reqValues := url.Values{} // Build up our request parameters @@ -187,13 +192,13 @@ func (cli *Client) NewRequestWitNotEncodedParams(params map[string]string, notEn // NewRequest creates a new HTTP request and applies necessary auth headers if // set. func (cli *Client) NewRequest(params map[string]string, method string, reqUrl url.URL, body io.Reader) *http.Request { - return cli.NewRequestWitNotEncodedParams(params, nil, method, reqUrl, body, cli.APIVersion) + return cli.NewRequestWitNotEncodedParams(params, nil, method, reqUrl, body) } // NewRequest creates a new HTTP request and applies necessary auth headers if set. // Allows to override default request API Version func (cli *Client) NewRequestWithApiVersion(params map[string]string, method string, reqUrl url.URL, body io.Reader, apiVersion string) *http.Request { - return cli.NewRequestWitNotEncodedParams(params, nil, method, reqUrl, body, apiVersion) + return cli.NewRequestWitNotEncodedParamsWithApiVersion(params, nil, method, reqUrl, body, apiVersion) } // ParseErr takes an error XML resp, error interface for unmarshaling and returns a single string for diff --git a/govcd/query.go b/govcd/query.go index 7552461cd..564c2b8b9 100644 --- a/govcd/query.go +++ b/govcd/query.go @@ -45,7 +45,7 @@ func (client *Client) QueryWithNotEncodedParams(params map[string]string, notEnc queryUlr := client.VCDHREF queryUlr.Path += "/query" - req := client.NewRequestWitNotEncodedParams(params, notEncodedParams, http.MethodGet, queryUlr, nil, client.APIVersion) + req := client.NewRequestWitNotEncodedParams(params, notEncodedParams, http.MethodGet, queryUlr, nil) req.Header.Add("Accept", "vnd.vmware.vcloud.org+xml;version="+client.APIVersion) return getResult(client, req) diff --git a/govcd/upload.go b/govcd/upload.go index d5cb5aa3f..d937b551c 100644 --- a/govcd/upload.go +++ b/govcd/upload.go @@ -119,7 +119,7 @@ func newFileUploadRequest(client *Client, requestUrl string, filePart []byte, of return nil, fmt.Errorf("error decoding vdc response: %s", err) } - uploadReq := client.NewRequestWitNotEncodedParams(nil, nil, http.MethodPut, *parsedRequestURL, bytes.NewReader(filePart), client.APIVersion) + uploadReq := client.NewRequestWitNotEncodedParams(nil, nil, http.MethodPut, *parsedRequestURL, bytes.NewReader(filePart)) uploadReq.ContentLength = filePartSize uploadReq.Header.Set("Content-Length", strconv.FormatInt(uploadReq.ContentLength, 10)) diff --git a/util/logging.go b/util/logging.go index 06f5a3a45..b454456a1 100644 --- a/util/logging.go +++ b/util/logging.go @@ -357,7 +357,7 @@ func FuncNameCallStack() string { fpcs := make([]uintptr, 10) runtime.Callers(0, fpcs) // Removes the function names from the reflect stack itself and the ones from the API management - removeReflect := regexp.MustCompile(`^ runtime.call|reflect.Value|\bNewRequest\b|NewRequestWitNotEncodedParams|ExecuteRequest|ExecuteRequestWithoutResponse|ExecuteTaskRequest`) + removeReflect := regexp.MustCompile(`^ runtime.call|reflect.Value|\bNewRequest\b|NewRequestWitNotEncodedParamsWithApiVersion|NewRequestWitNotEncodedParams|ExecuteRequest|ExecuteRequestWithoutResponse|ExecuteTaskRequest`) var stackStr []string // Gets up to 10 functions from the stack for N := 0; N < len(fpcs) && N < 10; N++ { From 5fd40cb4a2190765c37ca92951d965f86a7fd030 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 9 Dec 2019 12:52:21 +0200 Subject: [PATCH 03/10] Improve docs Signed-off-by: Vaidotas Bauzys --- govcd/api.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/govcd/api.go b/govcd/api.go index 50cba14ff..a8fd7a722 100644 --- a/govcd/api.go +++ b/govcd/api.go @@ -127,12 +127,13 @@ func ContainsNotFound(err error) bool { return err != nil && strings.Contains(err.Error(), ErrorEntityNotFound.Error()) } -// NewRequestWitNotEncodedParamsWithApi allows passing complex values params that shouldn't be encoded like for queries. e.g. /query?filter=name=foo +// NewRequestWitNotEncodedParams allows passing complex values params that 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 { return cli.NewRequestWitNotEncodedParamsWithApiVersion(params, notEncodedParams, method, reqUrl, body, cli.APIVersion) } // NewRequestWitNotEncodedParamsWithApiVersion allows passing complex values params that shouldn't be encoded like for queries. e.g. /query?filter=name=foo +// Passed Api version allows to you override default values used in request parameters. func (cli *Client) NewRequestWitNotEncodedParamsWithApiVersion(params map[string]string, notEncodedParams map[string]string, method string, reqUrl url.URL, body io.Reader, apiVersion string) *http.Request { reqValues := url.Values{} From a4a3f60a666b9c3520e213dec310fb71cc19e6df Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 9 Dec 2019 13:16:40 +0200 Subject: [PATCH 04/10] Add changelog Signed-off-by: Vaidotas Bauzys --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cab3e7360..7e41715de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,10 @@ * Added IP set handling functions `CreateNsxvIpSet`, `UpdateNsxvIpSet`, `GetNsxvIpSetByName`, `GetNsxvIpSetById`, `GetNsxvIpSetByNameOrId`, `GetAllNsxvIpSets`, `DeleteNsxvIpSetById`, `DeleteNsxvIpSetByName` [#269](https://github.com/vmware/go-vcloud-director/pull/269) +* Added methods new methods which allows override API versions `NewRequestWitNotEncodedParamsWithApiVersion`, + `ExecuteTaskRequestWithApiVersion`, `ExecuteRequestWithoutResponseWithApiVersion`, + `ExecuteRequestWithApiVersion` [#274](https://github.com/vmware/go-vcloud-director/pull/274) +* Moved `VCDClient.supportedVersions` to `VCDClient.Client.supportedVersions` [#274](https://github.com/vmware/go-vcloud-director/pull/274) BUGS FIXED: * Remove parentheses from filtering since they weren't treated correctly in some environment [#256] From 398d99037e2351ac06580a607e1c3569789cd9fb Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 9 Dec 2019 13:22:25 +0200 Subject: [PATCH 05/10] Improve docs and changelog Signed-off-by: Vaidotas Bauzys --- CHANGELOG.md | 2 +- govcd/api.go | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e41715de..af89e1509 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,7 @@ * Added IP set handling functions `CreateNsxvIpSet`, `UpdateNsxvIpSet`, `GetNsxvIpSetByName`, `GetNsxvIpSetById`, `GetNsxvIpSetByNameOrId`, `GetAllNsxvIpSets`, `DeleteNsxvIpSetById`, `DeleteNsxvIpSetByName` [#269](https://github.com/vmware/go-vcloud-director/pull/269) -* Added methods new methods which allows override API versions `NewRequestWitNotEncodedParamsWithApiVersion`, +* Added methods which allow override API versions `NewRequestWitNotEncodedParamsWithApiVersion`, `ExecuteTaskRequestWithApiVersion`, `ExecuteRequestWithoutResponseWithApiVersion`, `ExecuteRequestWithApiVersion` [#274](https://github.com/vmware/go-vcloud-director/pull/274) * Moved `VCDClient.supportedVersions` to `VCDClient.Client.supportedVersions` [#274](https://github.com/vmware/go-vcloud-director/pull/274) diff --git a/govcd/api.go b/govcd/api.go index a8fd7a722..31d99ddb0 100644 --- a/govcd/api.go +++ b/govcd/api.go @@ -133,7 +133,12 @@ func (cli *Client) NewRequestWitNotEncodedParams(params map[string]string, notEn } // NewRequestWitNotEncodedParamsWithApiVersion allows passing complex values params that shouldn't be encoded like for queries. e.g. /query?filter=name=foo -// Passed Api version allows to you override default values used in request parameters. +// * params - request parameters +// * notEncodedParams - request parameters which will be added not encoded +// * method - request type +// * reqUrl - request url +// * body - request body +// * apiVersion - provided Api version overrides default Api version value used in request parameter func (cli *Client) NewRequestWitNotEncodedParamsWithApiVersion(params map[string]string, notEncodedParams map[string]string, method string, reqUrl url.URL, body io.Reader, apiVersion string) *http.Request { reqValues := url.Values{} From 1ae3d74b7343876c4492ca9dfbc5828242a528f9 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 9 Dec 2019 13:41:50 +0200 Subject: [PATCH 06/10] Improve docs Signed-off-by: Vaidotas Bauzys --- govcd/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/govcd/api.go b/govcd/api.go index 31d99ddb0..6ecb29dc1 100644 --- a/govcd/api.go +++ b/govcd/api.go @@ -201,7 +201,7 @@ func (cli *Client) NewRequest(params map[string]string, method string, reqUrl ur return cli.NewRequestWitNotEncodedParams(params, nil, method, reqUrl, body) } -// NewRequest creates a new HTTP request and applies necessary auth headers if set. +// NewRequestWithApiVersion creates a new HTTP request and applies necessary auth headers if set. // Allows to override default request API Version func (cli *Client) NewRequestWithApiVersion(params map[string]string, method string, reqUrl url.URL, body io.Reader, apiVersion string) *http.Request { return cli.NewRequestWitNotEncodedParamsWithApiVersion(params, nil, method, reqUrl, body, apiVersion) From 4b22e8b412d6dc373293979f8b5168b9b75ca80c Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 9 Dec 2019 16:22:04 +0200 Subject: [PATCH 07/10] Add test Signed-off-by: Vaidotas Bauzys --- govcd/api_test.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/govcd/api_test.go b/govcd/api_test.go index e84de1c82..857bf4528 100644 --- a/govcd/api_test.go +++ b/govcd/api_test.go @@ -8,8 +8,11 @@ package govcd import ( "fmt" + "net/http" "os" "testing" + + . "gopkg.in/check.v1" ) var testingTags = make(map[string]string) @@ -95,3 +98,23 @@ func TestTags(t *testing.T) { showTags() } } + +// Test_NewRequestWitNotEncodedParamsWithApiVersion verifies that api version override works +func (vcd *TestVCD) Test_NewRequestWitNotEncodedParamsWithApiVersion(check *C) { + fmt.Printf("Running: %s\n", check.TestName()) + queryUlr := vcd.client.Client.VCDHREF + queryUlr.Path += "/query" + + apiVersion, err := vcd.client.Client.maxSupportedVersion() + check.Assert(err, IsNil) + + req := vcd.client.Client.NewRequestWitNotEncodedParamsWithApiVersion(nil, map[string]string{"type": "media", + "filter": "name==any"}, http.MethodGet, queryUlr, nil, apiVersion) + + resp, err := checkResp(vcd.client.Client.Http.Do(req)) + check.Assert(err, IsNil) + + check.Assert(resp.Header.Get("Content-Type"), Equals, "application/vnd.vmware.vcloud.query.records+xml;version="+apiVersion) + + fmt.Printf("Test: %s run with api Version: %s\n", check.TestName(), apiVersion) +} From 448b61fae90fcfa8ce4d648ca5ef5923f2048563 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 9 Dec 2019 16:42:48 +0200 Subject: [PATCH 08/10] Add directive Signed-off-by: Vaidotas Bauzys --- govcd/api_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/govcd/api_test.go b/govcd/api_test.go index 857bf4528..fec20d2c2 100644 --- a/govcd/api_test.go +++ b/govcd/api_test.go @@ -1,3 +1,5 @@ +// +build api functional ALL + /* * Copyright 2018 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. */ From dd83fc409d91e5c152ded0e640f8bb8401cf5330 Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 16 Dec 2019 11:58:38 +0200 Subject: [PATCH 09/10] Improved test Signed-off-by: Vaidotas Bauzys --- govcd/api_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/govcd/api_test.go b/govcd/api_test.go index fec20d2c2..afe18298e 100644 --- a/govcd/api_test.go +++ b/govcd/api_test.go @@ -118,5 +118,15 @@ func (vcd *TestVCD) Test_NewRequestWitNotEncodedParamsWithApiVersion(check *C) { check.Assert(resp.Header.Get("Content-Type"), Equals, "application/vnd.vmware.vcloud.query.records+xml;version="+apiVersion) + // Repeats the call without API version change + req = vcd.client.Client.NewRequestWitNotEncodedParams(nil, map[string]string{"type": "media", + "filter": "name==any"}, http.MethodGet, queryUlr, nil) + + resp, err = checkResp(vcd.client.Client.Http.Do(req)) + check.Assert(err, IsNil) + + // Checks that the regularAPI version was not affected by the previous call + check.Assert(resp.Header.Get("Content-Type"), Equals, "application/vnd.vmware.vcloud.query.records+xml;version="+vcd.client.Client.APIVersion) + fmt.Printf("Test: %s run with api Version: %s\n", check.TestName(), apiVersion) } From fdedb017e7c8f52aca6c349300a476267969b8af Mon Sep 17 00:00:00 2001 From: Vaidotas Bauzys Date: Mon, 16 Dec 2019 12:07:14 +0200 Subject: [PATCH 10/10] Fixes Signed-off-by: Vaidotas Bauzys --- govcd/system_test.go | 4 ++-- govcd/vm_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/govcd/system_test.go b/govcd/system_test.go index c60a9a1f2..c5516b771 100644 --- a/govcd/system_test.go +++ b/govcd/system_test.go @@ -269,7 +269,7 @@ func (vcd *TestVCD) Test_CreateDeleteEdgeGatewayAdvanced(check *C) { // vCD 9.0 does not support FIPS Mode and fails if XML tag is sent therefore // field value must be sent only if user specified its value - if vcd.client.APIVCDMaxVersionIs("> 29.0") { // Newer than vCD 9.0 + if vcd.client.Client.APIVCDMaxVersionIs("> 29.0") { // Newer than vCD 9.0 edgeGatewayConfig.Configuration.FipsModeEnabled = takeBoolPointer(false) } else { if testVerbose { @@ -341,7 +341,7 @@ func (vcd *TestVCD) Test_CreateDeleteEdgeGatewayAdvanced(check *C) { edge.EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface[0].Network.HREF // TODO get rid of this when vCD 9.0 does not return network ID - if vcd.client.APIVCDMaxVersionIs("= 29.0") { + if vcd.client.Client.APIVCDMaxVersionIs("= 29.0") { edgeGatewayConfig.Configuration.GatewayInterfaces.GatewayInterface[0].Network.ID = "" } diff --git a/govcd/vm_test.go b/govcd/vm_test.go index 4816424c2..65faee919 100644 --- a/govcd/vm_test.go +++ b/govcd/vm_test.go @@ -884,7 +884,7 @@ func (vcd *TestVCD) Test_PowerOnAndForceCustomization(check *C) { // Ensure that VM has the status set to "GC_PENDING" after forced re-customization recustomizedVmStatus, err := vm.GetGuestCustomizationStatus() check.Assert(err, IsNil) - if vcd.client.APIVCDMaxVersionIs("> 29.0") { // vCD 9.0 reports types.GuestCustStatusComplete + if vcd.client.Client.APIVCDMaxVersionIs("> 29.0") { // vCD 9.0 reports types.GuestCustStatusComplete check.Assert(recustomizedVmStatus, Equals, types.GuestCustStatusPending) } // Check that VM is deployed