pax_global_header00006660000000000000000000000064133500536640014517gustar00rootroot0000000000000052 comment=45f1c769265325fa6edae517f41dda5dcd34b325 golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/000077500000000000000000000000001335005366400235545ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/.github/000077500000000000000000000000001335005366400251145ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/.github/CONTRIBUTING.md000066400000000000000000000207261335005366400273540ustar00rootroot00000000000000# Contributing to Gophercloud - [New Contributor Tutorial](#new-contributor-tutorial) - [3 ways to get involved](#3-ways-to-get-involved) - [Getting started](#getting-started) - [Tests](#tests) - [Style guide](#basic-style-guide) ## New Contributor Tutorial For new contributors, we've put together a detailed tutorial [here](https://github.com/gophercloud/gophercloud/tree/master/docs/contributor-tutorial)! ## 3 ways to get involved There are three main ways you can get involved in our open-source project, and each is described briefly below. ### 1. Fixing bugs If you want to start fixing open bugs, we'd really appreciate that! Bug fixing is central to any project. The best way to get started is by heading to our [bug tracker](https://github.com/gophercloud/gophercloud/issues) and finding open bugs that you think nobody is working on. It might be useful to comment on the thread to see the current state of the issue and if anybody has made any breakthroughs on it so far. ### 2. Improving documentation Gophercloud's documentation is automatically generated from the source code and can be read online at [godoc.org](https://godoc.org/github.com/gophercloud/gophercloud). If you feel that a certain section could be improved - whether it's to clarify ambiguity, correct a technical mistake, or to fix a grammatical error - please feel entitled to do so! We welcome doc pull requests with the same childlike enthusiasm as any other contribution! ### 3. Working on a new feature If you've found something we've left out, we'd love for you to add it! Please first open an issue to indicate your interest to a core contributor - this enables quick/early feedback and can help steer you in the right direction by avoiding known issues. It might also help you avoid losing time implementing something that might not ever work or is outside the scope of the project. While you're implementing the feature, one tip is to prefix your Pull Request title with `[wip]` - then people know it's a work in progress. Once the PR is ready for review, you can remove the `[wip]` tag and request a review. We ask that you do not submit a feature that you have not spent time researching and testing first-hand in an actual OpenStack environment. While we appreciate the contribution, submitting code which you are unfamiliar with is a risk to the users who will ultimately use it. See our [acceptance tests readme](/acceptance) for information about how you can create a local development environment to better understand the feature you're working on. Please do not hesitate to ask questions or request clarification. Your contribution is very much appreciated and we are happy to work with you to get it merged. ## Getting Started As a contributor you will need to setup your workspace in a slightly different way than just downloading it. Here are the basic instructions: 1. Configure your `$GOPATH` and run `go get` as described in the main [README](/README.md#how-to-install) but add `-tags "fixtures acceptance"` to get dependencies for unit and acceptance tests. ```bash go get -tags "fixtures acceptance" github.com/gophercloud/gophercloud ``` 2. Move into the directory that houses your local repository: ```bash cd ${GOPATH}/src/github.com/gophercloud/gophercloud ``` 3. Fork the `gophercloud/gophercloud` repository and update your remote refs. You will need to rename the `origin` remote branch to `upstream`, and add your fork as `origin` instead: ```bash git remote rename origin upstream git remote add origin git@github.com:/gophercloud.git ``` 4. Checkout the latest development branch: ```bash git checkout master ``` 5. If you're working on something (discussed more in detail below), you will need to checkout a new feature branch: ```bash git checkout -b my-new-feature ``` 6. Use a standard text editor or IDE of your choice to make your changes to the code or documentation. Once finished, commit them. ```bash git status git add path/to/changed/file.go git commit ``` 7. Submit your branch as a [Pull Request](https://help.github.com/articles/creating-a-pull-request/). When submitting a Pull Request, please follow our [Style Guide](https://github.com/gophercloud/gophercloud/blob/master/docs/STYLEGUIDE.md). > Further information about using Git can be found [here](https://git-scm.com/book/en/v2). Happy Hacking! ## Tests When working on a new or existing feature, testing will be the backbone of your work since it helps uncover and prevent regressions in the codebase. There are two types of test we use in Gophercloud: unit tests and acceptance tests, which are both described below. ### Unit tests Unit tests are the fine-grained tests that establish and ensure the behavior of individual units of functionality. We usually test on an operation-by-operation basis (an operation typically being an API action) with the use of mocking to set up explicit expectations. Each operation will set up its HTTP response expectation, and then test how the system responds when fed this controlled, pre-determined input. To make life easier, we've introduced a bunch of test helpers to simplify the process of testing expectations with assertions: ```go import ( "testing" "github.com/gophercloud/gophercloud/testhelper" ) func TestSomething(t *testing.T) { result, err := Operation() testhelper.AssertEquals(t, "foo", result.Bar) testhelper.AssertNoErr(t, err) } func TestSomethingElse(t *testing.T) { testhelper.CheckEquals(t, "expected", "actual") } ``` `AssertEquals` and `AssertNoErr` will throw a fatal error if a value does not match an expected value or if an error has been declared, respectively. You can also use `CheckEquals` and `CheckNoErr` for the same purpose; the only difference being that `t.Errorf` is raised rather than `t.Fatalf`. Here is a truncated example of mocked HTTP responses: ```go import ( "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" ) func TestGet(t *testing.T) { // Setup the HTTP request multiplexer and server th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { // Test we're using the correct HTTP method th.TestMethod(t, r, "GET") // Test we're setting the auth token th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) // Set the appropriate headers for our mocked response w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) // Set the HTTP body fmt.Fprintf(w, ` { "network": { "status": "ACTIVE", "name": "private-network", "admin_state_up": true, "tenant_id": "4fd44f30292945e481c7b8a0c8908869", "shared": true, "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22" } } `) }) // Call our API operation network, err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() // Assert no errors and equality th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "ACTIVE") } ``` ### Acceptance tests As we've already mentioned, unit tests have a very narrow and confined focus - they test small units of behavior. Acceptance tests on the other hand have a far larger scope: they are fully functional tests that test the entire API of a service in one fell swoop. They don't care about unit isolation or mocking expectations, they instead do a full run-through and consequently test how the entire system _integrates_ together. When an API satisfies expectations, it proves by default that the requirements for a contract have been met. Please be aware that acceptance tests will hit a live API - and may incur service charges from your provider. Although most tests handle their own teardown procedures, it is always worth manually checking that resources are deleted after the test suite finishes. We provide detailed information about how to set up local acceptance test environments in our [acceptance tests readme](/acceptance). ### Running tests To run all tests: ```bash go test -tags fixtures ./... ``` To run all tests with verbose output: ```bash go test -v -tags fixtures ./... ``` To run tests that match certain [build tags](): ```bash go test -tags "fixtures foo bar" ./... ``` To run tests for a particular sub-package: ```bash cd ./path/to/package && go test -tags fixtures ./... ``` ## Style guide See [here](/docs/STYLEGUIDE.md) golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/.github/ISSUE_TEMPLATE000066400000000000000000000002201335005366400272140ustar00rootroot00000000000000Before starting a PR, please read our [contributor tutorial](https://github.com/gophercloud/gophercloud/tree/master/docs/contributor-tutorial). golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/.github/PULL_REQUEST_TEMPLATE000066400000000000000000000007071335005366400303220ustar00rootroot00000000000000Prior to starting a PR, please make sure you have read our [contributor tutorial](https://github.com/gophercloud/gophercloud/tree/master/docs/contributor-tutorial). Prior to a PR being reviewed, there needs to be a Github issue that the PR addresses. Replace the brackets and text below with that issue number. For #[PUT ISSUE NUMBER HERE] Links to the line numbers/files in the OpenStack source code that support the code in this PR: [PUT URLS HERE] golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/.gitignore000066400000000000000000000000271335005366400255430ustar00rootroot00000000000000**/*.swp .idea .vscode golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/.travis.yml000066400000000000000000000021461335005366400256700ustar00rootroot00000000000000language: go sudo: false install: - go get golang.org/x/crypto/ssh - go get -v -tags 'fixtures acceptance' ./... - go get github.com/wadey/gocovmerge - go get github.com/mattn/goveralls - go get golang.org/x/tools/cmd/goimports go: - "1.10" - "tip" env: global: - secure: "xSQsAG5wlL9emjbCdxzz/hYQsSpJ/bABO1kkbwMSISVcJ3Nk0u4ywF+LS4bgeOnwPfmFvNTOqVDu3RwEvMeWXSI76t1piCPcObutb2faKLVD/hLoAS76gYX+Z8yGWGHrSB7Do5vTPj1ERe2UljdrnsSeOXzoDwFxYRaZLX4bBOB4AyoGvRniil5QXPATiA1tsWX1VMicj8a4F8X+xeESzjt1Q5Iy31e7vkptu71bhvXCaoo5QhYwT+pLR9dN0S1b7Ro0KVvkRefmr1lUOSYd2e74h6Lc34tC1h3uYZCS4h47t7v5cOXvMNxinEj2C51RvbjvZI1RLVdkuAEJD1Iz4+Ote46nXbZ//6XRZMZz/YxQ13l7ux1PFjgEB6HAapmF5Xd8PRsgeTU9LRJxpiTJ3P5QJ3leS1va8qnziM5kYipj/Rn+V8g2ad/rgkRox9LSiR9VYZD2Pe45YCb1mTKSl2aIJnV7nkOqsShY5LNB4JZSg7xIffA+9YVDktw8dJlATjZqt7WvJJ49g6A61mIUV4C15q2JPGKTkZzDiG81NtmS7hFa7k0yaE2ELgYocbcuyUcAahhxntYTC0i23nJmEHVNiZmBO3u7EgpWe4KGVfumU+lt12tIn5b3dZRBBUk3QakKKozSK1QPHGpk/AZGrhu7H6l8to6IICKWtDcyMPQ=" before_script: - go vet ./... script: - ./script/coverage - ./script/format after_success: - $HOME/gopath/bin/goveralls -service=travis-ci -coverprofile=cover.out golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/.zuul.yaml000066400000000000000000000041761335005366400255250ustar00rootroot00000000000000- job: name: gophercloud-unittest parent: golang-test description: | Run gophercloud unit test run: .zuul/playbooks/gophercloud-unittest/run.yaml nodeset: ubuntu-xenial-ut - job: name: gophercloud-acceptance-test parent: golang-test description: | Run gophercloud acceptance test on master branch run: .zuul/playbooks/gophercloud-acceptance-test/run.yaml - job: name: gophercloud-acceptance-test-queens parent: gophercloud-acceptance-test description: | Run gophercloud acceptance test on queens branch vars: global_env: OS_BRANCH: stable/queens - job: name: gophercloud-acceptance-test-pike parent: gophercloud-acceptance-test description: | Run gophercloud acceptance test on pike branch vars: global_env: OS_BRANCH: stable/pike - job: name: gophercloud-acceptance-test-ocata parent: gophercloud-acceptance-test description: | Run gophercloud acceptance test on ocata branch vars: global_env: OS_BRANCH: stable/ocata - job: name: gophercloud-acceptance-test-newton parent: gophercloud-acceptance-test description: | Run gophercloud acceptance test on newton branch vars: global_env: OS_BRANCH: stable/newton - job: name: gophercloud-acceptance-test-mitaka parent: gophercloud-acceptance-test description: | Run gophercloud acceptance test on mitaka branch vars: global_env: OS_BRANCH: stable/mitaka nodeset: ubuntu-trusty - project: name: gophercloud/gophercloud check: jobs: - gophercloud-unittest - gophercloud-acceptance-test recheck-mitaka: jobs: - gophercloud-acceptance-test-mitaka recheck-newton: jobs: - gophercloud-acceptance-test-newton recheck-ocata: jobs: - gophercloud-acceptance-test-ocata recheck-pike: jobs: - gophercloud-acceptance-test-pike recheck-queens: jobs: - gophercloud-acceptance-test-queens periodic: jobs: - gophercloud-unittest - gophercloud-acceptance-test golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/.zuul/000077500000000000000000000000001335005366400246315ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/.zuul/playbooks/000077500000000000000000000000001335005366400266345ustar00rootroot00000000000000gophercloud-acceptance-test/000077500000000000000000000000001335005366400341315ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/.zuul/playbooksrun.yaml000066400000000000000000000012171335005366400356220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/.zuul/playbooks/gophercloud-acceptance-test- hosts: all become: yes roles: - config-golang - clone-devstack-gate-to-workspace - role: create-devstack-local-conf enable_services: - 'manila' - 'designate' - 'zun' - 'octavia' - install-devstack tasks: - name: Run acceptance tests with gophercloud shell: cmd: | set -e set -o pipefail set -x echo $(export |grep OS_BRANCH) go get ./... || true ./script/acceptancetest -v 2>&1 | tee $TEST_RESULTS_TXT executable: /bin/bash chdir: '{{ zuul.project.src_dir }}' environment: '{{ global_env }}' golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/.zuul/playbooks/gophercloud-unittest/000077500000000000000000000000001335005366400330245ustar00rootroot00000000000000run.yaml000066400000000000000000000006201335005366400344330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/.zuul/playbooks/gophercloud-unittest- hosts: all become: yes roles: - config-golang tasks: - name: Run unit tests with gophercloud shell: cmd: | set -e set -o pipefail set -x go get ./... || true ./script/unittest -v 2>&1 | tee $TEST_RESULTS_TXT executable: /bin/bash chdir: '{{ zuul.project.src_dir }}' environment: '{{ global_env }}' golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/CHANGELOG.md000066400000000000000000000000001335005366400253530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/LICENSE000066400000000000000000000247731335005366400245760ustar00rootroot00000000000000Copyright 2012-2013 Rackspace, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ------ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/README.md000066400000000000000000000126571335005366400250460ustar00rootroot00000000000000# Gophercloud: an OpenStack SDK for Go [![Build Status](https://travis-ci.org/gophercloud/gophercloud.svg?branch=master)](https://travis-ci.org/gophercloud/gophercloud) [![Coverage Status](https://coveralls.io/repos/github/gophercloud/gophercloud/badge.svg?branch=master)](https://coveralls.io/github/gophercloud/gophercloud?branch=master) Gophercloud is an OpenStack Go SDK. ## Useful links * [Reference documentation](http://godoc.org/github.com/gophercloud/gophercloud) * [Effective Go](https://golang.org/doc/effective_go.html) ## How to install Before installing, you need to ensure that your [GOPATH environment variable](https://golang.org/doc/code.html#GOPATH) is pointing to an appropriate directory where you want to install Gophercloud: ```bash mkdir $HOME/go export GOPATH=$HOME/go ``` To protect yourself against changes in your dependencies, we highly recommend choosing a [dependency management solution](https://github.com/golang/go/wiki/PackageManagementTools) for your projects, such as [godep](https://github.com/tools/godep). Once this is set up, you can install Gophercloud as a dependency like so: ```bash go get github.com/gophercloud/gophercloud # Edit your code to import relevant packages from "github.com/gophercloud/gophercloud" godep save ./... ``` This will install all the source files you need into a `Godeps/_workspace` directory, which is referenceable from your own source files when you use the `godep go` command. ## Getting started ### Credentials Because you'll be hitting an API, you will need to retrieve your OpenStack credentials and either store them as environment variables or in your local Go files. The first method is recommended because it decouples credential information from source code, allowing you to push the latter to your version control system without any security risk. You will need to retrieve the following: * username * password * a valid Keystone identity URL For users that have the OpenStack dashboard installed, there's a shortcut. If you visit the `project/access_and_security` path in Horizon and click on the "Download OpenStack RC File" button at the top right hand corner, you will download a bash file that exports all of your access details to environment variables. To execute the file, run `source admin-openrc.sh` and you will be prompted for your password. ### Authentication Once you have access to your credentials, you can begin plugging them into Gophercloud. The next step is authentication, and this is handled by a base "Provider" struct. To get one, you can either pass in your credentials explicitly, or tell Gophercloud to use environment variables: ```go import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/utils" ) // Option 1: Pass in the values yourself opts := gophercloud.AuthOptions{ IdentityEndpoint: "https://openstack.example.com:5000/v2.0", Username: "{username}", Password: "{password}", } // Option 2: Use a utility function to retrieve all your environment variables opts, err := openstack.AuthOptionsFromEnv() ``` Once you have the `opts` variable, you can pass it in and get back a `ProviderClient` struct: ```go provider, err := openstack.AuthenticatedClient(opts) ``` The `ProviderClient` is the top-level client that all of your OpenStack services derive from. The provider contains all of the authentication details that allow your Go code to access the API - such as the base URL and token ID. ### Provision a server Once we have a base Provider, we inject it as a dependency into each OpenStack service. In order to work with the Compute API, we need a Compute service client; which can be created like so: ```go client, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) ``` We then use this `client` for any Compute API operation we want. In our case, we want to provision a new server - so we invoke the `Create` method and pass in the flavor ID (hardware specification) and image ID (operating system) we're interested in: ```go import "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" server, err := servers.Create(client, servers.CreateOpts{ Name: "My new server!", FlavorRef: "flavor_id", ImageRef: "image_id", }).Extract() ``` The above code sample creates a new server with the parameters, and embodies the new resource in the `server` variable (a [`servers.Server`](http://godoc.org/github.com/gophercloud/gophercloud) struct). ## Advanced Usage Have a look at the [FAQ](./docs/FAQ.md) for some tips on customizing the way Gophercloud works. ## Backwards-Compatibility Guarantees None. Vendor it and write tests covering the parts you use. ## Contributing See the [contributing guide](./.github/CONTRIBUTING.md). ## Help and feedback If you're struggling with something or have spotted a potential bug, feel free to submit an issue to our [bug tracker](https://github.com/gophercloud/gophercloud/issues). ## Thank You We'd like to extend special thanks and appreciation to the following: ### OpenLab OpenLab is providing a full CI environment to test each PR and merge for a variety of OpenStack releases. ### VEXXHOST VEXXHOST is providing their services to assist with the development and testing of Gophercloud. golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/000077500000000000000000000000001335005366400256425ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/README.md000066400000000000000000000075541335005366400271340ustar00rootroot00000000000000# Gophercloud Acceptance tests The purpose of these acceptance tests is to validate that SDK features meet the requirements of a contract - to consumers, other parts of the library, and to a remote API. > **Note:** Because every test will be run against a real API endpoint, you > may incur bandwidth and service charges for all the resource usage. These > tests *should* remove their remote products automatically. However, there may > be certain cases where this does not happen; always double-check to make sure > you have no stragglers left behind. ### Step 1. Creating a Testing Environment Running tests on an existing OpenStack cloud can be risky. Malformed tests, especially ones which require Admin privileges, can cause damage to the environment. Additionally, you may incur bandwidth and service charges for the resources used, as mentioned in the note above. Therefore, it is usually best to first practice running acceptance tests in an isolated test environment. Two options to easily create a testing environment are [DevStack](https://docs.openstack.org/devstack/latest/) and [PackStack](https://www.rdoproject.org/install/packstack/). The following blog posts detail how to create reusable PackStack environments. These posts were written with Gophercloud in mind: * http://terrarum.net/blog/building-openstack-environments.html * http://terrarum.net/blog/building-openstack-environments-2.html * http://terrarum.net/blog/building-openstack-environments-3.html ### Step 2. Set environment variables A lot of tests rely on environment variables for configuration - so you will need to set them before running the suite. If you're testing against pure OpenStack APIs, you can download a file that contains all of these variables for you: just visit the `project/access_and_security` page in your control panel and click the "Download OpenStack RC File" button at the top right. For all other providers, you will need to set them manually. #### Authentication |Name|Description| |---|---| |`OS_USERNAME`|Your API username| |`OS_PASSWORD`|Your API password| |`OS_AUTH_URL`|The identity URL you need to authenticate| |`OS_TENANT_NAME`|Your API tenant name| |`OS_TENANT_ID`|Your API tenant ID| #### General |Name|Description| |---|---| |`OS_REGION_NAME`|The region you want your resources to reside in| #### Compute |Name|Description| |---|---| |`OS_IMAGE_ID`|The ID of the image your want your server to be based on| |`OS_FLAVOR_ID`|The ID of the flavor you want your server to be based on| |`OS_FLAVOR_ID_RESIZE`|The ID of the flavor you want your server to be resized to| |`OS_POOL_NAME`|The Pool from where to obtain Floating IPs| |`OS_NETWORK_NAME`|The internal/private network to launch instances on| |`OS_EXTGW_ID`|The external/public network| #### Database |Name|Description| |---|---| |`OS_DB_DATASTORE_TYPE`|The Datastore type to use. Example: `mariadb`| |`OS_DB_DATASTORE_VERSION`|The Datastore version to use. Example: `mariadb-10`| #### Shared file systems |Name|Description| |---|---| |`OS_SHARE_NETWORK_ID`| The share network ID to use when creating shares| ### 3. Run the test suite From the root directory, run: ``` ./script/acceptancetest ``` Alternatively, add the following to your `.bashrc`: ```bash gophercloudtest() { if [[ -n $1 ]] && [[ -n $2 ]]; then pushd $GOPATH/src/github.com/gophercloud/gophercloud go test -v -tags "fixtures acceptance" -run "$1" github.com/gophercloud/gophercloud/acceptance/openstack/$2 | tee ~/gophercloud.log popd fi } ``` Then run either groups or individual tests by doing: ```shell $ gophercloudtest TestFlavorsList compute/v2 $ gophercloudtest TestFlavors compute/v2 $ gophercloudtest Test compute/v2 ``` ### 4. Notes #### Compute Tests * In order to run the `TestBootFromVolumeMultiEphemeral` test, a flavor with ephemeral disk space must be used. * The `TestDefSecRules` tests require a compatible network driver and admin privileges. golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/clients/000077500000000000000000000000001335005366400273035ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/clients/clients.go000066400000000000000000000443621335005366400313040ustar00rootroot00000000000000// Package clients contains functions for creating OpenStack service clients // for use in acceptance tests. It also manages the required environment // variables to run the tests. package clients import ( "fmt" "net/http" "os" "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/blockstorage/noauth" ) // AcceptanceTestChoices contains image and flavor selections for use by the acceptance tests. type AcceptanceTestChoices struct { // ImageID contains the ID of a valid image. ImageID string // FlavorID contains the ID of a valid flavor. FlavorID string // FlavorIDResize contains the ID of a different flavor available on the same OpenStack installation, that is distinct // from FlavorID. FlavorIDResize string // FloatingIPPool contains the name of the pool from where to obtain floating IPs. FloatingIPPoolName string // MagnumKeypair contains the ID of a valid key pair. MagnumKeypair string // MagnumImageID contains the ID of a valid magnum image. MagnumImageID string // NetworkName is the name of a network to launch the instance on. NetworkName string // ExternalNetworkID is the network ID of the external network. ExternalNetworkID string // ShareNetworkID is the Manila Share network ID ShareNetworkID string // DBDatastoreType is the datastore type for DB tests. DBDatastoreType string // DBDatastoreTypeID is the datastore type version for DB tests. DBDatastoreVersion string } // AcceptanceTestChoicesFromEnv populates a ComputeChoices struct from environment variables. // If any required state is missing, an `error` will be returned that enumerates the missing properties. func AcceptanceTestChoicesFromEnv() (*AcceptanceTestChoices, error) { imageID := os.Getenv("OS_IMAGE_ID") flavorID := os.Getenv("OS_FLAVOR_ID") flavorIDResize := os.Getenv("OS_FLAVOR_ID_RESIZE") magnumImageID := os.Getenv("OS_MAGNUM_IMAGE_ID") magnumKeypair := os.Getenv("OS_MAGNUM_KEYPAIR") networkName := os.Getenv("OS_NETWORK_NAME") floatingIPPoolName := os.Getenv("OS_POOL_NAME") externalNetworkID := os.Getenv("OS_EXTGW_ID") shareNetworkID := os.Getenv("OS_SHARE_NETWORK_ID") dbDatastoreType := os.Getenv("OS_DB_DATASTORE_TYPE") dbDatastoreVersion := os.Getenv("OS_DB_DATASTORE_VERSION") missing := make([]string, 0, 3) if imageID == "" { missing = append(missing, "OS_IMAGE_ID") } if flavorID == "" { missing = append(missing, "OS_FLAVOR_ID") } if flavorIDResize == "" { missing = append(missing, "OS_FLAVOR_ID_RESIZE") } if floatingIPPoolName == "" { missing = append(missing, "OS_POOL_NAME") } if externalNetworkID == "" { missing = append(missing, "OS_EXTGW_ID") } if networkName == "" { networkName = "private" } if shareNetworkID == "" { missing = append(missing, "OS_SHARE_NETWORK_ID") } notDistinct := "" if flavorID == flavorIDResize { notDistinct = "OS_FLAVOR_ID and OS_FLAVOR_ID_RESIZE must be distinct." } if len(missing) > 0 || notDistinct != "" { text := "You're missing some important setup:\n" if len(missing) > 0 { text += " * These environment variables must be provided: " + strings.Join(missing, ", ") + "\n" } if notDistinct != "" { text += " * " + notDistinct + "\n" } return nil, fmt.Errorf(text) } return &AcceptanceTestChoices{ ImageID: imageID, FlavorID: flavorID, FlavorIDResize: flavorIDResize, FloatingIPPoolName: floatingIPPoolName, MagnumImageID: magnumImageID, MagnumKeypair: magnumKeypair, NetworkName: networkName, ExternalNetworkID: externalNetworkID, ShareNetworkID: shareNetworkID, DBDatastoreType: dbDatastoreType, DBDatastoreVersion: dbDatastoreVersion, }, nil } // NewBlockStorageV1Client returns a *ServiceClient for making calls // to the OpenStack Block Storage v1 API. An error will be returned // if authentication or client creation was not possible. func NewBlockStorageV1Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewBlockStorageV1(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } // NewBlockStorageV2Client returns a *ServiceClient for making calls // to the OpenStack Block Storage v2 API. An error will be returned // if authentication or client creation was not possible. func NewBlockStorageV2Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewBlockStorageV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } // NewBlockStorageV3Client returns a *ServiceClient for making calls // to the OpenStack Block Storage v3 API. An error will be returned // if authentication or client creation was not possible. func NewBlockStorageV3Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewBlockStorageV3(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } // NewBlockStorageV2NoAuthClient returns a noauth *ServiceClient for // making calls to the OpenStack Block Storage v2 API. An error will be // returned if client creation was not possible. func NewBlockStorageV2NoAuthClient() (*gophercloud.ServiceClient, error) { client, err := noauth.NewClient(gophercloud.AuthOptions{ Username: os.Getenv("OS_USERNAME"), TenantName: os.Getenv("OS_TENANT_NAME"), }) if err != nil { return nil, err } client = configureDebug(client) return noauth.NewBlockStorageNoAuth(client, noauth.EndpointOpts{ CinderEndpoint: os.Getenv("CINDER_ENDPOINT"), }) } // NewBlockStorageV3NoAuthClient returns a noauth *ServiceClient for // making calls to the OpenStack Block Storage v2 API. An error will be // returned if client creation was not possible. func NewBlockStorageV3NoAuthClient() (*gophercloud.ServiceClient, error) { client, err := noauth.NewClient(gophercloud.AuthOptions{ Username: os.Getenv("OS_USERNAME"), TenantName: os.Getenv("OS_TENANT_NAME"), }) if err != nil { return nil, err } client = configureDebug(client) return noauth.NewBlockStorageNoAuth(client, noauth.EndpointOpts{ CinderEndpoint: os.Getenv("CINDER_ENDPOINT"), }) } // NewComputeV2Client returns a *ServiceClient for making calls // to the OpenStack Compute v2 API. An error will be returned // if authentication or client creation was not possible. func NewComputeV2Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewComputeV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } // NewDBV1Client returns a *ServiceClient for making calls // to the OpenStack Database v1 API. An error will be returned // if authentication or client creation was not possible. func NewDBV1Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewDBV1(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } // NewDNSV2Client returns a *ServiceClient for making calls // to the OpenStack Compute v2 API. An error will be returned // if authentication or client creation was not possible. func NewDNSV2Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewDNSV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } // NewIdentityV2Client returns a *ServiceClient for making calls // to the OpenStack Identity v2 API. An error will be returned // if authentication or client creation was not possible. func NewIdentityV2Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewIdentityV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } // NewIdentityV2AdminClient returns a *ServiceClient for making calls // to the Admin Endpoint of the OpenStack Identity v2 API. An error // will be returned if authentication or client creation was not possible. func NewIdentityV2AdminClient() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewIdentityV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), Availability: gophercloud.AvailabilityAdmin, }) } // NewIdentityV2UnauthenticatedClient returns an unauthenticated *ServiceClient // for the OpenStack Identity v2 API. An error will be returned if // authentication or client creation was not possible. func NewIdentityV2UnauthenticatedClient() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.NewClient(ao.IdentityEndpoint) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewIdentityV2(client, gophercloud.EndpointOpts{}) } // NewIdentityV3Client returns a *ServiceClient for making calls // to the OpenStack Identity v3 API. An error will be returned // if authentication or client creation was not possible. func NewIdentityV3Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewIdentityV3(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } // NewIdentityV3UnauthenticatedClient returns an unauthenticated *ServiceClient // for the OpenStack Identity v3 API. An error will be returned if // authentication or client creation was not possible. func NewIdentityV3UnauthenticatedClient() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.NewClient(ao.IdentityEndpoint) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewIdentityV3(client, gophercloud.EndpointOpts{}) } // NewImageServiceV2Client returns a *ServiceClient for making calls to the // OpenStack Image v2 API. An error will be returned if authentication or // client creation was not possible. func NewImageServiceV2Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewImageServiceV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } // NewNetworkV2Client returns a *ServiceClient for making calls to the // OpenStack Networking v2 API. An error will be returned if authentication // or client creation was not possible. func NewNetworkV2Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewNetworkV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } // NewObjectStorageV1Client returns a *ServiceClient for making calls to the // OpenStack Object Storage v1 API. An error will be returned if authentication // or client creation was not possible. func NewObjectStorageV1Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewObjectStorageV1(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } // NewSharedFileSystemV2Client returns a *ServiceClient for making calls // to the OpenStack Shared File System v2 API. An error will be returned // if authentication or client creation was not possible. func NewSharedFileSystemV2Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewSharedFileSystemV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } // NewLoadBalancerV2Client returns a *ServiceClient for making calls to the // OpenStack Octavia v2 API. An error will be returned if authentication // or client creation was not possible. func NewLoadBalancerV2Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewLoadBalancerV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } // NewClusteringV1Client returns a *ServiceClient for making calls // to the OpenStack Clustering v1 API. An error will be returned // if authentication or client creation was not possible. func NewClusteringV1Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewClusteringV1(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } // NewMessagingV2Client returns a *ServiceClient for making calls // to the OpenStack Messaging (Zaqar) v2 API. An error will be returned // if authentication or client creation was not possible. func NewMessagingV2Client(clientID string) (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewMessagingV2(client, clientID, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } // NewContainerV1Client returns a *ServiceClient for making calls // to the OpenStack Container V1 API. An error will be returned // if authentication or client creation was not possible. func NewContainerV1Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } return openstack.NewContainerV1(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } // NewKeyManagerV1Client returns a *ServiceClient for making calls // to the OpenStack Key Manager (Barbican) v1 API. An error will be // returned if authentication or client creation was not possible. func NewKeyManagerV1Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewKeyManagerV1(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } // configureDebug will configure the provider client to print the API // requests and responses if OS_DEBUG is enabled. func configureDebug(client *gophercloud.ProviderClient) *gophercloud.ProviderClient { if os.Getenv("OS_DEBUG") != "" { client.HTTPClient = http.Client{ Transport: &LogRoundTripper{ Rt: &http.Transport{}, }, } } return client } // NewContainerInfraV1Client returns a *ServiceClient for making calls // to the OpenStack Container Infra Management v1 API. An error will be returned // if authentication or client creation was not possible. func NewContainerInfraV1Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewContainerInfraV1(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } // NewWorkflowV2Client returns a *ServiceClient for making calls // to the OpenStack Workflow v2 API (Mistral). An error will be returned if // authentication or client creation failed. func NewWorkflowV2Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewWorkflowV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } // NewOrchestrationV1Client returns a *ServiceClient for making calls // to the OpenStack Orchestration v1 API. An error will be returned // if authentication or client creation was not possible. func NewOrchestrationV1Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err } client, err := openstack.AuthenticatedClient(ao) if err != nil { return nil, err } client = configureDebug(client) return openstack.NewOrchestrationV1(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/clients/conditions.go000066400000000000000000000042121335005366400320020ustar00rootroot00000000000000package clients import ( "os" "testing" ) // RequireAdmin will restrict a test to only be run by admin users. func RequireAdmin(t *testing.T) { if os.Getenv("OS_USERNAME") != "admin" { t.Skip("must be admin to run this test") } } // RequireNonAdmin will restrict a test to only be run by non-admin users. func RequireNonAdmin(t *testing.T) { if os.Getenv("OS_USERNAME") == "admin" { t.Skip("must be a non-admin to run this test") } } // RequireDNS will restrict a test to only be run in environments // that support DNSaaS. func RequireDNS(t *testing.T) { if os.Getenv("OS_DNS_ENVIRONMENT") == "" { t.Skip("this test requires DNSaaS") } } // RequireGuestAgent will restrict a test to only be run in // environments that support the QEMU guest agent. func RequireGuestAgent(t *testing.T) { if os.Getenv("OS_GUEST_AGENT") == "" { t.Skip("this test requires support for qemu guest agent and to set OS_GUEST_AGENT to 1") } } // RequireIdentityV2 will restrict a test to only be run in // environments that support the Identity V2 API. func RequireIdentityV2(t *testing.T) { if os.Getenv("OS_IDENTITY_API_VERSION") != "2.0" { t.Skip("this test requires support for the identity v2 API") } } // RequireLiveMigration will restrict a test to only be run in // environments that support live migration. func RequireLiveMigration(t *testing.T) { if os.Getenv("OS_LIVE_MIGRATE") == "" { t.Skip("this test requires support for live migration and to set OS_LIVE_MIGRATE to 1") } } // RequireLong will ensure long-running tests can run. func RequireLong(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode") } } // RequireNovaNetwork will restrict a test to only be run in // environments that support nova-network. func RequireNovaNetwork(t *testing.T) { if os.Getenv("OS_NOVANET") == "" { t.Skip("this test requires nova-network and to set OS_NOVANET to 1") } } // SkipRelease will have the test be skipped on a certain // release. Releases are named such as 'stable/mitaka', master, etc. func SkipRelease(t *testing.T, release string) { if os.Getenv("OS_BRANCH") == release { t.Skipf("this is not supported in %s", release) } } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/clients/http.go000066400000000000000000000111741335005366400306150ustar00rootroot00000000000000package clients import ( "bytes" "encoding/json" "fmt" "io" "io/ioutil" "log" "net/http" "sort" "strings" ) // List of headers that need to be redacted var REDACT_HEADERS = []string{"x-auth-token", "x-auth-key", "x-service-token", "x-storage-token", "x-account-meta-temp-url-key", "x-account-meta-temp-url-key-2", "x-container-meta-temp-url-key", "x-container-meta-temp-url-key-2", "set-cookie", "x-subject-token"} // LogRoundTripper satisfies the http.RoundTripper interface and is used to // customize the default http client RoundTripper to allow logging. type LogRoundTripper struct { Rt http.RoundTripper } // RoundTrip performs a round-trip HTTP request and logs relevant information // about it. func (lrt *LogRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) { defer func() { if request.Body != nil { request.Body.Close() } }() var err error log.Printf("[DEBUG] OpenStack Request URL: %s %s", request.Method, request.URL) log.Printf("[DEBUG] OpenStack request Headers:\n%s", formatHeaders(request.Header)) if request.Body != nil { request.Body, err = lrt.logRequest(request.Body, request.Header.Get("Content-Type")) if err != nil { return nil, err } } response, err := lrt.Rt.RoundTrip(request) if response == nil { return nil, err } log.Printf("[DEBUG] OpenStack Response Code: %d", response.StatusCode) log.Printf("[DEBUG] OpenStack Response Headers:\n%s", formatHeaders(response.Header)) response.Body, err = lrt.logResponse(response.Body, response.Header.Get("Content-Type")) return response, err } // logRequest will log the HTTP Request details. // If the body is JSON, it will attempt to be pretty-formatted. func (lrt *LogRoundTripper) logRequest(original io.ReadCloser, contentType string) (io.ReadCloser, error) { defer original.Close() var bs bytes.Buffer _, err := io.Copy(&bs, original) if err != nil { return nil, err } // Handle request contentType if strings.HasPrefix(contentType, "application/json") { debugInfo := lrt.formatJSON(bs.Bytes()) log.Printf("[DEBUG] OpenStack Request Body: %s", debugInfo) } return ioutil.NopCloser(strings.NewReader(bs.String())), nil } // logResponse will log the HTTP Response details. // If the body is JSON, it will attempt to be pretty-formatted. func (lrt *LogRoundTripper) logResponse(original io.ReadCloser, contentType string) (io.ReadCloser, error) { if strings.HasPrefix(contentType, "application/json") { var bs bytes.Buffer defer original.Close() _, err := io.Copy(&bs, original) if err != nil { return nil, err } debugInfo := lrt.formatJSON(bs.Bytes()) if debugInfo != "" { log.Printf("[DEBUG] OpenStack Response Body: %s", debugInfo) } return ioutil.NopCloser(strings.NewReader(bs.String())), nil } log.Printf("[DEBUG] Not logging because OpenStack response body isn't JSON") return original, nil } // formatJSON will try to pretty-format a JSON body. // It will also mask known fields which contain sensitive information. func (lrt *LogRoundTripper) formatJSON(raw []byte) string { var data map[string]interface{} err := json.Unmarshal(raw, &data) if err != nil { log.Printf("[DEBUG] Unable to parse OpenStack JSON: %s", err) return string(raw) } // Mask known password fields if v, ok := data["auth"].(map[string]interface{}); ok { if v, ok := v["identity"].(map[string]interface{}); ok { if v, ok := v["password"].(map[string]interface{}); ok { if v, ok := v["user"].(map[string]interface{}); ok { v["password"] = "***" } } } } // Ignore the catalog if v, ok := data["token"].(map[string]interface{}); ok { if _, ok := v["catalog"]; ok { return "" } } pretty, err := json.MarshalIndent(data, "", " ") if err != nil { log.Printf("[DEBUG] Unable to re-marshal OpenStack JSON: %s", err) return string(raw) } return string(pretty) } // redactHeaders processes a headers object, returning a redacted list func redactHeaders(headers http.Header) (processedHeaders []string) { for name, header := range headers { var sensitive bool for _, redact_header := range REDACT_HEADERS { if strings.ToLower(name) == strings.ToLower(redact_header) { sensitive = true } } for _, v := range header { if sensitive { processedHeaders = append(processedHeaders, fmt.Sprintf("%v: %v", name, "***")) } else { processedHeaders = append(processedHeaders, fmt.Sprintf("%v: %v", name, v)) } } } return } // formatHeaders processes a headers object plus a deliminator, returning a string func formatHeaders(headers http.Header) string { redactedHeaders := redactHeaders(headers) sort.Strings(redactedHeaders) return strings.Join(redactedHeaders, "\n") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/000077500000000000000000000000001335005366400276315ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/000077500000000000000000000000001335005366400323105ustar00rootroot00000000000000extensions/000077500000000000000000000000001335005366400344305ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorageextensions.go000066400000000000000000000125061335005366400371620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/extensions// Package extensions contains common functions for creating block storage // resources that are extensions of the block storage API. See the `*_test.go` // files for example usages. package extensions import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" "github.com/gophercloud/gophercloud/openstack/compute/v2/images" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" ) // CreateUploadImage will upload volume it as volume-baked image. An name of new image or err will be // returned func CreateUploadImage(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (volumeactions.VolumeImage, error) { if testing.Short() { t.Skip("Skipping test that requires volume-backed image uploading in short mode.") } imageName := tools.RandomString("ACPTTEST", 16) uploadImageOpts := volumeactions.UploadImageOpts{ ImageName: imageName, Force: true, } volumeImage, err := volumeactions.UploadImage(client, volume.ID, uploadImageOpts).Extract() if err != nil { return volumeImage, err } t.Logf("Uploading volume %s as volume-backed image %s", volume.ID, imageName) if err := volumes.WaitForStatus(client, volume.ID, "available", 60); err != nil { return volumeImage, err } t.Logf("Uploaded volume %s as volume-backed image %s", volume.ID, imageName) return volumeImage, nil } // DeleteUploadedImage deletes uploaded image. An error will be returned // if the deletion request failed. func DeleteUploadedImage(t *testing.T, client *gophercloud.ServiceClient, imageName string) error { if testing.Short() { t.Skip("Skipping test that requires volume-backed image removing in short mode.") } t.Logf("Getting image id for image name %s", imageName) imageID, err := images.IDFromName(client, imageName) if err != nil { return err } t.Logf("Removing image %s", imageID) err = images.Delete(client, imageID).ExtractErr() if err != nil { return err } return nil } // CreateVolumeAttach will attach a volume to an instance. An error will be // returned if the attachment failed. func CreateVolumeAttach(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume, server *servers.Server) error { if testing.Short() { t.Skip("Skipping test that requires volume attachment in short mode.") } attachOpts := volumeactions.AttachOpts{ MountPoint: "/mnt", Mode: "rw", InstanceUUID: server.ID, } t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID) if err := volumeactions.Attach(client, volume.ID, attachOpts).ExtractErr(); err != nil { return err } if err := volumes.WaitForStatus(client, volume.ID, "in-use", 60); err != nil { return err } t.Logf("Attached volume %s to server %s", volume.ID, server.ID) return nil } // CreateVolumeReserve creates a volume reservation. An error will be returned // if the reservation failed. func CreateVolumeReserve(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error { if testing.Short() { t.Skip("Skipping test that requires volume reservation in short mode.") } t.Logf("Attempting to reserve volume %s", volume.ID) if err := volumeactions.Reserve(client, volume.ID).ExtractErr(); err != nil { return err } t.Logf("Reserved volume %s", volume.ID) return nil } // DeleteVolumeAttach will detach a volume from an instance. A fatal error will // occur if the snapshot failed to be deleted. This works best when used as a // deferred function. func DeleteVolumeAttach(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { t.Logf("Attepting to detach volume volume: %s", volume.ID) detachOpts := volumeactions.DetachOpts{ AttachmentID: volume.Attachments[0].AttachmentID, } if err := volumeactions.Detach(client, volume.ID, detachOpts).ExtractErr(); err != nil { t.Fatalf("Unable to detach volume %s: %v", volume.ID, err) } if err := volumes.WaitForStatus(client, volume.ID, "available", 60); err != nil { t.Fatalf("Volume %s failed to become unavailable in 60 seconds: %v", volume.ID, err) } t.Logf("Detached volume: %s", volume.ID) } // DeleteVolumeReserve deletes a volume reservation. A fatal error will occur // if the deletion request failed. This works best when used as a deferred // function. func DeleteVolumeReserve(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { if testing.Short() { t.Skip("Skipping test that requires volume reservation in short mode.") } t.Logf("Attempting to unreserve volume %s", volume.ID) if err := volumeactions.Unreserve(client, volume.ID).ExtractErr(); err != nil { t.Fatalf("Unable to unreserve volume %s: %v", volume.ID, err) } t.Logf("Unreserved volume %s", volume.ID) } // ExtendVolumeSize will extend the size of a volume. func ExtendVolumeSize(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error { t.Logf("Attempting to extend the size of volume %s", volume.ID) extendOpts := volumeactions.ExtendSizeOpts{ NewSize: 2, } err := volumeactions.ExtendSize(client, volume.ID, extendOpts).ExtractErr() if err != nil { return err } if err := volumes.WaitForStatus(client, volume.ID, "available", 60); err != nil { return err } return nil } pkg.go000066400000000000000000000001651335005366400355420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/extensions// The extensions package contains acceptance tests for the Openstack Cinder extensions service. package extensions schedulerstats_test.go000066400000000000000000000015411335005366400410540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/extensions// +build acceptance blockstorage package extensions import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/schedulerstats" ) func TestSchedulerStatsList(t *testing.T) { blockClient, err := clients.NewBlockStorageV2Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) } listOpts := schedulerstats.ListOpts{ Detail: true, } allPages, err := schedulerstats.List(blockClient, listOpts).AllPages() if err != nil { t.Fatalf("Unable to query schedulerstats: %v", err) } allStats, err := schedulerstats.ExtractStoragePools(allPages) if err != nil { t.Fatalf("Unable to extract pools: %v", err) } for _, stat := range allStats { tools.PrintResource(t, stat) } } services_test.go000066400000000000000000000014251335005366400376430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/extensions// +build acceptance blockstorage package extensions import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/services" ) func TestServicesList(t *testing.T) { blockClient, err := clients.NewBlockStorageV3Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) } allPages, err := services.List(blockClient, services.ListOpts{}).AllPages() if err != nil { t.Fatalf("Unable to list services: %v", err) } allServices, err := services.ExtractServices(allPages) if err != nil { t.Fatalf("Unable to extract services") } for _, service := range allServices { tools.PrintResource(t, service) } } volumeactions_test.go000066400000000000000000000113111335005366400407030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/extensions// +build acceptance blockstorage package extensions import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" blockstorage "github.com/gophercloud/gophercloud/acceptance/openstack/blockstorage/v2" compute "github.com/gophercloud/gophercloud/acceptance/openstack/compute/v2" ) func TestVolumeActionsUploadImageDestroy(t *testing.T) { blockClient, err := clients.NewBlockStorageV2Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) } computeClient, err := clients.NewComputeV2Client() if err != nil { t.Fatalf("Unable to create a compute client: %v", err) } volume, err := blockstorage.CreateVolume(t, blockClient) if err != nil { t.Fatalf("Unable to create volume: %v", err) } defer blockstorage.DeleteVolume(t, blockClient, volume) volumeImage, err := CreateUploadImage(t, blockClient, volume) if err != nil { t.Fatalf("Unable to upload volume-backed image: %v", err) } tools.PrintResource(t, volumeImage) err = DeleteUploadedImage(t, computeClient, volumeImage.ImageName) if err != nil { t.Fatalf("Unable to delete volume-backed image: %v", err) } } func TestVolumeActionsAttachCreateDestroy(t *testing.T) { blockClient, err := clients.NewBlockStorageV2Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) } computeClient, err := clients.NewComputeV2Client() if err != nil { t.Fatalf("Unable to create a compute client: %v", err) } server, err := compute.CreateServer(t, computeClient) if err != nil { t.Fatalf("Unable to create server: %v", err) } defer compute.DeleteServer(t, computeClient, server) volume, err := blockstorage.CreateVolume(t, blockClient) if err != nil { t.Fatalf("Unable to create volume: %v", err) } defer blockstorage.DeleteVolume(t, blockClient, volume) err = CreateVolumeAttach(t, blockClient, volume, server) if err != nil { t.Fatalf("Unable to attach volume: %v", err) } newVolume, err := volumes.Get(blockClient, volume.ID).Extract() if err != nil { t.Fatal("Unable to get updated volume information: %v", err) } DeleteVolumeAttach(t, blockClient, newVolume) } func TestVolumeActionsReserveUnreserve(t *testing.T) { client, err := clients.NewBlockStorageV2Client() if err != nil { t.Fatalf("Unable to create blockstorage client: %v", err) } volume, err := blockstorage.CreateVolume(t, client) if err != nil { t.Fatalf("Unable to create volume: %v", err) } defer blockstorage.DeleteVolume(t, client, volume) err = CreateVolumeReserve(t, client, volume) if err != nil { t.Fatalf("Unable to create volume reserve: %v", err) } defer DeleteVolumeReserve(t, client, volume) } func TestVolumeActionsExtendSize(t *testing.T) { blockClient, err := clients.NewBlockStorageV2Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) } volume, err := blockstorage.CreateVolume(t, blockClient) if err != nil { t.Fatalf("Unable to create volume: %v", err) } defer blockstorage.DeleteVolume(t, blockClient, volume) tools.PrintResource(t, volume) err = ExtendVolumeSize(t, blockClient, volume) if err != nil { t.Fatalf("Unable to resize volume: %v", err) } newVolume, err := volumes.Get(blockClient, volume.ID).Extract() if err != nil { t.Fatal("Unable to get updated volume information: %v", err) } tools.PrintResource(t, newVolume) } // Note(jtopjian): I plan to work on this at some point, but it requires // setting up a server with iscsi utils. /* func TestVolumeConns(t *testing.T) { client, err := newClient() th.AssertNoErr(t, err) t.Logf("Creating volume") cv, err := volumes.Create(client, &volumes.CreateOpts{ Size: 1, Name: "blockv2-volume", }).Extract() th.AssertNoErr(t, err) defer func() { err = volumes.WaitForStatus(client, cv.ID, "available", 60) th.AssertNoErr(t, err) t.Logf("Deleting volume") err = volumes.Delete(client, cv.ID).ExtractErr() th.AssertNoErr(t, err) }() err = volumes.WaitForStatus(client, cv.ID, "available", 60) th.AssertNoErr(t, err) connOpts := &volumeactions.ConnectorOpts{ IP: "127.0.0.1", Host: "stack", Initiator: "iqn.1994-05.com.redhat:17cf566367d2", Multipath: false, Platform: "x86_64", OSType: "linux2", } t.Logf("Initializing connection") _, err = volumeactions.InitializeConnection(client, cv.ID, connOpts).Extract() th.AssertNoErr(t, err) t.Logf("Terminating connection") err = volumeactions.TerminateConnection(client, cv.ID, connOpts).ExtractErr() th.AssertNoErr(t, err) } */ noauth/000077500000000000000000000000001335005366400335275ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorageblockstorage.go000066400000000000000000000102251335005366400365350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/noauth// Package noauth contains common functions for creating block storage based // resources for use in acceptance tests. See the `*_test.go` files for // example usages. package noauth import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" ) // CreateVolume will create a volume with a random name and size of 1GB. An // error will be returned if the volume was unable to be created. func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) { if testing.Short() { t.Skip("Skipping test that requires volume creation in short mode.") } volumeName := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create volume: %s", volumeName) createOpts := volumes.CreateOpts{ Size: 1, Name: volumeName, } volume, err := volumes.Create(client, createOpts).Extract() if err != nil { return volume, err } err = volumes.WaitForStatus(client, volume.ID, "available", 60) if err != nil { return volume, err } return volume, nil } // CreateVolumeFromImage will create a volume from with a random name and size of // 1GB. An error will be returned if the volume was unable to be created. func CreateVolumeFromImage(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) { if testing.Short() { t.Skip("Skipping test that requires volume creation in short mode.") } choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatal(err) } volumeName := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create volume: %s", volumeName) createOpts := volumes.CreateOpts{ Size: 1, Name: volumeName, ImageID: choices.ImageID, } volume, err := volumes.Create(client, createOpts).Extract() if err != nil { return volume, err } err = volumes.WaitForStatus(client, volume.ID, "available", 60) if err != nil { return volume, err } return volume, nil } // DeleteVolume will delete a volume. A fatal error will occur if the volume // failed to be deleted. This works best when used as a deferred function. func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { err := volumes.Delete(client, volume.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) } t.Logf("Deleted volume: %s", volume.ID) } // CreateSnapshot will create a snapshot of the specified volume. // Snapshot will be assigned a random name and description. func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (*snapshots.Snapshot, error) { if testing.Short() { t.Skip("Skipping test that requires snapshot creation in short mode.") } snapshotName := tools.RandomString("ACPTTEST", 16) snapshotDescription := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create snapshot: %s", snapshotName) createOpts := snapshots.CreateOpts{ VolumeID: volume.ID, Name: snapshotName, Description: snapshotDescription, } snapshot, err := snapshots.Create(client, createOpts).Extract() if err != nil { return snapshot, err } err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60) if err != nil { return snapshot, err } return snapshot, nil } // DeleteSnapshot will delete a snapshot. A fatal error will occur if the // snapshot failed to be deleted. func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { err := snapshots.Delete(client, snapshot.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete snapshot %s: %+v", snapshot.ID, err) } // Volumes can't be deleted until their snapshots have been, // so block up to 120 seconds for the snapshot to delete. err = gophercloud.WaitFor(120, func() (bool, error) { _, err := snapshots.Get(client, snapshot.ID).Extract() if err != nil { return true, nil } return false, nil }) if err != nil { t.Fatalf("Error waiting for snapshot to delete: %v", err) } t.Logf("Deleted snapshot: %s", snapshot.ID) } pkg.go000066400000000000000000000001551335005366400346400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/noauth// The noauth package contains acceptance tests for the Openstack Cinder standalone service. package noauth snapshots_test.go000066400000000000000000000027021335005366400371400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/noauth// +build acceptance blockstorage package noauth import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots" ) func TestSnapshotsList(t *testing.T) { client, err := clients.NewBlockStorageV2NoAuthClient() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) } allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages() if err != nil { t.Fatalf("Unable to retrieve snapshots: %v", err) } allSnapshots, err := snapshots.ExtractSnapshots(allPages) if err != nil { t.Fatalf("Unable to extract snapshots: %v", err) } for _, snapshot := range allSnapshots { tools.PrintResource(t, snapshot) } } func TestSnapshotsCreateDelete(t *testing.T) { client, err := clients.NewBlockStorageV2NoAuthClient() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) } volume, err := CreateVolume(t, client) if err != nil { t.Fatalf("Unable to create volume: %v", err) } defer DeleteVolume(t, client, volume) snapshot, err := CreateSnapshot(t, client, volume) if err != nil { t.Fatalf("Unable to create snapshot: %v", err) } defer DeleteSnapshot(t, client, snapshot) newSnapshot, err := snapshots.Get(client, snapshot.ID).Extract() if err != nil { t.Errorf("Unable to retrieve snapshot: %v", err) } tools.PrintResource(t, newSnapshot) } volumes_test.go000066400000000000000000000023701335005366400366110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/noauth// +build acceptance blockstorage package noauth import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" ) func TestVolumesList(t *testing.T) { client, err := clients.NewBlockStorageV2NoAuthClient() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) } allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages() if err != nil { t.Fatalf("Unable to retrieve volumes: %v", err) } allVolumes, err := volumes.ExtractVolumes(allPages) if err != nil { t.Fatalf("Unable to extract volumes: %v", err) } for _, volume := range allVolumes { tools.PrintResource(t, volume) } } func TestVolumesCreateDestroy(t *testing.T) { client, err := clients.NewBlockStorageV2NoAuthClient() if err != nil { t.Fatalf("Unable to create blockstorage client: %v", err) } volume, err := CreateVolume(t, client) if err != nil { t.Fatalf("Unable to create volume: %v", err) } defer DeleteVolume(t, client, volume) newVolume, err := volumes.Get(client, volume.ID).Extract() if err != nil { t.Errorf("Unable to retrieve volume: %v", err) } tools.PrintResource(t, newVolume) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/v1/000077500000000000000000000000001335005366400326365ustar00rootroot00000000000000blockstorage.go000066400000000000000000000107001335005366400355630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/v1// Package v1 contains common functions for creating block storage based // resources for use in acceptance tests. See the `*_test.go` files for // example usages. package v1 import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/snapshots" "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes" "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumetypes" ) // CreateSnapshot will create a volume snapshot based off of a given volume and // with a random name. An error will be returned if the snapshot failed to be // created. func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (*snapshots.Snapshot, error) { if testing.Short() { t.Skip("Skipping test that requires snapshot creation in short mode.") } snapshotName := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create snapshot %s based on volume %s", snapshotName, volume.ID) createOpts := snapshots.CreateOpts{ Name: snapshotName, VolumeID: volume.ID, } snapshot, err := snapshots.Create(client, createOpts).Extract() if err != nil { return snapshot, err } err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60) if err != nil { return snapshot, err } return snapshot, nil } // CreateVolume will create a volume with a random name and size of 1GB. An // error will be returned if the volume was unable to be created. func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) { if testing.Short() { t.Skip("Skipping test that requires volume creation in short mode.") } volumeName := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create volume: %s", volumeName) createOpts := volumes.CreateOpts{ Size: 1, Name: volumeName, } volume, err := volumes.Create(client, createOpts).Extract() if err != nil { return volume, err } err = volumes.WaitForStatus(client, volume.ID, "available", 60) if err != nil { return volume, err } return volume, nil } // CreateVolumeType will create a volume type with a random name. An error will // be returned if the volume type was unable to be created. func CreateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (*volumetypes.VolumeType, error) { volumeTypeName := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create volume type: %s", volumeTypeName) createOpts := volumetypes.CreateOpts{ Name: volumeTypeName, ExtraSpecs: map[string]interface{}{ "capabilities": "ssd", "priority": 3, }, } volumeType, err := volumetypes.Create(client, createOpts).Extract() if err != nil { return volumeType, err } return volumeType, nil } // DeleteSnapshot will delete a snapshot. A fatal error will occur if the // snapshot failed to be deleted. This works best when used as a deferred // function. func DeleteSnapshotshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { err := snapshots.Delete(client, snapshot.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete snapshot %s: %v", snapshot.ID, err) } // Volumes can't be deleted until their snapshots have been, // so block up to 120 seconds for the snapshot to delete. err = gophercloud.WaitFor(120, func() (bool, error) { _, err := snapshots.Get(client, snapshot.ID).Extract() if err != nil { return true, nil } return false, nil }) if err != nil { t.Fatalf("Unable to wait for snapshot to delete: %v", err) } t.Logf("Deleted snapshot: %s", snapshot.ID) } // DeleteVolume will delete a volume. A fatal error will occur if the volume // failed to be deleted. This works best when used as a deferred function. func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { err := volumes.Delete(client, volume.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) } t.Logf("Deleted volume: %s", volume.ID) } // DeleteVolumeType will delete a volume type. A fatal error will occur if the // volume type failed to be deleted. This works best when used as a deferred // function. func DeleteVolumeType(t *testing.T, client *gophercloud.ServiceClient, volumeType *volumetypes.VolumeType) { err := volumetypes.Delete(client, volumeType.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete volume type %s: %v", volumeType.ID, err) } t.Logf("Deleted volume type: %s", volumeType.ID) } pkg.go000066400000000000000000000001041335005366400336620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/v1// Package v1 contains openstack cinder acceptance tests package v1 snapshots_test.go000066400000000000000000000026661335005366400362010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/v1// +build acceptance blockstorage package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/snapshots" ) func TestSnapshotsList(t *testing.T) { client, err := clients.NewBlockStorageV1Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) } allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages() if err != nil { t.Fatalf("Unable to retrieve snapshots: %v", err) } allSnapshots, err := snapshots.ExtractSnapshots(allPages) if err != nil { t.Fatalf("Unable to extract snapshots: %v", err) } for _, snapshot := range allSnapshots { tools.PrintResource(t, snapshot) } } func TestSnapshotsCreateDelete(t *testing.T) { client, err := clients.NewBlockStorageV1Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) } volume, err := CreateVolume(t, client) if err != nil { t.Fatalf("Unable to create volume: %v", err) } defer DeleteVolume(t, client, volume) snapshot, err := CreateSnapshot(t, client, volume) if err != nil { t.Fatalf("Unable to create snapshot: %v", err) } defer DeleteSnapshotshot(t, client, snapshot) newSnapshot, err := snapshots.Get(client, snapshot.ID).Extract() if err != nil { t.Errorf("Unable to retrieve snapshot: %v", err) } tools.PrintResource(t, newSnapshot) } volumes_test.go000066400000000000000000000023501335005366400356370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/v1// +build acceptance blockstorage package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes" ) func TestVolumesList(t *testing.T) { client, err := clients.NewBlockStorageV1Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) } allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages() if err != nil { t.Fatalf("Unable to retrieve volumes: %v", err) } allVolumes, err := volumes.ExtractVolumes(allPages) if err != nil { t.Fatalf("Unable to extract volumes: %v", err) } for _, volume := range allVolumes { tools.PrintResource(t, volume) } } func TestVolumesCreateDestroy(t *testing.T) { client, err := clients.NewBlockStorageV1Client() if err != nil { t.Fatalf("Unable to create blockstorage client: %v", err) } volume, err := CreateVolume(t, client) if err != nil { t.Fatalf("Unable to create volume: %v", err) } defer DeleteVolume(t, client, volume) newVolume, err := volumes.Get(client, volume.ID).Extract() if err != nil { t.Errorf("Unable to retrieve volume: %v", err) } tools.PrintResource(t, newVolume) } volumetypes_test.go000066400000000000000000000022341335005366400365420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/v1// +build acceptance blockstorage package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumetypes" ) func TestVolumeTypesList(t *testing.T) { client, err := clients.NewBlockStorageV1Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) } allPages, err := volumetypes.List(client).AllPages() if err != nil { t.Fatalf("Unable to retrieve volume types: %v", err) } allVolumeTypes, err := volumetypes.ExtractVolumeTypes(allPages) if err != nil { t.Fatalf("Unable to extract volume types: %v", err) } for _, volumeType := range allVolumeTypes { tools.PrintResource(t, volumeType) } } func TestVolumeTypesCreateDestroy(t *testing.T) { client, err := clients.NewBlockStorageV1Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) } volumeType, err := CreateVolumeType(t, client) if err != nil { t.Fatalf("Unable to create volume type: %v", err) } defer DeleteVolumeType(t, client, volumeType) tools.PrintResource(t, volumeType) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/v2/000077500000000000000000000000001335005366400326375ustar00rootroot00000000000000blockstorage.go000066400000000000000000000110201335005366400355600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/v2// Package v2 contains common functions for creating block storage based // resources for use in acceptance tests. See the `*_test.go` files for // example usages. package v2 import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" th "github.com/gophercloud/gophercloud/testhelper" ) // CreateSnapshot will create a snapshot of the specified volume. // Snapshot will be assigned a random name and description. func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (*snapshots.Snapshot, error) { snapshotName := tools.RandomString("ACPTTEST", 16) snapshotDescription := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create snapshot: %s", snapshotName) createOpts := snapshots.CreateOpts{ VolumeID: volume.ID, Name: snapshotName, Description: snapshotDescription, } snapshot, err := snapshots.Create(client, createOpts).Extract() if err != nil { return snapshot, err } err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60) if err != nil { return snapshot, err } t.Logf("Successfully created snapshot: %s", snapshot.ID) return snapshot, nil } // CreateVolume will create a volume with a random name and size of 1GB. An // error will be returned if the volume was unable to be created. func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) { volumeName := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create volume: %s", volumeName) createOpts := volumes.CreateOpts{ Size: 1, Name: volumeName, } volume, err := volumes.Create(client, createOpts).Extract() if err != nil { return volume, err } err = volumes.WaitForStatus(client, volume.ID, "available", 60) if err != nil { return volume, err } th.AssertEquals(t, volume.Name, volumeName) th.AssertEquals(t, volume.Size, 1) tools.PrintResource(t, volume) t.Logf("Successfully created volume: %s", volume.ID) return volume, nil } // CreateVolumeFromImage will create a volume from with a random name and size of // 1GB. An error will be returned if the volume was unable to be created. func CreateVolumeFromImage(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) { choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatal(err) } volumeName := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create volume: %s", volumeName) createOpts := volumes.CreateOpts{ Size: 1, Name: volumeName, ImageID: choices.ImageID, } volume, err := volumes.Create(client, createOpts).Extract() if err != nil { return volume, err } err = volumes.WaitForStatus(client, volume.ID, "available", 60) if err != nil { return volume, err } newVolume, err := volumes.Get(client, volume.ID).Extract() if err != nil { return nil, err } th.AssertEquals(t, newVolume.Name, volumeName) th.AssertEquals(t, newVolume.Size, 1) t.Logf("Successfully created volume from image: %s", newVolume.ID) return newVolume, nil } // DeleteVolume will delete a volume. A fatal error will occur if the volume // failed to be deleted. This works best when used as a deferred function. func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { t.Logf("Attempting to delete volume: %s", volume.ID) err := volumes.Delete(client, volume.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) } t.Logf("Successfully deleted volume: %s", volume.ID) } // DeleteSnapshot will delete a snapshot. A fatal error will occur if the // snapshot failed to be deleted. func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { t.Logf("Attempting to delete snapshot: %s", snapshot.ID) err := snapshots.Delete(client, snapshot.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete snapshot %s: %+v", snapshot.ID, err) } // Volumes can't be deleted until their snapshots have been, // so block up to 120 seconds for the snapshot to delete. err = gophercloud.WaitFor(120, func() (bool, error) { _, err := snapshots.Get(client, snapshot.ID).Extract() if err != nil { return true, nil } return false, nil }) if err != nil { t.Fatalf("Error waiting for snapshot to delete: %v", err) } t.Logf("Successfully deleted snapshot: %s", snapshot.ID) } pkg.go000066400000000000000000000001351335005366400336670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/v2// The v2 package contains acceptance tests for the Openstack Cinder V2 service. package v2 snapshots_test.go000066400000000000000000000021341335005366400361700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/v2// +build acceptance blockstorage package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots" th "github.com/gophercloud/gophercloud/testhelper" ) func TestSnapshots(t *testing.T) { clients.RequireLong(t) client, err := clients.NewBlockStorageV2Client() th.AssertNoErr(t, err) volume, err := CreateVolume(t, client) th.AssertNoErr(t, err) defer DeleteVolume(t, client, volume) snapshot, err := CreateSnapshot(t, client, volume) th.AssertNoErr(t, err) defer DeleteSnapshot(t, client, snapshot) newSnapshot, err := snapshots.Get(client, snapshot.ID).Extract() th.AssertNoErr(t, err) allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages() th.AssertNoErr(t, err) allSnapshots, err := snapshots.ExtractSnapshots(allPages) th.AssertNoErr(t, err) var found bool for _, v := range allSnapshots { tools.PrintResource(t, snapshot) if v.ID == newSnapshot.ID { found = true } } th.AssertEquals(t, found, true) } volumes_test.go000066400000000000000000000027351335005366400356470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/v2// +build acceptance blockstorage package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" th "github.com/gophercloud/gophercloud/testhelper" ) func TestVolumesCreateDestroy(t *testing.T) { clients.RequireLong(t) client, err := clients.NewBlockStorageV2Client() th.AssertNoErr(t, err) volume, err := CreateVolume(t, client) th.AssertNoErr(t, err) defer DeleteVolume(t, client, volume) newVolume, err := volumes.Get(client, volume.ID).Extract() th.AssertNoErr(t, err) allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages() th.AssertNoErr(t, err) allVolumes, err := volumes.ExtractVolumes(allPages) th.AssertNoErr(t, err) var found bool for _, v := range allVolumes { tools.PrintResource(t, volume) if v.ID == newVolume.ID { found = true } } th.AssertEquals(t, found, true) } func TestVolumesCreateForceDestroy(t *testing.T) { clients.RequireLong(t) client, err := clients.NewBlockStorageV2Client() th.AssertNoErr(t, err) volume, err := CreateVolume(t, client) th.AssertNoErr(t, err) newVolume, err := volumes.Get(client, volume.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newVolume) err = volumeactions.ForceDelete(client, newVolume.ID).ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/v3/000077500000000000000000000000001335005366400326405ustar00rootroot00000000000000blockstorage.go000066400000000000000000000114431335005366400355720ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/v3// Package v3 contains common functions for creating block storage based // resources for use in acceptance tests. See the `*_test.go` files for // example usages. package v3 import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes" th "github.com/gophercloud/gophercloud/testhelper" ) // CreateSnapshot will create a snapshot of the specified volume. // Snapshot will be assigned a random name and description. func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (*snapshots.Snapshot, error) { snapshotName := tools.RandomString("ACPTTEST", 16) snapshotDescription := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create snapshot: %s", snapshotName) createOpts := snapshots.CreateOpts{ VolumeID: volume.ID, Name: snapshotName, Description: snapshotDescription, } snapshot, err := snapshots.Create(client, createOpts).Extract() if err != nil { return snapshot, err } err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60) if err != nil { return snapshot, err } th.AssertEquals(t, snapshot.Name, snapshotName) th.AssertEquals(t, snapshot.VolumeID, volume.ID) tools.PrintResource(t, snapshot) t.Logf("Successfully created snapshot: %s", snapshot.ID) return snapshot, nil } // CreateVolume will create a volume with a random name and size of 1GB. An // error will be returned if the volume was unable to be created. func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) { volumeName := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create volume: %s", volumeName) createOpts := volumes.CreateOpts{ Size: 1, Name: volumeName, } volume, err := volumes.Create(client, createOpts).Extract() if err != nil { return volume, err } err = volumes.WaitForStatus(client, volume.ID, "available", 60) if err != nil { return volume, err } th.AssertEquals(t, volume.Name, volumeName) tools.PrintResource(t, volume) t.Logf("Successfully created volume: %s", volume.ID) return volume, nil } // CreateVolumeType will create a volume type with a random name. An // error will be returned if the volume was unable to be created. func CreateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (*volumetypes.VolumeType, error) { name := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create volume type: %s", name) createOpts := volumetypes.CreateOpts{ Name: name, ExtraSpecs: map[string]string{"volume_backend_name": "fake_backend_name"}, Description: "create_from_gophercloud", } vt, err := volumetypes.Create(client, createOpts).Extract() if err != nil { return nil, err } th.AssertEquals(t, vt.IsPublic, true) tools.PrintResource(t, vt) t.Logf("Successfully created volume type: %s", vt.ID) return vt, nil } // DeleteSnapshot will delete a snapshot. A fatal error will occur if the // snapshot failed to be deleted. func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { err := snapshots.Delete(client, snapshot.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete snapshot %s: %+v", snapshot.ID, err) } // Volumes can't be deleted until their snapshots have been, // so block up to 120 seconds for the snapshot to delete. err = gophercloud.WaitFor(120, func() (bool, error) { _, err := snapshots.Get(client, snapshot.ID).Extract() if err != nil { return true, nil } return false, nil }) if err != nil { t.Fatalf("Error waiting for snapshot to delete: %v", err) } t.Logf("Deleted snapshot: %s", snapshot.ID) } // DeleteVolume will delete a volume. A fatal error will occur if the volume // failed to be deleted. This works best when used as a deferred function. func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { t.Logf("Attempting to delete volume: %s", volume.ID) err := volumes.Delete(client, volume.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) } t.Logf("Successfully deleted volume: %s", volume.ID) } // DeleteVolumeType will delete a volume type. A fatal error will occur if the // volume type failed to be deleted. This works best when used as a deferred // function. func DeleteVolumeType(t *testing.T, client *gophercloud.ServiceClient, vt *volumetypes.VolumeType) { t.Logf("Attempting to delete volume type: %s", vt.ID) err := volumetypes.Delete(client, vt.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete volume %s: %v", vt.ID, err) } t.Logf("Successfully deleted volume type: %s", vt.ID) } pkg.go000066400000000000000000000001351335005366400336700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/v3// The v3 package contains acceptance tests for the OpenStack Cinder V3 service. package v3 quotaset_test.go000066400000000000000000000076721335005366400360300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/v3// +build acceptance quotasets package v3 import ( "os" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/quotasets" th "github.com/gophercloud/gophercloud/testhelper" ) func TestQuotasetGet(t *testing.T) { clients.RequireAdmin(t) client, projectID := getClientAndProject(t) quotaSet, err := quotasets.Get(client, projectID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, quotaSet) } func TestQuotasetGetDefaults(t *testing.T) { clients.RequireAdmin(t) client, projectID := getClientAndProject(t) quotaSet, err := quotasets.GetDefaults(client, projectID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, quotaSet) } func TestQuotasetGetUsage(t *testing.T) { clients.RequireAdmin(t) client, projectID := getClientAndProject(t) quotaSetUsage, err := quotasets.GetUsage(client, projectID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, quotaSetUsage) } var UpdateQuotaOpts = quotasets.UpdateOpts{ Volumes: gophercloud.IntToPointer(100), Snapshots: gophercloud.IntToPointer(200), Gigabytes: gophercloud.IntToPointer(300), PerVolumeGigabytes: gophercloud.IntToPointer(50), Backups: gophercloud.IntToPointer(2), BackupGigabytes: gophercloud.IntToPointer(300), } var UpdatedQuotas = quotasets.QuotaSet{ Volumes: 100, Snapshots: 200, Gigabytes: 300, PerVolumeGigabytes: 50, Backups: 2, BackupGigabytes: 300, } func TestQuotasetUpdate(t *testing.T) { clients.RequireAdmin(t) client, projectID := getClientAndProject(t) // save original quotas orig, err := quotasets.Get(client, projectID).Extract() th.AssertNoErr(t, err) defer func() { restore := quotasets.UpdateOpts{} FillUpdateOptsFromQuotaSet(*orig, &restore) _, err = quotasets.Update(client, projectID, restore).Extract() th.AssertNoErr(t, err) }() // test Update resultQuotas, err := quotasets.Update(client, projectID, UpdateQuotaOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, UpdatedQuotas, *resultQuotas) // We dont know the default quotas, so just check if the quotas are not the // same as before newQuotas, err := quotasets.Get(client, projectID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, resultQuotas.Volumes, newQuotas.Volumes) } func TestQuotasetDelete(t *testing.T) { clients.RequireAdmin(t) client, projectID := getClientAndProject(t) // save original quotas orig, err := quotasets.Get(client, projectID).Extract() th.AssertNoErr(t, err) defer func() { restore := quotasets.UpdateOpts{} FillUpdateOptsFromQuotaSet(*orig, &restore) _, err = quotasets.Update(client, projectID, restore).Extract() th.AssertNoErr(t, err) }() // Obtain environment default quotaset values to validate deletion. defaultQuotaSet, err := quotasets.GetDefaults(client, projectID).Extract() th.AssertNoErr(t, err) // Test Delete err = quotasets.Delete(client, projectID).ExtractErr() th.AssertNoErr(t, err) newQuotas, err := quotasets.Get(client, projectID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newQuotas.Volumes, defaultQuotaSet.Volumes) } // getClientAndProject reduces boilerplate by returning a new blockstorage v3 // ServiceClient and a project ID obtained from the OS_PROJECT_NAME envvar. func getClientAndProject(t *testing.T) (*gophercloud.ServiceClient, string) { client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) projectID := os.Getenv("OS_PROJECT_NAME") th.AssertNoErr(t, err) return client, projectID } func FillUpdateOptsFromQuotaSet(src quotasets.QuotaSet, dest *quotasets.UpdateOpts) { dest.Volumes = &src.Volumes dest.Snapshots = &src.Snapshots dest.Gigabytes = &src.Gigabytes dest.PerVolumeGigabytes = &src.PerVolumeGigabytes dest.Backups = &src.Backups dest.BackupGigabytes = &src.BackupGigabytes } snapshots_test.go000066400000000000000000000025211335005366400361710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/v3// +build acceptance blockstorage package v3 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestSnapshots(t *testing.T) { clients.RequireLong(t) client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) volume1, err := CreateVolume(t, client) th.AssertNoErr(t, err) defer DeleteVolume(t, client, volume1) snapshot1, err := CreateSnapshot(t, client, volume1) th.AssertNoErr(t, err) defer DeleteSnapshot(t, client, snapshot1) volume2, err := CreateVolume(t, client) th.AssertNoErr(t, err) defer DeleteVolume(t, client, volume2) snapshot2, err := CreateSnapshot(t, client, volume2) th.AssertNoErr(t, err) defer DeleteSnapshot(t, client, snapshot2) listOpts := snapshots.ListOpts{ Limit: 1, } err = snapshots.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { actual, err := snapshots.ExtractSnapshots(page) th.AssertNoErr(t, err) th.AssertEquals(t, 1, len(actual)) var found bool for _, v := range actual { if v.ID == snapshot1.ID || v.ID == snapshot2.ID { found = true } } th.AssertEquals(t, found, true) return true, nil }) th.AssertNoErr(t, err) } volumes_test.go000066400000000000000000000021131335005366400356360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/v3// +build acceptance blockstorage package v3 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestVolumes(t *testing.T) { clients.RequireLong(t) client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) volume1, err := CreateVolume(t, client) th.AssertNoErr(t, err) defer DeleteVolume(t, client, volume1) volume2, err := CreateVolume(t, client) th.AssertNoErr(t, err) defer DeleteVolume(t, client, volume2) listOpts := volumes.ListOpts{ Limit: 1, } err = volumes.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { actual, err := volumes.ExtractVolumes(page) th.AssertNoErr(t, err) th.AssertEquals(t, 1, len(actual)) var found bool for _, v := range actual { if v.ID == volume1.ID || v.ID == volume2.ID { found = true } } th.AssertEquals(t, found, true) return true, nil }) th.AssertNoErr(t, err) } volumetypes_test.go000066400000000000000000000023201335005366400365400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/blockstorage/v3// +build acceptance blockstorage package v3 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes" th "github.com/gophercloud/gophercloud/testhelper" ) func TestVolumeTypes(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) vt, err := CreateVolumeType(t, client) th.AssertNoErr(t, err) defer DeleteVolumeType(t, client, vt) allPages, err := volumetypes.List(client, nil).AllPages() th.AssertNoErr(t, err) allVolumeTypes, err := volumetypes.ExtractVolumeTypes(allPages) th.AssertNoErr(t, err) var found bool for _, v := range allVolumeTypes { tools.PrintResource(t, v) if v.ID == vt.ID { found = true } } th.AssertEquals(t, found, true) var isPublic = false updateOpts := volumetypes.UpdateOpts{ Name: vt.Name + "-UPDATED", IsPublic: &isPublic, } newVT, err := volumetypes.Update(client, vt.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, vt.Name+"-UPDATED", newVT.Name) th.AssertEquals(t, false, newVT.IsPublic) tools.PrintResource(t, newVT) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/client_test.go000066400000000000000000000040311335005366400324730ustar00rootroot00000000000000// +build acceptance package openstack import ( "os" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack" ) func TestAuthenticatedClient(t *testing.T) { // Obtain credentials from the environment. ao, err := openstack.AuthOptionsFromEnv() if err != nil { t.Fatalf("Unable to acquire credentials: %v", err) } client, err := openstack.AuthenticatedClient(ao) if err != nil { t.Fatalf("Unable to authenticate: %v", err) } if client.TokenID == "" { t.Errorf("No token ID assigned to the client") } t.Logf("Client successfully acquired a token: %v", client.TokenID) // Find the storage service in the service catalog. storage, err := openstack.NewObjectStorageV1(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) if err != nil { t.Errorf("Unable to locate a storage service: %v", err) } else { t.Logf("Located a storage service at endpoint: [%s]", storage.Endpoint) } } func TestReauth(t *testing.T) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { t.Fatalf("Unable to obtain environment auth options: %v", err) } // Allow reauth ao.AllowReauth = true provider, err := openstack.NewClient(ao.IdentityEndpoint) if err != nil { t.Fatalf("Unable to create provider: %v", err) } err = openstack.Authenticate(provider, ao) if err != nil { t.Fatalf("Unable to authenticate: %v", err) } t.Logf("Creating a compute client") _, err = openstack.NewComputeV2(provider, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) if err != nil { t.Fatalf("Unable to create compute client: %v", err) } t.Logf("Sleeping for 1 second") time.Sleep(1 * time.Second) t.Logf("Attempting to reauthenticate") err = provider.ReauthFunc() if err != nil { t.Fatalf("Unable to reauthenticate: %v", err) } t.Logf("Creating a compute client") _, err = openstack.NewComputeV2(provider, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) if err != nil { t.Fatalf("Unable to create compute client: %v", err) } } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/clustering/000077500000000000000000000000001335005366400320105ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/clustering/v1/000077500000000000000000000000001335005366400323365ustar00rootroot00000000000000actions_test.go000066400000000000000000000012741335005366400353110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/clustering/v1// +build acceptance clustering actions package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/actions" th "github.com/gophercloud/gophercloud/testhelper" ) func TestActionsList(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) opts := actions.ListOpts{ Limit: 200, } allPages, err := actions.List(client, opts).AllPages() th.AssertNoErr(t, err) allActions, err := actions.ExtractActions(allPages) th.AssertNoErr(t, err) for _, action := range allActions { tools.PrintResource(t, action) } } clustering.go000066400000000000000000000262451335005366400347760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/clustering/v1package v1 import ( "fmt" "net/http" "strings" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/actions" "github.com/gophercloud/gophercloud/openstack/clustering/v1/clusters" "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" "github.com/gophercloud/gophercloud/openstack/clustering/v1/policies" "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiles" "github.com/gophercloud/gophercloud/openstack/clustering/v1/receivers" th "github.com/gophercloud/gophercloud/testhelper" ) var TestPolicySpec = policies.Spec{ Description: "new policy description", Properties: map[string]interface{}{ "destroy_after_deletion": true, "grace_period": 60, "reduce_desired_capacity": false, "criteria": "OLDEST_FIRST", }, Type: "senlin.policy.deletion", Version: "1.1", } // CreateCluster creates a random cluster. An error will be returned if // the cluster could not be created. func CreateCluster(t *testing.T, client *gophercloud.ServiceClient, profileID string) (*clusters.Cluster, error) { name := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create cluster: %s", name) createOpts := clusters.CreateOpts{ Name: name, DesiredCapacity: 1, ProfileID: profileID, MinSize: new(int), MaxSize: 20, Timeout: 3600, Metadata: map[string]interface{}{ "foo": "bar", "test": map[string]interface{}{ "nil_interface": interface{}(nil), "float_value": float64(123.3), "string_value": "test_string", "bool_value": false, }, }, Config: map[string]interface{}{}, } res := clusters.Create(client, createOpts) if res.Err != nil { return nil, res.Err } requestID := res.Header.Get("X-OpenStack-Request-Id") th.AssertEquals(t, true, requestID != "") t.Logf("Cluster %s request ID: %s", name, requestID) actionID, err := GetActionID(res.Header) th.AssertNoErr(t, err) th.AssertEquals(t, true, actionID != "") t.Logf("Cluster %s action ID: %s", name, actionID) err = WaitForAction(client, actionID) if err != nil { return nil, err } cluster, err := res.Extract() if err != nil { return nil, err } t.Logf("Successfully created cluster: %s", cluster.ID) tools.PrintResource(t, cluster) tools.PrintResource(t, cluster.CreatedAt) th.AssertEquals(t, name, cluster.Name) th.AssertEquals(t, profileID, cluster.ProfileID) return cluster, nil } // CreateNode creates a random node. An error will be returned if // the node could not be created. func CreateNode(t *testing.T, client *gophercloud.ServiceClient, clusterID, profileID string) (*nodes.Node, error) { name := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create node: %s", name) createOpts := nodes.CreateOpts{ ClusterID: clusterID, Metadata: map[string]interface{}{ "foo": "bar", "test": map[string]interface{}{ "nil_interface": interface{}(nil), "float_value": float64(123.3), "string_value": "test_string", "bool_value": false, }, }, Name: name, ProfileID: profileID, Role: "", } res := nodes.Create(client, createOpts) if res.Err != nil { return nil, res.Err } requestID := res.Header.Get("X-OpenStack-Request-Id") th.AssertEquals(t, true, requestID != "") t.Logf("Node %s request ID: %s", name, requestID) actionID, err := GetActionID(res.Header) th.AssertNoErr(t, err) th.AssertEquals(t, true, actionID != "") t.Logf("Node %s action ID: %s", name, actionID) err = WaitForAction(client, actionID) if err != nil { return nil, err } node, err := res.Extract() if err != nil { return nil, err } err = WaitForNodeStatus(client, node.ID, "ACTIVE") if err != nil { return nil, err } t.Logf("Successfully created node: %s", node.ID) node, err = nodes.Get(client, node.ID).Extract() if err != nil { return nil, err } tools.PrintResource(t, node) tools.PrintResource(t, node.CreatedAt) th.AssertEquals(t, profileID, node.ProfileID) th.AssertEquals(t, clusterID, node.ClusterID) th.AssertDeepEquals(t, createOpts.Metadata, node.Metadata) return node, nil } // CreatePolicy creates a random policy. An error will be returned if the // policy could not be created. func CreatePolicy(t *testing.T, client *gophercloud.ServiceClient) (*policies.Policy, error) { name := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create policy: %s", name) createOpts := policies.CreateOpts{ Name: name, Spec: TestPolicySpec, } res := policies.Create(client, createOpts) if res.Err != nil { return nil, res.Err } requestID := res.Header.Get("X-OpenStack-Request-Id") th.AssertEquals(t, true, requestID != "") t.Logf("Policy %s request ID: %s", name, requestID) policy, err := res.Extract() if err != nil { return nil, err } t.Logf("Successfully created policy: %s", policy.ID) tools.PrintResource(t, policy) tools.PrintResource(t, policy.CreatedAt) th.AssertEquals(t, name, policy.Name) return policy, nil } // CreateProfile will create a random profile. An error will be returned if the // profile could not be created. func CreateProfile(t *testing.T, client *gophercloud.ServiceClient) (*profiles.Profile, error) { choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { return nil, err } name := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create profile: %s", name) networks := []map[string]interface{}{ {"network": choices.NetworkName}, } props := map[string]interface{}{ "name": name, "flavor": choices.FlavorID, "image": choices.ImageID, "networks": networks, "security_groups": "", } createOpts := profiles.CreateOpts{ Name: name, Spec: profiles.Spec{ Type: "os.nova.server", Version: "1.0", Properties: props, }, } res := profiles.Create(client, createOpts) if res.Err != nil { return nil, res.Err } requestID := res.Header.Get("X-OpenStack-Request-Id") th.AssertEquals(t, true, requestID != "") t.Logf("Profile %s request ID: %s", name, requestID) profile, err := res.Extract() if err != nil { return nil, err } t.Logf("Successfully created profile: %s", profile.ID) tools.PrintResource(t, profile) tools.PrintResource(t, profile.CreatedAt) th.AssertEquals(t, name, profile.Name) th.AssertEquals(t, profile.Spec.Type, "os.nova.server") th.AssertEquals(t, profile.Spec.Version, "1.0") return profile, nil } // CreateReceiver will create a random profile. An error will be returned if the // profile could not be created. func CreateReceiver(t *testing.T, client *gophercloud.ServiceClient, clusterID string) (*receivers.Receiver, error) { name := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create receiver: %s", name) createOpts := receivers.CreateOpts{ Name: name, ClusterID: clusterID, Type: receivers.WebhookReceiver, Action: "CLUSTER_SCALE_OUT", } res := receivers.Create(client, createOpts) if res.Err != nil { return nil, res.Err } receiver, err := res.Extract() if err != nil { return nil, err } t.Logf("Successfully created receiver: %s", receiver.ID) tools.PrintResource(t, receiver) tools.PrintResource(t, receiver.CreatedAt) th.AssertEquals(t, name, receiver.Name) th.AssertEquals(t, createOpts.Action, receiver.Action) return receiver, nil } // DeleteCluster will delete a given policy. A fatal error will occur if the // cluster could not be deleted. This works best as a deferred function. func DeleteCluster(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete cluster: %s", id) res := clusters.Delete(client, id) if res.Err != nil { t.Fatalf("Error deleting cluster %s: %s:", id, res.Err) } actionID, err := GetActionID(res.Header) if err != nil { t.Fatalf("Error deleting cluster %s: %s:", id, res.Err) } err = WaitForAction(client, actionID) if err != nil { t.Fatalf("Error deleting cluster %s: %s:", id, res.Err) } t.Logf("Successfully deleted cluster: %s", id) return } // DeleteNode will delete a given node. A fatal error will occur if the // node could not be deleted. This works best as a deferred function. func DeleteNode(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete node: %s", id) err := nodes.Delete(client, id).ExtractErr() if err != nil { t.Fatalf("Error deleting node %s: %s:", id, err) } err = WaitForNodeStatus(client, id, "DELETED") if err != nil { t.Fatalf("Error deleting node %s: %s", id, err) } t.Logf("Successfully deleted node: %s", id) return } // DeletePolicy will delete a given policy. A fatal error will occur if the // policy could not be deleted. This works best as a deferred function. func DeletePolicy(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete policy: %s", id) err := policies.Delete(client, id).ExtractErr() if err != nil { t.Fatalf("Error deleting policy %s: %s:", id, err) } t.Logf("Successfully deleted policy: %s", id) return } // DeleteProfile will delete a given profile. A fatal error will occur if the // profile could not be deleted. This works best as a deferred function. func DeleteProfile(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete profile: %s", id) err := profiles.Delete(client, id).ExtractErr() if err != nil { t.Fatalf("Error deleting profile %s: %s:", id, err) } t.Logf("Successfully deleted profile: %s", id) return } // DeleteReceiver will delete a given receiver. A fatal error will occur if the // receiver could not be deleted. This works best as a deferred function. func DeleteReceiver(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete Receiver: %s", id) res := receivers.Delete(client, id) if res.Err != nil { t.Fatalf("Error deleting receiver %s: %s:", id, res.Err) } t.Logf("Successfully deleted receiver: %s", id) return } // GetActionID parses an HTTP header and returns the action ID. func GetActionID(headers http.Header) (string, error) { location := headers.Get("Location") v := strings.Split(location, "actions/") if len(v) < 2 { return "", fmt.Errorf("unable to determine action ID") } actionID := v[1] return actionID, nil } func WaitForAction(client *gophercloud.ServiceClient, actionID string) error { return tools.WaitFor(func() (bool, error) { action, err := actions.Get(client, actionID).Extract() if err != nil { return false, err } if action.Status == "SUCCEEDED" { return true, nil } if action.Status == "FAILED" { return false, fmt.Errorf("Action %s in FAILED state", actionID) } return false, nil }) } func WaitForNodeStatus(client *gophercloud.ServiceClient, id string, status string) error { return tools.WaitFor(func() (bool, error) { latest, err := nodes.Get(client, id).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok && status == "DELETED" { return true, nil } return false, err } if latest.Status == status { return true, nil } if latest.Status == "ERROR" { return false, fmt.Errorf("Node %s in ERROR state", id) } return false, nil }) } clusters_test.go000066400000000000000000000152431335005366400355160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/clustering/v1// +build acceptance clustering policies package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/clusters" th "github.com/gophercloud/gophercloud/testhelper" ) func TestClustersCRUD(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) profile, err := CreateProfile(t, client) th.AssertNoErr(t, err) defer DeleteProfile(t, client, profile.ID) cluster, err := CreateCluster(t, client, profile.ID) th.AssertNoErr(t, err) defer DeleteCluster(t, client, cluster.ID) // Test clusters list allPages, err := clusters.List(client, nil).AllPages() th.AssertNoErr(t, err) allClusters, err := clusters.ExtractClusters(allPages) th.AssertNoErr(t, err) var found bool for _, v := range allClusters { if v.ID == cluster.ID { found = true } } th.AssertEquals(t, found, true) // Test cluster update updateOpts := clusters.UpdateOpts{ Name: cluster.Name + "-UPDATED", } res := clusters.Update(client, cluster.ID, updateOpts) th.AssertNoErr(t, res.Err) actionID, err := GetActionID(res.Header) th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) newCluster, err := clusters.Get(client, cluster.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newCluster.Name, cluster.Name+"-UPDATED") tools.PrintResource(t, newCluster) // Test cluster health actionID, err = clusters.Check(client, cluster.ID).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) } func TestClustersResize(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) profile, err := CreateProfile(t, client) th.AssertNoErr(t, err) defer DeleteProfile(t, client, profile.ID) cluster, err := CreateCluster(t, client, profile.ID) th.AssertNoErr(t, err) defer DeleteCluster(t, client, cluster.ID) iTrue := true resizeOpts := clusters.ResizeOpts{ AdjustmentType: clusters.ChangeInCapacityAdjustment, Number: 1, Strict: &iTrue, } actionID, err := clusters.Resize(client, cluster.ID, resizeOpts).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) newCluster, err := clusters.Get(client, cluster.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newCluster.DesiredCapacity, 2) tools.PrintResource(t, newCluster) } func TestClustersScale(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) profile, err := CreateProfile(t, client) th.AssertNoErr(t, err) defer DeleteProfile(t, client, profile.ID) cluster, err := CreateCluster(t, client, profile.ID) th.AssertNoErr(t, err) defer DeleteCluster(t, client, cluster.ID) // increase cluster size to 2 scaleOutOpts := clusters.ScaleOutOpts{ Count: 1, } actionID, err := clusters.ScaleOut(client, cluster.ID, scaleOutOpts).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) newCluster, err := clusters.Get(client, cluster.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newCluster.DesiredCapacity, 2) // reduce cluster size to 0 count := 2 scaleInOpts := clusters.ScaleInOpts{ Count: &count, } actionID, err = clusters.ScaleIn(client, cluster.ID, scaleInOpts).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) newCluster, err = clusters.Get(client, cluster.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newCluster.DesiredCapacity, 0) tools.PrintResource(t, newCluster) } func TestClustersPolicies(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) client.Microversion = "1.5" profile, err := CreateProfile(t, client) th.AssertNoErr(t, err) defer DeleteProfile(t, client, profile.ID) cluster, err := CreateCluster(t, client, profile.ID) th.AssertNoErr(t, err) defer DeleteCluster(t, client, cluster.ID) policy, err := CreatePolicy(t, client) th.AssertNoErr(t, err) defer DeletePolicy(t, client, policy.ID) iTrue := true attachPolicyOpts := clusters.AttachPolicyOpts{ PolicyID: policy.ID, Enabled: &iTrue, } actionID, err := clusters.AttachPolicy(client, cluster.ID, attachPolicyOpts).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) // List all policies in the cluster to see if the policy was // successfully attached. allPages, err := clusters.ListPolicies(client, cluster.ID, nil).AllPages() th.AssertNoErr(t, err) allPolicies, err := clusters.ExtractClusterPolicies(allPages) th.AssertNoErr(t, err) var found bool for _, v := range allPolicies { tools.PrintResource(t, v) if v.PolicyID == policy.ID { found = true } } th.AssertEquals(t, found, true) // Set the policy to disabled iFalse := false updatePolicyOpts := clusters.UpdatePolicyOpts{ PolicyID: policy.ID, Enabled: &iFalse, } actionID, err = clusters.UpdatePolicy(client, cluster.ID, updatePolicyOpts).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) clusterPolicy, err := clusters.GetPolicy(client, cluster.ID, policy.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, clusterPolicy.Enabled, false) // Detach the policy detachPolicyOpts := clusters.DetachPolicyOpts{ PolicyID: policy.ID, } actionID, err = clusters.DetachPolicy(client, cluster.ID, detachPolicyOpts).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) // List all policies in the cluster to see if the policy was // successfully detached. allPages, err = clusters.ListPolicies(client, cluster.ID, nil).AllPages() th.AssertNoErr(t, err) allPolicies, err = clusters.ExtractClusterPolicies(allPages) th.AssertNoErr(t, err) found = false for _, v := range allPolicies { tools.PrintResource(t, v) if v.PolicyID == policy.ID { found = true } } th.AssertEquals(t, found, false) } func TestClustersRecovery(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) profile, err := CreateProfile(t, client) th.AssertNoErr(t, err) defer DeleteProfile(t, client, profile.ID) cluster, err := CreateCluster(t, client, profile.ID) th.AssertNoErr(t, err) defer DeleteCluster(t, client, cluster.ID) recoverOpts := clusters.RecoverOpts{ Operation: clusters.RebuildRecovery, } actionID, err := clusters.Recover(client, cluster.ID, recoverOpts).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) newCluster, err := clusters.Get(client, cluster.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newCluster) } nodes_test.go000066400000000000000000000030731335005366400347600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/clustering/v1// +build acceptance clustering policies package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" th "github.com/gophercloud/gophercloud/testhelper" ) func TestNodesCRUD(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) profile, err := CreateProfile(t, client) th.AssertNoErr(t, err) defer DeleteProfile(t, client, profile.ID) cluster, err := CreateCluster(t, client, profile.ID) th.AssertNoErr(t, err) defer DeleteCluster(t, client, cluster.ID) node, err := CreateNode(t, client, cluster.ID, profile.ID) th.AssertNoErr(t, err) defer DeleteNode(t, client, node.ID) // Test nodes list allPages, err := nodes.List(client, nil).AllPages() th.AssertNoErr(t, err) allNodes, err := nodes.ExtractNodes(allPages) th.AssertNoErr(t, err) var found bool for _, v := range allNodes { if v.ID == node.ID { found = true } } th.AssertEquals(t, found, true) // Test nodes update t.Logf("Attempting to update node %s", node.ID) updateOpts := nodes.UpdateOpts{ Metadata: map[string]interface{}{ "bar": "baz", }, } res := nodes.Update(client, node.ID, updateOpts) th.AssertNoErr(t, res.Err) actionID, err := GetActionID(res.Header) th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) node, err = nodes.Get(client, node.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, node) tools.PrintResource(t, node.Metadata) } pkg.go000066400000000000000000000001441335005366400333660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/clustering/v1// Package v1 package contains acceptance tests for the Openstack Clustering V1 service. package v1 policies_test.go000066400000000000000000000033771335005366400354660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/clustering/v1// +build acceptance clustering policies package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/policies" th "github.com/gophercloud/gophercloud/testhelper" ) func TestPoliciesCRUD(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) client.Microversion = "1.5" policy, err := CreatePolicy(t, client) th.AssertNoErr(t, err) defer DeletePolicy(t, client, policy.ID) // Test listing policies allPages, err := policies.List(client, nil).AllPages() th.AssertNoErr(t, err) allPolicies, err := policies.ExtractPolicies(allPages) th.AssertNoErr(t, err) var found bool for _, v := range allPolicies { if v.ID == policy.ID { found = true } } th.AssertEquals(t, found, true) // Test Get policy getPolicy, err := policies.Get(client, policy.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, getPolicy) // Test updating policy updateOpts := policies.UpdateOpts{ Name: policy.Name + "-UPDATE", } t.Logf("Attempting to update policy: %s", policy.ID) updatePolicy, err := policies.Update(client, policy.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatePolicy) tools.PrintResource(t, updatePolicy.UpdatedAt) // Test validating policy t.Logf("Attempting to validate policy: %s", policy.ID) validateOpts := policies.ValidateOpts{ Spec: TestPolicySpec, } validatePolicy, err := policies.Validate(client, validateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, validatePolicy) th.AssertEquals(t, validatePolicy.Name, "validated_policy") th.AssertEquals(t, validatePolicy.Spec.Version, TestPolicySpec.Version) } policytypes_test.go000066400000000000000000000031061335005366400362310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/clustering/v1// +build acceptance clustering policytypes package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/policytypes" th "github.com/gophercloud/gophercloud/testhelper" ) func TestPolicyTypeList(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) allPages, err := policytypes.List(client).AllPages() th.AssertNoErr(t, err) allPolicyTypes, err := policytypes.ExtractPolicyTypes(allPages) th.AssertNoErr(t, err) for _, v := range allPolicyTypes { tools.PrintResource(t, v) } } func TestPolicyTypeList_v_1_5(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) client.Microversion = "1.5" allPages, err := policytypes.List(client).AllPages() th.AssertNoErr(t, err) allPolicyTypes, err := policytypes.ExtractPolicyTypes(allPages) th.AssertNoErr(t, err) for _, v := range allPolicyTypes { tools.PrintResource(t, v) } } func TestPolicyTypeGet(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) policyType, err := policytypes.Get(client, "senlin.policy.batch-1.0").Extract() th.AssertNoErr(t, err) tools.PrintResource(t, policyType) } func TestPolicyTypeGet_v_1_5(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) client.Microversion = "1.5" policyType, err := policytypes.Get(client, "senlin.policy.batch-1.0").Extract() th.AssertNoErr(t, err) tools.PrintResource(t, policyType) } profiles_test.go000066400000000000000000000022661335005366400354760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/clustering/v1// +build acceptance clustering policies package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiles" th "github.com/gophercloud/gophercloud/testhelper" ) func TestProfilesCRUD(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) profile, err := CreateProfile(t, client) th.AssertNoErr(t, err) defer DeleteProfile(t, client, profile.ID) // Test listing profiles allPages, err := profiles.List(client, nil).AllPages() th.AssertNoErr(t, err) allProfiles, err := profiles.ExtractProfiles(allPages) th.AssertNoErr(t, err) var found bool for _, v := range allProfiles { if v.ID == profile.ID { found = true } } th.AssertEquals(t, found, true) // Test updating profile updateOpts := profiles.UpdateOpts{ Name: profile.Name + "-UPDATED", } newProfile, err := profiles.Update(client, profile.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newProfile.Name, profile.Name+"-UPDATED") tools.PrintResource(t, newProfile) tools.PrintResource(t, newProfile.UpdatedAt) } profiletypes_test.go000066400000000000000000000022301335005366400363670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/clustering/v1// +build acceptance clustering profiletypes package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiletypes" th "github.com/gophercloud/gophercloud/testhelper" ) func TestProfileTypesList(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) client.Microversion = "1.5" allPages, err := profiletypes.List(client).AllPages() th.AssertNoErr(t, err) allProfileTypes, err := profiletypes.ExtractProfileTypes(allPages) th.AssertNoErr(t, err) for _, profileType := range allProfileTypes { tools.PrintResource(t, profileType) } } func TestProfileTypesOpsList(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) client.Microversion = "1.5" profileTypeName := "os.nova.server-1.0" allPages, err := profiletypes.ListOps(client, profileTypeName).AllPages() th.AssertNoErr(t, err) ops, err := profiletypes.ExtractOps(allPages) th.AssertNoErr(t, err) for k, v := range ops { tools.PrintResource(t, k) tools.PrintResource(t, v) } } receivers_test.go000066400000000000000000000026731335005366400356440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/clustering/v1// +build acceptance clustering policies package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/receivers" th "github.com/gophercloud/gophercloud/testhelper" ) func TestReceiversCRUD(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) profile, err := CreateProfile(t, client) th.AssertNoErr(t, err) defer DeleteProfile(t, client, profile.ID) cluster, err := CreateCluster(t, client, profile.ID) th.AssertNoErr(t, err) defer DeleteCluster(t, client, cluster.ID) receiver, err := CreateReceiver(t, client, cluster.ID) th.AssertNoErr(t, err) defer DeleteReceiver(t, client, receiver.ID) // Test listing receivers allPages, err := receivers.List(client, nil).AllPages() th.AssertNoErr(t, err) allReceivers, err := receivers.ExtractReceivers(allPages) th.AssertNoErr(t, err) var found bool for _, v := range allReceivers { if v.ID == receiver.ID { found = true } } th.AssertEquals(t, found, true) // Test updating receivers newName := receiver.Name + "-UPDATED" updateOpts := receivers.UpdateOpts{ Name: newName, } receiver, err = receivers.Update(client, receiver.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, receiver) tools.PrintResource(t, receiver.UpdatedAt) th.AssertEquals(t, receiver.Name, newName) } webhooktrigger_test.go000066400000000000000000000032611335005366400366710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/clustering/v1// +build acceptance clustering webhooks package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/clustering/v1/webhooks" "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" th "github.com/gophercloud/gophercloud/testhelper" ) func TestClusteringWebhookTrigger(t *testing.T) { client, err := clients.NewClusteringV1Client() if err != nil { t.Fatalf("Unable to create clustering client: %v", err) } opts := webhooks.TriggerOpts{ V: "1", } // create profile, cluster and receiver first profile, err := CreateProfile(t, client) th.AssertNoErr(t, err) defer DeleteProfile(t, client, profile.ID) cluster, err := CreateCluster(t, client, profile.ID) th.AssertNoErr(t, err) defer DeleteCluster(t, client, cluster.ID) receiver, err := CreateReceiver(t, client, cluster.ID) th.AssertNoErr(t, err) defer DeleteReceiver(t, client, receiver.ID) // trigger webhook actionID, err := webhooks.Trigger(client, receiver.ID, opts).Extract() if err != nil { t.Fatalf("Unable to extract webhooks trigger: %v", err) } else { t.Logf("Webhook trigger action id %s", actionID) } err = WaitForAction(client, actionID) if err != nil { t.Fatalf("Error scaling out cluster %s as a result from webhook trigger: %s:", cluster.ID, err) } // check that new node was created nodelistopts := nodes.ListOpts{ ClusterID: cluster.ID, } allPages, err := nodes.List(client, nodelistopts).AllPages() th.AssertNoErr(t, err) allNodes, err := nodes.ExtractNodes(allPages) th.AssertNoErr(t, err) // there should be 2 nodes in the cluster after triggering webhook th.AssertEquals(t, len(allNodes), 2) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/common.go000066400000000000000000000011521335005366400314470ustar00rootroot00000000000000// Package openstack contains common functions that can be used // across all OpenStack components for acceptance testing. package openstack import ( "testing" "github.com/gophercloud/gophercloud/openstack/common/extensions" ) // PrintExtension prints an extension and all of its attributes. func PrintExtension(t *testing.T, extension *extensions.Extension) { t.Logf("Name: %s", extension.Name) t.Logf("Namespace: %s", extension.Namespace) t.Logf("Alias: %s", extension.Alias) t.Logf("Description: %s", extension.Description) t.Logf("Updated: %s", extension.Updated) t.Logf("Links: %v", extension.Links) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/000077500000000000000000000000001335005366400313055ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2/000077500000000000000000000000001335005366400316345ustar00rootroot00000000000000aggregates_test.go000066400000000000000000000076011335005366400352600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute aggregates package v2 import ( "fmt" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/hypervisors" th "github.com/gophercloud/gophercloud/testhelper" ) func TestAggregatesList(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) allPages, err := aggregates.List(client).AllPages() th.AssertNoErr(t, err) allAggregates, err := aggregates.ExtractAggregates(allPages) th.AssertNoErr(t, err) for _, v := range allAggregates { tools.PrintResource(t, v) } } func TestAggregatesCRUD(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) aggregate, err := CreateAggregate(t, client) th.AssertNoErr(t, err) defer DeleteAggregate(t, client, aggregate) tools.PrintResource(t, aggregate) updateOpts := aggregates.UpdateOpts{ Name: "new_aggregate_name", AvailabilityZone: "new_azone", } updatedAggregate, err := aggregates.Update(client, aggregate.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, aggregate) th.AssertEquals(t, updatedAggregate.Name, "new_aggregate_name") th.AssertEquals(t, updatedAggregate.AvailabilityZone, "new_azone") } func TestAggregatesAddRemoveHost(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) hostToAdd, err := getHypervisor(t, client) th.AssertNoErr(t, err) aggregate, err := CreateAggregate(t, client) th.AssertNoErr(t, err) defer DeleteAggregate(t, client, aggregate) addHostOpts := aggregates.AddHostOpts{ Host: hostToAdd.HypervisorHostname, } aggregateWithNewHost, err := aggregates.AddHost(client, aggregate.ID, addHostOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, aggregateWithNewHost) th.AssertEquals(t, aggregateWithNewHost.Hosts[0], hostToAdd.HypervisorHostname) removeHostOpts := aggregates.RemoveHostOpts{ Host: hostToAdd.HypervisorHostname, } aggregateWithRemovedHost, err := aggregates.RemoveHost(client, aggregate.ID, removeHostOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, aggregateWithRemovedHost) th.AssertEquals(t, len(aggregateWithRemovedHost.Hosts), 0) } func TestAggregatesSetRemoveMetadata(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) aggregate, err := CreateAggregate(t, client) th.AssertNoErr(t, err) defer DeleteAggregate(t, client, aggregate) opts := aggregates.SetMetadataOpts{ Metadata: map[string]interface{}{"key": "value"}, } aggregateWithMetadata, err := aggregates.SetMetadata(client, aggregate.ID, opts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, aggregateWithMetadata) if _, ok := aggregateWithMetadata.Metadata["key"]; !ok { t.Fatalf("aggregate %s did not contain metadata", aggregateWithMetadata.Name) } optsToRemove := aggregates.SetMetadataOpts{ Metadata: map[string]interface{}{"key": nil}, } aggregateWithRemovedKey, err := aggregates.SetMetadata(client, aggregate.ID, optsToRemove).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, aggregateWithRemovedKey) if _, ok := aggregateWithRemovedKey.Metadata["key"]; ok { t.Fatalf("aggregate %s still contains metadata", aggregateWithRemovedKey.Name) } } func getHypervisor(t *testing.T, client *gophercloud.ServiceClient) (*hypervisors.Hypervisor, error) { allPages, err := hypervisors.List(client).AllPages() th.AssertNoErr(t, err) allHypervisors, err := hypervisors.ExtractHypervisors(allPages) th.AssertNoErr(t, err) for _, h := range allHypervisors { return &h, nil } return nil, fmt.Errorf("Unable to get hypervisor") } attachinterfaces_test.go000066400000000000000000000023661335005366400364620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute servers package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" ) func TestAttachDetachInterface(t *testing.T) { clients.RequireLong(t) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) iface, err := AttachInterface(t, client, server.ID) th.AssertNoErr(t, err) defer DetachInterface(t, client, server.ID, iface.PortID) tools.PrintResource(t, iface) server, err = servers.Get(client, server.ID).Extract() th.AssertNoErr(t, err) var found bool for _, networkAddresses := range server.Addresses[choices.NetworkName].([]interface{}) { address := networkAddresses.(map[string]interface{}) if address["OS-EXT-IPS:type"] == "fixed" { fixedIP := address["addr"].(string) for _, v := range iface.FixedIPs { if fixedIP == v.IPAddress { found = true } } } } th.AssertEquals(t, found, true) } availabilityzones_test.go000066400000000000000000000025511335005366400366770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute availabilityzones package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones" th "github.com/gophercloud/gophercloud/testhelper" ) func TestAvailabilityZonesList(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) allPages, err := availabilityzones.List(client).AllPages() th.AssertNoErr(t, err) availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) th.AssertNoErr(t, err) var found bool for _, zoneInfo := range availabilityZoneInfo { tools.PrintResource(t, zoneInfo) if zoneInfo.ZoneName == "nova" { found = true } } th.AssertEquals(t, found, true) } func TestAvailabilityZonesListDetail(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) allPages, err := availabilityzones.ListDetail(client).AllPages() th.AssertNoErr(t, err) availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) th.AssertNoErr(t, err) var found bool for _, zoneInfo := range availabilityZoneInfo { tools.PrintResource(t, zoneInfo) if zoneInfo.ZoneName == "nova" { found = true } } th.AssertEquals(t, found, true) } bootfromvolume_test.go000066400000000000000000000205301335005366400362220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute bootfromvolume package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" blockstorage "github.com/gophercloud/gophercloud/acceptance/openstack/blockstorage/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach" th "github.com/gophercloud/gophercloud/testhelper" ) func TestBootFromImage(t *testing.T) { clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) blockDevices := []bootfromvolume.BlockDevice{ bootfromvolume.BlockDevice{ BootIndex: 0, DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationLocal, SourceType: bootfromvolume.SourceImage, UUID: choices.ImageID, }, } server, err := CreateBootableVolumeServer(t, client, blockDevices) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) tools.PrintResource(t, server) th.AssertEquals(t, server.Image["id"], choices.ImageID) } func TestBootFromNewVolume(t *testing.T) { clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) blockDevices := []bootfromvolume.BlockDevice{ bootfromvolume.BlockDevice{ DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationVolume, SourceType: bootfromvolume.SourceImage, UUID: choices.ImageID, VolumeSize: 2, }, } server, err := CreateBootableVolumeServer(t, client, blockDevices) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) attachPages, err := volumeattach.List(client, server.ID).AllPages() th.AssertNoErr(t, err) attachments, err := volumeattach.ExtractVolumeAttachments(attachPages) th.AssertNoErr(t, err) tools.PrintResource(t, server) tools.PrintResource(t, attachments) if server.Image != nil { t.Fatalf("server image should be nil") } th.AssertEquals(t, len(attachments), 1) // TODO: volumes_attached extension } func TestBootFromExistingVolume(t *testing.T) { clients.RequireLong(t) computeClient, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) blockStorageClient, err := clients.NewBlockStorageV2Client() th.AssertNoErr(t, err) volume, err := blockstorage.CreateVolumeFromImage(t, blockStorageClient) th.AssertNoErr(t, err) tools.PrintResource(t, volume) blockDevices := []bootfromvolume.BlockDevice{ bootfromvolume.BlockDevice{ DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationVolume, SourceType: bootfromvolume.SourceVolume, UUID: volume.ID, }, } server, err := CreateBootableVolumeServer(t, computeClient, blockDevices) th.AssertNoErr(t, err) defer DeleteServer(t, computeClient, server) attachPages, err := volumeattach.List(computeClient, server.ID).AllPages() th.AssertNoErr(t, err) attachments, err := volumeattach.ExtractVolumeAttachments(attachPages) th.AssertNoErr(t, err) tools.PrintResource(t, server) tools.PrintResource(t, attachments) if server.Image != nil { t.Fatalf("server image should be nil") } th.AssertEquals(t, len(attachments), 1) th.AssertEquals(t, attachments[0].VolumeID, volume.ID) // TODO: volumes_attached extension } func TestBootFromMultiEphemeralServer(t *testing.T) { clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) blockDevices := []bootfromvolume.BlockDevice{ bootfromvolume.BlockDevice{ BootIndex: 0, DestinationType: bootfromvolume.DestinationLocal, DeleteOnTermination: true, SourceType: bootfromvolume.SourceImage, UUID: choices.ImageID, VolumeSize: 5, }, bootfromvolume.BlockDevice{ BootIndex: -1, DestinationType: bootfromvolume.DestinationLocal, DeleteOnTermination: true, GuestFormat: "ext4", SourceType: bootfromvolume.SourceBlank, VolumeSize: 1, }, bootfromvolume.BlockDevice{ BootIndex: -1, DestinationType: bootfromvolume.DestinationLocal, DeleteOnTermination: true, GuestFormat: "ext4", SourceType: bootfromvolume.SourceBlank, VolumeSize: 1, }, } server, err := CreateMultiEphemeralServer(t, client, blockDevices) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) tools.PrintResource(t, server) } func TestAttachNewVolume(t *testing.T) { clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) blockDevices := []bootfromvolume.BlockDevice{ bootfromvolume.BlockDevice{ BootIndex: 0, DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationLocal, SourceType: bootfromvolume.SourceImage, UUID: choices.ImageID, }, bootfromvolume.BlockDevice{ BootIndex: 1, DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationVolume, SourceType: bootfromvolume.SourceBlank, VolumeSize: 2, }, } server, err := CreateBootableVolumeServer(t, client, blockDevices) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) attachPages, err := volumeattach.List(client, server.ID).AllPages() th.AssertNoErr(t, err) attachments, err := volumeattach.ExtractVolumeAttachments(attachPages) th.AssertNoErr(t, err) tools.PrintResource(t, server) tools.PrintResource(t, attachments) th.AssertEquals(t, server.Image["id"], choices.ImageID) th.AssertEquals(t, len(attachments), 1) // TODO: volumes_attached extension } func TestAttachExistingVolume(t *testing.T) { clients.RequireLong(t) computeClient, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) blockStorageClient, err := clients.NewBlockStorageV2Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) volume, err := blockstorage.CreateVolume(t, blockStorageClient) th.AssertNoErr(t, err) blockDevices := []bootfromvolume.BlockDevice{ bootfromvolume.BlockDevice{ BootIndex: 0, DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationLocal, SourceType: bootfromvolume.SourceImage, UUID: choices.ImageID, }, bootfromvolume.BlockDevice{ BootIndex: 1, DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationVolume, SourceType: bootfromvolume.SourceVolume, UUID: volume.ID, }, } server, err := CreateBootableVolumeServer(t, computeClient, blockDevices) th.AssertNoErr(t, err) defer DeleteServer(t, computeClient, server) attachPages, err := volumeattach.List(computeClient, server.ID).AllPages() th.AssertNoErr(t, err) attachments, err := volumeattach.ExtractVolumeAttachments(attachPages) th.AssertNoErr(t, err) tools.PrintResource(t, server) tools.PrintResource(t, attachments) th.AssertEquals(t, server.Image["id"], choices.ImageID) th.AssertEquals(t, len(attachments), 1) th.AssertEquals(t, attachments[0].VolumeID, volume.ID) // TODO: volumes_attached extension } func TestBootFromNewCustomizedVolume(t *testing.T) { if testing.Short() { t.Skip("Skipping test that requires server creation in short mode.") } client, err := clients.NewComputeV2Client() if err != nil { t.Fatalf("Unable to create a compute client: %v", err) } choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatal(err) } blockDevices := []bootfromvolume.BlockDevice{ bootfromvolume.BlockDevice{ DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationVolume, SourceType: bootfromvolume.SourceImage, UUID: choices.ImageID, VolumeSize: 2, DeviceType: "disk", DiskBus: "virtio", }, } server, err := CreateBootableVolumeServer(t, client, blockDevices) if err != nil { t.Fatalf("Unable to create server: %v", err) } defer DeleteServer(t, client, server) tools.PrintResource(t, server) } compute.go000066400000000000000000000776231335005366400335770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// Package v2 contains common functions for creating compute-based resources // for use in acceptance tests. See the `*_test.go` files for example usages. package v2 import ( "crypto/rand" "crypto/rsa" "fmt" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume" dsr "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/defsecrules" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/networks" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/rescueunrescue" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach" "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates" "golang.org/x/crypto/ssh" ) // AssociateFloatingIP will associate a floating IP with an instance. An error // will be returned if the floating IP was unable to be associated. func AssociateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server) error { associateOpts := floatingips.AssociateOpts{ FloatingIP: floatingIP.IP, } t.Logf("Attempting to associate floating IP %s to instance %s", floatingIP.IP, server.ID) err := floatingips.AssociateInstance(client, server.ID, associateOpts).ExtractErr() if err != nil { return err } return nil } // AssociateFloatingIPWithFixedIP will associate a floating IP with an // instance's specific fixed IP. An error will be returend if the floating IP // was unable to be associated. func AssociateFloatingIPWithFixedIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server, fixedIP string) error { associateOpts := floatingips.AssociateOpts{ FloatingIP: floatingIP.IP, FixedIP: fixedIP, } t.Logf("Attempting to associate floating IP %s to fixed IP %s on instance %s", floatingIP.IP, fixedIP, server.ID) err := floatingips.AssociateInstance(client, server.ID, associateOpts).ExtractErr() if err != nil { return err } return nil } // AttachInterface will create and attach an interface on a given server. // An error will returned if the interface could not be created. func AttachInterface(t *testing.T, client *gophercloud.ServiceClient, serverID string) (*attachinterfaces.Interface, error) { t.Logf("Attempting to attach interface to server %s", serverID) choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatal(err) } networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) if err != nil { return nil, err } createOpts := attachinterfaces.CreateOpts{ NetworkID: networkID, } iface, err := attachinterfaces.Create(client, serverID, createOpts).Extract() if err != nil { return nil, err } t.Logf("Successfully created interface %s on server %s", iface.PortID, serverID) return iface, nil } // CreateAggregate will create an aggregate with random name and available zone. // An error will be returned if the aggregate could not be created. func CreateAggregate(t *testing.T, client *gophercloud.ServiceClient) (*aggregates.Aggregate, error) { aggregateName := tools.RandomString("aggregate_", 5) availabilityZone := tools.RandomString("zone_", 5) t.Logf("Attempting to create aggregate %s", aggregateName) createOpts := aggregates.CreateOpts{ Name: aggregateName, AvailabilityZone: availabilityZone, } aggregate, err := aggregates.Create(client, createOpts).Extract() if err != nil { return nil, err } t.Logf("Successfully created aggregate %d", aggregate.ID) aggregate, err = aggregates.Get(client, aggregate.ID).Extract() if err != nil { return nil, err } th.AssertEquals(t, aggregate.Name, aggregateName) th.AssertEquals(t, aggregate.AvailabilityZone, availabilityZone) return aggregate, nil } // CreateBootableVolumeServer works like CreateServer but is configured with // one or more block devices defined by passing in []bootfromvolume.BlockDevice. // An error will be returned if a server was unable to be created. func CreateBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []bootfromvolume.BlockDevice) (*servers.Server, error) { var server *servers.Server choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatal(err) } networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) if err != nil { return server, err } name := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create bootable volume server: %s", name) serverCreateOpts := servers.CreateOpts{ Name: name, FlavorRef: choices.FlavorID, Networks: []servers.Network{ servers.Network{UUID: networkID}, }, } if blockDevices[0].SourceType == bootfromvolume.SourceImage && blockDevices[0].DestinationType == bootfromvolume.DestinationLocal { serverCreateOpts.ImageRef = blockDevices[0].UUID } server, err = bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, BlockDevice: blockDevices, }).Extract() if err != nil { return server, err } if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { return server, err } newServer, err := servers.Get(client, server.ID).Extract() if err != nil { return nil, err } th.AssertEquals(t, newServer.Name, name) th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) return newServer, nil } // CreateDefaultRule will create a default security group rule with a // random port range between 80 and 90. An error will be returned if // a default rule was unable to be created. func CreateDefaultRule(t *testing.T, client *gophercloud.ServiceClient) (dsr.DefaultRule, error) { createOpts := dsr.CreateOpts{ FromPort: tools.RandomInt(80, 89), ToPort: tools.RandomInt(90, 99), IPProtocol: "TCP", CIDR: "0.0.0.0/0", } defaultRule, err := dsr.Create(client, createOpts).Extract() if err != nil { return *defaultRule, err } t.Logf("Created default rule: %s", defaultRule.ID) return *defaultRule, nil } // CreateFlavor will create a flavor with a random name. // An error will be returned if the flavor could not be created. func CreateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flavors.Flavor, error) { flavorName := tools.RandomString("flavor_", 5) t.Logf("Attempting to create flavor %s", flavorName) isPublic := true createOpts := flavors.CreateOpts{ Name: flavorName, RAM: 1, VCPUs: 1, Disk: gophercloud.IntToPointer(1), IsPublic: &isPublic, } flavor, err := flavors.Create(client, createOpts).Extract() if err != nil { return nil, err } t.Logf("Successfully created flavor %s", flavor.ID) th.AssertEquals(t, flavor.Name, flavorName) th.AssertEquals(t, flavor.RAM, 1) th.AssertEquals(t, flavor.Disk, 1) th.AssertEquals(t, flavor.VCPUs, 1) th.AssertEquals(t, flavor.IsPublic, true) return flavor, nil } // CreateFloatingIP will allocate a floating IP. // An error will be returend if one was unable to be allocated. func CreateFloatingIP(t *testing.T, client *gophercloud.ServiceClient) (*floatingips.FloatingIP, error) { choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatal(err) } createOpts := floatingips.CreateOpts{ Pool: choices.FloatingIPPoolName, } floatingIP, err := floatingips.Create(client, createOpts).Extract() if err != nil { return floatingIP, err } t.Logf("Created floating IP: %s", floatingIP.ID) return floatingIP, nil } func createKey() (string, error) { privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return "", err } publicKey := privateKey.PublicKey pub, err := ssh.NewPublicKey(&publicKey) if err != nil { return "", err } pubBytes := ssh.MarshalAuthorizedKey(pub) pk := string(pubBytes) return pk, nil } // CreateKeyPair will create a KeyPair with a random name. An error will occur // if the keypair failed to be created. An error will be returned if the // keypair was unable to be created. func CreateKeyPair(t *testing.T, client *gophercloud.ServiceClient) (*keypairs.KeyPair, error) { keyPairName := tools.RandomString("keypair_", 5) t.Logf("Attempting to create keypair: %s", keyPairName) createOpts := keypairs.CreateOpts{ Name: keyPairName, } keyPair, err := keypairs.Create(client, createOpts).Extract() if err != nil { return keyPair, err } t.Logf("Created keypair: %s", keyPairName) th.AssertEquals(t, keyPair.Name, keyPairName) return keyPair, nil } // CreateMultiEphemeralServer works like CreateServer but is configured with // one or more block devices defined by passing in []bootfromvolume.BlockDevice. // These block devices act like block devices when booting from a volume but // are actually local ephemeral disks. // An error will be returned if a server was unable to be created. func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []bootfromvolume.BlockDevice) (*servers.Server, error) { var server *servers.Server choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatal(err) } networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) if err != nil { return server, err } name := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create bootable volume server: %s", name) serverCreateOpts := servers.CreateOpts{ Name: name, FlavorRef: choices.FlavorID, ImageRef: choices.ImageID, Networks: []servers.Network{ servers.Network{UUID: networkID}, }, } server, err = bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, BlockDevice: blockDevices, }).Extract() if err != nil { return server, err } if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { return server, err } newServer, err := servers.Get(client, server.ID).Extract() th.AssertEquals(t, newServer.Name, name) th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) th.AssertEquals(t, newServer.Image["id"], choices.ImageID) return newServer, nil } // CreatePrivateFlavor will create a private flavor with a random name. // An error will be returned if the flavor could not be created. func CreatePrivateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flavors.Flavor, error) { flavorName := tools.RandomString("flavor_", 5) t.Logf("Attempting to create flavor %s", flavorName) isPublic := false createOpts := flavors.CreateOpts{ Name: flavorName, RAM: 1, VCPUs: 1, Disk: gophercloud.IntToPointer(1), IsPublic: &isPublic, } flavor, err := flavors.Create(client, createOpts).Extract() if err != nil { return nil, err } t.Logf("Successfully created flavor %s", flavor.ID) th.AssertEquals(t, flavor.Name, flavorName) th.AssertEquals(t, flavor.RAM, 1) th.AssertEquals(t, flavor.Disk, 1) th.AssertEquals(t, flavor.VCPUs, 1) th.AssertEquals(t, flavor.IsPublic, false) return flavor, nil } // CreateSecurityGroup will create a security group with a random name. // An error will be returned if one was failed to be created. func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (*secgroups.SecurityGroup, error) { name := tools.RandomString("secgroup_", 5) createOpts := secgroups.CreateOpts{ Name: name, Description: "something", } securityGroup, err := secgroups.Create(client, createOpts).Extract() if err != nil { return nil, err } t.Logf("Created security group: %s", securityGroup.ID) th.AssertEquals(t, securityGroup.Name, name) return securityGroup, nil } // CreateSecurityGroupRule will create a security group rule with a random name // and a random TCP port range between port 80 and 99. An error will be // returned if the rule failed to be created. func CreateSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, securityGroupID string) (*secgroups.Rule, error) { fromPort := tools.RandomInt(80, 89) toPort := tools.RandomInt(90, 99) createOpts := secgroups.CreateRuleOpts{ ParentGroupID: securityGroupID, FromPort: fromPort, ToPort: toPort, IPProtocol: "TCP", CIDR: "0.0.0.0/0", } rule, err := secgroups.CreateRule(client, createOpts).Extract() if err != nil { return nil, err } t.Logf("Created security group rule: %s", rule.ID) th.AssertEquals(t, rule.FromPort, fromPort) th.AssertEquals(t, rule.ToPort, toPort) th.AssertEquals(t, rule.ParentGroupID, securityGroupID) return rule, nil } // CreateServer creates a basic instance with a randomly generated name. // The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable. // The image will be the value of the OS_IMAGE_ID environment variable. // The instance will be launched on the network specified in OS_NETWORK_NAME. // An error will be returned if the instance was unable to be created. func CreateServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) { choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatal(err) } networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) if err != nil { return nil, err } name := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create server: %s", name) pwd := tools.MakeNewPassword("") server, err := servers.Create(client, servers.CreateOpts{ Name: name, FlavorRef: choices.FlavorID, ImageRef: choices.ImageID, AdminPass: pwd, Networks: []servers.Network{ servers.Network{UUID: networkID}, }, Metadata: map[string]string{ "abc": "def", }, Personality: servers.Personality{ &servers.File{ Path: "/etc/test", Contents: []byte("hello world"), }, }, }).Extract() if err != nil { return server, err } if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { return nil, err } newServer, err := servers.Get(client, server.ID).Extract() if err != nil { return nil, err } th.AssertEquals(t, newServer.Name, name) th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) th.AssertEquals(t, newServer.Image["id"], choices.ImageID) return newServer, nil } // CreateServerWithoutImageRef creates a basic instance with a randomly generated name. // The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable. // The image is intentionally missing to trigger an error. // The instance will be launched on the network specified in OS_NETWORK_NAME. // An error will be returned if the instance was unable to be created. func CreateServerWithoutImageRef(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) { choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatal(err) } networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) if err != nil { return nil, err } name := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create server: %s", name) pwd := tools.MakeNewPassword("") server, err := servers.Create(client, servers.CreateOpts{ Name: name, FlavorRef: choices.FlavorID, AdminPass: pwd, Networks: []servers.Network{ servers.Network{UUID: networkID}, }, Personality: servers.Personality{ &servers.File{ Path: "/etc/test", Contents: []byte("hello world"), }, }, }).Extract() if err != nil { return nil, err } if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { return nil, err } return server, nil } // CreateServerGroup will create a server with a random name. An error will be // returned if the server group failed to be created. func CreateServerGroup(t *testing.T, client *gophercloud.ServiceClient, policy string) (*servergroups.ServerGroup, error) { name := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create server group %s", name) sg, err := servergroups.Create(client, &servergroups.CreateOpts{ Name: name, Policies: []string{policy}, }).Extract() if err != nil { return nil, err } t.Logf("Successfully created server group %s", name) th.AssertEquals(t, sg.Name, name) return sg, nil } // CreateServerInServerGroup works like CreateServer but places the instance in // a specified Server Group. func CreateServerInServerGroup(t *testing.T, client *gophercloud.ServiceClient, serverGroup *servergroups.ServerGroup) (*servers.Server, error) { choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatal(err) } networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) if err != nil { return nil, err } name := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create server: %s", name) pwd := tools.MakeNewPassword("") serverCreateOpts := servers.CreateOpts{ Name: name, FlavorRef: choices.FlavorID, ImageRef: choices.ImageID, AdminPass: pwd, Networks: []servers.Network{ servers.Network{UUID: networkID}, }, } schedulerHintsOpts := schedulerhints.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, SchedulerHints: schedulerhints.SchedulerHints{ Group: serverGroup.ID, }, } server, err := servers.Create(client, schedulerHintsOpts).Extract() if err != nil { return nil, err } if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { return nil, err } newServer, err := servers.Get(client, server.ID).Extract() if err != nil { return nil, err } th.AssertEquals(t, newServer.Name, name) th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) th.AssertEquals(t, newServer.Image["id"], choices.ImageID) return newServer, nil } // CreateServerWithPublicKey works the same as CreateServer, but additionally // configures the server with a specified Key Pair name. func CreateServerWithPublicKey(t *testing.T, client *gophercloud.ServiceClient, keyPairName string) (*servers.Server, error) { choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatal(err) } networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) if err != nil { return nil, err } name := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create server: %s", name) serverCreateOpts := servers.CreateOpts{ Name: name, FlavorRef: choices.FlavorID, ImageRef: choices.ImageID, Networks: []servers.Network{ servers.Network{UUID: networkID}, }, } server, err := servers.Create(client, keypairs.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, KeyName: keyPairName, }).Extract() if err != nil { return nil, err } if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { return nil, err } newServer, err := servers.Get(client, server.ID).Extract() if err != nil { return nil, err } th.AssertEquals(t, newServer.Name, name) th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) th.AssertEquals(t, newServer.Image["id"], choices.ImageID) return newServer, nil } // CreateVolumeAttachment will attach a volume to a server. An error will be // returned if the volume failed to attach. func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, server *servers.Server, volume *volumes.Volume) (*volumeattach.VolumeAttachment, error) { volumeAttachOptions := volumeattach.CreateOpts{ VolumeID: volume.ID, } t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID) volumeAttachment, err := volumeattach.Create(client, server.ID, volumeAttachOptions).Extract() if err != nil { return volumeAttachment, err } if err := volumes.WaitForStatus(blockClient, volume.ID, "in-use", 60); err != nil { return volumeAttachment, err } return volumeAttachment, nil } // DeleteAggregate will delete a given host aggregate. A fatal error will occur if // the aggregate deleting is failed. This works best when using it as a // deferred function. func DeleteAggregate(t *testing.T, client *gophercloud.ServiceClient, aggregate *aggregates.Aggregate) { err := aggregates.Delete(client, aggregate.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete aggregate %d", aggregate.ID) } t.Logf("Deleted aggregate: %d", aggregate.ID) } // DeleteDefaultRule deletes a default security group rule. // A fatal error will occur if the rule failed to delete. This works best when // using it as a deferred function. func DeleteDefaultRule(t *testing.T, client *gophercloud.ServiceClient, defaultRule dsr.DefaultRule) { err := dsr.Delete(client, defaultRule.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete default rule %s: %v", defaultRule.ID, err) } t.Logf("Deleted default rule: %s", defaultRule.ID) } // DeleteFlavor will delete a flavor. A fatal error will occur if the flavor // could not be deleted. This works best when using it as a deferred function. func DeleteFlavor(t *testing.T, client *gophercloud.ServiceClient, flavor *flavors.Flavor) { err := flavors.Delete(client, flavor.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete flavor %s", flavor.ID) } t.Logf("Deleted flavor: %s", flavor.ID) } // DeleteFloatingIP will de-allocate a floating IP. A fatal error will occur if // the floating IP failed to de-allocate. This works best when using it as a // deferred function. func DeleteFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP) { err := floatingips.Delete(client, floatingIP.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete floating IP %s: %v", floatingIP.ID, err) } t.Logf("Deleted floating IP: %s", floatingIP.ID) } // DeleteKeyPair will delete a specified keypair. A fatal error will occur if // the keypair failed to be deleted. This works best when used as a deferred // function. func DeleteKeyPair(t *testing.T, client *gophercloud.ServiceClient, keyPair *keypairs.KeyPair) { err := keypairs.Delete(client, keyPair.Name).ExtractErr() if err != nil { t.Fatalf("Unable to delete keypair %s: %v", keyPair.Name, err) } t.Logf("Deleted keypair: %s", keyPair.Name) } // DeleteSecurityGroup will delete a security group. A fatal error will occur // if the group failed to be deleted. This works best as a deferred function. func DeleteSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, securityGroupID string) { err := secgroups.Delete(client, securityGroupID).ExtractErr() if err != nil { t.Fatalf("Unable to delete security group %s: %s", securityGroupID, err) } t.Logf("Deleted security group: %s", securityGroupID) } // DeleteSecurityGroupRule will delete a security group rule. A fatal error // will occur if the rule failed to be deleted. This works best when used // as a deferred function. func DeleteSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, ruleID string) { err := secgroups.DeleteRule(client, ruleID).ExtractErr() if err != nil { t.Fatalf("Unable to delete rule: %v", err) } t.Logf("Deleted security group rule: %s", ruleID) } // DeleteServer deletes an instance via its UUID. // A fatal error will occur if the instance failed to be destroyed. This works // best when using it as a deferred function. func DeleteServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) { err := servers.Delete(client, server.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete server %s: %s", server.ID, err) } if err := WaitForComputeStatus(client, server, "DELETED"); err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { t.Logf("Deleted server: %s", server.ID) return } t.Fatalf("Error deleting server %s: %s", server.ID, err) } // If we reach this point, the API returned an actual DELETED status // which is a very short window of time, but happens occasionally. t.Logf("Deleted server: %s", server.ID) } // DeleteServerGroup will delete a server group. A fatal error will occur if // the server group failed to be deleted. This works best when used as a // deferred function. func DeleteServerGroup(t *testing.T, client *gophercloud.ServiceClient, serverGroup *servergroups.ServerGroup) { err := servergroups.Delete(client, serverGroup.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete server group %s: %v", serverGroup.ID, err) } t.Logf("Deleted server group %s", serverGroup.ID) } // DeleteVolumeAttachment will disconnect a volume from an instance. A fatal // error will occur if the volume failed to detach. This works best when used // as a deferred function. func DeleteVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, server *servers.Server, volumeAttachment *volumeattach.VolumeAttachment) { err := volumeattach.Delete(client, server.ID, volumeAttachment.VolumeID).ExtractErr() if err != nil { t.Fatalf("Unable to detach volume: %v", err) } if err := volumes.WaitForStatus(blockClient, volumeAttachment.ID, "available", 60); err != nil { t.Fatalf("Unable to wait for volume: %v", err) } t.Logf("Deleted volume: %s", volumeAttachment.VolumeID) } // DetachInterface will detach an interface from a server. A fatal // error will occur if the interface could not be detached. This works best // when used as a deferred function. func DetachInterface(t *testing.T, client *gophercloud.ServiceClient, serverID, portID string) { t.Logf("Attempting to detach interface %s from server %s", portID, serverID) err := attachinterfaces.Delete(client, serverID, portID).ExtractErr() if err != nil { t.Fatalf("Unable to detach interface %s from server %s", portID, serverID) } t.Logf("Detached interface %s from server %s", portID, serverID) } // DisassociateFloatingIP will disassociate a floating IP from an instance. A // fatal error will occur if the floating IP failed to disassociate. This works // best when using it as a deferred function. func DisassociateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server) { disassociateOpts := floatingips.DisassociateOpts{ FloatingIP: floatingIP.IP, } err := floatingips.DisassociateInstance(client, server.ID, disassociateOpts).ExtractErr() if err != nil { t.Fatalf("Unable to disassociate floating IP %s from server %s: %v", floatingIP.IP, server.ID, err) } t.Logf("Disassociated floating IP %s from server %s", floatingIP.IP, server.ID) } // GetNetworkIDFromNetworks will return the network ID from a specified network // UUID using the os-networks API extension. An error will be returned if the // network could not be retrieved. func GetNetworkIDFromNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) { allPages, err := networks.List(client).AllPages() if err != nil { t.Fatalf("Unable to list networks: %v", err) } networkList, err := networks.ExtractNetworks(allPages) if err != nil { t.Fatalf("Unable to list networks: %v", err) } networkID := "" for _, network := range networkList { t.Logf("Network: %v", network) if network.Label == networkName { networkID = network.ID } } t.Logf("Found network ID for %s: %s", networkName, networkID) return networkID, nil } // GetNetworkIDFromTenantNetworks will return the network UUID for a given // network name using the os-tenant-networks API extension. An error will be // returned if the network could not be retrieved. func GetNetworkIDFromTenantNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) { allPages, err := tenantnetworks.List(client).AllPages() if err != nil { return "", err } allTenantNetworks, err := tenantnetworks.ExtractNetworks(allPages) if err != nil { return "", err } for _, network := range allTenantNetworks { if network.Name == networkName { return network.ID, nil } } return "", fmt.Errorf("Failed to obtain network ID for network %s", networkName) } // ImportPublicKey will create a KeyPair with a random name and a specified // public key. An error will be returned if the keypair failed to be created. func ImportPublicKey(t *testing.T, client *gophercloud.ServiceClient, publicKey string) (*keypairs.KeyPair, error) { keyPairName := tools.RandomString("keypair_", 5) t.Logf("Attempting to create keypair: %s", keyPairName) createOpts := keypairs.CreateOpts{ Name: keyPairName, PublicKey: publicKey, } keyPair, err := keypairs.Create(client, createOpts).Extract() if err != nil { return keyPair, err } t.Logf("Created keypair: %s", keyPairName) th.AssertEquals(t, keyPair.Name, keyPairName) th.AssertEquals(t, keyPair.PublicKey, publicKey) return keyPair, nil } // ResizeServer performs a resize action on an instance. An error will be // returned if the instance failed to resize. // The new flavor that the instance will be resized to is specified in OS_FLAVOR_ID_RESIZE. func ResizeServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) error { choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatal(err) } opts := &servers.ResizeOpts{ FlavorRef: choices.FlavorIDResize, } if res := servers.Resize(client, server.ID, opts); res.Err != nil { return res.Err } if err := WaitForComputeStatus(client, server, "VERIFY_RESIZE"); err != nil { return err } return nil } // WaitForComputeStatus will poll an instance's status until it either matches // the specified status or the status becomes ERROR. func WaitForComputeStatus(client *gophercloud.ServiceClient, server *servers.Server, status string) error { return tools.WaitFor(func() (bool, error) { latest, err := servers.Get(client, server.ID).Extract() if err != nil { return false, err } if latest.Status == status { // Success! return true, nil } if latest.Status == "ERROR" { return false, fmt.Errorf("Instance in ERROR state") } return false, nil }) } //Convenience method to fill an QuotaSet-UpdateOpts-struct from a QuotaSet-struct func FillUpdateOptsFromQuotaSet(src quotasets.QuotaSet, dest *quotasets.UpdateOpts) { dest.FixedIPs = &src.FixedIPs dest.FloatingIPs = &src.FloatingIPs dest.InjectedFileContentBytes = &src.InjectedFileContentBytes dest.InjectedFilePathBytes = &src.InjectedFilePathBytes dest.InjectedFiles = &src.InjectedFiles dest.KeyPairs = &src.KeyPairs dest.RAM = &src.RAM dest.SecurityGroupRules = &src.SecurityGroupRules dest.SecurityGroups = &src.SecurityGroups dest.Cores = &src.Cores dest.Instances = &src.Instances dest.ServerGroups = &src.ServerGroups dest.ServerGroupMembers = &src.ServerGroupMembers dest.MetadataItems = &src.MetadataItems } // RescueServer will place the specified server into rescue mode. func RescueServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) error { t.Logf("Attempting to put server %s into rescue mode", server.ID) _, err := rescueunrescue.Rescue(client, server.ID, rescueunrescue.RescueOpts{}).Extract() if err != nil { return err } if err := WaitForComputeStatus(client, server, "RESCUE"); err != nil { return err } return nil } // UnrescueServer will return server from rescue mode. func UnrescueServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) error { t.Logf("Attempting to return server %s from rescue mode", server.ID) if err := rescueunrescue.Unrescue(client, server.ID).ExtractErr(); err != nil { return err } if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { return err } return nil } defsecrules_test.go000066400000000000000000000027341335005366400354550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute defsecrules package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" dsr "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/defsecrules" th "github.com/gophercloud/gophercloud/testhelper" ) func TestDefSecRulesList(t *testing.T) { clients.RequireAdmin(t) clients.RequireNovaNetwork(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) allPages, err := dsr.List(client).AllPages() th.AssertNoErr(t, err) allDefaultRules, err := dsr.ExtractDefaultRules(allPages) th.AssertNoErr(t, err) for _, defaultRule := range allDefaultRules { tools.PrintResource(t, defaultRule) } } func TestDefSecRulesCreate(t *testing.T) { clients.RequireAdmin(t) clients.RequireNovaNetwork(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) defaultRule, err := CreateDefaultRule(t, client) th.AssertNoErr(t, err) defer DeleteDefaultRule(t, client, defaultRule) tools.PrintResource(t, defaultRule) } func TestDefSecRulesGet(t *testing.T) { clients.RequireAdmin(t) clients.RequireNovaNetwork(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) defaultRule, err := CreateDefaultRule(t, client) th.AssertNoErr(t, err) defer DeleteDefaultRule(t, client, defaultRule) newDefaultRule, err := dsr.Get(client, defaultRule.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newDefaultRule) } extension_test.go000066400000000000000000000020641335005366400351610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute extensions package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/common/extensions" th "github.com/gophercloud/gophercloud/testhelper" ) func TestExtensionsList(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) allPages, err := extensions.List(client).AllPages() th.AssertNoErr(t, err) allExtensions, err := extensions.ExtractExtensions(allPages) th.AssertNoErr(t, err) var found bool for _, extension := range allExtensions { tools.PrintResource(t, extension) if extension.Name == "SchedulerHints" { found = true } } th.AssertEquals(t, found, true) } func TestExtensionsGet(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) extension, err := extensions.Get(client, "os-admin-actions").Extract() th.AssertNoErr(t, err) tools.PrintResource(t, extension) th.AssertEquals(t, extension.Name, "AdminActions") } flavors_test.go000066400000000000000000000124611335005366400346230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute flavors package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" th "github.com/gophercloud/gophercloud/testhelper" identity "github.com/gophercloud/gophercloud/acceptance/openstack/identity/v3" ) func TestFlavorsList(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) allPages, err := flavors.ListDetail(client, nil).AllPages() th.AssertNoErr(t, err) allFlavors, err := flavors.ExtractFlavors(allPages) th.AssertNoErr(t, err) var found bool for _, flavor := range allFlavors { tools.PrintResource(t, flavor) if flavor.ID == choices.FlavorID { found = true } } th.AssertEquals(t, found, true) } func TestFlavorsAccessTypeList(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) flavorAccessTypes := map[string]flavors.AccessType{ "public": flavors.PublicAccess, "private": flavors.PrivateAccess, "all": flavors.AllAccess, } for flavorTypeName, flavorAccessType := range flavorAccessTypes { t.Logf("** %s flavors: **", flavorTypeName) allPages, err := flavors.ListDetail(client, flavors.ListOpts{AccessType: flavorAccessType}).AllPages() th.AssertNoErr(t, err) allFlavors, err := flavors.ExtractFlavors(allPages) th.AssertNoErr(t, err) for _, flavor := range allFlavors { tools.PrintResource(t, flavor) } } } func TestFlavorsGet(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) flavor, err := flavors.Get(client, choices.FlavorID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, flavor) th.AssertEquals(t, flavor.ID, choices.FlavorID) } func TestFlavorsCreateDelete(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) flavor, err := CreateFlavor(t, client) th.AssertNoErr(t, err) defer DeleteFlavor(t, client, flavor) tools.PrintResource(t, flavor) } func TestFlavorsAccessesList(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) flavor, err := CreatePrivateFlavor(t, client) th.AssertNoErr(t, err) defer DeleteFlavor(t, client, flavor) allPages, err := flavors.ListAccesses(client, flavor.ID).AllPages() th.AssertNoErr(t, err) allAccesses, err := flavors.ExtractAccesses(allPages) th.AssertNoErr(t, err) th.AssertEquals(t, len(allAccesses), 0) } func TestFlavorsAccessCRUD(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) identityClient, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) project, err := identity.CreateProject(t, identityClient, nil) th.AssertNoErr(t, err) defer identity.DeleteProject(t, identityClient, project.ID) flavor, err := CreatePrivateFlavor(t, client) th.AssertNoErr(t, err) defer DeleteFlavor(t, client, flavor) addAccessOpts := flavors.AddAccessOpts{ Tenant: project.ID, } accessList, err := flavors.AddAccess(client, flavor.ID, addAccessOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, len(accessList), 1) th.AssertEquals(t, accessList[0].TenantID, project.ID) th.AssertEquals(t, accessList[0].FlavorID, flavor.ID) for _, access := range accessList { tools.PrintResource(t, access) } removeAccessOpts := flavors.RemoveAccessOpts{ Tenant: project.ID, } accessList, err = flavors.RemoveAccess(client, flavor.ID, removeAccessOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, len(accessList), 0) } func TestFlavorsExtraSpecsCRUD(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) flavor, err := CreatePrivateFlavor(t, client) th.AssertNoErr(t, err) defer DeleteFlavor(t, client, flavor) createOpts := flavors.ExtraSpecsOpts{ "hw:cpu_policy": "CPU-POLICY", "hw:cpu_thread_policy": "CPU-THREAD-POLICY", } createdExtraSpecs, err := flavors.CreateExtraSpecs(client, flavor.ID, createOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, createdExtraSpecs) th.AssertEquals(t, len(createdExtraSpecs), 2) th.AssertEquals(t, createdExtraSpecs["hw:cpu_policy"], "CPU-POLICY") th.AssertEquals(t, createdExtraSpecs["hw:cpu_thread_policy"], "CPU-THREAD-POLICY") err = flavors.DeleteExtraSpec(client, flavor.ID, "hw:cpu_policy").ExtractErr() th.AssertNoErr(t, err) updateOpts := flavors.ExtraSpecsOpts{ "hw:cpu_thread_policy": "CPU-THREAD-POLICY-BETTER", } updatedExtraSpec, err := flavors.UpdateExtraSpec(client, flavor.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedExtraSpec) allExtraSpecs, err := flavors.ListExtraSpecs(client, flavor.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, allExtraSpecs) th.AssertEquals(t, len(allExtraSpecs), 1) th.AssertEquals(t, allExtraSpecs["hw:cpu_thread_policy"], "CPU-THREAD-POLICY-BETTER") spec, err := flavors.GetExtraSpec(client, flavor.ID, "hw:cpu_thread_policy").Extract() th.AssertNoErr(t, err) tools.PrintResource(t, spec) th.AssertEquals(t, spec["hw:cpu_thread_policy"], "CPU-THREAD-POLICY-BETTER") } floatingip_test.go000066400000000000000000000064651335005366400353120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute servers package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" ) func TestFloatingIPsCreateDelete(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) floatingIP, err := CreateFloatingIP(t, client) th.AssertNoErr(t, err) defer DeleteFloatingIP(t, client, floatingIP) tools.PrintResource(t, floatingIP) allPages, err := floatingips.List(client).AllPages() th.AssertNoErr(t, err) allFloatingIPs, err := floatingips.ExtractFloatingIPs(allPages) th.AssertNoErr(t, err) var found bool for _, fip := range allFloatingIPs { tools.PrintResource(t, floatingIP) if fip.ID == floatingIP.ID { found = true } } th.AssertEquals(t, found, true) fip, err := floatingips.Get(client, floatingIP.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, floatingIP.ID, fip.ID) } func TestFloatingIPsAssociate(t *testing.T) { clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) floatingIP, err := CreateFloatingIP(t, client) th.AssertNoErr(t, err) defer DeleteFloatingIP(t, client, floatingIP) tools.PrintResource(t, floatingIP) err = AssociateFloatingIP(t, client, floatingIP, server) th.AssertNoErr(t, err) defer DisassociateFloatingIP(t, client, floatingIP, server) newFloatingIP, err := floatingips.Get(client, floatingIP.ID).Extract() th.AssertNoErr(t, err) t.Logf("Floating IP %s is associated with Fixed IP %s", floatingIP.IP, newFloatingIP.FixedIP) tools.PrintResource(t, newFloatingIP) th.AssertEquals(t, newFloatingIP.InstanceID, server.ID) } func TestFloatingIPsFixedIPAssociate(t *testing.T) { clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) newServer, err := servers.Get(client, server.ID).Extract() th.AssertNoErr(t, err) floatingIP, err := CreateFloatingIP(t, client) th.AssertNoErr(t, err) defer DeleteFloatingIP(t, client, floatingIP) tools.PrintResource(t, floatingIP) var fixedIP string for _, networkAddresses := range newServer.Addresses[choices.NetworkName].([]interface{}) { address := networkAddresses.(map[string]interface{}) if address["OS-EXT-IPS:type"] == "fixed" { if address["version"].(float64) == 4 { fixedIP = address["addr"].(string) } } } err = AssociateFloatingIPWithFixedIP(t, client, floatingIP, newServer, fixedIP) th.AssertNoErr(t, err) defer DisassociateFloatingIP(t, client, floatingIP, newServer) newFloatingIP, err := floatingips.Get(client, floatingIP.ID).Extract() th.AssertNoErr(t, err) t.Logf("Floating IP %s is associated with Fixed IP %s", floatingIP.IP, newFloatingIP.FixedIP) tools.PrintResource(t, newFloatingIP) th.AssertEquals(t, newFloatingIP.InstanceID, server.ID) th.AssertEquals(t, newFloatingIP.FixedIP, fixedIP) } hypervisors_test.go000066400000000000000000000043601335005366400355430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute hypervisors package v2 import ( "fmt" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/hypervisors" th "github.com/gophercloud/gophercloud/testhelper" ) func TestHypervisorsList(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) allPages, err := hypervisors.List(client).AllPages() th.AssertNoErr(t, err) allHypervisors, err := hypervisors.ExtractHypervisors(allPages) th.AssertNoErr(t, err) for _, h := range allHypervisors { tools.PrintResource(t, h) } } func TestHypervisorsGet(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) hypervisorID, err := getHypervisorID(t, client) th.AssertNoErr(t, err) hypervisor, err := hypervisors.Get(client, hypervisorID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, hypervisor) th.AssertEquals(t, hypervisorID, hypervisor.ID) } func TestHypervisorsGetStatistics(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) hypervisorsStats, err := hypervisors.GetStatistics(client).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, hypervisorsStats) if hypervisorsStats.Count == 0 { t.Fatalf("Unable to get hypervisor stats") } } func TestHypervisorsGetUptime(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) hypervisorID, err := getHypervisorID(t, client) th.AssertNoErr(t, err) hypervisor, err := hypervisors.GetUptime(client, hypervisorID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, hypervisor) th.AssertEquals(t, hypervisorID, hypervisor.ID) } func getHypervisorID(t *testing.T, client *gophercloud.ServiceClient) (int, error) { allPages, err := hypervisors.List(client).AllPages() th.AssertNoErr(t, err) allHypervisors, err := hypervisors.ExtractHypervisors(allPages) th.AssertNoErr(t, err) if len(allHypervisors) > 0 { return allHypervisors[0].ID, nil } return 0, fmt.Errorf("Unable to get hypervisor ID") } images_test.go000066400000000000000000000022361335005366400344130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute images package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/images" th "github.com/gophercloud/gophercloud/testhelper" ) func TestImagesList(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) allPages, err := images.ListDetail(client, nil).AllPages() th.AssertNoErr(t, err) allImages, err := images.ExtractImages(allPages) th.AssertNoErr(t, err) var found bool for _, image := range allImages { tools.PrintResource(t, image) if image.ID == choices.ImageID { found = true } } th.AssertEquals(t, found, true) } func TestImagesGet(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) image, err := images.Get(client, choices.ImageID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, image) th.AssertEquals(t, choices.ImageID, image.ID) } keypairs_test.go000066400000000000000000000047711335005366400350030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute keypairs package v2 import ( "testing" "golang.org/x/crypto/ssh" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" ) const keyName = "gophercloud_test_key_pair" func TestKeypairsParse(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) keyPair, err := CreateKeyPair(t, client) th.AssertNoErr(t, err) defer DeleteKeyPair(t, client, keyPair) // There was a series of OpenStack releases, between Liberty and Ocata, // where the returned SSH key was not parsable by Go. // This checks if the issue is happening again. _, err = ssh.ParsePrivateKey([]byte(keyPair.PrivateKey)) th.AssertNoErr(t, err) tools.PrintResource(t, keyPair) } func TestKeypairsCreateDelete(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) keyPair, err := CreateKeyPair(t, client) th.AssertNoErr(t, err) defer DeleteKeyPair(t, client, keyPair) tools.PrintResource(t, keyPair) allPages, err := keypairs.List(client).AllPages() th.AssertNoErr(t, err) allKeys, err := keypairs.ExtractKeyPairs(allPages) th.AssertNoErr(t, err) var found bool for _, kp := range allKeys { tools.PrintResource(t, kp) if kp.Name == keyPair.Name { found = true } } th.AssertEquals(t, found, true) } func TestKeypairsImportPublicKey(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) publicKey, err := createKey() th.AssertNoErr(t, err) keyPair, err := ImportPublicKey(t, client, publicKey) th.AssertNoErr(t, err) defer DeleteKeyPair(t, client, keyPair) tools.PrintResource(t, keyPair) } func TestKeypairsServerCreateWithKey(t *testing.T) { clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) publicKey, err := createKey() th.AssertNoErr(t, err) keyPair, err := ImportPublicKey(t, client, publicKey) th.AssertNoErr(t, err) defer DeleteKeyPair(t, client, keyPair) server, err := CreateServerWithPublicKey(t, client, keyPair.Name) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) server, err = servers.Get(client, server.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, server.KeyName, keyPair.Name) } limits_test.go000066400000000000000000000024261335005366400344500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute limits package v2 import ( "strings" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/limits" th "github.com/gophercloud/gophercloud/testhelper" ) func TestLimits(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) limits, err := limits.Get(client, nil).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, limits) th.AssertEquals(t, limits.Absolute.MaxPersonalitySize, 10240) } func TestLimitsForTenant(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) // I think this is the easiest way to get the tenant ID while being // agnostic to Identity v2 and v3. // Technically we're just returning the limits for ourselves, but it's // the fact that we're specifying a tenant ID that is important here. endpointParts := strings.Split(client.Endpoint, "/") tenantID := endpointParts[4] getOpts := limits.GetOpts{ TenantID: tenantID, } limits, err := limits.Get(client, getOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, limits) th.AssertEquals(t, limits.Absolute.MaxPersonalitySize, 10240) } migrate_test.go000066400000000000000000000024661335005366400346030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute servers package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/migrate" th "github.com/gophercloud/gophercloud/testhelper" ) func TestMigrate(t *testing.T) { t.Skip("This is not passing in OpenLab. Works locally") clients.RequireLong(t) clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) t.Logf("Attempting to migrate server %s", server.ID) err = migrate.Migrate(client, server.ID).ExtractErr() th.AssertNoErr(t, err) } func TestLiveMigrate(t *testing.T) { clients.RequireLong(t) clients.RequireAdmin(t) clients.RequireLiveMigration(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) t.Logf("Attempting to migrate server %s", server.ID) blockMigration := false diskOverCommit := false liveMigrateOpts := migrate.LiveMigrateOpts{ BlockMigration: &blockMigration, DiskOverCommit: &diskOverCommit, } err = migrate.LiveMigrate(client, server.ID, liveMigrateOpts).ExtractErr() th.AssertNoErr(t, err) } network_test.go000066400000000000000000000024521335005366400346370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute servers package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/networks" th "github.com/gophercloud/gophercloud/testhelper" ) func TestNetworksList(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) allPages, err := networks.List(client).AllPages() th.AssertNoErr(t, err) allNetworks, err := networks.ExtractNetworks(allPages) th.AssertNoErr(t, err) var found bool for _, network := range allNetworks { tools.PrintResource(t, network) if network.Label == choices.NetworkName { found = true } } th.AssertEquals(t, found, true) } func TestNetworksGet(t *testing.T) { choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName) th.AssertNoErr(t, err) network, err := networks.Get(client, networkID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, network) th.AssertEquals(t, network.Label, choices.NetworkName) } pkg.go000066400000000000000000000001411335005366400326610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// Package v2 package contains acceptance tests for the Openstack Compute V2 service. package v2 quotaset_test.go000066400000000000000000000103341335005366400350110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute quotasets package v2 import ( "fmt" "os" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets" "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" th "github.com/gophercloud/gophercloud/testhelper" ) func TestQuotasetGet(t *testing.T) { clients.SkipRelease(t, "master") client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) identityClient, err := clients.NewIdentityV2Client() th.AssertNoErr(t, err) tenantID, err := getTenantID(t, identityClient) th.AssertNoErr(t, err) quotaSet, err := quotasets.Get(client, tenantID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, quotaSet) th.AssertEquals(t, quotaSet.FixedIPs, -1) } func getTenantID(t *testing.T, client *gophercloud.ServiceClient) (string, error) { allPages, err := tenants.List(client, nil).AllPages() th.AssertNoErr(t, err) allTenants, err := tenants.ExtractTenants(allPages) th.AssertNoErr(t, err) for _, tenant := range allTenants { return tenant.ID, nil } return "", fmt.Errorf("Unable to get tenant ID") } func getTenantIDByName(t *testing.T, client *gophercloud.ServiceClient, name string) (string, error) { allPages, err := tenants.List(client, nil).AllPages() th.AssertNoErr(t, err) allTenants, err := tenants.ExtractTenants(allPages) th.AssertNoErr(t, err) for _, tenant := range allTenants { if tenant.Name == name { return tenant.ID, nil } } return "", fmt.Errorf("Unable to get tenant ID") } // What will be sent as desired Quotas to the Server var UpdateQuotaOpts = quotasets.UpdateOpts{ FixedIPs: gophercloud.IntToPointer(10), FloatingIPs: gophercloud.IntToPointer(10), InjectedFileContentBytes: gophercloud.IntToPointer(10240), InjectedFilePathBytes: gophercloud.IntToPointer(255), InjectedFiles: gophercloud.IntToPointer(5), KeyPairs: gophercloud.IntToPointer(10), MetadataItems: gophercloud.IntToPointer(128), RAM: gophercloud.IntToPointer(20000), SecurityGroupRules: gophercloud.IntToPointer(20), SecurityGroups: gophercloud.IntToPointer(10), Cores: gophercloud.IntToPointer(10), Instances: gophercloud.IntToPointer(4), ServerGroups: gophercloud.IntToPointer(2), ServerGroupMembers: gophercloud.IntToPointer(3), } // What the Server hopefully returns as the new Quotas var UpdatedQuotas = quotasets.QuotaSet{ FixedIPs: 10, FloatingIPs: 10, InjectedFileContentBytes: 10240, InjectedFilePathBytes: 255, InjectedFiles: 5, KeyPairs: 10, MetadataItems: 128, RAM: 20000, SecurityGroupRules: 20, SecurityGroups: 10, Cores: 10, Instances: 4, ServerGroups: 2, ServerGroupMembers: 3, } func TestQuotasetUpdateDelete(t *testing.T) { clients.SkipRelease(t, "master") clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) idclient, err := clients.NewIdentityV2Client() th.AssertNoErr(t, err) tenantid, err := getTenantIDByName(t, idclient, os.Getenv("OS_TENANT_NAME")) th.AssertNoErr(t, err) // save original quotas orig, err := quotasets.Get(client, tenantid).Extract() th.AssertNoErr(t, err) // Test Update res, err := quotasets.Update(client, tenantid, UpdateQuotaOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, UpdatedQuotas, *res) // Test Delete _, err = quotasets.Delete(client, tenantid).Extract() th.AssertNoErr(t, err) // We dont know the default quotas, so just check if the quotas are not the same as before newres, err := quotasets.Get(client, tenantid).Extract() th.AssertNoErr(t, err) if newres.RAM == res.RAM { t.Fatalf("Failed to update quotas") } restore := quotasets.UpdateOpts{} FillUpdateOptsFromQuotaSet(*orig, &restore) // restore original quotas res, err = quotasets.Update(client, tenantid, restore).Extract() th.AssertNoErr(t, err) orig.ID = "" th.AssertDeepEquals(t, orig, res) } rescueunrescue_test.go000066400000000000000000000010351335005366400362020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute rescueunrescue package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" th "github.com/gophercloud/gophercloud/testhelper" ) func TestServerRescueUnrescue(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) err = RescueServer(t, client, server) th.AssertNoErr(t, err) err = UnrescueServer(t, client, server) th.AssertNoErr(t, err) } secgroup_test.go000066400000000000000000000071571335005366400350040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute secgroups package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" ) func TestSecGroupsList(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) allPages, err := secgroups.List(client).AllPages() th.AssertNoErr(t, err) allSecGroups, err := secgroups.ExtractSecurityGroups(allPages) th.AssertNoErr(t, err) var found bool for _, secgroup := range allSecGroups { tools.PrintResource(t, secgroup) if secgroup.Name == "default" { found = true } } th.AssertEquals(t, found, true) } func TestSecGroupsCRUD(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) securityGroup, err := CreateSecurityGroup(t, client) th.AssertNoErr(t, err) defer DeleteSecurityGroup(t, client, securityGroup.ID) tools.PrintResource(t, securityGroup) newName := tools.RandomString("secgroup_", 4) updateOpts := secgroups.UpdateOpts{ Name: newName, Description: tools.RandomString("dec_", 10), } updatedSecurityGroup, err := secgroups.Update(client, securityGroup.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedSecurityGroup) t.Logf("Updated %s's name to %s", updatedSecurityGroup.ID, updatedSecurityGroup.Name) th.AssertEquals(t, updatedSecurityGroup.Name, newName) } func TestSecGroupsRuleCreate(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) securityGroup, err := CreateSecurityGroup(t, client) th.AssertNoErr(t, err) defer DeleteSecurityGroup(t, client, securityGroup.ID) tools.PrintResource(t, securityGroup) rule, err := CreateSecurityGroupRule(t, client, securityGroup.ID) th.AssertNoErr(t, err) defer DeleteSecurityGroupRule(t, client, rule.ID) tools.PrintResource(t, rule) newSecurityGroup, err := secgroups.Get(client, securityGroup.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newSecurityGroup) th.AssertEquals(t, len(newSecurityGroup.Rules), 1) } func TestSecGroupsAddGroupToServer(t *testing.T) { clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) securityGroup, err := CreateSecurityGroup(t, client) th.AssertNoErr(t, err) defer DeleteSecurityGroup(t, client, securityGroup.ID) rule, err := CreateSecurityGroupRule(t, client, securityGroup.ID) th.AssertNoErr(t, err) defer DeleteSecurityGroupRule(t, client, rule.ID) t.Logf("Adding group %s to server %s", securityGroup.ID, server.ID) err = secgroups.AddServer(client, server.ID, securityGroup.Name).ExtractErr() th.AssertNoErr(t, err) server, err = servers.Get(client, server.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, server) var found bool for _, sg := range server.SecurityGroups { if sg["name"] == securityGroup.Name { found = true } } th.AssertEquals(t, found, true) t.Logf("Removing group %s from server %s", securityGroup.ID, server.ID) err = secgroups.RemoveServer(client, server.ID, securityGroup.Name).ExtractErr() th.AssertNoErr(t, err) server, err = servers.Get(client, server.ID).Extract() th.AssertNoErr(t, err) found = false tools.PrintResource(t, server) for _, sg := range server.SecurityGroups { if sg["name"] == securityGroup.Name { found = true } } th.AssertEquals(t, found, false) } servergroup_test.go000066400000000000000000000036451335005366400355360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute servergroups package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" ) func TestServergroupsCreateDelete(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) serverGroup, err := CreateServerGroup(t, client, "anti-affinity") th.AssertNoErr(t, err) defer DeleteServerGroup(t, client, serverGroup) serverGroup, err = servergroups.Get(client, serverGroup.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, serverGroup) allPages, err := servergroups.List(client).AllPages() th.AssertNoErr(t, err) allServerGroups, err := servergroups.ExtractServerGroups(allPages) th.AssertNoErr(t, err) var found bool for _, sg := range allServerGroups { tools.PrintResource(t, serverGroup) if sg.ID == serverGroup.ID { found = true } } th.AssertEquals(t, found, true) } func TestServergroupsAffinityPolicy(t *testing.T) { clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) serverGroup, err := CreateServerGroup(t, client, "affinity") th.AssertNoErr(t, err) defer DeleteServerGroup(t, client, serverGroup) firstServer, err := CreateServerInServerGroup(t, client, serverGroup) th.AssertNoErr(t, err) defer DeleteServer(t, client, firstServer) firstServer, err = servers.Get(client, firstServer.ID).Extract() th.AssertNoErr(t, err) secondServer, err := CreateServerInServerGroup(t, client, serverGroup) th.AssertNoErr(t, err) defer DeleteServer(t, client, secondServer) secondServer, err = servers.Get(client, secondServer.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, firstServer.HostID, secondServer.HostID) } servers_test.go000066400000000000000000000316141335005366400346410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute servers package v2 import ( "strings" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/extendedstatus" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/lockunlock" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/pauseunpause" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/serverusage" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/suspendresume" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" ) func TestServersCreateDestroy(t *testing.T) { clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) allPages, err := servers.List(client, servers.ListOpts{}).AllPages() th.AssertNoErr(t, err) allServers, err := servers.ExtractServers(allPages) th.AssertNoErr(t, err) var found bool for _, s := range allServers { tools.PrintResource(t, server) if s.ID == server.ID { found = true } } th.AssertEquals(t, found, true) allAddressPages, err := servers.ListAddresses(client, server.ID).AllPages() th.AssertNoErr(t, err) allAddresses, err := servers.ExtractAddresses(allAddressPages) th.AssertNoErr(t, err) for network, address := range allAddresses { t.Logf("Addresses on %s: %+v", network, address) } allInterfacePages, err := attachinterfaces.List(client, server.ID).AllPages() th.AssertNoErr(t, err) allInterfaces, err := attachinterfaces.ExtractInterfaces(allInterfacePages) th.AssertNoErr(t, err) for _, iface := range allInterfaces { t.Logf("Interfaces: %+v", iface) } allNetworkAddressPages, err := servers.ListAddressesByNetwork(client, server.ID, choices.NetworkName).AllPages() th.AssertNoErr(t, err) allNetworkAddresses, err := servers.ExtractNetworkAddresses(allNetworkAddressPages) th.AssertNoErr(t, err) t.Logf("Addresses on %s:", choices.NetworkName) for _, address := range allNetworkAddresses { t.Logf("%+v", address) } } func TestServersWithExtensionsCreateDestroy(t *testing.T) { clients.RequireLong(t) var extendedServer struct { servers.Server availabilityzones.ServerAvailabilityZoneExt extendedstatus.ServerExtendedStatusExt serverusage.UsageExt } client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) err = servers.Get(client, server.ID).ExtractInto(&extendedServer) th.AssertNoErr(t, err) tools.PrintResource(t, extendedServer) th.AssertEquals(t, extendedServer.AvailabilityZone, "nova") th.AssertEquals(t, int(extendedServer.PowerState), extendedstatus.RUNNING) th.AssertEquals(t, extendedServer.TaskState, "") th.AssertEquals(t, extendedServer.VmState, "active") th.AssertEquals(t, extendedServer.LaunchedAt.IsZero(), false) th.AssertEquals(t, extendedServer.TerminatedAt.IsZero(), true) } func TestServersWithoutImageRef(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := CreateServerWithoutImageRef(t, client) if err != nil { if err400, ok := err.(*gophercloud.ErrUnexpectedResponseCode); ok { if !strings.Contains("Missing imageRef attribute", string(err400.Body)) { defer DeleteServer(t, client, server) } } } } func TestServersUpdate(t *testing.T) { clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) alternateName := tools.RandomString("ACPTTEST", 16) for alternateName == server.Name { alternateName = tools.RandomString("ACPTTEST", 16) } t.Logf("Attempting to rename the server to %s.", alternateName) updateOpts := servers.UpdateOpts{ Name: alternateName, } updated, err := servers.Update(client, server.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, updated.ID, server.ID) err = tools.WaitFor(func() (bool, error) { latest, err := servers.Get(client, updated.ID).Extract() if err != nil { return false, err } return latest.Name == alternateName, nil }) } func TestServersMetadata(t *testing.T) { clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) tools.PrintResource(t, server) metadata, err := servers.UpdateMetadata(client, server.ID, servers.MetadataOpts{ "foo": "bar", "this": "that", }).Extract() th.AssertNoErr(t, err) t.Logf("UpdateMetadata result: %+v\n", metadata) server, err = servers.Get(client, server.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, server) expectedMetadata := map[string]string{ "abc": "def", "foo": "bar", "this": "that", } th.AssertDeepEquals(t, expectedMetadata, server.Metadata) err = servers.DeleteMetadatum(client, server.ID, "foo").ExtractErr() th.AssertNoErr(t, err) server, err = servers.Get(client, server.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, server) expectedMetadata = map[string]string{ "abc": "def", "this": "that", } th.AssertDeepEquals(t, expectedMetadata, server.Metadata) metadata, err = servers.CreateMetadatum(client, server.ID, servers.MetadatumOpts{ "foo": "baz", }).Extract() th.AssertNoErr(t, err) t.Logf("CreateMetadatum result: %+v\n", metadata) server, err = servers.Get(client, server.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, server) expectedMetadata = map[string]string{ "abc": "def", "this": "that", "foo": "baz", } th.AssertDeepEquals(t, expectedMetadata, server.Metadata) metadata, err = servers.Metadatum(client, server.ID, "foo").Extract() th.AssertNoErr(t, err) t.Logf("Metadatum result: %+v\n", metadata) th.AssertEquals(t, "baz", metadata["foo"]) metadata, err = servers.Metadata(client, server.ID).Extract() th.AssertNoErr(t, err) t.Logf("Metadata result: %+v\n", metadata) th.AssertDeepEquals(t, expectedMetadata, metadata) metadata, err = servers.ResetMetadata(client, server.ID, servers.MetadataOpts{}).Extract() th.AssertNoErr(t, err) t.Logf("ResetMetadata result: %+v\n", metadata) th.AssertDeepEquals(t, map[string]string{}, metadata) } func TestServersActionChangeAdminPassword(t *testing.T) { clients.RequireLong(t) clients.RequireGuestAgent(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) randomPassword := tools.MakeNewPassword(server.AdminPass) res := servers.ChangeAdminPassword(client, server.ID, randomPassword) th.AssertNoErr(t, res.Err) if err = WaitForComputeStatus(client, server, "PASSWORD"); err != nil { t.Fatal(err) } if err = WaitForComputeStatus(client, server, "ACTIVE"); err != nil { t.Fatal(err) } } func TestServersActionReboot(t *testing.T) { clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) rebootOpts := servers.RebootOpts{ Type: servers.SoftReboot, } t.Logf("Attempting reboot of server %s", server.ID) res := servers.Reboot(client, server.ID, rebootOpts) th.AssertNoErr(t, res.Err) if err = WaitForComputeStatus(client, server, "REBOOT"); err != nil { t.Fatal(err) } if err = WaitForComputeStatus(client, server, "ACTIVE"); err != nil { t.Fatal(err) } } func TestServersActionRebuild(t *testing.T) { clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) t.Logf("Attempting to rebuild server %s", server.ID) rebuildOpts := servers.RebuildOpts{ Name: tools.RandomString("ACPTTEST", 16), AdminPass: tools.MakeNewPassword(server.AdminPass), ImageID: choices.ImageID, } rebuilt, err := servers.Rebuild(client, server.ID, rebuildOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, rebuilt.ID, server.ID) if err = WaitForComputeStatus(client, rebuilt, "REBUILD"); err != nil { t.Fatal(err) } if err = WaitForComputeStatus(client, rebuilt, "ACTIVE"); err != nil { t.Fatal(err) } } func TestServersActionResizeConfirm(t *testing.T) { clients.RequireLong(t) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) t.Logf("Attempting to resize server %s", server.ID) ResizeServer(t, client, server) t.Logf("Attempting to confirm resize for server %s", server.ID) if res := servers.ConfirmResize(client, server.ID); res.Err != nil { t.Fatal(res.Err) } if err = WaitForComputeStatus(client, server, "ACTIVE"); err != nil { t.Fatal(err) } server, err = servers.Get(client, server.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, server.Flavor["id"], choices.FlavorIDResize) } func TestServersActionResizeRevert(t *testing.T) { clients.RequireLong(t) choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) t.Logf("Attempting to resize server %s", server.ID) ResizeServer(t, client, server) t.Logf("Attempting to revert resize for server %s", server.ID) if res := servers.RevertResize(client, server.ID); res.Err != nil { t.Fatal(res.Err) } if err = WaitForComputeStatus(client, server, "ACTIVE"); err != nil { t.Fatal(err) } server, err = servers.Get(client, server.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, server.Flavor["id"], choices.FlavorID) } func TestServersActionPause(t *testing.T) { clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) t.Logf("Attempting to pause server %s", server.ID) err = pauseunpause.Pause(client, server.ID).ExtractErr() th.AssertNoErr(t, err) err = WaitForComputeStatus(client, server, "PAUSED") th.AssertNoErr(t, err) err = pauseunpause.Unpause(client, server.ID).ExtractErr() th.AssertNoErr(t, err) err = WaitForComputeStatus(client, server, "ACTIVE") th.AssertNoErr(t, err) } func TestServersActionSuspend(t *testing.T) { clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) t.Logf("Attempting to suspend server %s", server.ID) err = suspendresume.Suspend(client, server.ID).ExtractErr() th.AssertNoErr(t, err) err = WaitForComputeStatus(client, server, "SUSPENDED") th.AssertNoErr(t, err) err = suspendresume.Resume(client, server.ID).ExtractErr() th.AssertNoErr(t, err) err = WaitForComputeStatus(client, server, "ACTIVE") th.AssertNoErr(t, err) } func TestServersActionLock(t *testing.T) { clients.RequireLong(t) clients.RequireNonAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) t.Logf("Attempting to Lock server %s", server.ID) err = lockunlock.Lock(client, server.ID).ExtractErr() th.AssertNoErr(t, err) t.Logf("Attempting to delete locked server %s", server.ID) err = servers.Delete(client, server.ID).ExtractErr() th.AssertEquals(t, err != nil, true) t.Logf("Attempting to unlock server %s", server.ID) err = lockunlock.Unlock(client, server.ID).ExtractErr() th.AssertNoErr(t, err) err = WaitForComputeStatus(client, server, "ACTIVE") th.AssertNoErr(t, err) } func TestServersConsoleOutput(t *testing.T) { client, err := clients.NewComputeV2Client() if err != nil { t.Fatalf("Unable to create a compute client: %v", err) } server, err := CreateServer(t, client) if err != nil { t.Fatalf("Unable to create server: %v", err) } defer DeleteServer(t, client, server) outputOpts := &servers.ShowConsoleOutputOpts{ Length: 4, } output, err := servers.ShowConsoleOutput(client, server.ID, outputOpts).Extract() if err != nil { t.Fatal(err) } tools.PrintResource(t, output) } services_test.go000066400000000000000000000014401335005366400347650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute services package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/services" th "github.com/gophercloud/gophercloud/testhelper" ) func TestServicesList(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) allPages, err := services.List(client).AllPages() th.AssertNoErr(t, err) allServices, err := services.ExtractServices(allPages) th.AssertNoErr(t, err) var found bool for _, service := range allServices { tools.PrintResource(t, service) if service.Binary == "nova-scheduler" { found = true } } th.AssertEquals(t, found, true) } tenantnetworks_test.go000066400000000000000000000024461335005366400362370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute servers package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks" th "github.com/gophercloud/gophercloud/testhelper" ) func TestTenantNetworksList(t *testing.T) { choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) allPages, err := tenantnetworks.List(client).AllPages() th.AssertNoErr(t, err) allTenantNetworks, err := tenantnetworks.ExtractNetworks(allPages) th.AssertNoErr(t, err) var found bool for _, network := range allTenantNetworks { tools.PrintResource(t, network) if network.Name == choices.NetworkName { found = true } } th.AssertEquals(t, found, true) } func TestTenantNetworksGet(t *testing.T) { choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) th.AssertNoErr(t, err) network, err := tenantnetworks.Get(client, networkID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, network) } usage_test.go000066400000000000000000000040161335005366400342500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute usage package v2 import ( "strings" "testing" "time" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/usage" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestUsageSingleTenant(t *testing.T) { t.Skip("This is not passing in OpenLab. Works locally") clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) DeleteServer(t, client, server) endpointParts := strings.Split(client.Endpoint, "/") tenantID := endpointParts[4] end := time.Now() start := end.AddDate(0, -1, 0) opts := usage.SingleTenantOpts{ Start: &start, End: &end, } err = usage.SingleTenant(client, tenantID, opts).EachPage(func(page pagination.Page) (bool, error) { tenantUsage, err := usage.ExtractSingleTenant(page) th.AssertNoErr(t, err) tools.PrintResource(t, tenantUsage) if tenantUsage.TotalHours == 0 { t.Fatalf("TotalHours should not be 0") } return true, nil }) th.AssertNoErr(t, err) } func TestUsageAllTenants(t *testing.T) { t.Skip("This is not passing in OpenLab. Works locally") clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) DeleteServer(t, client, server) end := time.Now() start := end.AddDate(0, -1, 0) opts := usage.AllTenantsOpts{ Detailed: true, Start: &start, End: &end, } err = usage.AllTenants(client, opts).EachPage(func(page pagination.Page) (bool, error) { allUsage, err := usage.ExtractAllTenants(page) th.AssertNoErr(t, err) tools.PrintResource(t, allUsage) if len(allUsage) == 0 { t.Fatalf("No usage returned") } if allUsage[0].TotalHours == 0 { t.Fatalf("TotalHours should not be 0") } return true, nil }) th.AssertNoErr(t, err) } volumeattach_test.go000066400000000000000000000020511335005366400356350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/compute/v2// +build acceptance compute volumeattach package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" bs "github.com/gophercloud/gophercloud/acceptance/openstack/blockstorage/v2" "github.com/gophercloud/gophercloud/acceptance/tools" th "github.com/gophercloud/gophercloud/testhelper" ) func TestVolumeAttachAttachment(t *testing.T) { clients.RequireLong(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) blockClient, err := clients.NewBlockStorageV2Client() th.AssertNoErr(t, err) server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) volume, err := bs.CreateVolume(t, blockClient) th.AssertNoErr(t, err) defer bs.DeleteVolume(t, blockClient, volume) volumeAttachment, err := CreateVolumeAttachment(t, client, blockClient, server, volume) th.AssertNoErr(t, err) defer DeleteVolumeAttachment(t, client, blockClient, server, volumeAttachment) tools.PrintResource(t, volumeAttachment) th.AssertEquals(t, volumeAttachment.ServerID, server.ID) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/container/000077500000000000000000000000001335005366400316135ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/container/v1/000077500000000000000000000000001335005366400321415ustar00rootroot00000000000000capsules.go000066400000000000000000000014121335005366400342260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/container/v1package v1 import ( "fmt" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/container/v1/capsules" ) // WaitForCapsuleStatus will poll a capsule's status until it either matches // the specified status or the status becomes Failed. func WaitForCapsuleStatus(client *gophercloud.ServiceClient, capsule *capsules.Capsule, status string) error { return tools.WaitFor(func() (bool, error) { latest, err := capsules.Get(client, capsule.UUID).Extract() if err != nil { return false, err } if latest.Status == status { // Success! return true, nil } if latest.Status == "Failed" { return false, fmt.Errorf("Capsule in FAILED state") } return false, nil }) } capsules_test.go000066400000000000000000000036601335005366400352740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/container/v1package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/container/v1/capsules" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestCapsule(t *testing.T) { client, err := clients.NewContainerV1Client() if err != nil { t.Fatalf("Unable to create an container v1 client: %v", err) } th.AssertNoErr(t, err) template := new(capsules.Template) template.Bin = []byte(`{ "capsuleVersion": "beta", "kind": "capsule", "metadata": { "labels": { "app": "web", "app1": "web1" }, "name": "template" }, "spec": { "restartPolicy": "Always", "containers": [ { "command": [ "sleep", "1000000" ], "env": { "ENV1": "/usr/local/bin", "ENV2": "/usr/bin" }, "image": "ubuntu", "imagePullPolicy": "ifnotpresent", "ports": [ { "containerPort": 80, "hostPort": 80, "name": "nginx-port", "protocol": "TCP" } ], "resources": { "requests": { "cpu": 1, "memory": 1024 } }, "workDir": "/root" } ] } }`) createOpts := capsules.CreateOpts{ TemplateOpts: template, } capsule, err := capsules.Create(client, createOpts).Extract() th.AssertNoErr(t, err) err = WaitForCapsuleStatus(client, capsule, "Running") th.AssertNoErr(t, err) pager := capsules.List(client, nil) err = pager.EachPage(func(page pagination.Page) (bool, error) { CapsuleList, err := capsules.ExtractCapsules(page) th.AssertNoErr(t, err) for _, m := range CapsuleList { capsuleUUID := m.UUID capsule, err := capsules.Get(client, capsuleUUID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, capsule.MetaName, "template") err = capsules.Delete(client, capsuleUUID).ExtractErr() th.AssertNoErr(t, err) } return true, nil }) th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/containerinfra/000077500000000000000000000000001335005366400326335ustar00rootroot00000000000000v1/000077500000000000000000000000001335005366400331025ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/containerinfracertificates_test.go000066400000000000000000000031411335005366400371340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/containerinfra/v1// +build acceptance containerinfra package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/certificates" th "github.com/gophercloud/gophercloud/testhelper" ) func TestCertificatesCRUD(t *testing.T) { client, err := clients.NewContainerInfraV1Client() th.AssertNoErr(t, err) clusterUUID := "8934d2d1-6bce-4ffa-a017-fb437777269d" opts := certificates.CreateOpts{ BayUUID: clusterUUID, CSR: "-----BEGIN CERTIFICATE REQUEST-----\n" + "MIIByjCCATMCAQAwgYkxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh" + "MRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgSW5jMR8w" + "HQYDVQQLExZJbmZvcm1hdGlvbiBUZWNobm9sb2d5MRcwFQYDVQQDEw53d3cuZ29v" + "Z2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEApZtYJCHJ4VpVXHfV" + "IlstQTlO4qC03hjX+ZkPyvdYd1Q4+qbAeTwXmCUKYHThVRd5aXSqlPzyIBwieMZr" + "WFlRQddZ1IzXAlVRDWwAo60KecqeAXnnUK+5fXoTI/UgWshre8tJ+x/TMHaQKR/J" + "cIWPhqaQhsJuzZbvAdGA80BLxdMCAwEAAaAAMA0GCSqGSIb3DQEBBQUAA4GBAIhl" + "4PvFq+e7ipARgI5ZM+GZx6mpCz44DTo0JkwfRDf+BtrsaC0q68eTf2XhYOsq4fkH" + "Q0uA0aVog3f5iJxCa3Hp5gxbJQ6zV6kJ0TEsuaaOhEko9sdpCoPOnRBm2i/XRD2D" + "6iNh8f8z0ShGsFqjDgFHyF3o+lUyj+UC6H1QW7bn\n" + "-----END CERTIFICATE REQUEST-----", } createResponse, err := certificates.Create(client, opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, opts.CSR, createResponse.CSR) certificate, err := certificates.Get(client, clusterUUID).Extract() th.AssertNoErr(t, err) t.Log(certificate.PEM) err = certificates.Update(client, clusterUUID).ExtractErr() th.AssertNoErr(t, err) } clusters_test.go000066400000000000000000000033141335005366400363350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/containerinfra/v1// +build acceptance containerinfra package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clusters" th "github.com/gophercloud/gophercloud/testhelper" ) func TestClustersCRUD(t *testing.T) { client, err := clients.NewContainerInfraV1Client() th.AssertNoErr(t, err) clusterTemplate, err := CreateClusterTemplate(t, client) th.AssertNoErr(t, err) defer DeleteClusterTemplate(t, client, clusterTemplate.UUID) clusterID, err := CreateCluster(t, client, clusterTemplate.UUID) tools.PrintResource(t, clusterID) defer DeleteCluster(t, client, clusterID) allPages, err := clusters.List(client, nil).AllPages() th.AssertNoErr(t, err) allClusters, err := clusters.ExtractClusters(allPages) th.AssertNoErr(t, err) var found bool for _, v := range allClusters { if v.UUID == clusterID { found = true } } th.AssertEquals(t, found, true) updateOpts := []clusters.UpdateOptsBuilder{ clusters.UpdateOpts{ Op: clusters.ReplaceOp, Path: "/node_count", Value: "2", }, } updateResult := clusters.Update(client, clusterID, updateOpts) th.AssertNoErr(t, updateResult.Err) if len(updateResult.Header["X-Openstack-Request-Id"]) > 0 { t.Logf("Cluster Update Request ID: %s", updateResult.Header["X-Openstack-Request-Id"][0]) } clusterID, err = updateResult.Extract() th.AssertNoErr(t, err) err = WaitForCluster(client, clusterID, "UPDATE_COMPLETE") th.AssertNoErr(t, err) newCluster, err := clusters.Get(client, clusterID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newCluster.UUID, clusterID) tools.PrintResource(t, newCluster) } clustertemplates_test.go000066400000000000000000000036641335005366400401010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/containerinfra/v1// +build acceptance containerinfra package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clustertemplates" th "github.com/gophercloud/gophercloud/testhelper" ) func TestClusterTemplatesCRUD(t *testing.T) { client, err := clients.NewContainerInfraV1Client() th.AssertNoErr(t, err) clusterTemplate, err := CreateClusterTemplate(t, client) th.AssertNoErr(t, err) t.Log(clusterTemplate.Name) defer DeleteClusterTemplate(t, client, clusterTemplate.UUID) // Test clusters list allPages, err := clustertemplates.List(client, nil).AllPages() th.AssertNoErr(t, err) allClusterTemplates, err := clustertemplates.ExtractClusterTemplates(allPages) th.AssertNoErr(t, err) var found bool for _, v := range allClusterTemplates { if v.UUID == clusterTemplate.UUID { found = true } } th.AssertEquals(t, found, true) template, err := clustertemplates.Get(client, clusterTemplate.UUID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, clusterTemplate.UUID, template.UUID) // Test cluster update updateOpts := []clustertemplates.UpdateOptsBuilder{ clustertemplates.UpdateOpts{ Op: clustertemplates.ReplaceOp, Path: "/master_lb_enabled", Value: "false", }, clustertemplates.UpdateOpts{ Op: clustertemplates.ReplaceOp, Path: "/registry_enabled", Value: "false", }, clustertemplates.UpdateOpts{ Op: clustertemplates.AddOp, Path: "/labels/test", Value: "test", }, } updateClusterTemplate, err := clustertemplates.Update(client, clusterTemplate.UUID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, false, updateClusterTemplate.MasterLBEnabled) th.AssertEquals(t, false, updateClusterTemplate.RegistryEnabled) th.AssertEquals(t, "test", updateClusterTemplate.Labels["test"]) tools.PrintResource(t, updateClusterTemplate) } containerinfra.go000066400000000000000000000125051335005366400364360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/containerinfra/v1package v1 import ( "fmt" "strings" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clusters" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clustertemplates" th "github.com/gophercloud/gophercloud/testhelper" ) // CreateClusterTemplate will create a random cluster tempalte. An error will be returned if the // cluster-template could not be created. func CreateClusterTemplate(t *testing.T, client *gophercloud.ServiceClient) (*clustertemplates.ClusterTemplate, error) { choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { return nil, err } name := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create cluster template: %s", name) boolFalse := false createOpts := clustertemplates.CreateOpts{ COE: "swarm", DNSNameServer: "8.8.8.8", DockerStorageDriver: "devicemapper", ExternalNetworkID: choices.ExternalNetworkID, FlavorID: choices.FlavorID, FloatingIPEnabled: &boolFalse, ImageID: choices.MagnumImageID, MasterFlavorID: choices.FlavorID, MasterLBEnabled: &boolFalse, Name: name, Public: &boolFalse, RegistryEnabled: &boolFalse, ServerType: "vm", } res := clustertemplates.Create(client, createOpts) if res.Err != nil { return nil, res.Err } requestID := res.Header.Get("X-OpenStack-Request-Id") th.AssertEquals(t, true, requestID != "") t.Logf("Cluster Template %s request ID: %s", name, requestID) clusterTemplate, err := res.Extract() if err != nil { return nil, err } t.Logf("Successfully created cluster template: %s", clusterTemplate.Name) tools.PrintResource(t, clusterTemplate) tools.PrintResource(t, clusterTemplate.CreatedAt) th.AssertEquals(t, name, clusterTemplate.Name) th.AssertEquals(t, choices.ExternalNetworkID, clusterTemplate.ExternalNetworkID) th.AssertEquals(t, choices.MagnumImageID, clusterTemplate.ImageID) return clusterTemplate, nil } // DeleteClusterTemplate will delete a given cluster-template. A fatal error will occur if the // cluster-template could not be deleted. This works best as a deferred function. func DeleteClusterTemplate(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete cluster-template: %s", id) err := clustertemplates.Delete(client, id).ExtractErr() if err != nil { t.Fatalf("Error deleting cluster-template %s: %s:", id, err) } t.Logf("Successfully deleted cluster-template: %s", id) return } // CreateCluster will create a random cluster. An error will be returned if the // cluster could not be created. func CreateCluster(t *testing.T, client *gophercloud.ServiceClient, clusterTemplateID string) (string, error) { clusterName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create cluster: %s using template %s", clusterName, clusterTemplateID) choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { return "", err } masterCount := 1 nodeCount := 1 createTimeout := 100 createOpts := clusters.CreateOpts{ ClusterTemplateID: clusterTemplateID, CreateTimeout: &createTimeout, FlavorID: choices.FlavorID, Keypair: choices.MagnumKeypair, Labels: map[string]string{}, MasterCount: &masterCount, MasterFlavorID: choices.FlavorID, Name: clusterName, NodeCount: &nodeCount, } createResult := clusters.Create(client, createOpts) th.AssertNoErr(t, createResult.Err) if len(createResult.Header["X-Openstack-Request-Id"]) > 0 { t.Logf("Cluster Create Request ID: %s", createResult.Header["X-Openstack-Request-Id"][0]) } clusterID, err := createResult.Extract() if err != nil { return "", err } t.Logf("Cluster created: %+v", clusterID) err = WaitForCluster(client, clusterID, "CREATE_COMPLETE") if err != nil { return clusterID, err } t.Logf("Successfully created cluster: %s id: %s", clusterName, clusterID) return clusterID, nil } func DeleteCluster(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete cluster: %s", id) r := clusters.Delete(client, id) err := clusters.Delete(client, id).ExtractErr() deleteRequestID := "" idKey := "X-Openstack-Request-Id" if len(r.Header[idKey]) > 0 { deleteRequestID = r.Header[idKey][0] } if err != nil { t.Fatalf("Error deleting cluster. requestID=%s clusterID=%s: err%s:", deleteRequestID, id, err) } err = WaitForCluster(client, id, "DELETE_COMPLETE") if err != nil { t.Fatalf("Error deleting cluster %s: %s:", id, err) } t.Logf("Successfully deleted cluster: %s", id) return } func WaitForCluster(client *gophercloud.ServiceClient, clusterID string, status string) error { return tools.WaitFor(func() (bool, error) { cluster, err := clusters.Get(client, clusterID).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok && status == "DELETE_COMPLETE" { return true, nil } return false, err } if cluster.Status == status { return true, nil } if strings.Contains(cluster.Status, "FAILED") { return false, fmt.Errorf("Cluster %s FAILED. Status=%s StatusReason=%s", clusterID, cluster.Status, cluster.StatusReason) } return false, nil }) } pkg.go000066400000000000000000000000131335005366400342040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/containerinfra/v1package v1 golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/db/000077500000000000000000000000001335005366400302165ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/db/v1/000077500000000000000000000000001335005366400305445ustar00rootroot00000000000000configurations_test.go000066400000000000000000000022771335005366400351150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/db/v1// +build acceptance db package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/db/v1/configurations" ) func TestConfigurationsCRUD(t *testing.T) { client, err := clients.NewDBV1Client() if err != nil { t.Fatalf("Unable to create a DB client: %v", err) } choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatalf("Unable to get environment settings") } createOpts := &configurations.CreateOpts{ Name: "test", Description: "description", } datastore := configurations.DatastoreOpts{ Type: choices.DBDatastoreType, Version: choices.DBDatastoreVersion, } createOpts.Datastore = &datastore values := make(map[string]interface{}) values["collation_server"] = "latin1_swedish_ci" createOpts.Values = values cgroup, err := configurations.Create(client, createOpts).Extract() if err != nil { t.Fatalf("Unable to create configuration: %v", err) } err = configurations.Delete(client, cgroup.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete configuration: %v", err) } tools.PrintResource(t, cgroup) } databases_test.go000066400000000000000000000024241335005366400340040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/db/v1// +build acceptance db package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/db/v1/databases" ) // Because it takes so long to create an instance, // all tests will be housed in a single function. func TestDatabases(t *testing.T) { if testing.Short() { t.Skip("Skipping in short mode") } client, err := clients.NewDBV1Client() if err != nil { t.Fatalf("Unable to create a DB client: %v", err) } // Create and Get an instance. instance, err := CreateInstance(t, client) if err != nil { t.Fatalf("Unable to create instance: %v", err) } defer DeleteInstance(t, client, instance.ID) // Create a database. err = CreateDatabase(t, client, instance.ID) if err != nil { t.Fatalf("Unable to create database: %v", err) } // List all databases. allPages, err := databases.List(client, instance.ID).AllPages() if err != nil { t.Fatalf("Unable to list databases: %v", err) } allDatabases, err := databases.ExtractDBs(allPages) if err != nil { t.Fatalf("Unable to extract databases: %v", err) } for _, db := range allDatabases { tools.PrintResource(t, db) } defer DeleteDatabase(t, client, instance.ID, allDatabases[0].Name) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/db/v1/db.go000066400000000000000000000111531335005366400314610ustar00rootroot00000000000000// Package v2 contains common functions for creating db resources for use // in acceptance tests. See the `*_test.go` files for example usages. package v1 import ( "fmt" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/db/v1/databases" "github.com/gophercloud/gophercloud/openstack/db/v1/instances" "github.com/gophercloud/gophercloud/openstack/db/v1/users" ) // CreateDatabase will create a database with a randomly generated name. // An error will be returned if the database was unable to be created. func CreateDatabase(t *testing.T, client *gophercloud.ServiceClient, instanceID string) error { name := tools.RandomString("ACPTTEST", 8) t.Logf("Attempting to create database: %s", name) createOpts := databases.BatchCreateOpts{ databases.CreateOpts{ Name: name, }, } return databases.Create(client, instanceID, createOpts).ExtractErr() } // CreateInstance will create an instance with a randomly generated name. // The flavor of the instance will be the value of the OS_FLAVOR_ID // environment variable. The Datastore will be pulled from the // OS_DATASTORE_TYPE_ID environment variable. // An error will be returned if the instance was unable to be created. func CreateInstance(t *testing.T, client *gophercloud.ServiceClient) (*instances.Instance, error) { if testing.Short() { t.Skip("Skipping test that requires instance creation in short mode.") } choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { return nil, err } name := tools.RandomString("ACPTTEST", 8) t.Logf("Attempting to create instance: %s", name) createOpts := instances.CreateOpts{ FlavorRef: choices.FlavorID, Size: 1, Name: name, Datastore: &instances.DatastoreOpts{ Type: choices.DBDatastoreType, Version: choices.DBDatastoreVersion, }, } instance, err := instances.Create(client, createOpts).Extract() if err != nil { return instance, err } if err := WaitForInstanceStatus(client, instance, "ACTIVE"); err != nil { return instance, err } return instances.Get(client, instance.ID).Extract() } // CreateUser will create a user with a randomly generated name. // An error will be returned if the user was unable to be created. func CreateUser(t *testing.T, client *gophercloud.ServiceClient, instanceID string) error { name := tools.RandomString("ACPTTEST", 8) password := tools.RandomString("", 8) t.Logf("Attempting to create user: %s", name) createOpts := users.BatchCreateOpts{ users.CreateOpts{ Name: name, Password: password, }, } return users.Create(client, instanceID, createOpts).ExtractErr() } // DeleteDatabase deletes a database. A fatal error will occur if the database // failed to delete. This works best when used as a deferred function. func DeleteDatabase(t *testing.T, client *gophercloud.ServiceClient, instanceID, name string) { t.Logf("Attempting to delete database: %s", name) err := databases.Delete(client, instanceID, name).ExtractErr() if err != nil { t.Fatalf("Unable to delete database %s: %s", name, err) } t.Logf("Deleted database: %s", name) } // DeleteInstance deletes an instance. A fatal error will occur if the instance // failed to delete. This works best when used as a deferred function. func DeleteInstance(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete instance: %s", id) err := instances.Delete(client, id).ExtractErr() if err != nil { t.Fatalf("Unable to delete instance %s: %s", id, err) } t.Logf("Deleted instance: %s", id) } // DeleteUser deletes a user. A fatal error will occur if the user // failed to delete. This works best when used as a deferred function. func DeleteUser(t *testing.T, client *gophercloud.ServiceClient, instanceID, name string) { t.Logf("Attempting to delete user: %s", name) err := users.Delete(client, instanceID, name).ExtractErr() if err != nil { t.Fatalf("Unable to delete users %s: %s", name, err) } t.Logf("Deleted users: %s", name) } // WaitForInstanceState will poll an instance's status until it either matches // the specified status or the status becomes ERROR. func WaitForInstanceStatus( client *gophercloud.ServiceClient, instance *instances.Instance, status string) error { return tools.WaitFor(func() (bool, error) { latest, err := instances.Get(client, instance.ID).Extract() if err != nil { return false, err } if latest.Status == status { return true, nil } if latest.Status == "ERROR" { return false, fmt.Errorf("Instance in ERROR state") } return false, nil }) } flavors_test.go000066400000000000000000000024231335005366400335300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/db/v1// +build acceptance db package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/db/v1/flavors" ) func TestFlavorsList(t *testing.T) { client, err := clients.NewDBV1Client() if err != nil { t.Fatalf("Unable to create a DB client: %v", err) } allPages, err := flavors.List(client).AllPages() if err != nil { t.Fatalf("Unable to retrieve flavors: %v", err) } allFlavors, err := flavors.ExtractFlavors(allPages) if err != nil { t.Fatalf("Unable to extract flavors: %v", err) } for _, flavor := range allFlavors { tools.PrintResource(t, &flavor) } } func TestFlavorsGet(t *testing.T) { client, err := clients.NewDBV1Client() if err != nil { t.Fatalf("Unable to create a DB client: %v", err) } allPages, err := flavors.List(client).AllPages() if err != nil { t.Fatalf("Unable to retrieve flavors: %v", err) } allFlavors, err := flavors.ExtractFlavors(allPages) if err != nil { t.Fatalf("Unable to extract flavors: %v", err) } if len(allFlavors) > 0 { flavor, err := flavors.Get(client, allFlavors[0].StrID).Extract() if err != nil { t.Fatalf("Unable to get flavor: %v", err) } tools.PrintResource(t, flavor) } } instances_test.go000066400000000000000000000033421335005366400340440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/db/v1// +build acceptance db package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/db/v1/instances" ) // Because it takes so long to create an instance, // all tests will be housed in a single function. func TestInstances(t *testing.T) { if testing.Short() { t.Skip("Skipping in short mode") } client, err := clients.NewDBV1Client() if err != nil { t.Fatalf("Unable to create a DB client: %v", err) } // Create and Get an instance. instance, err := CreateInstance(t, client) if err != nil { t.Fatalf("Unable to create instance: %v", err) } defer DeleteInstance(t, client, instance.ID) tools.PrintResource(t, &instance) // List all instances. allPages, err := instances.List(client).AllPages() if err != nil { t.Fatalf("Unable to list instances: %v", err) } allInstances, err := instances.ExtractInstances(allPages) if err != nil { t.Fatalf("Unable to extract instances: %v", err) } for _, instance := range allInstances { tools.PrintResource(t, instance) } // Enable root user. _, err = instances.EnableRootUser(client, instance.ID).Extract() if err != nil { t.Fatalf("Unable to enable root user: %v", err) } enabled, err := instances.IsRootEnabled(client, instance.ID).Extract() if err != nil { t.Fatalf("Unable to check if root user is enabled: %v", err) } t.Logf("Root user is enabled: %t", enabled) // Restart err = instances.Restart(client, instance.ID).ExtractErr() if err != nil { t.Fatalf("Unable to restart instance: %v", err) } err = WaitForInstanceStatus(client, instance, "ACTIVE") if err != nil { t.Fatalf("Unable to restart instance: %v", err) } } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/db/v1/pkg.go000066400000000000000000000000131335005366400316460ustar00rootroot00000000000000package v1 users_test.go000066400000000000000000000023411335005366400332140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/db/v1// +build acceptance db package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/db/v1/users" ) // Because it takes so long to create an instance, // all tests will be housed in a single function. func TestUsers(t *testing.T) { if testing.Short() { t.Skip("Skipping in short mode") } client, err := clients.NewDBV1Client() if err != nil { t.Fatalf("Unable to create a DB client: %v", err) } // Create and Get an instance. instance, err := CreateInstance(t, client) if err != nil { t.Fatalf("Unable to create instance: %v", err) } defer DeleteInstance(t, client, instance.ID) // Create a user. err = CreateUser(t, client, instance.ID) if err != nil { t.Fatalf("Unable to create user: %v", err) } // List all users. allPages, err := users.List(client, instance.ID).AllPages() if err != nil { t.Fatalf("Unable to list users: %v", err) } allUsers, err := users.ExtractUsers(allPages) if err != nil { t.Fatalf("Unable to extract users: %v", err) } for _, user := range allUsers { tools.PrintResource(t, user) } defer DeleteUser(t, client, instance.ID, allUsers[0].Name) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/dns/000077500000000000000000000000001335005366400304155ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/dns/v2/000077500000000000000000000000001335005366400307445ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/dns/v2/dns.go000066400000000000000000000115101335005366400320550ustar00rootroot00000000000000package v2 import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets" "github.com/gophercloud/gophercloud/openstack/dns/v2/zones" th "github.com/gophercloud/gophercloud/testhelper" ) // CreateRecordSet will create a RecordSet with a random name. An error will // be returned if the zone was unable to be created. func CreateRecordSet(t *testing.T, client *gophercloud.ServiceClient, zone *zones.Zone) (*recordsets.RecordSet, error) { t.Logf("Attempting to create recordset: %s", zone.Name) createOpts := recordsets.CreateOpts{ Name: zone.Name, Type: "A", TTL: 3600, Description: "Test recordset", Records: []string{"10.1.0.2"}, } rs, err := recordsets.Create(client, zone.ID, createOpts).Extract() if err != nil { return rs, err } if err := WaitForRecordSetStatus(client, rs, "ACTIVE"); err != nil { return rs, err } newRS, err := recordsets.Get(client, rs.ZoneID, rs.ID).Extract() if err != nil { return newRS, err } t.Logf("Created record set: %s", newRS.Name) th.AssertEquals(t, newRS.Name, zone.Name) return rs, nil } // CreateZone will create a Zone with a random name. An error will // be returned if the zone was unable to be created. func CreateZone(t *testing.T, client *gophercloud.ServiceClient) (*zones.Zone, error) { zoneName := tools.RandomString("ACPTTEST", 8) + ".com." t.Logf("Attempting to create zone: %s", zoneName) createOpts := zones.CreateOpts{ Name: zoneName, Email: "root@example.com", Type: "PRIMARY", TTL: 7200, Description: "Test zone", } zone, err := zones.Create(client, createOpts).Extract() if err != nil { return zone, err } if err := WaitForZoneStatus(client, zone, "ACTIVE"); err != nil { return zone, err } newZone, err := zones.Get(client, zone.ID).Extract() if err != nil { return zone, err } t.Logf("Created Zone: %s", zoneName) th.AssertEquals(t, newZone.Name, zoneName) th.AssertEquals(t, newZone.TTL, 7200) return newZone, nil } // CreateSecondaryZone will create a Zone with a random name. An error will // be returned if the zone was unable to be created. // // This is only for example purposes as it will try to do a zone transfer. func CreateSecondaryZone(t *testing.T, client *gophercloud.ServiceClient) (*zones.Zone, error) { zoneName := tools.RandomString("ACPTTEST", 8) + ".com." t.Logf("Attempting to create zone: %s", zoneName) createOpts := zones.CreateOpts{ Name: zoneName, Type: "SECONDARY", Masters: []string{"10.0.0.1"}, } zone, err := zones.Create(client, createOpts).Extract() if err != nil { return zone, err } if err := WaitForZoneStatus(client, zone, "ACTIVE"); err != nil { return zone, err } newZone, err := zones.Get(client, zone.ID).Extract() if err != nil { return zone, err } t.Logf("Created Zone: %s", zoneName) th.AssertEquals(t, newZone.Name, zoneName) th.AssertEquals(t, newZone.Masters[0], "10.0.0.1") return newZone, nil } // DeleteRecordSet will delete a specified record set. A fatal error will occur if // the record set failed to be deleted. This works best when used as a deferred // function. func DeleteRecordSet(t *testing.T, client *gophercloud.ServiceClient, rs *recordsets.RecordSet) { err := recordsets.Delete(client, rs.ZoneID, rs.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete record set %s: %v", rs.ID, err) } t.Logf("Deleted record set: %s", rs.ID) } // DeleteZone will delete a specified zone. A fatal error will occur if // the zone failed to be deleted. This works best when used as a deferred // function. func DeleteZone(t *testing.T, client *gophercloud.ServiceClient, zone *zones.Zone) { _, err := zones.Delete(client, zone.ID).Extract() if err != nil { t.Fatalf("Unable to delete zone %s: %v", zone.ID, err) } t.Logf("Deleted zone: %s", zone.ID) } // WaitForRecordSetStatus will poll a record set's status until it either matches // the specified status or the status becomes ERROR. func WaitForRecordSetStatus(client *gophercloud.ServiceClient, rs *recordsets.RecordSet, status string) error { return gophercloud.WaitFor(600, func() (bool, error) { current, err := recordsets.Get(client, rs.ZoneID, rs.ID).Extract() if err != nil { return false, err } if current.Status == status { return true, nil } return false, nil }) } // WaitForZoneStatus will poll a zone's status until it either matches // the specified status or the status becomes ERROR. func WaitForZoneStatus(client *gophercloud.ServiceClient, zone *zones.Zone, status string) error { return gophercloud.WaitFor(600, func() (bool, error) { current, err := zones.Get(client, zone.ID).Extract() if err != nil { return false, err } if current.Status == status { return true, nil } return false, nil }) } recordsets_test.go000066400000000000000000000036651335005366400344420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/dns/v2// +build acceptance dns recordsets package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestRecordSetsListByZone(t *testing.T) { clients.RequireDNS(t) client, err := clients.NewDNSV2Client() th.AssertNoErr(t, err) zone, err := CreateZone(t, client) th.AssertNoErr(t, err) defer DeleteZone(t, client, zone) allPages, err := recordsets.ListByZone(client, zone.ID, nil).AllPages() th.AssertNoErr(t, err) allRecordSets, err := recordsets.ExtractRecordSets(allPages) th.AssertNoErr(t, err) var found bool for _, recordset := range allRecordSets { tools.PrintResource(t, &recordset) if recordset.ZoneID == zone.ID { found = true } } th.AssertEquals(t, found, true) listOpts := recordsets.ListOpts{ Limit: 1, } err = recordsets.ListByZone(client, zone.ID, listOpts).EachPage( func(page pagination.Page) (bool, error) { rr, err := recordsets.ExtractRecordSets(page) th.AssertNoErr(t, err) th.AssertEquals(t, len(rr), 1) return true, nil }, ) th.AssertNoErr(t, err) } func TestRecordSetsCRUD(t *testing.T) { clients.RequireDNS(t) client, err := clients.NewDNSV2Client() th.AssertNoErr(t, err) zone, err := CreateZone(t, client) th.AssertNoErr(t, err) defer DeleteZone(t, client, zone) tools.PrintResource(t, &zone) rs, err := CreateRecordSet(t, client, zone) th.AssertNoErr(t, err) defer DeleteRecordSet(t, client, rs) tools.PrintResource(t, &rs) updateOpts := recordsets.UpdateOpts{ Description: "New description", TTL: 0, } newRS, err := recordsets.Update(client, rs.ZoneID, rs.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, &newRS) th.AssertEquals(t, newRS.Description, "New description") } zones_test.go000066400000000000000000000021611335005366400334110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/dns/v2// +build acceptance dns zones package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/dns/v2/zones" th "github.com/gophercloud/gophercloud/testhelper" ) func TestZonesCRUD(t *testing.T) { clients.RequireDNS(t) client, err := clients.NewDNSV2Client() th.AssertNoErr(t, err) zone, err := CreateZone(t, client) th.AssertNoErr(t, err) defer DeleteZone(t, client, zone) tools.PrintResource(t, &zone) allPages, err := zones.List(client, nil).AllPages() th.AssertNoErr(t, err) allZones, err := zones.ExtractZones(allPages) th.AssertNoErr(t, err) var found bool for _, z := range allZones { tools.PrintResource(t, &z) if zone.Name == z.Name { found = true } } th.AssertEquals(t, found, true) updateOpts := zones.UpdateOpts{ Description: "New description", TTL: 0, } newZone, err := zones.Update(client, zone.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, &newZone) th.AssertEquals(t, newZone.Description, "New description") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/000077500000000000000000000000001335005366400314625ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v2/000077500000000000000000000000001335005366400320115ustar00rootroot00000000000000extension_test.go000066400000000000000000000021371335005366400353370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v2// +build acceptance identity package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v2/extensions" th "github.com/gophercloud/gophercloud/testhelper" ) func TestExtensionsList(t *testing.T) { clients.RequireIdentityV2(t) clients.RequireAdmin(t) client, err := clients.NewIdentityV2Client() th.AssertNoErr(t, err) allPages, err := extensions.List(client).AllPages() th.AssertNoErr(t, err) allExtensions, err := extensions.ExtractExtensions(allPages) th.AssertNoErr(t, err) var found bool for _, extension := range allExtensions { tools.PrintResource(t, extension) if extension.Name == "OS-KSCRUD" { found = true } } th.AssertEquals(t, found, true) } func TestExtensionsGet(t *testing.T) { clients.RequireIdentityV2(t) clients.RequireAdmin(t) client, err := clients.NewIdentityV2Client() th.AssertNoErr(t, err) extension, err := extensions.Get(client, "OS-KSCRUD").Extract() th.AssertNoErr(t, err) tools.PrintResource(t, extension) } identity.go000066400000000000000000000133211335005366400341120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v2// Package v2 contains common functions for creating identity-based resources // for use in acceptance tests. See the `*_test.go` files for example usages. package v2 import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v2/extensions/admin/roles" "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" "github.com/gophercloud/gophercloud/openstack/identity/v2/users" th "github.com/gophercloud/gophercloud/testhelper" ) // AddUserRole will grant a role to a user in a tenant. An error will be // returned if the grant was unsuccessful. func AddUserRole(t *testing.T, client *gophercloud.ServiceClient, tenant *tenants.Tenant, user *users.User, role *roles.Role) error { t.Logf("Attempting to grant user %s role %s in tenant %s", user.ID, role.ID, tenant.ID) err := roles.AddUser(client, tenant.ID, user.ID, role.ID).ExtractErr() if err != nil { return err } t.Logf("Granted user %s role %s in tenant %s", user.ID, role.ID, tenant.ID) return nil } // CreateTenant will create a project with a random name. // It takes an optional createOpts parameter since creating a project // has so many options. An error will be returned if the project was // unable to be created. func CreateTenant(t *testing.T, client *gophercloud.ServiceClient, c *tenants.CreateOpts) (*tenants.Tenant, error) { name := tools.RandomString("ACPTTEST", 8) t.Logf("Attempting to create tenant: %s", name) var createOpts tenants.CreateOpts if c != nil { createOpts = *c } else { createOpts = tenants.CreateOpts{} } createOpts.Name = name tenant, err := tenants.Create(client, createOpts).Extract() if err != nil { return tenant, err } t.Logf("Successfully created project %s with ID %s", name, tenant.ID) th.AssertEquals(t, name, tenant.Name) return tenant, nil } // CreateUser will create a user with a random name and adds them to the given // tenant. An error will be returned if the user was unable to be created. func CreateUser(t *testing.T, client *gophercloud.ServiceClient, tenant *tenants.Tenant) (*users.User, error) { userName := tools.RandomString("user_", 5) userEmail := userName + "@foo.com" t.Logf("Creating user: %s", userName) createOpts := users.CreateOpts{ Name: userName, Enabled: gophercloud.Disabled, TenantID: tenant.ID, Email: userEmail, } user, err := users.Create(client, createOpts).Extract() if err != nil { return user, err } th.AssertEquals(t, userName, user.Name) return user, nil } // DeleteTenant will delete a tenant by ID. A fatal error will occur if // the tenant ID failed to be deleted. This works best when using it as // a deferred function. func DeleteTenant(t *testing.T, client *gophercloud.ServiceClient, tenantID string) { err := tenants.Delete(client, tenantID).ExtractErr() if err != nil { t.Fatalf("Unable to delete tenant %s: %v", tenantID, err) } t.Logf("Deleted tenant: %s", tenantID) } // DeleteUser will delete a user. A fatal error will occur if the delete was // unsuccessful. This works best when used as a deferred function. func DeleteUser(t *testing.T, client *gophercloud.ServiceClient, user *users.User) { t.Logf("Attempting to delete user: %s", user.Name) result := users.Delete(client, user.ID) if result.Err != nil { t.Fatalf("Unable to delete user") } t.Logf("Deleted user: %s", user.Name) } // DeleteUserRole will revoke a role of a user in a tenant. A fatal error will // occur if the revoke was unsuccessful. This works best when used as a // deferred function. func DeleteUserRole(t *testing.T, client *gophercloud.ServiceClient, tenant *tenants.Tenant, user *users.User, role *roles.Role) { t.Logf("Attempting to remove role %s from user %s in tenant %s", role.ID, user.ID, tenant.ID) err := roles.DeleteUser(client, tenant.ID, user.ID, role.ID).ExtractErr() if err != nil { t.Fatalf("Unable to remove role") } t.Logf("Removed role %s from user %s in tenant %s", role.ID, user.ID, tenant.ID) } // FindRole finds all roles that the current authenticated client has access // to and returns the first one found. An error will be returned if the lookup // was unsuccessful. func FindRole(t *testing.T, client *gophercloud.ServiceClient) (*roles.Role, error) { var role *roles.Role allPages, err := roles.List(client).AllPages() if err != nil { return role, err } allRoles, err := roles.ExtractRoles(allPages) if err != nil { return role, err } for _, r := range allRoles { role = &r break } return role, nil } // FindTenant finds all tenants that the current authenticated client has access // to and returns the first one found. An error will be returned if the lookup // was unsuccessful. func FindTenant(t *testing.T, client *gophercloud.ServiceClient) (*tenants.Tenant, error) { var tenant *tenants.Tenant allPages, err := tenants.List(client, nil).AllPages() if err != nil { return tenant, err } allTenants, err := tenants.ExtractTenants(allPages) if err != nil { return tenant, err } for _, t := range allTenants { tenant = &t break } return tenant, nil } // UpdateUser will update an existing user with a new randomly generated name. // An error will be returned if the update was unsuccessful. func UpdateUser(t *testing.T, client *gophercloud.ServiceClient, user *users.User) (*users.User, error) { userName := tools.RandomString("user_", 5) userEmail := userName + "@foo.com" t.Logf("Attempting to update user name from %s to %s", user.Name, userName) updateOpts := users.UpdateOpts{ Name: userName, Email: userEmail, } newUser, err := users.Update(client, user.ID, updateOpts).Extract() if err != nil { return newUser, err } th.AssertEquals(t, userName, newUser.Name) return newUser, nil } pkg.go000066400000000000000000000000131335005366400330340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v2package v2 role_test.go000066400000000000000000000033341335005366400342640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v2// +build acceptance identity roles package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v2/extensions/admin/roles" "github.com/gophercloud/gophercloud/openstack/identity/v2/users" th "github.com/gophercloud/gophercloud/testhelper" ) func TestRolesAddToUser(t *testing.T) { clients.RequireIdentityV2(t) clients.RequireAdmin(t) client, err := clients.NewIdentityV2AdminClient() th.AssertNoErr(t, err) tenant, err := FindTenant(t, client) th.AssertNoErr(t, err) role, err := FindRole(t, client) th.AssertNoErr(t, err) user, err := CreateUser(t, client, tenant) th.AssertNoErr(t, err) defer DeleteUser(t, client, user) err = AddUserRole(t, client, tenant, user, role) th.AssertNoErr(t, err) defer DeleteUserRole(t, client, tenant, user, role) allPages, err := users.ListRoles(client, tenant.ID, user.ID).AllPages() th.AssertNoErr(t, err) allRoles, err := users.ExtractRoles(allPages) th.AssertNoErr(t, err) t.Logf("Roles of user %s:", user.Name) var found bool for _, r := range allRoles { tools.PrintResource(t, role) if r.Name == role.Name { found = true } } th.AssertEquals(t, found, true) } func TestRolesList(t *testing.T) { clients.RequireIdentityV2(t) clients.RequireAdmin(t) client, err := clients.NewIdentityV2AdminClient() th.AssertNoErr(t, err) allPages, err := roles.List(client).AllPages() th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoles(allPages) th.AssertNoErr(t, err) var found bool for _, r := range allRoles { tools.PrintResource(t, r) if r.Name == "admin" { found = true } } th.AssertEquals(t, found, true) } tenant_test.go000066400000000000000000000026621335005366400346170ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v2// +build acceptance identity package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" th "github.com/gophercloud/gophercloud/testhelper" ) func TestTenantsList(t *testing.T) { clients.RequireIdentityV2(t) clients.RequireAdmin(t) client, err := clients.NewIdentityV2Client() th.AssertNoErr(t, err) allPages, err := tenants.List(client, nil).AllPages() th.AssertNoErr(t, err) allTenants, err := tenants.ExtractTenants(allPages) th.AssertNoErr(t, err) var found bool for _, tenant := range allTenants { tools.PrintResource(t, tenant) if tenant.Name == "admin" { found = true } } th.AssertEquals(t, found, true) } func TestTenantsCRUD(t *testing.T) { clients.RequireIdentityV2(t) clients.RequireAdmin(t) client, err := clients.NewIdentityV2AdminClient() th.AssertNoErr(t, err) tenant, err := CreateTenant(t, client, nil) th.AssertNoErr(t, err) defer DeleteTenant(t, client, tenant.ID) tenant, err = tenants.Get(client, tenant.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, tenant) updateOpts := tenants.UpdateOpts{ Description: "some tenant", } newTenant, err := tenants.Update(client, tenant.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newTenant) th.AssertEquals(t, newTenant.Description, "some tenant") } token_test.go000066400000000000000000000026151335005366400344440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v2// +build acceptance identity package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" th "github.com/gophercloud/gophercloud/testhelper" ) func TestTokenAuthenticate(t *testing.T) { clients.RequireIdentityV2(t) clients.RequireAdmin(t) client, err := clients.NewIdentityV2UnauthenticatedClient() th.AssertNoErr(t, err) authOptions, err := openstack.AuthOptionsFromEnv() th.AssertNoErr(t, err) result := tokens.Create(client, authOptions) token, err := result.ExtractToken() th.AssertNoErr(t, err) tools.PrintResource(t, token) catalog, err := result.ExtractServiceCatalog() th.AssertNoErr(t, err) for _, entry := range catalog.Entries { tools.PrintResource(t, entry) } } func TestTokenValidate(t *testing.T) { clients.RequireIdentityV2(t) clients.RequireAdmin(t) client, err := clients.NewIdentityV2Client() th.AssertNoErr(t, err) authOptions, err := openstack.AuthOptionsFromEnv() th.AssertNoErr(t, err) result := tokens.Create(client, authOptions) token, err := result.ExtractToken() th.AssertNoErr(t, err) tools.PrintResource(t, token) getResult := tokens.Get(client, token.ID) user, err := getResult.ExtractUser() th.AssertNoErr(t, err) tools.PrintResource(t, user) } user_test.go000066400000000000000000000023561335005366400343040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v2// +build acceptance identity package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v2/users" th "github.com/gophercloud/gophercloud/testhelper" ) func TestUsersList(t *testing.T) { clients.RequireIdentityV2(t) clients.RequireAdmin(t) client, err := clients.NewIdentityV2AdminClient() th.AssertNoErr(t, err) allPages, err := users.List(client).AllPages() th.AssertNoErr(t, err) allUsers, err := users.ExtractUsers(allPages) th.AssertNoErr(t, err) var found bool for _, user := range allUsers { tools.PrintResource(t, user) if user.Name == "admin" { found = true } } th.AssertEquals(t, found, true) } func TestUsersCreateUpdateDelete(t *testing.T) { clients.RequireIdentityV2(t) clients.RequireAdmin(t) client, err := clients.NewIdentityV2AdminClient() th.AssertNoErr(t, err) tenant, err := FindTenant(t, client) th.AssertNoErr(t, err) user, err := CreateUser(t, client, tenant) th.AssertNoErr(t, err) defer DeleteUser(t, client, user) tools.PrintResource(t, user) newUser, err := UpdateUser(t, client, user) th.AssertNoErr(t, err) tools.PrintResource(t, newUser) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v3/000077500000000000000000000000001335005366400320125ustar00rootroot00000000000000domains_test.go000066400000000000000000000035751335005366400347650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v3// +build acceptance package v3 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/domains" th "github.com/gophercloud/gophercloud/testhelper" ) func TestDomainsList(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) var iTrue bool = true listOpts := domains.ListOpts{ Enabled: &iTrue, } allPages, err := domains.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allDomains, err := domains.ExtractDomains(allPages) th.AssertNoErr(t, err) var found bool for _, domain := range allDomains { tools.PrintResource(t, domain) if domain.Name == "Default" { found = true } } th.AssertEquals(t, found, true) } func TestDomainsGet(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) p, err := domains.Get(client, "default").Extract() th.AssertNoErr(t, err) tools.PrintResource(t, p) th.AssertEquals(t, p.Name, "Default") } func TestDomainsCRUD(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) var iTrue bool = true createOpts := domains.CreateOpts{ Description: "Testing Domain", Enabled: &iTrue, } domain, err := CreateDomain(t, client, &createOpts) th.AssertNoErr(t, err) defer DeleteDomain(t, client, domain.ID) tools.PrintResource(t, domain) th.AssertEquals(t, domain.Description, "Testing Domain") var iFalse bool = false updateOpts := domains.UpdateOpts{ Description: "Staging Test Domain", Enabled: &iFalse, } newDomain, err := domains.Update(client, domain.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newDomain) th.AssertEquals(t, newDomain.Description, "Staging Test Domain") } endpoint_test.go000066400000000000000000000035661335005366400351530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v3// +build acceptance package v3 import ( "strings" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/endpoints" "github.com/gophercloud/gophercloud/openstack/identity/v3/services" th "github.com/gophercloud/gophercloud/testhelper" ) func TestEndpointsList(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) allPages, err := endpoints.List(client, nil).AllPages() th.AssertNoErr(t, err) allEndpoints, err := endpoints.ExtractEndpoints(allPages) th.AssertNoErr(t, err) var found bool for _, endpoint := range allEndpoints { tools.PrintResource(t, endpoint) if strings.Contains(endpoint.URL, "/v3") { found = true } } th.AssertEquals(t, found, true) } func TestEndpointsNavigateCatalog(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) // Discover the service we're interested in. serviceListOpts := services.ListOpts{ ServiceType: "compute", } allPages, err := services.List(client, serviceListOpts).AllPages() th.AssertNoErr(t, err) allServices, err := services.ExtractServices(allPages) th.AssertNoErr(t, err) th.AssertEquals(t, len(allServices), 1) computeService := allServices[0] tools.PrintResource(t, computeService) // Enumerate the endpoints available for this service. endpointListOpts := endpoints.ListOpts{ Availability: gophercloud.AvailabilityPublic, ServiceID: computeService.ID, } allPages, err = endpoints.List(client, endpointListOpts).AllPages() th.AssertNoErr(t, err) allEndpoints, err := endpoints.ExtractEndpoints(allPages) th.AssertNoErr(t, err) th.AssertEquals(t, len(allServices), 1) tools.PrintResource(t, allEndpoints[0]) } groups_test.go000066400000000000000000000052371335005366400346470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v3// +build acceptance package v3 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" th "github.com/gophercloud/gophercloud/testhelper" ) func TestGroupCRUD(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) createOpts := groups.CreateOpts{ Name: "testgroup", DomainID: "default", Extra: map[string]interface{}{ "email": "testgroup@example.com", }, } // Create Group in the default domain group, err := CreateGroup(t, client, &createOpts) th.AssertNoErr(t, err) defer DeleteGroup(t, client, group.ID) tools.PrintResource(t, group) tools.PrintResource(t, group.Extra) updateOpts := groups.UpdateOpts{ Description: "Test Groups", Extra: map[string]interface{}{ "email": "thetestgroup@example.com", }, } newGroup, err := groups.Update(client, group.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newGroup) tools.PrintResource(t, newGroup.Extra) listOpts := groups.ListOpts{ DomainID: "default", } // List all Groups in default domain allPages, err := groups.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allGroups, err := groups.ExtractGroups(allPages) th.AssertNoErr(t, err) for _, g := range allGroups { tools.PrintResource(t, g) tools.PrintResource(t, g.Extra) } var found bool for _, group := range allGroups { tools.PrintResource(t, group) tools.PrintResource(t, group.Extra) if group.Name == newGroup.Name { found = true } } th.AssertEquals(t, found, true) listOpts.Filters = map[string]string{ "name__contains": "TEST", } allPages, err = groups.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allGroups, err = groups.ExtractGroups(allPages) th.AssertNoErr(t, err) found = false for _, group := range allGroups { tools.PrintResource(t, group) tools.PrintResource(t, group.Extra) if group.Name == newGroup.Name { found = true } } th.AssertEquals(t, found, true) listOpts.Filters = map[string]string{ "name__contains": "foo", } allPages, err = groups.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allGroups, err = groups.ExtractGroups(allPages) th.AssertNoErr(t, err) found = false for _, group := range allGroups { tools.PrintResource(t, group) tools.PrintResource(t, group.Extra) if group.Name == newGroup.Name { found = true } } th.AssertEquals(t, found, false) // Get the recently created group by ID p, err := groups.Get(client, group.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, p) } identity.go000066400000000000000000000242361335005366400341220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v3package v3 import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/domains" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" "github.com/gophercloud/gophercloud/openstack/identity/v3/regions" "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" "github.com/gophercloud/gophercloud/openstack/identity/v3/services" "github.com/gophercloud/gophercloud/openstack/identity/v3/users" th "github.com/gophercloud/gophercloud/testhelper" ) // CreateProject will create a project with a random name. // It takes an optional createOpts parameter since creating a project // has so many options. An error will be returned if the project was // unable to be created. func CreateProject(t *testing.T, client *gophercloud.ServiceClient, c *projects.CreateOpts) (*projects.Project, error) { name := tools.RandomString("ACPTTEST", 8) t.Logf("Attempting to create project: %s", name) var createOpts projects.CreateOpts if c != nil { createOpts = *c } else { createOpts = projects.CreateOpts{} } createOpts.Name = name project, err := projects.Create(client, createOpts).Extract() if err != nil { return project, err } t.Logf("Successfully created project %s with ID %s", name, project.ID) th.AssertEquals(t, project.Name, name) return project, nil } // CreateUser will create a user with a random name. // It takes an optional createOpts parameter since creating a user // has so many options. An error will be returned if the user was // unable to be created. func CreateUser(t *testing.T, client *gophercloud.ServiceClient, c *users.CreateOpts) (*users.User, error) { name := tools.RandomString("ACPTTEST", 8) t.Logf("Attempting to create user: %s", name) var createOpts users.CreateOpts if c != nil { createOpts = *c } else { createOpts = users.CreateOpts{} } createOpts.Name = name user, err := users.Create(client, createOpts).Extract() if err != nil { return user, err } t.Logf("Successfully created user %s with ID %s", name, user.ID) th.AssertEquals(t, user.Name, name) return user, nil } // CreateGroup will create a group with a random name. // It takes an optional createOpts parameter since creating a group // has so many options. An error will be returned if the group was // unable to be created. func CreateGroup(t *testing.T, client *gophercloud.ServiceClient, c *groups.CreateOpts) (*groups.Group, error) { name := tools.RandomString("ACPTTEST", 8) t.Logf("Attempting to create group: %s", name) var createOpts groups.CreateOpts if c != nil { createOpts = *c } else { createOpts = groups.CreateOpts{} } createOpts.Name = name group, err := groups.Create(client, createOpts).Extract() if err != nil { return group, err } t.Logf("Successfully created group %s with ID %s", name, group.ID) th.AssertEquals(t, group.Name, name) return group, nil } // CreateDomain will create a domain with a random name. // It takes an optional createOpts parameter since creating a domain // has many options. An error will be returned if the domain was // unable to be created. func CreateDomain(t *testing.T, client *gophercloud.ServiceClient, c *domains.CreateOpts) (*domains.Domain, error) { name := tools.RandomString("ACPTTEST", 8) t.Logf("Attempting to create domain: %s", name) var createOpts domains.CreateOpts if c != nil { createOpts = *c } else { createOpts = domains.CreateOpts{} } createOpts.Name = name domain, err := domains.Create(client, createOpts).Extract() if err != nil { return domain, err } t.Logf("Successfully created domain %s with ID %s", name, domain.ID) th.AssertEquals(t, domain.Name, name) return domain, nil } // CreateRole will create a role with a random name. // It takes an optional createOpts parameter since creating a role // has so many options. An error will be returned if the role was // unable to be created. func CreateRole(t *testing.T, client *gophercloud.ServiceClient, c *roles.CreateOpts) (*roles.Role, error) { name := tools.RandomString("ACPTTEST", 8) t.Logf("Attempting to create role: %s", name) var createOpts roles.CreateOpts if c != nil { createOpts = *c } else { createOpts = roles.CreateOpts{} } createOpts.Name = name role, err := roles.Create(client, createOpts).Extract() if err != nil { return role, err } t.Logf("Successfully created role %s with ID %s", name, role.ID) th.AssertEquals(t, role.Name, name) return role, nil } // CreateRegion will create a region with a random name. // It takes an optional createOpts parameter since creating a region // has so many options. An error will be returned if the region was // unable to be created. func CreateRegion(t *testing.T, client *gophercloud.ServiceClient, c *regions.CreateOpts) (*regions.Region, error) { id := tools.RandomString("ACPTTEST", 8) t.Logf("Attempting to create region: %s", id) var createOpts regions.CreateOpts if c != nil { createOpts = *c } else { createOpts = regions.CreateOpts{} } createOpts.ID = id region, err := regions.Create(client, createOpts).Extract() if err != nil { return region, err } t.Logf("Successfully created region %s", id) th.AssertEquals(t, region.ID, id) return region, nil } // CreateService will create a service with a random name. // It takes an optional createOpts parameter since creating a service // has so many options. An error will be returned if the service was // unable to be created. func CreateService(t *testing.T, client *gophercloud.ServiceClient, c *services.CreateOpts) (*services.Service, error) { name := tools.RandomString("ACPTTEST", 8) t.Logf("Attempting to create service: %s", name) var createOpts services.CreateOpts if c != nil { createOpts = *c } else { createOpts = services.CreateOpts{} } createOpts.Extra["name"] = name service, err := services.Create(client, createOpts).Extract() if err != nil { return service, err } t.Logf("Successfully created service %s", service.ID) th.AssertEquals(t, service.Extra["name"], name) return service, nil } // DeleteProject will delete a project by ID. A fatal error will occur if // the project ID failed to be deleted. This works best when using it as // a deferred function. func DeleteProject(t *testing.T, client *gophercloud.ServiceClient, projectID string) { err := projects.Delete(client, projectID).ExtractErr() if err != nil { t.Fatalf("Unable to delete project %s: %v", projectID, err) } t.Logf("Deleted project: %s", projectID) } // DeleteUser will delete a user by ID. A fatal error will occur if // the user failed to be deleted. This works best when using it as // a deferred function. func DeleteUser(t *testing.T, client *gophercloud.ServiceClient, userID string) { err := users.Delete(client, userID).ExtractErr() if err != nil { t.Fatalf("Unable to delete user with ID %s: %v", userID, err) } t.Logf("Deleted user with ID: %s", userID) } // DeleteGroup will delete a group by ID. A fatal error will occur if // the group failed to be deleted. This works best when using it as // a deferred function. func DeleteGroup(t *testing.T, client *gophercloud.ServiceClient, groupID string) { err := groups.Delete(client, groupID).ExtractErr() if err != nil { t.Fatalf("Unable to delete group %s: %v", groupID, err) } t.Logf("Deleted group: %s", groupID) } // DeleteDomain will delete a domain by ID. A fatal error will occur if // the project ID failed to be deleted. This works best when using it as // a deferred function. func DeleteDomain(t *testing.T, client *gophercloud.ServiceClient, domainID string) { err := domains.Delete(client, domainID).ExtractErr() if err != nil { t.Fatalf("Unable to delete domain %s: %v", domainID, err) } t.Logf("Deleted domain: %s", domainID) } // DeleteRole will delete a role by ID. A fatal error will occur if // the role failed to be deleted. This works best when using it as // a deferred function. func DeleteRole(t *testing.T, client *gophercloud.ServiceClient, roleID string) { err := roles.Delete(client, roleID).ExtractErr() if err != nil { t.Fatalf("Unable to delete role %s: %v", roleID, err) } t.Logf("Deleted role: %s", roleID) } // DeleteRegion will delete a reg by ID. A fatal error will occur if // the region failed to be deleted. This works best when using it as // a deferred function. func DeleteRegion(t *testing.T, client *gophercloud.ServiceClient, regionID string) { err := regions.Delete(client, regionID).ExtractErr() if err != nil { t.Fatalf("Unable to delete region %s: %v", regionID, err) } t.Logf("Deleted region: %s", regionID) } // DeleteService will delete a reg by ID. A fatal error will occur if // the service failed to be deleted. This works best when using it as // a deferred function. func DeleteService(t *testing.T, client *gophercloud.ServiceClient, serviceID string) { err := services.Delete(client, serviceID).ExtractErr() if err != nil { t.Fatalf("Unable to delete service %s: %v", serviceID, err) } t.Logf("Deleted service: %s", serviceID) } // UnassignRole will delete a role assigned to a user/group on a project/domain // A fatal error will occur if it fails to delete the assignment. // This works best when using it as a deferred function. func UnassignRole(t *testing.T, client *gophercloud.ServiceClient, roleID string, opts *roles.UnassignOpts) { err := roles.Unassign(client, roleID, *opts).ExtractErr() if err != nil { t.Fatalf("Unable to unassign a role %v on context %+v: %v", roleID, *opts, err) } t.Logf("Unassigned the role %v on context %+v", roleID, *opts) } // FindRole finds all roles that the current authenticated client has access // to and returns the first one found. An error will be returned if the lookup // was unsuccessful. func FindRole(t *testing.T, client *gophercloud.ServiceClient) (*roles.Role, error) { t.Log("Attempting to find a role") var role *roles.Role allPages, err := roles.List(client, nil).AllPages() if err != nil { return nil, err } allRoles, err := roles.ExtractRoles(allPages) if err != nil { return nil, err } for _, r := range allRoles { role = &r break } t.Logf("Successfully found a role %s with ID %s", role.Name, role.ID) return role, nil } pkg.go000066400000000000000000000000131335005366400330350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v3package v3 policies_test.go000066400000000000000000000065701335005366400351400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v3// +build acceptance package v3 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/policies" th "github.com/gophercloud/gophercloud/testhelper" ) func TestPoliciesList(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) allPages, err := policies.List(client, policies.ListOpts{}).AllPages() th.AssertNoErr(t, err) allPolicies, err := policies.ExtractPolicies(allPages) th.AssertNoErr(t, err) for _, policy := range allPolicies { tools.PrintResource(t, policy) } } func TestPoliciesCRUD(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) createOpts := policies.CreateOpts{ Type: "application/json", Blob: []byte("{'foobar_user': 'role:compute-user'}"), Extra: map[string]interface{}{ "description": "policy for foobar_user", }, } policy, err := policies.Create(client, &createOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, policy) tools.PrintResource(t, policy.Extra) th.AssertEquals(t, policy.Type, createOpts.Type) th.AssertEquals(t, policy.Blob, string(createOpts.Blob)) th.AssertEquals(t, policy.Extra["description"], createOpts.Extra["description"]) var listOpts policies.ListOpts allPages, err := policies.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allPolicies, err := policies.ExtractPolicies(allPages) th.AssertNoErr(t, err) var found bool for _, p := range allPolicies { tools.PrintResource(t, p) tools.PrintResource(t, p.Extra) if p.ID == policy.ID { found = true } } th.AssertEquals(t, true, found) listOpts.Filters = map[string]string{ "type__contains": "json", } allPages, err = policies.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allPolicies, err = policies.ExtractPolicies(allPages) th.AssertNoErr(t, err) found = false for _, p := range allPolicies { tools.PrintResource(t, p) tools.PrintResource(t, p.Extra) if p.ID == policy.ID { found = true } } th.AssertEquals(t, true, found) listOpts.Filters = map[string]string{ "type__contains": "foobar", } allPages, err = policies.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allPolicies, err = policies.ExtractPolicies(allPages) th.AssertNoErr(t, err) found = false for _, p := range allPolicies { tools.PrintResource(t, p) tools.PrintResource(t, p.Extra) if p.ID == policy.ID { found = true } } th.AssertEquals(t, false, found) gotPolicy, err := policies.Get(client, policy.ID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, policy, gotPolicy) updateOpts := policies.UpdateOpts{ Type: "text/plain", Blob: []byte("'foobar_user': 'role:compute-user'"), Extra: map[string]interface{}{ "description": "updated policy for foobar_user", }, } updatedPolicy, err := policies.Update(client, policy.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedPolicy) tools.PrintResource(t, updatedPolicy.Extra) th.AssertEquals(t, updatedPolicy.Type, updateOpts.Type) th.AssertEquals(t, updatedPolicy.Blob, string(updateOpts.Blob)) th.AssertEquals(t, updatedPolicy.Extra["description"], updateOpts.Extra["description"]) err = policies.Delete(client, policy.ID).ExtractErr() th.AssertNoErr(t, err) } projects_test.go000066400000000000000000000100701335005366400351500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v3// +build acceptance package v3 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" th "github.com/gophercloud/gophercloud/testhelper" ) func TestProjectsList(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) var iTrue bool = true listOpts := projects.ListOpts{ Enabled: &iTrue, } allPages, err := projects.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allProjects, err := projects.ExtractProjects(allPages) th.AssertNoErr(t, err) var found bool for _, project := range allProjects { tools.PrintResource(t, project) if project.Name == "admin" { found = true } } th.AssertEquals(t, found, true) listOpts.Filters = map[string]string{ "name__contains": "dmi", } allPages, err = projects.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allProjects, err = projects.ExtractProjects(allPages) th.AssertNoErr(t, err) found = false for _, project := range allProjects { tools.PrintResource(t, project) if project.Name == "admin" { found = true } } th.AssertEquals(t, found, true) listOpts.Filters = map[string]string{ "name__contains": "foo", } allPages, err = projects.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allProjects, err = projects.ExtractProjects(allPages) th.AssertNoErr(t, err) found = false for _, project := range allProjects { tools.PrintResource(t, project) if project.Name == "admin" { found = true } } th.AssertEquals(t, found, false) } func TestProjectsGet(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) allPages, err := projects.List(client, nil).AllPages() th.AssertNoErr(t, err) allProjects, err := projects.ExtractProjects(allPages) th.AssertNoErr(t, err) project := allProjects[0] p, err := projects.Get(client, project.ID).Extract() if err != nil { t.Fatalf("Unable to get project: %v", err) } tools.PrintResource(t, p) th.AssertEquals(t, project.Name, p.Name) } func TestProjectsCRUD(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) project, err := CreateProject(t, client, nil) th.AssertNoErr(t, err) defer DeleteProject(t, client, project.ID) tools.PrintResource(t, project) var iFalse bool = false updateOpts := projects.UpdateOpts{ Enabled: &iFalse, } updatedProject, err := projects.Update(client, project.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedProject) } func TestProjectsDomain(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) var iTrue = true createOpts := projects.CreateOpts{ IsDomain: &iTrue, } projectDomain, err := CreateProject(t, client, &createOpts) th.AssertNoErr(t, err) defer DeleteProject(t, client, projectDomain.ID) tools.PrintResource(t, projectDomain) createOpts = projects.CreateOpts{ DomainID: projectDomain.ID, } project, err := CreateProject(t, client, &createOpts) th.AssertNoErr(t, err) defer DeleteProject(t, client, project.ID) tools.PrintResource(t, project) th.AssertEquals(t, project.DomainID, projectDomain.ID) var iFalse = false updateOpts := projects.UpdateOpts{ Enabled: &iFalse, } _, err = projects.Update(client, projectDomain.ID, updateOpts).Extract() th.AssertNoErr(t, err) } func TestProjectsNested(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) projectMain, err := CreateProject(t, client, nil) th.AssertNoErr(t, err) defer DeleteProject(t, client, projectMain.ID) tools.PrintResource(t, projectMain) createOpts := projects.CreateOpts{ ParentID: projectMain.ID, } project, err := CreateProject(t, client, &createOpts) th.AssertNoErr(t, err) defer DeleteProject(t, client, project.ID) tools.PrintResource(t, project) th.AssertEquals(t, project.ParentID, projectMain.ID) } regions_test.go000066400000000000000000000043761335005366400350010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v3// +build acceptance package v3 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/regions" th "github.com/gophercloud/gophercloud/testhelper" ) func TestRegionsList(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) listOpts := regions.ListOpts{ ParentRegionID: "RegionOne", } allPages, err := regions.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allRegions, err := regions.ExtractRegions(allPages) th.AssertNoErr(t, err) for _, region := range allRegions { tools.PrintResource(t, region) } } func TestRegionsGet(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) allPages, err := regions.List(client, nil).AllPages() th.AssertNoErr(t, err) allRegions, err := regions.ExtractRegions(allPages) th.AssertNoErr(t, err) region := allRegions[0] p, err := regions.Get(client, region.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, p) th.AssertEquals(t, region.ID, p.ID) } func TestRegionsCRUD(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) createOpts := regions.CreateOpts{ ID: "testregion", Description: "Region for testing", Extra: map[string]interface{}{ "email": "testregion@example.com", }, } // Create region in the default domain region, err := CreateRegion(t, client, &createOpts) th.AssertNoErr(t, err) defer DeleteRegion(t, client, region.ID) tools.PrintResource(t, region) tools.PrintResource(t, region.Extra) updateOpts := regions.UpdateOpts{ Description: "Region A for testing", /* // Due to a bug in Keystone, the Extra column of the Region table // is not updatable, see: https://bugs.launchpad.net/keystone/+bug/1729933 // The following lines should be uncommented once the fix is merged. Extra: map[string]interface{}{ "email": "testregionA@example.com", }, */ } newRegion, err := regions.Update(client, region.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newRegion) tools.PrintResource(t, newRegion.Extra) } roles_test.go000066400000000000000000000421371335005366400344540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v3// +build acceptance package v3 import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/domains" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" th "github.com/gophercloud/gophercloud/testhelper" ) func TestRolesList(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) listOpts := roles.ListOpts{ DomainID: "default", } allPages, err := roles.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoles(allPages) th.AssertNoErr(t, err) for _, role := range allRoles { tools.PrintResource(t, role) } } func TestRolesGet(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) role, err := FindRole(t, client) th.AssertNoErr(t, err) p, err := roles.Get(client, role.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, p) } func TestRolesCRUD(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) createOpts := roles.CreateOpts{ Name: "testrole", DomainID: "default", Extra: map[string]interface{}{ "description": "test role description", }, } // Create Role in the default domain role, err := CreateRole(t, client, &createOpts) th.AssertNoErr(t, err) defer DeleteRole(t, client, role.ID) tools.PrintResource(t, role) tools.PrintResource(t, role.Extra) listOpts := roles.ListOpts{ DomainID: "default", } allPages, err := roles.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoles(allPages) th.AssertNoErr(t, err) var found bool for _, r := range allRoles { tools.PrintResource(t, r) tools.PrintResource(t, r.Extra) if r.Name == role.Name { found = true } } th.AssertEquals(t, found, true) updateOpts := roles.UpdateOpts{ Extra: map[string]interface{}{ "description": "updated test role description", }, } newRole, err := roles.Update(client, role.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newRole) tools.PrintResource(t, newRole.Extra) th.AssertEquals(t, newRole.Extra["description"], "updated test role description") } func TestRolesFilterList(t *testing.T) { clients.RequireAdmin(t) // For some reason this is not longer working. // It might be a temporary issue. clients.SkipRelease(t, "master") client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) createOpts := roles.CreateOpts{ Name: "testrole", DomainID: "default", Extra: map[string]interface{}{ "description": "test role description", }, } // Create Role in the default domain role, err := CreateRole(t, client, &createOpts) th.AssertNoErr(t, err) defer DeleteRole(t, client, role.ID) var listOpts roles.ListOpts listOpts.Filters = map[string]string{ "name__contains": "TEST", } allPages, err := roles.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoles(allPages) th.AssertNoErr(t, err) found := false for _, r := range allRoles { tools.PrintResource(t, r) tools.PrintResource(t, r.Extra) if r.Name == role.Name { found = true } } th.AssertEquals(t, found, true) listOpts.Filters = map[string]string{ "name__contains": "reader", } allPages, err = roles.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allRoles, err = roles.ExtractRoles(allPages) th.AssertNoErr(t, err) found = false for _, r := range allRoles { tools.PrintResource(t, r) tools.PrintResource(t, r.Extra) if r.Name == role.Name { found = true } } th.AssertEquals(t, found, false) } func TestRoleListAssignmentForUserOnProject(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) project, err := CreateProject(t, client, nil) th.AssertNoErr(t, err) defer DeleteProject(t, client, project.ID) roleCreateOpts := roles.CreateOpts{ DomainID: "default", } role, err := CreateRole(t, client, &roleCreateOpts) th.AssertNoErr(t, err) defer DeleteRole(t, client, role.ID) user, err := CreateUser(t, client, nil) th.AssertNoErr(t, err) defer DeleteUser(t, client, user.ID) t.Logf("Attempting to assign a role %s to a user %s on a project %s", role.Name, user.Name, project.Name) assignOpts := roles.AssignOpts{ UserID: user.ID, ProjectID: project.ID, } err = roles.Assign(client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a user %s on a project %s", role.Name, user.Name, project.Name) defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{ UserID: user.ID, ProjectID: project.ID, }) listAssignmentsOnResourceOpts := roles.ListAssignmentsOnResourceOpts{ UserID: user.ID, ProjectID: project.ID, } allPages, err := roles.ListAssignmentsOnResource(client, listAssignmentsOnResourceOpts).AllPages() th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoles(allPages) th.AssertNoErr(t, err) t.Logf("Role assignments of user %s on project %s:", user.Name, project.Name) var found bool for _, _role := range allRoles { tools.PrintResource(t, _role) if _role.ID == role.ID { found = true } } th.AssertEquals(t, found, true) } func TestRoleListAssignmentForUserOnDomain(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) domain, err := CreateDomain(t, client, &domains.CreateOpts{ Enabled: gophercloud.Disabled, }) th.AssertNoErr(t, err) defer DeleteDomain(t, client, domain.ID) roleCreateOpts := roles.CreateOpts{ DomainID: "default", } role, err := CreateRole(t, client, &roleCreateOpts) th.AssertNoErr(t, err) defer DeleteRole(t, client, role.ID) user, err := CreateUser(t, client, nil) th.AssertNoErr(t, err) defer DeleteUser(t, client, user.ID) t.Logf("Attempting to assign a role %s to a user %s on a domain %s", role.Name, user.Name, domain.Name) assignOpts := roles.AssignOpts{ UserID: user.ID, DomainID: domain.ID, } err = roles.Assign(client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a user %s on a domain %s", role.Name, user.Name, domain.Name) defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{ UserID: user.ID, DomainID: domain.ID, }) listAssignmentsOnResourceOpts := roles.ListAssignmentsOnResourceOpts{ UserID: user.ID, DomainID: domain.ID, } allPages, err := roles.ListAssignmentsOnResource(client, listAssignmentsOnResourceOpts).AllPages() th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoles(allPages) th.AssertNoErr(t, err) t.Logf("Role assignments of user %s on domain %s:", user.Name, domain.Name) var found bool for _, _role := range allRoles { tools.PrintResource(t, _role) if _role.ID == role.ID { found = true } } th.AssertEquals(t, found, true) } func TestRoleListAssignmentForGroupOnProject(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) project, err := CreateProject(t, client, nil) th.AssertNoErr(t, err) defer DeleteProject(t, client, project.ID) roleCreateOpts := roles.CreateOpts{ DomainID: "default", } role, err := CreateRole(t, client, &roleCreateOpts) th.AssertNoErr(t, err) defer DeleteRole(t, client, role.ID) groupCreateOpts := &groups.CreateOpts{ DomainID: "default", } group, err := CreateGroup(t, client, groupCreateOpts) th.AssertNoErr(t, err) defer DeleteGroup(t, client, group.ID) t.Logf("Attempting to assign a role %s to a group %s on a project %s", role.Name, group.Name, project.Name) assignOpts := roles.AssignOpts{ GroupID: group.ID, ProjectID: project.ID, } err = roles.Assign(client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a group %s on a project %s", role.Name, group.Name, project.Name) defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{ GroupID: group.ID, ProjectID: project.ID, }) listAssignmentsOnResourceOpts := roles.ListAssignmentsOnResourceOpts{ GroupID: group.ID, ProjectID: project.ID, } allPages, err := roles.ListAssignmentsOnResource(client, listAssignmentsOnResourceOpts).AllPages() th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoles(allPages) th.AssertNoErr(t, err) t.Logf("Role assignments of group %s on project %s:", group.Name, project.Name) var found bool for _, _role := range allRoles { tools.PrintResource(t, _role) if _role.ID == role.ID { found = true } } th.AssertEquals(t, found, true) } func TestRoleListAssignmentForGroupOnDomain(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) domain, err := CreateDomain(t, client, &domains.CreateOpts{ Enabled: gophercloud.Disabled, }) th.AssertNoErr(t, err) defer DeleteDomain(t, client, domain.ID) roleCreateOpts := roles.CreateOpts{ DomainID: "default", } role, err := CreateRole(t, client, &roleCreateOpts) th.AssertNoErr(t, err) defer DeleteRole(t, client, role.ID) groupCreateOpts := &groups.CreateOpts{ DomainID: "default", } group, err := CreateGroup(t, client, groupCreateOpts) th.AssertNoErr(t, err) defer DeleteGroup(t, client, group.ID) t.Logf("Attempting to assign a role %s to a group %s on a domain %s", role.Name, group.Name, domain.Name) assignOpts := roles.AssignOpts{ GroupID: group.ID, DomainID: domain.ID, } err = roles.Assign(client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a group %s on a domain %s", role.Name, group.Name, domain.Name) defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{ GroupID: group.ID, DomainID: domain.ID, }) listAssignmentsOnResourceOpts := roles.ListAssignmentsOnResourceOpts{ GroupID: group.ID, DomainID: domain.ID, } allPages, err := roles.ListAssignmentsOnResource(client, listAssignmentsOnResourceOpts).AllPages() th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoles(allPages) th.AssertNoErr(t, err) t.Logf("Role assignments of group %s on domain %s:", group.Name, domain.Name) var found bool for _, _role := range allRoles { tools.PrintResource(t, _role) if _role.ID == role.ID { found = true } } th.AssertEquals(t, found, true) } func TestRolesAssignToUserOnProject(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) project, err := CreateProject(t, client, nil) th.AssertNoErr(t, err) defer DeleteProject(t, client, project.ID) roleCreateOpts := roles.CreateOpts{ DomainID: "default", } role, err := CreateRole(t, client, &roleCreateOpts) th.AssertNoErr(t, err) defer DeleteRole(t, client, role.ID) user, err := CreateUser(t, client, nil) th.AssertNoErr(t, err) defer DeleteUser(t, client, user.ID) t.Logf("Attempting to assign a role %s to a user %s on a project %s", role.Name, user.Name, project.Name) assignOpts := roles.AssignOpts{ UserID: user.ID, ProjectID: project.ID, } err = roles.Assign(client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a user %s on a project %s", role.Name, user.Name, project.Name) defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{ UserID: user.ID, ProjectID: project.ID, }) lao := roles.ListAssignmentsOpts{ RoleID: role.ID, ScopeProjectID: project.ID, UserID: user.ID, } allPages, err := roles.ListAssignments(client, lao).AllPages() th.AssertNoErr(t, err) allRoleAssignments, err := roles.ExtractRoleAssignments(allPages) th.AssertNoErr(t, err) t.Logf("Role assignments of user %s on project %s:", user.Name, project.Name) var found bool for _, roleAssignment := range allRoleAssignments { tools.PrintResource(t, roleAssignment) if roleAssignment.Role.ID == role.ID { found = true } } th.AssertEquals(t, found, true) } func TestRolesAssignToUserOnDomain(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) domain, err := CreateDomain(t, client, &domains.CreateOpts{ Enabled: gophercloud.Disabled, }) th.AssertNoErr(t, err) defer DeleteDomain(t, client, domain.ID) roleCreateOpts := roles.CreateOpts{ DomainID: "default", } role, err := CreateRole(t, client, &roleCreateOpts) th.AssertNoErr(t, err) defer DeleteRole(t, client, role.ID) user, err := CreateUser(t, client, nil) th.AssertNoErr(t, err) defer DeleteUser(t, client, user.ID) t.Logf("Attempting to assign a role %s to a user %s on a domain %s", role.Name, user.Name, domain.Name) assignOpts := roles.AssignOpts{ UserID: user.ID, DomainID: domain.ID, } err = roles.Assign(client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a user %s on a domain %s", role.Name, user.Name, domain.Name) defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{ UserID: user.ID, DomainID: domain.ID, }) lao := roles.ListAssignmentsOpts{ RoleID: role.ID, ScopeDomainID: domain.ID, UserID: user.ID, } allPages, err := roles.ListAssignments(client, lao).AllPages() th.AssertNoErr(t, err) allRoleAssignments, err := roles.ExtractRoleAssignments(allPages) th.AssertNoErr(t, err) t.Logf("Role assignments of user %s on domain %s:", user.Name, domain.Name) var found bool for _, roleAssignment := range allRoleAssignments { tools.PrintResource(t, roleAssignment) if roleAssignment.Role.ID == role.ID { found = true } } th.AssertEquals(t, found, true) } func TestRolesAssignToGroupOnDomain(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) domain, err := CreateDomain(t, client, &domains.CreateOpts{ Enabled: gophercloud.Disabled, }) th.AssertNoErr(t, err) defer DeleteDomain(t, client, domain.ID) roleCreateOpts := roles.CreateOpts{ DomainID: "default", } role, err := CreateRole(t, client, &roleCreateOpts) th.AssertNoErr(t, err) defer DeleteRole(t, client, role.ID) groupCreateOpts := &groups.CreateOpts{ DomainID: "default", } group, err := CreateGroup(t, client, groupCreateOpts) th.AssertNoErr(t, err) defer DeleteGroup(t, client, group.ID) t.Logf("Attempting to assign a role %s to a group %s on a domain %s", role.Name, group.Name, domain.Name) assignOpts := roles.AssignOpts{ GroupID: group.ID, DomainID: domain.ID, } err = roles.Assign(client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a group %s on a domain %s", role.Name, group.Name, domain.Name) defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{ GroupID: group.ID, DomainID: domain.ID, }) lao := roles.ListAssignmentsOpts{ RoleID: role.ID, ScopeDomainID: domain.ID, GroupID: group.ID, } allPages, err := roles.ListAssignments(client, lao).AllPages() th.AssertNoErr(t, err) allRoleAssignments, err := roles.ExtractRoleAssignments(allPages) th.AssertNoErr(t, err) t.Logf("Role assignments of group %s on domain %s:", group.Name, domain.Name) var found bool for _, roleAssignment := range allRoleAssignments { tools.PrintResource(t, roleAssignment) if roleAssignment.Role.ID == role.ID { found = true } } th.AssertEquals(t, found, true) } func TestRolesAssignToGroupOnProject(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) project, err := CreateProject(t, client, nil) th.AssertNoErr(t, err) defer DeleteProject(t, client, project.ID) roleCreateOpts := roles.CreateOpts{ DomainID: "default", } role, err := CreateRole(t, client, &roleCreateOpts) th.AssertNoErr(t, err) defer DeleteRole(t, client, role.ID) groupCreateOpts := &groups.CreateOpts{ DomainID: "default", } group, err := CreateGroup(t, client, groupCreateOpts) th.AssertNoErr(t, err) defer DeleteGroup(t, client, group.ID) t.Logf("Attempting to assign a role %s to a group %s on a project %s", role.Name, group.Name, project.Name) assignOpts := roles.AssignOpts{ GroupID: group.ID, ProjectID: project.ID, } err = roles.Assign(client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a group %s on a project %s", role.Name, group.Name, project.Name) defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{ GroupID: group.ID, ProjectID: project.ID, }) lao := roles.ListAssignmentsOpts{ RoleID: role.ID, ScopeProjectID: project.ID, GroupID: group.ID, } allPages, err := roles.ListAssignments(client, lao).AllPages() th.AssertNoErr(t, err) allRoleAssignments, err := roles.ExtractRoleAssignments(allPages) th.AssertNoErr(t, err) t.Logf("Role assignments of group %s on project %s:", group.Name, project.Name) var found bool for _, roleAssignment := range allRoleAssignments { tools.PrintResource(t, roleAssignment) if roleAssignment.Role.ID == role.ID { found = true } } th.AssertEquals(t, found, true) } service_test.go000066400000000000000000000033621335005366400347650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v3// +build acceptance package v3 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/services" th "github.com/gophercloud/gophercloud/testhelper" ) func TestServicesList(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) listOpts := services.ListOpts{ ServiceType: "identity", } allPages, err := services.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allServices, err := services.ExtractServices(allPages) th.AssertNoErr(t, err) var found bool for _, service := range allServices { tools.PrintResource(t, service) if service.Type == "identity" { found = true } } th.AssertEquals(t, found, true) } func TestServicesCRUD(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) createOpts := services.CreateOpts{ Type: "testing", Extra: map[string]interface{}{ "email": "testservice@example.com", }, } // Create service in the default domain service, err := CreateService(t, client, &createOpts) th.AssertNoErr(t, err) defer DeleteService(t, client, service.ID) tools.PrintResource(t, service) tools.PrintResource(t, service.Extra) updateOpts := services.UpdateOpts{ Type: "testing2", Extra: map[string]interface{}{ "description": "Test Users", "email": "thetestservice@example.com", }, } newService, err := services.Update(client, service.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newService) tools.PrintResource(t, newService.Extra) th.AssertEquals(t, newService.Extra["description"], "Test Users") } token_test.go000066400000000000000000000023421335005366400344420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v3// +build acceptance package v3 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" th "github.com/gophercloud/gophercloud/testhelper" ) func TestTokensGet(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) ao, err := openstack.AuthOptionsFromEnv() th.AssertNoErr(t, err) authOptions := tokens.AuthOptions{ Username: ao.Username, Password: ao.Password, DomainName: "default", } token, err := tokens.Create(client, &authOptions).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, token) catalog, err := tokens.Get(client, token.ID).ExtractServiceCatalog() th.AssertNoErr(t, err) tools.PrintResource(t, catalog) user, err := tokens.Get(client, token.ID).ExtractUser() th.AssertNoErr(t, err) tools.PrintResource(t, user) roles, err := tokens.Get(client, token.ID).ExtractRoles() th.AssertNoErr(t, err) tools.PrintResource(t, roles) project, err := tokens.Get(client, token.ID).ExtractProject() th.AssertNoErr(t, err) tools.PrintResource(t, project) } users_test.go000066400000000000000000000164571335005366400344770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/identity/v3// +build acceptance package v3 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" "github.com/gophercloud/gophercloud/openstack/identity/v3/users" th "github.com/gophercloud/gophercloud/testhelper" ) func TestUsersList(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) var iTrue bool = true listOpts := users.ListOpts{ Enabled: &iTrue, } allPages, err := users.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allUsers, err := users.ExtractUsers(allPages) th.AssertNoErr(t, err) var found bool for _, user := range allUsers { tools.PrintResource(t, user) tools.PrintResource(t, user.Extra) if user.Name == "admin" { found = true } } th.AssertEquals(t, found, true) listOpts.Filters = map[string]string{ "name__contains": "dmi", } allPages, err = users.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allUsers, err = users.ExtractUsers(allPages) th.AssertNoErr(t, err) found = false for _, user := range allUsers { tools.PrintResource(t, user) tools.PrintResource(t, user.Extra) if user.Name == "admin" { found = true } } th.AssertEquals(t, found, true) listOpts.Filters = map[string]string{ "name__contains": "foo", } allPages, err = users.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allUsers, err = users.ExtractUsers(allPages) th.AssertNoErr(t, err) found = false for _, user := range allUsers { tools.PrintResource(t, user) tools.PrintResource(t, user.Extra) if user.Name == "admin" { found = true } } th.AssertEquals(t, found, false) } func TestUsersGet(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) allPages, err := users.List(client, nil).AllPages() th.AssertNoErr(t, err) allUsers, err := users.ExtractUsers(allPages) th.AssertNoErr(t, err) user := allUsers[0] p, err := users.Get(client, user.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, p) th.AssertEquals(t, user.Name, p.Name) } func TestUserCRUD(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) project, err := CreateProject(t, client, nil) th.AssertNoErr(t, err) defer DeleteProject(t, client, project.ID) tools.PrintResource(t, project) createOpts := users.CreateOpts{ DefaultProjectID: project.ID, Password: "foobar", DomainID: "default", Options: map[users.Option]interface{}{ users.IgnorePasswordExpiry: true, users.MultiFactorAuthRules: []interface{}{ []string{"password", "totp"}, []string{"password", "custom-auth-method"}, }, }, Extra: map[string]interface{}{ "email": "jsmith@example.com", }, } user, err := CreateUser(t, client, &createOpts) th.AssertNoErr(t, err) defer DeleteUser(t, client, user.ID) tools.PrintResource(t, user) tools.PrintResource(t, user.Extra) iFalse := false updateOpts := users.UpdateOpts{ Enabled: &iFalse, Options: map[users.Option]interface{}{ users.MultiFactorAuthRules: nil, }, Extra: map[string]interface{}{ "disabled_reason": "DDOS", }, } newUser, err := users.Update(client, user.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newUser) tools.PrintResource(t, newUser.Extra) th.AssertEquals(t, newUser.Extra["disabled_reason"], "DDOS") } func TestUserChangePassword(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) createOpts := users.CreateOpts{ Password: "secretsecret", DomainID: "default", } user, err := CreateUser(t, client, &createOpts) th.AssertNoErr(t, err) defer DeleteUser(t, client, user.ID) tools.PrintResource(t, user) tools.PrintResource(t, user.Extra) changePasswordOpts := users.ChangePasswordOpts{ OriginalPassword: "secretsecret", Password: "new_secretsecret", } err = users.ChangePassword(client, user.ID, changePasswordOpts).ExtractErr() th.AssertNoErr(t, err) } func TestUsersGroups(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) createOpts := users.CreateOpts{ Password: "foobar", DomainID: "default", } user, err := CreateUser(t, client, &createOpts) th.AssertNoErr(t, err) defer DeleteUser(t, client, user.ID) tools.PrintResource(t, user) tools.PrintResource(t, user.Extra) createGroupOpts := groups.CreateOpts{ Name: "testgroup", DomainID: "default", } // Create Group in the default domain group, err := CreateGroup(t, client, &createGroupOpts) th.AssertNoErr(t, err) defer DeleteGroup(t, client, group.ID) tools.PrintResource(t, group) tools.PrintResource(t, group.Extra) err = users.AddToGroup(client, group.ID, user.ID).ExtractErr() th.AssertNoErr(t, err) allGroupPages, err := users.ListGroups(client, user.ID).AllPages() th.AssertNoErr(t, err) allGroups, err := groups.ExtractGroups(allGroupPages) th.AssertNoErr(t, err) var found bool for _, g := range allGroups { tools.PrintResource(t, g) tools.PrintResource(t, g.Extra) if g.ID == group.ID { found = true } } th.AssertEquals(t, found, true) found = false allUserPages, err := users.ListInGroup(client, group.ID, nil).AllPages() th.AssertNoErr(t, err) allUsers, err := users.ExtractUsers(allUserPages) th.AssertNoErr(t, err) for _, u := range allUsers { tools.PrintResource(t, user) tools.PrintResource(t, user.Extra) if u.ID == user.ID { found = true } } th.AssertEquals(t, found, true) ok, err := users.IsMemberOfGroup(client, group.ID, user.ID).Extract() if err != nil { t.Fatalf("Unable to check whether user belongs to group: %v", err) } if !ok { t.Fatalf("User %s is expected to be a member of group %s", user.ID, group.ID) } err = users.RemoveFromGroup(client, group.ID, user.ID).ExtractErr() th.AssertNoErr(t, err) allGroupPages, err = users.ListGroups(client, user.ID).AllPages() th.AssertNoErr(t, err) allGroups, err = groups.ExtractGroups(allGroupPages) th.AssertNoErr(t, err) found = false for _, g := range allGroups { tools.PrintResource(t, g) tools.PrintResource(t, g.Extra) if g.ID == group.ID { found = true } } th.AssertEquals(t, found, false) found = false allUserPages, err = users.ListInGroup(client, group.ID, nil).AllPages() th.AssertNoErr(t, err) allUsers, err = users.ExtractUsers(allUserPages) th.AssertNoErr(t, err) for _, u := range allUsers { tools.PrintResource(t, user) tools.PrintResource(t, user.Extra) if u.ID == user.ID { found = true } } th.AssertEquals(t, found, false) } func TestUsersListProjects(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) allUserPages, err := users.List(client, nil).AllPages() th.AssertNoErr(t, err) allUsers, err := users.ExtractUsers(allUserPages) th.AssertNoErr(t, err) user := allUsers[0] allProjectPages, err := users.ListProjects(client, user.ID).AllPages() th.AssertNoErr(t, err) allProjects, err := projects.ExtractProjects(allProjectPages) th.AssertNoErr(t, err) for _, project := range allProjects { tools.PrintResource(t, project) } } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/imageservice/000077500000000000000000000000001335005366400322745ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/imageservice/v2/000077500000000000000000000000001335005366400326235ustar00rootroot00000000000000imagedata_test.go000066400000000000000000000013471335005366400360530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/imageservice/v2package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" th "github.com/gophercloud/gophercloud/testhelper" ) func TestImageStage(t *testing.T) { client, err := clients.NewImageServiceV2Client() th.AssertNoErr(t, err) image, err := CreateEmptyImage(t, client) th.AssertNoErr(t, err) defer DeleteImage(t, client, image) imageFileName := tools.RandomString("image_", 8) imageFilepath := "/tmp/" + imageFileName imageURL := ImportImageURL err = DownloadImageFileFromURL(t, imageURL, imageFilepath) th.AssertNoErr(t, err) defer DeleteImageFile(t, imageFilepath) err = StageImage(t, client, imageFilepath, image.ID) th.AssertNoErr(t, err) } imageimport_test.go000066400000000000000000000007251335005366400364530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/imageservice/v2// +build acceptance imageservice imageimport package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" th "github.com/gophercloud/gophercloud/testhelper" ) func TestGetImportInfo(t *testing.T) { client, err := clients.NewImageServiceV2Client() th.AssertNoErr(t, err) importInfo, err := GetImportInfo(t, client) th.AssertNoErr(t, err) tools.PrintResource(t, importInfo) } images_test.go000066400000000000000000000103741335005366400354040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/imageservice/v2// +build acceptance imageservice images package v2 import ( "testing" "time" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestImagesListEachPage(t *testing.T) { client, err := clients.NewImageServiceV2Client() th.AssertNoErr(t, err) listOpts := images.ListOpts{ Limit: 1, } pager := images.List(client, listOpts) err = pager.EachPage(func(page pagination.Page) (bool, error) { images, err := images.ExtractImages(page) if err != nil { t.Fatalf("Unable to extract images: %v", err) } for _, image := range images { tools.PrintResource(t, image) tools.PrintResource(t, image.Properties) } return true, nil }) } func TestImagesListAllPages(t *testing.T) { client, err := clients.NewImageServiceV2Client() th.AssertNoErr(t, err) image, err := CreateEmptyImage(t, client) th.AssertNoErr(t, err) defer DeleteImage(t, client, image) listOpts := images.ListOpts{} allPages, err := images.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allImages, err := images.ExtractImages(allPages) th.AssertNoErr(t, err) var found bool for _, i := range allImages { tools.PrintResource(t, i) tools.PrintResource(t, i.Properties) if i.Name == image.Name { found = true } } th.AssertEquals(t, found, true) } func TestImagesListByDate(t *testing.T) { client, err := clients.NewImageServiceV2Client() th.AssertNoErr(t, err) date := time.Date(2014, 1, 1, 1, 1, 1, 0, time.UTC) listOpts := images.ListOpts{ Limit: 1, CreatedAtQuery: &images.ImageDateQuery{ Date: date, Filter: images.FilterGTE, }, } allPages, err := images.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allImages, err := images.ExtractImages(allPages) th.AssertNoErr(t, err) if len(allImages) == 0 { t.Fatalf("Query resulted in no results") } for _, image := range allImages { tools.PrintResource(t, image) tools.PrintResource(t, image.Properties) } date = time.Date(2049, 1, 1, 1, 1, 1, 0, time.UTC) listOpts = images.ListOpts{ Limit: 1, CreatedAtQuery: &images.ImageDateQuery{ Date: date, Filter: images.FilterGTE, }, } allPages, err = images.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allImages, err = images.ExtractImages(allPages) th.AssertNoErr(t, err) if len(allImages) > 0 { t.Fatalf("Expected 0 images, got %d", len(allImages)) } } func TestImagesFilter(t *testing.T) { client, err := clients.NewImageServiceV2Client() th.AssertNoErr(t, err) image, err := CreateEmptyImage(t, client) th.AssertNoErr(t, err) defer DeleteImage(t, client, image) listOpts := images.ListOpts{ Tags: []string{"foo", "bar"}, ContainerFormat: "bare", DiskFormat: "qcow2", } allPages, err := images.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allImages, err := images.ExtractImages(allPages) th.AssertNoErr(t, err) if len(allImages) == 0 { t.Fatalf("Query resulted in no results") } } func TestImagesUpdate(t *testing.T) { client, err := clients.NewImageServiceV2Client() th.AssertNoErr(t, err) image, err := CreateEmptyImage(t, client) th.AssertNoErr(t, err) defer DeleteImage(t, client, image) newTags := []string{"foo", "bar"} updateOpts := images.UpdateOpts{ images.ReplaceImageName{NewName: image.Name + "foo"}, images.ReplaceImageTags{NewTags: newTags}, images.UpdateImageProperty{ Op: images.AddOp, Name: "hw_disk_bus", Value: "scsi", }, images.UpdateImageProperty{ Op: images.RemoveOp, Name: "architecture", }, } newImage, err := images.Update(client, image.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newImage) tools.PrintResource(t, newImage.Properties) th.AssertEquals(t, newImage.Name, image.Name+"foo") th.AssertDeepEquals(t, newImage.Tags, newTags) // Because OpenStack is now adding additional properties automatically, // it's not possible to do an easy AssertDeepEquals. th.AssertEquals(t, newImage.Properties["hw_disk_bus"], "scsi") if _, ok := newImage.Properties["architecture"]; ok { t.Fatal("architecture property still exists") } } imageservice.go000066400000000000000000000111031335005366400355320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/imageservice/v2// Package v2 contains common functions for creating imageservice resources // for use in acceptance tests. See the `*_test.go` files for example usages. package v2 import ( "io" "net/http" "os" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/tasks" th "github.com/gophercloud/gophercloud/testhelper" ) // CreateEmptyImage will create an image, but with no actual image data. // An error will be returned if an image was unable to be created. func CreateEmptyImage(t *testing.T, client *gophercloud.ServiceClient) (*images.Image, error) { var image *images.Image name := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create image: %s", name) protected := false visibility := images.ImageVisibilityPrivate createOpts := &images.CreateOpts{ Name: name, ContainerFormat: "bare", DiskFormat: "qcow2", MinDisk: 0, MinRAM: 0, Protected: &protected, Visibility: &visibility, Properties: map[string]string{ "architecture": "x86_64", }, Tags: []string{"foo", "bar", "baz"}, } image, err := images.Create(client, createOpts).Extract() if err != nil { return image, err } newImage, err := images.Get(client, image.ID).Extract() if err != nil { return image, err } t.Logf("Created image %s: %#v", name, newImage) th.CheckEquals(t, newImage.Name, name) th.CheckEquals(t, newImage.Properties["architecture"], "x86_64") return newImage, nil } // DeleteImage deletes an image. // A fatal error will occur if the image failed to delete. This works best when // used as a deferred function. func DeleteImage(t *testing.T, client *gophercloud.ServiceClient, image *images.Image) { err := images.Delete(client, image.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete image %s: %v", image.ID, err) } t.Logf("Deleted image: %s", image.ID) } // ImportImageURL contains an URL of a test image that can be imported. const ImportImageURL = "http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img" // CreateTask will create a task to import the CirrOS image. // An error will be returned if a task couldn't be created. func CreateTask(t *testing.T, client *gophercloud.ServiceClient, imageURL string) (*tasks.Task, error) { t.Logf("Attempting to create an Imageservice import task with image: %s", imageURL) opts := tasks.CreateOpts{ Type: "import", Input: map[string]interface{}{ "image_properties": map[string]interface{}{ "container_format": "bare", "disk_format": "raw", }, "import_from_format": "raw", "import_from": imageURL, }, } task, err := tasks.Create(client, opts).Extract() if err != nil { return nil, err } newTask, err := tasks.Get(client, task.ID).Extract() if err != nil { return nil, err } return newTask, nil } // GetImportInfo will retrieve Import API information. func GetImportInfo(t *testing.T, client *gophercloud.ServiceClient) (*imageimport.ImportInfo, error) { t.Log("Attempting to get the Imageservice Import API information") importInfo, err := imageimport.Get(client).Extract() if err != nil { return nil, err } return importInfo, nil } // StageImage will stage local image file to the referenced remote queued image. func StageImage(t *testing.T, client *gophercloud.ServiceClient, filepath, imageID string) error { imageData, err := os.Open(filepath) if err != nil { return err } defer imageData.Close() return imagedata.Stage(client, imageID, imageData).ExtractErr() } // DownloadImageFileFromURL will download an image from the specified URL and // place it into the specified path. func DownloadImageFileFromURL(t *testing.T, url, filepath string) error { file, err := os.Create(filepath) if err != nil { return err } defer file.Close() t.Logf("Attempting to download image from %s", url) resp, err := http.Get(url) if err != nil { return err } defer resp.Body.Close() size, err := io.Copy(file, resp.Body) if err != nil { return err } t.Logf("Downloaded image with size of %d bytes in %s", size, filepath) return nil } // DeleteImageFile will delete local image file. func DeleteImageFile(t *testing.T, filepath string) { err := os.Remove(filepath) if err != nil { t.Fatalf("Unable to delete image file %s", filepath) } t.Logf("Successfully deleted image file %s", filepath) } tasks_test.go000066400000000000000000000026311335005366400352610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/imageservice/v2// +build acceptance imageservice tasks package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/tasks" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestTasksListEachPage(t *testing.T) { client, err := clients.NewImageServiceV2Client() th.AssertNoErr(t, err) listOpts := tasks.ListOpts{ Limit: 1, } pager := tasks.List(client, listOpts) err = pager.EachPage(func(page pagination.Page) (bool, error) { tasks, err := tasks.ExtractTasks(page) th.AssertNoErr(t, err) for _, task := range tasks { tools.PrintResource(t, task) } return true, nil }) } func TestTasksListAllPages(t *testing.T) { client, err := clients.NewImageServiceV2Client() th.AssertNoErr(t, err) listOpts := tasks.ListOpts{} allPages, err := tasks.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allTasks, err := tasks.ExtractTasks(allPages) th.AssertNoErr(t, err) for _, i := range allTasks { tools.PrintResource(t, i) } } func TestTaskCreate(t *testing.T) { client, err := clients.NewImageServiceV2Client() th.AssertNoErr(t, err) task, err := CreateTask(t, client, ImportImageURL) if err != nil { t.Fatalf("Unable to create an Imageservice task: %v", err) } tools.PrintResource(t, task) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/keymanager/000077500000000000000000000000001335005366400317545ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/keymanager/v1/000077500000000000000000000000001335005366400323025ustar00rootroot00000000000000acls_test.go000066400000000000000000000062161335005366400345400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/keymanager/v1// +build acceptance keymanager acls package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/acls" th "github.com/gophercloud/gophercloud/testhelper" ) func TestACLCRUD(t *testing.T) { client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) payload := tools.RandomString("SUPERSECRET-", 8) secret, err := CreateSecretWithPayload(t, client, payload) th.AssertNoErr(t, err) secretID, err := ParseID(secret.SecretRef) th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) user := tools.RandomString("", 32) users := []string{user} iFalse := false setOpts := acls.SetOpts{ Type: "read", Users: &users, ProjectAccess: &iFalse, } aclRef, err := acls.SetSecretACL(client, secretID, setOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, aclRef) defer func() { err := acls.DeleteSecretACL(client, secretID).ExtractErr() th.AssertNoErr(t, err) acl, err := acls.GetSecretACL(client, secretID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, acl) }() acl, err := acls.GetSecretACL(client, secretID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, acl) tools.PrintResource(t, (*acl)["read"].Created) th.AssertEquals(t, len((*acl)["read"].Users), 1) th.AssertEquals(t, (*acl)["read"].ProjectAccess, false) newUsers := []string{} updateOpts := acls.SetOpts{ Type: "read", Users: &newUsers, } aclRef, err = acls.UpdateSecretACL(client, secretID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, aclRef) acl, err = acls.GetSecretACL(client, secretID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, acl) tools.PrintResource(t, (*acl)["read"].Created) th.AssertEquals(t, len((*acl)["read"].Users), 0) th.AssertEquals(t, (*acl)["read"].ProjectAccess, false) container, err := CreateGenericContainer(t, client, secret) th.AssertNoErr(t, err) containerID, err := ParseID(container.ContainerRef) th.AssertNoErr(t, err) defer DeleteContainer(t, client, containerID) aclRef, err = acls.SetContainerACL(client, containerID, setOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, aclRef) defer func() { err := acls.DeleteContainerACL(client, containerID).ExtractErr() th.AssertNoErr(t, err) acl, err := acls.GetContainerACL(client, containerID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, acl) }() acl, err = acls.GetContainerACL(client, containerID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, acl) tools.PrintResource(t, (*acl)["read"].Created) th.AssertEquals(t, len((*acl)["read"].Users), 1) th.AssertEquals(t, (*acl)["read"].ProjectAccess, false) aclRef, err = acls.UpdateContainerACL(client, containerID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, aclRef) acl, err = acls.GetContainerACL(client, containerID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, acl) tools.PrintResource(t, (*acl)["read"].Created) th.AssertEquals(t, len((*acl)["read"].Users), 0) th.AssertEquals(t, (*acl)["read"].ProjectAccess, false) } containers_test.go000066400000000000000000000131551335005366400357630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/keymanager/v1// +build acceptance keymanager containers package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/containers" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/secrets" th "github.com/gophercloud/gophercloud/testhelper" ) func TestGenericContainersCRUD(t *testing.T) { client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) payload := tools.RandomString("SUPERSECRET-", 8) secret, err := CreateSecretWithPayload(t, client, payload) th.AssertNoErr(t, err) secretID, err := ParseID(secret.SecretRef) th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) container, err := CreateGenericContainer(t, client, secret) th.AssertNoErr(t, err) containerID, err := ParseID(container.ContainerRef) th.AssertNoErr(t, err) defer DeleteContainer(t, client, containerID) allPages, err := containers.List(client, nil).AllPages() th.AssertNoErr(t, err) allContainers, err := containers.ExtractContainers(allPages) th.AssertNoErr(t, err) var found bool for _, v := range allContainers { if v.ContainerRef == container.ContainerRef { found = true } } th.AssertEquals(t, found, true) } func TestCertificateContainer(t *testing.T) { client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) pass := tools.RandomString("", 16) priv, cert, err := CreateCertificate(t, pass) th.AssertNoErr(t, err) private, err := CreatePrivateSecret(t, client, priv) th.AssertNoErr(t, err) secretID, err := ParseID(private.SecretRef) th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) payload, err := secrets.GetPayload(client, secretID, nil).Extract() th.AssertNoErr(t, err) t.Logf("Private Payload: %s", string(payload)) certificate, err := CreateCertificateSecret(t, client, cert) th.AssertNoErr(t, err) secretID, err = ParseID(certificate.SecretRef) th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) payload, err = secrets.GetPayload(client, secretID, nil).Extract() th.AssertNoErr(t, err) t.Logf("Certificate Payload: %s", string(payload)) passphrase, err := CreatePassphraseSecret(t, client, pass) th.AssertNoErr(t, err) secretID, err = ParseID(passphrase.SecretRef) th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) payload, err = secrets.GetPayload(client, secretID, nil).Extract() th.AssertNoErr(t, err) t.Logf("Passphrase Payload: %s", string(payload)) container, err := CreateCertificateContainer(t, client, passphrase, private, certificate) th.AssertNoErr(t, err) containerID, err := ParseID(container.ContainerRef) defer DeleteContainer(t, client, containerID) } func TestRSAContainer(t *testing.T) { client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) pass := tools.RandomString("", 16) priv, pub, err := CreateRSAKeyPair(t, pass) th.AssertNoErr(t, err) private, err := CreatePrivateSecret(t, client, priv) th.AssertNoErr(t, err) secretID, err := ParseID(private.SecretRef) th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) payload, err := secrets.GetPayload(client, secretID, nil).Extract() th.AssertNoErr(t, err) t.Logf("Private Payload: %s", string(payload)) public, err := CreatePublicSecret(t, client, pub) th.AssertNoErr(t, err) secretID, err = ParseID(public.SecretRef) th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) payload, err = secrets.GetPayload(client, secretID, nil).Extract() th.AssertNoErr(t, err) t.Logf("Public Payload: %s", string(payload)) passphrase, err := CreatePassphraseSecret(t, client, pass) th.AssertNoErr(t, err) secretID, err = ParseID(passphrase.SecretRef) th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) payload, err = secrets.GetPayload(client, secretID, nil).Extract() th.AssertNoErr(t, err) t.Logf("Passphrase Payload: %s", string(payload)) container, err := CreateRSAContainer(t, client, passphrase, private, public) th.AssertNoErr(t, err) containerID, err := ParseID(container.ContainerRef) defer DeleteContainer(t, client, containerID) } func TestContainerConsumersCRUD(t *testing.T) { client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) payload := tools.RandomString("SUPERSECRET-", 8) secret, err := CreateSecretWithPayload(t, client, payload) th.AssertNoErr(t, err) secretID, err := ParseID(secret.SecretRef) th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) container, err := CreateGenericContainer(t, client, secret) th.AssertNoErr(t, err) containerID, err := ParseID(container.ContainerRef) th.AssertNoErr(t, err) defer DeleteContainer(t, client, containerID) consumerName := tools.RandomString("CONSUMER-", 8) consumerCreateOpts := containers.CreateConsumerOpts{ Name: consumerName, URL: "http://example.com", } container, err = containers.CreateConsumer(client, containerID, consumerCreateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, container.Consumers) th.AssertEquals(t, len(container.Consumers), 1) defer func() { deleteOpts := containers.DeleteConsumerOpts{ Name: consumerName, URL: "http://example.com", } container, err := containers.DeleteConsumer(client, containerID, deleteOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, len(container.Consumers), 0) }() allPages, err := containers.ListConsumers(client, containerID, nil).AllPages() th.AssertNoErr(t, err) allConsumers, err := containers.ExtractConsumers(allPages) th.AssertNoErr(t, err) var found bool for _, v := range allConsumers { if v.Name == consumerName { found = true } } th.AssertEquals(t, found, true) } keymanager.go000066400000000000000000000442231335005366400347020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/keymanager/v1package v1 import ( "crypto/rand" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "encoding/base64" "encoding/pem" "fmt" "math/big" "strings" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/containers" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/orders" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/secrets" th "github.com/gophercloud/gophercloud/testhelper" ) // CreateAsymmetric Order will create a random asymmetric order. // An error will be returned if the order could not be created. func CreateAsymmetricOrder(t *testing.T, client *gophercloud.ServiceClient) (*orders.Order, error) { name := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create order %s", name) expiration := time.Date(2049, 1, 1, 1, 1, 1, 0, time.UTC) createOpts := orders.CreateOpts{ Type: orders.AsymmetricOrder, Meta: orders.MetaOpts{ Name: name, Algorithm: "rsa", BitLength: 2048, Mode: "cbc", Expiration: &expiration, }, } order, err := orders.Create(client, createOpts).Extract() if err != nil { return nil, err } orderID, err := ParseID(order.OrderRef) if err != nil { return nil, err } err = WaitForOrder(client, orderID) th.AssertNoErr(t, err) order, err = orders.Get(client, orderID).Extract() if err != nil { return nil, err } tools.PrintResource(t, order) tools.PrintResource(t, order.Meta.Expiration) th.AssertEquals(t, order.Meta.Name, name) th.AssertEquals(t, order.Type, "asymmetric") return order, nil } // CreateCertificateContainer will create a random certificate container. // An error will be returned if the container could not be created. func CreateCertificateContainer(t *testing.T, client *gophercloud.ServiceClient, passphrase, private, certificate *secrets.Secret) (*containers.Container, error) { containerName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create container %s", containerName) createOpts := containers.CreateOpts{ Type: containers.CertificateContainer, Name: containerName, SecretRefs: []containers.SecretRef{ { Name: "certificate", SecretRef: certificate.SecretRef, }, { Name: "private_key", SecretRef: private.SecretRef, }, { Name: "private_key_passphrase", SecretRef: passphrase.SecretRef, }, }, } container, err := containers.Create(client, createOpts).Extract() if err != nil { return nil, err } t.Logf("Successfully created container: %s", container.ContainerRef) containerID, err := ParseID(container.ContainerRef) if err != nil { return nil, err } container, err = containers.Get(client, containerID).Extract() if err != nil { return nil, err } tools.PrintResource(t, container) th.AssertEquals(t, container.Name, containerName) th.AssertEquals(t, container.Type, "certificate") return container, nil } // CreateKeyOrder will create a random key order. // An error will be returned if the order could not be created. func CreateKeyOrder(t *testing.T, client *gophercloud.ServiceClient) (*orders.Order, error) { name := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create order %s", name) expiration := time.Date(2049, 1, 1, 1, 1, 1, 0, time.UTC) createOpts := orders.CreateOpts{ Type: orders.KeyOrder, Meta: orders.MetaOpts{ Name: name, Algorithm: "aes", BitLength: 256, Mode: "cbc", Expiration: &expiration, }, } order, err := orders.Create(client, createOpts).Extract() if err != nil { return nil, err } orderID, err := ParseID(order.OrderRef) if err != nil { return nil, err } order, err = orders.Get(client, orderID).Extract() if err != nil { return nil, err } tools.PrintResource(t, order) tools.PrintResource(t, order.Meta.Expiration) th.AssertEquals(t, order.Meta.Name, name) th.AssertEquals(t, order.Type, "key") return order, nil } // CreateRSAContainer will create a random RSA container. // An error will be returned if the container could not be created. func CreateRSAContainer(t *testing.T, client *gophercloud.ServiceClient, passphrase, private, public *secrets.Secret) (*containers.Container, error) { containerName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create container %s", containerName) createOpts := containers.CreateOpts{ Type: containers.RSAContainer, Name: containerName, SecretRefs: []containers.SecretRef{ { Name: "public_key", SecretRef: public.SecretRef, }, { Name: "private_key", SecretRef: private.SecretRef, }, { Name: "private_key_passphrase", SecretRef: passphrase.SecretRef, }, }, } container, err := containers.Create(client, createOpts).Extract() if err != nil { return nil, err } t.Logf("Successfully created container: %s", container.ContainerRef) containerID, err := ParseID(container.ContainerRef) if err != nil { return nil, err } container, err = containers.Get(client, containerID).Extract() if err != nil { return nil, err } tools.PrintResource(t, container) th.AssertEquals(t, container.Name, containerName) th.AssertEquals(t, container.Type, "rsa") return container, nil } // CreateCertificateSecret will create a random certificate secret. An error // will be returned if the secret could not be created. func CreateCertificateSecret(t *testing.T, client *gophercloud.ServiceClient, cert []byte) (*secrets.Secret, error) { b64Cert := base64.StdEncoding.EncodeToString(cert) name := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create public key %s", name) createOpts := secrets.CreateOpts{ Name: name, SecretType: secrets.CertificateSecret, Payload: b64Cert, PayloadContentType: "application/octet-stream", PayloadContentEncoding: "base64", Algorithm: "rsa", } secret, err := secrets.Create(client, createOpts).Extract() if err != nil { return nil, err } t.Logf("Successfully created secret: %s", secret.SecretRef) secretID, err := ParseID(secret.SecretRef) if err != nil { return nil, err } secret, err = secrets.Get(client, secretID).Extract() if err != nil { return nil, err } tools.PrintResource(t, secret) th.AssertEquals(t, secret.Name, name) th.AssertEquals(t, secret.Algorithm, "rsa") return secret, nil } // CreateEmptySecret will create a random secret with no payload. An error will // be returned if the secret could not be created. func CreateEmptySecret(t *testing.T, client *gophercloud.ServiceClient) (*secrets.Secret, error) { secretName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create secret %s", secretName) createOpts := secrets.CreateOpts{ Algorithm: "aes", BitLength: 256, Mode: "cbc", Name: secretName, SecretType: secrets.OpaqueSecret, } secret, err := secrets.Create(client, createOpts).Extract() if err != nil { return nil, err } t.Logf("Successfully created secret: %s", secret.SecretRef) secretID, err := ParseID(secret.SecretRef) if err != nil { return nil, err } secret, err = secrets.Get(client, secretID).Extract() if err != nil { return nil, err } tools.PrintResource(t, secret) th.AssertEquals(t, secret.Name, secretName) th.AssertEquals(t, secret.Algorithm, "aes") return secret, nil } // CreateGenericContainer will create a random generic container with a // specified secret. An error will be returned if the container could not // be created. func CreateGenericContainer(t *testing.T, client *gophercloud.ServiceClient, secret *secrets.Secret) (*containers.Container, error) { containerName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create container %s", containerName) createOpts := containers.CreateOpts{ Type: containers.GenericContainer, Name: containerName, SecretRefs: []containers.SecretRef{ { Name: secret.Name, SecretRef: secret.SecretRef, }, }, } container, err := containers.Create(client, createOpts).Extract() if err != nil { return nil, err } t.Logf("Successfully created container: %s", container.ContainerRef) containerID, err := ParseID(container.ContainerRef) if err != nil { return nil, err } container, err = containers.Get(client, containerID).Extract() if err != nil { return nil, err } tools.PrintResource(t, container) th.AssertEquals(t, container.Name, containerName) th.AssertEquals(t, container.Type, "generic") return container, nil } // CreatePassphraseSecret will create a random passphrase secret. // An error will be returned if the secret could not be created. func CreatePassphraseSecret(t *testing.T, client *gophercloud.ServiceClient, passphrase string) (*secrets.Secret, error) { secretName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create secret %s", secretName) createOpts := secrets.CreateOpts{ Algorithm: "aes", BitLength: 256, Mode: "cbc", Name: secretName, Payload: passphrase, PayloadContentType: "text/plain", SecretType: secrets.PassphraseSecret, } secret, err := secrets.Create(client, createOpts).Extract() if err != nil { return nil, err } t.Logf("Successfully created secret: %s", secret.SecretRef) secretID, err := ParseID(secret.SecretRef) if err != nil { return nil, err } secret, err = secrets.Get(client, secretID).Extract() if err != nil { return nil, err } tools.PrintResource(t, secret) th.AssertEquals(t, secret.Name, secretName) th.AssertEquals(t, secret.Algorithm, "aes") return secret, nil } // CreatePublicSecret will create a random public secret. An error // will be returned if the secret could not be created. func CreatePublicSecret(t *testing.T, client *gophercloud.ServiceClient, pub []byte) (*secrets.Secret, error) { b64Cert := base64.StdEncoding.EncodeToString(pub) name := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create public key %s", name) createOpts := secrets.CreateOpts{ Name: name, SecretType: secrets.PublicSecret, Payload: b64Cert, PayloadContentType: "application/octet-stream", PayloadContentEncoding: "base64", Algorithm: "rsa", } secret, err := secrets.Create(client, createOpts).Extract() if err != nil { return nil, err } t.Logf("Successfully created secret: %s", secret.SecretRef) secretID, err := ParseID(secret.SecretRef) if err != nil { return nil, err } secret, err = secrets.Get(client, secretID).Extract() if err != nil { return nil, err } tools.PrintResource(t, secret) th.AssertEquals(t, secret.Name, name) th.AssertEquals(t, secret.Algorithm, "rsa") return secret, nil } // CreatePrivateSecret will create a random private secret. An error // will be returned if the secret could not be created. func CreatePrivateSecret(t *testing.T, client *gophercloud.ServiceClient, priv []byte) (*secrets.Secret, error) { b64Cert := base64.StdEncoding.EncodeToString(priv) name := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create public key %s", name) createOpts := secrets.CreateOpts{ Name: name, SecretType: secrets.PrivateSecret, Payload: b64Cert, PayloadContentType: "application/octet-stream", PayloadContentEncoding: "base64", Algorithm: "rsa", } secret, err := secrets.Create(client, createOpts).Extract() if err != nil { return nil, err } t.Logf("Successfully created secret: %s", secret.SecretRef) secretID, err := ParseID(secret.SecretRef) if err != nil { return nil, err } secret, err = secrets.Get(client, secretID).Extract() if err != nil { return nil, err } tools.PrintResource(t, secret) th.AssertEquals(t, secret.Name, name) th.AssertEquals(t, secret.Algorithm, "rsa") return secret, nil } // CreateSecretWithPayload will create a random secret with a given payload. // An error will be returned if the secret could not be created. func CreateSecretWithPayload(t *testing.T, client *gophercloud.ServiceClient, payload string) (*secrets.Secret, error) { secretName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create secret %s", secretName) createOpts := secrets.CreateOpts{ Algorithm: "aes", BitLength: 256, Mode: "cbc", Name: secretName, Payload: payload, PayloadContentType: "text/plain", SecretType: secrets.OpaqueSecret, } secret, err := secrets.Create(client, createOpts).Extract() if err != nil { return nil, err } t.Logf("Successfully created secret: %s", secret.SecretRef) secretID, err := ParseID(secret.SecretRef) if err != nil { return nil, err } secret, err = secrets.Get(client, secretID).Extract() if err != nil { return nil, err } tools.PrintResource(t, secret) th.AssertEquals(t, secret.Name, secretName) th.AssertEquals(t, secret.Algorithm, "aes") return secret, nil } // CreateSymmetricSecret will create a random symmetric secret. An error // will be returned if the secret could not be created. func CreateSymmetricSecret(t *testing.T, client *gophercloud.ServiceClient) (*secrets.Secret, error) { name := tools.RandomString("TESTACC-", 8) key := tools.RandomString("", 256) b64Key := base64.StdEncoding.EncodeToString([]byte(key)) t.Logf("Attempting to create symmetric key %s", name) createOpts := secrets.CreateOpts{ Name: name, SecretType: secrets.SymmetricSecret, Payload: b64Key, PayloadContentType: "application/octet-stream", PayloadContentEncoding: "base64", Algorithm: "aes", BitLength: 256, Mode: "cbc", } secret, err := secrets.Create(client, createOpts).Extract() if err != nil { return nil, err } t.Logf("Successfully created secret: %s", secret.SecretRef) secretID, err := ParseID(secret.SecretRef) if err != nil { return nil, err } secret, err = secrets.Get(client, secretID).Extract() if err != nil { return nil, err } tools.PrintResource(t, secret) th.AssertEquals(t, secret.Name, name) th.AssertEquals(t, secret.Algorithm, "aes") return secret, nil } // DeleteContainer will delete a container. A fatal error will occur if the // container could not be deleted. This works best when used as a deferred // function. func DeleteContainer(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete container %s", id) err := containers.Delete(client, id).ExtractErr() if err != nil { t.Fatalf("Could not delete container: %s", err) } t.Logf("Successfully deleted container %s", id) } // DeleteOrder will delete an order. A fatal error will occur if the // order could not be deleted. This works best when used as a deferred // function. func DeleteOrder(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete order %s", id) err := orders.Delete(client, id).ExtractErr() if err != nil { t.Fatalf("Could not delete order: %s", err) } t.Logf("Successfully deleted order %s", id) } // DeleteSecret will delete a secret. A fatal error will occur if the secret // could not be deleted. This works best when used as a deferred function. func DeleteSecret(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete secret %s", id) err := secrets.Delete(client, id).ExtractErr() if err != nil { t.Fatalf("Could not delete secret: %s", err) } t.Logf("Successfully deleted secret %s", id) } func ParseID(ref string) (string, error) { parts := strings.Split(ref, "/") if len(parts) < 2 { return "", fmt.Errorf("Could not parse %s", ref) } return parts[len(parts)-1], nil } // CreateCertificate will create a random certificate. A fatal error will // be returned if creation failed. // https://golang.org/src/crypto/tls/generate_cert.go func CreateCertificate(t *testing.T, passphrase string) ([]byte, []byte, error) { key, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return nil, nil, err } block := &pem.Block{ Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key), } if passphrase != "" { block, err = x509.EncryptPEMBlock(rand.Reader, block.Type, block.Bytes, []byte(passphrase), x509.PEMCipherAES256) if err != nil { return nil, nil, err } } keyPem := pem.EncodeToMemory(block) serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) if err != nil { return nil, nil, err } tpl := x509.Certificate{ SerialNumber: serialNumber, Subject: pkix.Name{ Organization: []string{"Some Org"}, }, NotBefore: time.Now(), NotAfter: time.Now().AddDate(5, 0, 0), BasicConstraintsValid: true, } cert, err := x509.CreateCertificate(rand.Reader, &tpl, &tpl, &key.PublicKey, key) if err != nil { return nil, nil, err } certPem := pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: cert, }) return keyPem, certPem, nil } // CreateRSAKeyPair will create a random RSA key pair. An error will be // returned if the pair could not be created. func CreateRSAKeyPair(t *testing.T, passphrase string) ([]byte, []byte, error) { key, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return nil, nil, err } block := &pem.Block{ Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key), } if passphrase != "" { block, err = x509.EncryptPEMBlock(rand.Reader, block.Type, block.Bytes, []byte(passphrase), x509.PEMCipherAES256) if err != nil { return nil, nil, err } } keyPem := pem.EncodeToMemory(block) asn1Bytes, err := asn1.Marshal(key.PublicKey) if err != nil { return nil, nil, err } block = &pem.Block{ Type: "RSA PUBLIC KEY", Bytes: asn1Bytes, } pubPem := pem.EncodeToMemory(block) return keyPem, pubPem, nil } func WaitForOrder(client *gophercloud.ServiceClient, orderID string) error { return tools.WaitFor(func() (bool, error) { order, err := orders.Get(client, orderID).Extract() if err != nil { return false, err } if order.SecretRef != "" { return true, nil } if order.ContainerRef != "" { return true, nil } if order.Status == "ERROR" { return false, fmt.Errorf("Order %s in ERROR state", orderID) } return false, nil }) } orders_test.go000066400000000000000000000042131335005366400351070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/keymanager/v1// +build acceptance keymanager orders package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/containers" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/orders" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/secrets" th "github.com/gophercloud/gophercloud/testhelper" ) func TestOrdersCRUD(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) order, err := CreateKeyOrder(t, client) th.AssertNoErr(t, err) orderID, err := ParseID(order.OrderRef) th.AssertNoErr(t, err) defer DeleteOrder(t, client, orderID) secretID, err := ParseID(order.SecretRef) th.AssertNoErr(t, err) payloadOpts := secrets.GetPayloadOpts{ PayloadContentType: "application/octet-stream", } payload, err := secrets.GetPayload(client, secretID, payloadOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, payload) allPages, err := orders.List(client, nil).AllPages() th.AssertNoErr(t, err) allOrders, err := orders.ExtractOrders(allPages) th.AssertNoErr(t, err) var found bool for _, v := range allOrders { if v.OrderRef == order.OrderRef { found = true } } th.AssertEquals(t, found, true) } func TestOrdersAsymmetric(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) order, err := CreateAsymmetricOrder(t, client) th.AssertNoErr(t, err) orderID, err := ParseID(order.OrderRef) th.AssertNoErr(t, err) defer DeleteOrder(t, client, orderID) containerID, err := ParseID(order.ContainerRef) th.AssertNoErr(t, err) container, err := containers.Get(client, containerID).Extract() th.AssertNoErr(t, err) for _, v := range container.SecretRefs { secretID, err := ParseID(v.SecretRef) th.AssertNoErr(t, err) payloadOpts := secrets.GetPayloadOpts{ PayloadContentType: "application/octet-stream", } payload, err := secrets.GetPayload(client, secretID, payloadOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, string(payload)) } } secrets_test.go000066400000000000000000000155331335005366400352700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/keymanager/v1// +build acceptance keymanager secrets package v1 import ( "testing" "time" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/secrets" th "github.com/gophercloud/gophercloud/testhelper" ) func TestSecretsCRUD(t *testing.T) { client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) payload := tools.RandomString("SUPERSECRET-", 8) secret, err := CreateSecretWithPayload(t, client, payload) th.AssertNoErr(t, err) secretID, err := ParseID(secret.SecretRef) th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) // Test payload retrieval actual, err := secrets.GetPayload(client, secretID, nil).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, payload, string(actual)) // Test listing secrets createdQuery := &secrets.DateQuery{ Date: time.Date(2049, 6, 7, 1, 2, 3, 0, time.UTC), Filter: secrets.DateFilterLT, } listOpts := secrets.ListOpts{ CreatedQuery: createdQuery, } allPages, err := secrets.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allSecrets, err := secrets.ExtractSecrets(allPages) th.AssertNoErr(t, err) var found bool for _, v := range allSecrets { if v.SecretRef == secret.SecretRef { found = true } } th.AssertEquals(t, found, true) } func TestSecretsDelayedPayload(t *testing.T) { client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) secret, err := CreateEmptySecret(t, client) th.AssertNoErr(t, err) secretID, err := ParseID(secret.SecretRef) th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) payload := tools.RandomString("SUPERSECRET-", 8) updateOpts := secrets.UpdateOpts{ ContentType: "text/plain", Payload: payload, } err = secrets.Update(client, secretID, updateOpts).ExtractErr() th.AssertNoErr(t, err) // Test payload retrieval actual, err := secrets.GetPayload(client, secretID, nil).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, payload, string(actual)) } func TestSecretsMetadataCRUD(t *testing.T) { client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) payload := tools.RandomString("SUPERSECRET-", 8) secret, err := CreateSecretWithPayload(t, client, payload) th.AssertNoErr(t, err) secretID, err := ParseID(secret.SecretRef) th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) // Create some metadata createOpts := secrets.MetadataOpts{ "foo": "bar", "something": "something else", } ref, err := secrets.CreateMetadata(client, secretID, createOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ref["metadata_ref"], secret.SecretRef+"/metadata") // Get the metadata metadata, err := secrets.GetMetadata(client, secretID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, metadata) th.AssertEquals(t, metadata["foo"], "bar") th.AssertEquals(t, metadata["something"], "something else") // Add a single metadatum metadatumOpts := secrets.MetadatumOpts{ Key: "bar", Value: "baz", } err = secrets.CreateMetadatum(client, secretID, metadatumOpts).ExtractErr() th.AssertNoErr(t, err) metadata, err = secrets.GetMetadata(client, secretID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, metadata) th.AssertEquals(t, len(metadata), 3) th.AssertEquals(t, metadata["foo"], "bar") th.AssertEquals(t, metadata["something"], "something else") th.AssertEquals(t, metadata["bar"], "baz") // Update a metadatum metadatumOpts.Key = "foo" metadatumOpts.Value = "foo" metadatum, err := secrets.UpdateMetadatum(client, secretID, metadatumOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, metadatum) th.AssertDeepEquals(t, metadatum.Key, "foo") th.AssertDeepEquals(t, metadatum.Value, "foo") metadata, err = secrets.GetMetadata(client, secretID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, metadata) th.AssertEquals(t, len(metadata), 3) th.AssertEquals(t, metadata["foo"], "foo") th.AssertEquals(t, metadata["something"], "something else") th.AssertEquals(t, metadata["bar"], "baz") // Delete a metadatum err = secrets.DeleteMetadatum(client, secretID, "foo").ExtractErr() th.AssertNoErr(t, err) metadata, err = secrets.GetMetadata(client, secretID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, metadata) th.AssertEquals(t, len(metadata), 2) th.AssertEquals(t, metadata["something"], "something else") th.AssertEquals(t, metadata["bar"], "baz") } func TestSymmetricSecret(t *testing.T) { client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) secret, err := CreateSymmetricSecret(t, client) th.AssertNoErr(t, err) secretID, err := ParseID(secret.SecretRef) th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) payload, err := secrets.GetPayload(client, secretID, nil).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, string(payload)) } func TestCertificateSecret(t *testing.T) { client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) pass := tools.RandomString("", 16) cert, _, err := CreateCertificate(t, pass) th.AssertNoErr(t, err) secret, err := CreateCertificateSecret(t, client, cert) th.AssertNoErr(t, err) secretID, err := ParseID(secret.SecretRef) th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) payload, err := secrets.GetPayload(client, secretID, nil).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, string(payload)) } func TestPrivateSecret(t *testing.T) { client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) pass := tools.RandomString("", 16) priv, _, err := CreateCertificate(t, pass) th.AssertNoErr(t, err) secret, err := CreatePrivateSecret(t, client, priv) th.AssertNoErr(t, err) secretID, err := ParseID(secret.SecretRef) th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) payload, err := secrets.GetPayload(client, secretID, nil).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, string(payload)) } func TestPublicSecret(t *testing.T) { client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) _, pub, err := CreateRSAKeyPair(t, "") th.AssertNoErr(t, err) secret, err := CreatePublicSecret(t, client, pub) th.AssertNoErr(t, err) secretID, err := ParseID(secret.SecretRef) th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) payload, err := secrets.GetPayload(client, secretID, nil).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, string(payload)) } func TestPassphraseSecret(t *testing.T) { client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) pass := tools.RandomString("", 16) secret, err := CreatePassphraseSecret(t, client, pass) th.AssertNoErr(t, err) secretID, err := ParseID(secret.SecretRef) th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) payload, err := secrets.GetPayload(client, secretID, nil).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, string(payload)) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/loadbalancer/000077500000000000000000000000001335005366400322405ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/loadbalancer/v2/000077500000000000000000000000001335005366400325675ustar00rootroot00000000000000amphorae_test.go000066400000000000000000000013531335005366400356740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/loadbalancer/v2package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/amphorae" ) func TestAmphoraeList(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewLoadBalancerV2Client() if err != nil { t.Fatalf("Unable to create a loadbalancer client: %v", err) } allPages, err := amphorae.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list amphorae: %v", err) } allAmphorae, err := amphorae.ExtractAmphorae(allPages) if err != nil { t.Fatalf("Unable to extract amphorae: %v", err) } for _, amphora := range allAmphorae { tools.PrintResource(t, amphora) } } l7policies_test.go000066400000000000000000000014321335005366400361500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/loadbalancer/v2// +build acceptance networking loadbalancer l7policies package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" ) func TestL7PoliciesList(t *testing.T) { client, err := clients.NewLoadBalancerV2Client() if err != nil { t.Fatalf("Unable to create a loadbalancer client: %v", err) } allPages, err := l7policies.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list l7policies: %v", err) } allL7Policies, err := l7policies.ExtractL7Policies(allPages) if err != nil { t.Fatalf("Unable to extract l7policies: %v", err) } for _, policy := range allL7Policies { tools.PrintResource(t, policy) } } listeners_test.go000066400000000000000000000014241335005366400361070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/loadbalancer/v2// +build acceptance networking loadbalancer listeners package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" ) func TestListenersList(t *testing.T) { client, err := clients.NewLoadBalancerV2Client() if err != nil { t.Fatalf("Unable to create a loadbalancer client: %v", err) } allPages, err := listeners.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list listeners: %v", err) } allListeners, err := listeners.ExtractListeners(allPages) if err != nil { t.Fatalf("Unable to extract listeners: %v", err) } for _, listener := range allListeners { tools.PrintResource(t, listener) } } loadbalancer.go000066400000000000000000000333051335005366400354520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/loadbalancer/v2package v2 import ( "fmt" "strings" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" ) const loadbalancerActiveTimeoutSeconds = 300 const loadbalancerDeleteTimeoutSeconds = 300 // CreateListener will create a listener for a given load balancer on a random // port with a random name. An error will be returned if the listener could not // be created. func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*listeners.Listener, error) { listenerName := tools.RandomString("TESTACCT-", 8) listenerPort := tools.RandomInt(1, 100) t.Logf("Attempting to create listener %s on port %d", listenerName, listenerPort) createOpts := listeners.CreateOpts{ Name: listenerName, LoadbalancerID: lb.ID, Protocol: "TCP", ProtocolPort: listenerPort, } listener, err := listeners.Create(client, createOpts).Extract() if err != nil { return listener, err } t.Logf("Successfully created listener %s", listenerName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active") } return listener, nil } // CreateLoadBalancer will create a load balancer with a random name on a given // subnet. An error will be returned if the loadbalancer could not be created. func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetID string) (*loadbalancers.LoadBalancer, error) { lbName := tools.RandomString("TESTACCT-", 8) t.Logf("Attempting to create loadbalancer %s on subnet %s", lbName, subnetID) createOpts := loadbalancers.CreateOpts{ Name: lbName, VipSubnetID: subnetID, AdminStateUp: gophercloud.Enabled, } lb, err := loadbalancers.Create(client, createOpts).Extract() if err != nil { return lb, err } t.Logf("Successfully created loadbalancer %s on subnet %s", lbName, subnetID) t.Logf("Waiting for loadbalancer %s to become active", lbName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { return lb, err } t.Logf("LoadBalancer %s is active", lbName) return lb, nil } // CreateMember will create a member with a random name, port, address, and // weight. An error will be returned if the member could not be created. func CreateMember(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer, pool *pools.Pool, subnetID, subnetCIDR string) (*pools.Member, error) { memberName := tools.RandomString("TESTACCT-", 8) memberPort := tools.RandomInt(100, 1000) memberWeight := tools.RandomInt(1, 10) cidrParts := strings.Split(subnetCIDR, "/") subnetParts := strings.Split(cidrParts[0], ".") memberAddress := fmt.Sprintf("%s.%s.%s.%d", subnetParts[0], subnetParts[1], subnetParts[2], tools.RandomInt(10, 100)) t.Logf("Attempting to create member %s", memberName) createOpts := pools.CreateMemberOpts{ Name: memberName, ProtocolPort: memberPort, Weight: memberWeight, Address: memberAddress, SubnetID: subnetID, } t.Logf("Member create opts: %#v", createOpts) member, err := pools.CreateMember(client, pool.ID, createOpts).Extract() if err != nil { return member, err } t.Logf("Successfully created member %s", memberName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { return member, fmt.Errorf("Timed out waiting for loadbalancer to become active") } return member, nil } // CreateMonitor will create a monitor with a random name for a specific pool. // An error will be returned if the monitor could not be created. func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer, pool *pools.Pool) (*monitors.Monitor, error) { monitorName := tools.RandomString("TESTACCT-", 8) t.Logf("Attempting to create monitor %s", monitorName) createOpts := monitors.CreateOpts{ PoolID: pool.ID, Name: monitorName, Delay: 10, Timeout: 5, MaxRetries: 5, Type: "PING", } monitor, err := monitors.Create(client, createOpts).Extract() if err != nil { return monitor, err } t.Logf("Successfully created monitor: %s", monitorName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { return monitor, fmt.Errorf("Timed out waiting for loadbalancer to become active") } return monitor, nil } // CreatePool will create a pool with a random name with a specified listener // and loadbalancer. An error will be returned if the pool could not be // created. func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*pools.Pool, error) { poolName := tools.RandomString("TESTACCT-", 8) t.Logf("Attempting to create pool %s", poolName) createOpts := pools.CreateOpts{ Name: poolName, Protocol: pools.ProtocolTCP, LoadbalancerID: lb.ID, LBMethod: pools.LBMethodLeastConnections, } pool, err := pools.Create(client, createOpts).Extract() if err != nil { return pool, err } t.Logf("Successfully created pool %s", poolName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active") } return pool, nil } // CreateL7Policy will create a l7 policy with a random name with a specified listener // and loadbalancer. An error will be returned if the l7 policy could not be // created. func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *listeners.Listener, lb *loadbalancers.LoadBalancer) (*l7policies.L7Policy, error) { policyName := tools.RandomString("TESTACCT-", 8) t.Logf("Attempting to create l7 policy %s", policyName) createOpts := l7policies.CreateOpts{ Name: policyName, ListenerID: listener.ID, Action: l7policies.ActionRedirectToURL, RedirectURL: "http://www.example.com", } policy, err := l7policies.Create(client, createOpts).Extract() if err != nil { return policy, err } t.Logf("Successfully created l7 policy %s", policyName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { return policy, fmt.Errorf("Timed out waiting for loadbalancer to become active") } return policy, nil } // CreateL7Rule creates a l7 rule for specified l7 policy. func CreateL7Rule(t *testing.T, client *gophercloud.ServiceClient, policyID string, lb *loadbalancers.LoadBalancer) (*l7policies.Rule, error) { t.Logf("Attempting to create l7 rule for policy %s", policyID) createOpts := l7policies.CreateRuleOpts{ RuleType: l7policies.TypePath, CompareType: l7policies.CompareTypeStartWith, Value: "/api", } rule, err := l7policies.CreateRule(client, policyID, createOpts).Extract() if err != nil { return rule, err } t.Logf("Successfully created l7 rule for policy %s", policyID) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { return rule, fmt.Errorf("Timed out waiting for loadbalancer to become active") } return rule, nil } // DeleteL7Policy will delete a specified l7 policy. A fatal error will occur if // the l7 policy could not be deleted. This works best when used as a deferred // function. func DeleteL7Policy(t *testing.T, client *gophercloud.ServiceClient, lbID, policyID string) { t.Logf("Attempting to delete l7 policy %s", policyID) if err := l7policies.Delete(client, policyID).ExtractErr(); err != nil { t.Fatalf("Unable to delete l7 policy: %v", err) } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } t.Logf("Successfully deleted l7 policy %s", policyID) } // DeleteL7Rule will delete a specified l7 rule. A fatal error will occur if // the l7 rule could not be deleted. This works best when used as a deferred // function. func DeleteL7Rule(t *testing.T, client *gophercloud.ServiceClient, lbID, policyID, ruleID string) { t.Logf("Attempting to delete l7 rule %s", ruleID) if err := l7policies.DeleteRule(client, policyID, ruleID).ExtractErr(); err != nil { t.Fatalf("Unable to delete l7 rule: %v", err) } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } t.Logf("Successfully deleted l7 rule %s", ruleID) } // DeleteListener will delete a specified listener. A fatal error will occur if // the listener could not be deleted. This works best when used as a deferred // function. func DeleteListener(t *testing.T, client *gophercloud.ServiceClient, lbID, listenerID string) { t.Logf("Attempting to delete listener %s", listenerID) if err := listeners.Delete(client, listenerID).ExtractErr(); err != nil { t.Fatalf("Unable to delete listener: %v", err) } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } t.Logf("Successfully deleted listener %s", listenerID) } // DeleteMember will delete a specified member. A fatal error will occur if the // member could not be deleted. This works best when used as a deferred // function. func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID, memberID string) { t.Logf("Attempting to delete member %s", memberID) if err := pools.DeleteMember(client, poolID, memberID).ExtractErr(); err != nil { t.Fatalf("Unable to delete member: %s", memberID) } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } t.Logf("Successfully deleted member %s", memberID) } // DeleteLoadBalancer will delete a specified loadbalancer. A fatal error will // occur if the loadbalancer could not be deleted. This works best when used // as a deferred function. func DeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID string) { t.Logf("Attempting to delete loadbalancer %s", lbID) deleteOpts := loadbalancers.DeleteOpts{ Cascade: false, } if err := loadbalancers.Delete(client, lbID, deleteOpts).ExtractErr(); err != nil { t.Fatalf("Unable to delete loadbalancer: %v", err) } t.Logf("Waiting for loadbalancer %s to delete", lbID) if err := WaitForLoadBalancerState(client, lbID, "DELETED", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Loadbalancer did not delete in time.") } t.Logf("Successfully deleted loadbalancer %s", lbID) } // CascadeDeleteLoadBalancer will perform a cascading delete on a loadbalancer. // A fatal error will occur if the loadbalancer could not be deleted. This works // best when used as a deferred function. func CascadeDeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID string) { t.Logf("Attempting to cascade delete loadbalancer %s", lbID) deleteOpts := loadbalancers.DeleteOpts{ Cascade: true, } if err := loadbalancers.Delete(client, lbID, deleteOpts).ExtractErr(); err != nil { t.Fatalf("Unable to cascade delete loadbalancer: %v", err) } t.Logf("Waiting for loadbalancer %s to cascade delete", lbID) if err := WaitForLoadBalancerState(client, lbID, "DELETED", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Loadbalancer did not delete in time.") } t.Logf("Successfully deleted loadbalancer %s", lbID) } // DeleteMonitor will delete a specified monitor. A fatal error will occur if // the monitor could not be deleted. This works best when used as a deferred // function. func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID, monitorID string) { t.Logf("Attempting to delete monitor %s", monitorID) if err := monitors.Delete(client, monitorID).ExtractErr(); err != nil { t.Fatalf("Unable to delete monitor: %v", err) } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } t.Logf("Successfully deleted monitor %s", monitorID) } // DeletePool will delete a specified pool. A fatal error will occur if the // pool could not be deleted. This works best when used as a deferred function. func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID string) { t.Logf("Attempting to delete pool %s", poolID) if err := pools.Delete(client, poolID).ExtractErr(); err != nil { t.Fatalf("Unable to delete pool: %v", err) } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } t.Logf("Successfully deleted pool %s", poolID) } // WaitForLoadBalancerState will wait until a loadbalancer reaches a given state. func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { current, err := loadbalancers.Get(client, lbID).Extract() if err != nil { if httpStatus, ok := err.(gophercloud.ErrDefault404); ok { if httpStatus.Actual == 404 { if status == "DELETED" { return true, nil } } } return false, err } if current.ProvisioningStatus == status { return true, nil } return false, nil }) } loadbalancers_test.go000066400000000000000000000303411335005366400366710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/loadbalancer/v2// +build acceptance networking loadbalancer loadbalancers package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" ) func TestLoadbalancersList(t *testing.T) { client, err := clients.NewLoadBalancerV2Client() if err != nil { t.Fatalf("Unable to create a loadbalancer client: %v", err) } allPages, err := loadbalancers.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list loadbalancers: %v", err) } allLoadbalancers, err := loadbalancers.ExtractLoadBalancers(allPages) if err != nil { t.Fatalf("Unable to extract loadbalancers: %v", err) } for _, lb := range allLoadbalancers { tools.PrintResource(t, lb) } } func TestLoadbalancersCRUD(t *testing.T) { netClient, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a networking client: %v", err) } lbClient, err := clients.NewLoadBalancerV2Client() if err != nil { t.Fatalf("Unable to create a loadbalancer client: %v", err) } network, err := networking.CreateNetwork(t, netClient) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer networking.DeleteNetwork(t, netClient, network.ID) subnet, err := networking.CreateSubnet(t, netClient, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer networking.DeleteSubnet(t, netClient, subnet.ID) lb, err := CreateLoadBalancer(t, lbClient, subnet.ID) if err != nil { t.Fatalf("Unable to create loadbalancer: %v", err) } defer DeleteLoadBalancer(t, lbClient, lb.ID) newLB, err := loadbalancers.Get(lbClient, lb.ID).Extract() if err != nil { t.Fatalf("Unable to get loadbalancer: %v", err) } tools.PrintResource(t, newLB) lbStats, err := loadbalancers.GetStats(lbClient, lb.ID).Extract() if err != nil { t.Fatalf("Unable to get loadbalancer's statistics: %v", err) } tools.PrintResource(t, lbStats) // Because of the time it takes to create a loadbalancer, // this test will include some other resources. // Listener listener, err := CreateListener(t, lbClient, lb) if err != nil { t.Fatalf("Unable to create listener: %v", err) } defer DeleteListener(t, lbClient, lb.ID, listener.ID) updateListenerOpts := listeners.UpdateOpts{ Description: "Some listener description", } _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() if err != nil { t.Fatalf("Unable to update listener") } if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newListener, err := listeners.Get(lbClient, listener.ID).Extract() if err != nil { t.Fatalf("Unable to get listener") } tools.PrintResource(t, newListener) listenerStats, err := listeners.GetStats(lbClient, listener.ID).Extract() if err != nil { t.Fatalf("Unable to get listener's statistics: %v", err) } tools.PrintResource(t, listenerStats) // L7 policy policy, err := CreateL7Policy(t, lbClient, listener, lb) if err != nil { t.Fatalf("Unable to create l7 policy: %v", err) } defer DeleteL7Policy(t, lbClient, lb.ID, policy.ID) newDescription := "New l7 policy description" updateL7policyOpts := l7policies.UpdateOpts{ Description: &newDescription, } _, err = l7policies.Update(lbClient, policy.ID, updateL7policyOpts).Extract() if err != nil { t.Fatalf("Unable to update l7 policy") } if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newPolicy, err := l7policies.Get(lbClient, policy.ID).Extract() if err != nil { t.Fatalf("Unable to get l7 policy: %v", err) } tools.PrintResource(t, newPolicy) // L7 rule rule, err := CreateL7Rule(t, lbClient, newPolicy.ID, lb) if err != nil { t.Fatalf("Unable to create l7 rule: %v", err) } defer DeleteL7Rule(t, lbClient, lb.ID, policy.ID, rule.ID) allPages, err := l7policies.ListRules(lbClient, policy.ID, l7policies.ListRulesOpts{}).AllPages() if err != nil { t.Fatalf("Unable to get l7 rules: %v", err) } allRules, err := l7policies.ExtractRules(allPages) if err != nil { t.Fatalf("Unable to extract l7 rules: %v", err) } for _, rule := range allRules { tools.PrintResource(t, rule) } updateL7ruleOpts := l7policies.UpdateRuleOpts{ RuleType: l7policies.TypePath, CompareType: l7policies.CompareTypeRegex, Value: "/images/special*", } _, err = l7policies.UpdateRule(lbClient, policy.ID, rule.ID, updateL7ruleOpts).Extract() if err != nil { t.Fatalf("Unable to update l7 rule: %v", err) } if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newRule, err := l7policies.GetRule(lbClient, newPolicy.ID, rule.ID).Extract() if err != nil { t.Fatalf("Unable to get l7 rule: %v", err) } tools.PrintResource(t, newRule) // Pool pool, err := CreatePool(t, lbClient, lb) if err != nil { t.Fatalf("Unable to create pool: %v", err) } defer DeletePool(t, lbClient, lb.ID, pool.ID) updatePoolOpts := pools.UpdateOpts{ Description: "Some pool description", } _, err = pools.Update(lbClient, pool.ID, updatePoolOpts).Extract() if err != nil { t.Fatalf("Unable to update pool") } if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newPool, err := pools.Get(lbClient, pool.ID).Extract() if err != nil { t.Fatalf("Unable to get pool") } tools.PrintResource(t, newPool) // Member member, err := CreateMember(t, lbClient, lb, newPool, subnet.ID, subnet.CIDR) if err != nil { t.Fatalf("Unable to create member: %v", err) } defer DeleteMember(t, lbClient, lb.ID, pool.ID, member.ID) newWeight := tools.RandomInt(11, 100) updateMemberOpts := pools.UpdateMemberOpts{ Weight: newWeight, } _, err = pools.UpdateMember(lbClient, pool.ID, member.ID, updateMemberOpts).Extract() if err != nil { t.Fatalf("Unable to update pool") } if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newMember, err := pools.GetMember(lbClient, pool.ID, member.ID).Extract() if err != nil { t.Fatalf("Unable to get member") } tools.PrintResource(t, newMember) newWeight = tools.RandomInt(11, 100) memberOpts := pools.BatchUpdateMemberOpts{ Address: member.Address, ProtocolPort: member.ProtocolPort, Weight: newWeight, } batchMembers := []pools.BatchUpdateMemberOpts{memberOpts} if err := pools.BatchUpdateMembers(lbClient, pool.ID, batchMembers).ExtractErr(); err != nil { t.Fatalf("Unable to batch update members") } if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newMember, err = pools.GetMember(lbClient, pool.ID, member.ID).Extract() if err != nil { t.Fatalf("Unable to get member") } tools.PrintResource(t, newMember) // Monitor monitor, err := CreateMonitor(t, lbClient, lb, newPool) if err != nil { t.Fatalf("Unable to create monitor: %v", err) } defer DeleteMonitor(t, lbClient, lb.ID, monitor.ID) newDelay := tools.RandomInt(20, 30) updateMonitorOpts := monitors.UpdateOpts{ Delay: newDelay, } _, err = monitors.Update(lbClient, monitor.ID, updateMonitorOpts).Extract() if err != nil { t.Fatalf("Unable to update monitor") } if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newMonitor, err := monitors.Get(lbClient, monitor.ID).Extract() if err != nil { t.Fatalf("Unable to get monitor") } tools.PrintResource(t, newMonitor) } func TestLoadbalancersCascadeCRUD(t *testing.T) { netClient, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a networking client: %v", err) } lbClient, err := clients.NewLoadBalancerV2Client() if err != nil { t.Fatalf("Unable to create a loadbalancer client: %v", err) } network, err := networking.CreateNetwork(t, netClient) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer networking.DeleteNetwork(t, netClient, network.ID) subnet, err := networking.CreateSubnet(t, netClient, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer networking.DeleteSubnet(t, netClient, subnet.ID) lb, err := CreateLoadBalancer(t, lbClient, subnet.ID) if err != nil { t.Fatalf("Unable to create loadbalancer: %v", err) } defer CascadeDeleteLoadBalancer(t, lbClient, lb.ID) newLB, err := loadbalancers.Get(lbClient, lb.ID).Extract() if err != nil { t.Fatalf("Unable to get loadbalancer: %v", err) } tools.PrintResource(t, newLB) // Because of the time it takes to create a loadbalancer, // this test will include some other resources. // Listener listener, err := CreateListener(t, lbClient, lb) if err != nil { t.Fatalf("Unable to create listener: %v", err) } updateListenerOpts := listeners.UpdateOpts{ Description: "Some listener description", } _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() if err != nil { t.Fatalf("Unable to update listener") } if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newListener, err := listeners.Get(lbClient, listener.ID).Extract() if err != nil { t.Fatalf("Unable to get listener") } tools.PrintResource(t, newListener) // Pool pool, err := CreatePool(t, lbClient, lb) if err != nil { t.Fatalf("Unable to create pool: %v", err) } updatePoolOpts := pools.UpdateOpts{ Description: "Some pool description", } _, err = pools.Update(lbClient, pool.ID, updatePoolOpts).Extract() if err != nil { t.Fatalf("Unable to update pool") } if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newPool, err := pools.Get(lbClient, pool.ID).Extract() if err != nil { t.Fatalf("Unable to get pool") } tools.PrintResource(t, newPool) // Member member, err := CreateMember(t, lbClient, lb, newPool, subnet.ID, subnet.CIDR) if err != nil { t.Fatalf("Unable to create member: %v", err) } newWeight := tools.RandomInt(11, 100) updateMemberOpts := pools.UpdateMemberOpts{ Weight: newWeight, } _, err = pools.UpdateMember(lbClient, pool.ID, member.ID, updateMemberOpts).Extract() if err != nil { t.Fatalf("Unable to update pool") } if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newMember, err := pools.GetMember(lbClient, pool.ID, member.ID).Extract() if err != nil { t.Fatalf("Unable to get member") } tools.PrintResource(t, newMember) // Monitor monitor, err := CreateMonitor(t, lbClient, lb, newPool) if err != nil { t.Fatalf("Unable to create monitor: %v", err) } newDelay := tools.RandomInt(20, 30) updateMonitorOpts := monitors.UpdateOpts{ Delay: newDelay, } _, err = monitors.Update(lbClient, monitor.ID, updateMonitorOpts).Extract() if err != nil { t.Fatalf("Unable to update monitor") } if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newMonitor, err := monitors.Get(lbClient, monitor.ID).Extract() if err != nil { t.Fatalf("Unable to get monitor") } tools.PrintResource(t, newMonitor) } monitors_test.go000066400000000000000000000014101335005366400357440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/loadbalancer/v2// +build acceptance networking loadbalancer monitors package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" ) func TestMonitorsList(t *testing.T) { client, err := clients.NewLoadBalancerV2Client() if err != nil { t.Fatalf("Unable to create a loadbalancer client: %v", err) } allPages, err := monitors.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list monitors: %v", err) } allMonitors, err := monitors.ExtractMonitors(allPages) if err != nil { t.Fatalf("Unable to extract monitors: %v", err) } for _, monitor := range allMonitors { tools.PrintResource(t, monitor) } } pkg.go000066400000000000000000000000131335005366400336120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/loadbalancer/v2package v2 pools_test.go000066400000000000000000000013441335005366400352340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/loadbalancer/v2// +build acceptance networking loadbalancer pools package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" ) func TestPoolsList(t *testing.T) { client, err := clients.NewLoadBalancerV2Client() if err != nil { t.Fatalf("Unable to create a loadbalancer client: %v", err) } allPages, err := pools.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list pools: %v", err) } allPools, err := pools.ExtractPools(allPages) if err != nil { t.Fatalf("Unable to extract pools: %v", err) } for _, pool := range allPools { tools.PrintResource(t, pool) } } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/messaging/000077500000000000000000000000001335005366400316065ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/messaging/v2/000077500000000000000000000000001335005366400321355ustar00rootroot00000000000000claims_test.go000066400000000000000000000032571335005366400347230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/messaging/v2// +build acceptance messaging claims package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/messaging/v2/claims" ) func TestCRUDClaim(t *testing.T) { clientID := "3381af92-2b9e-11e3-b191-71861300734c" client, err := clients.NewMessagingV2Client(clientID) if err != nil { t.Fatalf("Unable to create a messaging service client: %v", err) } createdQueueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, createdQueueName) clientID = "3381af92-2b9e-11e3-b191-71861300734d" client, err = clients.NewMessagingV2Client(clientID) if err != nil { t.Fatalf("Unable to create a messaging service client: %v", err) } for i := 0; i < 3; i++ { CreateMessage(t, client, createdQueueName) } clientID = "3381af92-2b9e-11e3-b191-7186130073dd" claimedMessages, err := CreateClaim(t, client, createdQueueName) claimIDs, _ := ExtractIDs(claimedMessages) tools.PrintResource(t, claimedMessages) updateOpts := claims.UpdateOpts{ TTL: 600, Grace: 500, } for _, claimID := range claimIDs { t.Logf("Attempting to update claim: %s", claimID) updateErr := claims.Update(client, createdQueueName, claimID, updateOpts).ExtractErr() if updateErr != nil { t.Fatalf("Unable to update claim %s: %v", claimID, err) } else { t.Logf("Successfully updated claim: %s", claimID) } updatedClaim, getErr := GetClaim(t, client, createdQueueName, claimID) if getErr != nil { t.Fatalf("Unable to retrieve claim %s: %v", claimID, getErr) } tools.PrintResource(t, updatedClaim) DeleteClaim(t, client, createdQueueName, claimID) } } message_test.go000066400000000000000000000206771335005366400351040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/messaging/v2// +build acceptance messaging messages package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/messaging/v2/messages" "github.com/gophercloud/gophercloud/pagination" ) func TestListMessages(t *testing.T) { clientID := "3381af92-2b9e-11e3-b191-718613007343" client, err := clients.NewMessagingV2Client(clientID) if err != nil { t.Fatalf("Unable to create a messaging service client: %v", err) } createdQueueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, createdQueueName) for i := 0; i < 3; i++ { CreateMessage(t, client, createdQueueName) } // Use a different client/clientID in order to see messages on the Queue clientID = "3381af92-2b9e-11e3-b191-71861300734d" client, err = clients.NewMessagingV2Client(clientID) listOpts := messages.ListOpts{} pager := messages.List(client, createdQueueName, listOpts) err = pager.EachPage(func(page pagination.Page) (bool, error) { allMessages, err := messages.ExtractMessages(page) if err != nil { t.Fatalf("Unable to extract messages: %v", err) } for _, message := range allMessages { tools.PrintResource(t, message) } return true, nil }) } func TestCreateMessages(t *testing.T) { clientID := "3381af92-2b9e-11e3-b191-71861300734c" client, err := clients.NewMessagingV2Client(clientID) if err != nil { t.Fatalf("Unable to create a messaging service client: %v", err) } createdQueueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, createdQueueName) CreateMessage(t, client, createdQueueName) } func TestGetMessages(t *testing.T) { clientID := "3381af92-2b9e-11e3-b191-718613007343" client, err := clients.NewMessagingV2Client(clientID) if err != nil { t.Fatalf("Unable to create a messaging service client: %v", err) } createdQueueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, createdQueueName) CreateMessage(t, client, createdQueueName) CreateMessage(t, client, createdQueueName) // Use a different client/clientID in order to see messages on the Queue clientID = "3381af92-2b9e-11e3-b191-71861300734d" client, err = clients.NewMessagingV2Client(clientID) listOpts := messages.ListOpts{} var messageIDs []string pager := messages.List(client, createdQueueName, listOpts) err = pager.EachPage(func(page pagination.Page) (bool, error) { allMessages, err := messages.ExtractMessages(page) if err != nil { t.Fatalf("Unable to extract messages: %v", err) } for _, message := range allMessages { messageIDs = append(messageIDs, message.ID) } return true, nil }) getMessageOpts := messages.GetMessagesOpts{ IDs: messageIDs, } t.Logf("Attempting to get messages from queue %s with ids: %v", createdQueueName, messageIDs) messagesList, err := messages.GetMessages(client, createdQueueName, getMessageOpts).Extract() if err != nil { t.Fatalf("Unable to get messages from queue: %s", createdQueueName) } tools.PrintResource(t, messagesList) } func TestGetMessage(t *testing.T) { clientID := "3381af92-2b9e-11e3-b191-718613007343" client, err := clients.NewMessagingV2Client(clientID) if err != nil { t.Fatalf("Unable to create a messaging service client: %v", err) } createdQueueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, createdQueueName) CreateMessage(t, client, createdQueueName) // Use a different client/clientID in order to see messages on the Queue clientID = "3381af92-2b9e-11e3-b191-71861300734d" client, err = clients.NewMessagingV2Client(clientID) listOpts := messages.ListOpts{} var messageIDs []string pager := messages.List(client, createdQueueName, listOpts) err = pager.EachPage(func(page pagination.Page) (bool, error) { allMessages, err := messages.ExtractMessages(page) if err != nil { t.Fatalf("Unable to extract messages: %v", err) } for _, message := range allMessages { messageIDs = append(messageIDs, message.ID) } return true, nil }) for _, messageID := range messageIDs { t.Logf("Attempting to get message from queue %s: %s", createdQueueName, messageID) message, getErr := messages.Get(client, createdQueueName, messageID).Extract() if getErr != nil { t.Fatalf("Unable to get message from queue %s: %s", createdQueueName, messageID) } tools.PrintResource(t, message) } } func TestDeleteMessagesIDs(t *testing.T) { clientID := "3381af92-2b9e-11e3-b191-718613007343" client, err := clients.NewMessagingV2Client(clientID) if err != nil { t.Fatalf("Unable to create a messaging service client: %v", err) } createdQueueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, createdQueueName) CreateMessage(t, client, createdQueueName) CreateMessage(t, client, createdQueueName) // Use a different client/clientID in order to see messages on the Queue clientID = "3381af92-2b9e-11e3-b191-71861300734d" client, err = clients.NewMessagingV2Client(clientID) listOpts := messages.ListOpts{} var messageIDs []string pager := messages.List(client, createdQueueName, listOpts) err = pager.EachPage(func(page pagination.Page) (bool, error) { allMessages, err := messages.ExtractMessages(page) if err != nil { t.Fatalf("Unable to extract messages: %v", err) } for _, message := range allMessages { messageIDs = append(messageIDs, message.ID) tools.PrintResource(t, message) } return true, nil }) deleteOpts := messages.DeleteMessagesOpts{ IDs: messageIDs, } t.Logf("Attempting to delete messages: %v", messageIDs) deleteErr := messages.DeleteMessages(client, createdQueueName, deleteOpts).ExtractErr() if deleteErr != nil { t.Fatalf("Unable to delete messages: %v", deleteErr) } t.Logf("Attempting to list messages.") messageList, err := ListMessages(t, client, createdQueueName) if len(messageList) > 0 { t.Fatalf("Did not delete all specified messages in the queue.") } } func TestDeleteMessagesPop(t *testing.T) { clientID := "3381af92-2b9e-11e3-b191-718613007343" client, err := clients.NewMessagingV2Client(clientID) if err != nil { t.Fatalf("Unable to create a messaging service client: %v", err) } createdQueueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, createdQueueName) for i := 0; i < 5; i++ { CreateMessage(t, client, createdQueueName) } // Use a different client/clientID in order to see messages on the Queue clientID = "3381af92-2b9e-11e3-b191-71861300734d" client, err = clients.NewMessagingV2Client(clientID) messageList, err := ListMessages(t, client, createdQueueName) messagesNumber := len(messageList) popNumber := 3 PopOpts := messages.PopMessagesOpts{ Pop: popNumber, } t.Logf("Attempting to Pop last %v messages.", popNumber) popMessages, deleteErr := messages.PopMessages(client, createdQueueName, PopOpts).Extract() if deleteErr != nil { t.Fatalf("Unable to Pop messages: %v", deleteErr) } tools.PrintResource(t, popMessages) messageList, err = ListMessages(t, client, createdQueueName) if len(messageList) != messagesNumber-popNumber { t.Fatalf("Unable to Pop specified number of messages.") } } func TestDeleteMessage(t *testing.T) { clientID := "3381af92-2b9e-11e3-b191-718613007343" client, err := clients.NewMessagingV2Client(clientID) if err != nil { t.Fatalf("Unable to create a messaging service client: %v", err) } createdQueueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, createdQueueName) CreateMessage(t, client, createdQueueName) // Use a different client/clientID in order to see messages on the Queue clientID = "3381af92-2b9e-11e3-b191-71861300734d" client, err = clients.NewMessagingV2Client(clientID) listOpts := messages.ListOpts{} var messageIDs []string pager := messages.List(client, createdQueueName, listOpts) err = pager.EachPage(func(page pagination.Page) (bool, error) { allMessages, err := messages.ExtractMessages(page) if err != nil { t.Fatalf("Unable to extract messages: %v", err) } for _, message := range allMessages { messageIDs = append(messageIDs, message.ID) } return true, nil }) for _, messageID := range messageIDs { t.Logf("Attempting to delete message from queue %s: %s", createdQueueName, messageID) deleteOpts := messages.DeleteOpts{} deleteErr := messages.Delete(client, createdQueueName, messageID, deleteOpts).ExtractErr() if deleteErr != nil { t.Fatalf("Unable to delete message from queue %s: %s", createdQueueName, messageID) } else { t.Logf("Successfully deleted message: %s", messageID) } } } messaging.go000066400000000000000000000121741335005366400343670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/messaging/v2package v2 import ( "strings" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/messaging/v2/claims" "github.com/gophercloud/gophercloud/openstack/messaging/v2/messages" "github.com/gophercloud/gophercloud/openstack/messaging/v2/queues" "github.com/gophercloud/gophercloud/pagination" ) func CreateQueue(t *testing.T, client *gophercloud.ServiceClient) (string, error) { queueName := tools.RandomString("ACPTTEST", 5) t.Logf("Attempting to create Queue: %s", queueName) createOpts := queues.CreateOpts{ QueueName: queueName, MaxMessagesPostSize: 262143, DefaultMessageTTL: 3700, DefaultMessageDelay: 25, DeadLetterQueueMessagesTTL: 3500, MaxClaimCount: 10, Extra: map[string]interface{}{"description": "Test Queue for Gophercloud acceptance tests."}, } createErr := queues.Create(client, createOpts).ExtractErr() if createErr != nil { t.Fatalf("Unable to create Queue: %v", createErr) } GetQueue(t, client, queueName) t.Logf("Created Queue: %s", queueName) return queueName, nil } func DeleteQueue(t *testing.T, client *gophercloud.ServiceClient, queueName string) { t.Logf("Attempting to delete Queue: %s", queueName) err := queues.Delete(client, queueName).ExtractErr() if err != nil { t.Fatalf("Unable to delete Queue %s: %v", queueName, err) } t.Logf("Deleted Queue: %s", queueName) } func GetQueue(t *testing.T, client *gophercloud.ServiceClient, queueName string) (queues.QueueDetails, error) { t.Logf("Attempting to get Queue: %s", queueName) queue, err := queues.Get(client, queueName).Extract() if err != nil { t.Fatalf("Unable to get Queue %s: %v", queueName, err) } return queue, nil } func CreateShare(t *testing.T, client *gophercloud.ServiceClient, queueName string) (queues.QueueShare, error) { t.Logf("Attempting to create share for queue: %s", queueName) shareOpts := queues.ShareOpts{ Paths: []queues.SharePath{queues.PathMessages}, Methods: []queues.ShareMethod{queues.MethodPost}, } share, err := queues.Share(client, queueName, shareOpts).Extract() return share, err } func CreateMessage(t *testing.T, client *gophercloud.ServiceClient, queueName string) (messages.ResourceList, error) { t.Logf("Attempting to add message to Queue: %s", queueName) createOpts := messages.BatchCreateOpts{ messages.CreateOpts{ TTL: 300, Body: map[string]interface{}{"Key": tools.RandomString("ACPTTEST", 8)}, }, } resource, err := messages.Create(client, queueName, createOpts).Extract() if err != nil { t.Fatalf("Unable to add message to queue %s: %v", queueName, err) } else { t.Logf("Successfully added message to queue: %s", queueName) } return resource, err } func ListMessages(t *testing.T, client *gophercloud.ServiceClient, queueName string) ([]messages.Message, error) { listOpts := messages.ListOpts{} var allMessages []messages.Message var listErr error t.Logf("Attempting to list messages on queue: %s", queueName) pager := messages.List(client, queueName, listOpts) err := pager.EachPage(func(page pagination.Page) (bool, error) { allMessages, listErr = messages.ExtractMessages(page) if listErr != nil { t.Fatalf("Unable to extract messages: %v", listErr) } for _, message := range allMessages { tools.PrintResource(t, message) } return true, nil }) return allMessages, err } func CreateClaim(t *testing.T, client *gophercloud.ServiceClient, queueName string) ([]claims.Messages, error) { createOpts := claims.CreateOpts{} t.Logf("Attempting to create claim on queue: %s", queueName) claimedMessages, err := claims.Create(client, queueName, createOpts).Extract() tools.PrintResource(t, claimedMessages) if err != nil { t.Fatalf("Unable to create claim: %v", err) } return claimedMessages, err } func GetClaim(t *testing.T, client *gophercloud.ServiceClient, queueName string, claimID string) (*claims.Claim, error) { t.Logf("Attempting to get claim: %s", claimID) claim, err := claims.Get(client, queueName, claimID).Extract() if err != nil { t.Fatalf("Unable to get claim: %s", claimID) } return claim, err } func DeleteClaim(t *testing.T, client *gophercloud.ServiceClient, queueName string, claimID string) error { t.Logf("Attempting to delete claim: %s", claimID) err := claims.Delete(client, queueName, claimID).ExtractErr() if err != nil { t.Fatalf("Unable to delete claim: %s", claimID) } t.Logf("Sucessfully deleted claim: %s", claimID) return err } func ExtractIDs(claim []claims.Messages) ([]string, []string) { var claimIDs []string var messageID []string for _, msg := range claim { parts := strings.Split(msg.Href, "?claim_id=") if len(parts) == 2 { pieces := strings.Split(parts[0], "/") if len(pieces) > 0 { messageID = append(messageID, pieces[len(pieces)-1]) } claimIDs = append(claimIDs, parts[1]) } } encountered := map[string]bool{} for v := range claimIDs { encountered[claimIDs[v]] = true } var uniqueClaimIDs []string for key := range encountered { uniqueClaimIDs = append(uniqueClaimIDs, key) } return uniqueClaimIDs, messageID } queue_test.go000066400000000000000000000077411335005366400346010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/messaging/v2// +build acceptance messaging queues package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/messaging/v2/queues" "github.com/gophercloud/gophercloud/pagination" ) func TestCRUDQueues(t *testing.T) { clientID := "3381af92-2b9e-11e3-b191-71861300734d" client, err := clients.NewMessagingV2Client(clientID) if err != nil { t.Fatalf("Unable to create a messaging service client: %v", err) } createdQueueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, createdQueueName) createdQueue, err := queues.Get(client, createdQueueName).Extract() tools.PrintResource(t, createdQueue) tools.PrintResource(t, createdQueue.Extra) updateOpts := queues.BatchUpdateOpts{ queues.UpdateOpts{ Op: "replace", Path: "/metadata/_max_claim_count", Value: 15, }, queues.UpdateOpts{ Op: "replace", Path: "/metadata/description", Value: "Updated description for queues acceptance test.", }, } t.Logf("Attempting to update Queue: %s", createdQueueName) updateResult, updateErr := queues.Update(client, createdQueueName, updateOpts).Extract() if updateErr != nil { t.Fatalf("Unable to update Queue %s: %v", createdQueueName, updateErr) } updatedQueue, err := GetQueue(t, client, createdQueueName) tools.PrintResource(t, updateResult) tools.PrintResource(t, updatedQueue) tools.PrintResource(t, updatedQueue.Extra) } func TestListQueues(t *testing.T) { clientID := "3381af92-2b9e-11e3-b191-71861300734d" client, err := clients.NewMessagingV2Client(clientID) if err != nil { t.Fatalf("Unable to create a messaging service client: %v", err) } firstQueueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, firstQueueName) secondQueueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, secondQueueName) listOpts := queues.ListOpts{ Limit: 10, Detailed: true, } pager := queues.List(client, listOpts) err = pager.EachPage(func(page pagination.Page) (bool, error) { allQueues, err := queues.ExtractQueues(page) if err != nil { t.Fatalf("Unable to extract Queues: %v", err) } for _, queue := range allQueues { tools.PrintResource(t, queue) } return true, nil }) } func TestStatQueue(t *testing.T) { clientID := "3381af92-2b9e-11e3-b191-71861300734c" client, err := clients.NewMessagingV2Client(clientID) if err != nil { t.Fatalf("Unable to create a messaging service client: %v", err) } createdQueueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, createdQueueName) queueStats, err := queues.GetStats(client, createdQueueName).Extract() if err != nil { t.Fatalf("Unable to stat queue: %v", err) } tools.PrintResource(t, queueStats) } func TestShare(t *testing.T) { clientID := "3381af92-2b9e-11e3-b191-71861300734c" client, err := clients.NewMessagingV2Client(clientID) if err != nil { t.Fatalf("Unable to create a messaging service client: %v", err) } queueName, err := CreateQueue(t, client) if err != nil { t.Logf("Unable to create queue for share.") } defer DeleteQueue(t, client, queueName) t.Logf("Attempting to create share for queue: %s", queueName) share, shareErr := CreateShare(t, client, queueName) if shareErr != nil { t.Fatalf("Unable to create share: %v", shareErr) } tools.PrintResource(t, share) } func TestPurge(t *testing.T) { clientID := "3381af92-2b9e-11e3-b191-71861300734c" client, err := clients.NewMessagingV2Client(clientID) if err != nil { t.Fatalf("Unable to create a messaging service client: %v", err) } queueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, queueName) purgeOpts := queues.PurgeOpts{ ResourceTypes: []queues.PurgeResource{ queues.ResourceMessages, }, } t.Logf("Attempting to purge queue: %s", queueName) purgeErr := queues.Purge(client, queueName, purgeOpts).ExtractErr() if purgeErr != nil { t.Fatalf("Unable to purge queue %s: %v", queueName, purgeErr) } } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/000077500000000000000000000000001335005366400320205ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/000077500000000000000000000000001335005366400323475ustar00rootroot00000000000000apiversion_test.go000066400000000000000000000025231335005366400360370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2// +build acceptance networking package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/apiversions" ) func TestAPIVersionsList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := apiversions.ListVersions(client).AllPages() if err != nil { t.Fatalf("Unable to list api versions: %v", err) } allAPIVersions, err := apiversions.ExtractAPIVersions(allPages) if err != nil { t.Fatalf("Unable to extract api versions: %v", err) } for _, apiVersion := range allAPIVersions { tools.PrintResource(t, apiVersion) } } func TestAPIResourcesList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := apiversions.ListVersionResources(client, "v2.0").AllPages() if err != nil { t.Fatalf("Unable to list api version reosources: %v", err) } allVersionResources, err := apiversions.ExtractVersionResources(allPages) if err != nil { t.Fatalf("Unable to extract version resources: %v", err) } for _, versionResource := range allVersionResources { tools.PrintResource(t, versionResource) } } extension_test.go000066400000000000000000000021221335005366400356670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2// +build acceptance networking extensions package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/common/extensions" ) func TestExtensionsList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := extensions.List(client).AllPages() if err != nil { t.Fatalf("Unable to list extensions: %v", err) } allExtensions, err := extensions.ExtractExtensions(allPages) if err != nil { t.Fatalf("Unable to extract extensions: %v", err) } for _, extension := range allExtensions { tools.PrintResource(t, extension) } } func TestExtensionGet(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } extension, err := extensions.Get(client, "router").Extract() if err != nil { t.Fatalf("Unable to get extension port-security: %v", err) } tools.PrintResource(t, extension) } extensions/000077500000000000000000000000001335005366400344675ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2extensions.go000066400000000000000000000105661335005366400372250ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensionspackage extensions import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" ) // CreateExternalNetwork will create an external network. An error will be // returned if the creation failed. func CreateExternalNetwork(t *testing.T, client *gophercloud.ServiceClient) (*networks.Network, error) { networkName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create external network: %s", networkName) adminStateUp := true isExternal := true networkCreateOpts := networks.CreateOpts{ Name: networkName, AdminStateUp: &adminStateUp, } createOpts := external.CreateOptsExt{ CreateOptsBuilder: networkCreateOpts, External: &isExternal, } network, err := networks.Create(client, createOpts).Extract() if err != nil { return network, err } t.Logf("Created external network: %s", networkName) return network, nil } // CreatePortWithSecurityGroup will create a port with a security group // attached. An error will be returned if the port could not be created. func CreatePortWithSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID, secGroupID string) (*ports.Port, error) { portName := tools.RandomString("TESTACC-", 8) iFalse := false t.Logf("Attempting to create port: %s", portName) createOpts := ports.CreateOpts{ NetworkID: networkID, Name: portName, AdminStateUp: &iFalse, FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, SecurityGroups: &[]string{secGroupID}, } port, err := ports.Create(client, createOpts).Extract() if err != nil { return port, err } t.Logf("Successfully created port: %s", portName) return port, nil } // CreateSecurityGroup will create a security group with a random name. // An error will be returned if one was failed to be created. func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (*groups.SecGroup, error) { secGroupName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create security group: %s", secGroupName) createOpts := groups.CreateOpts{ Name: secGroupName, } secGroup, err := groups.Create(client, createOpts).Extract() if err != nil { return secGroup, err } t.Logf("Created security group: %s", secGroup.ID) return secGroup, nil } // CreateSecurityGroupRule will create a security group rule with a random name // and random port between 80 and 99. // An error will be returned if one was failed to be created. func CreateSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, secGroupID string) (*rules.SecGroupRule, error) { t.Logf("Attempting to create security group rule in group: %s", secGroupID) fromPort := tools.RandomInt(80, 89) toPort := tools.RandomInt(90, 99) createOpts := rules.CreateOpts{ Direction: "ingress", EtherType: "IPv4", SecGroupID: secGroupID, PortRangeMin: fromPort, PortRangeMax: toPort, Protocol: rules.ProtocolTCP, } rule, err := rules.Create(client, createOpts).Extract() if err != nil { return rule, err } t.Logf("Created security group rule: %s", rule.ID) return rule, nil } // DeleteSecurityGroup will delete a security group of a specified ID. // A fatal error will occur if the deletion failed. This works best as a // deferred function func DeleteSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, secGroupID string) { t.Logf("Attempting to delete security group: %s", secGroupID) err := groups.Delete(client, secGroupID).ExtractErr() if err != nil { t.Fatalf("Unable to delete security group: %v", err) } } // DeleteSecurityGroupRule will delete a security group rule of a specified ID. // A fatal error will occur if the deletion failed. This works best as a // deferred function func DeleteSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, ruleID string) { t.Logf("Attempting to delete security group rule: %s", ruleID) err := rules.Delete(client, ruleID).ExtractErr() if err != nil { t.Fatalf("Unable to delete security group rule: %v", err) } } fwaas/000077500000000000000000000000001335005366400355705ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensionsfirewall_test.go000066400000000000000000000124011335005366400407610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/fwaas// +build acceptance networking fwaas package fwaas import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" layer3 "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/layer3" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/routerinsertion" ) func TestFirewallList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := firewalls.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list firewalls: %v", err) } allFirewalls, err := firewalls.ExtractFirewalls(allPages) if err != nil { t.Fatalf("Unable to extract firewalls: %v", err) } for _, firewall := range allFirewalls { tools.PrintResource(t, firewall) } } func TestFirewallCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } router, err := layer3.CreateExternalRouter(t, client) if err != nil { t.Fatalf("Unable to create router: %v", err) } defer layer3.DeleteRouter(t, client, router.ID) rule, err := CreateRule(t, client) if err != nil { t.Fatalf("Unable to create rule: %v", err) } defer DeleteRule(t, client, rule.ID) tools.PrintResource(t, rule) policy, err := CreatePolicy(t, client, rule.ID) if err != nil { t.Fatalf("Unable to create policy: %v", err) } defer DeletePolicy(t, client, policy.ID) tools.PrintResource(t, policy) firewall, err := CreateFirewall(t, client, policy.ID) if err != nil { t.Fatalf("Unable to create firewall: %v", err) } defer DeleteFirewall(t, client, firewall.ID) tools.PrintResource(t, firewall) updateOpts := firewalls.UpdateOpts{ PolicyID: policy.ID, Description: "Some firewall description", } _, err = firewalls.Update(client, firewall.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update firewall: %v", err) } newFirewall, err := firewalls.Get(client, firewall.ID).Extract() if err != nil { t.Fatalf("Unable to get firewall: %v", err) } tools.PrintResource(t, newFirewall) } func TestFirewallCRUDRouter(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } router, err := layer3.CreateExternalRouter(t, client) if err != nil { t.Fatalf("Unable to create router: %v", err) } defer layer3.DeleteRouter(t, client, router.ID) rule, err := CreateRule(t, client) if err != nil { t.Fatalf("Unable to create rule: %v", err) } defer DeleteRule(t, client, rule.ID) tools.PrintResource(t, rule) policy, err := CreatePolicy(t, client, rule.ID) if err != nil { t.Fatalf("Unable to create policy: %v", err) } defer DeletePolicy(t, client, policy.ID) tools.PrintResource(t, policy) firewall, err := CreateFirewallOnRouter(t, client, policy.ID, router.ID) if err != nil { t.Fatalf("Unable to create firewall: %v", err) } defer DeleteFirewall(t, client, firewall.ID) tools.PrintResource(t, firewall) router2, err := layer3.CreateExternalRouter(t, client) if err != nil { t.Fatalf("Unable to create router: %v", err) } defer layer3.DeleteRouter(t, client, router2.ID) firewallUpdateOpts := firewalls.UpdateOpts{ PolicyID: policy.ID, Description: "Some firewall description", } updateOpts := routerinsertion.UpdateOptsExt{ firewallUpdateOpts, []string{router2.ID}, } _, err = firewalls.Update(client, firewall.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update firewall: %v", err) } newFirewall, err := firewalls.Get(client, firewall.ID).Extract() if err != nil { t.Fatalf("Unable to get firewall: %v", err) } tools.PrintResource(t, newFirewall) } func TestFirewallCRUDRemoveRouter(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } router, err := layer3.CreateExternalRouter(t, client) if err != nil { t.Fatalf("Unable to create router: %v", err) } defer layer3.DeleteRouter(t, client, router.ID) rule, err := CreateRule(t, client) if err != nil { t.Fatalf("Unable to create rule: %v", err) } defer DeleteRule(t, client, rule.ID) tools.PrintResource(t, rule) policy, err := CreatePolicy(t, client, rule.ID) if err != nil { t.Fatalf("Unable to create policy: %v", err) } defer DeletePolicy(t, client, policy.ID) tools.PrintResource(t, policy) firewall, err := CreateFirewallOnRouter(t, client, policy.ID, router.ID) if err != nil { t.Fatalf("Unable to create firewall: %v", err) } defer DeleteFirewall(t, client, firewall.ID) tools.PrintResource(t, firewall) firewallUpdateOpts := firewalls.UpdateOpts{ PolicyID: policy.ID, Description: "Some firewall description", } updateOpts := routerinsertion.UpdateOptsExt{ firewallUpdateOpts, []string{}, } _, err = firewalls.Update(client, firewall.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update firewall: %v", err) } newFirewall, err := firewalls.Get(client, firewall.ID).Extract() if err != nil { t.Fatalf("Unable to get firewall: %v", err) } tools.PrintResource(t, newFirewall) } fwaas.go000066400000000000000000000145451335005366400372310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/fwaaspackage fwaas import ( "fmt" "strconv" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/routerinsertion" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules" ) // CreateFirewall will create a Firewaill with a random name and a specified // policy ID. An error will be returned if the firewall could not be created. func CreateFirewall(t *testing.T, client *gophercloud.ServiceClient, policyID string) (*firewalls.Firewall, error) { firewallName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create firewall %s", firewallName) iTrue := true createOpts := firewalls.CreateOpts{ Name: firewallName, PolicyID: policyID, AdminStateUp: &iTrue, } firewall, err := firewalls.Create(client, createOpts).Extract() if err != nil { return firewall, err } t.Logf("Waiting for firewall to become active.") if err := WaitForFirewallState(client, firewall.ID, "ACTIVE", 60); err != nil { return firewall, err } t.Logf("Successfully created firewall %s", firewallName) return firewall, nil } // CreateFirewallOnRouter will create a Firewall with a random name and a // specified policy ID attached to a specified Router. An error will be // returned if the firewall could not be created. func CreateFirewallOnRouter(t *testing.T, client *gophercloud.ServiceClient, policyID string, routerID string) (*firewalls.Firewall, error) { firewallName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create firewall %s", firewallName) firewallCreateOpts := firewalls.CreateOpts{ Name: firewallName, PolicyID: policyID, } createOpts := routerinsertion.CreateOptsExt{ CreateOptsBuilder: firewallCreateOpts, RouterIDs: []string{routerID}, } firewall, err := firewalls.Create(client, createOpts).Extract() if err != nil { return firewall, err } t.Logf("Waiting for firewall to become active.") if err := WaitForFirewallState(client, firewall.ID, "ACTIVE", 60); err != nil { return firewall, err } t.Logf("Successfully created firewall %s", firewallName) return firewall, nil } // CreatePolicy will create a Firewall Policy with a random name and given // rule. An error will be returned if the rule could not be created. func CreatePolicy(t *testing.T, client *gophercloud.ServiceClient, ruleID string) (*policies.Policy, error) { policyName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create policy %s", policyName) createOpts := policies.CreateOpts{ Name: policyName, Rules: []string{ ruleID, }, } policy, err := policies.Create(client, createOpts).Extract() if err != nil { return policy, err } t.Logf("Successfully created policy %s", policyName) return policy, nil } // CreateRule will create a Firewall Rule with a random source address and //source port, destination address and port. An error will be returned if // the rule could not be created. func CreateRule(t *testing.T, client *gophercloud.ServiceClient) (*rules.Rule, error) { ruleName := tools.RandomString("TESTACC-", 8) sourceAddress := fmt.Sprintf("192.168.1.%d", tools.RandomInt(1, 100)) sourcePort := strconv.Itoa(tools.RandomInt(1, 100)) destinationAddress := fmt.Sprintf("192.168.2.%d", tools.RandomInt(1, 100)) destinationPort := strconv.Itoa(tools.RandomInt(1, 100)) t.Logf("Attempting to create rule %s with source %s:%s and destination %s:%s", ruleName, sourceAddress, sourcePort, destinationAddress, destinationPort) createOpts := rules.CreateOpts{ Name: ruleName, Protocol: rules.ProtocolTCP, Action: "allow", SourceIPAddress: sourceAddress, SourcePort: sourcePort, DestinationIPAddress: destinationAddress, DestinationPort: destinationPort, } rule, err := rules.Create(client, createOpts).Extract() if err != nil { return rule, err } t.Logf("Rule %s successfully created", ruleName) return rule, nil } // DeleteFirewall will delete a firewall with a specified ID. A fatal error // will occur if the delete was not successful. This works best when used as // a deferred function. func DeleteFirewall(t *testing.T, client *gophercloud.ServiceClient, firewallID string) { t.Logf("Attempting to delete firewall: %s", firewallID) err := firewalls.Delete(client, firewallID).ExtractErr() if err != nil { t.Fatalf("Unable to delete firewall %s: %v", firewallID, err) } t.Logf("Waiting for firewall to delete.") if err := WaitForFirewallState(client, firewallID, "DELETED", 60); err != nil { t.Logf("Unable to delete firewall: %s", firewallID) } t.Logf("Firewall deleted: %s", firewallID) } // DeletePolicy will delete a policy with a specified ID. A fatal error will // occur if the delete was not successful. This works best when used as a // deferred function. func DeletePolicy(t *testing.T, client *gophercloud.ServiceClient, policyID string) { t.Logf("Attempting to delete policy: %s", policyID) err := policies.Delete(client, policyID).ExtractErr() if err != nil { t.Fatalf("Unable to delete policy %s: %v", policyID, err) } t.Logf("Deleted policy: %s", policyID) } // DeleteRule will delete a rule with a specified ID. A fatal error will occur // if the delete was not successful. This works best when used as a deferred // function. func DeleteRule(t *testing.T, client *gophercloud.ServiceClient, ruleID string) { t.Logf("Attempting to delete rule: %s", ruleID) err := rules.Delete(client, ruleID).ExtractErr() if err != nil { t.Fatalf("Unable to delete rule %s: %v", ruleID, err) } t.Logf("Deleted rule: %s", ruleID) } // WaitForFirewallState will wait until a firewall reaches a given state. func WaitForFirewallState(client *gophercloud.ServiceClient, firewallID, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { current, err := firewalls.Get(client, firewallID).Extract() if err != nil { if httpStatus, ok := err.(gophercloud.ErrDefault404); ok { if httpStatus.Actual == 404 { if status == "DELETED" { return true, nil } } } return false, err } if current.Status == status { return true, nil } return false, nil }) } pkg.go000066400000000000000000000000161335005366400366750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/fwaaspackage fwaas policy_test.go000066400000000000000000000032031335005366400404530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/fwaas// +build acceptance networking fwaas package fwaas import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies" ) func TestPolicyList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := policies.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list policies: %v", err) } allPolicies, err := policies.ExtractPolicies(allPages) if err != nil { t.Fatalf("Unable to extract policies: %v", err) } for _, policy := range allPolicies { tools.PrintResource(t, policy) } } func TestPolicyCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } rule, err := CreateRule(t, client) if err != nil { t.Fatalf("Unable to create rule: %v", err) } defer DeleteRule(t, client, rule.ID) tools.PrintResource(t, rule) policy, err := CreatePolicy(t, client, rule.ID) if err != nil { t.Fatalf("Unable to create policy: %v", err) } defer DeletePolicy(t, client, policy.ID) tools.PrintResource(t, policy) updateOpts := policies.UpdateOpts{ Description: "Some policy description", } _, err = policies.Update(client, policy.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update policy: %v", err) } newPolicy, err := policies.Get(client, policy.ID).Extract() if err != nil { t.Fatalf("Unable to get policy: %v", err) } tools.PrintResource(t, newPolicy) } rule_test.go000066400000000000000000000026611335005366400401320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/fwaas// +build acceptance networking fwaas package fwaas import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules" ) func TestRuleList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := rules.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list rules: %v", err) } allRules, err := rules.ExtractRules(allPages) if err != nil { t.Fatalf("Unable to extract rules: %v", err) } for _, rule := range allRules { tools.PrintResource(t, rule) } } func TestRuleCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } rule, err := CreateRule(t, client) if err != nil { t.Fatalf("Unable to create rule: %v", err) } defer DeleteRule(t, client, rule.ID) tools.PrintResource(t, rule) ruleDescription := "Some rule description" updateOpts := rules.UpdateOpts{ Description: &ruleDescription, } _, err = rules.Update(client, rule.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update rule: %v", err) } newRule, err := rules.Get(client, rule.ID).Extract() if err != nil { t.Fatalf("Unable to get rule: %v", err) } tools.PrintResource(t, newRule) } layer3/000077500000000000000000000000001335005366400356665ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensionsfloatingips_test.go000066400000000000000000000074441335005366400416040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/layer3// +build acceptance networking layer3 floatingips package layer3 import ( "os" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" ) func TestLayer3FloatingIPsList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a compute client: %v", err) } listOpts := floatingips.ListOpts{ Status: "DOWN", } allPages, err := floatingips.List(client, listOpts).AllPages() if err != nil { t.Fatalf("Unable to list floating IPs: %v", err) } allFIPs, err := floatingips.ExtractFloatingIPs(allPages) if err != nil { t.Fatalf("Unable to extract floating IPs: %v", err) } for _, fip := range allFIPs { tools.PrintResource(t, fip) } } func TestLayer3FloatingIPsCreateDelete(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a compute client: %v", err) } choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatalf("Unable to get choices: %v", err) } netid, err := networks.IDFromName(client, choices.NetworkName) if err != nil { t.Fatalf("Unable to find network id: %v", err) } subnet, err := networking.CreateSubnet(t, client, netid) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer networking.DeleteSubnet(t, client, subnet.ID) router, err := CreateExternalRouter(t, client) if err != nil { t.Fatalf("Unable to create router: %v", err) } defer DeleteRouter(t, client, router.ID) port, err := networking.CreatePort(t, client, netid, subnet.ID) if err != nil { t.Fatalf("Unable to create port: %v", err) } _, err = CreateRouterInterface(t, client, port.ID, router.ID) if err != nil { t.Fatalf("Unable to create router interface: %v", err) } defer DeleteRouterInterface(t, client, port.ID, router.ID) fip, err := CreateFloatingIP(t, client, choices.ExternalNetworkID, port.ID) if err != nil { t.Fatalf("Unable to create floating IP: %v", err) } defer DeleteFloatingIP(t, client, fip.ID) newFip, err := floatingips.Get(client, fip.ID).Extract() if err != nil { t.Fatalf("Unable to get floating ip: %v", err) } tools.PrintResource(t, newFip) // Disassociate the floating IP updateOpts := floatingips.UpdateOpts{ PortID: nil, } newFip, err = floatingips.Update(client, fip.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to disassociate floating IP: %v", err) } } func TestLayer3FloatingIPsCreateDeleteBySubnetID(t *testing.T) { username := os.Getenv("OS_USERNAME") if username != "admin" { t.Skip("must be admin to run this test") } client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a compute client: %v", err) } choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatalf("Unable to get choices: %v", err) } listOpts := subnets.ListOpts{ NetworkID: choices.ExternalNetworkID, } subnetPages, err := subnets.List(client, listOpts).AllPages() if err != nil { t.Fatalf("Unable to list subnets: %v", err) } allSubnets, err := subnets.ExtractSubnets(subnetPages) if err != nil { t.Fatalf("Unable to extract subnets: %v", err) } createOpts := floatingips.CreateOpts{ FloatingNetworkID: choices.ExternalNetworkID, SubnetID: allSubnets[0].ID, } fip, err := floatingips.Create(client, createOpts).Extract() if err != nil { t.Fatalf("Unable to create floating IP: %v", err) } tools.PrintResource(t, fip) DeleteFloatingIP(t, client, fip.ID) } layer3.go000066400000000000000000000157071335005366400374260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/layer3package layer3 import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" ) // CreateFloatingIP creates a floating IP on a given network and port. An error // will be returned if the creation failed. func CreateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, networkID, portID string) (*floatingips.FloatingIP, error) { t.Logf("Attempting to create floating IP on port: %s", portID) createOpts := &floatingips.CreateOpts{ FloatingNetworkID: networkID, PortID: portID, } floatingIP, err := floatingips.Create(client, createOpts).Extract() if err != nil { return floatingIP, err } t.Logf("Created floating IP.") return floatingIP, err } // CreateExternalRouter creates a router on the external network. This requires // the OS_EXTGW_ID environment variable to be set. An error is returned if the // creation failed. func CreateExternalRouter(t *testing.T, client *gophercloud.ServiceClient) (*routers.Router, error) { var router *routers.Router choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { return router, err } routerName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create external router: %s", routerName) adminStateUp := true enableSNAT := false gatewayInfo := routers.GatewayInfo{ NetworkID: choices.ExternalNetworkID, EnableSNAT: &enableSNAT, } createOpts := routers.CreateOpts{ Name: routerName, AdminStateUp: &adminStateUp, GatewayInfo: &gatewayInfo, } router, err = routers.Create(client, createOpts).Extract() if err != nil { return router, err } if err := WaitForRouterToCreate(client, router.ID, 60); err != nil { return router, err } t.Logf("Created router: %s", routerName) return router, nil } // CreateRouter creates a router on a specified Network ID. An error will be // returned if the creation failed. func CreateRouter(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*routers.Router, error) { routerName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create router: %s", routerName) adminStateUp := true gatewayInfo := routers.GatewayInfo{ NetworkID: networkID, } createOpts := routers.CreateOpts{ Name: routerName, AdminStateUp: &adminStateUp, GatewayInfo: &gatewayInfo, } router, err := routers.Create(client, createOpts).Extract() if err != nil { return router, err } if err := WaitForRouterToCreate(client, router.ID, 60); err != nil { return router, err } t.Logf("Created router: %s", routerName) return router, nil } // CreateRouterInterface will attach a subnet to a router. An error will be // returned if the operation fails. func CreateRouterInterface(t *testing.T, client *gophercloud.ServiceClient, portID, routerID string) (*routers.InterfaceInfo, error) { t.Logf("Attempting to add port %s to router %s", portID, routerID) aiOpts := routers.AddInterfaceOpts{ PortID: portID, } iface, err := routers.AddInterface(client, routerID, aiOpts).Extract() if err != nil { return iface, err } if err := WaitForRouterInterfaceToAttach(client, portID, 60); err != nil { return iface, err } t.Logf("Successfully added port %s to router %s", portID, routerID) return iface, nil } // DeleteRouter deletes a router of a specified ID. A fatal error will occur // if the deletion failed. This works best when used as a deferred function. func DeleteRouter(t *testing.T, client *gophercloud.ServiceClient, routerID string) { t.Logf("Attempting to delete router: %s", routerID) err := routers.Delete(client, routerID).ExtractErr() if err != nil { t.Fatalf("Error deleting router: %v", err) } if err := WaitForRouterToDelete(client, routerID, 60); err != nil { t.Fatalf("Error waiting for router to delete: %v", err) } t.Logf("Deleted router: %s", routerID) } // DeleteRouterInterface will detach a subnet to a router. A fatal error will // occur if the deletion failed. This works best when used as a deferred // function. func DeleteRouterInterface(t *testing.T, client *gophercloud.ServiceClient, portID, routerID string) { t.Logf("Attempting to detach port %s from router %s", portID, routerID) riOpts := routers.RemoveInterfaceOpts{ PortID: portID, } _, err := routers.RemoveInterface(client, routerID, riOpts).Extract() if err != nil { t.Fatalf("Failed to detach port %s from router %s", portID, routerID) } if err := WaitForRouterInterfaceToDetach(client, portID, 60); err != nil { t.Fatalf("Failed to wait for port %s to detach from router %s", portID, routerID) } t.Logf("Successfully detached port %s from router %s", portID, routerID) } // DeleteFloatingIP deletes a floatingIP of a specified ID. A fatal error will // occur if the deletion failed. This works best when used as a deferred // function. func DeleteFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIPID string) { t.Logf("Attempting to delete floating IP: %s", floatingIPID) err := floatingips.Delete(client, floatingIPID).ExtractErr() if err != nil { t.Fatalf("Failed to delete floating IP: %v", err) } t.Logf("Deleted floating IP: %s", floatingIPID) } func WaitForRouterToCreate(client *gophercloud.ServiceClient, routerID string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { r, err := routers.Get(client, routerID).Extract() if err != nil { return false, err } if r.Status == "ACTIVE" { return true, nil } return false, nil }) } func WaitForRouterToDelete(client *gophercloud.ServiceClient, routerID string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { _, err := routers.Get(client, routerID).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { return true, nil } return false, err } return false, nil }) } func WaitForRouterInterfaceToAttach(client *gophercloud.ServiceClient, routerInterfaceID string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { r, err := ports.Get(client, routerInterfaceID).Extract() if err != nil { return false, err } if r.Status == "ACTIVE" { return true, nil } return false, nil }) } func WaitForRouterInterfaceToDetach(client *gophercloud.ServiceClient, routerInterfaceID string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { r, err := ports.Get(client, routerInterfaceID).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { return true, nil } if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok { if errCode.Actual == 409 { return false, nil } } return false, err } if r.Status == "ACTIVE" { return true, nil } return false, nil }) } routers_test.go000066400000000000000000000057311335005366400407650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/layer3// +build acceptance networking layer3 router package layer3 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" ) func TestLayer3RouterList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } listOpts := routers.ListOpts{} allPages, err := routers.List(client, listOpts).AllPages() if err != nil { t.Fatalf("Unable to list routers: %v", err) } allRouters, err := routers.ExtractRouters(allPages) if err != nil { t.Fatalf("Unable to extract routers: %v", err) } for _, router := range allRouters { tools.PrintResource(t, router) } } func TestLayer3ExternalRouterCreateDelete(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } router, err := CreateExternalRouter(t, client) if err != nil { t.Fatalf("Unable to create router: %v", err) } defer DeleteRouter(t, client, router.ID) tools.PrintResource(t, router) newName := tools.RandomString("TESTACC-", 8) updateOpts := routers.UpdateOpts{ Name: newName, } _, err = routers.Update(client, router.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update router: %v", err) } newRouter, err := routers.Get(client, router.ID).Extract() if err != nil { t.Fatalf("Unable to get router: %v", err) } tools.PrintResource(t, newRouter) } func TestLayer3RouterInterface(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a compute client: %v", err) } choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatalf("Unable to get choices: %v", err) } netid, err := networks.IDFromName(client, choices.NetworkName) if err != nil { t.Fatalf("Unable to find network id: %v", err) } subnet, err := networking.CreateSubnet(t, client, netid) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer networking.DeleteSubnet(t, client, subnet.ID) tools.PrintResource(t, subnet) router, err := CreateExternalRouter(t, client) if err != nil { t.Fatalf("Unable to create router: %v", err) } defer DeleteRouter(t, client, router.ID) aiOpts := routers.AddInterfaceOpts{ SubnetID: subnet.ID, } iface, err := routers.AddInterface(client, router.ID, aiOpts).Extract() if err != nil { t.Fatalf("Failed to add interface to router: %v", err) } tools.PrintResource(t, router) tools.PrintResource(t, iface) riOpts := routers.RemoveInterfaceOpts{ SubnetID: subnet.ID, } _, err = routers.RemoveInterface(client, router.ID, riOpts).Extract() if err != nil { t.Fatalf("Failed to remove interface from router: %v", err) } } lbaas/000077500000000000000000000000001335005366400355515ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensionslbaas.go000066400000000000000000000117651335005366400371740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/lbaaspackage lbaas import ( "fmt" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/members" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/vips" ) // CreateMember will create a load balancer member in a specified pool on a // random port. An error will be returned if the member could not be created. func CreateMember(t *testing.T, client *gophercloud.ServiceClient, poolID string) (*members.Member, error) { protocolPort := tools.RandomInt(100, 1000) address := tools.RandomInt(2, 200) t.Logf("Attempting to create member in port %d", protocolPort) createOpts := members.CreateOpts{ PoolID: poolID, ProtocolPort: protocolPort, Address: fmt.Sprintf("192.168.1.%d", address), } member, err := members.Create(client, createOpts).Extract() if err != nil { return member, err } t.Logf("Successfully created member %s", member.ID) return member, nil } // CreateMonitor will create a monitor with a random name for a specific pool. // An error will be returned if the monitor could not be created. func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient) (*monitors.Monitor, error) { t.Logf("Attempting to create monitor.") createOpts := monitors.CreateOpts{ Type: monitors.TypePING, Delay: 90, Timeout: 60, MaxRetries: 10, AdminStateUp: gophercloud.Enabled, } monitor, err := monitors.Create(client, createOpts).Extract() if err != nil { return monitor, err } t.Logf("Successfully created monitor %s", monitor.ID) return monitor, nil } // CreatePool will create a pool with a random name. An error will be returned // if the pool could not be deleted. func CreatePool(t *testing.T, client *gophercloud.ServiceClient, subnetID string) (*pools.Pool, error) { poolName := tools.RandomString("TESTACCT-", 8) t.Logf("Attempting to create pool %s", poolName) createOpts := pools.CreateOpts{ Name: poolName, SubnetID: subnetID, Protocol: pools.ProtocolTCP, LBMethod: pools.LBMethodRoundRobin, } pool, err := pools.Create(client, createOpts).Extract() if err != nil { return pool, err } t.Logf("Successfully created pool %s", poolName) return pool, nil } // CreateVIP will create a vip with a random name and a random port in a // specified subnet and pool. An error will be returned if the vip could // not be created. func CreateVIP(t *testing.T, client *gophercloud.ServiceClient, subnetID, poolID string) (*vips.VirtualIP, error) { vipName := tools.RandomString("TESTACCT-", 8) vipPort := tools.RandomInt(100, 10000) t.Logf("Attempting to create VIP %s", vipName) createOpts := vips.CreateOpts{ Name: vipName, SubnetID: subnetID, PoolID: poolID, Protocol: "TCP", ProtocolPort: vipPort, } vip, err := vips.Create(client, createOpts).Extract() if err != nil { return vip, err } t.Logf("Successfully created vip %s", vipName) return vip, nil } // DeleteMember will delete a specified member. A fatal error will occur if // the member could not be deleted. This works best when used as a deferred // function. func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, memberID string) { t.Logf("Attempting to delete member %s", memberID) if err := members.Delete(client, memberID).ExtractErr(); err != nil { t.Fatalf("Unable to delete member: %v", err) } t.Logf("Successfully deleted member %s", memberID) } // DeleteMonitor will delete a specified monitor. A fatal error will occur if // the monitor could not be deleted. This works best when used as a deferred // function. func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, monitorID string) { t.Logf("Attempting to delete monitor %s", monitorID) if err := monitors.Delete(client, monitorID).ExtractErr(); err != nil { t.Fatalf("Unable to delete monitor: %v", err) } t.Logf("Successfully deleted monitor %s", monitorID) } // DeletePool will delete a specified pool. A fatal error will occur if the // pool could not be deleted. This works best when used as a deferred function. func DeletePool(t *testing.T, client *gophercloud.ServiceClient, poolID string) { t.Logf("Attempting to delete pool %s", poolID) if err := pools.Delete(client, poolID).ExtractErr(); err != nil { t.Fatalf("Unable to delete pool: %v", err) } t.Logf("Successfully deleted pool %s", poolID) } // DeleteVIP will delete a specified vip. A fatal error will occur if the vip // could not be deleted. This works best when used as a deferred function. func DeleteVIP(t *testing.T, client *gophercloud.ServiceClient, vipID string) { t.Logf("Attempting to delete vip %s", vipID) if err := vips.Delete(client, vipID).ExtractErr(); err != nil { t.Fatalf("Unable to delete vip: %v", err) } t.Logf("Successfully deleted vip %s", vipID) } members_test.go000066400000000000000000000041201335005366400405660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/lbaas// +build acceptance networking lbaas member package lbaas import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/members" ) func TestMembersList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := members.List(client, members.ListOpts{}).AllPages() if err != nil { t.Fatalf("Unable to list members: %v", err) } allMembers, err := members.ExtractMembers(allPages) if err != nil { t.Fatalf("Unable to extract members: %v", err) } for _, member := range allMembers { tools.PrintResource(t, member) } } func TestMembersCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } network, err := networking.CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer networking.DeleteNetwork(t, client, network.ID) subnet, err := networking.CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer networking.DeleteSubnet(t, client, subnet.ID) pool, err := CreatePool(t, client, subnet.ID) if err != nil { t.Fatalf("Unable to create pool: %v", err) } defer DeletePool(t, client, pool.ID) member, err := CreateMember(t, client, pool.ID) if err != nil { t.Fatalf("Unable to create member: %v", err) } defer DeleteMember(t, client, member.ID) tools.PrintResource(t, member) updateOpts := members.UpdateOpts{ AdminStateUp: gophercloud.Enabled, } _, err = members.Update(client, member.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update member: %v") } newMember, err := members.Get(client, member.ID).Extract() if err != nil { t.Fatalf("Unable to get member: %v") } tools.PrintResource(t, newMember) } monitors_test.go000066400000000000000000000027241335005366400410160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/lbaas// +build acceptance networking lbaas monitors package lbaas import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors" ) func TestMonitorsList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := monitors.List(client, monitors.ListOpts{}).AllPages() if err != nil { t.Fatalf("Unable to list monitors: %v", err) } allMonitors, err := monitors.ExtractMonitors(allPages) if err != nil { t.Fatalf("Unable to extract monitors: %v", err) } for _, monitor := range allMonitors { tools.PrintResource(t, monitor) } } func TestMonitorsCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } monitor, err := CreateMonitor(t, client) if err != nil { t.Fatalf("Unable to create monitor: %v", err) } defer DeleteMonitor(t, client, monitor.ID) tools.PrintResource(t, monitor) updateOpts := monitors.UpdateOpts{ Delay: 999, } _, err = monitors.Update(client, monitor.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update monitor: %v") } newMonitor, err := monitors.Get(client, monitor.ID).Extract() if err != nil { t.Fatalf("Unable to get monitor: %v") } tools.PrintResource(t, newMonitor) } pkg.go000066400000000000000000000000161335005366400366560ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/lbaaspackage lbaas pools_test.go000066400000000000000000000061031335005366400402730ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/lbaas// +build acceptance networking lbaas pool package lbaas import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools" ) func TestPoolsList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := pools.List(client, pools.ListOpts{}).AllPages() if err != nil { t.Fatalf("Unable to list pools: %v", err) } allPools, err := pools.ExtractPools(allPages) if err != nil { t.Fatalf("Unable to extract pools: %v", err) } for _, pool := range allPools { tools.PrintResource(t, pool) } } func TestPoolsCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } network, err := networking.CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer networking.DeleteNetwork(t, client, network.ID) subnet, err := networking.CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer networking.DeleteSubnet(t, client, subnet.ID) pool, err := CreatePool(t, client, subnet.ID) if err != nil { t.Fatalf("Unable to create pool: %v", err) } defer DeletePool(t, client, pool.ID) tools.PrintResource(t, pool) updateOpts := pools.UpdateOpts{ LBMethod: pools.LBMethodLeastConnections, } _, err = pools.Update(client, pool.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update pool: %v") } newPool, err := pools.Get(client, pool.ID).Extract() if err != nil { t.Fatalf("Unable to get pool: %v") } tools.PrintResource(t, newPool) } func TestPoolsMonitors(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } network, err := networking.CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer networking.DeleteNetwork(t, client, network.ID) subnet, err := networking.CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer networking.DeleteSubnet(t, client, subnet.ID) pool, err := CreatePool(t, client, subnet.ID) if err != nil { t.Fatalf("Unable to create pool: %v", err) } defer DeletePool(t, client, pool.ID) monitor, err := CreateMonitor(t, client) if err != nil { t.Fatalf("Unable to create monitor: %v", err) } defer DeleteMonitor(t, client, monitor.ID) t.Logf("Associating monitor %s with pool %s", monitor.ID, pool.ID) if res := pools.AssociateMonitor(client, pool.ID, monitor.ID); res.Err != nil { t.Fatalf("Unable to associate monitor to pool") } t.Logf("Disassociating monitor %s with pool %s", monitor.ID, pool.ID) if res := pools.DisassociateMonitor(client, pool.ID, monitor.ID); res.Err != nil { t.Fatalf("Unable to disassociate monitor from pool") } } vips_test.go000066400000000000000000000037441335005366400401300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/lbaas// +build acceptance networking lbaas vip package lbaas import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/vips" ) func TestVIPsList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := vips.List(client, vips.ListOpts{}).AllPages() if err != nil { t.Fatalf("Unable to list vips: %v", err) } allVIPs, err := vips.ExtractVIPs(allPages) if err != nil { t.Fatalf("Unable to extract vips: %v", err) } for _, vip := range allVIPs { tools.PrintResource(t, vip) } } func TestVIPsCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } network, err := networking.CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer networking.DeleteNetwork(t, client, network.ID) subnet, err := networking.CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer networking.DeleteSubnet(t, client, subnet.ID) pool, err := CreatePool(t, client, subnet.ID) if err != nil { t.Fatalf("Unable to create pool: %v", err) } defer DeletePool(t, client, pool.ID) vip, err := CreateVIP(t, client, subnet.ID, pool.ID) if err != nil { t.Fatalf("Unable to create vip: %v", err) } defer DeleteVIP(t, client, vip.ID) tools.PrintResource(t, vip) connLimit := 100 updateOpts := vips.UpdateOpts{ ConnLimit: &connLimit, } _, err = vips.Update(client, vip.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update vip: %v") } newVIP, err := vips.Get(client, vip.ID).Extract() if err != nil { t.Fatalf("Unable to get vip: %v") } tools.PrintResource(t, newVIP) } lbaas_v2/000077500000000000000000000000001335005366400361605ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensionslbaas_v2.go000066400000000000000000000232321335005366400402020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/lbaas_v2package lbaas_v2 import ( "fmt" "strings" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" ) const loadbalancerActiveTimeoutSeconds = 300 const loadbalancerDeleteTimeoutSeconds = 300 // CreateListener will create a listener for a given load balancer on a random // port with a random name. An error will be returned if the listener could not // be created. func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*listeners.Listener, error) { listenerName := tools.RandomString("TESTACCT-", 8) listenerPort := tools.RandomInt(1, 100) t.Logf("Attempting to create listener %s on port %d", listenerName, listenerPort) createOpts := listeners.CreateOpts{ Name: listenerName, LoadbalancerID: lb.ID, Protocol: "TCP", ProtocolPort: listenerPort, } listener, err := listeners.Create(client, createOpts).Extract() if err != nil { return listener, err } t.Logf("Successfully created listener %s", listenerName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active") } return listener, nil } // CreateLoadBalancer will create a load balancer with a random name on a given // subnet. An error will be returned if the loadbalancer could not be created. func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetID string) (*loadbalancers.LoadBalancer, error) { lbName := tools.RandomString("TESTACCT-", 8) t.Logf("Attempting to create loadbalancer %s on subnet %s", lbName, subnetID) createOpts := loadbalancers.CreateOpts{ Name: lbName, VipSubnetID: subnetID, AdminStateUp: gophercloud.Enabled, } lb, err := loadbalancers.Create(client, createOpts).Extract() if err != nil { return lb, err } t.Logf("Successfully created loadbalancer %s on subnet %s", lbName, subnetID) t.Logf("Waiting for loadbalancer %s to become active", lbName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { return lb, err } t.Logf("LoadBalancer %s is active", lbName) return lb, nil } // CreateMember will create a member with a random name, port, address, and // weight. An error will be returned if the member could not be created. func CreateMember(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer, pool *pools.Pool, subnetID, subnetCIDR string) (*pools.Member, error) { memberName := tools.RandomString("TESTACCT-", 8) memberPort := tools.RandomInt(100, 1000) memberWeight := tools.RandomInt(1, 10) cidrParts := strings.Split(subnetCIDR, "/") subnetParts := strings.Split(cidrParts[0], ".") memberAddress := fmt.Sprintf("%s.%s.%s.%d", subnetParts[0], subnetParts[1], subnetParts[2], tools.RandomInt(10, 100)) t.Logf("Attempting to create member %s", memberName) createOpts := pools.CreateMemberOpts{ Name: memberName, ProtocolPort: memberPort, Weight: memberWeight, Address: memberAddress, SubnetID: subnetID, } t.Logf("Member create opts: %#v", createOpts) member, err := pools.CreateMember(client, pool.ID, createOpts).Extract() if err != nil { return member, err } t.Logf("Successfully created member %s", memberName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { return member, fmt.Errorf("Timed out waiting for loadbalancer to become active") } return member, nil } // CreateMonitor will create a monitor with a random name for a specific pool. // An error will be returned if the monitor could not be created. func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer, pool *pools.Pool) (*monitors.Monitor, error) { monitorName := tools.RandomString("TESTACCT-", 8) t.Logf("Attempting to create monitor %s", monitorName) createOpts := monitors.CreateOpts{ PoolID: pool.ID, Name: monitorName, Delay: 10, Timeout: 5, MaxRetries: 5, Type: "PING", } monitor, err := monitors.Create(client, createOpts).Extract() if err != nil { return monitor, err } t.Logf("Successfully created monitor: %s", monitorName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { return monitor, fmt.Errorf("Timed out waiting for loadbalancer to become active") } return monitor, nil } // CreatePool will create a pool with a random name with a specified listener // and loadbalancer. An error will be returned if the pool could not be // created. func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*pools.Pool, error) { poolName := tools.RandomString("TESTACCT-", 8) t.Logf("Attempting to create pool %s", poolName) createOpts := pools.CreateOpts{ Name: poolName, Protocol: pools.ProtocolTCP, LoadbalancerID: lb.ID, LBMethod: pools.LBMethodLeastConnections, } pool, err := pools.Create(client, createOpts).Extract() if err != nil { return pool, err } t.Logf("Successfully created pool %s", poolName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active") } return pool, nil } // DeleteListener will delete a specified listener. A fatal error will occur if // the listener could not be deleted. This works best when used as a deferred // function. func DeleteListener(t *testing.T, client *gophercloud.ServiceClient, lbID, listenerID string) { t.Logf("Attempting to delete listener %s", listenerID) if err := listeners.Delete(client, listenerID).ExtractErr(); err != nil { t.Fatalf("Unable to delete listener: %v", err) } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } t.Logf("Successfully deleted listener %s", listenerID) } // DeleteMember will delete a specified member. A fatal error will occur if the // member could not be deleted. This works best when used as a deferred // function. func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID, memberID string) { t.Logf("Attempting to delete member %s", memberID) if err := pools.DeleteMember(client, poolID, memberID).ExtractErr(); err != nil { t.Fatalf("Unable to delete member: %s", memberID) } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } t.Logf("Successfully deleted member %s", memberID) } // DeleteLoadBalancer will delete a specified loadbalancer. A fatal error will // occur if the loadbalancer could not be deleted. This works best when used // as a deferred function. func DeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID string) { t.Logf("Attempting to delete loadbalancer %s", lbID) if err := loadbalancers.Delete(client, lbID).ExtractErr(); err != nil { t.Fatalf("Unable to delete loadbalancer: %v", err) } t.Logf("Waiting for loadbalancer %s to delete", lbID) if err := WaitForLoadBalancerState(client, lbID, "DELETED", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Loadbalancer did not delete in time.") } t.Logf("Successfully deleted loadbalancer %s", lbID) } // DeleteMonitor will delete a specified monitor. A fatal error will occur if // the monitor could not be deleted. This works best when used as a deferred // function. func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID, monitorID string) { t.Logf("Attempting to delete monitor %s", monitorID) if err := monitors.Delete(client, monitorID).ExtractErr(); err != nil { t.Fatalf("Unable to delete monitor: %v", err) } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } t.Logf("Successfully deleted monitor %s", monitorID) } // DeletePool will delete a specified pool. A fatal error will occur if the // pool could not be deleted. This works best when used as a deferred function. func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID string) { t.Logf("Attempting to delete pool %s", poolID) if err := pools.Delete(client, poolID).ExtractErr(); err != nil { t.Fatalf("Unable to delete pool: %v", err) } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } t.Logf("Successfully deleted pool %s", poolID) } // WaitForLoadBalancerState will wait until a loadbalancer reaches a given state. func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { current, err := loadbalancers.Get(client, lbID).Extract() if err != nil { if httpStatus, ok := err.(gophercloud.ErrDefault404); ok { if httpStatus.Actual == 404 { if status == "DELETED" { return true, nil } } } return false, err } if current.ProvisioningStatus == status { return true, nil } return false, nil }) } listeners_test.go000066400000000000000000000014361335005366400415620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/lbaas_v2// +build acceptance networking lbaas_v2 listeners package lbaas_v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners" ) func TestListenersList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := listeners.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list listeners: %v", err) } allListeners, err := listeners.ExtractListeners(allPages) if err != nil { t.Fatalf("Unable to extract listeners: %v", err) } for _, listener := range allListeners { tools.PrintResource(t, listener) } } loadbalancers_test.go000066400000000000000000000120501335005366400423360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/lbaas_v2// +build acceptance networking lbaas_v2 loadbalancers package lbaas_v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" ) func TestLoadbalancersList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := loadbalancers.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list loadbalancers: %v", err) } allLoadbalancers, err := loadbalancers.ExtractLoadBalancers(allPages) if err != nil { t.Fatalf("Unable to extract loadbalancers: %v", err) } for _, lb := range allLoadbalancers { tools.PrintResource(t, lb) } } func TestLoadbalancersCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } network, err := networking.CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer networking.DeleteNetwork(t, client, network.ID) subnet, err := networking.CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer networking.DeleteSubnet(t, client, subnet.ID) lb, err := CreateLoadBalancer(t, client, subnet.ID) if err != nil { t.Fatalf("Unable to create loadbalancer: %v", err) } defer DeleteLoadBalancer(t, client, lb.ID) newLB, err := loadbalancers.Get(client, lb.ID).Extract() if err != nil { t.Fatalf("Unable to get loadbalancer: %v", err) } tools.PrintResource(t, newLB) // Because of the time it takes to create a loadbalancer, // this test will include some other resources. // Listener listener, err := CreateListener(t, client, lb) if err != nil { t.Fatalf("Unable to create listener: %v", err) } defer DeleteListener(t, client, lb.ID, listener.ID) updateListenerOpts := listeners.UpdateOpts{ Description: "Some listener description", } _, err = listeners.Update(client, listener.ID, updateListenerOpts).Extract() if err != nil { t.Fatalf("Unable to update listener") } if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newListener, err := listeners.Get(client, listener.ID).Extract() if err != nil { t.Fatalf("Unable to get listener") } tools.PrintResource(t, newListener) // Pool pool, err := CreatePool(t, client, lb) if err != nil { t.Fatalf("Unable to create pool: %v", err) } defer DeletePool(t, client, lb.ID, pool.ID) updatePoolOpts := pools.UpdateOpts{ Description: "Some pool description", } _, err = pools.Update(client, pool.ID, updatePoolOpts).Extract() if err != nil { t.Fatalf("Unable to update pool") } if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newPool, err := pools.Get(client, pool.ID).Extract() if err != nil { t.Fatalf("Unable to get pool") } tools.PrintResource(t, newPool) // Member member, err := CreateMember(t, client, lb, newPool, subnet.ID, subnet.CIDR) if err != nil { t.Fatalf("Unable to create member: %v", err) } defer DeleteMember(t, client, lb.ID, pool.ID, member.ID) newWeight := tools.RandomInt(11, 100) updateMemberOpts := pools.UpdateMemberOpts{ Weight: newWeight, } _, err = pools.UpdateMember(client, pool.ID, member.ID, updateMemberOpts).Extract() if err != nil { t.Fatalf("Unable to update pool") } if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newMember, err := pools.GetMember(client, pool.ID, member.ID).Extract() if err != nil { t.Fatalf("Unable to get member") } tools.PrintResource(t, newMember) // Monitor monitor, err := CreateMonitor(t, client, lb, newPool) if err != nil { t.Fatalf("Unable to create monitor: %v", err) } defer DeleteMonitor(t, client, lb.ID, monitor.ID) newDelay := tools.RandomInt(20, 30) updateMonitorOpts := monitors.UpdateOpts{ Delay: newDelay, } _, err = monitors.Update(client, monitor.ID, updateMonitorOpts).Extract() if err != nil { t.Fatalf("Unable to update monitor") } if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newMonitor, err := monitors.Get(client, monitor.ID).Extract() if err != nil { t.Fatalf("Unable to get monitor") } tools.PrintResource(t, newMonitor) } monitors_test.go000066400000000000000000000014221335005366400414170ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/lbaas_v2// +build acceptance networking lbaas_v2 monitors package lbaas_v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" ) func TestMonitorsList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := monitors.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list monitors: %v", err) } allMonitors, err := monitors.ExtractMonitors(allPages) if err != nil { t.Fatalf("Unable to extract monitors: %v", err) } for _, monitor := range allMonitors { tools.PrintResource(t, monitor) } } pkg.go000066400000000000000000000000211335005366400372610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/lbaas_v2package lbaas_v2 pools_test.go000066400000000000000000000013561335005366400407070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/lbaas_v2// +build acceptance networking lbaas_v2 pools package lbaas_v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" ) func TestPoolsList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := pools.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list pools: %v", err) } allPools, err := pools.ExtractPools(allPages) if err != nil { t.Fatalf("Unable to extract pools: %v", err) } for _, pool := range allPools { tools.PrintResource(t, pool) } } networkipavailabilities/000077500000000000000000000000001335005366400414145ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensionsnetworkipavailabilities_test.go000066400000000000000000000016371335005366400477360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/networkipavailabilities// +build acceptance networking networkipavailabilities package networkipavailabilities import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/networkipavailabilities" ) func TestNetworkIPAvailabilityList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := networkipavailabilities.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list network IP availabilities: %v", err) } allAvailabilities, err := networkipavailabilities.ExtractNetworkIPAvailabilities(allPages) if err != nil { t.Fatalf("Unable to extract network IP availabilities: %v", err) } for _, availability := range allAvailabilities { tools.PrintResource(t, availability) } } pkg.go000066400000000000000000000000401335005366400425160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/networkipavailabilitiespackage networkipavailabilities pkg.go000066400000000000000000000000231335005366400355720ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensionspackage extensions portsbinding/000077500000000000000000000000001335005366400371715ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensionspkg.go000066400000000000000000000000251335005366400402760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/portsbindingpackage portsbinding portsbinding.go000066400000000000000000000024241335005366400422240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/portsbindingpackage portsbinding import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" ) // PortWithBindingExt represents a port with the binding fields type PortWithBindingExt struct { ports.Port portsbinding.PortsBindingExt } // CreatePortsbinding will create a port on the specified subnet. An error will be // returned if the port could not be created. func CreatePortsbinding(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID, hostID string) (PortWithBindingExt, error) { portName := tools.RandomString("TESTACC-", 8) iFalse := false t.Logf("Attempting to create port: %s", portName) portCreateOpts := ports.CreateOpts{ NetworkID: networkID, Name: portName, AdminStateUp: &iFalse, FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, } createOpts := portsbinding.CreateOptsExt{ CreateOptsBuilder: portCreateOpts, HostID: hostID, } var s PortWithBindingExt err := ports.Create(client, createOpts).ExtractInto(&s) if err != nil { return s, err } t.Logf("Successfully created port: %s", portName) return s, nil } portsbinding_test.go000066400000000000000000000027061335005366400432660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/portsbinding// +build acceptance networking package portsbinding import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" ) func TestPortsbindingCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create Network network, err := networking.CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer networking.DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := networking.CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer networking.DeleteSubnet(t, client, subnet.ID) // Define a host hostID := "localhost" // Create port port, err := CreatePortsbinding(t, client, network.ID, subnet.ID, hostID) if err != nil { t.Fatalf("Unable to create port: %v", err) } defer networking.DeletePort(t, client, port.ID) tools.PrintResource(t, port) // Update port newPortName := tools.RandomString("TESTACC-", 8) updateOpts := ports.UpdateOpts{ Name: newPortName, } newPort, err := ports.Update(client, port.ID, updateOpts).Extract() if err != nil { t.Fatalf("Could not update port: %v", err) } tools.PrintResource(t, newPort) } provider_test.go000066400000000000000000000016231335005366400377110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions// +build acceptance networking provider package extensions import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" ) func TestNetworksProviderCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create a network network, err := networking.CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer networking.DeleteNetwork(t, client, network.ID) getResult := networks.Get(client, network.ID) newNetwork, err := getResult.Extract() if err != nil { t.Fatalf("Unable to extract network: %v", err) } tools.PrintResource(t, newNetwork) } qos/000077500000000000000000000000001335005366400352715ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensionsruletypes/000077500000000000000000000000001335005366400373255ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/qosruletypes_test.go000066400000000000000000000013231335005366400427460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/qos/ruletypespackage ruletypes import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/ruletypes" ) func TestListRuleTypes(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) return } page, err := ruletypes.ListRuleTypes(client).AllPages() if err != nil { t.Fatalf("Failed to list rule types pages: %v", err) return } ruleTypes, err := ruletypes.ExtractRuleTypes(page) if err != nil { t.Fatalf("Failed to list rule types: %v", err) return } tools.PrintResource(t, ruleTypes) } rbacpolicies/000077500000000000000000000000001335005366400371265ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensionspkg.go000066400000000000000000000000251335005366400402330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/rbacpoliciespackage rbacpolicies rbacpolicies.go000066400000000000000000000025221335005366400421150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/rbacpoliciespackage rbacpolicies import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/rbacpolicies" ) // CreateRBACPolicy will create a rbac-policy. An error will be returned if the // rbac-policy could not be created. func CreateRBACPolicy(t *testing.T, client *gophercloud.ServiceClient, tenantID, networkID string) (*rbacpolicies.RBACPolicy, error) { createOpts := rbacpolicies.CreateOpts{ Action: rbacpolicies.ActionAccessShared, ObjectType: "network", TargetTenant: tenantID, ObjectID: networkID, } t.Logf("Trying to create rbac_policy") rbacPolicy, err := rbacpolicies.Create(client, createOpts).Extract() if err != nil { return rbacPolicy, err } t.Logf("Successfully created rbac_policy") return rbacPolicy, nil } // DeleteRBACPolicy will delete a rbac-policy with a specified ID. A fatal error will // occur if the delete was not successful. This works best when used as a // deferred function. func DeleteRBACPolicy(t *testing.T, client *gophercloud.ServiceClient, rbacPolicyID string) { t.Logf("Trying to delete rbac_policy: %s", rbacPolicyID) err := rbacpolicies.Delete(client, rbacPolicyID).ExtractErr() if err != nil { t.Fatalf("Unable to delete rbac_policy %s: %v", rbacPolicyID, err) } t.Logf("Deleted rbac_policy: %s", rbacPolicyID) } rbacpolicies_test.go000066400000000000000000000056531335005366400431640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/rbacpolicies// +build acceptance package rbacpolicies import ( "os" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" projects "github.com/gophercloud/gophercloud/acceptance/openstack/identity/v3" networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/rbacpolicies" ) func TestRBACPolicyCRUD(t *testing.T) { username := os.Getenv("OS_USERNAME") if username != "admin" { t.Skip("must be admin to run this test") } client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create a network network, err := networking.CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer networking.DeleteNetwork(t, client, network.ID) tools.PrintResource(t, network) identityClient, err := clients.NewIdentityV3Client() if err != nil { t.Fatalf("Unable to obtain an identity client: %v") } // Create a project/tenant project, err := projects.CreateProject(t, identityClient, nil) if err != nil { t.Fatalf("Unable to create project: %v", err) } defer projects.DeleteProject(t, identityClient, project.ID) tools.PrintResource(t, project) // Create a rbac-policy rbacPolicy, err := CreateRBACPolicy(t, client, project.ID, network.ID) if err != nil { t.Fatalf("Unable to create rbac-policy: %v", err) } defer DeleteRBACPolicy(t, client, rbacPolicy.ID) tools.PrintResource(t, rbacPolicy) // Create another project/tenant for rbac-update project2, err := projects.CreateProject(t, identityClient, nil) if err != nil { t.Fatalf("Unable to create project2: %v", err) } defer projects.DeleteProject(t, identityClient, project2.ID) tools.PrintResource(t, project2) // Update a rbac-policy updateOpts := rbacpolicies.UpdateOpts{ TargetTenant: project2.ID, } _, err = rbacpolicies.Update(client, rbacPolicy.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update rbac-policy: %v", err) } // Get the rbac-policy by ID t.Logf("Get rbac_policy by ID") newrbacPolicy, err := rbacpolicies.Get(client, rbacPolicy.ID).Extract() if err != nil { t.Fatalf("Unable to retrieve rbac policy: %v", err) } tools.PrintResource(t, newrbacPolicy) } func TestRBACPolicyList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } type rbacPolicy struct { rbacpolicies.RBACPolicy } var allRBACPolicies []rbacPolicy allPages, err := rbacpolicies.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list rbac policies: %v", err) } err = rbacpolicies.ExtractRBACPolicesInto(allPages, &allRBACPolicies) if err != nil { t.Fatalf("Unable to extract rbac policies: %v", err) } for _, rbacpolicy := range allRBACPolicies { tools.PrintResource(t, rbacpolicy) } } security_test.go000066400000000000000000000054301335005366400377260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions// +build acceptance networking security package extensions import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups" ) func TestSecurityGroupsList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } listOpts := groups.ListOpts{} allPages, err := groups.List(client, listOpts).AllPages() if err != nil { t.Fatalf("Unable to list groups: %v", err) } allGroups, err := groups.ExtractGroups(allPages) if err != nil { t.Fatalf("Unable to extract groups: %v", err) } for _, group := range allGroups { tools.PrintResource(t, group) } } func TestSecurityGroupsCreateUpdateDelete(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } group, err := CreateSecurityGroup(t, client) if err != nil { t.Fatalf("Unable to create security group: %v", err) } defer DeleteSecurityGroup(t, client, group.ID) rule, err := CreateSecurityGroupRule(t, client, group.ID) if err != nil { t.Fatalf("Unable to create security group rule: %v", err) } defer DeleteSecurityGroupRule(t, client, rule.ID) tools.PrintResource(t, group) updateOpts := groups.UpdateOpts{ Description: "A security group", } newGroup, err := groups.Update(client, group.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update security group: %v", err) } tools.PrintResource(t, newGroup) } func TestSecurityGroupsPort(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } network, err := networking.CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer networking.DeleteNetwork(t, client, network.ID) subnet, err := networking.CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer networking.DeleteSubnet(t, client, subnet.ID) group, err := CreateSecurityGroup(t, client) if err != nil { t.Fatalf("Unable to create security group: %v", err) } defer DeleteSecurityGroup(t, client, group.ID) rule, err := CreateSecurityGroupRule(t, client, group.ID) if err != nil { t.Fatalf("Unable to create security group rule: %v", err) } defer DeleteSecurityGroupRule(t, client, rule.ID) port, err := CreatePortWithSecurityGroup(t, client, network.ID, subnet.ID, group.ID) if err != nil { t.Fatalf("Unable to create port: %v", err) } defer networking.DeletePort(t, client, port.ID) tools.PrintResource(t, port) } subnetpools/000077500000000000000000000000001335005366400370445ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensionssubnetpools.go000066400000000000000000000025451335005366400417560ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/subnetpoolspackage v2 import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools" ) // CreateSubnetPool will create a subnetpool. An error will be returned if the // subnetpool could not be created. func CreateSubnetPool(t *testing.T, client *gophercloud.ServiceClient) (*subnetpools.SubnetPool, error) { subnetPoolName := tools.RandomString("TESTACC-", 8) subnetPoolPrefixes := []string{ "10.0.0.0/8", } createOpts := subnetpools.CreateOpts{ Name: subnetPoolName, Prefixes: subnetPoolPrefixes, } t.Logf("Attempting to create a subnetpool: %s", subnetPoolName) subnetPool, err := subnetpools.Create(client, createOpts).Extract() if err != nil { return nil, err } t.Logf("Successfully created the subnetpool.") return subnetPool, nil } // DeleteSubnetPool will delete a subnetpool with a specified ID. // A fatal error will occur if the delete was not successful. func DeleteSubnetPool(t *testing.T, client *gophercloud.ServiceClient, subnetPoolID string) { t.Logf("Attempting to delete the subnetpool: %s", subnetPoolID) err := subnetpools.Delete(client, subnetPoolID).ExtractErr() if err != nil { t.Fatalf("Unable to delete subnetpool %s: %v", subnetPoolID, err) } t.Logf("Deleted subnetpool: %s", subnetPoolID) } subnetpools_test.go000066400000000000000000000031431335005366400430100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/subnetpools// +build acceptance networking subnetpools package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools" ) func TestSubnetPoolsCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create a subnetpool subnetPool, err := CreateSubnetPool(t, client) if err != nil { t.Fatalf("Unable to create a subnetpool: %v", err) } defer DeleteSubnetPool(t, client, subnetPool.ID) tools.PrintResource(t, subnetPool) newName := tools.RandomString("TESTACC-", 8) updateOpts := &subnetpools.UpdateOpts{ Name: newName, } _, err = subnetpools.Update(client, subnetPool.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update the subnetpool: %v", err) } newSubnetPool, err := subnetpools.Get(client, subnetPool.ID).Extract() if err != nil { t.Fatalf("Unable to get subnetpool: %v", err) } tools.PrintResource(t, newSubnetPool) } func TestSubnetPoolsList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := subnetpools.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list subnetpools: %v", err) } allSubnetPools, err := subnetpools.ExtractSubnetPools(allPages) if err != nil { t.Fatalf("Unable to extract subnetpools: %v", err) } for _, subnetpool := range allSubnetPools { tools.PrintResource(t, subnetpool) } } vpnaas/000077500000000000000000000000001335005366400357575ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensionsgroup_test.go000066400000000000000000000032701335005366400405030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/vpnaas// +build acceptance networking vpnaas package vpnaas import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/endpointgroups" ) func TestGroupList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := endpointgroups.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list endpoint groups: %v", err) } allGroups, err := endpointgroups.ExtractEndpointGroups(allPages) if err != nil { t.Fatalf("Unable to extract endpoint groups: %v", err) } for _, group := range allGroups { tools.PrintResource(t, group) } } func TestGroupCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } group, err := CreateEndpointGroup(t, client) if err != nil { t.Fatalf("Unable to create Endpoint group: %v", err) } defer DeleteEndpointGroup(t, client, group.ID) tools.PrintResource(t, group) newGroup, err := endpointgroups.Get(client, group.ID).Extract() if err != nil { t.Fatalf("Unable to retrieve Endpoint group: %v", err) } tools.PrintResource(t, newGroup) updatedName := "updatedname" updatedDescription := "updated description" updateOpts := endpointgroups.UpdateOpts{ Name: &updatedName, Description: &updatedDescription, } updatedGroup, err := endpointgroups.Update(client, group.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update endpoint group: %v", err) } tools.PrintResource(t, updatedGroup) } ikepolicy_test.go000066400000000000000000000033261335005366400413410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/vpnaas// +build acceptance networking vpnaas package vpnaas import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ikepolicies" ) func TestIKEPolicyList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := ikepolicies.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list IKE policies: %v", err) } allPolicies, err := ikepolicies.ExtractPolicies(allPages) if err != nil { t.Fatalf("Unable to extract IKE policies: %v", err) } for _, policy := range allPolicies { tools.PrintResource(t, policy) } } func TestIKEPolicyCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } policy, err := CreateIKEPolicy(t, client) if err != nil { t.Fatalf("Unable to create IKE policy: %v", err) } defer DeleteIKEPolicy(t, client, policy.ID) tools.PrintResource(t, policy) newPolicy, err := ikepolicies.Get(client, policy.ID).Extract() if err != nil { t.Fatalf("Unable to get IKE policy: %v", err) } tools.PrintResource(t, newPolicy) updatedName := "updatedname" updatedDescription := "updated policy" updateOpts := ikepolicies.UpdateOpts{ Name: &updatedName, Description: &updatedDescription, Lifetime: &ikepolicies.LifetimeUpdateOpts{ Value: 7000, }, } updatedPolicy, err := ikepolicies.Update(client, policy.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update IKE policy: %v", err) } tools.PrintResource(t, updatedPolicy) } ipsecpolicy_test.go000066400000000000000000000031551335005366400416740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/vpnaas// +build acceptance networking vpnaas package vpnaas import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ipsecpolicies" ) func TestIPSecPolicyList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := ipsecpolicies.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list IPSec policies: %v", err) } allPolicies, err := ipsecpolicies.ExtractPolicies(allPages) if err != nil { t.Fatalf("Unable to extract policies: %v", err) } for _, policy := range allPolicies { tools.PrintResource(t, policy) } } func TestIPSecPolicyCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } policy, err := CreateIPSecPolicy(t, client) if err != nil { t.Fatalf("Unable to create IPSec policy: %v", err) } defer DeleteIPSecPolicy(t, client, policy.ID) tools.PrintResource(t, policy) updatedDescription := "Updated policy description" updateOpts := ipsecpolicies.UpdateOpts{ Description: &updatedDescription, } policy, err = ipsecpolicies.Update(client, policy.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update IPSec policy: %v", err) } tools.PrintResource(t, policy) newPolicy, err := ipsecpolicies.Get(client, policy.ID).Extract() if err != nil { t.Fatalf("Unable to get IPSec policy: %v", err) } tools.PrintResource(t, newPolicy) } service_test.go000066400000000000000000000030361335005366400410070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/vpnaas// +build acceptance networking fwaas package vpnaas import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" layer3 "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/layer3" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/services" ) func TestServiceList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := services.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list services: %v", err) } allServices, err := services.ExtractServices(allPages) if err != nil { t.Fatalf("Unable to extract services: %v", err) } for _, service := range allServices { tools.PrintResource(t, service) } } func TestServiceCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } router, err := layer3.CreateExternalRouter(t, client) if err != nil { t.Fatalf("Unable to create router: %v", err) } defer layer3.DeleteRouter(t, client, router.ID) service, err := CreateService(t, client, router.ID) if err != nil { t.Fatalf("Unable to create service: %v", err) } defer DeleteService(t, client, service.ID) newService, err := services.Get(client, service.ID).Extract() if err != nil { t.Fatalf("Unable to get service: %v", err) } tools.PrintResource(t, service) tools.PrintResource(t, newService) } siteconnection_test.go000066400000000000000000000071171335005366400423770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/vpnaas// +build acceptance networking vpnaas package vpnaas import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" networks "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" layer3 "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/layer3" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/siteconnections" ) func TestConnectionList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := siteconnections.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list IPSec site connections: %v", err) } allConnections, err := siteconnections.ExtractConnections(allPages) if err != nil { t.Fatalf("Unable to extract IPSec site connections: %v", err) } for _, connection := range allConnections { tools.PrintResource(t, connection) } } func TestConnectionCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create Network network, err := networks.CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer networks.DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := networks.CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer networks.DeleteSubnet(t, client, subnet.ID) router, err := layer3.CreateExternalRouter(t, client) if err != nil { t.Fatalf("Unable to create router: %v", err) } defer layer3.DeleteRouter(t, client, router.ID) // Link router and subnet aiOpts := routers.AddInterfaceOpts{ SubnetID: subnet.ID, } _, err = routers.AddInterface(client, router.ID, aiOpts).Extract() if err != nil { t.Fatalf("Failed to add interface to router: %v", err) } defer func() { riOpts := routers.RemoveInterfaceOpts{ SubnetID: subnet.ID, } routers.RemoveInterface(client, router.ID, riOpts) }() // Create all needed resources for the connection service, err := CreateService(t, client, router.ID) if err != nil { t.Fatalf("Unable to create service: %v", err) } defer DeleteService(t, client, service.ID) ikepolicy, err := CreateIKEPolicy(t, client) if err != nil { t.Fatalf("Unable to create IKE policy: %v", err) } defer DeleteIKEPolicy(t, client, ikepolicy.ID) ipsecpolicy, err := CreateIPSecPolicy(t, client) if err != nil { t.Fatalf("Unable to create IPSec Policy: %v", err) } defer DeleteIPSecPolicy(t, client, ipsecpolicy.ID) peerEPGroup, err := CreateEndpointGroup(t, client) if err != nil { t.Fatalf("Unable to create Endpoint Group with CIDR endpoints: %v", err) } defer DeleteEndpointGroup(t, client, peerEPGroup.ID) localEPGroup, err := CreateEndpointGroupWithSubnet(t, client, subnet.ID) if err != nil { t.Fatalf("Unable to create Endpoint Group with subnet endpoints: %v", err) } defer DeleteEndpointGroup(t, client, localEPGroup.ID) conn, err := CreateSiteConnection(t, client, ikepolicy.ID, ipsecpolicy.ID, service.ID, peerEPGroup.ID, localEPGroup.ID) if err != nil { t.Fatalf("Unable to create IPSec Site Connection: %v", err) } defer DeleteSiteConnection(t, client, conn.ID) newConnection, err := siteconnections.Get(client, conn.ID).Extract() if err != nil { t.Fatalf("Unable to get connection: %v", err) } tools.PrintResource(t, conn) tools.PrintResource(t, newConnection) } vpnaas.go000066400000000000000000000210571335005366400376030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2/extensions/vpnaaspackage vpnaas import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/endpointgroups" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ikepolicies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ipsecpolicies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/services" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/siteconnections" ) // CreateService will create a Service with a random name and a specified router ID // An error will be returned if the service could not be created. func CreateService(t *testing.T, client *gophercloud.ServiceClient, routerID string) (*services.Service, error) { serviceName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create service %s", serviceName) iTrue := true createOpts := services.CreateOpts{ Name: serviceName, AdminStateUp: &iTrue, RouterID: routerID, } service, err := services.Create(client, createOpts).Extract() if err != nil { return service, err } t.Logf("Successfully created service %s", serviceName) return service, nil } // DeleteService will delete a service with a specified ID. A fatal error // will occur if the delete was not successful. This works best when used as // a deferred function. func DeleteService(t *testing.T, client *gophercloud.ServiceClient, serviceID string) { t.Logf("Attempting to delete service: %s", serviceID) err := services.Delete(client, serviceID).ExtractErr() if err != nil { t.Fatalf("Unable to delete service %s: %v", serviceID, err) } t.Logf("Service deleted: %s", serviceID) } // CreateIPSecPolicy will create an IPSec Policy with a random name and given // rule. An error will be returned if the rule could not be created. func CreateIPSecPolicy(t *testing.T, client *gophercloud.ServiceClient) (*ipsecpolicies.Policy, error) { policyName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create IPSec policy %s", policyName) createOpts := ipsecpolicies.CreateOpts{ Name: policyName, } policy, err := ipsecpolicies.Create(client, createOpts).Extract() if err != nil { return policy, err } t.Logf("Successfully created IPSec policy %s", policyName) return policy, nil } // CreateIKEPolicy will create an IKE Policy with a random name and given // rule. An error will be returned if the policy could not be created. func CreateIKEPolicy(t *testing.T, client *gophercloud.ServiceClient) (*ikepolicies.Policy, error) { policyName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create IKE policy %s", policyName) createOpts := ikepolicies.CreateOpts{ Name: policyName, EncryptionAlgorithm: ikepolicies.EncryptionAlgorithm3DES, PFS: ikepolicies.PFSGroup5, } policy, err := ikepolicies.Create(client, createOpts).Extract() if err != nil { return policy, err } t.Logf("Successfully created IKE policy %s", policyName) return policy, nil } // DeleteIPSecPolicy will delete an IPSec policy with a specified ID. A fatal error will // occur if the delete was not successful. This works best when used as a // deferred function. func DeleteIPSecPolicy(t *testing.T, client *gophercloud.ServiceClient, policyID string) { t.Logf("Attempting to delete IPSec policy: %s", policyID) err := ipsecpolicies.Delete(client, policyID).ExtractErr() if err != nil { t.Fatalf("Unable to delete IPSec policy %s: %v", policyID, err) } t.Logf("Deleted IPSec policy: %s", policyID) } // DeleteIKEPolicy will delete an IKE policy with a specified ID. A fatal error will // occur if the delete was not successful. This works best when used as a // deferred function. func DeleteIKEPolicy(t *testing.T, client *gophercloud.ServiceClient, policyID string) { t.Logf("Attempting to delete policy: %s", policyID) err := ikepolicies.Delete(client, policyID).ExtractErr() if err != nil { t.Fatalf("Unable to delete IKE policy %s: %v", policyID, err) } t.Logf("Deleted IKE policy: %s", policyID) } // CreateEndpointGroup will create an endpoint group with a random name. // An error will be returned if the group could not be created. func CreateEndpointGroup(t *testing.T, client *gophercloud.ServiceClient) (*endpointgroups.EndpointGroup, error) { groupName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create group %s", groupName) createOpts := endpointgroups.CreateOpts{ Name: groupName, Type: endpointgroups.TypeCIDR, Endpoints: []string{ "10.2.0.0/24", "10.3.0.0/24", }, } group, err := endpointgroups.Create(client, createOpts).Extract() if err != nil { return group, err } t.Logf("Successfully created group %s", groupName) return group, nil } // CreateEndpointGroupWithCIDR will create an endpoint group with a random name and a specified CIDR. // An error will be returned if the group could not be created. func CreateEndpointGroupWithCIDR(t *testing.T, client *gophercloud.ServiceClient, cidr string) (*endpointgroups.EndpointGroup, error) { groupName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create group %s", groupName) createOpts := endpointgroups.CreateOpts{ Name: groupName, Type: endpointgroups.TypeCIDR, Endpoints: []string{ cidr, }, } group, err := endpointgroups.Create(client, createOpts).Extract() if err != nil { return group, err } t.Logf("Successfully created group %s", groupName) t.Logf("%v", group) return group, nil } // DeleteEndpointGroup will delete an Endpoint group with a specified ID. A fatal error will // occur if the delete was not successful. This works best when used as a // deferred function. func DeleteEndpointGroup(t *testing.T, client *gophercloud.ServiceClient, epGroupID string) { t.Logf("Attempting to delete endpoint group: %s", epGroupID) err := endpointgroups.Delete(client, epGroupID).ExtractErr() if err != nil { t.Fatalf("Unable to delete endpoint group %s: %v", epGroupID, err) } t.Logf("Deleted endpoint group: %s", epGroupID) } // CreateEndpointGroupWithSubnet will create an endpoint group with a random name. // An error will be returned if the group could not be created. func CreateEndpointGroupWithSubnet(t *testing.T, client *gophercloud.ServiceClient, subnetID string) (*endpointgroups.EndpointGroup, error) { groupName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create group %s", groupName) createOpts := endpointgroups.CreateOpts{ Name: groupName, Type: endpointgroups.TypeSubnet, Endpoints: []string{ subnetID, }, } group, err := endpointgroups.Create(client, createOpts).Extract() if err != nil { return group, err } t.Logf("Successfully created group %s", groupName) return group, nil } // CreateSiteConnection will create an IPSec site connection with a random name and specified // IKE policy, IPSec policy, service, peer EP group and local EP Group. // An error will be returned if the connection could not be created. func CreateSiteConnection(t *testing.T, client *gophercloud.ServiceClient, ikepolicyID string, ipsecpolicyID string, serviceID string, peerEPGroupID string, localEPGroupID string) (*siteconnections.Connection, error) { connectionName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create IPSec site connection %s", connectionName) createOpts := siteconnections.CreateOpts{ Name: connectionName, PSK: "secret", Initiator: siteconnections.InitiatorBiDirectional, AdminStateUp: gophercloud.Enabled, IPSecPolicyID: ipsecpolicyID, PeerEPGroupID: peerEPGroupID, IKEPolicyID: ikepolicyID, VPNServiceID: serviceID, LocalEPGroupID: localEPGroupID, PeerAddress: "172.24.4.233", PeerID: "172.24.4.233", MTU: 1500, } connection, err := siteconnections.Create(client, createOpts).Extract() if err != nil { return connection, err } t.Logf("Successfully created IPSec Site Connection %s", connectionName) return connection, nil } // DeleteSiteConnection will delete an IPSec site connection with a specified ID. A fatal error will // occur if the delete was not successful. This works best when used as a // deferred function. func DeleteSiteConnection(t *testing.T, client *gophercloud.ServiceClient, siteConnectionID string) { t.Logf("Attempting to delete site connection: %s", siteConnectionID) err := siteconnections.Delete(client, siteConnectionID).ExtractErr() if err != nil { t.Fatalf("Unable to delete site connection %s: %v", siteConnectionID, err) } t.Logf("Deleted site connection: %s", siteConnectionID) } networking.go000066400000000000000000000304321335005366400350100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2package v2 import ( "fmt" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/extradhcpopts" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsecurity" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" ) // CreateNetwork will create basic network. An error will be returned if the // network could not be created. func CreateNetwork(t *testing.T, client *gophercloud.ServiceClient) (*networks.Network, error) { networkName := tools.RandomString("TESTACC-", 8) createOpts := networks.CreateOpts{ Name: networkName, AdminStateUp: gophercloud.Enabled, } t.Logf("Attempting to create network: %s", networkName) network, err := networks.Create(client, createOpts).Extract() if err != nil { return network, err } t.Logf("Successfully created network.") return network, nil } // CreateNetworkWithoutPortSecurity will create a network without port security. // An error will be returned if the network could not be created. func CreateNetworkWithoutPortSecurity(t *testing.T, client *gophercloud.ServiceClient) (*networks.Network, error) { networkName := tools.RandomString("TESTACC-", 8) networkCreateOpts := networks.CreateOpts{ Name: networkName, AdminStateUp: gophercloud.Enabled, } iFalse := false createOpts := portsecurity.NetworkCreateOptsExt{ CreateOptsBuilder: networkCreateOpts, PortSecurityEnabled: &iFalse, } t.Logf("Attempting to create network: %s", networkName) network, err := networks.Create(client, createOpts).Extract() if err != nil { return network, err } t.Logf("Successfully created network.") return network, nil } // CreatePort will create a port on the specified subnet. An error will be // returned if the port could not be created. func CreatePort(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*ports.Port, error) { portName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create port: %s", portName) createOpts := ports.CreateOpts{ NetworkID: networkID, Name: portName, AdminStateUp: gophercloud.Enabled, FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, } port, err := ports.Create(client, createOpts).Extract() if err != nil { return port, err } if err := WaitForPortToCreate(client, port.ID, 60); err != nil { return port, err } newPort, err := ports.Get(client, port.ID).Extract() if err != nil { return newPort, err } t.Logf("Successfully created port: %s", portName) return newPort, nil } // CreatePortWithNoSecurityGroup will create a port with no security group // attached. An error will be returned if the port could not be created. func CreatePortWithNoSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*ports.Port, error) { portName := tools.RandomString("TESTACC-", 8) iFalse := false t.Logf("Attempting to create port: %s", portName) createOpts := ports.CreateOpts{ NetworkID: networkID, Name: portName, AdminStateUp: &iFalse, FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, SecurityGroups: &[]string{}, } port, err := ports.Create(client, createOpts).Extract() if err != nil { return port, err } if err := WaitForPortToCreate(client, port.ID, 60); err != nil { return port, err } newPort, err := ports.Get(client, port.ID).Extract() if err != nil { return newPort, err } t.Logf("Successfully created port: %s", portName) return newPort, nil } // CreatePortWithoutPortSecurity will create a port without port security on the // specified subnet. An error will be returned if the port could not be created. func CreatePortWithoutPortSecurity(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*ports.Port, error) { portName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create port: %s", portName) portCreateOpts := ports.CreateOpts{ NetworkID: networkID, Name: portName, AdminStateUp: gophercloud.Enabled, FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, } iFalse := false createOpts := portsecurity.PortCreateOptsExt{ CreateOptsBuilder: portCreateOpts, PortSecurityEnabled: &iFalse, } port, err := ports.Create(client, createOpts).Extract() if err != nil { return port, err } if err := WaitForPortToCreate(client, port.ID, 60); err != nil { return port, err } newPort, err := ports.Get(client, port.ID).Extract() if err != nil { return newPort, err } t.Logf("Successfully created port: %s", portName) return newPort, nil } // CreateSubnet will create a subnet on the specified Network ID. An error // will be returned if the subnet could not be created. func CreateSubnet(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*subnets.Subnet, error) { subnetName := tools.RandomString("TESTACC-", 8) subnetOctet := tools.RandomInt(1, 250) subnetCIDR := fmt.Sprintf("192.168.%d.0/24", subnetOctet) subnetGateway := fmt.Sprintf("192.168.%d.1", subnetOctet) createOpts := subnets.CreateOpts{ NetworkID: networkID, CIDR: subnetCIDR, IPVersion: 4, Name: subnetName, EnableDHCP: gophercloud.Disabled, GatewayIP: &subnetGateway, } t.Logf("Attempting to create subnet: %s", subnetName) subnet, err := subnets.Create(client, createOpts).Extract() if err != nil { return subnet, err } t.Logf("Successfully created subnet.") return subnet, nil } // CreateSubnetWithDefaultGateway will create a subnet on the specified Network // ID and have Neutron set the gateway by default An error will be returned if // the subnet could not be created. func CreateSubnetWithDefaultGateway(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*subnets.Subnet, error) { subnetName := tools.RandomString("TESTACC-", 8) subnetOctet := tools.RandomInt(1, 250) subnetCIDR := fmt.Sprintf("192.168.%d.0/24", subnetOctet) createOpts := subnets.CreateOpts{ NetworkID: networkID, CIDR: subnetCIDR, IPVersion: 4, Name: subnetName, EnableDHCP: gophercloud.Disabled, } t.Logf("Attempting to create subnet: %s", subnetName) subnet, err := subnets.Create(client, createOpts).Extract() if err != nil { return subnet, err } t.Logf("Successfully created subnet.") return subnet, nil } // CreateSubnetWithNoGateway will create a subnet with no gateway on the // specified Network ID. An error will be returned if the subnet could not be // created. func CreateSubnetWithNoGateway(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*subnets.Subnet, error) { var noGateway = "" subnetName := tools.RandomString("TESTACC-", 8) subnetOctet := tools.RandomInt(1, 250) subnetCIDR := fmt.Sprintf("192.168.%d.0/24", subnetOctet) dhcpStart := fmt.Sprintf("192.168.%d.10", subnetOctet) dhcpEnd := fmt.Sprintf("192.168.%d.200", subnetOctet) createOpts := subnets.CreateOpts{ NetworkID: networkID, CIDR: subnetCIDR, IPVersion: 4, Name: subnetName, EnableDHCP: gophercloud.Disabled, GatewayIP: &noGateway, AllocationPools: []subnets.AllocationPool{ { Start: dhcpStart, End: dhcpEnd, }, }, } t.Logf("Attempting to create subnet: %s", subnetName) subnet, err := subnets.Create(client, createOpts).Extract() if err != nil { return subnet, err } t.Logf("Successfully created subnet.") return subnet, nil } // CreateSubnetWithSubnetPool will create a subnet associated with the provided subnetpool on the specified Network ID. // An error will be returned if the subnet or the subnetpool could not be created. func CreateSubnetWithSubnetPool(t *testing.T, client *gophercloud.ServiceClient, networkID string, subnetPoolID string) (*subnets.Subnet, error) { subnetName := tools.RandomString("TESTACC-", 8) subnetOctet := tools.RandomInt(1, 250) subnetCIDR := fmt.Sprintf("10.%d.0.0/24", subnetOctet) createOpts := subnets.CreateOpts{ NetworkID: networkID, CIDR: subnetCIDR, IPVersion: 4, Name: subnetName, EnableDHCP: gophercloud.Disabled, SubnetPoolID: subnetPoolID, } t.Logf("Attempting to create subnet: %s", subnetName) subnet, err := subnets.Create(client, createOpts).Extract() if err != nil { return subnet, err } t.Logf("Successfully created subnet.") return subnet, nil } // CreateSubnetWithSubnetPoolNoCIDR will create a subnet associated with the // provided subnetpool on the specified Network ID. // An error will be returned if the subnet or the subnetpool could not be created. func CreateSubnetWithSubnetPoolNoCIDR(t *testing.T, client *gophercloud.ServiceClient, networkID string, subnetPoolID string) (*subnets.Subnet, error) { subnetName := tools.RandomString("TESTACC-", 8) createOpts := subnets.CreateOpts{ NetworkID: networkID, IPVersion: 4, Name: subnetName, EnableDHCP: gophercloud.Disabled, SubnetPoolID: subnetPoolID, } t.Logf("Attempting to create subnet: %s", subnetName) subnet, err := subnets.Create(client, createOpts).Extract() if err != nil { return subnet, err } t.Logf("Successfully created subnet.") return subnet, nil } // DeleteNetwork will delete a network with a specified ID. A fatal error will // occur if the delete was not successful. This works best when used as a // deferred function. func DeleteNetwork(t *testing.T, client *gophercloud.ServiceClient, networkID string) { t.Logf("Attempting to delete network: %s", networkID) err := networks.Delete(client, networkID).ExtractErr() if err != nil { t.Fatalf("Unable to delete network %s: %v", networkID, err) } t.Logf("Deleted network: %s", networkID) } // DeletePort will delete a port with a specified ID. A fatal error will // occur if the delete was not successful. This works best when used as a // deferred function. func DeletePort(t *testing.T, client *gophercloud.ServiceClient, portID string) { t.Logf("Attempting to delete port: %s", portID) err := ports.Delete(client, portID).ExtractErr() if err != nil { t.Fatalf("Unable to delete port %s: %v", portID, err) } t.Logf("Deleted port: %s", portID) } // DeleteSubnet will delete a subnet with a specified ID. A fatal error will // occur if the delete was not successful. This works best when used as a // deferred function. func DeleteSubnet(t *testing.T, client *gophercloud.ServiceClient, subnetID string) { t.Logf("Attempting to delete subnet: %s", subnetID) err := subnets.Delete(client, subnetID).ExtractErr() if err != nil { t.Fatalf("Unable to delete subnet %s: %v", subnetID, err) } t.Logf("Deleted subnet: %s", subnetID) } func WaitForPortToCreate(client *gophercloud.ServiceClient, portID string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { p, err := ports.Get(client, portID).Extract() if err != nil { return false, err } if p.Status == "ACTIVE" || p.Status == "DOWN" { return true, nil } return false, nil }) } // PortWithExtraDHCPOpts represents a port with extra DHCP options configuration. type PortWithExtraDHCPOpts struct { ports.Port extradhcpopts.ExtraDHCPOptsExt } // CreatePortWithExtraDHCPOpts will create a port with DHCP options on the // specified subnet. An error will be returned if the port could not be created. func CreatePortWithExtraDHCPOpts(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*PortWithExtraDHCPOpts, error) { portName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create port: %s", portName) portCreateOpts := ports.CreateOpts{ NetworkID: networkID, Name: portName, AdminStateUp: gophercloud.Enabled, FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, } createOpts := extradhcpopts.CreateOptsExt{ CreateOptsBuilder: portCreateOpts, ExtraDHCPOpts: []extradhcpopts.CreateExtraDHCPOpt{ { OptName: "test_option_1", OptValue: "test_value_1", }, }, } port := &PortWithExtraDHCPOpts{} err := ports.Create(client, createOpts).ExtractInto(port) if err != nil { return nil, err } if err := WaitForPortToCreate(client, port.ID, 60); err != nil { return nil, err } err = ports.Get(client, port.ID).ExtractInto(port) if err != nil { return port, err } t.Logf("Successfully created port: %s", portName) return port, nil } networks_test.go000066400000000000000000000104711335005366400355350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2// +build acceptance networking package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsecurity" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" th "github.com/gophercloud/gophercloud/testhelper" ) func TestNetworksList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } type networkWithExt struct { networks.Network portsecurity.PortSecurityExt } var allNetworks []networkWithExt allPages, err := networks.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list networks: %v", err) } err = networks.ExtractNetworksInto(allPages, &allNetworks) if err != nil { t.Fatalf("Unable to extract networks: %v", err) } for _, network := range allNetworks { tools.PrintResource(t, network) } } func TestNetworksExternalList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatalf("Unable to fetch environment information: %s", err) } type networkWithExt struct { networks.Network external.NetworkExternalExt } var allNetworks []networkWithExt iTrue := true networkListOpts := networks.ListOpts{ ID: choices.ExternalNetworkID, } listOpts := external.ListOptsExt{ ListOptsBuilder: networkListOpts, External: &iTrue, } allPages, err := networks.List(client, listOpts).AllPages() if err != nil { t.Fatalf("Unable to list networks: %v", err) } err = networks.ExtractNetworksInto(allPages, &allNetworks) if err != nil { t.Fatalf("Unable to extract networks: %v", err) } var found bool for _, network := range allNetworks { if network.External == true && network.ID == choices.ExternalNetworkID { found = true } } th.AssertEquals(t, found, true) iFalse := false networkListOpts = networks.ListOpts{ ID: choices.ExternalNetworkID, } listOpts = external.ListOptsExt{ ListOptsBuilder: networkListOpts, External: &iFalse, } allPages, err = networks.List(client, listOpts).AllPages() if err != nil { t.Fatalf("Unable to list networks: %v", err) } v, err := networks.ExtractNetworks(allPages) th.AssertEquals(t, len(v), 0) } func TestNetworksCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create a network network, err := CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer DeleteNetwork(t, client, network.ID) tools.PrintResource(t, network) newName := tools.RandomString("TESTACC-", 8) updateOpts := &networks.UpdateOpts{ Name: newName, } _, err = networks.Update(client, network.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update network: %v", err) } newNetwork, err := networks.Get(client, network.ID).Extract() if err != nil { t.Fatalf("Unable to retrieve network: %v", err) } tools.PrintResource(t, newNetwork) } func TestNetworksPortSecurityCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create a network without port security network, err := CreateNetworkWithoutPortSecurity(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer DeleteNetwork(t, client, network.ID) var networkWithExtensions struct { networks.Network portsecurity.PortSecurityExt } err = networks.Get(client, network.ID).ExtractInto(&networkWithExtensions) if err != nil { t.Fatalf("Unable to retrieve network: %v", err) } tools.PrintResource(t, networkWithExtensions) iTrue := true networkUpdateOpts := networks.UpdateOpts{} updateOpts := portsecurity.NetworkUpdateOptsExt{ UpdateOptsBuilder: networkUpdateOpts, PortSecurityEnabled: &iTrue, } err = networks.Update(client, network.ID, updateOpts).ExtractInto(&networkWithExtensions) if err != nil { t.Fatalf("Unable to update network: %v", err) } tools.PrintResource(t, networkWithExtensions) } pkg.go000066400000000000000000000000131335005366400333720ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2package v2 ports_test.go000066400000000000000000000270141335005366400350310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2// +build acceptance networking package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" extensions "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/extradhcpopts" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsecurity" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" ) func TestPortsList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := ports.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list ports: %v", err) } allPorts, err := ports.ExtractPorts(allPages) if err != nil { t.Fatalf("Unable to extract ports: %v", err) } for _, port := range allPorts { tools.PrintResource(t, port) } } func TestPortsCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create Network network, err := CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer DeleteSubnet(t, client, subnet.ID) // Create port port, err := CreatePort(t, client, network.ID, subnet.ID) if err != nil { t.Fatalf("Unable to create port: %v", err) } defer DeletePort(t, client, port.ID) if len(port.SecurityGroups) != 1 { t.Logf("WARNING: Port did not have a default security group applied") } tools.PrintResource(t, port) // Update port newPortName := tools.RandomString("TESTACC-", 8) updateOpts := ports.UpdateOpts{ Name: newPortName, } newPort, err := ports.Update(client, port.ID, updateOpts).Extract() if err != nil { t.Fatalf("Could not update port: %v", err) } tools.PrintResource(t, newPort) } func TestPortsRemoveSecurityGroups(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create Network network, err := CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer DeleteSubnet(t, client, subnet.ID) // Create port port, err := CreatePort(t, client, network.ID, subnet.ID) if err != nil { t.Fatalf("Unable to create port: %v", err) } defer DeletePort(t, client, port.ID) tools.PrintResource(t, port) // Create a Security Group group, err := extensions.CreateSecurityGroup(t, client) if err != nil { t.Fatalf("Unable to create security group: %v", err) } defer extensions.DeleteSecurityGroup(t, client, group.ID) // Add the group to the port updateOpts := ports.UpdateOpts{ SecurityGroups: &[]string{group.ID}, } newPort, err := ports.Update(client, port.ID, updateOpts).Extract() if err != nil { t.Fatalf("Could not update port: %v", err) } // Remove the group updateOpts = ports.UpdateOpts{ SecurityGroups: &[]string{}, } newPort, err = ports.Update(client, port.ID, updateOpts).Extract() if err != nil { t.Fatalf("Could not update port: %v", err) } tools.PrintResource(t, newPort) if len(newPort.SecurityGroups) > 0 { t.Fatalf("Unable to remove security group from port") } } func TestPortsDontAlterSecurityGroups(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create Network network, err := CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer DeleteSubnet(t, client, subnet.ID) // Create a Security Group group, err := extensions.CreateSecurityGroup(t, client) if err != nil { t.Fatalf("Unable to create security group: %v", err) } defer extensions.DeleteSecurityGroup(t, client, group.ID) // Create port port, err := CreatePort(t, client, network.ID, subnet.ID) if err != nil { t.Fatalf("Unable to create port: %v", err) } defer DeletePort(t, client, port.ID) tools.PrintResource(t, port) // Add the group to the port updateOpts := ports.UpdateOpts{ SecurityGroups: &[]string{group.ID}, } newPort, err := ports.Update(client, port.ID, updateOpts).Extract() if err != nil { t.Fatalf("Could not update port: %v", err) } // Update the port again updateOpts = ports.UpdateOpts{ Name: "some_port", } newPort, err = ports.Update(client, port.ID, updateOpts).Extract() if err != nil { t.Fatalf("Could not update port: %v", err) } tools.PrintResource(t, newPort) if len(newPort.SecurityGroups) == 0 { t.Fatalf("Port had security group updated") } } func TestPortsWithNoSecurityGroup(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create Network network, err := CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer DeleteSubnet(t, client, subnet.ID) // Create port port, err := CreatePortWithNoSecurityGroup(t, client, network.ID, subnet.ID) if err != nil { t.Fatalf("Unable to create port: %v", err) } defer DeletePort(t, client, port.ID) tools.PrintResource(t, port) if len(port.SecurityGroups) != 0 { t.Fatalf("Port was created with security groups") } } func TestPortsRemoveAddressPair(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create Network network, err := CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer DeleteSubnet(t, client, subnet.ID) // Create port port, err := CreatePort(t, client, network.ID, subnet.ID) if err != nil { t.Fatalf("Unable to create port: %v", err) } defer DeletePort(t, client, port.ID) tools.PrintResource(t, port) // Add an address pair to the port updateOpts := ports.UpdateOpts{ AllowedAddressPairs: &[]ports.AddressPair{ ports.AddressPair{IPAddress: "192.168.255.10", MACAddress: "aa:bb:cc:dd:ee:ff"}, }, } newPort, err := ports.Update(client, port.ID, updateOpts).Extract() if err != nil { t.Fatalf("Could not update port: %v", err) } // Remove the address pair updateOpts = ports.UpdateOpts{ AllowedAddressPairs: &[]ports.AddressPair{}, } newPort, err = ports.Update(client, port.ID, updateOpts).Extract() if err != nil { t.Fatalf("Could not update port: %v", err) } tools.PrintResource(t, newPort) if len(newPort.AllowedAddressPairs) > 0 { t.Fatalf("Unable to remove the address pair") } } func TestPortsDontUpdateAllowedAddressPairs(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create Network network, err := CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer DeleteSubnet(t, client, subnet.ID) // Create port port, err := CreatePort(t, client, network.ID, subnet.ID) if err != nil { t.Fatalf("Unable to create port: %v", err) } defer DeletePort(t, client, port.ID) tools.PrintResource(t, port) // Add an address pair to the port updateOpts := ports.UpdateOpts{ AllowedAddressPairs: &[]ports.AddressPair{ ports.AddressPair{IPAddress: "192.168.255.10", MACAddress: "aa:bb:cc:dd:ee:ff"}, }, } newPort, err := ports.Update(client, port.ID, updateOpts).Extract() if err != nil { t.Fatalf("Could not update port: %v", err) } tools.PrintResource(t, newPort) // Remove the address pair updateOpts = ports.UpdateOpts{ Name: "some_port", } newPort, err = ports.Update(client, port.ID, updateOpts).Extract() if err != nil { t.Fatalf("Could not update port: %v", err) } tools.PrintResource(t, newPort) if len(newPort.AllowedAddressPairs) == 0 { t.Fatalf("Address Pairs were removed") } } func TestPortsPortSecurityCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create Network network, err := CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer DeleteSubnet(t, client, subnet.ID) // Create port port, err := CreatePortWithoutPortSecurity(t, client, network.ID, subnet.ID) if err != nil { t.Fatalf("Unable to create port: %v", err) } defer DeletePort(t, client, port.ID) var portWithExt struct { ports.Port portsecurity.PortSecurityExt } err = ports.Get(client, port.ID).ExtractInto(&portWithExt) if err != nil { t.Fatalf("Unable to create port: %v", err) } tools.PrintResource(t, portWithExt) iTrue := true portUpdateOpts := ports.UpdateOpts{} updateOpts := portsecurity.PortUpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, PortSecurityEnabled: &iTrue, } err = ports.Update(client, port.ID, updateOpts).ExtractInto(&portWithExt) if err != nil { t.Fatalf("Unable to update port: %v", err) } tools.PrintResource(t, portWithExt) } func TestPortsWithExtraDHCPOptsCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create a Network network, err := CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create a network: %v", err) } defer DeleteNetwork(t, client, network.ID) // Create a Subnet subnet, err := CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create a subnet: %v", err) } defer DeleteSubnet(t, client, subnet.ID) // Create a port with extra DHCP options. port, err := CreatePortWithExtraDHCPOpts(t, client, network.ID, subnet.ID) if err != nil { t.Fatalf("Unable to create a port: %v", err) } defer DeletePort(t, client, port.ID) tools.PrintResource(t, port) // Update the port with extra DHCP options. newPortName := tools.RandomString("TESTACC-", 8) portUpdateOpts := ports.UpdateOpts{ Name: newPortName, } existingOpt := port.ExtraDHCPOpts[0] newOptValue := "test_value_2" updateOpts := extradhcpopts.UpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, ExtraDHCPOpts: []extradhcpopts.UpdateExtraDHCPOpt{ { OptName: existingOpt.OptName, OptValue: nil, }, { OptName: "test_option_2", OptValue: &newOptValue, }, }, } newPort := &PortWithExtraDHCPOpts{} err = ports.Update(client, port.ID, updateOpts).ExtractInto(newPort) if err != nil { t.Fatalf("Could not update port: %v", err) } tools.PrintResource(t, newPort) } subnets_test.go000066400000000000000000000130351335005366400353430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/networking/v2// +build acceptance networking package v2 import ( "fmt" "strings" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" subnetpools "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/subnetpools" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" ) func TestSubnetsList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } allPages, err := subnets.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list subnets: %v", err) } allSubnets, err := subnets.ExtractSubnets(allPages) if err != nil { t.Fatalf("Unable to extract subnets: %v", err) } for _, subnet := range allSubnets { tools.PrintResource(t, subnet) } } func TestSubnetCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create Network network, err := CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer DeleteSubnet(t, client, subnet.ID) tools.PrintResource(t, subnet) // Update Subnet newSubnetName := tools.RandomString("TESTACC-", 8) updateOpts := subnets.UpdateOpts{ Name: newSubnetName, } _, err = subnets.Update(client, subnet.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update subnet: %v", err) } // Get subnet newSubnet, err := subnets.Get(client, subnet.ID).Extract() if err != nil { t.Fatalf("Unable to get subnet: %v", err) } tools.PrintResource(t, newSubnet) } func TestSubnetsDefaultGateway(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create Network network, err := CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnetWithDefaultGateway(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer DeleteSubnet(t, client, subnet.ID) tools.PrintResource(t, subnet) if subnet.GatewayIP == "" { t.Fatalf("A default gateway was not created.") } var noGateway = "" updateOpts := subnets.UpdateOpts{ GatewayIP: &noGateway, } newSubnet, err := subnets.Update(client, subnet.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update subnet") } if newSubnet.GatewayIP != "" { t.Fatalf("Gateway was not updated correctly") } } func TestSubnetsNoGateway(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create Network network, err := CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnetWithNoGateway(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer DeleteSubnet(t, client, subnet.ID) tools.PrintResource(t, subnet) if subnet.GatewayIP != "" { t.Fatalf("A gateway exists when it shouldn't.") } subnetParts := strings.Split(subnet.CIDR, ".") newGateway := fmt.Sprintf("%s.%s.%s.1", subnetParts[0], subnetParts[1], subnetParts[2]) updateOpts := subnets.UpdateOpts{ GatewayIP: &newGateway, } newSubnet, err := subnets.Update(client, subnet.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update subnet") } if newSubnet.GatewayIP == "" { t.Fatalf("Gateway was not updated correctly") } } func TestSubnetsWithSubnetPool(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create Network network, err := CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer DeleteNetwork(t, client, network.ID) // Create SubnetPool subnetPool, err := subnetpools.CreateSubnetPool(t, client) if err != nil { t.Fatalf("Unable to create subnet pool: %v", err) } defer subnetpools.DeleteSubnetPool(t, client, subnetPool.ID) // Create Subnet subnet, err := CreateSubnetWithSubnetPool(t, client, network.ID, subnetPool.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer DeleteSubnet(t, client, subnet.ID) tools.PrintResource(t, subnet) if subnet.GatewayIP == "" { t.Fatalf("A subnet pool was not associated.") } } func TestSubnetsWithSubnetPoolNoCIDR(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } // Create Network network, err := CreateNetwork(t, client) if err != nil { t.Fatalf("Unable to create network: %v", err) } defer DeleteNetwork(t, client, network.ID) // Create SubnetPool subnetPool, err := subnetpools.CreateSubnetPool(t, client) if err != nil { t.Fatalf("Unable to create subnet pool: %v", err) } defer subnetpools.DeleteSubnetPool(t, client, subnetPool.ID) // Create Subnet subnet, err := CreateSubnetWithSubnetPoolNoCIDR(t, client, network.ID, subnetPool.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } defer DeleteSubnet(t, client, subnet.ID) tools.PrintResource(t, subnet) if subnet.GatewayIP == "" { t.Fatalf("A subnet pool was not associated.") } } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/objectstorage/000077500000000000000000000000001335005366400324645ustar00rootroot00000000000000v1/000077500000000000000000000000001335005366400327335ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/objectstorageaccounts_test.go000066400000000000000000000026411335005366400361430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/objectstorage/v1// +build acceptance package v1 import ( "strings" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts" th "github.com/gophercloud/gophercloud/testhelper" ) func TestAccounts(t *testing.T) { client, err := clients.NewObjectStorageV1Client() if err != nil { t.Fatalf("Unable to create client: %v", err) } // Update an account's metadata. metadata := map[string]string{ "Gophercloud-Test": "accounts", } updateres := accounts.Update(client, accounts.UpdateOpts{Metadata: metadata}) t.Logf("Update Account Response: %+v\n", updateres) updateHeaders, err := updateres.Extract() th.AssertNoErr(t, err) t.Logf("Update Account Response Headers: %+v\n", updateHeaders) // Defer the deletion of the metadata set above. defer func() { tempMap := make(map[string]string) for k := range metadata { tempMap[k] = "" } updateres = accounts.Update(client, accounts.UpdateOpts{Metadata: tempMap}) th.AssertNoErr(t, updateres.Err) }() // Extract the custom metadata from the 'Get' response. res := accounts.Get(client, nil) h, err := res.Extract() th.AssertNoErr(t, err) t.Logf("Get Account Response Headers: %+v\n", h) am, err := res.ExtractMetadata() th.AssertNoErr(t, err) for k := range metadata { if am[k] != metadata[strings.Title(k)] { t.Errorf("Expected custom metadata with key: %s", k) return } } } containers_test.go000066400000000000000000000111771335005366400364750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/objectstorage/v1// +build acceptance package v1 import ( "strings" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) // numContainers is the number of containers to create for testing. var numContainers = 2 func TestContainers(t *testing.T) { client, err := clients.NewObjectStorageV1Client() if err != nil { t.Fatalf("Unable to create client: %v", err) } // Create a slice of random container names. cNames := make([]string, numContainers) for i := 0; i < numContainers; i++ { cNames[i] = tools.RandomString("gophercloud-test-container-", 8) } // Create numContainers containers. for i := 0; i < len(cNames); i++ { res := containers.Create(client, cNames[i], nil) th.AssertNoErr(t, res.Err) } // Delete the numContainers containers after function completion. defer func() { for i := 0; i < len(cNames); i++ { res := containers.Delete(client, cNames[i]) th.AssertNoErr(t, res.Err) } }() // List the numContainer names that were just created. To just list those, // the 'prefix' parameter is used. err = containers.List(client, &containers.ListOpts{Full: true, Prefix: "gophercloud-test-container-"}).EachPage(func(page pagination.Page) (bool, error) { containerList, err := containers.ExtractInfo(page) th.AssertNoErr(t, err) for _, n := range containerList { t.Logf("Container: Name [%s] Count [%d] Bytes [%d]", n.Name, n.Count, n.Bytes) } return true, nil }) th.AssertNoErr(t, err) // List the info for the numContainer containers that were created. err = containers.List(client, &containers.ListOpts{Full: false, Prefix: "gophercloud-test-container-"}).EachPage(func(page pagination.Page) (bool, error) { containerList, err := containers.ExtractNames(page) th.AssertNoErr(t, err) for _, n := range containerList { t.Logf("Container: Name [%s]", n) } return true, nil }) th.AssertNoErr(t, err) // Update one of the numContainer container metadata. metadata := map[string]string{ "Gophercloud-Test": "containers", } updateres := containers.Update(client, cNames[0], &containers.UpdateOpts{Metadata: metadata}) th.AssertNoErr(t, updateres.Err) // After the tests are done, delete the metadata that was set. defer func() { tempMap := make(map[string]string) for k := range metadata { tempMap[k] = "" } res := containers.Update(client, cNames[0], &containers.UpdateOpts{Metadata: tempMap}) th.AssertNoErr(t, res.Err) }() // Retrieve a container's metadata. getOpts := containers.GetOpts{ Newest: true, } cm, err := containers.Get(client, cNames[0], getOpts).ExtractMetadata() th.AssertNoErr(t, err) for k := range metadata { if cm[k] != metadata[strings.Title(k)] { t.Errorf("Expected custom metadata with key: %s", k) } } } func TestListAllContainers(t *testing.T) { client, err := clients.NewObjectStorageV1Client() if err != nil { t.Fatalf("Unable to create client: %v", err) } numContainers := 20 // Create a slice of random container names. cNames := make([]string, numContainers) for i := 0; i < numContainers; i++ { cNames[i] = tools.RandomString("gophercloud-test-container-", 8) } // Create numContainers containers. for i := 0; i < len(cNames); i++ { res := containers.Create(client, cNames[i], nil) th.AssertNoErr(t, res.Err) } // Delete the numContainers containers after function completion. defer func() { for i := 0; i < len(cNames); i++ { res := containers.Delete(client, cNames[i]) th.AssertNoErr(t, res.Err) } }() // List all the numContainer names that were just created. To just list those, // the 'prefix' parameter is used. allPages, err := containers.List(client, &containers.ListOpts{Full: true, Limit: 5, Prefix: "gophercloud-test-container-"}).AllPages() th.AssertNoErr(t, err) containerInfoList, err := containers.ExtractInfo(allPages) th.AssertNoErr(t, err) for _, n := range containerInfoList { t.Logf("Container: Name [%s] Count [%d] Bytes [%d]", n.Name, n.Count, n.Bytes) } th.AssertEquals(t, numContainers, len(containerInfoList)) // List the info for all the numContainer containers that were created. allPages, err = containers.List(client, &containers.ListOpts{Full: false, Limit: 2, Prefix: "gophercloud-test-container-"}).AllPages() th.AssertNoErr(t, err) containerNamesList, err := containers.ExtractNames(allPages) th.AssertNoErr(t, err) for _, n := range containerNamesList { t.Logf("Container: Name [%s]", n) } th.AssertEquals(t, numContainers, len(containerNamesList)) } objects_test.go000066400000000000000000000157741335005366400357700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/objectstorage/v1// +build acceptance package v1 import ( "bytes" "strings" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects" th "github.com/gophercloud/gophercloud/testhelper" ) // numObjects is the number of objects to create for testing. var numObjects = 2 func TestObjects(t *testing.T) { client, err := clients.NewObjectStorageV1Client() if err != nil { t.Fatalf("Unable to create client: %v", err) } // Make a slice of length numObjects to hold the random object names. oNames := make([]string, numObjects) for i := 0; i < len(oNames); i++ { oNames[i] = tools.RandomString("test-object-", 8) } // Create a container to hold the test objects. cName := tools.RandomString("test-container-", 8) header, err := containers.Create(client, cName, nil).Extract() th.AssertNoErr(t, err) t.Logf("Create object headers: %+v\n", header) // Defer deletion of the container until after testing. defer func() { res := containers.Delete(client, cName) th.AssertNoErr(t, res.Err) }() // Create a slice of buffers to hold the test object content. oContents := make([]*bytes.Buffer, numObjects) for i := 0; i < numObjects; i++ { oContents[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10))) createOpts := objects.CreateOpts{ Content: oContents[i], } res := objects.Create(client, cName, oNames[i], createOpts) th.AssertNoErr(t, res.Err) } // Delete the objects after testing. defer func() { for i := 0; i < numObjects; i++ { res := objects.Delete(client, cName, oNames[i], nil) th.AssertNoErr(t, res.Err) } }() // List all created objects listOpts := objects.ListOpts{ Full: true, Prefix: "test-object-", } allPages, err := objects.List(client, cName, listOpts).AllPages() if err != nil { t.Fatalf("Unable to list objects: %v", err) } ons, err := objects.ExtractNames(allPages) if err != nil { t.Fatalf("Unable to extract objects: %v", err) } th.AssertEquals(t, len(ons), len(oNames)) ois, err := objects.ExtractInfo(allPages) if err != nil { t.Fatalf("Unable to extract object info: %v", err) } th.AssertEquals(t, len(ois), len(oNames)) // Copy the contents of one object to another. copyOpts := objects.CopyOpts{ Destination: cName + "/" + oNames[1], } copyres := objects.Copy(client, cName, oNames[0], copyOpts) th.AssertNoErr(t, copyres.Err) // Download one of the objects that was created above. downloadres := objects.Download(client, cName, oNames[0], nil) th.AssertNoErr(t, downloadres.Err) o1Content, err := downloadres.ExtractContent() th.AssertNoErr(t, err) // Download the another object that was create above. downloadOpts := objects.DownloadOpts{ Newest: true, } downloadres = objects.Download(client, cName, oNames[1], downloadOpts) th.AssertNoErr(t, downloadres.Err) o2Content, err := downloadres.ExtractContent() th.AssertNoErr(t, err) // Compare the two object's contents to test that the copy worked. th.AssertEquals(t, string(o2Content), string(o1Content)) // Update an object's metadata. metadata := map[string]string{ "Gophercloud-Test": "objects", } updateOpts := objects.UpdateOpts{ Metadata: metadata, } updateres := objects.Update(client, cName, oNames[0], updateOpts) th.AssertNoErr(t, updateres.Err) // Delete the object's metadata after testing. defer func() { tempMap := make(map[string]string) for k := range metadata { tempMap[k] = "" } res := objects.Update(client, cName, oNames[0], &objects.UpdateOpts{Metadata: tempMap}) th.AssertNoErr(t, res.Err) }() // Retrieve an object's metadata. getOpts := objects.GetOpts{ Newest: true, } om, err := objects.Get(client, cName, oNames[0], getOpts).ExtractMetadata() th.AssertNoErr(t, err) for k := range metadata { if om[k] != metadata[strings.Title(k)] { t.Errorf("Expected custom metadata with key: %s", k) return } } } func TestObjectsListSubdir(t *testing.T) { client, err := clients.NewObjectStorageV1Client() if err != nil { t.Fatalf("Unable to create client: %v", err) } // Create a random subdirectory name. cSubdir1 := tools.RandomString("test-subdir-", 8) cSubdir2 := tools.RandomString("test-subdir-", 8) // Make a slice of length numObjects to hold the random object names. oNames1 := make([]string, numObjects) for i := 0; i < len(oNames1); i++ { oNames1[i] = cSubdir1 + "/" + tools.RandomString("test-object-", 8) } oNames2 := make([]string, numObjects) for i := 0; i < len(oNames2); i++ { oNames2[i] = cSubdir2 + "/" + tools.RandomString("test-object-", 8) } // Create a container to hold the test objects. cName := tools.RandomString("test-container-", 8) _, err = containers.Create(client, cName, nil).Extract() th.AssertNoErr(t, err) // Defer deletion of the container until after testing. defer func() { t.Logf("Deleting container %s", cName) res := containers.Delete(client, cName) th.AssertNoErr(t, res.Err) }() // Create a slice of buffers to hold the test object content. oContents1 := make([]*bytes.Buffer, numObjects) for i := 0; i < numObjects; i++ { oContents1[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10))) createOpts := objects.CreateOpts{ Content: oContents1[i], } res := objects.Create(client, cName, oNames1[i], createOpts) th.AssertNoErr(t, res.Err) } // Delete the objects after testing. defer func() { for i := 0; i < numObjects; i++ { t.Logf("Deleting object %s", oNames1[i]) res := objects.Delete(client, cName, oNames1[i], nil) th.AssertNoErr(t, res.Err) } }() oContents2 := make([]*bytes.Buffer, numObjects) for i := 0; i < numObjects; i++ { oContents2[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10))) createOpts := objects.CreateOpts{ Content: oContents2[i], } res := objects.Create(client, cName, oNames2[i], createOpts) th.AssertNoErr(t, res.Err) } // Delete the objects after testing. defer func() { for i := 0; i < numObjects; i++ { t.Logf("Deleting object %s", oNames2[i]) res := objects.Delete(client, cName, oNames2[i], nil) th.AssertNoErr(t, res.Err) } }() listOpts := objects.ListOpts{ Full: true, Delimiter: "/", } allPages, err := objects.List(client, cName, listOpts).AllPages() if err != nil { t.Fatal(err) } allObjects, err := objects.ExtractNames(allPages) if err != nil { t.Fatal(err) } t.Logf("%#v\n", allObjects) expected := []string{cSubdir1, cSubdir2} for _, e := range expected { var valid bool for _, a := range allObjects { if e+"/" == a { valid = true } } if !valid { t.Fatalf("could not find %s in results", e) } } listOpts = objects.ListOpts{ Full: true, Delimiter: "/", Prefix: cSubdir2, } allPages, err = objects.List(client, cName, listOpts).AllPages() if err != nil { t.Fatal(err) } allObjects, err = objects.ExtractNames(allPages) if err != nil { t.Fatal(err) } th.AssertEquals(t, allObjects[0], cSubdir2+"/") t.Logf("%#v\n", allObjects) } pkg.go000066400000000000000000000000131335005366400340350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/objectstorage/v1package v1 golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/orchestration/000077500000000000000000000000001335005366400325155ustar00rootroot00000000000000v1/000077500000000000000000000000001335005366400327645ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/orchestrationbuildinfo_test.go000066400000000000000000000007221335005366400363260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/orchestration/v1// +build acceptance package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/buildinfo" th "github.com/gophercloud/gophercloud/testhelper" ) func TestBuildInfo(t *testing.T) { client, err := clients.NewOrchestrationV1Client() th.AssertNoErr(t, err) bi, err := buildinfo.Get(client).Extract() th.AssertNoErr(t, err) t.Logf("retrieved build info: %+v\n", bi) } orchestration.go000066400000000000000000000057501335005366400362060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/orchestration/v1package v1 import ( "fmt" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks" th "github.com/gophercloud/gophercloud/testhelper" ) const basicTemplateResourceName = "secgroup_1" const basicTemplate = ` { "heat_template_version": "2013-05-23", "description": "Simple template to test heat commands", "resources": { "secgroup_1": { "type": "OS::Neutron::SecurityGroup", "properties": { "description": "Gophercloud test", "name": "secgroup_1" } } } } ` const validateTemplate = ` { "heat_template_version": "2013-05-23", "description": "Simple template to test heat commands", "parameters": { "flavor": { "default": "m1.tiny", "type": "string" } }, "resources": { "hello_world": { "type": "OS::Nova::Server", "properties": { "key_name": "heat_key", "flavor": { "get_param": "flavor" }, "image": "ad091b52-742f-469e-8f3c-fd81cadf0743", "user_data": "#!/bin/bash -xv\necho \"hello world\" > /root/hello-world.txt\n" } } } } ` // CreateStack will create a heat stack with a randomly generated name. // An error will be returned if the stack failed to be created. func CreateStack(t *testing.T, client *gophercloud.ServiceClient) (*stacks.RetrievedStack, error) { stackName := tools.RandomString("ACCPTEST", 8) t.Logf("Attempting to create stack %s", stackName) template := new(stacks.Template) template.Bin = []byte(basicTemplate) createOpts := stacks.CreateOpts{ Name: stackName, Timeout: 60, TemplateOpts: template, DisableRollback: gophercloud.Disabled, } stack, err := stacks.Create(client, createOpts).Extract() th.AssertNoErr(t, err) if err := WaitForStackStatus(client, stackName, stack.ID, "CREATE_COMPLETE"); err != nil { return nil, err } newStack, err := stacks.Get(client, stackName, stack.ID).Extract() return newStack, err } // DeleteStack deletes a stack via its ID. // A fatal error will occur if the stack failed to be deleted. This works // best when used as a deferred function. func DeleteStack(t *testing.T, client *gophercloud.ServiceClient, stackName, stackID string) { t.Logf("Attempting to delete stack %s (%s)", stackName, stackID) err := stacks.Delete(client, stackName, stackID).ExtractErr() if err != nil { t.Fatalf("Failed to delete stack %s: %s", stackID, err) } t.Logf("Deleted stack: %s", stackID) } // WaitForStackStatus will wait until a stack has reached a certain status. func WaitForStackStatus(client *gophercloud.ServiceClient, stackName, stackID, status string) error { return tools.WaitFor(func() (bool, error) { latest, err := stacks.Get(client, stackName, stackID).Extract() if err != nil { return false, err } if latest.Status == status { return true, nil } if latest.Status == "ERROR" { return false, fmt.Errorf("Stack in ERROR state") } return false, nil }) } stackevents_test.go000066400000000000000000000021001335005366400366750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/orchestration/v1// +build acceptance package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" //"github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackevents" th "github.com/gophercloud/gophercloud/testhelper" ) func TestStackEvents(t *testing.T) { client, err := clients.NewOrchestrationV1Client() th.AssertNoErr(t, err) stack, err := CreateStack(t, client) th.AssertNoErr(t, err) defer DeleteStack(t, client, stack.Name, stack.ID) allPages, err := stackevents.List(client, stack.Name, stack.ID, nil).AllPages() th.AssertNoErr(t, err) allEvents, err := stackevents.ExtractEvents(allPages) th.AssertNoErr(t, err) th.AssertEquals(t, len(allEvents), 4) /* allPages is currently broke allPages, err = stackevents.ListResourceEvents(client, stack.Name, stack.ID, basicTemplateResourceName, nil).AllPages() th.AssertNoErr(t, err) allEvents, err = stackevents.ExtractEvents(allPages) th.AssertNoErr(t, err) for _, v := range allEvents { tools.PrintResource(t, v) } */ } stackresources_test.go000066400000000000000000000022751335005366400374200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/orchestration/v1// +build acceptance package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackresources" th "github.com/gophercloud/gophercloud/testhelper" ) func TestStackResources(t *testing.T) { client, err := clients.NewOrchestrationV1Client() th.AssertNoErr(t, err) stack, err := CreateStack(t, client) th.AssertNoErr(t, err) defer DeleteStack(t, client, stack.Name, stack.ID) resource, err := stackresources.Get(client, stack.Name, stack.ID, basicTemplateResourceName).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, resource) metadata, err := stackresources.Metadata(client, stack.Name, stack.ID, basicTemplateResourceName).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, metadata) allPages, err := stackresources.List(client, stack.Name, stack.ID, nil).AllPages() th.AssertNoErr(t, err) allResources, err := stackresources.ExtractResources(allPages) th.AssertNoErr(t, err) var found bool for _, v := range allResources { if v.Name == basicTemplateResourceName { found = true } } th.AssertEquals(t, found, true) } stacks_test.go000066400000000000000000000024331335005366400356440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/orchestration/v1// +build acceptance package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks" th "github.com/gophercloud/gophercloud/testhelper" ) func TestStacksCRUD(t *testing.T) { client, err := clients.NewOrchestrationV1Client() th.AssertNoErr(t, err) createdStack, err := CreateStack(t, client) th.AssertNoErr(t, err) defer DeleteStack(t, client, createdStack.Name, createdStack.ID) tools.PrintResource(t, createdStack) tools.PrintResource(t, createdStack.CreationTime) template := new(stacks.Template) template.Bin = []byte(basicTemplate) updateOpts := stacks.UpdateOpts{ TemplateOpts: template, Timeout: 20, } err = stacks.Update(client, createdStack.Name, createdStack.ID, updateOpts).ExtractErr() th.AssertNoErr(t, err) err = WaitForStackStatus(client, createdStack.Name, createdStack.ID, "UPDATE_COMPLETE") th.AssertNoErr(t, err) var found bool allPages, err := stacks.List(client, nil).AllPages() th.AssertNoErr(t, err) allStacks, err := stacks.ExtractStacks(allPages) th.AssertNoErr(t, err) for _, v := range allStacks { if v.ID == createdStack.ID { found = true } } th.AssertEquals(t, found, true) } stacktemplates_test.go000066400000000000000000000017771335005366400374120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/orchestration/v1// +build acceptance package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacktemplates" th "github.com/gophercloud/gophercloud/testhelper" ) func TestStackTemplatesCRUD(t *testing.T) { client, err := clients.NewOrchestrationV1Client() th.AssertNoErr(t, err) stack, err := CreateStack(t, client) th.AssertNoErr(t, err) defer DeleteStack(t, client, stack.Name, stack.ID) tmpl, err := stacktemplates.Get(client, stack.Name, stack.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, tmpl) } func TestStackTemplatesValidate(t *testing.T) { client, err := clients.NewOrchestrationV1Client() th.AssertNoErr(t, err) validateOpts := stacktemplates.ValidateOpts{ Template: validateTemplate, } validatedTemplate, err := stacktemplates.Validate(client, validateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, validatedTemplate) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/pkg.go000066400000000000000000000000501335005366400307340ustar00rootroot00000000000000// +build acceptance package openstack sharedfilesystems/000077500000000000000000000000001335005366400333105ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstackv2/000077500000000000000000000000001335005366400336375ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/sharedfilesystemsavailabilityzones_test.go000066400000000000000000000013571335005366400407640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/sharedfilesystems/v2package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/availabilityzones" ) func TestAvailabilityZonesList(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create shared file system client: %v", err) } allPages, err := availabilityzones.List(client).AllPages() if err != nil { t.Fatalf("Unable to list availability zones: %v", err) } zones, err := availabilityzones.ExtractAvailabilityZones(allPages) if err != nil { t.Fatalf("Unable to extract availability zones: %v", err) } if len(zones) == 0 { t.Fatal("At least one availability zone was expected to be found") } } pkg.go000066400000000000000000000001351335005366400347460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/sharedfilesystems/v2// The v2 package contains acceptance tests for the Openstack Manila V2 service. package v2 securityservices.go000066400000000000000000000043401335005366400376020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/sharedfilesystems/v2package v2 import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/securityservices" ) // CreateSecurityService will create a security service with a random name. An // error will be returned if the security service was unable to be created. func CreateSecurityService(t *testing.T, client *gophercloud.ServiceClient) (*securityservices.SecurityService, error) { if testing.Short() { t.Skip("Skipping test that requires share network creation in short mode.") } securityServiceName := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create security service: %s", securityServiceName) createOpts := securityservices.CreateOpts{ Name: securityServiceName, Type: "kerberos", } securityService, err := securityservices.Create(client, createOpts).Extract() if err != nil { return securityService, err } return securityService, nil } // DeleteSecurityService will delete a security service. An error will occur if // the security service was unable to be deleted. func DeleteSecurityService(t *testing.T, client *gophercloud.ServiceClient, securityService *securityservices.SecurityService) { err := securityservices.Delete(client, securityService.ID).ExtractErr() if err != nil { t.Fatalf("Failed to delete security service %s: %v", securityService.ID, err) } t.Logf("Deleted security service: %s", securityService.ID) } // PrintSecurityService will print a security service and all of its attributes. func PrintSecurityService(t *testing.T, securityService *securityservices.SecurityService) { t.Logf("ID: %s", securityService.ID) t.Logf("Project ID: %s", securityService.ProjectID) t.Logf("Domain: %s", securityService.Domain) t.Logf("Status: %s", securityService.Status) t.Logf("Type: %s", securityService.Type) t.Logf("Name: %s", securityService.Name) t.Logf("Description: %s", securityService.Description) t.Logf("DNS IP: %s", securityService.DNSIP) t.Logf("User: %s", securityService.User) t.Logf("Password: %s", securityService.Password) t.Logf("Server: %s", securityService.Server) t.Logf("Created at: %v", securityService.CreatedAt) t.Logf("Updated at: %v", securityService.UpdatedAt) } securityservices_test.go000066400000000000000000000103741335005366400406450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/sharedfilesystems/v2package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/securityservices" ) func TestSecurityServiceCreateDelete(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create shared file system client: %v", err) } securityService, err := CreateSecurityService(t, client) if err != nil { t.Fatalf("Unable to create security service: %v", err) } newSecurityService, err := securityservices.Get(client, securityService.ID).Extract() if err != nil { t.Errorf("Unable to retrieve the security service: %v", err) } if newSecurityService.Name != securityService.Name { t.Fatalf("Security service name was expeted to be: %s", securityService.Name) } PrintSecurityService(t, securityService) defer DeleteSecurityService(t, client, securityService) } func TestSecurityServiceList(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } allPages, err := securityservices.List(client, securityservices.ListOpts{}).AllPages() if err != nil { t.Fatalf("Unable to retrieve security services: %v", err) } allSecurityServices, err := securityservices.ExtractSecurityServices(allPages) if err != nil { t.Fatalf("Unable to extract security services: %v", err) } for _, securityService := range allSecurityServices { PrintSecurityService(t, &securityService) } } // The test creates 2 security services and verifies that only the one(s) with // a particular name are being listed func TestSecurityServiceListFiltering(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } securityService, err := CreateSecurityService(t, client) if err != nil { t.Fatalf("Unable to create security service: %v", err) } defer DeleteSecurityService(t, client, securityService) securityService, err = CreateSecurityService(t, client) if err != nil { t.Fatalf("Unable to create security service: %v", err) } defer DeleteSecurityService(t, client, securityService) options := securityservices.ListOpts{ Name: securityService.Name, } allPages, err := securityservices.List(client, options).AllPages() if err != nil { t.Fatalf("Unable to retrieve security services: %v", err) } allSecurityServices, err := securityservices.ExtractSecurityServices(allPages) if err != nil { t.Fatalf("Unable to extract security services: %v", err) } for _, listedSecurityService := range allSecurityServices { if listedSecurityService.Name != securityService.Name { t.Fatalf("The name of the security service was expected to be %s", securityService.Name) } PrintSecurityService(t, &listedSecurityService) } } // Create a security service and update the name and description. Get the security // service and verify that the name and description have been updated func TestSecurityServiceUpdate(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create shared file system client: %v", err) } securityService, err := CreateSecurityService(t, client) if err != nil { t.Fatalf("Unable to create security service: %v", err) } options := securityservices.UpdateOpts{ Name: "NewName", Description: "New security service description", Type: "ldap", } _, err = securityservices.Update(client, securityService.ID, options).Extract() if err != nil { t.Errorf("Unable to update the security service: %v", err) } newSecurityService, err := securityservices.Get(client, securityService.ID).Extract() if err != nil { t.Errorf("Unable to retrieve the security service: %v", err) } if newSecurityService.Name != options.Name { t.Fatalf("Security service name was expeted to be: %s", options.Name) } if newSecurityService.Description != options.Description { t.Fatalf("Security service description was expeted to be: %s", options.Description) } if newSecurityService.Type != options.Type { t.Fatalf("Security service type was expected to be: %s", options.Type) } PrintSecurityService(t, securityService) defer DeleteSecurityService(t, client, securityService) } sharenetworks.go000066400000000000000000000043111335005366400370640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/sharedfilesystems/v2package v2 import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharenetworks" ) // CreateShareNetwork will create a share network with a random name. An // error will be returned if the share network was unable to be created. func CreateShareNetwork(t *testing.T, client *gophercloud.ServiceClient) (*sharenetworks.ShareNetwork, error) { if testing.Short() { t.Skip("Skipping test that requires share network creation in short mode.") } shareNetworkName := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create share network: %s", shareNetworkName) createOpts := sharenetworks.CreateOpts{ Name: shareNetworkName, Description: "This is a shared network", } shareNetwork, err := sharenetworks.Create(client, createOpts).Extract() if err != nil { return shareNetwork, err } return shareNetwork, nil } // DeleteShareNetwork will delete a share network. An error will occur if // the share network was unable to be deleted. func DeleteShareNetwork(t *testing.T, client *gophercloud.ServiceClient, shareNetwork *sharenetworks.ShareNetwork) { err := sharenetworks.Delete(client, shareNetwork.ID).ExtractErr() if err != nil { t.Fatalf("Failed to delete share network %s: %v", shareNetwork.ID, err) } t.Logf("Deleted share network: %s", shareNetwork.ID) } // PrintShareNetwork will print a share network and all of its attributes. func PrintShareNetwork(t *testing.T, sharenetwork *sharenetworks.ShareNetwork) { t.Logf("ID: %s", sharenetwork.ID) t.Logf("Project ID: %s", sharenetwork.ProjectID) t.Logf("Neutron network ID: %s", sharenetwork.NeutronNetID) t.Logf("Neutron sub-network ID: %s", sharenetwork.NeutronSubnetID) t.Logf("Nova network ID: %s", sharenetwork.NovaNetID) t.Logf("Network type: %s", sharenetwork.NetworkType) t.Logf("Segmentation ID: %d", sharenetwork.SegmentationID) t.Logf("CIDR: %s", sharenetwork.CIDR) t.Logf("IP version: %d", sharenetwork.IPVersion) t.Logf("Name: %s", sharenetwork.Name) t.Logf("Description: %s", sharenetwork.Description) t.Logf("Created at: %v", sharenetwork.CreatedAt) t.Logf("Updated at: %v", sharenetwork.UpdatedAt) } sharenetworks_test.go000066400000000000000000000144711335005366400401330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/sharedfilesystems/v2package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharenetworks" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestShareNetworkCreateDestroy(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create shared file system client: %v", err) } shareNetwork, err := CreateShareNetwork(t, client) if err != nil { t.Fatalf("Unable to create share network: %v", err) } newShareNetwork, err := sharenetworks.Get(client, shareNetwork.ID).Extract() if err != nil { t.Errorf("Unable to retrieve shareNetwork: %v", err) } if newShareNetwork.Name != shareNetwork.Name { t.Fatalf("Share network name was expeted to be: %s", shareNetwork.Name) } PrintShareNetwork(t, shareNetwork) defer DeleteShareNetwork(t, client, shareNetwork) } // Create a share network and update the name and description. Get the share // network and verify that the name and description have been updated func TestShareNetworkUpdate(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create shared file system client: %v", err) } shareNetwork, err := CreateShareNetwork(t, client) if err != nil { t.Fatalf("Unable to create share network: %v", err) } expectedShareNetwork, err := sharenetworks.Get(client, shareNetwork.ID).Extract() if err != nil { t.Errorf("Unable to retrieve shareNetwork: %v", err) } options := sharenetworks.UpdateOpts{ Name: "NewName", Description: "New share network description", } expectedShareNetwork.Name = options.Name expectedShareNetwork.Description = options.Description _, err = sharenetworks.Update(client, shareNetwork.ID, options).Extract() if err != nil { t.Errorf("Unable to update shareNetwork: %v", err) } updatedShareNetwork, err := sharenetworks.Get(client, shareNetwork.ID).Extract() if err != nil { t.Errorf("Unable to retrieve shareNetwork: %v", err) } // Update time has to be set in order to get the assert equal to pass expectedShareNetwork.UpdatedAt = updatedShareNetwork.UpdatedAt th.CheckDeepEquals(t, expectedShareNetwork, updatedShareNetwork) PrintShareNetwork(t, shareNetwork) defer DeleteShareNetwork(t, client, shareNetwork) } func TestShareNetworkListDetail(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } allPages, err := sharenetworks.ListDetail(client, sharenetworks.ListOpts{}).AllPages() if err != nil { t.Fatalf("Unable to retrieve share networks: %v", err) } allShareNetworks, err := sharenetworks.ExtractShareNetworks(allPages) if err != nil { t.Fatalf("Unable to extract share networks: %v", err) } for _, shareNetwork := range allShareNetworks { PrintShareNetwork(t, &shareNetwork) } } // The test creates 2 shared networks and verifies that only the one(s) with // a particular name are being listed func TestShareNetworkListFiltering(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } shareNetwork, err := CreateShareNetwork(t, client) if err != nil { t.Fatalf("Unable to create share network: %v", err) } defer DeleteShareNetwork(t, client, shareNetwork) shareNetwork, err = CreateShareNetwork(t, client) if err != nil { t.Fatalf("Unable to create share network: %v", err) } defer DeleteShareNetwork(t, client, shareNetwork) options := sharenetworks.ListOpts{ Name: shareNetwork.Name, } allPages, err := sharenetworks.ListDetail(client, options).AllPages() if err != nil { t.Fatalf("Unable to retrieve share networks: %v", err) } allShareNetworks, err := sharenetworks.ExtractShareNetworks(allPages) if err != nil { t.Fatalf("Unable to extract share networks: %v", err) } for _, listedShareNetwork := range allShareNetworks { if listedShareNetwork.Name != shareNetwork.Name { t.Fatalf("The name of the share network was expected to be %s", shareNetwork.Name) } PrintShareNetwork(t, &listedShareNetwork) } } func TestShareNetworkListPagination(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } shareNetwork, err := CreateShareNetwork(t, client) if err != nil { t.Fatalf("Unable to create share network: %v", err) } defer DeleteShareNetwork(t, client, shareNetwork) shareNetwork, err = CreateShareNetwork(t, client) if err != nil { t.Fatalf("Unable to create share network: %v", err) } defer DeleteShareNetwork(t, client, shareNetwork) count := 0 err = sharenetworks.ListDetail(client, sharenetworks.ListOpts{Offset: 0, Limit: 1}).EachPage(func(page pagination.Page) (bool, error) { count++ _, err := sharenetworks.ExtractShareNetworks(page) if err != nil { t.Fatalf("Failed to extract share networks: %v", err) return false, err } return true, nil }) if err != nil { t.Fatalf("Unable to retrieve share networks: %v", err) } if count < 2 { t.Fatal("Expected to get at least 2 pages") } } func TestShareNetworkAddRemoveSecurityService(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } securityService, err := CreateSecurityService(t, client) if err != nil { t.Fatalf("Unable to create security service: %v", err) } defer DeleteSecurityService(t, client, securityService) shareNetwork, err := CreateShareNetwork(t, client) if err != nil { t.Fatalf("Unable to create share network: %v", err) } defer DeleteShareNetwork(t, client, shareNetwork) options := sharenetworks.AddSecurityServiceOpts{ SecurityServiceID: securityService.ID, } _, err = sharenetworks.AddSecurityService(client, shareNetwork.ID, options).Extract() if err != nil { t.Errorf("Unable to add security service: %v", err) } removeOptions := sharenetworks.RemoveSecurityServiceOpts{ SecurityServiceID: securityService.ID, } _, err = sharenetworks.RemoveSecurityService(client, shareNetwork.ID, removeOptions).Extract() if err != nil { t.Errorf("Unable to remove security service: %v", err) } PrintShareNetwork(t, shareNetwork) } shares.go000066400000000000000000000105171335005366400354570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/sharedfilesystems/v2package v2 import ( "encoding/json" "fmt" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares" ) // CreateShare will create a share with a name, and a size of 1Gb. An // error will be returned if the share could not be created func CreateShare(t *testing.T, client *gophercloud.ServiceClient) (*shares.Share, error) { if testing.Short() { t.Skip("Skipping test that requres share creation in short mode.") } choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatalf("Unable to fetch environment information") } t.Logf("Share network id %s", choices.ShareNetworkID) createOpts := shares.CreateOpts{ Size: 1, Name: "My Test Share", ShareProto: "NFS", ShareNetworkID: choices.ShareNetworkID, } share, err := shares.Create(client, createOpts).Extract() if err != nil { return share, err } err = waitForStatus(client, share.ID, "available", 600) if err != nil { return share, err } return share, nil } // ListShares lists all shares that belong to this tenant's project. // An error will be returned if the shares could not be listed.. func ListShares(t *testing.T, client *gophercloud.ServiceClient) ([]shares.Share, error) { r, err := shares.ListDetail(client, &shares.ListOpts{}).AllPages() if err != nil { return nil, err } return shares.ExtractShares(r) } // GrantAccess will grant access to an existing share. A fatal error will occur if // this operation fails. func GrantAccess(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share) (*shares.AccessRight, error) { return shares.GrantAccess(client, share.ID, shares.GrantAccessOpts{ AccessType: "ip", AccessTo: "0.0.0.0/32", AccessLevel: "r", }).Extract() } // RevokeAccess will revoke an exisiting access of a share. A fatal error will occur // if this operation fails. func RevokeAccess(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share, accessRight *shares.AccessRight) error { return shares.RevokeAccess(client, share.ID, shares.RevokeAccessOpts{ AccessID: accessRight.ID, }).ExtractErr() } // GetAccessRightsSlice will retrieve all access rules assigned to a share. // A fatal error will occur if this operation fails. func GetAccessRightsSlice(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share) ([]shares.AccessRight, error) { return shares.ListAccessRights(client, share.ID).Extract() } // DeleteShare will delete a share. A fatal error will occur if the share // failed to be deleted. This works best when used as a deferred function. func DeleteShare(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share) { err := shares.Delete(client, share.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete share %s: %v", share.ID, err) } t.Logf("Deleted share: %s", share.ID) } // PrintShare prints some information of the share func PrintShare(t *testing.T, share *shares.Share) { asJSON, err := json.MarshalIndent(share, "", " ") if err != nil { t.Logf("Cannot print the contents of %s", share.ID) } t.Logf("Share %s", string(asJSON)) } // PrintAccessRight prints contents of an access rule func PrintAccessRight(t *testing.T, accessRight *shares.AccessRight) { asJSON, err := json.MarshalIndent(accessRight, "", " ") if err != nil { t.Logf("Cannot print access rule") } t.Logf("Access rule %s", string(asJSON)) } // ExtendShare extends the capacity of an existing share func ExtendShare(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share, newSize int) error { return shares.Extend(client, share.ID, &shares.ExtendOpts{NewSize: newSize}).ExtractErr() } // ShrinkShare shrinks the capacity of an existing share func ShrinkShare(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share, newSize int) error { return shares.Shrink(client, share.ID, &shares.ShrinkOpts{NewSize: newSize}).ExtractErr() } func waitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { current, err := shares.Get(c, id).Extract() if err != nil { return false, err } if current.Status == "error" { return true, fmt.Errorf("An error occurred") } if current.Status == status { return true, nil } return false, nil }) } shares_test.go000066400000000000000000000066761335005366400365310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/sharedfilesystems/v2package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares" ) func TestShareCreate(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a sharedfs client: %v", err) } share, err := CreateShare(t, client) if err != nil { t.Fatalf("Unable to create a share: %v", err) } defer DeleteShare(t, client, share) created, err := shares.Get(client, share.ID).Extract() if err != nil { t.Errorf("Unable to retrieve share: %v", err) } PrintShare(t, created) } func TestShareListDetail(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a sharedfs client: %v", err) } share, err := CreateShare(t, client) if err != nil { t.Fatalf("Unable to create a share: %v", err) } defer DeleteShare(t, client, share) ss, err := ListShares(t, client) if err != nil { t.Fatalf("Unable to list shares: %v", err) } for i := range ss { PrintShare(t, &ss[i]) } } func TestGrantAndRevokeAccess(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a sharedfs client: %v", err) } share, err := CreateShare(t, client) if err != nil { t.Fatalf("Unable to create a share: %v", err) } defer DeleteShare(t, client, share) accessRight, err := GrantAccess(t, client, share) if err != nil { t.Fatalf("Unable to grant access: %v", err) } PrintAccessRight(t, accessRight) if err = RevokeAccess(t, client, share, accessRight); err != nil { t.Fatalf("Unable to revoke access: %v", err) } } func TestListAccessRights(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a sharedfs client: %v", err) } share, err := CreateShare(t, client) if err != nil { t.Fatalf("Unable to create a share: %v", err) } defer DeleteShare(t, client, share) _, err = GrantAccess(t, client, share) if err != nil { t.Fatalf("Unable to grant access: %v", err) } rs, err := GetAccessRightsSlice(t, client, share) if err != nil { t.Fatalf("Unable to retrieve list of access rules for share %s: %v", share.ID, err) } if len(rs) != 1 { t.Fatalf("Unexpected number of access rules for share %s: got %d, expected 1", share.ID, len(rs)) } t.Logf("Share %s has %d access rule(s):", share.ID, len(rs)) for _, r := range rs { PrintAccessRight(t, &r) } } func TestExtendAndShrink(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a sharedfs client: %v", err) } share, err := CreateShare(t, client) if err != nil { t.Fatalf("Unable to create a share: %v", err) } defer DeleteShare(t, client, share) err = ExtendShare(t, client, share, 2) if err != nil { t.Fatalf("Unable to extend a share: %v", err) } // We need to wait till the Extend operation is done err = waitForStatus(client, share.ID, "available", 120) if err != nil { t.Fatalf("Share status error: %v", err) } t.Logf("Share %s successfuly extended", share.ID) err = ShrinkShare(t, client, share, 1) if err != nil { t.Fatalf("Unable to shrink a share: %v", err) } // We need to wait till the Shrink operation is done err = waitForStatus(client, share.ID, "available", 120) if err != nil { t.Fatalf("Share status error: %v", err) } t.Logf("Share %s successfuly shrunk", share.ID) } sharetypes.go000066400000000000000000000033271335005366400363620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/sharedfilesystems/v2package v2 import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharetypes" ) // CreateShareType will create a share type with a random name. An // error will be returned if the share type was unable to be created. func CreateShareType(t *testing.T, client *gophercloud.ServiceClient) (*sharetypes.ShareType, error) { if testing.Short() { t.Skip("Skipping test that requires share type creation in short mode.") } shareTypeName := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create share type: %s", shareTypeName) extraSpecsOps := sharetypes.ExtraSpecsOpts{ DriverHandlesShareServers: true, } createOpts := sharetypes.CreateOpts{ Name: shareTypeName, IsPublic: false, ExtraSpecs: extraSpecsOps, } shareType, err := sharetypes.Create(client, createOpts).Extract() if err != nil { return shareType, err } return shareType, nil } // DeleteShareType will delete a share type. An error will occur if // the share type was unable to be deleted. func DeleteShareType(t *testing.T, client *gophercloud.ServiceClient, shareType *sharetypes.ShareType) { err := sharetypes.Delete(client, shareType.ID).ExtractErr() if err != nil { t.Fatalf("Failed to delete share type %s: %v", shareType.ID, err) } t.Logf("Deleted share type: %s", shareType.ID) } // PrintShareType will print a share type and all of its attributes. func PrintShareType(t *testing.T, shareType *sharetypes.ShareType) { t.Logf("Name: %s", shareType.Name) t.Logf("ID: %s", shareType.ID) t.Logf("OS share type access is public: %t", shareType.IsPublic) t.Logf("Extra specs: %#v", shareType.ExtraSpecs) } sharetypes_test.go000066400000000000000000000105311335005366400374140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/sharedfilesystems/v2package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharetypes" ) func TestShareTypeCreateDestroy(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create shared file system client: %v", err) } shareType, err := CreateShareType(t, client) if err != nil { t.Fatalf("Unable to create share type: %v", err) } PrintShareType(t, shareType) defer DeleteShareType(t, client, shareType) } func TestShareTypeList(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } allPages, err := sharetypes.List(client, sharetypes.ListOpts{}).AllPages() if err != nil { t.Fatalf("Unable to retrieve share types: %v", err) } allShareTypes, err := sharetypes.ExtractShareTypes(allPages) if err != nil { t.Fatalf("Unable to extract share types: %v", err) } for _, shareType := range allShareTypes { PrintShareType(t, &shareType) } } func TestShareTypeGetDefault(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } shareType, err := sharetypes.GetDefault(client).Extract() if err != nil { t.Fatalf("Unable to retrieve the default share type: %v", err) } PrintShareType(t, shareType) } func TestShareTypeExtraSpecs(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create shared file system client: %v", err) } shareType, err := CreateShareType(t, client) if err != nil { t.Fatalf("Unable to create share type: %v", err) } options := sharetypes.SetExtraSpecsOpts{ ExtraSpecs: map[string]interface{}{"my_new_key": "my_value"}, } _, err = sharetypes.SetExtraSpecs(client, shareType.ID, options).Extract() if err != nil { t.Fatalf("Unable to set extra specs for Share type: %s", shareType.Name) } extraSpecs, err := sharetypes.GetExtraSpecs(client, shareType.ID).Extract() if err != nil { t.Fatalf("Unable to retrieve share type: %s", shareType.Name) } if extraSpecs["driver_handles_share_servers"] != "True" { t.Fatal("driver_handles_share_servers was expected to be true") } if extraSpecs["my_new_key"] != "my_value" { t.Fatal("my_new_key was expected to be equal to my_value") } err = sharetypes.UnsetExtraSpecs(client, shareType.ID, "my_new_key").ExtractErr() if err != nil { t.Fatalf("Unable to unset extra specs for Share type: %s", shareType.Name) } extraSpecs, err = sharetypes.GetExtraSpecs(client, shareType.ID).Extract() if err != nil { t.Fatalf("Unable to retrieve share type: %s", shareType.Name) } if _, ok := extraSpecs["my_new_key"]; ok { t.Fatalf("my_new_key was expected to be unset for Share type: %s", shareType.Name) } PrintShareType(t, shareType) defer DeleteShareType(t, client, shareType) } func TestShareTypeAccess(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create shared file system client: %v", err) } shareType, err := CreateShareType(t, client) if err != nil { t.Fatalf("Unable to create share type: %v", err) } options := sharetypes.AccessOpts{ Project: "9e3a5a44e0134445867776ef53a37605", } err = sharetypes.AddAccess(client, shareType.ID, options).ExtractErr() if err != nil { t.Fatalf("Unable to add a new access to a share type: %v", err) } access, err := sharetypes.ShowAccess(client, shareType.ID).Extract() if err != nil { t.Fatalf("Unable to retrieve the access details for a share type: %v", err) } expected := []sharetypes.ShareTypeAccess{{ShareTypeID: shareType.ID, ProjectID: options.Project}} if access[0] != expected[0] { t.Fatal("Share type access is not the same than expected") } err = sharetypes.RemoveAccess(client, shareType.ID, options).ExtractErr() if err != nil { t.Fatalf("Unable to remove an access from a share type: %v", err) } access, err = sharetypes.ShowAccess(client, shareType.ID).Extract() if err != nil { t.Fatalf("Unable to retrieve the access details for a share type: %v", err) } if len(access) > 0 { t.Fatalf("No access should be left for the share type: %s", shareType.Name) } PrintShareType(t, shareType) defer DeleteShareType(t, client, shareType) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/workflow/000077500000000000000000000000001335005366400315035ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/workflow/v2/000077500000000000000000000000001335005366400320325ustar00rootroot00000000000000crontrigger.go000066400000000000000000000022621335005366400346310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/workflow/v2package v2 import ( "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/workflow/v2/crontriggers" "github.com/gophercloud/gophercloud/openstack/workflow/v2/workflows" th "github.com/gophercloud/gophercloud/testhelper" ) // CreateCronTrigger creates a cron trigger for the given workflow. func CreateCronTrigger(t *testing.T, client *gophercloud.ServiceClient, workflow *workflows.Workflow) (*crontriggers.CronTrigger, error) { crontriggerName := tools.RandomString("crontrigger_", 5) t.Logf("Attempting to create cron trigger: %s", crontriggerName) firstExecution := time.Now().AddDate(1, 0, 0) createOpts := crontriggers.CreateOpts{ WorkflowID: workflow.ID, Name: crontriggerName, Pattern: "0 0 1 1 *", WorkflowInput: map[string]interface{}{ "msg": "Hello World!", }, FirstExecutionTime: &firstExecution, } crontrigger, err := crontriggers.Create(client, createOpts).Extract() if err != nil { return crontrigger, err } t.Logf("Cron trigger created: %s", crontriggerName) th.AssertEquals(t, crontrigger.Name, crontriggerName) return crontrigger, nil } crontriggers_test.go000066400000000000000000000007721335005366400360570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/workflow/v2package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" th "github.com/gophercloud/gophercloud/testhelper" ) func TestCronTriggersCreateDelete(t *testing.T) { client, err := clients.NewWorkflowV2Client() th.AssertNoErr(t, err) workflow, err := CreateWorkflow(t, client) th.AssertNoErr(t, err) trigger, err := CreateCronTrigger(t, client, workflow) th.AssertNoErr(t, err) tools.PrintResource(t, trigger) } execution.go000066400000000000000000000022261335005366400343070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/workflow/v2package v2 import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/workflow/v2/executions" "github.com/gophercloud/gophercloud/openstack/workflow/v2/workflows" th "github.com/gophercloud/gophercloud/testhelper" ) // CreateExecution creates an execution for the given workflow. func CreateExecution(t *testing.T, client *gophercloud.ServiceClient, workflow *workflows.Workflow) (*executions.Execution, error) { executionDescription := tools.RandomString("execution_", 5) t.Logf("Attempting to create execution: %s", executionDescription) createOpts := executions.CreateOpts{ ID: executionDescription, WorkflowID: workflow.ID, WorkflowNamespace: workflow.Namespace, Description: executionDescription, Input: map[string]interface{}{ "msg": "Hello World!", }, } execution, err := executions.Create(client, createOpts).Extract() if err != nil { return execution, err } t.Logf("Execution created: %s", executionDescription) th.AssertEquals(t, execution.Description, executionDescription) return execution, nil } executions_test.go000066400000000000000000000010371335005366400355300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/workflow/v2package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" th "github.com/gophercloud/gophercloud/testhelper" ) func TestExecutionsCreate(t *testing.T) { client, err := clients.NewWorkflowV2Client() th.AssertNoErr(t, err) workflow, err := CreateWorkflow(t, client) th.AssertNoErr(t, err) defer DeleteWorkflow(t, client, workflow) execution, err := CreateExecution(t, client, workflow) th.AssertNoErr(t, err) tools.PrintResource(t, execution) } workflow.go000066400000000000000000000051731335005366400341620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/workflow/v2package v2 import ( "fmt" "strings" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/workflow/v2/workflows" th "github.com/gophercloud/gophercloud/testhelper" ) // GetEchoWorkflowDefinition returns a simple workflow definition that does nothing except a simple "echo" command. func GetEchoWorkflowDefinition(workflowName string) string { return fmt.Sprintf(`--- version: '2.0' %s: description: Simple workflow example type: direct input: - msg tasks: test: action: std.echo output="<%% $.msg %%>"`, workflowName) } // CreateWorkflow creates a workflow on Mistral API. // The created workflow is a dummy workflow that performs a simple echo. func CreateWorkflow(t *testing.T, client *gophercloud.ServiceClient) (*workflows.Workflow, error) { workflowName := tools.RandomString("workflow_echo_", 5) definition := GetEchoWorkflowDefinition(workflowName) t.Logf("Attempting to create workflow: %s", workflowName) opts := &workflows.CreateOpts{ Namespace: "some-namespace", Scope: "private", Definition: strings.NewReader(definition), } workflowList, err := workflows.Create(client, opts).Extract() if err != nil { return nil, err } th.AssertEquals(t, 1, len(workflowList)) workflow := workflowList[0] t.Logf("Workflow created: %s", workflowName) th.AssertEquals(t, workflowName, workflow.Name) return &workflow, nil } // DeleteWorkflow deletes the given workflow. func DeleteWorkflow(t *testing.T, client *gophercloud.ServiceClient, workflow *workflows.Workflow) { err := workflows.Delete(client, workflow.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete workflows %s: %v", workflow.Name, err) } t.Logf("Deleted workflow: %s", workflow.Name) } // GetWorkflow gets a workflow. func GetWorkflow(t *testing.T, client *gophercloud.ServiceClient, id string) (*workflows.Workflow, error) { workflow, err := workflows.Get(client, id).Extract() if err != nil { t.Fatalf("Unable to get workflow %s: %v", id, err) } t.Logf("Workflow get: %s", workflow.Name) return workflow, err } // ListWorkflows lists the workflows. func ListWorkflows(t *testing.T, client *gophercloud.ServiceClient, opts workflows.ListOptsBuilder) ([]workflows.Workflow, error) { allPages, err := workflows.List(client, opts).AllPages() if err != nil { t.Fatalf("Unable to list workflows: %v", err) } workflowsList, err := workflows.ExtractWorkflows(allPages) if err != nil { t.Fatalf("Unable to extract workflows: %v", err) } t.Logf("Workflows list find, length: %d", len(workflowsList)) return workflowsList, err } workflows_test.go000066400000000000000000000017761335005366400354110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/openstack/workflow/v2package v2 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/workflow/v2/workflows" th "github.com/gophercloud/gophercloud/testhelper" ) func TestWorkflowsCreateGetDelete(t *testing.T) { client, err := clients.NewWorkflowV2Client() th.AssertNoErr(t, err) workflow, err := CreateWorkflow(t, client) th.AssertNoErr(t, err) defer DeleteWorkflow(t, client, workflow) workflowget, err := GetWorkflow(t, client, workflow.ID) th.AssertNoErr(t, err) tools.PrintResource(t, workflowget) } func TestWorkflowsList(t *testing.T) { client, err := clients.NewWorkflowV2Client() th.AssertNoErr(t, err) workflow, err := CreateWorkflow(t, client) th.AssertNoErr(t, err) defer DeleteWorkflow(t, client, workflow) list, err := ListWorkflows(t, client, &workflows.ListOpts{ Name: workflow.Name, }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, len(list)) tools.PrintResource(t, list) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/tools/000077500000000000000000000000001335005366400270025ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/tools/pkg.go000066400000000000000000000000161335005366400301070ustar00rootroot00000000000000package tools golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/acceptance/tools/tools.go000066400000000000000000000037431335005366400305000ustar00rootroot00000000000000package tools import ( "crypto/rand" "encoding/json" "errors" mrand "math/rand" "testing" "time" ) // ErrTimeout is returned if WaitFor takes longer than 300 second to happen. var ErrTimeout = errors.New("Timed out") // WaitFor polls a predicate function once per second to wait for a certain state to arrive. func WaitFor(predicate func() (bool, error)) error { for i := 0; i < 300; i++ { time.Sleep(1 * time.Second) satisfied, err := predicate() if err != nil { return err } if satisfied { return nil } } return ErrTimeout } // MakeNewPassword generates a new string that's guaranteed to be different than the given one. func MakeNewPassword(oldPass string) string { randomPassword := RandomString("", 16) for randomPassword == oldPass { randomPassword = RandomString("", 16) } return randomPassword } // RandomString generates a string of given length, but random content. // All content will be within the ASCII graphic character set. // (Implementation from Even Shaw's contribution on // http://stackoverflow.com/questions/12771930/what-is-the-fastest-way-to-generate-a-long-random-string-in-go). func RandomString(prefix string, n int) string { const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" var bytes = make([]byte, n) rand.Read(bytes) for i, b := range bytes { bytes[i] = alphanum[b%byte(len(alphanum))] } return prefix + string(bytes) } // RandomInt will return a random integer between a specified range. func RandomInt(min, max int) int { mrand.Seed(time.Now().Unix()) return mrand.Intn(max-min) + min } // Elide returns the first bit of its input string with a suffix of "..." if it's longer than // a comfortable 40 characters. func Elide(value string) string { if len(value) > 40 { return value[0:37] + "..." } return value } // PrintResource returns a resource as a readable structure func PrintResource(t *testing.T, resource interface{}) { b, _ := json.MarshalIndent(resource, "", " ") t.Logf(string(b)) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/auth_options.go000066400000000000000000000325701335005366400266260ustar00rootroot00000000000000package gophercloud /* AuthOptions stores information needed to authenticate to an OpenStack Cloud. You can populate one manually, or use a provider's AuthOptionsFromEnv() function to read relevant information from the standard environment variables. Pass one to a provider's AuthenticatedClient function to authenticate and obtain a ProviderClient representing an active session on that provider. Its fields are the union of those recognized by each identity implementation and provider. An example of manually providing authentication information: opts := gophercloud.AuthOptions{ IdentityEndpoint: "https://openstack.example.com:5000/v2.0", Username: "{username}", Password: "{password}", TenantID: "{tenant_id}", } provider, err := openstack.AuthenticatedClient(opts) An example of using AuthOptionsFromEnv(), where the environment variables can be read from a file, such as a standard openrc file: opts, err := openstack.AuthOptionsFromEnv() provider, err := openstack.AuthenticatedClient(opts) */ type AuthOptions struct { // IdentityEndpoint specifies the HTTP endpoint that is required to work with // the Identity API of the appropriate version. While it's ultimately needed by // all of the identity services, it will often be populated by a provider-level // function. // // The IdentityEndpoint is typically referred to as the "auth_url" or // "OS_AUTH_URL" in the information provided by the cloud operator. IdentityEndpoint string `json:"-"` // Username is required if using Identity V2 API. Consult with your provider's // control panel to discover your account's username. In Identity V3, either // UserID or a combination of Username and DomainID or DomainName are needed. Username string `json:"username,omitempty"` UserID string `json:"-"` Password string `json:"password,omitempty"` // At most one of DomainID and DomainName must be provided if using Username // with Identity V3. Otherwise, either are optional. DomainID string `json:"-"` DomainName string `json:"name,omitempty"` // The TenantID and TenantName fields are optional for the Identity V2 API. // The same fields are known as project_id and project_name in the Identity // V3 API, but are collected as TenantID and TenantName here in both cases. // Some providers allow you to specify a TenantName instead of the TenantId. // Some require both. Your provider's authentication policies will determine // how these fields influence authentication. // If DomainID or DomainName are provided, they will also apply to TenantName. // It is not currently possible to authenticate with Username and a Domain // and scope to a Project in a different Domain by using TenantName. To // accomplish that, the ProjectID will need to be provided as the TenantID // option. TenantID string `json:"tenantId,omitempty"` TenantName string `json:"tenantName,omitempty"` // AllowReauth should be set to true if you grant permission for Gophercloud to // cache your credentials in memory, and to allow Gophercloud to attempt to // re-authenticate automatically if/when your token expires. If you set it to // false, it will not cache these settings, but re-authentication will not be // possible. This setting defaults to false. // // NOTE: The reauth function will try to re-authenticate endlessly if left // unchecked. The way to limit the number of attempts is to provide a custom // HTTP client to the provider client and provide a transport that implements // the RoundTripper interface and stores the number of failed retries. For an // example of this, see here: // https://github.com/rackspace/rack/blob/1.0.0/auth/clients.go#L311 AllowReauth bool `json:"-"` // TokenID allows users to authenticate (possibly as another user) with an // authentication token ID. TokenID string `json:"-"` // Scope determines the scoping of the authentication request. Scope *AuthScope `json:"-"` // Authentication through Application Credentials requires supplying name, project and secret // For project we can use TenantID ApplicationCredentialID string `json:"-"` ApplicationCredentialName string `json:"-"` ApplicationCredentialSecret string `json:"-"` } // AuthScope allows a created token to be limited to a specific domain or project. type AuthScope struct { ProjectID string ProjectName string DomainID string DomainName string } // ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder // interface in the v2 tokens package func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { // Populate the request map. authMap := make(map[string]interface{}) if opts.Username != "" { if opts.Password != "" { authMap["passwordCredentials"] = map[string]interface{}{ "username": opts.Username, "password": opts.Password, } } else { return nil, ErrMissingInput{Argument: "Password"} } } else if opts.TokenID != "" { authMap["token"] = map[string]interface{}{ "id": opts.TokenID, } } else { return nil, ErrMissingInput{Argument: "Username"} } if opts.TenantID != "" { authMap["tenantId"] = opts.TenantID } if opts.TenantName != "" { authMap["tenantName"] = opts.TenantName } return map[string]interface{}{"auth": authMap}, nil } func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { type domainReq struct { ID *string `json:"id,omitempty"` Name *string `json:"name,omitempty"` } type projectReq struct { Domain *domainReq `json:"domain,omitempty"` Name *string `json:"name,omitempty"` ID *string `json:"id,omitempty"` } type userReq struct { ID *string `json:"id,omitempty"` Name *string `json:"name,omitempty"` Password string `json:"password,omitempty"` Domain *domainReq `json:"domain,omitempty"` } type passwordReq struct { User userReq `json:"user"` } type tokenReq struct { ID string `json:"id"` } type applicationCredentialReq struct { ID *string `json:"id,omitempty"` Name *string `json:"name,omitempty"` User *userReq `json:"user,omitempty"` Secret *string `json:"secret,omitempty"` } type identityReq struct { Methods []string `json:"methods"` Password *passwordReq `json:"password,omitempty"` Token *tokenReq `json:"token,omitempty"` ApplicationCredential *applicationCredentialReq `json:"application_credential,omitempty"` } type authReq struct { Identity identityReq `json:"identity"` } type request struct { Auth authReq `json:"auth"` } // Populate the request structure based on the provided arguments. Create and return an error // if insufficient or incompatible information is present. var req request var userRequest userReq if opts.Password == "" { if opts.TokenID != "" { // Because we aren't using password authentication, it's an error to also provide any of the user-based authentication // parameters. if opts.Username != "" { return nil, ErrUsernameWithToken{} } if opts.UserID != "" { return nil, ErrUserIDWithToken{} } if opts.DomainID != "" { return nil, ErrDomainIDWithToken{} } if opts.DomainName != "" { return nil, ErrDomainNameWithToken{} } // Configure the request for Token authentication. req.Auth.Identity.Methods = []string{"token"} req.Auth.Identity.Token = &tokenReq{ ID: opts.TokenID, } } else if opts.ApplicationCredentialID != "" { // Configure the request for ApplicationCredentialID authentication. // https://github.com/openstack/keystoneauth/blob/stable/rocky/keystoneauth1/identity/v3/application_credential.py#L48-L67 // There are three kinds of possible application_credential requests // 1. application_credential id + secret // 2. application_credential name + secret + user_id // 3. application_credential name + secret + username + domain_id / domain_name if opts.ApplicationCredentialSecret == "" { return nil, ErrAppCredMissingSecret{} } req.Auth.Identity.Methods = []string{"application_credential"} req.Auth.Identity.ApplicationCredential = &applicationCredentialReq{ ID: &opts.ApplicationCredentialID, Secret: &opts.ApplicationCredentialSecret, } } else if opts.ApplicationCredentialName != "" { if opts.ApplicationCredentialSecret == "" { return nil, ErrAppCredMissingSecret{} } // make sure that only one of DomainName or DomainID were provided if opts.DomainID == "" && opts.DomainName == "" { return nil, ErrDomainIDOrDomainName{} } req.Auth.Identity.Methods = []string{"application_credential"} if opts.DomainID != "" { userRequest = userReq{ Name: &opts.Username, Domain: &domainReq{ID: &opts.DomainID}, } } else if opts.DomainName != "" { userRequest = userReq{ Name: &opts.Username, Domain: &domainReq{Name: &opts.DomainName}, } } req.Auth.Identity.ApplicationCredential = &applicationCredentialReq{ Name: &opts.ApplicationCredentialName, User: &userRequest, Secret: &opts.ApplicationCredentialSecret, } } else { // If no password or token ID or ApplicationCredential are available, authentication can't continue. return nil, ErrMissingPassword{} } } else { // Password authentication. req.Auth.Identity.Methods = []string{"password"} // At least one of Username and UserID must be specified. if opts.Username == "" && opts.UserID == "" { return nil, ErrUsernameOrUserID{} } if opts.Username != "" { // If Username is provided, UserID may not be provided. if opts.UserID != "" { return nil, ErrUsernameOrUserID{} } // Either DomainID or DomainName must also be specified. if opts.DomainID == "" && opts.DomainName == "" { return nil, ErrDomainIDOrDomainName{} } if opts.DomainID != "" { if opts.DomainName != "" { return nil, ErrDomainIDOrDomainName{} } // Configure the request for Username and Password authentication with a DomainID. req.Auth.Identity.Password = &passwordReq{ User: userReq{ Name: &opts.Username, Password: opts.Password, Domain: &domainReq{ID: &opts.DomainID}, }, } } if opts.DomainName != "" { // Configure the request for Username and Password authentication with a DomainName. req.Auth.Identity.Password = &passwordReq{ User: userReq{ Name: &opts.Username, Password: opts.Password, Domain: &domainReq{Name: &opts.DomainName}, }, } } } if opts.UserID != "" { // If UserID is specified, neither DomainID nor DomainName may be. if opts.DomainID != "" { return nil, ErrDomainIDWithUserID{} } if opts.DomainName != "" { return nil, ErrDomainNameWithUserID{} } // Configure the request for UserID and Password authentication. req.Auth.Identity.Password = &passwordReq{ User: userReq{ID: &opts.UserID, Password: opts.Password}, } } } b, err := BuildRequestBody(req, "") if err != nil { return nil, err } if len(scope) != 0 { b["auth"].(map[string]interface{})["scope"] = scope } return b, nil } func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { // For backwards compatibility. // If AuthOptions.Scope was not set, try to determine it. // This works well for common scenarios. if opts.Scope == nil { opts.Scope = new(AuthScope) if opts.TenantID != "" { opts.Scope.ProjectID = opts.TenantID } else { if opts.TenantName != "" { opts.Scope.ProjectName = opts.TenantName opts.Scope.DomainID = opts.DomainID opts.Scope.DomainName = opts.DomainName } } } if opts.Scope.ProjectName != "" { // ProjectName provided: either DomainID or DomainName must also be supplied. // ProjectID may not be supplied. if opts.Scope.DomainID == "" && opts.Scope.DomainName == "" { return nil, ErrScopeDomainIDOrDomainName{} } if opts.Scope.ProjectID != "" { return nil, ErrScopeProjectIDOrProjectName{} } if opts.Scope.DomainID != "" { // ProjectName + DomainID return map[string]interface{}{ "project": map[string]interface{}{ "name": &opts.Scope.ProjectName, "domain": map[string]interface{}{"id": &opts.Scope.DomainID}, }, }, nil } if opts.Scope.DomainName != "" { // ProjectName + DomainName return map[string]interface{}{ "project": map[string]interface{}{ "name": &opts.Scope.ProjectName, "domain": map[string]interface{}{"name": &opts.Scope.DomainName}, }, }, nil } } else if opts.Scope.ProjectID != "" { // ProjectID provided. ProjectName, DomainID, and DomainName may not be provided. if opts.Scope.DomainID != "" { return nil, ErrScopeProjectIDAlone{} } if opts.Scope.DomainName != "" { return nil, ErrScopeProjectIDAlone{} } // ProjectID return map[string]interface{}{ "project": map[string]interface{}{ "id": &opts.Scope.ProjectID, }, }, nil } else if opts.Scope.DomainID != "" { // DomainID provided. ProjectID, ProjectName, and DomainName may not be provided. if opts.Scope.DomainName != "" { return nil, ErrScopeDomainIDOrDomainName{} } // DomainID return map[string]interface{}{ "domain": map[string]interface{}{ "id": &opts.Scope.DomainID, }, }, nil } else if opts.Scope.DomainName != "" { // DomainName return map[string]interface{}{ "domain": map[string]interface{}{ "name": &opts.Scope.DomainName, }, }, nil } return nil, nil } func (opts AuthOptions) CanReauth() bool { return opts.AllowReauth } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/doc.go000066400000000000000000000067211335005366400246560ustar00rootroot00000000000000/* Package gophercloud provides a multi-vendor interface to OpenStack-compatible clouds. The library has a three-level hierarchy: providers, services, and resources. Authenticating with Providers Provider structs represent the cloud providers that offer and manage a collection of services. You will generally want to create one Provider client per OpenStack cloud. Use your OpenStack credentials to create a Provider client. The IdentityEndpoint is typically refered to as "auth_url" or "OS_AUTH_URL" in information provided by the cloud operator. Additionally, the cloud may refer to TenantID or TenantName as project_id and project_name. Credentials are specified like so: opts := gophercloud.AuthOptions{ IdentityEndpoint: "https://openstack.example.com:5000/v2.0", Username: "{username}", Password: "{password}", TenantID: "{tenant_id}", } provider, err := openstack.AuthenticatedClient(opts) You may also use the openstack.AuthOptionsFromEnv() helper function. This function reads in standard environment variables frequently found in an OpenStack `openrc` file. Again note that Gophercloud currently uses "tenant" instead of "project". opts, err := openstack.AuthOptionsFromEnv() provider, err := openstack.AuthenticatedClient(opts) Service Clients Service structs are specific to a provider and handle all of the logic and operations for a particular OpenStack service. Examples of services include: Compute, Object Storage, Block Storage. In order to define one, you need to pass in the parent provider, like so: opts := gophercloud.EndpointOpts{Region: "RegionOne"} client := openstack.NewComputeV2(provider, opts) Resources Resource structs are the domain models that services make use of in order to work with and represent the state of API resources: server, err := servers.Get(client, "{serverId}").Extract() Intermediate Result structs are returned for API operations, which allow generic access to the HTTP headers, response body, and any errors associated with the network transaction. To turn a result into a usable resource struct, you must call the Extract method which is chained to the response, or an Extract function from an applicable extension: result := servers.Get(client, "{serverId}") // Attempt to extract the disk configuration from the OS-DCF disk config // extension: config, err := diskconfig.ExtractGet(result) All requests that enumerate a collection return a Pager struct that is used to iterate through the results one page at a time. Use the EachPage method on that Pager to handle each successive Page in a closure, then use the appropriate extraction method from that request's package to interpret that Page as a slice of results: err := servers.List(client, nil).EachPage(func (page pagination.Page) (bool, error) { s, err := servers.ExtractServers(page) if err != nil { return false, err } // Handle the []servers.Server slice. // Return "false" or an error to prematurely stop fetching new pages. return true, nil }) If you want to obtain the entire collection of pages without doing any intermediary processing on each page, you can use the AllPages method: allPages, err := servers.List(client, nil).AllPages() allServers, err := servers.ExtractServers(allPages) This top-level package contains utility functions and data types that are used throughout the provider and service packages. Of particular note for end users are the AuthOptions and EndpointOpts structs. */ package gophercloud golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/000077500000000000000000000000001335005366400245045ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/FAQ.md000066400000000000000000000065311335005366400254420ustar00rootroot00000000000000# Tips ## Implementing default logging and re-authentication attempts You can implement custom logging and/or limit re-auth attempts by creating a custom HTTP client like the following and setting it as the provider client's HTTP Client (via the `gophercloud.ProviderClient.HTTPClient` field): ```go //... // LogRoundTripper satisfies the http.RoundTripper interface and is used to // customize the default Gophercloud RoundTripper to allow for logging. type LogRoundTripper struct { rt http.RoundTripper numReauthAttempts int } // newHTTPClient return a custom HTTP client that allows for logging relevant // information before and after the HTTP request. func newHTTPClient() http.Client { return http.Client{ Transport: &LogRoundTripper{ rt: http.DefaultTransport, }, } } // RoundTrip performs a round-trip HTTP request and logs relevant information about it. func (lrt *LogRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) { glog.Infof("Request URL: %s\n", request.URL) response, err := lrt.rt.RoundTrip(request) if response == nil { return nil, err } if response.StatusCode == http.StatusUnauthorized { if lrt.numReauthAttempts == 3 { return response, fmt.Errorf("Tried to re-authenticate 3 times with no success.") } lrt.numReauthAttempts++ } glog.Debugf("Response Status: %s\n", response.Status) return response, nil } endpoint := "https://127.0.0.1/auth" pc := openstack.NewClient(endpoint) pc.HTTPClient = newHTTPClient() //... ``` ## Implementing custom objects OpenStack request/response objects may differ among variable names or types. ### Custom request objects To pass custom options to a request, implement the desired `OptsBuilder` interface. For example, to pass in ```go type MyCreateServerOpts struct { Name string Size int } ``` to `servers.Create`, simply implement the `servers.CreateOptsBuilder` interface: ```go func (o MyCreateServeropts) ToServerCreateMap() (map[string]interface{}, error) { return map[string]interface{}{ "name": o.Name, "size": o.Size, }, nil } ``` create an instance of your custom options object, and pass it to `servers.Create`: ```go // ... myOpts := MyCreateServerOpts{ Name: "s1", Size: "100", } server, err := servers.Create(computeClient, myOpts).Extract() // ... ``` ### Custom response objects Some OpenStack services have extensions. Extensions that are supported in Gophercloud can be combined to create a custom object: ```go // ... type MyVolume struct { volumes.Volume tenantattr.VolumeExt } var v struct { MyVolume `json:"volume"` } err := volumes.Get(client, volID).ExtractInto(&v) // ... ``` ## Overriding default `UnmarshalJSON` method For some response objects, a field may be a custom type or may be allowed to take on different types. In these cases, overriding the default `UnmarshalJSON` method may be necessary. To do this, declare the JSON `struct` field tag as "-" and create an `UnmarshalJSON` method on the type: ```go // ... type MyVolume struct { ID string `json: "id"` TimeCreated time.Time `json: "-"` } func (r *MyVolume) UnmarshalJSON(b []byte) error { type tmp MyVolume var s struct { tmp TimeCreated gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Volume(s.tmp) r.TimeCreated = time.Time(s.CreatedAt) return err } // ... ``` golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/MIGRATING.md000066400000000000000000000015261335005366400263530ustar00rootroot00000000000000# Compute ## Floating IPs * `github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingip` is now `github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips` * `floatingips.Associate` and `floatingips.Disassociate` have been removed. * `floatingips.DisassociateOpts` is now required to disassociate a Floating IP. ## Security Groups * `secgroups.AddServerToGroup` is now `secgroups.AddServer`. * `secgroups.RemoveServerFromGroup` is now `secgroups.RemoveServer`. ## Servers * `servers.Reboot` now requires a `servers.RebootOpts` struct: ```golang rebootOpts := servers.RebootOpts{ Type: servers.SoftReboot, } res := servers.Reboot(client, server.ID, rebootOpts) ``` # Identity ## V3 ### Tokens * `Token.ExpiresAt` is now of type `gophercloud.JSONRFC3339Milli` instead of `time.Time` golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/STYLEGUIDE.md000066400000000000000000000072531335005366400265130ustar00rootroot00000000000000 ## On Pull Requests - Please make sure to read our [contributing guide](/.github/CONTRIBUTING.md). - Before you start a PR there needs to be a Github issue and a discussion about it on that issue with a core contributor, even if it's just a 'SGTM'. - A PR's description must reference the issue it closes with a `For ` (e.g. For #293). - A PR's description must contain link(s) to the line(s) in the OpenStack source code (on Github) that prove(s) the PR code to be valid. Links to documentation are not good enough. The link(s) should be to a non-`master` branch. For example, a pull request implementing the creation of a Neutron v2 subnet might put the following link in the description: https://github.com/openstack/neutron/blob/stable/mitaka/neutron/api/v2/attributes.py#L749 From that link, a reviewer (or user) can verify the fields in the request/response objects in the PR. - A PR that is in-progress should have `[wip]` in front of the PR's title. When ready for review, remove the `[wip]` and ping a core contributor with an `@`. - Forcing PRs to be small can have the effect of users submitting PRs in a hierarchical chain, with one depending on the next. If a PR depends on another one, it should have a [Pending #PRNUM] prefix in the PR title. In addition, it will be the PR submitter's responsibility to remove the [Pending #PRNUM] tag once the PR has been updated with the merged, dependent PR. That will let reviewers know it is ready to review. - A PR should be small. Even if you intend on implementing an entire service, a PR should only be one route of that service (e.g. create server or get server, but not both). - Unless explicitly asked, do not squash commits in the middle of a review; only append. It makes it difficult for the reviewer to see what's changed from one review to the next. - See [#583](https://github.com/gophercloud/gophercloud/issues/583) as an example of a well-formatted issue which contains all relevant information we need to review and approve. ## On Code - In re design: follow as closely as is reasonable the code already in the library. Most operations (e.g. create, delete) admit the same design. - Unit tests and acceptance (integration) tests must be written to cover each PR. Tests for operations with several options (e.g. list, create) should include all the options in the tests. This will allow users to verify an operation on their own infrastructure and see an example of usage. - If in doubt, ask in-line on the PR. ### File Structure - The following should be used in most cases: - `requests.go`: contains all the functions that make HTTP requests and the types associated with the HTTP request (parameters for URL, body, etc) - `results.go`: contains all the response objects and their methods - `urls.go`: contains the endpoints to which the requests are made ### Naming - For methods on a type in `results.go`, the receiver should be named `r` and the variable into which it will be unmarshalled `s`. - Functions in `requests.go`, with the exception of functions that return a `pagination.Pager`, should be named returns of the name `r`. - Functions in `requests.go` that accept request bodies should accept as their last parameter an `interface` named `OptsBuilder` (eg `CreateOptsBuilder`). This `interface` should have at the least a method named `ToMap` (eg `ToPortCreateMap`). - Functions in `requests.go` that accept query strings should accept as their last parameter an `interface` named `OptsBuilder` (eg `ListOptsBuilder`). This `interface` should have at the least a method named `ToQuery` (eg `ToServerListQuery`). golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/assets/000077500000000000000000000000001335005366400260065ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/assets/openlab.png000066400000000000000000000531001335005366400301330ustar00rootroot00000000000000‰PNG  IHDRR ú¢’ IDATxœìÝ[”Õ™'ú”ywò”OdDÑ”ÐÈë,R^gÖ™Õ+Ó—´1e¹ÛV&LŸ™éV•$h»}©* vYê™ÕÓ rgÊ6ˆ‹¡RHh2äv÷:=sz”ÌœC‹’šŠHžêÉé÷ãÚç!v¢BèR¹÷Žûÿ·V,h7ùEVdÄÞ_|{o€ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ(:–‰ƒ!L†Bâÿ^« ,ÿÏʹÿu€Âvqä6æ^äÐ@üVþ»'·‘ý¯ µH‰ˆˆˆˆˆˆÌ³,#éàX&ÂDJüü™“%el–ÿ¾5ÀÀ;˜Â€gŸa’…ˆˆˆˆˆˆ¢ÅDJÎù3OŽ$ÀÚƒW$Ý ØÄ;\ûÌ&Wˆˆˆˆˆˆ(4L¤äÌðÚ'Crö¨ˆÍT%M¶Åš‚ à$·ôÞ7Þhˆˆˆˆˆˆ(K˜HÉá¿y²Ž qRG'—5….‚ÄJ·ô/¼˜£!"""""¢c"%ƒ†ŸjÔìŦ¨ÇO¢LY€.€c¥ÿ—s¬Ñd˜HÉÿº­É0y²Sð “*ö;LªÑå1‘’r~¹$O€FÌ¡¤à€Ž=Xðâ …ˆˆˆˆˆˆ’Љ”ò?Ýr$Nö!]+ì¤EP¥ò?ºqBDDDDDDÉÂDJŠ ?ݪ Hž4â$7<U*+¥ÿ±0Š9""""""J&RR`ø¿·ö(ÇJžu,—þoû!"""""Ê3&Rìƒÿ£ÕÀ&Áá;IÒÁŽ]õO nÜQô˜HI˜þ]«y(…¸ã¡‹raaùªÿ‹ """""¢Lª„­ àÔpOk¹t’Õ)DDDDDDÄDÊG|PiÍ[Vds¡¸Ž\årrÓI”N.tt?¨´9íCøU*‹T‚ꔫ\V§å)>¨>¬õmE2JÀòUý^Ÿ•YW¹ €%KTŸlØp«ˆÊúTŸ\¸ª âçQ‚å>‘2üì“ËÂ*­B8`媷äzî“0ÈÄFçƒÏ>YA°ºR%¤*hðÙ'÷`¡Ä¿%QîLÅ@œüÏ>¹ /€‚Â6À²¦¯zûÀ“(áºêíîUo¨  *7¤¿)D0¤¨ïþIN(EDDDDD”3¹¬Hñ¿ðDÂjc õn^°b¿ÅäIÔJop¸þçC­P)#H¦,Øoq¨Q^ä®"ÅÿÂe}X¢K „­KLÛoXb%^ö[\û­UX¢ Kx!ü­ °DÛÿ­¸Ï•ˆˆˆˆˆˆ¢‘«Š¿öDV!B™Å°l¿yÐ áØ¤Á~ó`@ÇÿÂK&¥5ý÷Ÿ÷kO8švï “gDDDDDD–›Š”áŸhè(À n#XhÚo¬2‰’lö›—`a7,t _P·,ô‡_|"Š¥³‰ˆˆˆˆˆ(&¹H¤ ¿ôD ÚÖ`xëX¦íÞÁNÜçHÛc÷zvïà¬e¡jMÁ3y=ÀBÖ‡_z‚“ÐeTæ)Ã/=Ñ0oø°€jéõƒÍÒÊ‘F¥7ºvX1|è€>“)DDDDDDÙ”ÙDÊðú' ÃëŸhÃBÃð0Ž»K¯sOÚ•^?8*½~p@<ƒ×HúÃëŸhD{FDDDDDD¶L&R†×?Q@0JÃàaGfK¯\(½Î*”,‘I±Ý:[Ðf2…ˆˆˆˆˆ([2—HÞðDSèc eL†6S˜.½v°õùP4J¯•^;ØÄf1…‘Ák§=¼É"""""¢¬ÈT"僞(X@ßÊGó,—~q°Zú«Pò ô‹ƒ] Ømƒ×Pû&Sˆˆˆˆˆˆ2!3‰”ᇠÂB_X( 0°„…êU¿8¸÷¹Q´®úÅAOX¨ Cׄ…öðÆÃ¸Ïˆˆˆˆˆˆôd"‘âßtXΉ"Ê€€mˆÝ¥W9¡l^•^=8*½z° ˆ¦¡k Ú>“)DDDDDD©–‰D €¶ÁJ”ްP-½zÈ‹û¤(~¥WuduÊÈÄõ mÿ&&SˆˆˆˆˆˆÒ*õ‰ïá6,ÔM-ml¿r¨i¿rˆó¡Ð‡ìW¹°P……‘ël -ïárä'BDDDDDDÚ®ˆ;þÞÃm XF×´Oê9R‚ø¿×ª¨Ö'DÑy÷q ÀÀþ×…Ì$¤ìŸø{W,«­û=ôý½‡«ö‰Cý興ˆˆˆˆ(*©M¤øõà ‡XÈJÅŸiÔìÅ&êçþ?"Ê02–€cºö™/ÊÂ`Ÿ84’É”ô¯½,¬úõûí.+ ˆˆˆˆˆˆÒ"•‰”áìáºe¡m e$ªv7ýUÃkŸ,ˆMÌØ â! ·Êrkù3Ov­))½wÀ9&-ö‰C#Íáìa@?™âègWK«L¦¥Aü]í o9\Æ8‰¢·€Œ$Q>ÕšÇÖ­+°h]‚uÀ­Ž)ô‡ÿæÉÕá§ZNÜß™®Òê¡&,t \‡eXhEDDDDDD¤"U‰”á-‡ V1®¸P7P-­¦;‰â_×rüÿ­uJL¡%®° â )Øêb §üëZ¸¿?]¥—5t ª1¼åð’ãQÈR•H…UXp´+Q,TK/§<‰òéVNá(ã@ʶ,´ýr«ηÒËÆ*S‡·®üˆˆˆˆˆˆ(IR“HÞz¸ ¢y˜ å¥t'Q†Ÿn5, oM¡`M)ÞÃO·N ?ÝÒ­0ŠUé%c•)íá­\™ˆˆˆˆˆ(ÉR‘H~åpæT¢¤<‰òÁ¿m50%Ú˜ÈÈV¶>þÿ6ÉýÊ” Rç¶Ã©þ.ˆˆˆˆˆˆ²,ñ‰ÿ¶Ça¡-,@s›-ý,åI”×*c më²´a eL!ýÃ|~v¨),t5¯Ó2ÀÉg‰ˆˆˆˆˆ’*ñ‰XÖª|S¯ó¦¿i¿xÈ>xs†¿ß*ˆ)ôÅ'€Œnõáï·–âþžµYhÂÂ@ózmø·s¾"""""¢$Jt"Å¿ýñ%@”mÙ~áP'òàM –|ÖM(%}[þA+Õs„Ø/¢ ˆæuÛöo܉þ ˆˆˆˆˆˆèR›HÞñxÀ¢æa:ö .éG¯á¶ê°PO@¢#Š-õC|ìh"˜ÜXUÁÊÀwADDDDD”5‰L¤ ïx¼ mY€Æ6°_x°÷¹1•«93ÊÃÏ´q¡Ë~áÁeaVçPÞñø|̧BDDDDDD[\w±ÀÑØ j&”x ÷´°ààFçø­‘£}”`yjÇÀ±af9áX•žÐÞñøô&m ïxÜ-=ÿ`ª'J&"""""ÊŠÄ%R†w>^½·ðÕÒóê «H ËÂ~ÍCŒ„À€néäBèßÉpO«,cnhÆù Òª_å.t …›Òó® ï|ü:è}-d$1HDDDDD”v‰ÚãßõxÁÀRÇ Yy{ÿÁgŸt`‰2,Å­ KL—N.t¢H¢@éäÂà*w¡ KTa‰‘rìSb_ñFdAXh\Óÿ.ñ!"""""J‚D%RÌCohH×>þàŠ¡Xb'€†Fç»sUÿÀìUý±Tæ\Õ?à Uaa¤?P÷?ÿd!ŽØM+=gdòÙEÿ.®âCDDDDD·Ä$Rü»s`‰Eê –ÈÆä²’ø„Ø#>!0ù·ôöØ¿‹ÒÛb Uµs€%*qŸƒ)öñ°Ä‚Æõ]€%ò4é0Q"%&‘h/õÚ´Ÿ}(ó¢lQQÛ-9 %û­ªUBe“±ÄÍ~ö¡y_êþÝUÌDCDDDDDD*‘HÞóXݲPÑX*vÙ~ö!7îó0iøÅ'ÊÖ °uì7zqÇÿ–ÏeOÜ¡›fYhZF׺n‘ˆˆˆˆˆˆ4$"‘½åa¥gZ2HbX(À¶c±Ä{ vïຠ璉9R¶*=óÐx¾UÎðžÇ8ñ,QLbO¤ ïyl zÌ&f‹QÊ*‰”ÒëÝXâ½ ï(œO¦†öŒ•žy¨ ½!>‹Ã{Ë\’‰ˆˆˆˆˆ( bM¤ ¿úXökb¹ôÌC™Xêøc¦PÀ0áæÅëvLÁU8Ÿ,ÓYŧ ‹&ƒ!"""""¢í‰¹«*æQP[Å^&‡ô|HL¾YZËë†Lá| b‰4 ¥gÁBSyˆùáWèÄ}DDDDDDy["Ev5Þª'gešP¨Ì‘Ü¡0ŠC•²¬ôÓ‡º\C°*…ˆˆˆˆˆ(bñU¤XXTœL°Ð-ýôÏÜ¢ŽŒ˜‚+¦€I7ÿæÃNܱ_ˆ˜Âu çãÅwš¿ƒÆðk¬J!"""""ŠÒq|èðë?t`¡¡¸û &ãI$õjŒ:€sb¡®°—g:Œ¤)ýôϼá׸ õê’EduÂå°‹µŠü×Ê–ÿùBËv¼³åßFþF/›s<%Œ]¬9&5¯Èÿ)Î¥ÕOnù÷ñuàÆKîÈßl@€ ½Éî“në}Ç0ð7z »X+#øÛ—|Rþ3[ÿF@ðwâ³!ìb­ŽàY“Üjñdñøm$>'#Û9ãk.¬)Ø–=O,‰@è I8RúÉ7zOÈݳÁ.ÖTV!¨F•˜–I”>Ò­%‘‹ ܉9ŽK²‹µ%L^eîú½ªâçµåQaJl[Ö²ÌMÂC"åKPÆÐ,ûfGqßÔÞrX=Ãha¶ôÒ¡®Ùˆ&7¼õð*„Ò°˜.½|È3O’ ÷ý@-q¸²t웉¹I¥™]¬5$OT¯[Óº±ÿž“jKEµ²+ Æoæ:Ij$ºÓF6iQñ±„м'´Ì†)]'²ôw»œ$RtÚq´=€…¤¶‰¢L¤(~V\:îW±ÿÝR›Hñ?ªÔºžÝùæ´Â~©5¼õpAy Š J/=Ø1Ò¶ ¿r¸6Ô;£ƒÒK‡v›Œ)éüÆP¯JY¶;ß\2M¾È·– $»#î!èH¯°#}NN;ÎÉ57æ8Cvœuž9$ëši/ɶ‹µy‹¤ÞËM!¸'ÉzåZ’)ò%ŒjŽ&ç"¸_y1Çñ'R~ƒôÝç<ÄÜ–5™H‰zÕÒÊe“¤Aé¥C]Xð„% °„…¶Ûá¶[´+ùø·® §„%ꊱŽEsØov >Áî~s‘ä‡]¬9²,rÉox;bü]¬µeò'×ä¸à>ò•D‚¤_ß.Öú[&<Î-9ŒgL¢è*8%©ck»X[EP‰’ä{¹i㊼u>bµ7îr¦‚à~•Ëû¾|ö§ñ>ç CmÙˆ+R¾¯ZòæÙ?ÏU5ʘûá%˜)Ûêâ$ääeö zŽ ðo¼ü›5áhr`Ú~áPîÞºûï7 þF£iwþ¼c.šìÊPÙwA‰k®~+òï×WCs ÍpcŽ#r²Óߊ;Ž êø½Ô̽%ç¦hƒ÷„±‚{‚sF%¼"¥ÎÉ—£—ˆ]£ªH‘‰”þ„Ÿ“T+îW‘´eSY‘â7¾_„Àä[þªQÎ+€š[Ac³óÇ~°‰y@8bíæ1‰vçÏ;ð”~'¬JÙù[Gú“(@pëòœrI” ª ¨PYMûÛIÈj2&QÂѰ‹µSò÷–h[&øä=áœä³! C"Móòy@é4®¨K]5dd‰ì–…I7X–gËï[vû…G–…#ÁWan3Épl#ËB"²Ê±±°¬ò[@Ùk|Ÿ É‹°‹µŠ]¬Bò‡ðLª`Qvz*q&&Q.«Ž Ôy)î@Â–à• ²¤Œ A—Øû%WI¹¬E÷„JÜä@._&H#gÉ”¬]o-Ù–MM/’DŠ×xÔ *"&¯NÀ‘(bL¸¨Ï‘6Ë¥çÌÚÍa"rxާXÍê” Ø2—FjnÎ ÆžL¾¡geÛ¶&Öœ¸ƒ ƒ|kÕˆ;Žœ(#¡U?[î L¢\šƒàÙÐNrR,NÄ¡‘Õ6Ðùä¤à^Üq„`“±¤‘ld¯"ßo+ Vú&yÛ¶¬°DêR3qèe´‘ïßtÜ‘“‹qÇ‘r™I°&‰n±ÁJjŸr¿ ›\]mÙMàÛ²‰¬,¿"üªº®Óþ¶g2’,°Ÿ}¨9¼ç1 Aog5'¯¨–žyˆå#–¡öw®xÍGœ¼þ~bœ„Òùñª_þûu8×ùs ¿<ø¤íbÍNÓR¦çiÁlçÙ“ÛÀo ÷r>‰soÐ ˆþmúøÍάlì§Š¬†0ýVÊEð zÇðq“ÀFp¯)ÃÜïDZ‹µ%£·dèxªL'V®ƒ“†{9ã¿ϳ¡!ßÚÏFµìhÈ%§«2á—Ø7é 3n'U ³bku£§úR25üÞ’]¬­ ¸Þœ>&î¶lË.Ö®KZ[6‚DŠòMä˜Ñ(2¤ôLò’)Š˜D¹§ýmÏk>2€Z‡«Ž`’âÜØRö_‰àã:a'x“vLe«ŒàÁTAøê†ü~šij0ËF~ÃÀ¡F*;²‘›òü×À„í:†|US˜L1UÐp"ë1y¿Ù‡à·¤›TÙok+qÝGd‰wÅÀ¡<oq»Iº'Ê¿•ƒs÷ƒ°Ÿ œ»'$æ{ÈyMÛ}6vò7¾fžý-¨Ww§Šüýv¢ü̘ڲHR2ÅÈ !.<ç†×|¤µñÌ#§ýí+µ‚Êá=5`)½­].ýô¡%cq|õ1•IW\Ì–žyˆîKðš4 Vž8pÚßÞm8œÄŠhE—.‚Yù7¼e‡º‚ ñ欀Ô4˜ U-ˆ­ã7©-È:ÂÆ2Bp¤¢‘/«Qúš‡q$=ÝxÒJÞ'¡?ùör\U)ròpûüÀ‚œ[ ñäßlë=!,©x6ØÅšJ{³êoô\Ó±P¸d»¨ýë¾åï]§ž4ñïú½jáD*¶lG'™bi¥Ø*Ü9R,ìQd6Ô¸2¢ôÌC@ R:Ùì2“(Û`¡«8élÙ»÷{N,1G,ä$Ї #~¥¿Ñ›õ7z0šþFϓǞ0-?Ó3ý9“Іp\ã T£Œìö7zKIïlåoôºþF¯éoô®D0q¡ÂǼ…NËdºÕ( þF¯šç$ ¼±ô7z &v×ùMì7ÒDdBM皘NKøðo6~6\ `!>20e„lÍ"¸æuÄr¿Ê£Û²¤Ìñòd³¢®ØÉæ°žíJód³tYÎß~{ˆ®b²,/csÛ0ŸDñ¼Å˜Žº#.DKþFoAGÚ3üå¤<€.Cçúõt˜RQqq1²ARE0yaÇðáS‘LÙò†KUÓßèåj˜ãåÈaMU¨'S 1­¢ 3‘zÇßèíNSRõ|2©²"Ÿ ³0Ÿde2…GÞ¿u†r”“þœË¢Ú²‰H¦„–HñîýÞxœçÄ»:ûT7~#e µ-k1dÛ Åýö"äMÔdƒ~„s ”ŽÁã*‘éñCÈd  ËPí4±ÉýÞ@–±š^ ¢€`õŽ$wœt~ß©Â5™dœÕ8D«Ã©^ è¿ÕNY¹VEð7ô º ®’F #ïã:ɔܯf§Û²±Þ¯Â¬HQ}àqÉãI$¡$ 1d˜ó·ßéBíÆSñïû^’;HZäRh ƒ‡\AB˾eLÓ0;pÃ.ÖgŒ¬BP}ƒ´œöJ”‹‘ •qçÉTc$éýTÂ.+Q.Mα¬¸{Å\$—'‡õ¨<Ï2—XÝJ&TÆ%ô¦4ä<D‰!ÛAªÇVÌEBªBjËÎÇÙ– -‘bYØkYÁÒ¸n¹˜]Ùa ¥-k1de¡«ò{BF²¬ÜTÚC0ÝB’Û[æ7Ø so Û -y­(γš1 s«”ã~«s ÅýtÞ^æÉ Ôî'…ˆïÅýŽäan9ùïn˜[%f1¦á[D—¢ZYVNxåenliËVa®-ÛŠ«-J"Å¿ïá Ê s:xö¾ã…Sv)ÍA“Á2ï„â÷œ¹á=²ZÁÔ°”.‚ I]CÇ ¬¶09gÆj×)î—›ŠFÙ™…¹RÙù¤uœd‚ŠD-sd2y¬ú»© årö(î—ùÄ꘬XÛ sçÜ–Ï[¢D÷õŽâîI|i”[²Ý½f^S[6¬Š”ŠÚn‚Õ(“JB# 1dœý£ï°œñœ6Ì, » WâIlÊÅÈNtfÆý;0—˜2E¥ÁãÉJ\‘¥²¦Þì$­ãä(î§:¯T^u÷³MqŽÂ>¡¬²–tòm¯‰kÀª~DDF¥!ñKÛ°å…PjÛ²a%RTß°ñ3!a©mY‹!'T:‰ŽßÃŽé@â"ÇmW43žP6õo*å9˜˜/£ž°jGa×p ©±¥JI·¬¿€d%Õ•ò˜PÓ!“ *×N$oxee’£°ëI³‘¤Ç–«î³¡ÌùR(Iäó.w Ò,Û²2SêÚ²!%RDE¡Dadÿè»n8ñdYÊA’Cˆ“Šßu%ŽhM“ã53B0JG?¢d0°”éX’VoqöyÇti";ÃUè'S*I„x›Ü¸H)7îBÉI§·Kv8M<V©F¤òÛV}ÉO0˜ü´-k<‘âß·\€Ú[ ×p(ù„s’C>¨¾eÍÊÃCw2Ìq%skC æÒ½ìeæÄE IDATþ®“2˜Li%$©–•{Wü6îLËâ½~R“)IªT#Êýo;‹ ¶eu_ºn[)Åýr[‚©' Õ Iˆ!ûì-zP›¡b4È7äÍÃÌf¹a½å¤£¡1Á'%€¡dJš“j™ý'P%îèò uN* þIù–¹Ä/äýJwÕ½ù¨Vñ1ŸH±PV¬PpÇB”5\…ß–ãÏ-%áí²ùf\7»ÜLÓÊ<ª =€"ËäS8¶$St:N”–ó³Mtùl˜Õºäb¤ð».–€õÍ#˜¯AÅú¥Ìi¦³”œÃª”l0ÐqÒIdQò4¡6ßÀª"ŠÖ,ÔÛ²¡W¥®HeÅê×lùàß÷pÁ²P°,`ÒÍ$•Ï·,­¡9' ¿±´¾UÖéÄ5åð†\ò7z€eC°:Iµ´Î•BD Ÿ‹:ó°*…ˆ"a -êýÊôÐÕÎçG™ßà §Ô“W&)%ÏÚþ}÷ýûv “*+\9¦ƒ›|ë¥ZâÊɪrMV#¸Š»W8¬#dljI5"ðá|)ªÏHV,Qdd[V5WP óeéDŠÊðÏ>úpnßOÊ¿o¹âß÷ð:€6RØ9>OÀºßÃmÿ¾åÔ®*¥›‰?÷ÝŠá8¦Ub,ŠôÓ™#ƒèŒÐlˆ4 †BÙÁ¶[º-@ýo¸Ïd DD—‘ȶ¬éDŠ£°«Q¶Á¿o¹àß·¼  G}‚×ÄT¤lÝÖýû–†Ë*Õߌc2ˆ0ÉJÕjˆeY HøpÂÑŽâî|ë˜-ª ‘çE  `û-Åäsòˆâîu9<Qè4«èæ"ù¨$̑‰f/ß[®ÃÂ: ê’H`"&)nûsË}ŽÕ)—b}ØSüŽÂU¥ú¶k½¥³JuXG%ÜÙ!"®âî{ÍEB ”Ö ÉIÏ 4æO2Ñåè¼ ¥-k,‘âÏ}ÇQÜ•o4.Ÿ[*øsKm@¬¢ 0¶™d4.Qĺ?·ÄÎÛ¥¹ û¤©¡¬ú÷?’ç f/F¾yì(îÎt¶¨&Õø:ÛT*y¯M9ù¼ì(îÎá=DÙ–U­J ¥-k°"Ey¸ ÄàÏ-–ц½˜ªBItEÊÖ­ˆUn±e8Ð QZ9 9¬ÇQܽc.’Ì9¦¸“š"«R<ÅÝ+Æ¡ÄËC: »²¢8T‡÷”™\%¢ˆ)G4…d.‘bÁQ©@°>â‹!#ü¹ïÖeEu9éml&…£˜÷ç¾Û÷ç¾Ëõù,¼£ð{KË ,ª7»çF¹8ÙV©,ÈŽe‡jC„ÕIÙ¤ZYÀŠâ Ð|ËËD;EFãeP!Œ•(P‘B[ùsßmX…ú²¯YSÀdÊÇ/«)ö(îwÂhÙ¤Z•R1Å®£¸_Å` ”2IÚP܉”ìP}6¨>¯‰ˆT%¦*Åäd³ŸTØÇ5øù©çÏ}·…`Ycú¨2€uî»i©¨ˆ‚§²“ÿ·³a„¢¢°ÏÈß詾QËÕïˆå ‘ó"¨\ Ž]¬9†Ã¡˜ÈaªCh¬ÌùüTj_1 Ñå$¦-k2‘ÂN®î;m@̇8LÆÄ vÛG^2½¸M@tBŒ¿ˆ¾?÷^gÕ¹…“A˜¦1„„I”m•·È³‘P¨VpULA±jA½í挃’ÁUØÇá<)D%¶l‚‡ö¨­Ò²PþÜ·ÛgRÙQÜÀnûè÷¦í£ß[°~ÏøwnýÞÈ>ú½Ž}ô{MWhb^2åÛ¹O¦ØO?:HÄŠMæU÷ã°žíSú®Â[J±r÷KÓê_tv±Ö†Þòµª¥Õ”\ªÏQ>ˆ(j®Â>ÆçI1Y‘¢’‘þ­ÁÏO%ÿþo5`¡at a #XX†…iûè#Í0’'s.©òÈnX¨Â‚køÜ °Ð÷ïÿ߀¨IzƒÇVÜÏ5DƹŠû%ýÚ¡ È7:žÂ®¼RÌ.Ö»XëC/‰ârXO&¹ŠûU Æ@D´ª‰_Çd&'›U\a&¿üû¿ÕÐ6\µ± ˆiûéG–짉uiiûéG\ûéGª€¨Â5[™Âd Oñ»K2GaŸœó¶AÎx®Â1%ƒ«°))dk»X[p úßeí€(qdrLåYªú„ˆH•j‘€Ñ6Ì&¦Àùócã?ð­2,£˺hÚO?ê<¦öÓº\Y}Ó‚™‰ÊV7ª8VJ ÙëÜVöq ÇLþ0ÙF «wöIz2–$9甃`ÙjS«¸ÉXJ¾&;æÃ "º8£7²‹5“ߌ&~%R„ÈwuÉ$ü¾UÐ7t¸€û©G;†ŽûéG;þßê"X™ÈD£®â?ð­–ýÔ£ Ž•:üÍ}È;€RI¤Pö(½Ñ±‹µ²¿ÑËägrU¢EI'ÎXŠÕ(Ùv“'Rø,1LÎãPA0'•ƒà;ÎB{¼bÜ2‡’Lþœžô¿¿$“s¤Ð¶‰U9q*4·€ÝiH¢ŒÙO=:²ŸztÀ‚¡a>óþn|]ð Kl ®ÆŠ=™ìÐ…L%ùÄÆröxŠûe¡Aÿ1v±Ö@0ô¥&Q.d…Õ(™ç)ì“ÉûAÔäüE-»X[Gpj!¸U︀àœÖåý–H‡JU­c2#‰ïþo:&Ž“Þýß\BT„ÐÙtì§¾¿Û~*yCy¶Ã~êÑ»Œt¿ !DÛ»ÿ›YyÈLBe,³c:ˆðâ …<…}òøË47‚™»d"· ž›!°%<•¸²:»X«È  ×Ì#›í´ iÛÅ_„RÔ“3’H–p„% ²å‰wÿ7ËJ†u-ØO}¿ià8±²Ÿúþ@1 ýŠ‚`t¾™T–x‡¿9­Î`žyq@©–Åê¤Ü=C&0Ðä¤Þt Y¼'„jË Z}äwå#ÞwI‡wqíÉUI¾°D[5áôáÑtžþÁJÜçbŠóôFªÂÍ料þÀ7˜ÙN7'îˆrÈ;€¸É·¢NÜq$X3«sâÐÇð﹂Ö:ò›@+pˆ¥Y¬‰”é§þ"7o7ÖøÆ<ô3öÍé§ÿ¢c œD'S ÿo¯?ð –—¦—wtir<¢¬áu}qM£×;Š«ŽÂ%—!ïÃLuzV\wDª %RT' ͇õþ¬ˆEÍIU›ÓOe/‰2$ÕD5˜@Wù;’ßs^ðwG‘c¢’(ÆÃy:qB”òEÄ)° å|LdSjÅ=´'/ZÐë€t¦ŸúaÇP,‰5ýÔÇ•):oDæ×ø3ÇLDDt¾­$ʾ€*“(DfÈ$J¬¼½/îˆT1‘²õûr DB@iÜé§~˜ú‰e·ëÃdŠê÷l9ªJÉ/îèÒ8Oe”w Ò0Íß:‘[’(¬è¼°“q@¤ÊL"E±Ã»~ÿC#ŸŸhZCz<1GÔqš~ê‡@,h|oõûÌ~© zr.©¼¸ Ê¡ìß+/¯ V[yfýÞ,çÉ 2C. Ý“(3Bpÿ%J%S)#Î×ðqës‡ôªQšÓO?–ËÍôÓ¯@ «Q•²?îsŸ¸Ž¿9RÄF©\ žé â$ qÇ“€e»9©,‘qm0Y})ËLÜ’†Ø[F)ÓO?6`åBtªQ°2}ôq7ú˜“D4¡ž¤k¬Ïtb::…¯&s,»X«ÄC Åþð¡øÉ·¥*<“q$œd9î8"ÔE0™ì•þFo‰"³ìbm@=î8lÅßè­Ä¥Zì/‚ ΑÂLÊVës P¿z€ÈSƒî‚¦i~¯JQúͽK¨Û£:&ŸÕ“³öqMA±cBm £·„`Âs7ÞHBáèh¸RáéÄQVÉ$õbÜq$ÔÁdÖy­$sT–ÎöLp…ɃÑGÔ¡ÚÁ[ž>úddo‡Öç”,ǶA§´rÞ2Bpã ˜Ê*¾é£O¬¬Ïصù-×N£7²‹5•]ËàÛI9q@‰ ”Hñ7z®á8Cž›+;BYH4 XmB9ÝÕ:·ò´qÞAú«y?"“…}Œ^)JÕ%edóÍ ^ 1˜>údÇd "+fât.óŸ“+órÿ€cÓGŸtÉð#Ìx>©ÂúÜFßg<”uI6Àä—=a’q…}8³~ö¨¼ÍÉE#X6öݸ㠢t±‹5AûZ—‹`×À±ˆ²Håe‡ÑÊ|“Ë{ ûd±#ˆõ¹Ôßd…^A±>w` À:‚Œ¹£x˜€þúܾ¬h L֏лï5Iâdáméù<…}²ø=„F.Ũ"èœQ¹¸,.ÑÅéé!úRe…èÂ4æGô †atŽs¤ŒmÖML¾ /Ì õ¹ùòúÜü)`sØ,¨Åø±­lžZŸ›o…w@,+ÆW_Ÿ›ÏdÂNqŽ7ŽH' ’).h$ò¨¢¸;Т1t…×ÑÈûªÎ³ÓL ]VEq?£m“)*²Z’¿Om·ð&˜]ŸÛß@0<&¬çüúÜü©õ¹ý¡$-¦¶\¨g37kº¬zÊ"Wq¿ŠÁ²Né¾Ë†]æ¨Þ“™"ÐQŒ©b<–ø9*;M}2Ñ ÙˆP‰±,'x£KS­BpMAñ’ŠÂ®#Óosˆˆ2Dõ»àoô<“e˜êïÌø¢ &çH)T dqî ÅêqÂlÀú}ZÐ6}Üm(Cm•KšþёԖ¹uÖïûSÇp81ŽZuN*¸ŠûenWT+w¸bO¶¨–Ÿ»†ã "ÊÉ/ý^Ç`(DY§º2®k2Àl"e Ò±[Ÿ›ÏVUJP‘1YÅ0šþÑ_­HY¿÷O b5ÂJ”U¦¬ßû'K&ÏI:Áª¶ÂïÍ%ÔÉ©vÚUo¬¹ +v**ûú½P*æ(6ª 5ã "¢ŒPíÏ„6?"QÖÈÅ%T~k£0æú‹{ùc@}®‡¤šü+D”â_^zqýÞ?1›(ÊÉ€ëL†‘NÜ„EvÚUÆ ;v±Æª”‹[TÜI” ‘oM+Š»óZ "º0[q?ÞW‰¶/1Õ(€ÁDÊôÑOm¨ÈLEÊú½ÿ©¢ø-›—q4c1½]yúoÿ³µ¥¶3sD%îë,dª V¥\€æ’Œ¬BÈÕj”.'B$"º(¥É/y_%ÚYYÝPÜ=”¶¬éåU&¡ËN¥€ŽâD³f'ïb_ŒCzÎß*ëÍÿX1{~pU†!FÁÊPJÒô°V½áU4Æ)gÙ<Ô+Ôø¶,#’Ø!"Ê1NÞM´}‹Šûš‡Èl"E`¤P´à!^ŽÊNÓíÿbìFºÞüÔÊaQ}zâ…âædŽ€£X ”š¶Þã)în´ *ídçYõáÓáÛ²Li+î§:Ñ7]œÊЧD¹£ù"(´ö‹áŠq2ÏC.„ؼNˆML¸¹fcu…¶n#!6W1 ˆ* „Øt5itÞ !6*q¼ßø?+&ãˆÒ°"5‰é˜â~e»X›7Iºé$–Tÿ”0ºs£0¡FDDD1Q}GŒEqÓC{<•ä2½ [Ïp {;ÙDÓWwþëÂtû¯ºÓí¿r§Ûµruç¿VeRÅS©¯PX¿÷O¨Mjœ¦{¶ÒéÌ×íb­a*jC½óì…51EKéÑêÆ„Å¡ õþ[èmYÓ)€Ú:ÍÃ1ÄD©JÂ5øù*1ͳuWwþÆ bŽ{XjeLê©&ËRùFY³*Zv±–•J¤m“sÄè §cç9ääl«‡p9¼‹ˆˆˆ¢–†¶l‰•ù+œõ{ÿ“B,ÑR]"ØÜç«T*àêÎS“Íf,ÄI…Ï©˜;QÄÿ]ÇFìQL ¥2‘"éÜ Vó4_Ь@ÐÒÃj” ×ü*ô*ñ˜P#""¢H¥¥-B"E¼£V‘•ª”X9}ŽÑçÐù* E8Þôßþg/†hU)+‡pôóL‘Õ7:°`"Š] zÃM»¬F!""¢(j˪NE1ó‰Wí¹Øc< Kî†J$Ázó?:€(OüÛ©®F[ ³êP@ßP,‰$<}èU ¸þFo² 5J»Xkhh&­ 5¶%ˆˆ²í“q`˜wIa°-뚉èÒŒ'R¦ÛÿŃÚD £Ä!îá&B¸*Ÿÿþ×ç&KŒqÂçxæNñ×ñ¨¨í–Ú‰f?äoôFÐïØ•íb-“Ã| =xt&“¦„0”DY–•`qËB˜ˆèbì¸H©$¿ÐUyéç˜"ÒØ– cŽ@m2Rg½ù’üø<µ!MæÎYíóýÛýˆ÷¿6W€@]á3<íóÛJ  6UHš©ÎbrBãøÈ±Ž®æaêÈØ0C8’Î3)2”Dø½%ýhŒø­Â>énGQžð~¥ÆQØ'ªÄ¼Òçäqa„­ ¶e#}J"EqR…­FYxhÙÅZf9ª‰ŒóxiäVšÞÙÅšck}‹†Ùäžt’ S03¾ºõ›œËÑhÖÓô›&¢L`â7dò¾ÞPÜÝ3Ée)õå’¿¹•¶lH)€FUJjß “Bn¦)Çbغ5„@ÿÌÝ qæî†¿m!àhӽ晶gî•c1CôD=Ïó£œÏßè-ÀÜx×y§Ò𳋵y犡C.s•žô±‹µ²áÈÉ­JRù´MBDt1²šOi¢Q™§Ë[„Zå‚q'ÛUܯe2ˆ¤ÊR[6ÄD N(î—âÒ&¥ŽnáÌÝûŒ½9›y¶3Ä@1–°¶c¦Î/ ®S‰cæÙŽg6Žh¼ßø÷ªãAWwþÆ3M¢Taî ƒƒ`¨Ïª]¬9†ŽiŒ]¬Uä-è—?Žu8/JºÈ¹PZ0Û!x“£;÷PXTÛu»Xk˜ „ˆè2T;s‹œÛéÒäË®yÅÝ£îd«Vƒ—å3>“d[¶ µeCK¤\Ýùj™Ùòû³ÑDDÀUÌ5T Çq$þÜɇ›7ó챎áó++Ä‘¤qÿ“R-ûT퀤‚ìøÍBòÙ­êÖíb­„„Ê–‡Nfg÷@9iŠˆL ,X‡zCòBFª ›å|: à;'D!vW&&Áƒü^V5aø…î¥ÉêÕ¶é|Ö^œ×–­öeÙÅZ#¤‡Õ Hù¤ÒD”šèñýŠÉ”-d¬õçŸÓsN§ Þ–Ã_RÍ.ÖêYoˆœHÁ ÅI5ö…W(fŽÿØ0Rèð†ÑÈkÆŸD;süÇ“'u殯)&`tÕ ¨¼¿ïþ:„(Lü¼«;O%ºƒdJˆÉ H¨ôíbm]NJZG>pÚv±öó;TBø˜DÂí0%êÁ“T²ƒ]Fø“=òŸ•?ç|#³Ë Çiú+ñ4Ìíbm€ ñ[ÍcæÅ€Ë{Ñ¥ù½‘]¬~ÀApÏkÛÅš‹à7˜åûÕ'<M>—c¾g-ÃL¬ ak‚ç–oà˜ª>‰àoT@4í×­×–"‘rjQãý}÷/\}ìéÄ|YÛ"”—›Ýɘyî§Í3wÜD›LiÎ<ÿŒñ 3wÜS‡Z9­Õ ÅýW{:­8Zd2eÑ$(æíbí¤Æ’k‹ˆæ!Ôñ7zI]Ú6dR«…ÔÎϵ-i˜Xö¢ü^Ç.ÖöÁ\ò)Š„YæØÅZÁÜ:éj›EkÁ aÇÐñ*ˆ>ñžv£g¼_5 £çÙÅÚ ÌMï Ûí”KId[6ì9Rpõ±§»ð‡‡¤n¢™çŸ@Oa^çÌmwWBŠ© !:ŠóÕL²Tgž¦Æy@ˆ}Šq©.C/ýŠ¿›ÜU£l%øUDSΩ´L,ÑŒâþ¶ÄO’È¿Å:²Ý8˜Nke‹„7|¶§`Þ¨\ 7 š„l‡ðÙ¯¤|ÿËКšw‰mˆžH ¨®Þ“ÎIg!àbS`â-ÄIvg^x¶ ¥¸¶;Ø=óü3nñŸùÊ]6E]1¶ÔU¤¼ÿõ¹ j¿›xƃ&‰¿ÑÉ›nØË¢9Š«ú„½ŠÈ¸ú Ö·1)ÑF¸CÁâ¶âoôvg¡‚@&‚¸lwüʪþˆè"äJ>ƒã‘˜éä³×üœ‘ùø¶l4‰#ŠoÖ÷¿6׈$Fƒ¬Mq€ÂÖ8{ËNXqͼøìŠ%0m ¸Šñ]hYË3/>»{æÅg½°b·ã„Wh3Ý«œ²áp!’7ßÝP_>u;* ûì1Ä]ÕnˆŸ‘ v±Ö@v‡wŒ ™J<ȉg;1‡A@Yþ~ˆè"äý×;Žœé$­ã-“:‰¬¨H°T´e#I¤\ý“£Ôo$‹æ"‰Æ5/ï*ïB>ßk^:î]óÒñ*„¨+ )Ä8Æ#Ä2„˜¾æ¥ãKaÆ|ö–;ÑPŒ3uÃ\ÞÿÚ\êãjSw¾aó7z£·úKÑ%Ýx"ÑÙ,TDd_Ü„¤ƒ4@TÉj³ÔUfÐÞ¸ JY„û2‡ÎIlÕ¢| õv¨ ©jËF4´BB@asþõ«÷V¢ŠÓ!º›b [cmööÐß^óòsî5/?WÝ›Ë*q^óòsW^óòsK×¼ü\èù¦Ø\Tü.‘Æe€…‹Š¿ïêŸMÝùFÅßè-!¨Nq Ú‹hŸKYAÐqæß2•¸0ÌCP…ÒLCDS|Ó·,‰#2b˼mL¦„+q+ºœO¶C;1‡‘d¤¬-Y"å÷~ú£ <Å9RW•²)úcp®ïIDAT„êp&@¨M`©D5ƈ¬Õo¯@ ¡:ÌåšÕç½è¢Õwöž{+€¨(þaŽDqºÈê”*‚7Dž¡Ãº û˜šÙ°Ûßèq|hú½ÌV¡œO΃Õ¤ÒtaYGdÔ–dŠs(YÕA“(c²¢’Ã|>ÊEЖMÝK È)€VUJåì=ÍJ”±êÚÑ}Áƒ€«˜¨¬í½½I  O¤@ ¥‘JabA¹e$„èÄ}Zø½®¿Ñ›Fð0ó4Õñ7z*ûw¡·úˆ‹ ÑÚål‹;M#¥ÂÓ²l8w"šTš.,ª{ʽÒ3DŽ$ùûNíónKò7QówdÀrÚ:àòy= ®Bç"åmÙH)VÔ{î"uU)€XÖ˜ƒ¤µvómá—ͪƵ›o[‚eµø„·ãÄ n$röžFE½Ýkžiçý†<1£×‘ •YLþ¦hÅœÆÒˆYûj^*BæÆ€"AÊ•þFo)M È0D4©4}œ©ÊºK’ ìI¯q7„PòÂð¿÷_(¨p'üïGI{VÊ hMVÅæ•‡ ¾sJäð•iäó^ÕÁ¹Šs,Z"M¤ÈŽ^G±ï^9sw#ì%CÚqâEê »€UsѤËÚM_)CgH—Hß„NB`Q=¯%Rw¾I"+Tªj+¸|g€ ¡¡Ü•Ñ&.ßAOžv¥|ëÂ΢9iúÝŒp®ñ‘Û ”‹9oRé\'–"2¾£2i…iš~ÛI3éweõo’cÛ6ùüÏÃ$øa!¨BIýPÖ-UJyH¬yÚ²Ó²-ëÆŽ–‰ƒˆ *ÎÜÝp±®øQÞ̳Ǧ÷ÅÚM_©C/!²¼ã•Ÿ- çcÖnúÊ;^ù™‘kçBÖnúJÀ)¨¯\ãíxåg©ºNÎܽOã:±:3Ïv8ÞÒ0»X+#XÕ¥Œ`RRARã„ÉN¬]¬9ö¨ãÜ5ï8 ᛾İ‹5•²7¥7v±6D8/ÕdF†HÓäkq³‹µ€y¿_'Þh2«u2Ï.ÖNa{ó²,§õ-uRØÅZ Áoèr\ÙŒŒ\v»½ÿ4ñ“i4À œ/e„ 1¶’ô¿©*ym/";Ï­‚öì±$½´,s]ØÈ)pæî}m7 Í™gu÷ÅÚ·ö¡·:ÄìŽW_ ¥½vã­KPI¤¼úRx‰ý﫹ãÕ—:f¢‰Æ™»÷­CýÆYyö˜k.¢xE™H‘Ÿ—”dЇ áqA%1 ´²‹µ:‚ezë`'Å„€…8*¢d‚¬àoy1L¢bkK¸tû°‹ ¡y§Vv8[¸øoÚE°„jj:ÜòúÞz¿¢à~ã"èˆçæe–y[_°¥Á8qò‚‰üý¥?‘r×>€jUÊÀôÌñc‰üã\ÈÚ—o©èkb$€êµ¿|Ùx£zíË·,A%‘òË—CI¤¬}ù$¸;~ùr¤oGt¹kŸN'Î9~,UçKt9Q'Räg:Þ Fµ‰À—ÿô ’ÚèÈ Ù8­°ÁgTœ„úÛÆØÅZ¯68wlYs‘û¢‡ cëÆÒ‡’› ò:/#÷«‚g#_&àÃ뼂à~·'ÎXÎ3ž#k€`ž¤Ôü­RŸH€3wiU¥¬Ì?–ªYúßûò-mÑÐ8Ä@õÚ×V^¨ï]?»…Dʵ¯­O¤¼wý¬n°¬Ýa$œÂræ®}IEÕ7¥Õ™ã¬F¡l‰#‘BDDDDÙf2‘õª=[ˆe|æÏÜõõteg7ÅFKù ÐfÏ[5ÃÞûÒl ïXIS% Z€((ž°Ë$ Q´bK¤Ìÿ±½ß“0ž}Û®}}ud ±l ­`Aô×¾X7–LQŤµ/ÖÛ– ÍïÆKÛJ=gîúzz8©ªÊ""""""Ê‚+Rͪ”Ê™»¾¶ÅcÇÝá*­o{n+@ˆþZm¯™‰¨Ô×ÛÕ¶VÛ[X«í]… Íï¢yí«)›_@´5®ÿÎÌñ§¬ú†ˆˆˆˆˆ(ýbM¤Ìÿ‰¡‹gîüªCè:f5‡ø@óY=ýù›—´£QAÓéÏß\†@uÍïXÙÑ;áêG3w~m ŽÆ9§ªú†ˆˆˆˆˆ(+b®HT¥Œ{“ã¥ðRcGïÄhSˆÙÍÍMènbñôçnêŸþÜMÊK:*¶†ÓŸ»iBô777ËÚ߃ƒožHÕ—3w~µ ˆEÌÑòÌs?ñbˆˆˆˆˆ(÷bO¤Ì<÷Ó€e!•3wÜ“ª!>;ßú¹ `ÁÀp@ˆ „X?ýÙ¥`"Úsú³7:§?{cB´ä%½sF"}Kÿ ÑÖ:g`%æ3 """""Ê­Ø)0óÜOW¬C­jñÌ÷8†Â‰Äο{eÃZ0꣈êgNàtå†ÂéÊ K8Š¡sAˆêο{%U󢜹ãžõÉ‚…XÉG""""""ŠA")’N…FB¨%b´³ÿjB °)``+ŸÞsCcâ T?oN﹡pzÏ KX‡À"6EÁйB,ì|ûÕTM¶zæö»+b^£Åyþ™N¼gADDDDD”o‰I¤Ì<ÿŒ hUh”ÏÜvwª–DlŠ*„æ³wâÏahÏ»Ÿ¹Þy÷3×/Aˆu±hdÏG·æN÷Åo<gn»;¨R¿¾!R5 Q]w!°!êT'O?ó•»NÎüìx×dXaÚù«_ŽÞýÌõUô¡3ä#0ñ÷¶©8ßÉùÞý£ë êöÊbsÒ1@ÛÓÜõ÷¯uÂ8p¨‚Š)åI,ϼx{ë]ƒk^:*|»~õÚèÝ?º¾  !t“)“QL¤¼û‡_* HüT$OÂÛ²R™D9{ë]KVð=©ò„Å f‰ˆˆˆˆˆ’ 1C{Æ®yéxBtuçK9{Ë:oÿ#·ëï_Aˆê”€;%ÕmRŸõ›)þ”Àâ”@Y'æm×f:+QÎÞrg]oÒÊ4ó³ãœ`–ˆˆˆˆˆ(—H‘š–yUUºùRvýúõÑÎx½ !:Šî¨Ù¹KÂØFª»þñõŽñ/“)±X†Ó»þçß-íúŸ—êÄÁÚÍ·5 ¿Ì1,ì8ñ¢kà8DDDDDD’Ô$RvüüÅ€&„i.‹\†ýµ›¾Âd uЇ߉ ò¿Þ^Úõ¿ÞNuÖnúJB´5¯I@ˆÎŽŸ¿¸÷ùÑ¥¥&‘;~þâ@ÓÀ¡‚Ê&S¢I Ñ…Í]ïö§w½Û_ÚõÿôSŸ@dÅL%ÊÀ‚ãQÈR•H€¯ü¬ !š*‚Ê”oMåj>¦èݺ’^Wîúwv׿¸˜OÕ¨µo5U‰2PÝñÊÏ2‘\""""""ʺįÚs!;^}©³ví{ ¿ÌlP™ríÕ¿xÉüŠ8ið;aæ8–å8 `ð©³nª'޽œµnmCÿÚ‚¹bª;^e…ˆˆˆˆˆ(-R™H€¿x©¹öå[ ꚇ –Fþò-Õ¿|9É”M¸lŽü_œ-ÿ¾•»åß~+ÿ·Ñ§ÖOææ{[ûò-¦’(0›ËkŽˆˆˆˆˆ(ÅR›H qT–è(8õÞõ³Ík_[íèÆ•&ŸZ?éâ£Iº€÷®Ÿ-À²VT ²yí/_v ‹ˆˆˆˆˆˆ"’º9R¶ºö—/ DB ŒLì´ßûÒìŖE óÞ—f}Q14LóÚ_¾Ü‰ù´ˆˆˆˆˆˆHAª)pík«#T!0€´7`ñ½/Ͷßûâ,Wô!¼÷ÅÙ €S(¹¾–óVõDDDDDD”%©O¤Àµ¯˜Lêì6ô×¾XÏõŠ>y·öÅú<€> †®«Îµ¯¯.E|DDDDDDdP&)pí«# ¢jAŒ,± [°GÒwús7ÍCˆS—6oÍoý¼÷ù‘Y™M¤Àη~$S€ŽáCWœ:ý¹›– —"rús79§?wS@ Á\8¦ŒÌîü»W:IDDDDDD a™8ˆÂÄaBuú³7¶4B8ôÀÂη_uC86…àôgo\°¡Gª;ß~u±‰rÃ.ÖT*U£çšŽ…ˆˆˆˆ²Á²Œ¤?‚c™8H)pºzc@;¤Ãw,ìì¿: éø¤étõÆ ‚¿¿Âáfwö_õB86Q®0‘BDDDD¦™L¤dzhÏùvö_í@ˆ*„ž¢!ÖOï¹a)îó¤:½ç†òéÊ }чNû.„¨2‰BDDDDD”}¹ªH;½çÀ*€°–3ö ÄòÎ_ý²ÒñiNæË,ká é[ÞyòK!Ÿ(wX‘BDDDD¦qh§?ó傉F!~Œ· ±¼ëW¯uBü :Ï»Ÿ¹Þ™Bè ”€æÎ_ý²âgå)DDDDd)½û™ëç!Ð ùc<Ë»þž •0½ûG×;&‘m„úAV0Ê®_½æ…ú9D9ÅD ™ÆDŠaïþÑõe“†5Ôg̰  »ëï_㤴†È¿ß~„@ ¬ìúû×"ø¢Üb"…ˆˆˆˆLc"%ïþÑõ‹b>‚!XåçÈ®_¿îEðy™ôîÿßÞÝä¶UÅa~ݘQ‡1Ap Q¤(if„4]AÅ  +€¬€tD›òq#( À|ÔÂv6P381¤R iiîuâç™$±¿ɃŸÎ9÷奕$“ô|±Vk'É›ÝÏ>¬Ž|-˜qB šr„†//-Ÿçý$íš–\OruþæµÕšÖ;Ö¶Î-uRvž\ÌÑ<Æx?ëwZ¹Ôýüš]DP!€MH9b[ç–Ú)G}–k\v²KemþæµAëN½ÝÿÇrêÚ}òr¡ìÍk.”… )¿¸’T꺌ö UîŒ7“ ’ ºßÜ5<Ï¿>÷z?I/§ZgSJ§ÉyöXOòV÷ëQÓƒÅÜéÅ÷s¸GšRîG@øWBJÆϽÞN9ês9ÓT&v²U2ÿ™²ƒe§ûíGµÝ0|ö|?å}é¥Õ:›Lzu­ª$WºßܨžØÇ!bŠˆÀ¡)SbøìùvÊ†G9¬IdI’AÆùsŸ×RÉD+gw_»ûõé6Jr¥ûíG« Ï`îôb?%Zï½3¥J²¶}{cµ‘8†„”)3|æ|'åBÚ•f'á£$Wºß (³DH™R•ñx¥áQØ«ÕE@˜YBÊ”ö^ë¤ìN¹œ;Sw‡Êì8U¶ÿw¯6< RމáÓ¯µSÎõ¿ñ¸Óð8³£ÕZM²ÖýáãªáI˜BÊ14|êÕ~’‹qÊQ%YK²Úýñ“Q³£0M„”cløÔ«í$+/§<˜ÿg5­ÖÕ7=ÓIH9!†O¾ÒË\L9þÓixœãd=§r5Éz÷§Owš€é&¤œ@Ã'ú½DTùëÉn<ù¹O84!å„>Þï%é'yc÷ë,%©RâIÕýE<àþ)3æÖcýå$ I&å$ÚI '›Iª3¿VƒfÇà¤RfÜ­Gûý” r6%®tç~UII¾ORù­5: '–Â]n=²ÐN *½$sù'®tš›êoƒ”c:ßO¾?óû¦Ý&ÔFHáÐn=¼ÐKÒÎÝaeaϯt2¾àÒJµç§Q’íÝï)ÇtgþØt¯ {!êô ŽâO=¼ÆIEND®B`‚golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/assets/vexxhost.png000066400000000000000000000375121335005366400304140ustar00rootroot00000000000000‰PNG  IHDR‡`Rá³ IDATxœíÝw\SgðßÍBØ{ˆEQÜÛj­{¶ZmëÛZ[­{µZ·uk«âÞŠ{ïQ­ÛZ·UêÂ…âÙ{&@ȸïH4äHHå|?ÿ 7¹¹ä9÷yÎsò, B!„”+¼Ò¾B!„”< !„rˆB!¤¢€B)‡( „BÊ! !„rˆB!¤¢€B)‡( „BÊ! !„rˆB!¤¢€B)‡( „BÊ!Ai_1® ‹sp.^ >¦W—ö%B)ƒ(øä¨X\HÈÁŸ12\KÎ\•ûx=úñBáF#ÄìQºGb²q Œˆp$:ó_H‘&7lº¿–µ¿x[¢£3 ü„BL‡#‰ÈRâ·` n&Ë z}%K>&TµDWWsšê'„br“ŠvDdaÉËLd«ô¿ëw2ãatKô­`AÉ}„BJ Å‘¥ÄÄ'ü—ªÿ]¿9ÁJ" «,‚e1Fþ ¹ ÖBžÁ¯'„R>Q`À¨lü"E¦›ú?w5Çd1<, ¸ã³•8ž‰Co2ño‚ ’ï*‚G³„Bô@€ž’rT˜ôT‚ˉúWò«m#À¬b4´êýÚ4¹ GÃ3±çµ—â²ñþjCD¦•Äô£$„Rt4jèáz²g !G¥×ël &Tã;O ½îÔ•,p6: ;^IðWd²uÌ6„¤S@!D?4j‚–¼”bÓ›,½_ûµ‡&U³„£Yѧû_KØúR‚í¯$ˆÊ,¼ˆPHºÝ-ô¾6B!å—àÁã`dËdpqrDw7˜™ ‹×oÂáêìOwWX‰Åꄾ‰@fV¶AoV±‚¬­Ä…?± ‰ÎVaÔÃt½»öy[òñGM+4µ/Út¿ŠNFeaíó œÖ/Ðx™Q¼Ž‚„BÊ«³#&ÌZˆl™ ÍÕÇÄÑCàæâ„•·ãõ›89ØcéÜ©ê 6!KÖn6èÍüjø`ö¤_Àã}YëW’rðëã ½Šú`xeKŒª"‚Yæû“e*l|‘ !¼‘6¿H7¬ö!„ò‹¿dñâÙ.N¸u÷"cbagkƒêU« n-_\ºq éDÇÆ£U³F/w¤gHð2ôÞo–” †aPÛ·º±¿£R²ÀŠ×™˜,Låþú¶lm`‹î®æà3þ/3˜”Šo&âlt6Òäúå¼ð‹¯Á¯'„RþðàÓæMоõ'€m{ãMDÜ\œ0ò§ï÷‚pæâõ‹|Û•½< zÃC'ÎàɳŽn“I•³x? kC3‹ü3ƒ)>bll1¿Àç&ÊðÕ•Tÿ3 kŸg ³ód§¿0!„rI=?øû>ðòt‡\¡ÀÒõ[ “åà“& Ñ©M+ÀŽýG  …?r0ÌÍõ¯Wϲ,–lCZz†‘¾ã ‘(Ñ+0×õ(ç[ßV€“Íì0¤’¨ÀJ~—ã²Ñéï84;‹£á™(îm#ä¡U+œmï‚7½+ól„BÊu`f&Äøƒ`nf†¨˜8lÞs0°ïרìå ¹Beë·"[–ÛáÎÃÍÃûgЛ¦¤¦aõæ`Ù²sçz>>_ý—Šð¬¢µîå3Àøª–8ØØU ¸ë¿—VçbÑö|.Ä–<™ÇŒÇ wEKj팸o*`û'ŽèìQpàA!„páÏž={vÞ¶6Öp°·Càý‡ „‡› ªTòBmßêøçú¿HMKGrJš5¬¨ä剄¤d„½ÐGl|Ì„BÔ¬^Õhߌ¡Ö†fbú3 ŠšëçmÉǶ¶èîf®s_ÿå¸l ¸™„9Ó!-ZP¡KG3Ì­o‡m-ñ£·jÙ ! Ò„BŠA+¿m«æhÝ¢ `Ãö}ˆO„§»«únÿò[¸úo úùCúý<Ü zó}GÿÂó—¡½Öä*`“ ,{Uôõþ¾,p¢™jÛp—Pxœƒ.ãÑö|®Ä~Ço-äaDukÜïîŽÀnîTÍ ¶TóŸBˆ‘pŽ(Ãú7WdËdXºn3 Z·hŠv­ZvìGL\<ÀÜÜ ãF ‚P¨y[¥J…eë·@"-úl,©r?ÞOñY‘žo'd°±ž æùZAÄ1ç&Q ßõD4<ƒszîã_G3lnመ¯+`]3ÔwÐ?Ï‚B)ŒÆ@@€Z5ªáÒµ‘”’ ™,õëÔB=?_ܾ÷É©©xöâ5Ú¶j>;kØZ[áNÐc½/ 3+ѱñhÙ´!˜B¶ÎKD–}ï¦áI è4¶bW#[Ôå¸ëO“«0ãA*úÝHƒýû€Çàû*bìøÄ 3êÚ¡¡ƒY‘jB!†â ÀÎÖÖÖV¸ô!¯BQ­J%xyº£¶¯.^ûIÉ)ÈÊÎFƒ:~€ª•+"*&Q1z_DTL¬­Äðñ®\œï¥Hžf(ðýÝtDg¾ïžÇ#«Xb±Ÿ5lš²Š6¿” ×å\ˆÑlÎSTvf<Œ­iƒ½­œÐ¿ªÜDo!$„BŒEgÕªTBdL,"¢bðàq0Z·h7gØÙÚà΃Gxñ: U+W„‡›+ ~횸xÒLý§ô‡ a]?8ØÙüÍæF²¤#µÙ~vBêÙâ |n&ÈÐãR6¿”@jÀ>þ*VÌ­g‡­œÐÍSZÛ'„R yF è Wg'dH¤X°*• Z¢N\³e’RR"‘Æ@ÿCŠ·Û ³²‹·UN—Sq2 ¼ŸIìܽýöh堙ן­ÄÀ›Ihy6AL÷7t0áÖÎxù¥'~öµ†•€¦ù !„”Žg€Ü¢?¾>UqéÆmÄ%$‚ÏãÁÏ×õü|qó¿ûHNIÅËÐp´iÙ ÃÀÁÎ" LC½“1¸¯ß]3'sœiï‚[]ÝÐÅC¤÷uB!¦Vä€aŒ˜;¨'§¤bÍ–]`Y]Û†æëƒeY¬؆t‰`%ã×á jý+—˱d]n?C-z)Åò"øih+Äñfv[ü$ç ÉéXÌ JEŽéý~vBœlG?!„²¯HKyÌÌ„¨^µ .߸…¨˜8X‰-Q½j4¨S 7ï"19‘ѱhÕ¬1†“£=„>}®÷…¥gHšž& êêýÚE/¥+¼O/ws¬¯kë·Éx øãQú]ODL{€»ˆMÐÜ5lhªŸBHÙ§WNö033CГgx‚FõjÃÅÉ5ªVÁ¥·K‘jTóøúxãùË×¹yz }wWTÒ£õpQÿ ÕĘV]¬^ïž.G÷â±'TŠÂ+ä²0˜Q×û>uF3'Ý}!„²Æ  è=º´Gƒ:µ P(°tÝdËdðñ®Œú| Øuè8^……È]:3tìlm ºÀ€ûÔe‡ ³ôUf¡ƒ¿ÁÚºÖQùÝý† 4<ƒ;IE[rà3À¨ÖxÕËÓëØÂ’¶óBùÀ0 ƒ_†ö‡ƒ½bã°càóŽmѸ^m(•J,[¿E½§ßÖÆc‡0¨Ôon?‚-+ .Û»!, ëB ^ów0ãa_c[tqÉMöK”©ðÅ¥xŒ¸ŒÌ"ôéâ!ÂÓXÓÔ.T¹BȇIï%€<æff¨Z¹".߸7QpuvD•Š^hP§®ßºƒøÄdÄ'&«÷ô»:;A©TâiÈK½ß+5-ÒÌ,4¬ëÇy|Od6þ($Û¿²%{Ù¢ºUn²ß•¸ltú;w‹x×ïm%ÀŽ–N˜[ߎæ4ðBù°«­_ |ÛësÀÆ]Þ&¾Ëþ¿~ûþ¹ö¯úùÿû²;jÕ¨fÐ{ݾ¹\®õø±f>“øÚ¶jl‹Š">T,ðû£4´»‡¨ÌÂý,ø ~¯o‡§=<ðEÊì'„òq(vú¯>ïŒ:µj@&ËÁ²õ[ —Ëáëã¾½¿»§?*&· ÇÃØa?ÁÚJ¬×{˜›™á·1õZ_LÈÁ¤§¾¶“v5´…ƒ‰2º\ŒÃŒ©EjÞÓÕS„'=<0­Ž-Ì9ZB!ªb Ã`ìа³±FXDv8ø²[GÔ¯]²œ,]·Y}÷îho‡_†ô×ë=Æ €*ù*ÞK“ã—Gä½Üͱ¡ž D|‰24<ƒ 1…÷pñq¨µ3N·s·•þ} !„²Î(mèìlm0fXn’ß™‹WpûnPn¢àþ°·³Å›ÈhlÛwDýü†uýУKû"ûû¯{ Y£z½”*1äA:² ýTa±Ÿ5ø °ñ…Ÿž‹C„´àDBêc…§=<ðu%Ë"]!„ò!2ZÚºµ|Ñûó΀uÛv#1)¶6ÖøuøO`ç.]í»ÔÏï÷uOøxW.ðœ­[4Eïî5‹“©0à~Z-}Çz[bFu1ä*Ão'cØ­¤B+úU³àr'W4w„µç%„òq3êH÷íÛ$?‰4˶A©RÁ¯†¾éѰnën$$%ø|>Æ ±%wb¯7Fü^ã±L%‹ÁÒ“­»TÏoÕÅøÙÛ ÙJtü;!ç0ÆøZ#ès|æj¡ÇwK!„|¸Œ¼Ÿä÷ìÅkøóà›]Q§f H3³°|ÃV(U¹¸‹³#FþÔOë<ÎŽ˜4z(„‚wëïJû8O3tOãÏõµÂ Š"NÃÂÇéíùÔ ÝËÀgA¸Tºi¶@ï[EŒuÍô>WjŽ ¤8•…ÿ’r«Gc3c0ã1ð³¢›úT²DS'óÂ_TDw“rp<2×âd¸Ÿœƒ4#2ºß~/Ü,ÐÏ[Œzöf…¾fêýT¬^ðÒsYa²@©Tbú‚åyŠujaÚ¯#Á0 ö>£§ÎÁÎÆËæMƒ­5 &.Ÿ>Gç¶Ÿjœ'0EŽî¥AW¥Þeµ­ÑÃÍ“ï¥`ñ“tî'½ÕÓË›[8ÂÉ\ÿ•Waáy¥þº~효àá¦÷yÊ’;!6þ]“¦–MÂÞÎ@n7Æ«ÿþWZ—f2B¡@ãwL®PàâÕ›P(¸?$=Ü\tV ,+’SÓp3ðžÎã>Þ•Q£Zõ×AOž!"*†ó¹m[5‡¹Yát¥…eY\¹‰”»ô·¥Èm[5×*=>;(s¦iî?zŠg/¢g×ø¶÷çxòÏ^¼ÆÊÛ1cüh0 wW¸»ºhœ#&[…Ñ2tþs}­ÐÕÅ n$bçkÝ¥€Íù –6²ÇÈÖ0t6ÇÅÉó—¯Cjzî¶‚‡;–Ìž¬UœèC‰E«7ªó1ü|}нcõñ¤”TlÛw¸”®Ît,E"@( ’—'¯Ù„´tî?Ú¯¾è‚ïz}nP/ S{ñ: þ«7"%5óx»V-Ð¥fP]½jeœ¾p w‚s¾æâÕ›˜2f8ííŒ~½Å%“å`Õæ¸uççqWg'L3¼Lþ¬L)T¢@׋ñxž^º$—Û‰2´:‹Å í1®–~MárT,ÿ›„]|¾—¤ã™ø/Q†k]Ü>Š1&Ýïæäè€Qì9|/^‡Ïãa܈A°¶#èÉ3üyæçk³U,†¥#)‡{Šg®¯z¹™£Ç¥„ÿš¶Bvuèb þ`m%ưþß©¿ŽŒŽÁžÃ'ŠqÆÒ#W(°fóNõàonn†Ñ(wšyjúT…ÿÌI:ÛNùë,­Ù™¬h}#JÊõÛw0cá ÎÁŸaôÿ_oŒÔæ•È“†ž];pž÷õ›Lšã¯ÃLqÙKLJÆ´ùKuþ~5|à?s¼<ÝKøÊJW²L…ŽÇ•ÉÁ?ŠÆßM)tWV~Ãn%—™Á?Ot–}®&©šlYgò¦Y£zèÞ± N]¸Œå¶aÉœ)p´·ÃèA?`ÁÊ Øwä/øÕðAõªU4^7=X‚':2þ«.FWg3´½‡ÀD™Î÷þÁ[ŒõÍ!6RGÓ†õðió&¸v+wjüä…Kh\¿j׬n”ó—”CÇO#,âÝtãß| ç¢O5~Œœ0Úx¬ØŽÿî?Ô:x/SÿX‚©c†ÃÙQÿuYcbYûŽÄ‘¿Îr‰,0nøÀ—.x<~ìÓ ==°~û^(òuÛLMKÇŒË1r`?´nÑĨ×oˆW¡ð_ žË¯S›Vôý7ZÁNy0:0¯ ØÕÀÁ ­\ÌQÃFW >Ìyà1ÖÏT°H–©&UàaJ.Æf#YÆ}ã6öN :¸‹PÕºðŸÓ©¨,lÅû%`€ÎæhåbªÖT `-d`Ác ò Ôóû“(X(T,Òå*$ÊT“(ð %磳—­½ –¢ÒO$*ŠWaá8vúݬ‹Ÿ¯º´k]ŠWTvX˜›còÏC±÷È_8zêœÖñ7Q˜øçôý7°{»…6lß§3»,~ñ 'Î]TMSÿ…óñ®ŒE³&ku¢ÌsâìE,X¹YY…w˜,Ž+71Ó%çàÏçñ0ä‡ÿaèß<øç177äÑCðÕç9‡GEcòÜExúüe±Þ§0ñ‰Iøí÷¥:ÿzµkbጉåzð€Ç©9œkíþ 튕ðlLÍœÌñyíåÑKüû‚R´ïþ¿ð²4ÚÚ·!Ú»k—Š/é ÆVâ]oöýËæý†ª•+j<¾?*çâµèµmï-Bû qˆÊÔþ϶08ÔÚ¿×·ÓšŠQ(&ËÜο+@"•bݶ=0QY…b‘åä`Íæ]êk+ïYÿúp´·Ã¿Ã'Mr¿÷ð &Ï[¤QOÁXT*v<†U›vp®[‰-1cÂh£r àïW=ðëðŸ8·¸¦gH0{ñ*\¼zÓhïù¾§Ï_bòÜEŠæ<Þ½c[Lÿu¤Î"åÉýdíÏ6o+Q ï÷•µ—¤îq\ûûÂ¥ÚŸõK·6E}ŽÄ¾ü zš„B¡V‘‘—R%æq¬ûWñ1ÇGŒ.sþ^b®wvãLÄffá÷eë°vënã]|>yKyî=|‚󗯛ìý µûÐqÄÆ'¨¿¦¬ý˜›™aÜðèÓ³çñ¨˜8Lž»‚Ÿí=³²³á¿*ÇÏüÍyÜÓÝþ3'¡NÍF{Ï÷µjÖ󦌅­ö”«R©Äºm{°mßau>‰1\¼z³¯Bz†væ7ŸÏÇð}1°ï×%²äò!I× ?-ƒ Íšp Ü/3:ë»à,åîbQ¼®ââê›]† .¢Ôÿ’rT,Æ>Î@v¾u;!ƒ¾b|}%žsðoálŽÀ®nhÀ‘쟄©¿/Æ£àç¸x›ènÐ^ Øqà(bââMö~úz‚Ó_VMSÿ†aÿû²;Æ33í;c‰TŠyKÖàÜ¥kÅ~¯Üßß%:§ÀÖõÃÂáæâ\ì÷*ˆwe,ž=Ek¶.ÏÉó—0ÅzH3¹{t•J¥Âö}G°nÛ(•ÚëÖVbÌžø :~Ö²Xïó± ÏÔ*ŠKwäRI¬½F.W±HàØ^—GÊè»µÏØl… *‰ÿ,?ä @”`é«LçËè0ÀšVx#‘3Ûÿ›J–ø§£+Ü8Ê1>ù“ç-BTÌ„‡§IDAT»d·Í»êœR,®üK2YVmÚiÔ;#CeËd3 4õ_|Ÿ4mˆ?~Ž*yJ• wîǦ]ÔE–ôõôùKLšë¯³TïÛcê˜á%¶íÔÁÎó¦þªs äþ£§˜2o±ÁAofVæ¯X¿ÎÿÃy¼¢§ÍšŒZ5ªtþYÇúA%jK‹9Ÿ­P{¨áºþ<6£}ŠŽ¢p%¥“‡a½=5þuòø°—¢J5ø/UŽ-o´ïfհ„Àd§i' Nò³ÁÖÎ°àˆ¼®ß¾‹Yþ+µ¦år9–®Ûj²|€üK!¯Bqä¤ö>ò’¶mïaÄ'&©¿¦©ãð®ä…E3'ÁÇ»2çñ³ÿ\ż¥k ‘êWÁ았½x2$Ú¯ãóù5°|ۻħÀó–@¾íõ9çñèØ8L™·Ÿ>Óë¼±ñ ˜2o1î?zÊy¼qý:˜?}|¹Ú}¢®»dQ½#åÊÝK’éž°áBÊp¥ÃU©™JŸHÿWxX%ž¦áa¾,P4w„C{Î ×#Åò [µöm牌ŽÁæ=sñò/:~¯ÂÂMö~… z¬±ôASÿÆeog‹ySÆj~ï{ôô9&Ï]¬1¥‹J¥ÂÖ½‡±aû^Î)pk+Ì™<í>mQìë6Ã0ø¦GWL5XÇH&æ-]‹³ÿ\-Òù?Ç之tþÿôêÖ S~‘EÙ[Ó.+Ò8îˆËæðÏ= ´zÎUè†kk )žR ¾jmùëàl†[Ñ™¸§ùƒñùÌC}´÷* ¬Ú´{þUè{þsí_\ý7°x®Cþ¥¥J…•·CαÝÑÔ¤™YXCSÿ&' 1fhôÕQ%/ï÷ÞÃ':Ï!ÍÌÂË×áÔ…KœÇ+yybѬɨéSÕh×]-7À¿‡“ƒö>s•J…M» `Ç>Î@&ϹK×0oÉÎn~Bc†@¿ozÒï«lKh´©Õåȸ¿/+°ô;Ñ_©ü¶ÜL–cO¤æ ï#æC™%DZpÍ;3ÎwpålÙ)‘J1gÉj\¹YôA=`Ç~“%éå_ ˆŠ‰Ã®Cšä½ ²uïa$§¼+£ISÿ¦Ã0 ¾ú¼3&ý<ææÚZykÜ'Ïkð1qñ˜2o1<æs>¹K)|ž"><,ùð³¢±£9ŒÔX¶Ì(Ñài†[Â5³þ{»šã÷û)U·âbGWT°ÔÜÒ ¹\®¾KèÓ³ž<aPiÒ3¯ vÍêhÞ¨¾Þ¯-Lþ^I)©Ø´ëÆ`ô÷zß–=‡4Ú¥sê?,<—®ß*ü‰eœ@À×™¸g v6Ö˜3i Öoß§1“Ç·š7j׬;[e ÷®zÊ/ÃtN­—U½ºw‚§‡VlG¶LsÖÍÅ µjøàQpgЧg7ôéÙ­Ô‚Ô0‰ifIñuöaDuk¬áî‘G¦dKdYÀFÈÃD˜VÇ5K¸Ã¢©”X b©Á¼_8©™ÛƒÓ4ª)Õ¶⟎®pÎWõ)>1 Óç/ƒØR„eó~ƒ•X ‡_‡ý„q3çsn*̺­»á]ÑË$ëãï· €k·þC“uвi#£¿Üüïž:àŒ?õxÿ!ï?4ÚùJ‹¥HdÒÈúyð¨XÁ»þ –e!0r`?õ”ùâY“±pU^¼T­\SÆ ‡ƒ]Éu:3¦¦ êbþô X°b=’rgóš7ª_†ô‡¹¹~þ¼<ݱÿØI€™™¿ é_æh‘Òµº©x °VG×Ã’”.WaO¨ßdbU{ ¯n]ø‹Ê¸ËØ•Çï•®t·à!8.ñïUƒÒ5ø€‹“#Õ«¤”T¬Ý²[=Ýè`ogp+^if–l+0cÙPùwÀÆ´îúŒ!=C‚M;¨¿¦©ÿ²¡g—˜:f8Ü]]´’ÛÞ_CoÕ¬æMýõƒüóTªàÿ™“PÓ§*¾þ¢ &Œ¬NŒÌÛF8qÔx¸¹â߯ÓàO Åg€5Mp¹“+Ú¹•-¡r‹·“±ëµþ7eM‰Ì¤ÈUXòêÝŸì•*Üx¯!„®ÁÿÌÅ+¨Y½*{ybÄO}ñ2ô ï?Ä™‹WЭC¹¥Q{tig/B_!¯B±çð üø¿^†}sÈ¿ ‘J±vënL7ʨƒó†{‘.y—@IYÿeÃ0àóyÜ?o†Ç˼$Xãñxàñy:‹å++õü-øe¢œ«‚e9Kž“\Ÿ¹ZàbG Df*q&* ‰2ýyÈ7c>.]¿…ºµ|ѺEtmÿ‡ ð^6îܯÎpqvÄÈŸúañÚMz_g†DŠå¶bΤ1F$ò/ääȱjÓü1m<øÅx¯õÛöhÔ›7åÔ¿­z¶…è–.‘`ÑêMáÞ™ò(ø9DHJNÑ:–œ’Šé –aô L–,j ž¾€Ý‡km€èØx<|ú!¯B9_»ïè_ˆˆŠÆ¨?p–&¤,hïf5MðãDǦä@¦da^–‘ aÒÛ¡3ñ2ÜM}·=ƒ'S öí:—‡ˆ ´·úåg%ã×Áãñ°cbãÀ0 F ì'G\¹¨Q °yãúèÚþ3ƒ®÷éó—8h@]¢È_ èÅë0ùë¬Áç»tý–F‰Yšú/}áQјq‚_¹kWÀ¡gð2ôÞçJLJ.±©R4w<ÂÔyK4º/æa?öé…Ÿÿ¡ ÷w¾^íšðŸ9Qç2Сg°dÝf“u°,®Ôô Ìô_ÁYëjV¯ÿY“PÑÓ@îïÿÌñ£Ñ©M+Îç¿ Ç¤9þý=w$ò²4~È 8«ÒFÀÕMìˆÈR7ûqòp),wªZÄgpº½ êØé7Ý÷e·Ž¨çç‹×o"°ûm}ýÕªàûozB&ËQ@nC‘ £TF•eY¬Ü¸iéÆßwš¿W€J¥ÂÊ; Ë)ú‡<˲X·mÆZ+eý—®?O_ÀÂUZEp@da©c†£g×ZÇ<Ü\±`úDÔ©Uƒó¼·î<À´ùK5rÊ‚°ˆ(Lž»HÀ˜_ÇÏZböÄŸac¥™¨Åçó1¬ÿwôý7œËl)©i˜¾`9®ß¾c’ë.etÖHªÐ/KÿJ\6V§kü“)Kÿ{óµÑ·ôýÞÊ“‹õa¹wÿ €¨äl(Y<8ÐÚ™³Æsa†Á˜¡`og‹“ç/áNÐc@ÎíѰ®BÃ#±óÀ1õóÝ]]0¬ÿ·]JjVnÚa’)ØüKѱqØu°è ƒÎ_¾Ž 'ïú®ÓÔé‘+X¹q‡Î)pWg',œ1êÕÖy+±%fŒ­sÙ*4<²ÀÁ¶¤Ý¾„ßþX”0 ƒ}¿Æð}!èžÝëÖ¡ fŒÅY;/Yr/Gia¢ÉF¨=ãÇÑ!¸LàÚ¥WÐöË}a™øõNŠÆ¿²€«­qjYýO/“[³úv*Ê‘Ç"J’›°º‰¾¨`x=|[kŒ: Ã`ÍæHJIU' :ØÛáôß—ñß{ÕêZ·hjpõ ÇÁøóôƒ¯U®¥€3¯h êºÄ'$aÇ£ê¯iê¿ô¤¦g`–ÿ í¥ýjøÀæ$Tðp/ô\|ƒûõÁпåL Í›nק륱±,‹#'ÏaÑšœË–"fŒ…îÛé|ukùbá ÝK ¹ïµ‰sV…äâŒÞ/¬VV(X@Âq—ì`®;ÿ+›ãn¿,$Ú}¸C=7£)r¶¼É݆g+`ð_tîLÀD?Œ¬QüÒ‰µkVÇ7=º"C"ÅŠ€mP©T°±²Â¸sƒ-»ô^µ½Áß÷)Ò‡0—½Gÿ2ÉWþ¥X³y'gô<,ËbÍ–]¾4õ_:BßD`ò¿ڴ¬‰?ÃÚJ¬×y;·ý3'ü +±öë VmÚq62¥œ9VnܽGNp÷psÿ̉¨W»¦^çõpsÅ“PÏÏ—óxའüöÇRuia¢)B„KË^BZ¤T®Ù{wŽëÏ#á˜2È*K)2íK,(E­ aô+Ë‚ôí*<), |ée ÿ†öF{ozt…Ÿ¯ž>‰Ão3ékúTÅ·½>‡Dš‰åë·ª?$ÍÍÍ0~ä@…úo1R©TX¶~‹ÆV;cÉ¿œš†;÷ë|þ© —ñäù õ×4õ_:nß Â´ùËȱ÷öN~XÿïÀçV¬vÍêðŸ9 žî®œÇŸùþ«••ÍyÜØRRÓ0cár=üï«S«LŸ7îë-ŒØR„éãFé\yó6߀«»byW™#‰únRÙK}”ªÝ¨ÇÅ‚q­õ¸îöÊÀìFGµF®¥˜…Q€ävFä~0‰YI™ Ôw0ÃîVNF(ÏðÇÃØa?ÁÆÚ ŸVŒ_}Þõü|üâüyJýüŠž·þMLNÁÚ­{Œ¾ɵp#ð.gTL\<ö9®þš¦þK˲8tâLî8GÒ¦ØR„ãudúpsqÂÂuV¦¼ôS~_‚øíÆô*,“æ.Ò™™ßµýg˜9~4¬ÄÅ«¤—8 ×8¥½]ùºQSmŽDê{É9-cGhÏlú’ÎÕÖ÷1G QÒnÄk/IU—hS]£2j°9< 2 Á“ø,¸Xðñgç#=C9ØÙbìЀۑ.‘€aü2tìl¬qää9<Q?¿CëO .®x/§ÿ¾bŒËÖÀµ°qç~†Ay;rrÞýòÓÔÉÊÉ‘cEÀ6u'»ü<Ü\á?sêÖâžÊ6„¥H„©c†ã‹Îí9GFÇ`â;ši=¾ã•ûÂJ¿S]¦‚Åw×!ç¨ÏßͳàdðÚß×¾0)®qÜ—„L‹1ÿi碴p6‡ îoKŒÑ€máYÈT²°`X$dȱ´‘=gÑcûî«/àëã;AqêÂe¹ë’_}Þ)©iX½i§z0YX`܈AnQÒE¡P`Ùú-ûïk)àáÓg8sñ £¢±ïè_êÇiê¿dVœ¦{Ƕ˜þëHÎílÆÔ¾õ'˜3y l¬µ (•Jlر›w,ö±,'KÖmÆÁã§9WðpÇâY“áçëS¬÷)Œ›‹NŸ s äÞÃ'˜2o1bã9—'«iÿN@¿ë‰˜÷03›¾$¥ä å¹Xü› =`W·¢E!3Ÿ¹ZÀ2ßȪbÏÿ‰Ç¾°wååK£T9Ú_ˆã\‚èU±ôIÃaq;SÉ¢Õõdd(X„'d¡—»Zv§mˆÄ䌟9ٲ̟6U+W„J¥Â¬E+ñôùKôÿ_oôèòn*õÔ…ËØº÷PgÔ­eÓF7b ±.]mEÀvD+¡PWgGDFǪܯQÖ˜‹"4<f-Pý15ªTÁ£Ðç]¿}k·îâ¼ æóùúã·èÐúS\¢Nñ‰IX°b£¢9×­å‹ñ#´&Ÿ”’Š+7 ôMçñÆõjcì°Ÿ •\Ov•J…]‡þÔÙæÛJl‰ ££NMîBJùÍJÕš?ÖÆ¹Ìvì_Õ Û?)x©/GÅÂçÏhÙÿNæ<ô®h‰–.ðµÀ]Äç\_/ $f+.UàAЧ£²p%N÷Ò¶O1 *wàò¾¡·’°é…„ó˜µÝ¤YYX0}‚ºË˲X´fïrVnÃôEÇÏZóò‘!‘bì´yê¶Áùùùú`Τ1%v÷Ÿ?øXXŠDصn‰Îã,Ëbÿ±“êÝ%ùÙXYaâè!¨U£š©.±@Ù2Vlרwñ>7L3\¯¬üW¡ð_ ów¯g×è÷uÏé¤ÈåÒõ[X¿}/”Jí l>‡Aýú s‚Ó1rצ;\ˆ+Ñ»bCµs³Àß]‹”™©D­ÑÈàª"ÄÁZȃ€á®P,% ™’E¶ŠÕ«ÚàÚ¦FÙÚ^SÅþ‹V±Àöˆ,É98ÔÚ¹Äh\¿¾èܱñ Ø‘»ÎÁÎ?ùJ¥K×mQoÊm&ô=œ Ûš¸eÏ!¼‰ä¾ 3×R@šú/2YÁ¿¢§ügM*µÁ,ÌÍ1ùç¡èÕ­çñèØxLž»Aƒ‹t¾«ÿþ‡™ Wpþ?þ?öéUjƒ?´mÕs&­ö‡­R¥ÂÆû±qç~(Ëir`;7 ¬mæPÚ—Q¨ÚvBìÿ´è;Â*Xò±«¥#„¼¢½"C®BJŽ o¤ ½þÅg+‘&Wé5ø®feòÁ¿$û¯úŸÄ„e*Ÿ.Ãê&öðµ-½ŒÈ~_÷@µ*•píÖøçú¿€†uýЫ['Ä%$bÃŽ½êçZ‰Å7bAlrynÇ6c7jáÚPÖIHLJÆ´ùKqëÎÎãMÔÅüéãáâTú?†aÐ3t€º¹Ðû2³²0oÙZœºpIç9X–ÅîÃDZrãvu÷ÙÙXcîä1hÓ²™Q¯ÝP5}ªÂæ$TÒÑäëÜ¥k˜·dIjv|FT·Æñ¶.:[«—¶/½,qµ³îÖïºôô²Ä©v.ð( hPI²à3XÜÈ[”þç€1;Øž!€nÎfEZ×1%@€ñ#ÁR$ÂæÝø®÷ç¨^µ ®ß¾‹‹WoªŸ_£Zôíý…AïƒÍ»åºß—Weý›^È«PLž»¡á‘œÇ{uë„É?…È¢äÖ¿‹¢u‹&˜;e¬ÆïK–e±uïalؾŠ||¶LÿÕqìÔyÎóV©Xþ3'¡F5o“\·¡œ0Úx4iP—óø£à瘓›í¡ƒ+޵q†½kåÝ-ðüKO,jhꥴ宪µ3êÚâu/OL¨ecÔº6¥©X9/¤Jtù7ª,9‚º¸ê½öb*ÿÞ¹%k7£’—'ügL„P(DbR2ÆÏZ¹\E³Þm3bYó–­-ò”i~c†öGëMyùyŠWa€æêÁɱä§÷Ò3$¸ú/wõ·™P(ÐX/V(¸xí_ÈåÜITn.:³ÑˊĤdܺ«;Ÿ¥Fµ*ðñ®¬þúÑÓç:—°ø|ÚµjssímXe˲¸ró6$Rî­€––hÛ²¹Ö’Ù­DnåKèú¢‚%ªZ—þ`™.WaëKÍ„·Z¶Btò0|‡É³49n%Êð4MŽ©ñÙJ$ÉT(TP¨™ŠE–Âð̼ä7±€•3¼Ä|T¶ ®Z¹˜›$A.$]Žëñ2¥äàUFî~¦’…DÎ"]®Ç®ÃBYðXðˆ ìÍy¨`)€·•uì…hædïR ¨þŠÌ« ÍDäÕ¬Œ6Ö+˜ñL‚½áYØZÏŸ¹–­»£Í»âÌÅ+èÒ®5†üð?@àý‡ð_€ŠžX4k’º³'ý+± …‹ÖlBxd4~ò#üj¼+jx/ñ‰†u«_»&*x¸ëÒ !„“28h~% Ã*‰ðSåÒßCK!„ý´p7E† þ„BÈÊ `Ù æú–î–?B!„Nï >[‰0èX~!„bz§beBSÿ„BÈMï@ª:8—Ý"!„B)œ^€’*YòQÄÞ „B)£ôÚø0]ªb>Äetß?!„BŠF¯+Cƒ?!„òÐk€>š.H„BHy¦× þ„BÈÇ¡lôï%„BH‰¢€B)‡( „BÊ! !„rˆB!¤¢€B)‡( „BÊ! !„rˆB!¤¢€B)‡þ|Œä2wu@ŽIEND®B`‚golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/contributor-tutorial/000077500000000000000000000000001335005366400307175ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/contributor-tutorial/.template/000077500000000000000000000000001335005366400326105ustar00rootroot00000000000000doc.go000066400000000000000000000003321335005366400336230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/contributor-tutorial/.template/* Package NAME manages and retrieves RESOURCE in the OpenStack SERVICE Service. Example to List RESOURCE Example to Create a RESOURCE Example to Update a RESOURCE Example to Delete a RESOURCE */ package RESOURCE requests.go000066400000000000000000000056571335005366400347500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/contributor-tutorial/.templatepackage RESOURCE import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to // the List request type ListOptsBuilder interface { ToResourceListQuery() (string, error) } // ListOpts provides options to filter the List results. type ListOpts struct { } // ToResourceListQuery formats a ListOpts into a query string. func (opts ListOpts) ToResourceListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List retrieves a list of RESOURCES. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToResourceListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ResourcePage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves details of a RESOURCE. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { ToResourceCreateMap() (map[string]interface{}, error) } // CreateOpts provides options used to create a RESOURCE. type CreateOpts struct { } // ToResourceCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToResourceCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "resource") } // Create creates a new RESOURCE. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToResourceCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) return } // Delete deletes a RESOURCE. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { ToResourceUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents parameters to update a RESOURCE. type UpdateOpts struct { } // ToUpdateCreateMap formats a UpdateOpts into an update request. func (opts UpdateOpts) ToResourceUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "resource") } // Update modifies the attributes of a RESOURCE. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToResourceUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000037551335005366400345730ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/contributor-tutorial/.templatepackage RESOURCE import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // RESOURCE represents... type Resource struct { } type commonResult struct { gophercloud.Result } // GetResult is the response from a Get operation. Call its Extract method // to interpret it as a RESOURCE. type GetResult struct { commonResult } // CreateResult is the response from a Create operation. Call its Extract method // to interpret it as a RESOURCE. type CreateResult struct { commonResult } // DeleteResult is the response from a Delete operation. Call its ExtractErr to // determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // UpdateResult is the result of an Update request. Call its Extract method to // interpret it as a RESOURCE. type UpdateResult struct { commonResult } // ResourcePage is a single page of RESOURCE results. type ResourcePage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a page of RESOURCES contains any results. func (r ResourcePage) IsEmpty() (bool, error) { resources, err := ExtractResources(r) return len(resources) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (r ResourcePage) NextPageURL() (string, error) { var s struct { Links struct { Next string `json:"next"` Previous string `json:"previous"` } `json:"links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return s.Links.Next, err } // ExtractResources returns a slice of Resources contained in a single page of // results. func ExtractResources(r pagination.Page) ([]Resource, error) { var s struct { Resources []Resource `json:"resources"` } err := (r.(ResourcePage)).ExtractInto(&s) return s.Resources, err } // Extract interprets any commonResult as a Resource. func (r commonResult) Extract() (*Resource, error) { var s struct { Resource *Resource `json:"resource"` } err := r.ExtractInto(&s) return s.Resource, err } testing/000077500000000000000000000000001335005366400342065ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/contributor-tutorial/.templatefixtures.go000066400000000000000000000067331335005366400364170ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/contributor-tutorial/.template/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/service/vN/resources" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListResult provides a single page of RESOURCE results. const ListResult = ` { } ` // GetResult provides a Get result. const GetResult = ` { } ` // CreateRequest provides the input to a Create request. const CreateRequest = ` { } ` // UpdateRequest provides the input to as Update request. const UpdateRequest = ` { } ` // UpdateResult provides an update result. const UpdateResult = ` { } ` // FirstResource is the first resource in the List request. var FirstResource = resources.Resource{} // SecondResource is the second resource in the List request. var SecondResource = resources.Resource{} // SecondResourceUpdated is how SecondResource should look after an Update. var SecondResourceUpdated = resources.Resource{} // ExpectedResourcesSlice is the slice of resources expected to be returned from ListResult. var ExpectedResourcesSlice = []resources.Resource{FirstResource, SecondResource} // HandleListResourceSuccessfully creates an HTTP handler at `/resources` on the // test handler mux that responds with a list of two resources. func HandleListResourceSuccessfully(t *testing.T) { th.Mux.HandleFunc("/resources", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListResult) }) } // HandleGetResourceSuccessfully creates an HTTP handler at `/resources` on the // test handler mux that responds with a single resource. func HandleGetResourceSuccessfully(t *testing.T) { th.Mux.HandleFunc("/resources/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetResult) }) } // HandleCreateResourceSuccessfully creates an HTTP handler at `/resources` on the // test handler mux that tests resource creation. func HandleCreateResourceSuccessfully(t *testing.T) { th.Mux.HandleFunc("/resources", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, GetResult) }) } // HandleDeleteResourceSuccessfully creates an HTTP handler at `/resources` on the // test handler mux that tests resource deletion. func HandleDeleteResourceSuccessfully(t *testing.T) { th.Mux.HandleFunc("/resources/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleUpdateResourceSuccessfully creates an HTTP handler at `/resources` on the // test handler mux that tests resource update. func HandleUpdateResourceSuccessfully(t *testing.T) { th.Mux.HandleFunc("/resources/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, UpdateResult) }) } requests_test.go000066400000000000000000000043241335005366400374520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/contributor-tutorial/.template/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/service/vN/resources" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListResources(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListResourcesSuccessfully(t) count := 0 err := resources.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := resources.ExtractResources(page) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedResourcesSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, count, 1) } func TestListResourcesAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListResourcesSuccessfully(t) allPages, err := resources.List(client.ServiceClient(), nil).AllPages() th.AssertNoErr(t, err) actual, err := resources.ExtractResources(allPages) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedResourcesSlice, actual) } func TestGetResource(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetResourceSuccessfully(t) actual, err := resources.Get(client.ServiceClient(), "9fe1d3").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, SecondResource, *actual) } func TestCreateResource(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateResourceSuccessfully(t) createOpts := resources.CreateOpts{ Name: "resource two", } actual, err := resources.Create(client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, SecondResource, *actual) } func TestDeleteResource(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteResourceSuccessfully(t) res := resources.Delete(client.ServiceClient(), "9fe1d3") th.AssertNoErr(t, res.Err) } func TestUpdateResource(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateResourceSuccessfully(t) updateOpts := resources.UpdateOpts{ Description: "Staging Resource", } actual, err := resources.Update(client.ServiceClient(), "9fe1d3", updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, SecondResourceUpdated, *actual) } urls.go000066400000000000000000000011321335005366400340420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/contributor-tutorial/.templatepackage RESOURCE import "github.com/gophercloud/gophercloud" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("resource") } func getURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("resource", id) } func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("resource") } func deleteURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("resource", id) } func updateURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("resource", id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/contributor-tutorial/README.md000066400000000000000000000007551335005366400322050ustar00rootroot00000000000000Contributor Tutorial ==================== This tutorial is to help new contributors become familiar with the processes used by the Gophercloud team when adding a new feature or fixing a bug. While we have a defined process for working on Gophercloud, we're very mindful that everyone is new to this in the beginning. Please reach out for help or ask for clarification if needed. No question is ever "dumb" or not worth our time answering. To begin, go to [Step 1](step-01-introduction.md). step-01-introduction.md000066400000000000000000000012021335005366400350650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/contributor-tutorialStep 1: Read Our Guides ======================== There are two introductory guides you should read before proceeding: * [CONTRIBUTING](/.github/CONTRIBUTING.md): The Contributing guide is a detailed document which describes the different ways you can contribute to Gophercloud and how to get started. This tutorial you're reading is very similar to that guide, but presented in a different way. We still recommend you read it over. * [STYLE](/docs/STYLEGUIDE.md): The Style Guide documents coding conventions used in the Gophercloud project. --- When you've finished reading those guides, proceed to [Step 2](step-02-issues.md). step-02-issues.md000066400000000000000000000111651335005366400336710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/contributor-tutorialStep 2: Create an Issue ======================== Every patch / Pull Request requires a corresponding issue. If you're fixing a bug for an existing issue, then there's no need to create a new issue. However, if no prior issue exists, you must create an issue. Reporting a Bug --------------- When reporting a bug, please try to provide as much information as you can. The following issues are good examples for reporting a bug: * https://github.com/gophercloud/gophercloud/issues/108 * https://github.com/gophercloud/gophercloud/issues/212 * https://github.com/gophercloud/gophercloud/issues/424 * https://github.com/gophercloud/gophercloud/issues/588 * https://github.com/gophercloud/gophercloud/issues/629 * https://github.com/gophercloud/gophercloud/issues/647 Feature Request --------------- If you've noticed that a feature is missing from Gophercloud, you'll also need to create an issue before doing any work. This is start a discussion about whether or not the feature should be included in Gophercloud. We don't want to want to see you put in hours of work only to learn that the feature is out of scope of the project. Feature requests can come in different forms: ### Adding a Feature to Gophercloud Core The "core" of Gophercloud is the code which supports API requests and responses: pagination, error handling, building request bodies, and parsing response bodies are all examples of core code. Modifications to core will usually have the most amount of discussion than other requests since a change to core will affect _all_ of Gophercloud. The following issues are examples of core change discussions: * https://github.com/gophercloud/gophercloud/issues/310 * https://github.com/gophercloud/gophercloud/issues/613 * https://github.com/gophercloud/gophercloud/issues/729 * https://github.com/gophercloud/gophercloud/issues/713 ### Adding a Missing Field If you've found a missing field in an existing struct, submit an issue to request having it added. These kinds of issues are pretty easy to report and resolve. You should also provide a link to the actual service's Python code which defines the missing field. The following issues are examples of missing fields: * https://github.com/gophercloud/gophercloud/issues/620 * https://github.com/gophercloud/gophercloud/issues/621 * https://github.com/gophercloud/gophercloud/issues/658 There's one situation which can make adding fields more difficult: if the field is part of an API extension rather than the base API itself. An example of this can be seen in [this](https://github.com/gophercloud/gophercloud/issues/749) issue. Here, a user reported fields missing in the `Get` function of `networking/v2/networks`. The fields reported missing weren't missing at all, they're just part of various Networking extensions located in `networking/v2/extensions`. ### Adding a Missing API Call If you've found a missing API action, create an issue with details of the action. For example: * https://github.com/gophercloud/gophercloud/issues/715 * https://github.com/gophercloud/gophercloud/issues/719 You'll want to make sure the API call is part of the upstream OpenStack project and not an extension created by a third-party or vendor. Gophercloud only supports the OpenStack projects proper. ### Adding a Missing API Suite Adding support to a missing suite of API calls will require more than one Pull Request. However, you can use a single issue for all PRs. Examples of issues which track the addition of a missing API suite are: * https://github.com/gophercloud/gophercloud/issues/539 * https://github.com/gophercloud/gophercloud/issues/555 * https://github.com/gophercloud/gophercloud/issues/571 * https://github.com/gophercloud/gophercloud/issues/583 * https://github.com/gophercloud/gophercloud/issues/605 Note how the issue breaks down the implementation by request types (Create, Update, Delete, Get, List). Also note how these issues provide links to the service's Python code. These links are not required for _issues_, but it's usually a good idea to provide them, anyway. These links _are required_ for PRs and that will be covered in detail in a later step of this tutorial. ### Adding a Missing OpenStack Project These kinds of feature additions are large undertakings. Adding support for an entire OpenStack project is something the Gophercloud team very much appreciates, but you should be prepared for several weeks of work and interaction with the Gophercloud team. An example of how to create an issue for an entire project can be seen here: * https://github.com/gophercloud/gophercloud/issues/723 --- With all of the above in mind, proceed to [Step 3](step-03-code-hunting.md) to learn about Code Hunting. step-03-code-hunting.md000066400000000000000000000100441335005366400347360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/contributor-tutorialStep 3: Code Hunting ==================== If you plan to submit a feature or bug fix to Gophercloud, you must be able to prove your code correctly works with the OpenStack service in question. Let's use the following issue as an example: [https://github.com/gophercloud/gophercloud/issues/621](https://github.com/gophercloud/gophercloud/issues/621). In this issue, there's a request being made to add support for `availability_zone_hints` to the `networking/v2/networks` package. Meaning, we want to change: ```go type Network struct { ID string `json:"id"` Name string `json:"name"` AdminStateUp bool `json:"admin_state_up"` Status string `json:"status"` Subnets []string `json:"subnets"` TenantID string `json:"tenant_id"` Shared bool `json:"shared"` } ``` to look like ```go type Network struct { ID string `json:"id"` Name string `json:"name"` AdminStateUp bool `json:"admin_state_up"` Status string `json:"status"` Subnets []string `json:"subnets"` TenantID string `json:"tenant_id"` Shared bool `json:"shared"` AvailabilityZoneHints []string `json:"availability_zone_hints"` } ``` We need to be sure that `availability_zone_hints` is a field which really does exist in the OpenStack Neutron project and it's not a field which was added as a customization to a single OpenStack cloud. In addition, we need to ensure that `availability_zone_hints` is really a `[]string` and not a different kind of type. One way of verifying this is through the [OpenStack API reference documentation](https://developer.openstack.org/api-ref/network/v2/). However, the API docs might either be incorrect or they might not provide all of the details we need to know in order to ensure this field is added correctly. > Note: when we say the API docs might be incorrect, we are _not_ implying > that the API docs aren't useful or that the contributors who work on the API > docs are wrong. OpenStack moves fast. Typos happen. Forgetting to update > documentation happens. Since the OpenStack service itself correctly accepts and processes the fields, the best source of information on how the field works is in the service code itself. Continuing on with using #621 as an example, we can find the definition of `availability_zone_hints` in the following piece of code: https://github.com/openstack/neutron/blob/8e9959725eda4063a318b4ba6af1e3494cad9e35/neutron/objects/network.py#L191 The above code confirms that `availability_zone_hints` is indeed part of the `Network` object and that its type is a list of strings (`[]string`). This example is a best-case situation: the code is relatively easy to find and it's simple to understand. However, there will be times when proving the implementation in the service code is difficult. Make no mistake, this is _not_ fun work. This can sometimes be more difficult than writing the actual patch for Gophercloud. However, this is an essential step to ensuring the feature or bug fix is correctly added to Gophercloud. Examples of good code hunting can be seen here: * https://github.com/gophercloud/gophercloud/issues/539 * https://github.com/gophercloud/gophercloud/issues/555 * https://github.com/gophercloud/gophercloud/issues/571 * https://github.com/gophercloud/gophercloud/issues/583 * https://github.com/gophercloud/gophercloud/issues/605 Code Hunting Tips ----------------- OpenStack projects differ from one to another. Code is organized in different ways. However, the following tips should be useful across all projects. * The logic which implements Create and Delete actions is usually either located in the "model" or "controller" portion of the code. * Use Github's search box to search for the exact field you're working on. Review all results to gain a good understanding of everywhere the field is used. * When adding a field, look for an object model or a schema of some sort. --- Proceed to [Step 4](step-04-acceptance-testing.md) to learn about Acceptance Testing. step-04-acceptance-testing.md000066400000000000000000000020151335005366400361130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/contributor-tutorialStep 4: Acceptance Testing ========================== If we haven't started working on the feature or bug fix, why are we talking about Acceptance Testing now? Before you implement a feature or bug fix, you _must_ be able to test your code in a working OpenStack environment. Please do not submit code which you have only tested with offline unit tests. Blindly submitting code is dangerous to the Gophercloud project. Developers from all over the world use Gophercloud in many different projects. If you submit code which is untested, it can cause these projects to break or become unstable. And, to be frank, submitting untested code will inevitably cause someone else to have to spend time fixing it. If you don't have an OpenStack environment to test with, we have lots of documentation [here](/acceptance) to help you build your own small OpenStack environment for testing. --- Once you've confirmed you are able to test your code, proceed to [Step 5](step-05-pull-requests.md) to (finally!) start working on a Pull Request. step-05-pull-requests.md000066400000000000000000000141101335005366400351770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/contributor-tutorialStep 5: Writing the Code ======================== At this point, you should have: - [x] Identified a feature or bug fix - [x] Opened an Issue about it - [x] Located the project's service code which validates the feature or fix - [x] Have an OpenStack environment available to test with Now it's time to write the actual code! We recommend reading over the [CONTRIBUTING](/.github/CONTRIBUTING.md) guide again as a refresh. Notably the [Getting Started](/.github/CONTRIBUTING.md#getting-started) section will help you set up a `git` repository correctly. We encourage you to browse the existing Gophercloud code to find examples of similar implementations. It would be a _very_ rare occurrence for you to be implementing something that hasn't already been done. Use the existing packages as templates and mirror the style, naming, and logic. Types of Pull Requests ---------------------- The amount of changes you plan to make will determine how much code you should submit as Pull Requests. ### A Single Bug Fix If you're implementing a single bug fix, then creating one `git` branch and submitting one Pull Request is fine. ### Adding a Single Field If you're adding a single field, then a single Pull Request is also fine. See [#662](https://github.com/gophercloud/gophercloud/pull/662) as an example of this. If you plan to add more than one missing field, you will need to open a Pull Request for _each_ field. ### Adding a Single API Call Single API calls can also be submitted as a single Pull Request. See [#722](https://github.com/gophercloud/gophercloud/pull/722) as an example of this. ### Adding a Suite of API Calls If you're adding support for a "suite" of API calls (meaning: Create, Update, Delete, Get), then you will need to create one Pull Request for _each_ call. The following Pull Requests are good examples of how to do this: * https://github.com/gophercloud/gophercloud/pull/584 * https://github.com/gophercloud/gophercloud/pull/586 * https://github.com/gophercloud/gophercloud/pull/587 * https://github.com/gophercloud/gophercloud/pull/594 You can also use the provided [template](/docs/contributor-tutorial/.template) as it contains a lot of the repeated boiler plate code seen in each resource. However, please make sure to thoroughly review and edit it as needed. Leaving templated portions in-place might be interpreted as rushing through the work and will require further rounds of review to fix. ### Adding an Entire OpenStack Project To add an entire OpenStack project, you must break each set of API calls into individual Pull Requests. Implementing an entire project can be thought of as implementing multiple API suites. An example of this can be seen from the Pull Requests referenced in [#723](https://github.com/gophercloud/gophercloud/issues/723). What to Include in a Pull Request --------------------------------- Each Pull Request should contain the following: 1. The actual Go code to implement the feature or bug fix 2. Unit tests 3. Acceptance tests 4. Documentation Whether you want to bundle all of the above into a single commit or multiple commits is up to you. Use your preferred style. ### Unit Tests Unit tests should provide basic validation that your code works as intended. Please do not use JSON fixtures from the API reference documentation. Please generate your own fixtures using the OpenStack environment you're [testing](step-04-acceptance-testing.md) with. ### Acceptance Tests Since unit tests are not run against an actual OpenStack environment, acceptance tests can arguably be more important. The acceptance tests that you include in your Pull Request should confirm that your implemented code works as intended with an actual OpenStack environment. ### Documentation All documentation in Gophercloud is done through in-line `godoc`. Please make sure to document all fields, functions, and methods appropriately. In addition, each package has a `doc.go` file which should be created or amended with details of your Pull Request, where appropriate. Dealing with Related Pull Requests ---------------------------------- If you plan to open more than one Pull Request, it's only natural that code from one Pull Request will be dependent on code from the prior Pull Request. There are two methods of handling this: ### Create Independent Pull Requests With this method, each Pull Request has all of the code to fully implement the code in question. Each Pull Request can be merged in any order because it's self contained. Use the following `git` workflow to implement this method: ```shell $ git checkout master $ git pull $ git checkout -b identityv3-regions-create $ (write your code) $ git add . $ git commit -m "Implementing Regions Create" $ git checkout master $ git checkout -b identityv3-regions-update $ (write your code) $ git add . $ git commit -m "Implementing Regions Update" ``` Advantages of this Method: * Pull Requests can be merged in any order * Additional commits to one Pull Request are independent of other Pull Requests Disadvantages of this Method: * There will be _a lot_ of duplicate code in each Pull Request * You will have to rebase all other Pull Requests and resolve a good amount of merge conflicts. ### Create a Chain of Pull Requests With this method, each Pull Request is based off of a previous Pull Request. Pull Requests will have to be merged in a specific order since there is a defined relationship. Use the following `git` workflow to implement this method: ```shell $ git checkout master $ git pull $ git checkout -b identityv3-regions-create $ (write your code) $ git add . $ git commit -m "Implementing Regions Create" $ git checkout -b identityv3-regions-update $ (write your code) $ git add . $ git commit -m "Implementing Regions Update" ``` Advantages of this Method: * Each Pull Request becomes smaller since you are building off of the last Disadvantages of this Method: * If a Pull Request requires changes, you will have to rebase _all_ child Pull Requests based off of the parent. The choice of method is up to you. --- Once you have your code written, submit a Pull Request to Gophercloud and proceed to [Step 6](step-06-code-review.md). step-06-code-review.md000066400000000000000000000061341335005366400345730ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/contributor-tutorialStep 6: Code Review =================== Once you've submitted a Pull Request, three things will happen automatically: 1. Travis-CI will run a set of simple tests: a. Unit Tests b. Code Formatting checks c. `go vet` checks 2. Coveralls will run a coverage test. 3. [OpenLab](https://openlabtesting.org/) will run acceptance tests. Depending on the results of the above, you might need to make additional changes to your code. While you're working on the finishing touches to your code, it is helpful to add a `[wip]` tag to the title of your Pull Request. You are most welcomed to take as much time as you need to work on your Pull Request. As well, take advantage of the automatic testing that is done to each commit. ### Travis-CI If Travis reports code formatting issues, please make sure to run `gofmt` on all of your code. Travis will also report errors with unit tests, so you should ensure those are fixed, too. ### Coveralls If Coveralls reports a decrease in test coverage, check and make sure you have provided unit tests. A decrease in test coverage is _sometimes_ unavoidable and ignorable. ### OpenLab OpenLab does not yet run a full suite of acceptance tests, so it's possible that the acceptance tests you've included were not run. When this happens, a core member for Gophercloud will run the tests manually. There are times when a core reviewer does not have access to the resources required to run the acceptance tests. When this happens, it is essential that you've run them yourself (See [Step 4](step-04.md)). Request a Code Review --------------------- When you feel your Pull Request is ready for review, please leave a comment requesting a code review. If you don't explicitly ask for a code review, a core member might not know the Pull Request is ready for review. Additionally, if there are parts of your implementation that you are unsure about, please ask for help. We're more than happy to provide advice. During the code review process, a core member will review the code you've submitted and either request changes or request additional information. Generally these requests fall under the following categories: 1. Code which needs to be reformatted (See our [Style Guide](/docs/STYLEGUIDE.md) for conventions used. 2. Requests for additional information about the validity of something. This might happen because the included supporting service code URLs don't have enough information. 3. Missing unit tests or acceptance tests. Submitting Changes ------------------ If a code review requires changes to be submitted, please do not squash your commits. Please only add new commits to the Pull Request. This is to help the code reviewer see only the changes that were made. It's Never Personal ------------------- Code review is a healthy exercise where a new set of eyes can sometimes spot items forgotten by the author. Please don't take change requests personally. Our intention is to ensure the code is correct before merging. --- Once the code has been reviewed and approved, a core member will merge your Pull Request. Please proceed to [Step 7](step-07-congratulations.md). step-07-congratulations.md000066400000000000000000000005441335005366400355760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/docs/contributor-tutorialStep 7: Congratulations! ======================== At this point your code is merged and you've either fixed a bug or added a new feature to Gophercloud! We completely understand that this has been a long process. We appreciate your patience as well as the time you have taken for working on this. You've made Gophercloud a better project with your work. golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/endpoint_search.go000066400000000000000000000060271335005366400272550ustar00rootroot00000000000000package gophercloud // Availability indicates to whom a specific service endpoint is accessible: // the internet at large, internal networks only, or only to administrators. // Different identity services use different terminology for these. Identity v2 // lists them as different kinds of URLs within the service catalog ("adminURL", // "internalURL", and "publicURL"), while v3 lists them as "Interfaces" in an // endpoint's response. type Availability string const ( // AvailabilityAdmin indicates that an endpoint is only available to // administrators. AvailabilityAdmin Availability = "admin" // AvailabilityPublic indicates that an endpoint is available to everyone on // the internet. AvailabilityPublic Availability = "public" // AvailabilityInternal indicates that an endpoint is only available within // the cluster's internal network. AvailabilityInternal Availability = "internal" ) // EndpointOpts specifies search criteria used by queries against an // OpenStack service catalog. The options must contain enough information to // unambiguously identify one, and only one, endpoint within the catalog. // // Usually, these are passed to service client factory functions in a provider // package, like "openstack.NewComputeV2()". type EndpointOpts struct { // Type [required] is the service type for the client (e.g., "compute", // "object-store"). Generally, this will be supplied by the service client // function, but a user-given value will be honored if provided. Type string // Name [optional] is the service name for the client (e.g., "nova") as it // appears in the service catalog. Services can have the same Type but a // different Name, which is why both Type and Name are sometimes needed. Name string // Region [required] is the geographic region in which the endpoint resides, // generally specifying which datacenter should house your resources. // Required only for services that span multiple regions. Region string // Availability [optional] is the visibility of the endpoint to be returned. // Valid types include the constants AvailabilityPublic, AvailabilityInternal, // or AvailabilityAdmin from this package. // // Availability is not required, and defaults to AvailabilityPublic. Not all // providers or services offer all Availability options. Availability Availability } /* EndpointLocator is an internal function to be used by provider implementations. It provides an implementation that locates a single endpoint from a service catalog for a specific ProviderClient based on user-provided EndpointOpts. The provider then uses it to discover related ServiceClients. */ type EndpointLocator func(EndpointOpts) (string, error) // ApplyDefaults is an internal method to be used by provider implementations. // // It sets EndpointOpts fields if not already set, including a default type. // Currently, EndpointOpts.Availability defaults to the public endpoint. func (eo *EndpointOpts) ApplyDefaults(t string) { if eo.Type == "" { eo.Type = t } if eo.Availability == "" { eo.Availability = AvailabilityPublic } } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/errors.go000066400000000000000000000342401335005366400254220ustar00rootroot00000000000000package gophercloud import ( "fmt" "strings" ) // BaseError is an error type that all other error types embed. type BaseError struct { DefaultErrString string Info string } func (e BaseError) Error() string { e.DefaultErrString = "An error occurred while executing a Gophercloud request." return e.choseErrString() } func (e BaseError) choseErrString() string { if e.Info != "" { return e.Info } return e.DefaultErrString } // ErrMissingInput is the error when input is required in a particular // situation but not provided by the user type ErrMissingInput struct { BaseError Argument string } func (e ErrMissingInput) Error() string { e.DefaultErrString = fmt.Sprintf("Missing input for argument [%s]", e.Argument) return e.choseErrString() } // ErrInvalidInput is an error type used for most non-HTTP Gophercloud errors. type ErrInvalidInput struct { ErrMissingInput Value interface{} } func (e ErrInvalidInput) Error() string { e.DefaultErrString = fmt.Sprintf("Invalid input provided for argument [%s]: [%+v]", e.Argument, e.Value) return e.choseErrString() } // ErrMissingEnvironmentVariable is the error when environment variable is required // in a particular situation but not provided by the user type ErrMissingEnvironmentVariable struct { BaseError EnvironmentVariable string } func (e ErrMissingEnvironmentVariable) Error() string { e.DefaultErrString = fmt.Sprintf("Missing environment variable [%s]", e.EnvironmentVariable) return e.choseErrString() } // ErrMissingAnyoneOfEnvironmentVariables is the error when anyone of the environment variables // is required in a particular situation but not provided by the user type ErrMissingAnyoneOfEnvironmentVariables struct { BaseError EnvironmentVariables []string } func (e ErrMissingAnyoneOfEnvironmentVariables) Error() string { e.DefaultErrString = fmt.Sprintf( "Missing one of the following environment variables [%s]", strings.Join(e.EnvironmentVariables, ", "), ) return e.choseErrString() } // ErrUnexpectedResponseCode is returned by the Request method when a response code other than // those listed in OkCodes is encountered. type ErrUnexpectedResponseCode struct { BaseError URL string Method string Expected []int Actual int Body []byte } func (e ErrUnexpectedResponseCode) Error() string { e.DefaultErrString = fmt.Sprintf( "Expected HTTP response code %v when accessing [%s %s], but got %d instead\n%s", e.Expected, e.Method, e.URL, e.Actual, e.Body, ) return e.choseErrString() } // ErrDefault400 is the default error type returned on a 400 HTTP response code. type ErrDefault400 struct { ErrUnexpectedResponseCode } // ErrDefault401 is the default error type returned on a 401 HTTP response code. type ErrDefault401 struct { ErrUnexpectedResponseCode } // ErrDefault403 is the default error type returned on a 403 HTTP response code. type ErrDefault403 struct { ErrUnexpectedResponseCode } // ErrDefault404 is the default error type returned on a 404 HTTP response code. type ErrDefault404 struct { ErrUnexpectedResponseCode } // ErrDefault405 is the default error type returned on a 405 HTTP response code. type ErrDefault405 struct { ErrUnexpectedResponseCode } // ErrDefault408 is the default error type returned on a 408 HTTP response code. type ErrDefault408 struct { ErrUnexpectedResponseCode } // ErrDefault429 is the default error type returned on a 429 HTTP response code. type ErrDefault429 struct { ErrUnexpectedResponseCode } // ErrDefault500 is the default error type returned on a 500 HTTP response code. type ErrDefault500 struct { ErrUnexpectedResponseCode } // ErrDefault503 is the default error type returned on a 503 HTTP response code. type ErrDefault503 struct { ErrUnexpectedResponseCode } func (e ErrDefault400) Error() string { e.DefaultErrString = fmt.Sprintf( "Bad request with: [%s %s], error message: %s", e.Method, e.URL, e.Body, ) return e.choseErrString() } func (e ErrDefault401) Error() string { return "Authentication failed" } func (e ErrDefault403) Error() string { e.DefaultErrString = fmt.Sprintf( "Request forbidden: [%s %s], error message: %s", e.Method, e.URL, e.Body, ) return e.choseErrString() } func (e ErrDefault404) Error() string { return "Resource not found" } func (e ErrDefault405) Error() string { return "Method not allowed" } func (e ErrDefault408) Error() string { return "The server timed out waiting for the request" } func (e ErrDefault429) Error() string { return "Too many requests have been sent in a given amount of time. Pause" + " requests, wait up to one minute, and try again." } func (e ErrDefault500) Error() string { return "Internal Server Error" } func (e ErrDefault503) Error() string { return "The service is currently unable to handle the request due to a temporary" + " overloading or maintenance. This is a temporary condition. Try again later." } // Err400er is the interface resource error types implement to override the error message // from a 400 error. type Err400er interface { Error400(ErrUnexpectedResponseCode) error } // Err401er is the interface resource error types implement to override the error message // from a 401 error. type Err401er interface { Error401(ErrUnexpectedResponseCode) error } // Err403er is the interface resource error types implement to override the error message // from a 403 error. type Err403er interface { Error403(ErrUnexpectedResponseCode) error } // Err404er is the interface resource error types implement to override the error message // from a 404 error. type Err404er interface { Error404(ErrUnexpectedResponseCode) error } // Err405er is the interface resource error types implement to override the error message // from a 405 error. type Err405er interface { Error405(ErrUnexpectedResponseCode) error } // Err408er is the interface resource error types implement to override the error message // from a 408 error. type Err408er interface { Error408(ErrUnexpectedResponseCode) error } // Err429er is the interface resource error types implement to override the error message // from a 429 error. type Err429er interface { Error429(ErrUnexpectedResponseCode) error } // Err500er is the interface resource error types implement to override the error message // from a 500 error. type Err500er interface { Error500(ErrUnexpectedResponseCode) error } // Err503er is the interface resource error types implement to override the error message // from a 503 error. type Err503er interface { Error503(ErrUnexpectedResponseCode) error } // ErrTimeOut is the error type returned when an operations times out. type ErrTimeOut struct { BaseError } func (e ErrTimeOut) Error() string { e.DefaultErrString = "A time out occurred" return e.choseErrString() } // ErrUnableToReauthenticate is the error type returned when reauthentication fails. type ErrUnableToReauthenticate struct { BaseError ErrOriginal error } func (e ErrUnableToReauthenticate) Error() string { e.DefaultErrString = fmt.Sprintf("Unable to re-authenticate: %s", e.ErrOriginal) return e.choseErrString() } // ErrErrorAfterReauthentication is the error type returned when reauthentication // succeeds, but an error occurs afterword (usually an HTTP error). type ErrErrorAfterReauthentication struct { BaseError ErrOriginal error } func (e ErrErrorAfterReauthentication) Error() string { e.DefaultErrString = fmt.Sprintf("Successfully re-authenticated, but got error executing request: %s", e.ErrOriginal) return e.choseErrString() } // ErrServiceNotFound is returned when no service in a service catalog matches // the provided EndpointOpts. This is generally returned by provider service // factory methods like "NewComputeV2()" and can mean that a service is not // enabled for your account. type ErrServiceNotFound struct { BaseError } func (e ErrServiceNotFound) Error() string { e.DefaultErrString = "No suitable service could be found in the service catalog." return e.choseErrString() } // ErrEndpointNotFound is returned when no available endpoints match the // provided EndpointOpts. This is also generally returned by provider service // factory methods, and usually indicates that a region was specified // incorrectly. type ErrEndpointNotFound struct { BaseError } func (e ErrEndpointNotFound) Error() string { e.DefaultErrString = "No suitable endpoint could be found in the service catalog." return e.choseErrString() } // ErrResourceNotFound is the error when trying to retrieve a resource's // ID by name and the resource doesn't exist. type ErrResourceNotFound struct { BaseError Name string ResourceType string } func (e ErrResourceNotFound) Error() string { e.DefaultErrString = fmt.Sprintf("Unable to find %s with name %s", e.ResourceType, e.Name) return e.choseErrString() } // ErrMultipleResourcesFound is the error when trying to retrieve a resource's // ID by name and multiple resources have the user-provided name. type ErrMultipleResourcesFound struct { BaseError Name string Count int ResourceType string } func (e ErrMultipleResourcesFound) Error() string { e.DefaultErrString = fmt.Sprintf("Found %d %ss matching %s", e.Count, e.ResourceType, e.Name) return e.choseErrString() } // ErrUnexpectedType is the error when an unexpected type is encountered type ErrUnexpectedType struct { BaseError Expected string Actual string } func (e ErrUnexpectedType) Error() string { e.DefaultErrString = fmt.Sprintf("Expected %s but got %s", e.Expected, e.Actual) return e.choseErrString() } func unacceptedAttributeErr(attribute string) string { return fmt.Sprintf("The base Identity V3 API does not accept authentication by %s", attribute) } func redundantWithTokenErr(attribute string) string { return fmt.Sprintf("%s may not be provided when authenticating with a TokenID", attribute) } func redundantWithUserID(attribute string) string { return fmt.Sprintf("%s may not be provided when authenticating with a UserID", attribute) } // ErrAPIKeyProvided indicates that an APIKey was provided but can't be used. type ErrAPIKeyProvided struct{ BaseError } func (e ErrAPIKeyProvided) Error() string { return unacceptedAttributeErr("APIKey") } // ErrTenantIDProvided indicates that a TenantID was provided but can't be used. type ErrTenantIDProvided struct{ BaseError } func (e ErrTenantIDProvided) Error() string { return unacceptedAttributeErr("TenantID") } // ErrTenantNameProvided indicates that a TenantName was provided but can't be used. type ErrTenantNameProvided struct{ BaseError } func (e ErrTenantNameProvided) Error() string { return unacceptedAttributeErr("TenantName") } // ErrUsernameWithToken indicates that a Username was provided, but token authentication is being used instead. type ErrUsernameWithToken struct{ BaseError } func (e ErrUsernameWithToken) Error() string { return redundantWithTokenErr("Username") } // ErrUserIDWithToken indicates that a UserID was provided, but token authentication is being used instead. type ErrUserIDWithToken struct{ BaseError } func (e ErrUserIDWithToken) Error() string { return redundantWithTokenErr("UserID") } // ErrDomainIDWithToken indicates that a DomainID was provided, but token authentication is being used instead. type ErrDomainIDWithToken struct{ BaseError } func (e ErrDomainIDWithToken) Error() string { return redundantWithTokenErr("DomainID") } // ErrDomainNameWithToken indicates that a DomainName was provided, but token authentication is being used instead.s type ErrDomainNameWithToken struct{ BaseError } func (e ErrDomainNameWithToken) Error() string { return redundantWithTokenErr("DomainName") } // ErrUsernameOrUserID indicates that neither username nor userID are specified, or both are at once. type ErrUsernameOrUserID struct{ BaseError } func (e ErrUsernameOrUserID) Error() string { return "Exactly one of Username and UserID must be provided for password authentication" } // ErrDomainIDWithUserID indicates that a DomainID was provided, but unnecessary because a UserID is being used. type ErrDomainIDWithUserID struct{ BaseError } func (e ErrDomainIDWithUserID) Error() string { return redundantWithUserID("DomainID") } // ErrDomainNameWithUserID indicates that a DomainName was provided, but unnecessary because a UserID is being used. type ErrDomainNameWithUserID struct{ BaseError } func (e ErrDomainNameWithUserID) Error() string { return redundantWithUserID("DomainName") } // ErrDomainIDOrDomainName indicates that a username was provided, but no domain to scope it. // It may also indicate that both a DomainID and a DomainName were provided at once. type ErrDomainIDOrDomainName struct{ BaseError } func (e ErrDomainIDOrDomainName) Error() string { return "You must provide exactly one of DomainID or DomainName to authenticate by Username" } // ErrMissingPassword indicates that no password was provided and no token is available. type ErrMissingPassword struct{ BaseError } func (e ErrMissingPassword) Error() string { return "You must provide a password to authenticate" } // ErrScopeDomainIDOrDomainName indicates that a domain ID or Name was required in a Scope, but not present. type ErrScopeDomainIDOrDomainName struct{ BaseError } func (e ErrScopeDomainIDOrDomainName) Error() string { return "You must provide exactly one of DomainID or DomainName in a Scope with ProjectName" } // ErrScopeProjectIDOrProjectName indicates that both a ProjectID and a ProjectName were provided in a Scope. type ErrScopeProjectIDOrProjectName struct{ BaseError } func (e ErrScopeProjectIDOrProjectName) Error() string { return "You must provide at most one of ProjectID or ProjectName in a Scope" } // ErrScopeProjectIDAlone indicates that a ProjectID was provided with other constraints in a Scope. type ErrScopeProjectIDAlone struct{ BaseError } func (e ErrScopeProjectIDAlone) Error() string { return "ProjectID must be supplied alone in a Scope" } // ErrScopeEmpty indicates that no credentials were provided in a Scope. type ErrScopeEmpty struct{ BaseError } func (e ErrScopeEmpty) Error() string { return "You must provide either a Project or Domain in a Scope" } // ErrAppCredMissingSecret indicates that no Application Credential Secret was provided with Application Credential ID or Name type ErrAppCredMissingSecret struct{ BaseError } func (e ErrAppCredMissingSecret) Error() string { return "You must provide an Application Credential Secret" } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/internal/000077500000000000000000000000001335005366400253705ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/internal/pkg.go000066400000000000000000000000211335005366400264710ustar00rootroot00000000000000package internal golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/internal/testing/000077500000000000000000000000001335005366400270455ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/internal/testing/pkg.go000066400000000000000000000000201335005366400301450ustar00rootroot00000000000000package testing golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/internal/testing/util_test.go000066400000000000000000000016131335005366400314110ustar00rootroot00000000000000package testing import ( "reflect" "testing" "github.com/gophercloud/gophercloud/internal" ) func TestRemainingKeys(t *testing.T) { type User struct { UserID string `json:"user_id"` Username string `json:"username"` Location string `json:"-"` CreatedAt string `json:"-"` Status string IsAdmin bool } userResponse := map[string]interface{}{ "user_id": "abcd1234", "username": "jdoe", "location": "Hawaii", "created_at": "2017-06-08T02:49:03.000000", "status": "active", "is_admin": "true", "custom_field": "foo", } expected := map[string]interface{}{ "created_at": "2017-06-08T02:49:03.000000", "is_admin": "true", "custom_field": "foo", } actual := internal.RemainingKeys(User{}, userResponse) isEqual := reflect.DeepEqual(expected, actual) if !isEqual { t.Fatalf("expected %s but got %s", expected, actual) } } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/internal/util.go000066400000000000000000000016441335005366400267010ustar00rootroot00000000000000package internal import ( "reflect" "strings" ) // RemainingKeys will inspect a struct and compare it to a map. Any struct // field that does not have a JSON tag that matches a key in the map or // a matching lower-case field in the map will be returned as an extra. // // This is useful for determining the extra fields returned in response bodies // for resources that can contain an arbitrary or dynamic number of fields. func RemainingKeys(s interface{}, m map[string]interface{}) (extras map[string]interface{}) { extras = make(map[string]interface{}) for k, v := range m { extras[k] = v } valueOf := reflect.ValueOf(s) typeOf := reflect.TypeOf(s) for i := 0; i < valueOf.NumField(); i++ { field := typeOf.Field(i) lowerField := strings.ToLower(field.Name) delete(extras, lowerField) if tagValue := field.Tag.Get("json"); tagValue != "" && tagValue != "-" { delete(extras, tagValue) } } return } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/000077500000000000000000000000001335005366400255435ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/auth_env.go000066400000000000000000000062051335005366400277060ustar00rootroot00000000000000package openstack import ( "os" "github.com/gophercloud/gophercloud" ) var nilOptions = gophercloud.AuthOptions{} /* AuthOptionsFromEnv fills out an identity.AuthOptions structure with the settings found on the various OpenStack OS_* environment variables. The following variables provide sources of truth: OS_AUTH_URL, OS_USERNAME, OS_PASSWORD, OS_TENANT_ID, and OS_TENANT_NAME. Of these, OS_USERNAME, OS_PASSWORD, and OS_AUTH_URL must have settings, or an error will result. OS_TENANT_ID, OS_TENANT_NAME, OS_PROJECT_ID, and OS_PROJECT_NAME are optional. OS_TENANT_ID and OS_TENANT_NAME are mutually exclusive to OS_PROJECT_ID and OS_PROJECT_NAME. If OS_PROJECT_ID and OS_PROJECT_NAME are set, they will still be referred as "tenant" in Gophercloud. To use this function, first set the OS_* environment variables (for example, by sourcing an `openrc` file), then: opts, err := openstack.AuthOptionsFromEnv() provider, err := openstack.AuthenticatedClient(opts) */ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { authURL := os.Getenv("OS_AUTH_URL") username := os.Getenv("OS_USERNAME") userID := os.Getenv("OS_USERID") password := os.Getenv("OS_PASSWORD") tenantID := os.Getenv("OS_TENANT_ID") tenantName := os.Getenv("OS_TENANT_NAME") domainID := os.Getenv("OS_DOMAIN_ID") domainName := os.Getenv("OS_DOMAIN_NAME") applicationCredentialID := os.Getenv("OS_APPLICATION_CREDENTIAL_ID") applicationCredentialName := os.Getenv("OS_APPLICATION_CREDENTIAL_NAME") applicationCredentialSecret := os.Getenv("OS_APPLICATION_CREDENTIAL_SECRET") // If OS_PROJECT_ID is set, overwrite tenantID with the value. if v := os.Getenv("OS_PROJECT_ID"); v != "" { tenantID = v } // If OS_PROJECT_NAME is set, overwrite tenantName with the value. if v := os.Getenv("OS_PROJECT_NAME"); v != "" { tenantName = v } if authURL == "" { err := gophercloud.ErrMissingEnvironmentVariable{ EnvironmentVariable: "OS_AUTH_URL", } return nilOptions, err } if username == "" && userID == "" { err := gophercloud.ErrMissingAnyoneOfEnvironmentVariables{ EnvironmentVariables: []string{"OS_USERNAME", "OS_USERID"}, } return nilOptions, err } if password == "" && applicationCredentialID == "" && applicationCredentialName == "" { err := gophercloud.ErrMissingEnvironmentVariable{ EnvironmentVariable: "OS_PASSWORD", } return nilOptions, err } if (applicationCredentialID != "" || applicationCredentialName != "") && applicationCredentialSecret == "" { err := gophercloud.ErrMissingEnvironmentVariable{ EnvironmentVariable: "OS_APPLICATION_CREDENTIAL_SECRET", } return nilOptions, err } ao := gophercloud.AuthOptions{ IdentityEndpoint: authURL, UserID: userID, Username: username, Password: password, TenantID: tenantID, TenantName: tenantName, DomainID: domainID, DomainName: domainName, ApplicationCredentialID: applicationCredentialID, ApplicationCredentialName: applicationCredentialName, ApplicationCredentialSecret: applicationCredentialSecret, } return ao, nil } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/000077500000000000000000000000001335005366400302225ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/000077500000000000000000000000001335005366400324215ustar00rootroot00000000000000quotasets/000077500000000000000000000000001335005366400343725ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensionsdoc.go000066400000000000000000000015251335005366400354710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/quotasets/* Package quotasets enables retrieving and managing Block Storage quotas. Example to Get a Quota Set quotaset, err := quotasets.Get(blockStorageClient, "project-id").Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", quotaset) Example to Get Quota Set Usage quotaset, err := quotasets.GetUsage(blockStorageClient, "project-id").Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", quotaset) Example to Update a Quota Set updateOpts := quotasets.UpdateOpts{ Volumes: gophercloud.IntToPointer(100), } quotaset, err := quotasets.Update(blockStorageClient, "project-id", updateOpts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", quotaset) Example to Delete a Quota Set err := quotasets.Delete(blockStorageClient, "project-id").ExtractErr() if err != nil { panic(err) } */ package quotasets requests.go000066400000000000000000000064061335005366400366020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/quotasetspackage quotasets import ( "fmt" "github.com/gophercloud/gophercloud" ) // Get returns public data about a previously created QuotaSet. func Get(client *gophercloud.ServiceClient, projectID string) (r GetResult) { _, r.Err = client.Get(getURL(client, projectID), &r.Body, nil) return } // GetDefaults returns public data about the project's default block storage quotas. func GetDefaults(client *gophercloud.ServiceClient, projectID string) (r GetResult) { _, r.Err = client.Get(getDefaultsURL(client, projectID), &r.Body, nil) return } // GetUsage returns detailed public data about a previously created QuotaSet. func GetUsage(client *gophercloud.ServiceClient, projectID string) (r GetUsageResult) { u := fmt.Sprintf("%s?usage=true", getURL(client, projectID)) _, r.Err = client.Get(u, &r.Body, nil) return } // Updates the quotas for the given projectID and returns the new QuotaSet. func Update(client *gophercloud.ServiceClient, projectID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToBlockStorageQuotaUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Put(updateURL(client, projectID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return r } // UpdateOptsBuilder enables extensins to add parameters to the update request. type UpdateOptsBuilder interface { // Extra specific name to prevent collisions with interfaces for other quotas // (e.g. neutron) ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) } // ToBlockStorageQuotaUpdateMap builds the update options into a serializable // format. func (opts UpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "quota_set") } // Options for Updating the quotas of a Tenant. // All int-values are pointers so they can be nil if they are not needed. // You can use gopercloud.IntToPointer() for convenience type UpdateOpts struct { // Volumes is the number of volumes that are allowed for each project. Volumes *int `json:"volumes,omitempty"` // Snapshots is the number of snapshots that are allowed for each project. Snapshots *int `json:"snapshots,omitempty"` // Gigabytes is the size (GB) of volumes and snapshots that are allowed for // each project. Gigabytes *int `json:"gigabytes,omitempty"` // PerVolumeGigabytes is the size (GB) of volumes and snapshots that are // allowed for each project and the specifed volume type. PerVolumeGigabytes *int `json:"per_volume_gigabytes,omitempty"` // Backups is the number of backups that are allowed for each project. Backups *int `json:"backups,omitempty"` // BackupGigabytes is the size (GB) of backups that are allowed for each // project. BackupGigabytes *int `json:"backup_gigabytes,omitempty"` // Groups is the number of groups that are allowed for each project. Groups *int `json:"groups,omitempty"` // Force will update the quotaset even if the quota has already been used // and the reserved quota exceeds the new quota. Force bool `json:"force,omitempty"` } // Resets the quotas for the given tenant to their default values. func Delete(client *gophercloud.ServiceClient, projectID string) (r DeleteResult) { _, r.Err = client.Delete(updateURL(client, projectID), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000127431335005366400364310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/quotasetspackage quotasets import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // QuotaSet is a set of operational limits that allow for control of block // storage usage. type QuotaSet struct { // ID is project associated with this QuotaSet. ID string `json:"id"` // Volumes is the number of volumes that are allowed for each project. Volumes int `json:"volumes"` // Snapshots is the number of snapshots that are allowed for each project. Snapshots int `json:"snapshots"` // Gigabytes is the size (GB) of volumes and snapshots that are allowed for // each project. Gigabytes int `json:"gigabytes"` // PerVolumeGigabytes is the size (GB) of volumes and snapshots that are // allowed for each project and the specifed volume type. PerVolumeGigabytes int `json:"per_volume_gigabytes"` // Backups is the number of backups that are allowed for each project. Backups int `json:"backups"` // BackupGigabytes is the size (GB) of backups that are allowed for each // project. BackupGigabytes int `json:"backup_gigabytes"` } // QuotaUsageSet represents details of both operational limits of block // storage resources and the current usage of those resources. type QuotaUsageSet struct { // ID is the project ID associated with this QuotaUsageSet. ID string `json:"id"` // Volumes is the volume usage information for this project, including // in_use, limit, reserved and allocated attributes. Note: allocated // attribute is available only when nested quota is enabled. Volumes QuotaUsage `json:"volumes"` // Snapshots is the snapshot usage information for this project, including // in_use, limit, reserved and allocated attributes. Note: allocated // attribute is available only when nested quota is enabled. Snapshots QuotaUsage `json:"snapshots"` // Gigabytes is the size (GB) usage information of volumes and snapshots // for this project, including in_use, limit, reserved and allocated // attributes. Note: allocated attribute is available only when nested // quota is enabled. Gigabytes QuotaUsage `json:"gigabytes"` // PerVolumeGigabytes is the size (GB) usage information for each volume, // including in_use, limit, reserved and allocated attributes. Note: // allocated attribute is available only when nested quota is enabled and // only limit is meaningful here. PerVolumeGigabytes QuotaUsage `json:"per_volume_gigabytes"` // Backups is the backup usage information for this project, including // in_use, limit, reserved and allocated attributes. Note: allocated // attribute is available only when nested quota is enabled. Backups QuotaUsage `json:"backups"` // BackupGigabytes is the size (GB) usage information of backup for this // project, including in_use, limit, reserved and allocated attributes. // Note: allocated attribute is available only when nested quota is // enabled. BackupGigabytes QuotaUsage `json:"backup_gigabytes"` } // QuotaUsage is a set of details about a single operational limit that allows // for control of block storage usage. type QuotaUsage struct { // InUse is the current number of provisioned resources of the given type. InUse int `json:"in_use"` // Allocated is the current number of resources of a given type allocated // for use. It is only available when nested quota is enabled. Allocated int `json:"allocated"` // Reserved is a transitional state when a claim against quota has been made // but the resource is not yet fully online. Reserved int `json:"reserved"` // Limit is the maximum number of a given resource that can be // allocated/provisioned. This is what "quota" usually refers to. Limit int `json:"limit"` } // QuotaSetPage stores a single page of all QuotaSet results from a List call. type QuotaSetPage struct { pagination.SinglePageBase } // IsEmpty determines whether or not a QuotaSetsetPage is empty. func (r QuotaSetPage) IsEmpty() (bool, error) { ks, err := ExtractQuotaSets(r) return len(ks) == 0, err } // ExtractQuotaSets interprets a page of results as a slice of QuotaSets. func ExtractQuotaSets(r pagination.Page) ([]QuotaSet, error) { var s struct { QuotaSets []QuotaSet `json:"quotas"` } err := (r.(QuotaSetPage)).ExtractInto(&s) return s.QuotaSets, err } type quotaResult struct { gophercloud.Result } // Extract is a method that attempts to interpret any QuotaSet resource response // as a QuotaSet struct. func (r quotaResult) Extract() (*QuotaSet, error) { var s struct { QuotaSet *QuotaSet `json:"quota_set"` } err := r.ExtractInto(&s) return s.QuotaSet, err } // GetResult is the response from a Get operation. Call its Extract method to // interpret it as a QuotaSet. type GetResult struct { quotaResult } // UpdateResult is the response from a Update operation. Call its Extract method // to interpret it as a QuotaSet. type UpdateResult struct { quotaResult } type quotaUsageResult struct { gophercloud.Result } // GetUsageResult is the response from a Get operation. Call its Extract // method to interpret it as a QuotaSet. type GetUsageResult struct { quotaUsageResult } // Extract is a method that attempts to interpret any QuotaUsageSet resource // response as a set of QuotaUsageSet structs. func (r quotaUsageResult) Extract() (QuotaUsageSet, error) { var s struct { QuotaUsageSet QuotaUsageSet `json:"quota_set"` } err := r.ExtractInto(&s) return s.QuotaUsageSet, err } // DeleteResult is the response from a Delete operation. Call its ExtractErr // method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400360475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/quotasetsdoc.go000066400000000000000000000000501335005366400371360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/quotasets/testing// quotasets unit tests package testing fixtures.go000066400000000000000000000077411335005366400402600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/quotasets/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/quotasets" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const FirstTenantID = "555544443333222211110000ffffeeee" var getExpectedJSONBody = ` { "quota_set" : { "volumes" : 8, "snapshots" : 9, "gigabytes" : 10, "per_volume_gigabytes" : 11, "backups" : 12, "backup_gigabytes" : 13 } }` var getExpectedQuotaSet = quotasets.QuotaSet{ Volumes: 8, Snapshots: 9, Gigabytes: 10, PerVolumeGigabytes: 11, Backups: 12, BackupGigabytes: 13, } var getUsageExpectedJSONBody = ` { "quota_set" : { "id": "555544443333222211110000ffffeeee", "volumes" : { "in_use": 15, "limit": 16, "reserved": 17 }, "snapshots" : { "in_use": 18, "limit": 19, "reserved": 20 }, "gigabytes" : { "in_use": 21, "limit": 22, "reserved": 23 }, "per_volume_gigabytes" : { "in_use": 24, "limit": 25, "reserved": 26 }, "backups" : { "in_use": 27, "limit": 28, "reserved": 29 }, "backup_gigabytes" : { "in_use": 30, "limit": 31, "reserved": 32 } } } }` var getUsageExpectedQuotaSet = quotasets.QuotaUsageSet{ ID: FirstTenantID, Volumes: quotasets.QuotaUsage{InUse: 15, Limit: 16, Reserved: 17}, Snapshots: quotasets.QuotaUsage{InUse: 18, Limit: 19, Reserved: 20}, Gigabytes: quotasets.QuotaUsage{InUse: 21, Limit: 22, Reserved: 23}, PerVolumeGigabytes: quotasets.QuotaUsage{InUse: 24, Limit: 25, Reserved: 26}, Backups: quotasets.QuotaUsage{InUse: 27, Limit: 28, Reserved: 29}, BackupGigabytes: quotasets.QuotaUsage{InUse: 30, Limit: 31, Reserved: 32}, } var fullUpdateExpectedJSONBody = ` { "quota_set": { "volumes": 8, "snapshots": 9, "gigabytes": 10, "per_volume_gigabytes": 11, "backups": 12, "backup_gigabytes": 13 } }` var fullUpdateOpts = quotasets.UpdateOpts{ Volumes: gophercloud.IntToPointer(8), Snapshots: gophercloud.IntToPointer(9), Gigabytes: gophercloud.IntToPointer(10), PerVolumeGigabytes: gophercloud.IntToPointer(11), Backups: gophercloud.IntToPointer(12), BackupGigabytes: gophercloud.IntToPointer(13), } var fullUpdateExpectedQuotaSet = quotasets.QuotaSet{ Volumes: 8, Snapshots: 9, Gigabytes: 10, PerVolumeGigabytes: 11, Backups: 12, BackupGigabytes: 13, } var partialUpdateExpectedJSONBody = ` { "quota_set": { "volumes": 200, "snapshots": 0, "gigabytes": 0, "per_volume_gigabytes": 0, "backups": 0, "backup_gigabytes": 0 } }` var partialUpdateOpts = quotasets.UpdateOpts{ Volumes: gophercloud.IntToPointer(200), Snapshots: gophercloud.IntToPointer(0), Gigabytes: gophercloud.IntToPointer(0), PerVolumeGigabytes: gophercloud.IntToPointer(0), Backups: gophercloud.IntToPointer(0), BackupGigabytes: gophercloud.IntToPointer(0), } var partiualUpdateExpectedQuotaSet = quotasets.QuotaSet{Volumes: 200} // HandleSuccessfulRequest configures the test server to respond to an HTTP request. func HandleSuccessfulRequest(t *testing.T, httpMethod, uriPath, jsonOutput string, uriQueryParams map[string]string) { th.Mux.HandleFunc(uriPath, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, httpMethod) th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") if uriQueryParams != nil { th.TestFormValues(t, r, uriQueryParams) } fmt.Fprintf(w, jsonOutput) }) } // HandleDeleteSuccessfully tests quotaset deletion. func HandleDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusOK) }) } requests_test.go000066400000000000000000000050411335005366400413100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/quotasets/testingpackage testing import ( "errors" "testing" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/quotasets" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() uriQueryParms := map[string]string{} HandleSuccessfulRequest(t, "GET", "/os-quota-sets/"+FirstTenantID, getExpectedJSONBody, uriQueryParms) actual, err := quotasets.Get(client.ServiceClient(), FirstTenantID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &getExpectedQuotaSet, actual) } func TestGetUsage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() uriQueryParms := map[string]string{"usage": "true"} HandleSuccessfulRequest(t, "GET", "/os-quota-sets/"+FirstTenantID, getUsageExpectedJSONBody, uriQueryParms) actual, err := quotasets.GetUsage(client.ServiceClient(), FirstTenantID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, getUsageExpectedQuotaSet, actual) } func TestFullUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() uriQueryParms := map[string]string{} HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, fullUpdateExpectedJSONBody, uriQueryParms) actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, fullUpdateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &fullUpdateExpectedQuotaSet, actual) } func TestPartialUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() uriQueryParms := map[string]string{} HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, partialUpdateExpectedJSONBody, uriQueryParms) actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, partialUpdateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &partiualUpdateExpectedQuotaSet, actual) } type ErrorUpdateOpts quotasets.UpdateOpts func (opts ErrorUpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) { return nil, errors.New("This is an error") } func TestErrorInToBlockStorageQuotaUpdateMap(t *testing.T) { opts := &ErrorUpdateOpts{} th.SetupHTTP() defer th.TeardownHTTP() HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, "", nil) _, err := quotasets.Update(client.ServiceClient(), FirstTenantID, opts).Extract() if err == nil { t.Fatal("Error handling failed") } } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSuccessfully(t) err := quotasets.Delete(client.ServiceClient(), FirstTenantID).ExtractErr() th.AssertNoErr(t, err) } urls.go000066400000000000000000000010651335005366400357100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/quotasetspackage quotasets import "github.com/gophercloud/gophercloud" const resourcePath = "os-quota-sets" func getURL(c *gophercloud.ServiceClient, projectID string) string { return c.ServiceURL(resourcePath, projectID) } func getDefaultsURL(c *gophercloud.ServiceClient, projectID string) string { return c.ServiceURL(resourcePath, projectID, "defaults") } func updateURL(c *gophercloud.ServiceClient, projectID string) string { return getURL(c, projectID) } func deleteURL(c *gophercloud.ServiceClient, projectID string) string { return getURL(c, projectID) } schedulerstats/000077500000000000000000000000001335005366400353775ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensionsdoc.go000066400000000000000000000007051335005366400364750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/schedulerstats/* Package schedulerstats returns information about block storage pool capacity and utilisation. Example: listOpts := schedulerstats.ListOpts{ Detail: true, } allPages, err := schedulerstats.List(client, listOpts).AllPages() if err != nil { panic(err) } allStats, err := schedulerstats.ExtractStoragePools(allPages) if err != nil { panic(err) } for _, stat := range allStats { fmt.Printf("%+v\n", stat) } */ package schedulerstats requests.go000066400000000000000000000024041335005366400376010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/schedulerstatspackage schedulerstats import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToStoragePoolsListQuery() (string, error) } // ListOpts controls the view of data returned (e.g globally or per project) // via tenant_id and the verbosity via detail. type ListOpts struct { // ID of the tenant to look up storage pools for. TenantID string `q:"tenant_id"` // Whether to list extended details. Detail bool `q:"detail"` } // ToStoragePoolsListQuery formats a ListOpts into a query string. func (opts ListOpts) ToStoragePoolsListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List makes a request against the API to list storage pool information. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := storagePoolsListURL(client) if opts != nil { query, err := opts.ToStoragePoolsListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return StoragePoolPage{pagination.SinglePageBase(r)} }) } results.go000066400000000000000000000060371335005366400374350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/schedulerstatspackage schedulerstats import ( "encoding/json" "math" "github.com/gophercloud/gophercloud/pagination" ) // Capabilities represents the information of an individual StoragePool. type Capabilities struct { // The following fields should be present in all storage drivers. DriverVersion string `json:"driver_version"` FreeCapacityGB float64 `json:"-"` StorageProtocol string `json:"storage_protocol"` TotalCapacityGB float64 `json:"-"` VendorName string `json:"vendor_name"` VolumeBackendName string `json:"volume_backend_name"` // The following fields are optional and may have empty values depending // on the storage driver in use. ReservedPercentage int64 `json:"reserved_percentage"` LocationInfo string `json:"location_info"` QoSSupport bool `json:"QoS_support"` ProvisionedCapacityGB float64 `json:"provisioned_capacity_gb"` MaxOverSubscriptionRatio string `json:"max_over_subscription_ratio"` ThinProvisioningSupport bool `json:"thin_provisioning_support"` ThickProvisioningSupport bool `json:"thick_provisioning_support"` TotalVolumes int64 `json:"total_volumes"` FilterFunction string `json:"filter_function"` GoodnessFuction string `json:"goodness_function"` Multiattach bool `json:"multiattach"` SparseCopyVolume bool `json:"sparse_copy_volume"` } // StoragePool represents an individual StoragePool retrieved from the // schedulerstats API. type StoragePool struct { Name string `json:"name"` Capabilities Capabilities `json:"capabilities"` } func (r *Capabilities) UnmarshalJSON(b []byte) error { type tmp Capabilities var s struct { tmp FreeCapacityGB interface{} `json:"free_capacity_gb"` TotalCapacityGB interface{} `json:"total_capacity_gb"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Capabilities(s.tmp) // Generic function to parse a capacity value which may be a numeric // value, "unknown", or "infinite" parseCapacity := func(capacity interface{}) float64 { if capacity != nil { switch capacity.(type) { case float64: return capacity.(float64) case string: if capacity.(string) == "infinite" { return math.Inf(1) } } } return 0.0 } r.FreeCapacityGB = parseCapacity(s.FreeCapacityGB) r.TotalCapacityGB = parseCapacity(s.TotalCapacityGB) return nil } // StoragePoolPage is a single page of all List results. type StoragePoolPage struct { pagination.SinglePageBase } // IsEmpty satisfies the IsEmpty method of the Page interface. It returns true // if a List contains no results. func (page StoragePoolPage) IsEmpty() (bool, error) { va, err := ExtractStoragePools(page) return len(va) == 0, err } // ExtractStoragePools takes a List result and extracts the collection of // StoragePools returned by the API. func ExtractStoragePools(p pagination.Page) ([]StoragePool, error) { var s struct { StoragePools []StoragePool `json:"pools"` } err := (p.(StoragePoolPage)).ExtractInto(&s) return s.StoragePools, err } testing/000077500000000000000000000000001335005366400370545ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/schedulerstatsfixtures.go000066400000000000000000000061071335005366400412600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/schedulerstats/testingpackage testing import ( "fmt" "math" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/schedulerstats" "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const StoragePoolsListBody = ` { "pools": [ { "name": "rbd:cinder.volumes.ssd@cinder.volumes.ssd#cinder.volumes.ssd" }, { "name": "rbd:cinder.volumes.hdd@cinder.volumes#cinder.volumes.hdd" } ] } ` const StoragePoolsListBodyDetail = ` { "pools": [ { "capabilities": { "driver_version": "1.2.0", "filter_function": null, "free_capacity_gb": 64765, "goodness_function": null, "multiattach": false, "reserved_percentage": 0, "storage_protocol": "ceph", "timestamp": "2016-11-24T10:33:51.248360", "total_capacity_gb": 787947.93, "vendor_name": "Open Source", "volume_backend_name": "cinder.volumes.ssd" }, "name": "rbd:cinder.volumes.ssd@cinder.volumes.ssd#cinder.volumes.ssd" }, { "capabilities": { "driver_version": "1.2.0", "filter_function": null, "free_capacity_gb": "unknown", "goodness_function": null, "multiattach": false, "reserved_percentage": 0, "storage_protocol": "ceph", "timestamp": "2016-11-24T10:33:43.138628", "total_capacity_gb": "infinite", "vendor_name": "Open Source", "volume_backend_name": "cinder.volumes.hdd" }, "name": "rbd:cinder.volumes.hdd@cinder.volumes.hdd#cinder.volumes.hdd" } ] } ` var ( StoragePoolFake1 = schedulerstats.StoragePool{ Name: "rbd:cinder.volumes.ssd@cinder.volumes.ssd#cinder.volumes.ssd", Capabilities: schedulerstats.Capabilities{ DriverVersion: "1.2.0", FreeCapacityGB: 64765, StorageProtocol: "ceph", TotalCapacityGB: 787947.93, VendorName: "Open Source", VolumeBackendName: "cinder.volumes.ssd", }, } StoragePoolFake2 = schedulerstats.StoragePool{ Name: "rbd:cinder.volumes.hdd@cinder.volumes.hdd#cinder.volumes.hdd", Capabilities: schedulerstats.Capabilities{ DriverVersion: "1.2.0", FreeCapacityGB: 0.0, StorageProtocol: "ceph", TotalCapacityGB: math.Inf(1), VendorName: "Open Source", VolumeBackendName: "cinder.volumes.hdd", }, } ) func HandleStoragePoolsListSuccessfully(t *testing.T) { testhelper.Mux.HandleFunc("/scheduler-stats/get_pools", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() if r.FormValue("detail") == "true" { fmt.Fprintf(w, StoragePoolsListBodyDetail) } else { fmt.Fprintf(w, StoragePoolsListBody) } }) } requests_test.go000066400000000000000000000017701335005366400423220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/schedulerstats/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/schedulerstats" "github.com/gophercloud/gophercloud/pagination" "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListStoragePoolsDetail(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() HandleStoragePoolsListSuccessfully(t) pages := 0 err := schedulerstats.List(client.ServiceClient(), schedulerstats.ListOpts{Detail: true}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := schedulerstats.ExtractStoragePools(page) testhelper.AssertNoErr(t, err) if len(actual) != 2 { t.Fatalf("Expected 2 backends, got %d", len(actual)) } testhelper.CheckDeepEquals(t, StoragePoolFake1, actual[0]) testhelper.CheckDeepEquals(t, StoragePoolFake2, actual[1]) return true, nil }) testhelper.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } urls.go000066400000000000000000000002741335005366400367160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/schedulerstatspackage schedulerstats import "github.com/gophercloud/gophercloud" func storagePoolsListURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("scheduler-stats", "get_pools") } services/000077500000000000000000000000001335005366400341655ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensionsdoc.go000066400000000000000000000006731335005366400352670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/services/* Package services returns information about the blockstorage services in the OpenStack cloud. Example of Retrieving list of all services allPages, err := services.List(blockstorageClient, services.ListOpts{}).AllPages() if err != nil { panic(err) } allServices, err := services.ExtractServices(allPages) if err != nil { panic(err) } for _, service := range allServices { fmt.Printf("%+v\n", service) } */ package services requests.go000066400000000000000000000022401335005366400363650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/servicespackage services import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { ToServiceListQuery() (string, error) } // ListOpts holds options for listing Services. type ListOpts struct { // Filter the service list result by binary name of the service. Binary string `q:"binary"` // Filter the service list result by host name of the service. Host string `q:"host"` } // ToServiceListQuery formats a ListOpts into a query string. func (opts ListOpts) ToServiceListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List makes a request against the API to list services. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToServiceListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ServicePage{pagination.SinglePageBase(r)} }) } results.go000066400000000000000000000040661335005366400362230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/servicespackage services import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Service represents a Blockstorage service in the OpenStack cloud. type Service struct { // The binary name of the service. Binary string `json:"binary"` // The reason for disabling a service. DisabledReason string `json:"disabled_reason"` // The name of the host. Host string `json:"host"` // The state of the service. One of up or down. State string `json:"state"` // The status of the service. One of available or unavailable. Status string `json:"status"` // The date and time stamp when the extension was last updated. UpdatedAt time.Time `json:"-"` // The availability zone name. Zone string `json:"zone"` // The following fields are optional // The host is frozen or not. Only in cinder-volume service. Frozen bool `json:"frozen"` // The cluster name. Only in cinder-volume service. Cluster string `json:"cluster"` // The volume service replication status. Only in cinder-volume service. ReplicationStatus string `json:"replication_status"` // The ID of active storage backend. Only in cinder-volume service. ActiveBackendID string `json:"active_backend_id"` } // UnmarshalJSON to override default func (r *Service) UnmarshalJSON(b []byte) error { type tmp Service var s struct { tmp UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Service(s.tmp) r.UpdatedAt = time.Time(s.UpdatedAt) return nil } // ServicePage represents a single page of all Services from a List request. type ServicePage struct { pagination.SinglePageBase } // IsEmpty determines whether or not a page of Services contains any results. func (page ServicePage) IsEmpty() (bool, error) { services, err := ExtractServices(page) return len(services) == 0, err } func ExtractServices(r pagination.Page) ([]Service, error) { var s struct { Service []Service `json:"services"` } err := (r.(ServicePage)).ExtractInto(&s) return s.Service, err } testing/000077500000000000000000000000001335005366400356425ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/servicesfixtures.go000066400000000000000000000051601335005366400400440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/services/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/services" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ServiceListBody is sample response to the List call const ServiceListBody = ` { "services": [{ "status": "enabled", "binary": "cinder-scheduler", "zone": "nova", "state": "up", "updated_at": "2017-06-29T05:50:35.000000", "host": "devstack", "disabled_reason": null }, { "status": "enabled", "binary": "cinder-backup", "zone": "nova", "state": "up", "updated_at": "2017-06-29T05:50:42.000000", "host": "devstack", "disabled_reason": null }, { "status": "enabled", "binary": "cinder-volume", "zone": "nova", "frozen": false, "state": "up", "updated_at": "2017-06-29T05:50:39.000000", "cluster": null, "host": "devstack@lvmdriver-1", "replication_status": "disabled", "active_backend_id": null, "disabled_reason": null }] } ` // First service from the ServiceListBody var FirstFakeService = services.Service{ Binary: "cinder-scheduler", DisabledReason: "", Host: "devstack", State: "up", Status: "enabled", UpdatedAt: time.Date(2017, 6, 29, 5, 50, 35, 0, time.UTC), Zone: "nova", } // Second service from the ServiceListBody var SecondFakeService = services.Service{ Binary: "cinder-backup", DisabledReason: "", Host: "devstack", State: "up", Status: "enabled", UpdatedAt: time.Date(2017, 6, 29, 5, 50, 42, 0, time.UTC), Zone: "nova", } // Third service from the ServiceListBody var ThirdFakeService = services.Service{ ActiveBackendID: "", Binary: "cinder-volume", Cluster: "", DisabledReason: "", Frozen: false, Host: "devstack@lvmdriver-1", ReplicationStatus: "disabled", State: "up", Status: "enabled", UpdatedAt: time.Date(2017, 6, 29, 5, 50, 39, 0, time.UTC), Zone: "nova", } // HandleListSuccessfully configures the test server to respond to a List request. func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-services", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ServiceListBody) }) } requests_test.go000066400000000000000000000020021335005366400410750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/services/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/services" "github.com/gophercloud/gophercloud/pagination" "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListServices(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() HandleListSuccessfully(t) pages := 0 err := services.List(client.ServiceClient(), services.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := services.ExtractServices(page) if err != nil { return false, err } if len(actual) != 3 { t.Fatalf("Expected 3 services, got %d", len(actual)) } testhelper.CheckDeepEquals(t, FirstFakeService, actual[0]) testhelper.CheckDeepEquals(t, SecondFakeService, actual[1]) testhelper.CheckDeepEquals(t, ThirdFakeService, actual[2]) return true, nil }) testhelper.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } urls.go000066400000000000000000000002311335005366400354750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/servicespackage services import "github.com/gophercloud/gophercloud" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-services") } volumeactions/000077500000000000000000000000001335005366400352325ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensionsdoc.go000066400000000000000000000037671335005366400363430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/volumeactions/* Package volumeactions provides information and interaction with volumes in the OpenStack Block Storage service. A volume is a detachable block storage device, akin to a USB hard drive. Example of Attaching a Volume to an Instance attachOpts := volumeactions.AttachOpts{ MountPoint: "/mnt", Mode: "rw", InstanceUUID: server.ID, } err := volumeactions.Attach(client, volume.ID, attachOpts).ExtractErr() if err != nil { panic(err) } detachOpts := volumeactions.DetachOpts{ AttachmentID: volume.Attachments[0].AttachmentID, } err = volumeactions.Detach(client, volume.ID, detachOpts).ExtractErr() if err != nil { panic(err) } Example of Creating an Image from a Volume uploadImageOpts := volumeactions.UploadImageOpts{ ImageName: "my_vol", Force: true, } volumeImage, err := volumeactions.UploadImage(client, volume.ID, uploadImageOpts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", volumeImage) Example of Extending a Volume's Size extendOpts := volumeactions.ExtendSizeOpts{ NewSize: 100, } err := volumeactions.ExtendSize(client, volume.ID, extendOpts).ExtractErr() if err != nil { panic(err) } Example of Initializing a Volume Connection connectOpts := &volumeactions.InitializeConnectionOpts{ IP: "127.0.0.1", Host: "stack", Initiator: "iqn.1994-05.com.redhat:17cf566367d2", Multipath: gophercloud.Disabled, Platform: "x86_64", OSType: "linux2", } connectionInfo, err := volumeactions.InitializeConnection(client, volume.ID, connectOpts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", connectionInfo["data"]) terminateOpts := &volumeactions.InitializeConnectionOpts{ IP: "127.0.0.1", Host: "stack", Initiator: "iqn.1994-05.com.redhat:17cf566367d2", Multipath: gophercloud.Disabled, Platform: "x86_64", OSType: "linux2", } err = volumeactions.TerminateConnection(client, volume.ID, terminateOpts).ExtractErr() if err != nil { panic(err) } */ package volumeactions requests.go000066400000000000000000000223321335005366400374360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/volumeactionspackage volumeactions import ( "github.com/gophercloud/gophercloud" ) // AttachOptsBuilder allows extensions to add additional parameters to the // Attach request. type AttachOptsBuilder interface { ToVolumeAttachMap() (map[string]interface{}, error) } // AttachMode describes the attachment mode for volumes. type AttachMode string // These constants determine how a volume is attached. const ( ReadOnly AttachMode = "ro" ReadWrite AttachMode = "rw" ) // AttachOpts contains options for attaching a Volume. type AttachOpts struct { // The mountpoint of this volume. MountPoint string `json:"mountpoint,omitempty"` // The nova instance ID, can't set simultaneously with HostName. InstanceUUID string `json:"instance_uuid,omitempty"` // The hostname of baremetal host, can't set simultaneously with InstanceUUID. HostName string `json:"host_name,omitempty"` // Mount mode of this volume. Mode AttachMode `json:"mode,omitempty"` } // ToVolumeAttachMap assembles a request body based on the contents of a // AttachOpts. func (opts AttachOpts) ToVolumeAttachMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "os-attach") } // Attach will attach a volume based on the values in AttachOpts. func Attach(client *gophercloud.ServiceClient, id string, opts AttachOptsBuilder) (r AttachResult) { b, err := opts.ToVolumeAttachMap() if err != nil { r.Err = err return } _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return } // BeginDetach will mark the volume as detaching. func BeginDetaching(client *gophercloud.ServiceClient, id string) (r BeginDetachingResult) { b := map[string]interface{}{"os-begin_detaching": make(map[string]interface{})} _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return } // DetachOptsBuilder allows extensions to add additional parameters to the // Detach request. type DetachOptsBuilder interface { ToVolumeDetachMap() (map[string]interface{}, error) } // DetachOpts contains options for detaching a Volume. type DetachOpts struct { // AttachmentID is the ID of the attachment between a volume and instance. AttachmentID string `json:"attachment_id,omitempty"` } // ToVolumeDetachMap assembles a request body based on the contents of a // DetachOpts. func (opts DetachOpts) ToVolumeDetachMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "os-detach") } // Detach will detach a volume based on volume ID. func Detach(client *gophercloud.ServiceClient, id string, opts DetachOptsBuilder) (r DetachResult) { b, err := opts.ToVolumeDetachMap() if err != nil { r.Err = err return } _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return } // Reserve will reserve a volume based on volume ID. func Reserve(client *gophercloud.ServiceClient, id string) (r ReserveResult) { b := map[string]interface{}{"os-reserve": make(map[string]interface{})} _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) return } // Unreserve will unreserve a volume based on volume ID. func Unreserve(client *gophercloud.ServiceClient, id string) (r UnreserveResult) { b := map[string]interface{}{"os-unreserve": make(map[string]interface{})} _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) return } // InitializeConnectionOptsBuilder allows extensions to add additional parameters to the // InitializeConnection request. type InitializeConnectionOptsBuilder interface { ToVolumeInitializeConnectionMap() (map[string]interface{}, error) } // InitializeConnectionOpts hosts options for InitializeConnection. // The fields are specific to the storage driver in use and the destination // attachment. type InitializeConnectionOpts struct { IP string `json:"ip,omitempty"` Host string `json:"host,omitempty"` Initiator string `json:"initiator,omitempty"` Wwpns []string `json:"wwpns,omitempty"` Wwnns string `json:"wwnns,omitempty"` Multipath *bool `json:"multipath,omitempty"` Platform string `json:"platform,omitempty"` OSType string `json:"os_type,omitempty"` } // ToVolumeInitializeConnectionMap assembles a request body based on the contents of a // InitializeConnectionOpts. func (opts InitializeConnectionOpts) ToVolumeInitializeConnectionMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "connector") return map[string]interface{}{"os-initialize_connection": b}, err } // InitializeConnection initializes an iSCSI connection by volume ID. func InitializeConnection(client *gophercloud.ServiceClient, id string, opts InitializeConnectionOptsBuilder) (r InitializeConnectionResult) { b, err := opts.ToVolumeInitializeConnectionMap() if err != nil { r.Err = err return } _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) return } // TerminateConnectionOptsBuilder allows extensions to add additional parameters to the // TerminateConnection request. type TerminateConnectionOptsBuilder interface { ToVolumeTerminateConnectionMap() (map[string]interface{}, error) } // TerminateConnectionOpts hosts options for TerminateConnection. type TerminateConnectionOpts struct { IP string `json:"ip,omitempty"` Host string `json:"host,omitempty"` Initiator string `json:"initiator,omitempty"` Wwpns []string `json:"wwpns,omitempty"` Wwnns string `json:"wwnns,omitempty"` Multipath *bool `json:"multipath,omitempty"` Platform string `json:"platform,omitempty"` OSType string `json:"os_type,omitempty"` } // ToVolumeTerminateConnectionMap assembles a request body based on the contents of a // TerminateConnectionOpts. func (opts TerminateConnectionOpts) ToVolumeTerminateConnectionMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "connector") return map[string]interface{}{"os-terminate_connection": b}, err } // TerminateConnection terminates an iSCSI connection by volume ID. func TerminateConnection(client *gophercloud.ServiceClient, id string, opts TerminateConnectionOptsBuilder) (r TerminateConnectionResult) { b, err := opts.ToVolumeTerminateConnectionMap() if err != nil { r.Err = err return } _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return } // ExtendSizeOptsBuilder allows extensions to add additional parameters to the // ExtendSize request. type ExtendSizeOptsBuilder interface { ToVolumeExtendSizeMap() (map[string]interface{}, error) } // ExtendSizeOpts contains options for extending the size of an existing Volume. // This object is passed to the volumes.ExtendSize function. type ExtendSizeOpts struct { // NewSize is the new size of the volume, in GB. NewSize int `json:"new_size" required:"true"` } // ToVolumeExtendSizeMap assembles a request body based on the contents of an // ExtendSizeOpts. func (opts ExtendSizeOpts) ToVolumeExtendSizeMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "os-extend") } // ExtendSize will extend the size of the volume based on the provided information. // This operation does not return a response body. func ExtendSize(client *gophercloud.ServiceClient, id string, opts ExtendSizeOptsBuilder) (r ExtendSizeResult) { b, err := opts.ToVolumeExtendSizeMap() if err != nil { r.Err = err return } _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return } // UploadImageOptsBuilder allows extensions to add additional parameters to the // UploadImage request. type UploadImageOptsBuilder interface { ToVolumeUploadImageMap() (map[string]interface{}, error) } // UploadImageOpts contains options for uploading a Volume to image storage. type UploadImageOpts struct { // Container format, may be bare, ofv, ova, etc. ContainerFormat string `json:"container_format,omitempty"` // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. DiskFormat string `json:"disk_format,omitempty"` // The name of image that will be stored in glance. ImageName string `json:"image_name,omitempty"` // Force image creation, usable if volume attached to instance. Force bool `json:"force,omitempty"` } // ToVolumeUploadImageMap assembles a request body based on the contents of a // UploadImageOpts. func (opts UploadImageOpts) ToVolumeUploadImageMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "os-volume_upload_image") } // UploadImage will upload an image based on the values in UploadImageOptsBuilder. func UploadImage(client *gophercloud.ServiceClient, id string, opts UploadImageOptsBuilder) (r UploadImageResult) { b, err := opts.ToVolumeUploadImageMap() if err != nil { r.Err = err return } _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return } // ForceDelete will delete the volume regardless of state. func ForceDelete(client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil) return } results.go000066400000000000000000000115421335005366400372650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/volumeactionspackage volumeactions import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" ) // AttachResult contains the response body and error from an Attach request. type AttachResult struct { gophercloud.ErrResult } // BeginDetachingResult contains the response body and error from a BeginDetach // request. type BeginDetachingResult struct { gophercloud.ErrResult } // DetachResult contains the response body and error from a Detach request. type DetachResult struct { gophercloud.ErrResult } // UploadImageResult contains the response body and error from an UploadImage // request. type UploadImageResult struct { gophercloud.Result } // ReserveResult contains the response body and error from a Reserve request. type ReserveResult struct { gophercloud.ErrResult } // UnreserveResult contains the response body and error from an Unreserve // request. type UnreserveResult struct { gophercloud.ErrResult } // TerminateConnectionResult contains the response body and error from a // TerminateConnection request. type TerminateConnectionResult struct { gophercloud.ErrResult } // InitializeConnectionResult contains the response body and error from an // InitializeConnection request. type InitializeConnectionResult struct { gophercloud.Result } // ExtendSizeResult contains the response body and error from an ExtendSize request. type ExtendSizeResult struct { gophercloud.ErrResult } // Extract will get the connection information out of the // InitializeConnectionResult object. // // This will be a generic map[string]interface{} and the results will be // dependent on the type of connection made. func (r InitializeConnectionResult) Extract() (map[string]interface{}, error) { var s struct { ConnectionInfo map[string]interface{} `json:"connection_info"` } err := r.ExtractInto(&s) return s.ConnectionInfo, err } // ImageVolumeType contains volume type information obtained from UploadImage // action. type ImageVolumeType struct { // The ID of a volume type. ID string `json:"id"` // Human-readable display name for the volume type. Name string `json:"name"` // Human-readable description for the volume type. Description string `json:"display_description"` // Flag for public access. IsPublic bool `json:"is_public"` // Extra specifications for volume type. ExtraSpecs map[string]interface{} `json:"extra_specs"` // ID of quality of service specs. QosSpecsID string `json:"qos_specs_id"` // Flag for deletion status of volume type. Deleted bool `json:"deleted"` // The date when volume type was deleted. DeletedAt time.Time `json:"-"` // The date when volume type was created. CreatedAt time.Time `json:"-"` // The date when this volume was last updated. UpdatedAt time.Time `json:"-"` } func (r *ImageVolumeType) UnmarshalJSON(b []byte) error { type tmp ImageVolumeType var s struct { tmp CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` DeletedAt gophercloud.JSONRFC3339MilliNoZ `json:"deleted_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = ImageVolumeType(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) r.UpdatedAt = time.Time(s.UpdatedAt) r.DeletedAt = time.Time(s.DeletedAt) return err } // VolumeImage contains information about volume uploaded to an image service. type VolumeImage struct { // The ID of a volume an image is created from. VolumeID string `json:"id"` // Container format, may be bare, ofv, ova, etc. ContainerFormat string `json:"container_format"` // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. DiskFormat string `json:"disk_format"` // Human-readable description for the volume. Description string `json:"display_description"` // The ID of the created image. ImageID string `json:"image_id"` // Human-readable display name for the image. ImageName string `json:"image_name"` // Size of the volume in GB. Size int `json:"size"` // Current status of the volume. Status string `json:"status"` // The date when this volume was last updated. UpdatedAt time.Time `json:"-"` // Volume type object of used volume. VolumeType ImageVolumeType `json:"volume_type"` } func (r *VolumeImage) UnmarshalJSON(b []byte) error { type tmp VolumeImage var s struct { tmp UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = VolumeImage(s.tmp) r.UpdatedAt = time.Time(s.UpdatedAt) return err } // Extract will get an object with info about the uploaded image out of the // UploadImageResult object. func (r UploadImageResult) Extract() (VolumeImage, error) { var s struct { VolumeImage VolumeImage `json:"os-volume_upload_image"` } err := r.ExtractInto(&s) return s.VolumeImage, err } // ForceDeleteResult contains the response body and error from a ForceDelete request. type ForceDeleteResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400367075ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/volumeactionsdoc.go000066400000000000000000000000541335005366400400020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/volumeactions/testing// volumeactions unit tests package testing fixtures.go000066400000000000000000000174541335005366400411220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/volumeactions/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func MockAttachResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "os-attach": { "mountpoint": "/mnt", "mode": "rw", "instance_uuid": "50902f4f-a974-46a0-85e9-7efc5e22dfdd" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, `{}`) }) } func MockBeginDetachingResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "os-begin_detaching": {} } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, `{}`) }) } func MockDetachResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "os-detach": {} } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, `{}`) }) } func MockUploadImageResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "os-volume_upload_image": { "container_format": "bare", "force": true, "image_name": "test", "disk_format": "raw" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, ` { "os-volume_upload_image": { "container_format": "bare", "display_description": null, "id": "cd281d77-8217-4830-be95-9528227c105c", "image_id": "ecb92d98-de08-45db-8235-bbafe317269c", "image_name": "test", "disk_format": "raw", "size": 5, "status": "uploading", "updated_at": "2017-07-17T09:29:22.000000", "volume_type": { "created_at": "2016-05-04T08:54:14.000000", "deleted": false, "deleted_at": null, "description": null, "extra_specs": { "volume_backend_name": "basic.ru-2a" }, "id": "b7133444-62f6-4433-8da3-70ac332229b7", "is_public": true, "name": "basic.ru-2a", "updated_at": "2016-05-04T09:15:33.000000" } } } `) }) } func MockReserveResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "os-reserve": {} } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, `{}`) }) } func MockUnreserveResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "os-unreserve": {} } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, `{}`) }) } func MockInitializeConnectionResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "os-initialize_connection": { "connector": { "ip":"127.0.0.1", "host":"stack", "initiator":"iqn.1994-05.com.redhat:17cf566367d2", "multipath": false, "platform": "x86_64", "os_type": "linux2" } } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, `{ "connection_info": { "data": { "target_portals": [ "172.31.17.48:3260" ], "auth_method": "CHAP", "auth_username": "5MLtcsTEmNN5jFVcT6ui", "access_mode": "rw", "target_lun": 0, "volume_id": "cd281d77-8217-4830-be95-9528227c105c", "target_luns": [ 0 ], "target_iqns": [ "iqn.2010-10.org.openstack:volume-cd281d77-8217-4830-be95-9528227c105c" ], "auth_password": "x854ZY5Re3aCkdNL", "target_discovered": false, "encrypted": false, "qos_specs": null, "target_iqn": "iqn.2010-10.org.openstack:volume-cd281d77-8217-4830-be95-9528227c105c", "target_portal": "172.31.17.48:3260" }, "driver_volume_type": "iscsi" } }`) }) } func MockTerminateConnectionResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "os-terminate_connection": { "connector": { "ip":"127.0.0.1", "host":"stack", "initiator":"iqn.1994-05.com.redhat:17cf566367d2", "multipath": true, "platform": "x86_64", "os_type": "linux2" } } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, `{}`) }) } func MockExtendSizeResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "os-extend": { "new_size": 3 } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, `{}`) }) } func MockForceDeleteResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestBody(t, r, `{"os-force_delete":""}`) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000106621335005366400421550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/volumeactions/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestAttach(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockAttachResponse(t) options := &volumeactions.AttachOpts{ MountPoint: "/mnt", Mode: "rw", InstanceUUID: "50902f4f-a974-46a0-85e9-7efc5e22dfdd", } err := volumeactions.Attach(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() th.AssertNoErr(t, err) } func TestBeginDetaching(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockBeginDetachingResponse(t) err := volumeactions.BeginDetaching(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() th.AssertNoErr(t, err) } func TestDetach(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockDetachResponse(t) err := volumeactions.Detach(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", &volumeactions.DetachOpts{}).ExtractErr() th.AssertNoErr(t, err) } func TestUploadImage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUploadImageResponse(t) options := &volumeactions.UploadImageOpts{ ContainerFormat: "bare", DiskFormat: "raw", ImageName: "test", Force: true, } actual, err := volumeactions.UploadImage(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).Extract() th.AssertNoErr(t, err) expected := volumeactions.VolumeImage{ VolumeID: "cd281d77-8217-4830-be95-9528227c105c", ContainerFormat: "bare", DiskFormat: "raw", Description: "", ImageID: "ecb92d98-de08-45db-8235-bbafe317269c", ImageName: "test", Size: 5, Status: "uploading", UpdatedAt: time.Date(2017, 7, 17, 9, 29, 22, 0, time.UTC), VolumeType: volumeactions.ImageVolumeType{ ID: "b7133444-62f6-4433-8da3-70ac332229b7", Name: "basic.ru-2a", Description: "", IsPublic: true, ExtraSpecs: map[string]interface{}{"volume_backend_name": "basic.ru-2a"}, QosSpecsID: "", Deleted: false, DeletedAt: time.Time{}, CreatedAt: time.Date(2016, 5, 4, 8, 54, 14, 0, time.UTC), UpdatedAt: time.Date(2016, 5, 4, 9, 15, 33, 0, time.UTC), }, } th.AssertDeepEquals(t, expected, actual) } func TestReserve(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockReserveResponse(t) err := volumeactions.Reserve(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() th.AssertNoErr(t, err) } func TestUnreserve(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUnreserveResponse(t) err := volumeactions.Unreserve(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() th.AssertNoErr(t, err) } func TestInitializeConnection(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockInitializeConnectionResponse(t) options := &volumeactions.InitializeConnectionOpts{ IP: "127.0.0.1", Host: "stack", Initiator: "iqn.1994-05.com.redhat:17cf566367d2", Multipath: gophercloud.Disabled, Platform: "x86_64", OSType: "linux2", } _, err := volumeactions.InitializeConnection(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).Extract() th.AssertNoErr(t, err) } func TestTerminateConnection(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockTerminateConnectionResponse(t) options := &volumeactions.TerminateConnectionOpts{ IP: "127.0.0.1", Host: "stack", Initiator: "iqn.1994-05.com.redhat:17cf566367d2", Multipath: gophercloud.Enabled, Platform: "x86_64", OSType: "linux2", } err := volumeactions.TerminateConnection(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() th.AssertNoErr(t, err) } func TestExtendSize(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockExtendSizeResponse(t) options := &volumeactions.ExtendSizeOpts{ NewSize: 3, } err := volumeactions.ExtendSize(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() th.AssertNoErr(t, err) } func TestForceDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockForceDeleteResponse(t) res := volumeactions.ForceDelete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000002651335005366400365510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/volumeactionspackage volumeactions import "github.com/gophercloud/gophercloud" func actionURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("volumes", id, "action") } volumetenants/000077500000000000000000000000001335005366400352465ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensionsdoc.go000066400000000000000000000011031335005366400363350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/volumetenants/* Package volumetenants provides the ability to extend a volume result with tenant/project information. Example: type VolumeWithTenant struct { volumes.Volume volumetenants.VolumeTenantExt } var allVolumes []VolumeWithTenant allPages, err := volumes.List(client, nil).AllPages() if err != nil { panic("Unable to retrieve volumes: %s", err) } err = volumes.ExtractVolumesInto(allPages, &allVolumes) if err != nil { panic("Unable to extract volumes: %s", err) } for _, volume := range allVolumes { fmt.Println(volume.TenantID) } */ package volumetenants results.go000066400000000000000000000003471335005366400373020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/extensions/volumetenantspackage volumetenants // VolumeTenantExt is an extension to the base Volume object type VolumeTenantExt struct { // TenantID is the id of the project that owns the volume. TenantID string `json:"os-vol-tenant-attr:tenant_id"` } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/noauth/000077500000000000000000000000001335005366400315205ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/noauth/doc.go000066400000000000000000000010571335005366400326170ustar00rootroot00000000000000/* Package noauth creates a "noauth" *gophercloud.ServiceClient for use in Cinder environments configured with the noauth authentication middleware. Example of Creating a noauth Service Client provider, err := noauth.NewClient(gophercloud.AuthOptions{ Username: os.Getenv("OS_USERNAME"), TenantName: os.Getenv("OS_TENANT_NAME"), }) client, err := noauth.NewBlockStorageNoAuth(provider, noauth.EndpointOpts{ CinderEndpoint: os.Getenv("CINDER_ENDPOINT"), }) An example of a CinderEndpoint would be: http://example.com:8776/v2, */ package noauth requests.go000066400000000000000000000031351335005366400336450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/noauthpackage noauth import ( "fmt" "strings" "github.com/gophercloud/gophercloud" ) // EndpointOpts specifies a "noauth" Cinder Endpoint. type EndpointOpts struct { // CinderEndpoint [required] is currently only used with "noauth" Cinder. // A cinder endpoint with "auth_strategy=noauth" is necessary, for example: // http://example.com:8776/v2. CinderEndpoint string } // NewClient prepares an unauthenticated ProviderClient instance. func NewClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) { if options.Username == "" { options.Username = "admin" } if options.TenantName == "" { options.TenantName = "admin" } client := &gophercloud.ProviderClient{ TokenID: fmt.Sprintf("%s:%s", options.Username, options.TenantName), } return client, nil } func initClientOpts(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophercloud.ServiceClient, error) { sc := new(gophercloud.ServiceClient) if eo.CinderEndpoint == "" { return nil, fmt.Errorf("CinderEndpoint is required") } token := strings.Split(client.TokenID, ":") if len(token) != 2 { return nil, fmt.Errorf("Malformed noauth token") } endpoint := fmt.Sprintf("%s%s", gophercloud.NormalizeURL(eo.CinderEndpoint), token[1]) sc.Endpoint = gophercloud.NormalizeURL(endpoint) sc.ProviderClient = client return sc, nil } // NewBlockStorageNoAuth creates a ServiceClient that may be used to access a // "noauth" block storage service (V2 or V3 Cinder API). func NewBlockStorageNoAuth(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo) } testing/000077500000000000000000000000001335005366400331165ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/noauthdoc.go000066400000000000000000000000451335005366400342110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/noauth/testing// noauth unit tests package testing fixtures.go000066400000000000000000000006171335005366400353220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/noauth/testingpackage testing // NoAuthResult is the expected result of the noauth Service Client type NoAuthResult struct { TokenID string Endpoint string } var naTestResult = NoAuthResult{ TokenID: "user:test", Endpoint: "http://cinder:8776/v2/test/", } var naResult = NoAuthResult{ TokenID: "admin:admin", Endpoint: "http://cinder:8776/v2/admin/", } var errorResult = "CinderEndpoint is required" requests_test.go000066400000000000000000000022321335005366400363560ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/noauth/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/blockstorage/noauth" th "github.com/gophercloud/gophercloud/testhelper" ) func TestNoAuth(t *testing.T) { ao := gophercloud.AuthOptions{ Username: "user", TenantName: "test", } provider, err := noauth.NewClient(ao) th.AssertNoErr(t, err) noauthClient, err := noauth.NewBlockStorageNoAuth(provider, noauth.EndpointOpts{ CinderEndpoint: "http://cinder:8776/v2", }) th.AssertNoErr(t, err) th.AssertEquals(t, naTestResult.Endpoint, noauthClient.Endpoint) th.AssertEquals(t, naTestResult.TokenID, noauthClient.TokenID) ao2 := gophercloud.AuthOptions{} provider2, err := noauth.NewClient(ao2) th.AssertNoErr(t, err) noauthClient2, err := noauth.NewBlockStorageNoAuth(provider2, noauth.EndpointOpts{ CinderEndpoint: "http://cinder:8776/v2/", }) th.AssertNoErr(t, err) th.AssertEquals(t, naResult.Endpoint, noauthClient2.Endpoint) th.AssertEquals(t, naResult.TokenID, noauthClient2.TokenID) errTest, err := noauth.NewBlockStorageNoAuth(provider2, noauth.EndpointOpts{}) _ = errTest th.AssertEquals(t, errorResult, err.Error()) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/000077500000000000000000000000001335005366400305505ustar00rootroot00000000000000apiversions/000077500000000000000000000000001335005366400330335ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1doc.go000066400000000000000000000002571335005366400341330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/apiversions// Package apiversions provides information and interaction with the different // API versions for the OpenStack Block Storage service, code-named Cinder. package apiversions requests.go000066400000000000000000000012511335005366400352340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/apiversionspackage apiversions import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List lists all the Cinder API versions available to end-users. func List(c *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(c, listURL(c), func(r pagination.PageResult) pagination.Page { return APIVersionPage{pagination.SinglePageBase(r)} }) } // Get will retrieve the volume type with the provided ID. To extract the volume // type from the result, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, v string) (r GetResult) { _, r.Err = client.Get(getURL(client, v), &r.Body, nil) return } results.go000066400000000000000000000025661335005366400350740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/apiversionspackage apiversions import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // APIVersion represents an API version for Cinder. type APIVersion struct { ID string `json:"id"` // unique identifier Status string `json:"status"` // current status Updated string `json:"updated"` // date last updated } // APIVersionPage is the page returned by a pager when traversing over a // collection of API versions. type APIVersionPage struct { pagination.SinglePageBase } // IsEmpty checks whether an APIVersionPage struct is empty. func (r APIVersionPage) IsEmpty() (bool, error) { is, err := ExtractAPIVersions(r) return len(is) == 0, err } // ExtractAPIVersions takes a collection page, extracts all of the elements, // and returns them a slice of APIVersion structs. It is effectively a cast. func ExtractAPIVersions(r pagination.Page) ([]APIVersion, error) { var s struct { Versions []APIVersion `json:"versions"` } err := (r.(APIVersionPage)).ExtractInto(&s) return s.Versions, err } // GetResult represents the result of a get operation. type GetResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts an API version resource. func (r GetResult) Extract() (*APIVersion, error) { var s struct { Version *APIVersion `json:"version"` } err := r.ExtractInto(&s) return s.Version, err } testing/000077500000000000000000000000001335005366400345105ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/apiversionsdoc.go000066400000000000000000000000421335005366400356000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/apiversions/testing// apiversions_v1 package testing fixtures.go000066400000000000000000000037501335005366400367150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/apiversions/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func MockListResponse(t *testing.T) { th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, `{ "versions": [ { "status": "CURRENT", "updated": "2012-01-04T11:33:21Z", "id": "v1.0", "links": [ { "href": "http://23.253.228.211:8776/v1/", "rel": "self" } ] }, { "status": "CURRENT", "updated": "2012-11-21T11:33:21Z", "id": "v2.0", "links": [ { "href": "http://23.253.228.211:8776/v2/", "rel": "self" } ] } ] }`) }) } func MockGetResponse(t *testing.T) { th.Mux.HandleFunc("/v1/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, `{ "version": { "status": "CURRENT", "updated": "2012-01-04T11:33:21Z", "media-types": [ { "base": "application/xml", "type": "application/vnd.openstack.volume+xml;version=1" }, { "base": "application/json", "type": "application/vnd.openstack.volume+json;version=1" } ], "id": "v1.0", "links": [ { "href": "http://23.253.228.211:8776/v1/", "rel": "self" }, { "href": "http://jorgew.github.com/block-storage-api/content/os-block-storage-1.0.pdf", "type": "application/pdf", "rel": "describedby" }, { "href": "http://docs.rackspacecloud.com/servers/api/v1.1/application.wadl", "type": "application/vnd.sun.wadl+xml", "rel": "describedby" } ] } }`) }) } requests_test.go000066400000000000000000000025271335005366400377570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/apiversions/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListVersions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) count := 0 apiversions.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := apiversions.ExtractAPIVersions(page) th.AssertNoErr(t, err) expected := []apiversions.APIVersion{ { ID: "v1.0", Status: "CURRENT", Updated: "2012-01-04T11:33:21Z", }, { ID: "v2.0", Status: "CURRENT", Updated: "2012-11-21T11:33:21Z", }, } th.AssertDeepEquals(t, expected, actual) return true, nil }) th.AssertEquals(t, 1, count) } func TestAPIInfo(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetResponse(t) actual, err := apiversions.Get(client.ServiceClient(), "v1").Extract() th.AssertNoErr(t, err) expected := apiversions.APIVersion{ ID: "v1.0", Status: "CURRENT", Updated: "2012-01-04T11:33:21Z", } th.AssertEquals(t, actual.ID, expected.ID) th.AssertEquals(t, actual.Status, expected.Status) th.AssertEquals(t, actual.Updated, expected.Updated) } urls.go000066400000000000000000000005341335005366400343510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/apiversionspackage apiversions import ( "net/url" "strings" "github.com/gophercloud/gophercloud" ) func getURL(c *gophercloud.ServiceClient, version string) string { return c.ServiceURL(strings.TrimRight(version, "/") + "/") } func listURL(c *gophercloud.ServiceClient) string { u, _ := url.Parse(c.ServiceURL("")) u.Path = "/" return u.String() } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/snapshots/000077500000000000000000000000001335005366400325725ustar00rootroot00000000000000doc.go000066400000000000000000000004141335005366400336060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/snapshots// Package snapshots provides information and interaction with snapshots in the // OpenStack Block Storage service. A snapshot is a point in time copy of the // data contained in an external storage volume, and can be controlled // programmatically. package snapshots requests.go000066400000000000000000000120201335005366400347100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/snapshotspackage snapshots import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToSnapshotCreateMap() (map[string]interface{}, error) } // CreateOpts contains options for creating a Snapshot. This object is passed to // the snapshots.Create function. For more information about these parameters, // see the Snapshot object. type CreateOpts struct { VolumeID string `json:"volume_id" required:"true"` Description string `json:"display_description,omitempty"` Force bool `json:"force,omitempty"` Metadata map[string]interface{} `json:"metadata,omitempty"` Name string `json:"display_name,omitempty"` } // ToSnapshotCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "snapshot") } // Create will create a new Snapshot based on the values in CreateOpts. To // extract the Snapshot object from the response, call the Extract method on the // CreateResult. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSnapshotCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) return } // Delete will delete the existing Snapshot with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // Get retrieves the Snapshot with the provided ID. To extract the Snapshot // object from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { ToSnapshotListQuery() (string, error) } // ListOpts hold options for listing Snapshots. It is passed to the // snapshots.List function. type ListOpts struct { Name string `q:"display_name"` Status string `q:"status"` VolumeID string `q:"volume_id"` } // ToSnapshotListQuery formats a ListOpts into a query string. func (opts ListOpts) ToSnapshotListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns Snapshots optionally limited by the conditions provided in // ListOpts. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToSnapshotListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return SnapshotPage{pagination.SinglePageBase(r)} }) } // UpdateMetadataOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateMetadataOptsBuilder interface { ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) } // UpdateMetadataOpts contain options for updating an existing Snapshot. This // object is passed to the snapshots.Update function. For more information // about the parameters, see the Snapshot object. type UpdateMetadataOpts struct { Metadata map[string]interface{} `json:"metadata,omitempty"` } // ToSnapshotUpdateMetadataMap assembles a request body based on the contents of // an UpdateMetadataOpts. func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // UpdateMetadata will update the Snapshot with provided information. To // extract the updated Snapshot from the response, call the ExtractMetadata // method on the UpdateMetadataResult. func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { b, err := opts.ToSnapshotUpdateMetadataMap() if err != nil { r.Err = err return } _, r.Err = client.Put(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // IDFromName is a convienience function that returns a snapshot's ID given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" listOpts := ListOpts{ Name: name, } pages, err := List(client, listOpts).AllPages() if err != nil { return "", err } all, err := ExtractSnapshots(pages) if err != nil { return "", err } for _, s := range all { if s.Name == name { count++ id = s.ID } } switch count { case 0: return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "snapshot"} case 1: return id, nil default: return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "snapshot"} } } results.go000066400000000000000000000062341335005366400345500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/snapshotspackage snapshots import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Snapshot contains all the information associated with an OpenStack Snapshot. type Snapshot struct { // Currect status of the Snapshot. Status string `json:"status"` // Display name. Name string `json:"display_name"` // Instances onto which the Snapshot is attached. Attachments []string `json:"attachments"` // Logical group. AvailabilityZone string `json:"availability_zone"` // Is the Snapshot bootable? Bootable string `json:"bootable"` // Date created. CreatedAt time.Time `json:"-"` // Display description. Description string `json:"display_description"` // See VolumeType object for more information. VolumeType string `json:"volume_type"` // ID of the Snapshot from which this Snapshot was created. SnapshotID string `json:"snapshot_id"` // ID of the Volume from which this Snapshot was created. VolumeID string `json:"volume_id"` // User-defined key-value pairs. Metadata map[string]string `json:"metadata"` // Unique identifier. ID string `json:"id"` // Size of the Snapshot, in GB. Size int `json:"size"` } func (r *Snapshot) UnmarshalJSON(b []byte) error { type tmp Snapshot var s struct { tmp CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Snapshot(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) return err } // CreateResult contains the response body and error from a Create request. type CreateResult struct { commonResult } // GetResult contains the response body and error from a Get request. type GetResult struct { commonResult } // DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { gophercloud.ErrResult } // SnapshotPage is a pagination.Pager that is returned from a call to the List function. type SnapshotPage struct { pagination.SinglePageBase } // IsEmpty returns true if a SnapshotPage contains no Snapshots. func (r SnapshotPage) IsEmpty() (bool, error) { volumes, err := ExtractSnapshots(r) return len(volumes) == 0, err } // ExtractSnapshots extracts and returns Snapshots. It is used while iterating over a snapshots.List call. func ExtractSnapshots(r pagination.Page) ([]Snapshot, error) { var s struct { Snapshots []Snapshot `json:"snapshots"` } err := (r.(SnapshotPage)).ExtractInto(&s) return s.Snapshots, err } // UpdateMetadataResult contains the response body and error from an UpdateMetadata request. type UpdateMetadataResult struct { commonResult } // ExtractMetadata returns the metadata from a response from snapshots.UpdateMetadata. func (r UpdateMetadataResult) ExtractMetadata() (map[string]interface{}, error) { if r.Err != nil { return nil, r.Err } m := r.Body.(map[string]interface{})["metadata"] return m.(map[string]interface{}), nil } type commonResult struct { gophercloud.Result } // Extract will get the Snapshot object out of the commonResult object. func (r commonResult) Extract() (*Snapshot, error) { var s struct { Snapshot *Snapshot `json:"snapshot"` } err := r.ExtractInto(&s) return s.Snapshot, err } testing/000077500000000000000000000000001335005366400341705ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/snapshotsdoc.go000066400000000000000000000000401335005366400352560ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/snapshots/testing// snapshots_v1 package testing fixtures.go000066400000000000000000000067131335005366400363770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/snapshots/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func MockListResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "snapshots": [ { "id": "289da7f8-6440-407c-9fb4-7db01ec49164", "display_name": "snapshot-001", "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", "display_description": "Daily Backup", "status": "available", "size": 30, "created_at": "2012-02-14T20:53:07" }, { "id": "96c3bda7-c82a-4f50-be73-ca7621794835", "display_name": "snapshot-002", "volume_id": "76b8950a-8594-4e5b-8dce-0dfa9c696358", "display_description": "Weekly Backup", "status": "available", "size": 25, "created_at": "2012-02-14T20:53:08" } ] } `) }) } func MockGetResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "snapshot": { "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "display_name": "snapshot-001", "display_description": "Daily backup", "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", "status": "available", "size": 30, "created_at": "2012-02-29T03:50:07" } } `) }) } func MockCreateResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "snapshot": { "volume_id": "1234", "display_name": "snapshot-001" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "snapshot": { "volume_id": "1234", "display_name": "snapshot-001", "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "display_description": "Daily backup", "volume_id": "1234", "status": "available", "size": 30, "created_at": "2012-02-29T03:50:07" } } `) }) } func MockUpdateMetadataResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots/123/metadata", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, ` { "metadata": { "key": "v1" } } `) fmt.Fprintf(w, ` { "metadata": { "key": "v1" } } `) }) } func MockDeleteResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000054441335005366400374400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/snapshots/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/snapshots" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) count := 0 snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := snapshots.ExtractSnapshots(page) if err != nil { t.Errorf("Failed to extract snapshots: %v", err) return false, err } expected := []snapshots.Snapshot{ { ID: "289da7f8-6440-407c-9fb4-7db01ec49164", Name: "snapshot-001", VolumeID: "521752a6-acf6-4b2d-bc7a-119f9148cd8c", Status: "available", Size: 30, CreatedAt: time.Date(2012, 2, 14, 20, 53, 7, 0, time.UTC), Description: "Daily Backup", }, { ID: "96c3bda7-c82a-4f50-be73-ca7621794835", Name: "snapshot-002", VolumeID: "76b8950a-8594-4e5b-8dce-0dfa9c696358", Status: "available", Size: 25, CreatedAt: time.Date(2012, 2, 14, 20, 53, 8, 0, time.UTC), Description: "Weekly Backup", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetResponse(t) v, err := snapshots.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, v.Name, "snapshot-001") th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockCreateResponse(t) options := snapshots.CreateOpts{VolumeID: "1234", Name: "snapshot-001"} n, err := snapshots.Create(client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.VolumeID, "1234") th.AssertEquals(t, n.Name, "snapshot-001") th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } func TestUpdateMetadata(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUpdateMetadataResponse(t) expected := map[string]interface{}{"key": "v1"} options := &snapshots.UpdateMetadataOpts{ Metadata: map[string]interface{}{ "key": "v1", }, } actual, err := snapshots.UpdateMetadata(client.ServiceClient(), "123", options).ExtractMetadata() th.AssertNoErr(t, err) th.AssertDeepEquals(t, actual, expected) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockDeleteResponse(t) res := snapshots.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000012111335005366400340220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/snapshotspackage snapshots import "github.com/gophercloud/gophercloud" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("snapshots") } func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("snapshots", id) } func getURL(c *gophercloud.ServiceClient, id string) string { return deleteURL(c, id) } func listURL(c *gophercloud.ServiceClient) string { return createURL(c) } func metadataURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("snapshots", id, "metadata") } func updateMetadataURL(c *gophercloud.ServiceClient, id string) string { return metadataURL(c, id) } util.go000066400000000000000000000010071335005366400340150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/snapshotspackage snapshots import ( "github.com/gophercloud/gophercloud" ) // WaitForStatus will continually poll the resource, checking for a particular // status. It will do this for the amount of seconds defined. func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { current, err := Get(c, id).Extract() if err != nil { return false, err } if current.Status == status { return true, nil } return false, nil }) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/volumes/000077500000000000000000000000001335005366400322425ustar00rootroot00000000000000doc.go000066400000000000000000000004021335005366400332530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/volumes// Package volumes provides information and interaction with volumes in the // OpenStack Block Storage service. A volume is a detachable block storage // device, akin to a USB hard drive. It can only be attached to one instance at // a time. package volumes requests.go000066400000000000000000000127321335005366400343720ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/volumespackage volumes import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToVolumeCreateMap() (map[string]interface{}, error) } // CreateOpts contains options for creating a Volume. This object is passed to // the volumes.Create function. For more information about these parameters, // see the Volume object. type CreateOpts struct { Size int `json:"size" required:"true"` AvailabilityZone string `json:"availability_zone,omitempty"` Description string `json:"display_description,omitempty"` Metadata map[string]string `json:"metadata,omitempty"` Name string `json:"display_name,omitempty"` SnapshotID string `json:"snapshot_id,omitempty"` SourceVolID string `json:"source_volid,omitempty"` ImageID string `json:"imageRef,omitempty"` VolumeType string `json:"volume_type,omitempty"` } // ToVolumeCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "volume") } // Create will create a new Volume based on the values in CreateOpts. To extract // the Volume object from the response, call the Extract method on the // CreateResult. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToVolumeCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) return } // Delete will delete the existing Volume with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // Get retrieves the Volume with the provided ID. To extract the Volume object // from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { ToVolumeListQuery() (string, error) } // ListOpts holds options for listing Volumes. It is passed to the volumes.List // function. type ListOpts struct { // admin-only option. Set it to true to see all tenant volumes. AllTenants bool `q:"all_tenants"` // List only volumes that contain Metadata. Metadata map[string]string `q:"metadata"` // List only volumes that have Name as the display name. Name string `q:"display_name"` // List only volumes that have a status of Status. Status string `q:"status"` } // ToVolumeListQuery formats a ListOpts into a query string. func (opts ListOpts) ToVolumeListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns Volumes optionally limited by the conditions provided in ListOpts. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToVolumeListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return VolumePage{pagination.SinglePageBase(r)} }) } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToVolumeUpdateMap() (map[string]interface{}, error) } // UpdateOpts contain options for updating an existing Volume. This object is passed // to the volumes.Update function. For more information about the parameters, see // the Volume object. type UpdateOpts struct { Name string `json:"display_name,omitempty"` Description string `json:"display_description,omitempty"` Metadata map[string]string `json:"metadata,omitempty"` } // ToVolumeUpdateMap assembles a request body based on the contents of an // UpdateOpts. func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "volume") } // Update will update the Volume with provided information. To extract the updated // Volume from the response, call the Extract method on the UpdateResult. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToVolumeUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // IDFromName is a convienience function that returns a server's ID given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" listOpts := ListOpts{ Name: name, } pages, err := List(client, listOpts).AllPages() if err != nil { return "", err } all, err := ExtractVolumes(pages) if err != nil { return "", err } for _, s := range all { if s.Name == name { count++ id = s.ID } } switch count { case 0: return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "volume"} case 1: return id, nil default: return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "volume"} } } results.go000066400000000000000000000057201335005366400342170ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/volumespackage volumes import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Volume contains all the information associated with an OpenStack Volume. type Volume struct { // Current status of the volume. Status string `json:"status"` // Human-readable display name for the volume. Name string `json:"display_name"` // Instances onto which the volume is attached. Attachments []map[string]interface{} `json:"attachments"` // This parameter is no longer used. AvailabilityZone string `json:"availability_zone"` // Indicates whether this is a bootable volume. Bootable string `json:"bootable"` // The date when this volume was created. CreatedAt time.Time `json:"-"` // Human-readable description for the volume. Description string `json:"display_description"` // The type of volume to create, either SATA or SSD. VolumeType string `json:"volume_type"` // The ID of the snapshot from which the volume was created SnapshotID string `json:"snapshot_id"` // The ID of another block storage volume from which the current volume was created SourceVolID string `json:"source_volid"` // Arbitrary key-value pairs defined by the user. Metadata map[string]string `json:"metadata"` // Unique identifier for the volume. ID string `json:"id"` // Size of the volume in GB. Size int `json:"size"` } func (r *Volume) UnmarshalJSON(b []byte) error { type tmp Volume var s struct { tmp CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Volume(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) return err } // CreateResult contains the response body and error from a Create request. type CreateResult struct { commonResult } // GetResult contains the response body and error from a Get request. type GetResult struct { commonResult } // DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { gophercloud.ErrResult } // VolumePage is a pagination.pager that is returned from a call to the List function. type VolumePage struct { pagination.SinglePageBase } // IsEmpty returns true if a VolumePage contains no Volumes. func (r VolumePage) IsEmpty() (bool, error) { volumes, err := ExtractVolumes(r) return len(volumes) == 0, err } // ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call. func ExtractVolumes(r pagination.Page) ([]Volume, error) { var s struct { Volumes []Volume `json:"volumes"` } err := (r.(VolumePage)).ExtractInto(&s) return s.Volumes, err } // UpdateResult contains the response body and error from an Update request. type UpdateResult struct { commonResult } type commonResult struct { gophercloud.Result } // Extract will get the Volume object out of the commonResult object. func (r commonResult) Extract() (*Volume, error) { var s struct { Volume *Volume `json:"volume"` } err := r.ExtractInto(&s) return s.Volume, err } testing/000077500000000000000000000000001335005366400336405ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/volumesdoc.go000066400000000000000000000004221335005366400347320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/volumes/testing// volumes_v1 package testing /* This is package created is to hold fixtures (which imports testing), so that importing volumes package does not inadvertently import testing into production code More information here: https://github.com/rackspace/gophercloud/issues/473 */ fixtures.go000066400000000000000000000065311335005366400360450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/volumes/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func MockListResponse(t *testing.T) { th.Mux.HandleFunc("/volumes", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "volumes": [ { "id": "289da7f8-6440-407c-9fb4-7db01ec49164", "display_name": "vol-001" }, { "id": "96c3bda7-c82a-4f50-be73-ca7621794835", "display_name": "vol-002" } ] } `) }) } func MockGetResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "volume": { "id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", "display_name": "vol-001", "display_description": "Another volume.", "status": "active", "size": 30, "volume_type": "289da7f8-6440-407c-9fb4-7db01ec49164", "metadata": { "contents": "junk" }, "availability_zone": "us-east1", "bootable": "false", "snapshot_id": null, "attachments": [ { "attachment_id": "03987cd1-0ad5-40d1-9b2a-7cc48295d4fa", "id": "47e9ecc5-4045-4ee3-9a4b-d859d546a0cf", "volume_id": "6c80f8ac-e3e2-480c-8e6e-f1db92fe4bfe", "server_id": "d1c4788b-9435-42e2-9b81-29f3be1cd01f", "host_name": "mitaka", "device": "/" } ], "created_at": "2012-02-14T20:53:07" } } `) }) } func MockCreateResponse(t *testing.T) { th.Mux.HandleFunc("/volumes", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "volume": { "size": 75, "availability_zone": "us-east1" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "volume": { "size": 4, "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22" } } `) }) } func MockDeleteResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) } func MockUpdateResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "volume": { "display_name": "vol-002", "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22" } } `) }) } requests_test.go000066400000000000000000000066701335005366400371120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/volumes/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) count := 0 volumes.List(client.ServiceClient(), &volumes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := volumes.ExtractVolumes(page) if err != nil { t.Errorf("Failed to extract volumes: %v", err) return false, err } expected := []volumes.Volume{ { ID: "289da7f8-6440-407c-9fb4-7db01ec49164", Name: "vol-001", }, { ID: "96c3bda7-c82a-4f50-be73-ca7621794835", Name: "vol-002", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestListAll(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := volumes.ExtractVolumes(allPages) th.AssertNoErr(t, err) expected := []volumes.Volume{ { ID: "289da7f8-6440-407c-9fb4-7db01ec49164", Name: "vol-001", }, { ID: "96c3bda7-c82a-4f50-be73-ca7621794835", Name: "vol-002", }, } th.CheckDeepEquals(t, expected, actual) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetResponse(t) actual, err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) expected := &volumes.Volume{ Status: "active", Name: "vol-001", Attachments: []map[string]interface{}{ { "attachment_id": "03987cd1-0ad5-40d1-9b2a-7cc48295d4fa", "id": "47e9ecc5-4045-4ee3-9a4b-d859d546a0cf", "volume_id": "6c80f8ac-e3e2-480c-8e6e-f1db92fe4bfe", "server_id": "d1c4788b-9435-42e2-9b81-29f3be1cd01f", "host_name": "mitaka", "device": "/", }, }, AvailabilityZone: "us-east1", Bootable: "false", CreatedAt: time.Date(2012, 2, 14, 20, 53, 07, 0, time.UTC), Description: "Another volume.", VolumeType: "289da7f8-6440-407c-9fb4-7db01ec49164", SnapshotID: "", SourceVolID: "", Metadata: map[string]string{ "contents": "junk", }, ID: "521752a6-acf6-4b2d-bc7a-119f9148cd8c", Size: 30, } th.AssertDeepEquals(t, expected, actual) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockCreateResponse(t) options := &volumes.CreateOpts{ Size: 75, AvailabilityZone: "us-east1", } n, err := volumes.Create(client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Size, 4) th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockDeleteResponse(t) res := volumes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUpdateResponse(t) options := volumes.UpdateOpts{Name: "vol-002"} v, err := volumes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() th.AssertNoErr(t, err) th.CheckEquals(t, "vol-002", v.Name) } urls.go000066400000000000000000000010011335005366400334670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/volumespackage volumes import "github.com/gophercloud/gophercloud" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("volumes") } func listURL(c *gophercloud.ServiceClient) string { return createURL(c) } func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("volumes", id) } func getURL(c *gophercloud.ServiceClient, id string) string { return deleteURL(c, id) } func updateURL(c *gophercloud.ServiceClient, id string) string { return deleteURL(c, id) } util.go000066400000000000000000000010051335005366400334630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/volumespackage volumes import ( "github.com/gophercloud/gophercloud" ) // WaitForStatus will continually poll the resource, checking for a particular // status. It will do this for the amount of seconds defined. func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { current, err := Get(c, id).Extract() if err != nil { return false, err } if current.Status == status { return true, nil } return false, nil }) } volumetypes/000077500000000000000000000000001335005366400330655ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1doc.go000066400000000000000000000010121335005366400341530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/volumetypes// Package volumetypes provides information and interaction with volume types // in the OpenStack Block Storage service. A volume type indicates the type of // a block storage volume, such as SATA, SCSCI, SSD, etc. These can be // customized or defined by the OpenStack admin. // // You can also define extra_specs associated with your volume types. For // instance, you could have a VolumeType=SATA, with extra_specs (RPM=10000, // RAID-Level=5) . Extra_specs are defined and customized by the admin. package volumetypes requests.go000066400000000000000000000035651335005366400353000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/volumetypespackage volumetypes import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToVolumeTypeCreateMap() (map[string]interface{}, error) } // CreateOpts are options for creating a volume type. type CreateOpts struct { // See VolumeType. ExtraSpecs map[string]interface{} `json:"extra_specs,omitempty"` // See VolumeType. Name string `json:"name,omitempty"` } // ToVolumeTypeCreateMap casts a CreateOpts struct to a map. func (opts CreateOpts) ToVolumeTypeCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "volume_type") } // Create will create a new volume. To extract the created volume type object, // call the Extract method on the CreateResult. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToVolumeTypeCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) return } // Delete will delete the volume type with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // Get will retrieve the volume type with the provided ID. To extract the volume // type from the result, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // List returns all volume types. func List(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { return VolumeTypePage{pagination.SinglePageBase(r)} }) } results.go000066400000000000000000000032771335005366400351260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/volumetypespackage volumetypes import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // VolumeType contains all information associated with an OpenStack Volume Type. type VolumeType struct { ExtraSpecs map[string]interface{} `json:"extra_specs"` // user-defined metadata ID string `json:"id"` // unique identifier Name string `json:"name"` // display name } // CreateResult contains the response body and error from a Create request. type CreateResult struct { commonResult } // GetResult contains the response body and error from a Get request. type GetResult struct { commonResult } // DeleteResult contains the response error from a Delete request. type DeleteResult struct { gophercloud.ErrResult } // VolumeTypePage is a pagination.Pager that is returned from a call to the List function. type VolumeTypePage struct { pagination.SinglePageBase } // IsEmpty returns true if a VolumeTypePage contains no Volume Types. func (r VolumeTypePage) IsEmpty() (bool, error) { volumeTypes, err := ExtractVolumeTypes(r) return len(volumeTypes) == 0, err } // ExtractVolumeTypes extracts and returns Volume Types. func ExtractVolumeTypes(r pagination.Page) ([]VolumeType, error) { var s struct { VolumeTypes []VolumeType `json:"volume_types"` } err := (r.(VolumeTypePage)).ExtractInto(&s) return s.VolumeTypes, err } type commonResult struct { gophercloud.Result } // Extract will get the Volume Type object out of the commonResult object. func (r commonResult) Extract() (*VolumeType, error) { var s struct { VolumeType *VolumeType `json:"volume_type"` } err := r.ExtractInto(&s) return s.VolumeType, err } testing/000077500000000000000000000000001335005366400345425ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/volumetypesdoc.go000066400000000000000000000000421335005366400356320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/volumetypes/testing// volumetypes_v1 package testing fixtures.go000066400000000000000000000025371335005366400367510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/volumetypes/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func MockListResponse(t *testing.T) { th.Mux.HandleFunc("/types", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "volume_types": [ { "id": "289da7f8-6440-407c-9fb4-7db01ec49164", "name": "vol-type-001", "extra_specs": { "capabilities": "gpu" } }, { "id": "96c3bda7-c82a-4f50-be73-ca7621794835", "name": "vol-type-002", "extra_specs": {} } ] } `) }) } func MockGetResponse(t *testing.T) { th.Mux.HandleFunc("/types/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "volume_type": { "name": "vol-type-001", "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "extra_specs": { "serverNumber": "2" } } } `) }) } requests_test.go000066400000000000000000000056051335005366400400110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/volumetypes/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumetypes" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) count := 0 volumetypes.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := volumetypes.ExtractVolumeTypes(page) if err != nil { t.Errorf("Failed to extract volume types: %v", err) return false, err } expected := []volumetypes.VolumeType{ { ID: "289da7f8-6440-407c-9fb4-7db01ec49164", Name: "vol-type-001", ExtraSpecs: map[string]interface{}{ "capabilities": "gpu", }, }, { ID: "96c3bda7-c82a-4f50-be73-ca7621794835", Name: "vol-type-002", ExtraSpecs: map[string]interface{}{}, }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetResponse(t) vt, err := volumetypes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, vt.ExtraSpecs, map[string]interface{}{"serverNumber": "2"}) th.AssertEquals(t, vt.Name, "vol-type-001") th.AssertEquals(t, vt.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/types", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "volume_type": { "name": "vol-type-001" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "volume_type": { "name": "vol-type-001", "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22" } } `) }) options := &volumetypes.CreateOpts{Name: "vol-type-001"} n, err := volumetypes.Create(client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, "vol-type-001") th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/types/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusAccepted) }) err := volumetypes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractErr() th.AssertNoErr(t, err) } urls.go000066400000000000000000000006371335005366400344070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v1/volumetypespackage volumetypes import "github.com/gophercloud/gophercloud" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("types") } func createURL(c *gophercloud.ServiceClient) string { return listURL(c) } func getURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("types", id) } func deleteURL(c *gophercloud.ServiceClient, id string) string { return getURL(c, id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/000077500000000000000000000000001335005366400305515ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/snapshots/000077500000000000000000000000001335005366400325735ustar00rootroot00000000000000doc.go000066400000000000000000000004141335005366400336070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/snapshots// Package snapshots provides information and interaction with snapshots in the // OpenStack Block Storage service. A snapshot is a point in time copy of the // data contained in an external storage volume, and can be controlled // programmatically. package snapshots requests.go000066400000000000000000000125371335005366400347260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/snapshotspackage snapshots import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToSnapshotCreateMap() (map[string]interface{}, error) } // CreateOpts contains options for creating a Snapshot. This object is passed to // the snapshots.Create function. For more information about these parameters, // see the Snapshot object. type CreateOpts struct { VolumeID string `json:"volume_id" required:"true"` Force bool `json:"force,omitempty"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` Metadata map[string]string `json:"metadata,omitempty"` } // ToSnapshotCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "snapshot") } // Create will create a new Snapshot based on the values in CreateOpts. To // extract the Snapshot object from the response, call the Extract method on the // CreateResult. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSnapshotCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return } // Delete will delete the existing Snapshot with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // Get retrieves the Snapshot with the provided ID. To extract the Snapshot // object from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { ToSnapshotListQuery() (string, error) } // ListOpts hold options for listing Snapshots. It is passed to the // snapshots.List function. type ListOpts struct { // AllTenants will retrieve snapshots of all tenants/projects. AllTenants bool `q:"all_tenants"` // Name will filter by the specified snapshot name. Name string `q:"name"` // Status will filter by the specified status. Status string `q:"status"` // TenantID will filter by a specific tenant/project ID. // Setting AllTenants is required to use this. TenantID string `q:"project_id"` // VolumeID will filter by a specified volume ID. VolumeID string `q:"volume_id"` } // ToSnapshotListQuery formats a ListOpts into a query string. func (opts ListOpts) ToSnapshotListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns Snapshots optionally limited by the conditions provided in // ListOpts. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToSnapshotListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return SnapshotPage{pagination.SinglePageBase(r)} }) } // UpdateMetadataOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateMetadataOptsBuilder interface { ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) } // UpdateMetadataOpts contain options for updating an existing Snapshot. This // object is passed to the snapshots.Update function. For more information // about the parameters, see the Snapshot object. type UpdateMetadataOpts struct { Metadata map[string]interface{} `json:"metadata,omitempty"` } // ToSnapshotUpdateMetadataMap assembles a request body based on the contents of // an UpdateMetadataOpts. func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // UpdateMetadata will update the Snapshot with provided information. To // extract the updated Snapshot from the response, call the ExtractMetadata // method on the UpdateMetadataResult. func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { b, err := opts.ToSnapshotUpdateMetadataMap() if err != nil { r.Err = err return } _, r.Err = client.Put(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // IDFromName is a convienience function that returns a snapshot's ID given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" listOpts := ListOpts{ Name: name, } pages, err := List(client, listOpts).AllPages() if err != nil { return "", err } all, err := ExtractSnapshots(pages) if err != nil { return "", err } for _, s := range all { if s.Name == name { count++ id = s.ID } } switch count { case 0: return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "snapshot"} case 1: return id, nil default: return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "snapshot"} } } results.go000066400000000000000000000055711335005366400345540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/snapshotspackage snapshots import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Snapshot contains all the information associated with a Cinder Snapshot. type Snapshot struct { // Unique identifier. ID string `json:"id"` // Date created. CreatedAt time.Time `json:"-"` // Date updated. UpdatedAt time.Time `json:"-"` // Display name. Name string `json:"name"` // Display description. Description string `json:"description"` // ID of the Volume from which this Snapshot was created. VolumeID string `json:"volume_id"` // Currect status of the Snapshot. Status string `json:"status"` // Size of the Snapshot, in GB. Size int `json:"size"` // User-defined key-value pairs. Metadata map[string]string `json:"metadata"` } // CreateResult contains the response body and error from a Create request. type CreateResult struct { commonResult } // GetResult contains the response body and error from a Get request. type GetResult struct { commonResult } // DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { gophercloud.ErrResult } // SnapshotPage is a pagination.Pager that is returned from a call to the List function. type SnapshotPage struct { pagination.SinglePageBase } func (r *Snapshot) UnmarshalJSON(b []byte) error { type tmp Snapshot var s struct { tmp CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Snapshot(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) r.UpdatedAt = time.Time(s.UpdatedAt) return err } // IsEmpty returns true if a SnapshotPage contains no Snapshots. func (r SnapshotPage) IsEmpty() (bool, error) { volumes, err := ExtractSnapshots(r) return len(volumes) == 0, err } // ExtractSnapshots extracts and returns Snapshots. It is used while iterating over a snapshots.List call. func ExtractSnapshots(r pagination.Page) ([]Snapshot, error) { var s struct { Snapshots []Snapshot `json:"snapshots"` } err := (r.(SnapshotPage)).ExtractInto(&s) return s.Snapshots, err } // UpdateMetadataResult contains the response body and error from an UpdateMetadata request. type UpdateMetadataResult struct { commonResult } // ExtractMetadata returns the metadata from a response from snapshots.UpdateMetadata. func (r UpdateMetadataResult) ExtractMetadata() (map[string]interface{}, error) { if r.Err != nil { return nil, r.Err } m := r.Body.(map[string]interface{})["metadata"] return m.(map[string]interface{}), nil } type commonResult struct { gophercloud.Result } // Extract will get the Snapshot object out of the commonResult object. func (r commonResult) Extract() (*Snapshot, error) { var s struct { Snapshot *Snapshot `json:"snapshot"` } err := r.ExtractInto(&s) return s.Snapshot, err } testing/000077500000000000000000000000001335005366400341715ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/snapshotsdoc.go000066400000000000000000000000401335005366400352570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/snapshots/testing// snapshots_v2 package testing fixtures.go000066400000000000000000000066101335005366400363740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/snapshots/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func MockListResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "snapshots": [ { "id": "289da7f8-6440-407c-9fb4-7db01ec49164", "name": "snapshot-001", "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", "description": "Daily Backup", "status": "available", "size": 30, "created_at": "2017-05-30T03:35:03.000000" }, { "id": "96c3bda7-c82a-4f50-be73-ca7621794835", "name": "snapshot-002", "volume_id": "76b8950a-8594-4e5b-8dce-0dfa9c696358", "description": "Weekly Backup", "status": "available", "size": 25, "created_at": "2017-05-30T03:35:03.000000" } ] } `) }) } func MockGetResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "snapshot": { "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "name": "snapshot-001", "description": "Daily backup", "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", "status": "available", "size": 30, "created_at": "2017-05-30T03:35:03.000000" } } `) }) } func MockCreateResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "snapshot": { "volume_id": "1234", "name": "snapshot-001" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, ` { "snapshot": { "volume_id": "1234", "name": "snapshot-001", "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "description": "Daily backup", "volume_id": "1234", "status": "available", "size": 30, "created_at": "2017-05-30T03:35:03.000000" } } `) }) } func MockUpdateMetadataResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots/123/metadata", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, ` { "metadata": { "key": "v1" } } `) fmt.Fprintf(w, ` { "metadata": { "key": "v1" } } `) }) } func MockDeleteResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000054421335005366400374370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/snapshots/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) count := 0 snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := snapshots.ExtractSnapshots(page) if err != nil { t.Errorf("Failed to extract snapshots: %v", err) return false, err } expected := []snapshots.Snapshot{ { ID: "289da7f8-6440-407c-9fb4-7db01ec49164", Name: "snapshot-001", VolumeID: "521752a6-acf6-4b2d-bc7a-119f9148cd8c", Status: "available", Size: 30, CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), Description: "Daily Backup", }, { ID: "96c3bda7-c82a-4f50-be73-ca7621794835", Name: "snapshot-002", VolumeID: "76b8950a-8594-4e5b-8dce-0dfa9c696358", Status: "available", Size: 25, CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), Description: "Weekly Backup", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetResponse(t) v, err := snapshots.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, v.Name, "snapshot-001") th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockCreateResponse(t) options := snapshots.CreateOpts{VolumeID: "1234", Name: "snapshot-001"} n, err := snapshots.Create(client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.VolumeID, "1234") th.AssertEquals(t, n.Name, "snapshot-001") th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } func TestUpdateMetadata(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUpdateMetadataResponse(t) expected := map[string]interface{}{"key": "v1"} options := &snapshots.UpdateMetadataOpts{ Metadata: map[string]interface{}{ "key": "v1", }, } actual, err := snapshots.UpdateMetadata(client.ServiceClient(), "123", options).ExtractMetadata() th.AssertNoErr(t, err) th.AssertDeepEquals(t, actual, expected) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockDeleteResponse(t) res := snapshots.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000012111335005366400340230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/snapshotspackage snapshots import "github.com/gophercloud/gophercloud" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("snapshots") } func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("snapshots", id) } func getURL(c *gophercloud.ServiceClient, id string) string { return deleteURL(c, id) } func listURL(c *gophercloud.ServiceClient) string { return createURL(c) } func metadataURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("snapshots", id, "metadata") } func updateMetadataURL(c *gophercloud.ServiceClient, id string) string { return metadataURL(c, id) } util.go000066400000000000000000000010071335005366400340160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/snapshotspackage snapshots import ( "github.com/gophercloud/gophercloud" ) // WaitForStatus will continually poll the resource, checking for a particular // status. It will do this for the amount of seconds defined. func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { current, err := Get(c, id).Extract() if err != nil { return false, err } if current.Status == status { return true, nil } return false, nil }) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/volumes/000077500000000000000000000000001335005366400322435ustar00rootroot00000000000000doc.go000066400000000000000000000004021335005366400332540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/volumes// Package volumes provides information and interaction with volumes in the // OpenStack Block Storage service. A volume is a detachable block storage // device, akin to a USB hard drive. It can only be attached to one instance at // a time. package volumes requests.go000066400000000000000000000146121335005366400343720ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/volumespackage volumes import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToVolumeCreateMap() (map[string]interface{}, error) } // CreateOpts contains options for creating a Volume. This object is passed to // the volumes.Create function. For more information about these parameters, // see the Volume object. type CreateOpts struct { // The size of the volume, in GB Size int `json:"size" required:"true"` // The availability zone AvailabilityZone string `json:"availability_zone,omitempty"` // ConsistencyGroupID is the ID of a consistency group ConsistencyGroupID string `json:"consistencygroup_id,omitempty"` // The volume description Description string `json:"description,omitempty"` // One or more metadata key and value pairs to associate with the volume Metadata map[string]string `json:"metadata,omitempty"` // The volume name Name string `json:"name,omitempty"` // the ID of the existing volume snapshot SnapshotID string `json:"snapshot_id,omitempty"` // SourceReplica is a UUID of an existing volume to replicate with SourceReplica string `json:"source_replica,omitempty"` // the ID of the existing volume SourceVolID string `json:"source_volid,omitempty"` // The ID of the image from which you want to create the volume. // Required to create a bootable volume. ImageID string `json:"imageRef,omitempty"` // The associated volume type VolumeType string `json:"volume_type,omitempty"` } // ToVolumeCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "volume") } // Create will create a new Volume based on the values in CreateOpts. To extract // the Volume object from the response, call the Extract method on the // CreateResult. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToVolumeCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return } // Delete will delete the existing Volume with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // Get retrieves the Volume with the provided ID. To extract the Volume object // from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { ToVolumeListQuery() (string, error) } // ListOpts holds options for listing Volumes. It is passed to the volumes.List // function. type ListOpts struct { // AllTenants will retrieve volumes of all tenants/projects. AllTenants bool `q:"all_tenants"` // Metadata will filter results based on specified metadata. Metadata map[string]string `q:"metadata"` // Name will filter by the specified volume name. Name string `q:"name"` // Status will filter by the specified status. Status string `q:"status"` // TenantID will filter by a specific tenant/project ID. // Setting AllTenants is required for this. TenantID string `q:"project_id"` // Comma-separated list of sort keys and optional sort directions in the // form of [:]. Sort string `q:"sort"` // Requests a page size of items. Limit int `q:"limit"` // Used in conjunction with limit to return a slice of items. Offset int `q:"offset"` // The ID of the last-seen item. Marker string `q:"marker"` } // ToVolumeListQuery formats a ListOpts into a query string. func (opts ListOpts) ToVolumeListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns Volumes optionally limited by the conditions provided in ListOpts. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToVolumeListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return VolumePage{pagination.LinkedPageBase{PageResult: r}} }) } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToVolumeUpdateMap() (map[string]interface{}, error) } // UpdateOpts contain options for updating an existing Volume. This object is passed // to the volumes.Update function. For more information about the parameters, see // the Volume object. type UpdateOpts struct { Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` Metadata map[string]string `json:"metadata,omitempty"` } // ToVolumeUpdateMap assembles a request body based on the contents of an // UpdateOpts. func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "volume") } // Update will update the Volume with provided information. To extract the updated // Volume from the response, call the Extract method on the UpdateResult. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToVolumeUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // IDFromName is a convienience function that returns a server's ID given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" listOpts := ListOpts{ Name: name, } pages, err := List(client, listOpts).AllPages() if err != nil { return "", err } all, err := ExtractVolumes(pages) if err != nil { return "", err } for _, s := range all { if s.Name == name { count++ id = s.ID } } switch count { case 0: return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "volume"} case 1: return id, nil default: return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "volume"} } } results.go000066400000000000000000000112771335005366400342240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/volumespackage volumes import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type Attachment struct { AttachedAt time.Time `json:"-"` AttachmentID string `json:"attachment_id"` Device string `json:"device"` HostName string `json:"host_name"` ID string `json:"id"` ServerID string `json:"server_id"` VolumeID string `json:"volume_id"` } func (r *Attachment) UnmarshalJSON(b []byte) error { type tmp Attachment var s struct { tmp AttachedAt gophercloud.JSONRFC3339MilliNoZ `json:"attached_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Attachment(s.tmp) r.AttachedAt = time.Time(s.AttachedAt) return err } // Volume contains all the information associated with an OpenStack Volume. type Volume struct { // Unique identifier for the volume. ID string `json:"id"` // Current status of the volume. Status string `json:"status"` // Size of the volume in GB. Size int `json:"size"` // AvailabilityZone is which availability zone the volume is in. AvailabilityZone string `json:"availability_zone"` // The date when this volume was created. CreatedAt time.Time `json:"-"` // The date when this volume was last updated UpdatedAt time.Time `json:"-"` // Instances onto which the volume is attached. Attachments []Attachment `json:"attachments"` // Human-readable display name for the volume. Name string `json:"name"` // Human-readable description for the volume. Description string `json:"description"` // The type of volume to create, either SATA or SSD. VolumeType string `json:"volume_type"` // The ID of the snapshot from which the volume was created SnapshotID string `json:"snapshot_id"` // The ID of another block storage volume from which the current volume was created SourceVolID string `json:"source_volid"` // Arbitrary key-value pairs defined by the user. Metadata map[string]string `json:"metadata"` // UserID is the id of the user who created the volume. UserID string `json:"user_id"` // Indicates whether this is a bootable volume. Bootable string `json:"bootable"` // Encrypted denotes if the volume is encrypted. Encrypted bool `json:"encrypted"` // ReplicationStatus is the status of replication. ReplicationStatus string `json:"replication_status"` // ConsistencyGroupID is the consistency group ID. ConsistencyGroupID string `json:"consistencygroup_id"` // Multiattach denotes if the volume is multi-attach capable. Multiattach bool `json:"multiattach"` } func (r *Volume) UnmarshalJSON(b []byte) error { type tmp Volume var s struct { tmp CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Volume(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) r.UpdatedAt = time.Time(s.UpdatedAt) return err } // VolumePage is a pagination.pager that is returned from a call to the List function. type VolumePage struct { pagination.LinkedPageBase } // IsEmpty returns true if a ListResult contains no Volumes. func (r VolumePage) IsEmpty() (bool, error) { volumes, err := ExtractVolumes(r) return len(volumes) == 0, err } // NextPageURL uses the response's embedded link reference to navigate to the // next page of results. func (r VolumePage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"volumes_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call. func ExtractVolumes(r pagination.Page) ([]Volume, error) { var s []Volume err := ExtractVolumesInto(r, &s) return s, err } type commonResult struct { gophercloud.Result } // Extract will get the Volume object out of the commonResult object. func (r commonResult) Extract() (*Volume, error) { var s Volume err := r.ExtractInto(&s) return &s, err } func (r commonResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "volume") } func ExtractVolumesInto(r pagination.Page, v interface{}) error { return r.(VolumePage).Result.ExtractIntoSlicePtr(v, "volumes") } // CreateResult contains the response body and error from a Create request. type CreateResult struct { commonResult } // GetResult contains the response body and error from a Get request. type GetResult struct { commonResult } // UpdateResult contains the response body and error from an Update request. type UpdateResult struct { commonResult } // DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400336415ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/volumesdoc.go000066400000000000000000000000361335005366400347340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/volumes/testing// volumes_v2 package testing fixtures.go000066400000000000000000000135531335005366400360500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/volumes/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func MockListResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/detail", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "volumes": [ { "volume_type": "lvmdriver-1", "created_at": "2015-09-17T03:35:03.000000", "bootable": "false", "name": "vol-001", "os-vol-mig-status-attr:name_id": null, "consistencygroup_id": null, "source_volid": null, "os-volume-replication:driver_data": null, "multiattach": false, "snapshot_id": null, "replication_status": "disabled", "os-volume-replication:extended_status": null, "encrypted": false, "os-vol-host-attr:host": null, "availability_zone": "nova", "attachments": [{ "server_id": "83ec2e3b-4321-422b-8706-a84185f52a0a", "attachment_id": "05551600-a936-4d4a-ba42-79a037c1-c91a", "attached_at": "2016-08-06T14:48:20.000000", "host_name": "foobar", "volume_id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", "device": "/dev/vdc", "id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75" }], "id": "289da7f8-6440-407c-9fb4-7db01ec49164", "size": 75, "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459", "os-vol-mig-status-attr:migstat": null, "metadata": {"foo": "bar"}, "status": "available", "description": null }, { "volume_type": "lvmdriver-1", "created_at": "2015-09-17T03:32:29.000000", "bootable": "false", "name": "vol-002", "os-vol-mig-status-attr:name_id": null, "consistencygroup_id": null, "source_volid": null, "os-volume-replication:driver_data": null, "multiattach": false, "snapshot_id": null, "replication_status": "disabled", "os-volume-replication:extended_status": null, "encrypted": false, "os-vol-host-attr:host": null, "availability_zone": "nova", "attachments": [], "id": "96c3bda7-c82a-4f50-be73-ca7621794835", "size": 75, "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459", "os-vol-mig-status-attr:migstat": null, "metadata": {}, "status": "available", "description": null } ] } `) }) } func MockGetResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "volume": { "volume_type": "lvmdriver-1", "created_at": "2015-09-17T03:32:29.000000", "bootable": "false", "name": "vol-001", "os-vol-mig-status-attr:name_id": null, "consistencygroup_id": null, "source_volid": null, "os-volume-replication:driver_data": null, "multiattach": false, "snapshot_id": null, "replication_status": "disabled", "os-volume-replication:extended_status": null, "encrypted": false, "os-vol-host-attr:host": null, "availability_zone": "nova", "attachments": [{ "server_id": "83ec2e3b-4321-422b-8706-a84185f52a0a", "attachment_id": "05551600-a936-4d4a-ba42-79a037c1-c91a", "attached_at": "2016-08-06T14:48:20.000000", "host_name": "foobar", "volume_id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", "device": "/dev/vdc", "id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75" }], "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "size": 75, "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459", "os-vol-mig-status-attr:migstat": null, "metadata": {}, "status": "available", "description": null } } `) }) } func MockCreateResponse(t *testing.T) { th.Mux.HandleFunc("/volumes", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "volume": { "name": "vol-001", "size": 75 } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, ` { "volume": { "size": 75, "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "metadata": {}, "created_at": "2015-09-17T03:32:29.044216", "encrypted": false, "bootable": "false", "availability_zone": "nova", "attachments": [], "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", "status": "creating", "description": null, "volume_type": "lvmdriver-1", "name": "vol-001", "replication_status": "disabled", "consistencygroup_id": null, "source_volid": null, "snapshot_id": null, "multiattach": false } } `) }) } func MockDeleteResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusAccepted) }) } func MockUpdateResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "volume": { "name": "vol-002" } } `) }) } requests_test.go000066400000000000000000000166651335005366400371200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/volumes/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumetenants" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListWithExtensions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) count := 0 volumes.List(client.ServiceClient(), &volumes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := volumes.ExtractVolumes(page) if err != nil { t.Errorf("Failed to extract volumes: %v", err) return false, err } expected := []volumes.Volume{ { ID: "289da7f8-6440-407c-9fb4-7db01ec49164", Name: "vol-001", Attachments: []volumes.Attachment{{ ServerID: "83ec2e3b-4321-422b-8706-a84185f52a0a", AttachmentID: "05551600-a936-4d4a-ba42-79a037c1-c91a", AttachedAt: time.Date(2016, 8, 6, 14, 48, 20, 0, time.UTC), HostName: "foobar", VolumeID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", Device: "/dev/vdc", ID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", }}, AvailabilityZone: "nova", Bootable: "false", ConsistencyGroupID: "", CreatedAt: time.Date(2015, 9, 17, 3, 35, 3, 0, time.UTC), Description: "", Encrypted: false, Metadata: map[string]string{"foo": "bar"}, Multiattach: false, //TenantID: "304dc00909ac4d0da6c62d816bcb3459", //ReplicationDriverData: "", //ReplicationExtendedStatus: "", ReplicationStatus: "disabled", Size: 75, SnapshotID: "", SourceVolID: "", Status: "available", UserID: "ff1ce52c03ab433aaba9108c2e3ef541", VolumeType: "lvmdriver-1", }, { ID: "96c3bda7-c82a-4f50-be73-ca7621794835", Name: "vol-002", Attachments: []volumes.Attachment{}, AvailabilityZone: "nova", Bootable: "false", ConsistencyGroupID: "", CreatedAt: time.Date(2015, 9, 17, 3, 32, 29, 0, time.UTC), Description: "", Encrypted: false, Metadata: map[string]string{}, Multiattach: false, //TenantID: "304dc00909ac4d0da6c62d816bcb3459", //ReplicationDriverData: "", //ReplicationExtendedStatus: "", ReplicationStatus: "disabled", Size: 75, SnapshotID: "", SourceVolID: "", Status: "available", UserID: "ff1ce52c03ab433aaba9108c2e3ef541", VolumeType: "lvmdriver-1", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestListAllWithExtensions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) type VolumeWithExt struct { volumes.Volume volumetenants.VolumeTenantExt } allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() th.AssertNoErr(t, err) var actual []VolumeWithExt err = volumes.ExtractVolumesInto(allPages, &actual) th.AssertNoErr(t, err) th.AssertEquals(t, 2, len(actual)) th.AssertEquals(t, "304dc00909ac4d0da6c62d816bcb3459", actual[0].TenantID) } func TestListAll(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := volumes.ExtractVolumes(allPages) th.AssertNoErr(t, err) expected := []volumes.Volume{ { ID: "289da7f8-6440-407c-9fb4-7db01ec49164", Name: "vol-001", Attachments: []volumes.Attachment{{ ServerID: "83ec2e3b-4321-422b-8706-a84185f52a0a", AttachmentID: "05551600-a936-4d4a-ba42-79a037c1-c91a", AttachedAt: time.Date(2016, 8, 6, 14, 48, 20, 0, time.UTC), HostName: "foobar", VolumeID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", Device: "/dev/vdc", ID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", }}, AvailabilityZone: "nova", Bootable: "false", ConsistencyGroupID: "", CreatedAt: time.Date(2015, 9, 17, 3, 35, 3, 0, time.UTC), Description: "", Encrypted: false, Metadata: map[string]string{"foo": "bar"}, Multiattach: false, //TenantID: "304dc00909ac4d0da6c62d816bcb3459", //ReplicationDriverData: "", //ReplicationExtendedStatus: "", ReplicationStatus: "disabled", Size: 75, SnapshotID: "", SourceVolID: "", Status: "available", UserID: "ff1ce52c03ab433aaba9108c2e3ef541", VolumeType: "lvmdriver-1", }, { ID: "96c3bda7-c82a-4f50-be73-ca7621794835", Name: "vol-002", Attachments: []volumes.Attachment{}, AvailabilityZone: "nova", Bootable: "false", ConsistencyGroupID: "", CreatedAt: time.Date(2015, 9, 17, 3, 32, 29, 0, time.UTC), Description: "", Encrypted: false, Metadata: map[string]string{}, Multiattach: false, //TenantID: "304dc00909ac4d0da6c62d816bcb3459", //ReplicationDriverData: "", //ReplicationExtendedStatus: "", ReplicationStatus: "disabled", Size: 75, SnapshotID: "", SourceVolID: "", Status: "available", UserID: "ff1ce52c03ab433aaba9108c2e3ef541", VolumeType: "lvmdriver-1", }, } th.CheckDeepEquals(t, expected, actual) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetResponse(t) v, err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, v.Name, "vol-001") th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockCreateResponse(t) options := &volumes.CreateOpts{Size: 75, Name: "vol-001"} n, err := volumes.Create(client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Size, 75) th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockDeleteResponse(t) res := volumes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUpdateResponse(t) options := volumes.UpdateOpts{Name: "vol-002"} v, err := volumes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() th.AssertNoErr(t, err) th.CheckEquals(t, "vol-002", v.Name) } func TestGetWithExtensions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetResponse(t) var s struct { volumes.Volume volumetenants.VolumeTenantExt } err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "304dc00909ac4d0da6c62d816bcb3459", s.TenantID) err = volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(s) if err == nil { t.Errorf("Expected error when providing non-pointer struct") } } urls.go000066400000000000000000000010261335005366400334770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/volumespackage volumes import "github.com/gophercloud/gophercloud" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("volumes") } func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("volumes", "detail") } func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("volumes", id) } func getURL(c *gophercloud.ServiceClient, id string) string { return deleteURL(c, id) } func updateURL(c *gophercloud.ServiceClient, id string) string { return deleteURL(c, id) } util.go000066400000000000000000000010051335005366400334640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v2/volumespackage volumes import ( "github.com/gophercloud/gophercloud" ) // WaitForStatus will continually poll the resource, checking for a particular // status. It will do this for the amount of seconds defined. func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { current, err := Get(c, id).Extract() if err != nil { return false, err } if current.Status == status { return true, nil } return false, nil }) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/000077500000000000000000000000001335005366400305525ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/snapshots/000077500000000000000000000000001335005366400325745ustar00rootroot00000000000000doc.go000066400000000000000000000004141335005366400336100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/snapshots// Package snapshots provides information and interaction with snapshots in the // OpenStack Block Storage service. A snapshot is a point in time copy of the // data contained in an external storage volume, and can be controlled // programmatically. package snapshots requests.go000066400000000000000000000131421335005366400347200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/snapshotspackage snapshots import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToSnapshotCreateMap() (map[string]interface{}, error) } // CreateOpts contains options for creating a Snapshot. This object is passed to // the snapshots.Create function. For more information about these parameters, // see the Snapshot object. type CreateOpts struct { VolumeID string `json:"volume_id" required:"true"` Force bool `json:"force,omitempty"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` Metadata map[string]string `json:"metadata,omitempty"` } // ToSnapshotCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "snapshot") } // Create will create a new Snapshot based on the values in CreateOpts. To // extract the Snapshot object from the response, call the Extract method on the // CreateResult. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSnapshotCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return } // Delete will delete the existing Snapshot with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // Get retrieves the Snapshot with the provided ID. To extract the Snapshot // object from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { ToSnapshotListQuery() (string, error) } type ListOpts struct { // AllTenants will retrieve snapshots of all tenants/projects. AllTenants bool `q:"all_tenants"` // Name will filter by the specified snapshot name. Name string `q:"name"` // Status will filter by the specified status. Status string `q:"status"` // TenantID will filter by a specific tenant/project ID. // Setting AllTenants is required to use this. TenantID string `q:"project_id"` // VolumeID will filter by a specified volume ID. VolumeID string `q:"volume_id"` // Comma-separated list of sort keys and optional sort directions in the // form of [:]. Sort string `q:"sort"` // Requests a page size of items. Limit int `q:"limit"` // Used in conjunction with limit to return a slice of items. Offset int `q:"offset"` // The ID of the last-seen item. Marker string `q:"marker"` } // ToSnapshotListQuery formats a ListOpts into a query string. func (opts ListOpts) ToSnapshotListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns Snapshots optionally limited by the conditions provided in // ListOpts. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToSnapshotListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return SnapshotPage{pagination.LinkedPageBase{PageResult: r}} }) } // UpdateMetadataOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateMetadataOptsBuilder interface { ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) } // UpdateMetadataOpts contain options for updating an existing Snapshot. This // object is passed to the snapshots.Update function. For more information // about the parameters, see the Snapshot object. type UpdateMetadataOpts struct { Metadata map[string]interface{} `json:"metadata,omitempty"` } // ToSnapshotUpdateMetadataMap assembles a request body based on the contents of // an UpdateMetadataOpts. func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // UpdateMetadata will update the Snapshot with provided information. To // extract the updated Snapshot from the response, call the ExtractMetadata // method on the UpdateMetadataResult. func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { b, err := opts.ToSnapshotUpdateMetadataMap() if err != nil { r.Err = err return } _, r.Err = client.Put(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // IDFromName is a convienience function that returns a snapshot's ID given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" listOpts := ListOpts{ Name: name, } pages, err := List(client, listOpts).AllPages() if err != nil { return "", err } all, err := ExtractSnapshots(pages) if err != nil { return "", err } for _, s := range all { if s.Name == name { count++ id = s.ID } } switch count { case 0: return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "snapshot"} case 1: return id, nil default: return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "snapshot"} } } results.go000066400000000000000000000062631335005366400345540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/snapshotspackage snapshots import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Snapshot contains all the information associated with a Cinder Snapshot. type Snapshot struct { // Unique identifier. ID string `json:"id"` // Date created. CreatedAt time.Time `json:"-"` // Date updated. UpdatedAt time.Time `json:"-"` // Display name. Name string `json:"name"` // Display description. Description string `json:"description"` // ID of the Volume from which this Snapshot was created. VolumeID string `json:"volume_id"` // Currect status of the Snapshot. Status string `json:"status"` // Size of the Snapshot, in GB. Size int `json:"size"` // User-defined key-value pairs. Metadata map[string]string `json:"metadata"` } // CreateResult contains the response body and error from a Create request. type CreateResult struct { commonResult } // GetResult contains the response body and error from a Get request. type GetResult struct { commonResult } // DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { gophercloud.ErrResult } // SnapshotPage is a pagination.Pager that is returned from a call to the List function. type SnapshotPage struct { pagination.LinkedPageBase } // UnmarshalJSON converts our JSON API response into our snapshot struct func (r *Snapshot) UnmarshalJSON(b []byte) error { type tmp Snapshot var s struct { tmp CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Snapshot(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) r.UpdatedAt = time.Time(s.UpdatedAt) return err } // IsEmpty returns true if a SnapshotPage contains no Snapshots. func (r SnapshotPage) IsEmpty() (bool, error) { volumes, err := ExtractSnapshots(r) return len(volumes) == 0, err } func (page SnapshotPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"snapshots_links"` } err := page.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // ExtractSnapshots extracts and returns Snapshots. It is used while iterating over a snapshots.List call. func ExtractSnapshots(r pagination.Page) ([]Snapshot, error) { var s struct { Snapshots []Snapshot `json:"snapshots"` } err := (r.(SnapshotPage)).ExtractInto(&s) return s.Snapshots, err } // UpdateMetadataResult contains the response body and error from an UpdateMetadata request. type UpdateMetadataResult struct { commonResult } // ExtractMetadata returns the metadata from a response from snapshots.UpdateMetadata. func (r UpdateMetadataResult) ExtractMetadata() (map[string]interface{}, error) { if r.Err != nil { return nil, r.Err } m := r.Body.(map[string]interface{})["metadata"] return m.(map[string]interface{}), nil } type commonResult struct { gophercloud.Result } // Extract will get the Snapshot object out of the commonResult object. func (r commonResult) Extract() (*Snapshot, error) { var s struct { Snapshot *Snapshot `json:"snapshot"` } err := r.ExtractInto(&s) return s.Snapshot, err } testing/000077500000000000000000000000001335005366400341725ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/snapshotsdoc.go000066400000000000000000000000401335005366400352600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/snapshots/testing// snapshots_v3 package testing fixtures.go000066400000000000000000000073171335005366400364020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/snapshots/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func MockListResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, ` { "snapshots": [ { "id": "289da7f8-6440-407c-9fb4-7db01ec49164", "name": "snapshot-001", "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", "description": "Daily Backup", "status": "available", "size": 30, "created_at": "2017-05-30T03:35:03.000000" }, { "id": "96c3bda7-c82a-4f50-be73-ca7621794835", "name": "snapshot-002", "volume_id": "76b8950a-8594-4e5b-8dce-0dfa9c696358", "description": "Weekly Backup", "status": "available", "size": 25, "created_at": "2017-05-30T03:35:03.000000" } ], "snapshots_links": [ { "href": "%s/snapshots?marker=1", "rel": "next" }] } `, th.Server.URL) case "1": fmt.Fprintf(w, `{"snapshots": []}`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) } func MockGetResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "snapshot": { "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "name": "snapshot-001", "description": "Daily backup", "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", "status": "available", "size": 30, "created_at": "2017-05-30T03:35:03.000000" } } `) }) } func MockCreateResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "snapshot": { "volume_id": "1234", "name": "snapshot-001" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, ` { "snapshot": { "volume_id": "1234", "name": "snapshot-001", "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "description": "Daily backup", "volume_id": "1234", "status": "available", "size": 30, "created_at": "2017-05-30T03:35:03.000000" } } `) }) } func MockUpdateMetadataResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots/123/metadata", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, ` { "metadata": { "key": "v1" } } `) fmt.Fprintf(w, ` { "metadata": { "key": "v1" } } `) }) } func MockDeleteResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000054421335005366400374400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/snapshots/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) count := 0 snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := snapshots.ExtractSnapshots(page) if err != nil { t.Errorf("Failed to extract snapshots: %v", err) return false, err } expected := []snapshots.Snapshot{ { ID: "289da7f8-6440-407c-9fb4-7db01ec49164", Name: "snapshot-001", VolumeID: "521752a6-acf6-4b2d-bc7a-119f9148cd8c", Status: "available", Size: 30, CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), Description: "Daily Backup", }, { ID: "96c3bda7-c82a-4f50-be73-ca7621794835", Name: "snapshot-002", VolumeID: "76b8950a-8594-4e5b-8dce-0dfa9c696358", Status: "available", Size: 25, CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), Description: "Weekly Backup", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetResponse(t) v, err := snapshots.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, v.Name, "snapshot-001") th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockCreateResponse(t) options := snapshots.CreateOpts{VolumeID: "1234", Name: "snapshot-001"} n, err := snapshots.Create(client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.VolumeID, "1234") th.AssertEquals(t, n.Name, "snapshot-001") th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } func TestUpdateMetadata(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUpdateMetadataResponse(t) expected := map[string]interface{}{"key": "v1"} options := &snapshots.UpdateMetadataOpts{ Metadata: map[string]interface{}{ "key": "v1", }, } actual, err := snapshots.UpdateMetadata(client.ServiceClient(), "123", options).ExtractMetadata() th.AssertNoErr(t, err) th.AssertDeepEquals(t, actual, expected) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockDeleteResponse(t) res := snapshots.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000012111335005366400340240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/snapshotspackage snapshots import "github.com/gophercloud/gophercloud" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("snapshots") } func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("snapshots", id) } func getURL(c *gophercloud.ServiceClient, id string) string { return deleteURL(c, id) } func listURL(c *gophercloud.ServiceClient) string { return createURL(c) } func metadataURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("snapshots", id, "metadata") } func updateMetadataURL(c *gophercloud.ServiceClient, id string) string { return metadataURL(c, id) } util.go000066400000000000000000000010071335005366400340170ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/snapshotspackage snapshots import ( "github.com/gophercloud/gophercloud" ) // WaitForStatus will continually poll the resource, checking for a particular // status. It will do this for the amount of seconds defined. func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { current, err := Get(c, id).Extract() if err != nil { return false, err } if current.Status == status { return true, nil } return false, nil }) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/volumes/000077500000000000000000000000001335005366400322445ustar00rootroot00000000000000doc.go000066400000000000000000000004021335005366400332550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/volumes// Package volumes provides information and interaction with volumes in the // OpenStack Block Storage service. A volume is a detachable block storage // device, akin to a USB hard drive. It can only be attached to one instance at // a time. package volumes requests.go000066400000000000000000000146121335005366400343730ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/volumespackage volumes import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToVolumeCreateMap() (map[string]interface{}, error) } // CreateOpts contains options for creating a Volume. This object is passed to // the volumes.Create function. For more information about these parameters, // see the Volume object. type CreateOpts struct { // The size of the volume, in GB Size int `json:"size" required:"true"` // The availability zone AvailabilityZone string `json:"availability_zone,omitempty"` // ConsistencyGroupID is the ID of a consistency group ConsistencyGroupID string `json:"consistencygroup_id,omitempty"` // The volume description Description string `json:"description,omitempty"` // One or more metadata key and value pairs to associate with the volume Metadata map[string]string `json:"metadata,omitempty"` // The volume name Name string `json:"name,omitempty"` // the ID of the existing volume snapshot SnapshotID string `json:"snapshot_id,omitempty"` // SourceReplica is a UUID of an existing volume to replicate with SourceReplica string `json:"source_replica,omitempty"` // the ID of the existing volume SourceVolID string `json:"source_volid,omitempty"` // The ID of the image from which you want to create the volume. // Required to create a bootable volume. ImageID string `json:"imageRef,omitempty"` // The associated volume type VolumeType string `json:"volume_type,omitempty"` } // ToVolumeCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "volume") } // Create will create a new Volume based on the values in CreateOpts. To extract // the Volume object from the response, call the Extract method on the // CreateResult. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToVolumeCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return } // Delete will delete the existing Volume with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // Get retrieves the Volume with the provided ID. To extract the Volume object // from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { ToVolumeListQuery() (string, error) } // ListOpts holds options for listing Volumes. It is passed to the volumes.List // function. type ListOpts struct { // AllTenants will retrieve volumes of all tenants/projects. AllTenants bool `q:"all_tenants"` // Metadata will filter results based on specified metadata. Metadata map[string]string `q:"metadata"` // Name will filter by the specified volume name. Name string `q:"name"` // Status will filter by the specified status. Status string `q:"status"` // TenantID will filter by a specific tenant/project ID. // Setting AllTenants is required for this. TenantID string `q:"project_id"` // Comma-separated list of sort keys and optional sort directions in the // form of [:]. Sort string `q:"sort"` // Requests a page size of items. Limit int `q:"limit"` // Used in conjunction with limit to return a slice of items. Offset int `q:"offset"` // The ID of the last-seen item. Marker string `q:"marker"` } // ToVolumeListQuery formats a ListOpts into a query string. func (opts ListOpts) ToVolumeListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns Volumes optionally limited by the conditions provided in ListOpts. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToVolumeListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return VolumePage{pagination.LinkedPageBase{PageResult: r}} }) } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToVolumeUpdateMap() (map[string]interface{}, error) } // UpdateOpts contain options for updating an existing Volume. This object is passed // to the volumes.Update function. For more information about the parameters, see // the Volume object. type UpdateOpts struct { Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` Metadata map[string]string `json:"metadata,omitempty"` } // ToVolumeUpdateMap assembles a request body based on the contents of an // UpdateOpts. func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "volume") } // Update will update the Volume with provided information. To extract the updated // Volume from the response, call the Extract method on the UpdateResult. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToVolumeUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // IDFromName is a convienience function that returns a server's ID given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" listOpts := ListOpts{ Name: name, } pages, err := List(client, listOpts).AllPages() if err != nil { return "", err } all, err := ExtractVolumes(pages) if err != nil { return "", err } for _, s := range all { if s.Name == name { count++ id = s.ID } } switch count { case 0: return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "volume"} case 1: return id, nil default: return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "volume"} } } results.go000066400000000000000000000115771335005366400342300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/volumespackage volumes import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Attachment represents a Volume Attachment record type Attachment struct { AttachedAt time.Time `json:"-"` AttachmentID string `json:"attachment_id"` Device string `json:"device"` HostName string `json:"host_name"` ID string `json:"id"` ServerID string `json:"server_id"` VolumeID string `json:"volume_id"` } // UnmarshalJSON is our unmarshalling helper func (r *Attachment) UnmarshalJSON(b []byte) error { type tmp Attachment var s struct { tmp AttachedAt gophercloud.JSONRFC3339MilliNoZ `json:"attached_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Attachment(s.tmp) r.AttachedAt = time.Time(s.AttachedAt) return err } // Volume contains all the information associated with an OpenStack Volume. type Volume struct { // Unique identifier for the volume. ID string `json:"id"` // Current status of the volume. Status string `json:"status"` // Size of the volume in GB. Size int `json:"size"` // AvailabilityZone is which availability zone the volume is in. AvailabilityZone string `json:"availability_zone"` // The date when this volume was created. CreatedAt time.Time `json:"-"` // The date when this volume was last updated UpdatedAt time.Time `json:"-"` // Instances onto which the volume is attached. Attachments []Attachment `json:"attachments"` // Human-readable display name for the volume. Name string `json:"name"` // Human-readable description for the volume. Description string `json:"description"` // The type of volume to create, either SATA or SSD. VolumeType string `json:"volume_type"` // The ID of the snapshot from which the volume was created SnapshotID string `json:"snapshot_id"` // The ID of another block storage volume from which the current volume was created SourceVolID string `json:"source_volid"` // Arbitrary key-value pairs defined by the user. Metadata map[string]string `json:"metadata"` // UserID is the id of the user who created the volume. UserID string `json:"user_id"` // Indicates whether this is a bootable volume. Bootable string `json:"bootable"` // Encrypted denotes if the volume is encrypted. Encrypted bool `json:"encrypted"` // ReplicationStatus is the status of replication. ReplicationStatus string `json:"replication_status"` // ConsistencyGroupID is the consistency group ID. ConsistencyGroupID string `json:"consistencygroup_id"` // Multiattach denotes if the volume is multi-attach capable. Multiattach bool `json:"multiattach"` } // UnmarshalJSON another unmarshalling function func (r *Volume) UnmarshalJSON(b []byte) error { type tmp Volume var s struct { tmp CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Volume(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) r.UpdatedAt = time.Time(s.UpdatedAt) return err } // VolumePage is a pagination.pager that is returned from a call to the List function. type VolumePage struct { pagination.LinkedPageBase } // IsEmpty returns true if a ListResult contains no Volumes. func (r VolumePage) IsEmpty() (bool, error) { volumes, err := ExtractVolumes(r) return len(volumes) == 0, err } func (page VolumePage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"volumes_links"` } err := page.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call. func ExtractVolumes(r pagination.Page) ([]Volume, error) { var s []Volume err := ExtractVolumesInto(r, &s) return s, err } type commonResult struct { gophercloud.Result } // Extract will get the Volume object out of the commonResult object. func (r commonResult) Extract() (*Volume, error) { var s Volume err := r.ExtractInto(&s) return &s, err } // ExtractInto converts our response data into a volume struct func (r commonResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "volume") } // ExtractVolumesInto similar to ExtractInto but operates on a `list` of volumes func ExtractVolumesInto(r pagination.Page, v interface{}) error { return r.(VolumePage).Result.ExtractIntoSlicePtr(v, "volumes") } // CreateResult contains the response body and error from a Create request. type CreateResult struct { commonResult } // GetResult contains the response body and error from a Get request. type GetResult struct { commonResult } // UpdateResult contains the response body and error from an Update request. type UpdateResult struct { commonResult } // DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400336425ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/volumesdoc.go000066400000000000000000000000361335005366400347350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/volumes/testing// volumes_v3 package testing fixtures.go000066400000000000000000000142141335005366400360440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/volumes/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func MockListResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/detail", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, ` { "volumes": [ { "volume_type": "lvmdriver-1", "created_at": "2015-09-17T03:35:03.000000", "bootable": "false", "name": "vol-001", "os-vol-mig-status-attr:name_id": null, "consistencygroup_id": null, "source_volid": null, "os-volume-replication:driver_data": null, "multiattach": false, "snapshot_id": null, "replication_status": "disabled", "os-volume-replication:extended_status": null, "encrypted": false, "os-vol-host-attr:host": null, "availability_zone": "nova", "attachments": [{ "server_id": "83ec2e3b-4321-422b-8706-a84185f52a0a", "attachment_id": "05551600-a936-4d4a-ba42-79a037c1-c91a", "attached_at": "2016-08-06T14:48:20.000000", "host_name": "foobar", "volume_id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", "device": "/dev/vdc", "id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75" }], "id": "289da7f8-6440-407c-9fb4-7db01ec49164", "size": 75, "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459", "os-vol-mig-status-attr:migstat": null, "metadata": {"foo": "bar"}, "status": "available", "description": null }, { "volume_type": "lvmdriver-1", "created_at": "2015-09-17T03:32:29.000000", "bootable": "false", "name": "vol-002", "os-vol-mig-status-attr:name_id": null, "consistencygroup_id": null, "source_volid": null, "os-volume-replication:driver_data": null, "multiattach": false, "snapshot_id": null, "replication_status": "disabled", "os-volume-replication:extended_status": null, "encrypted": false, "os-vol-host-attr:host": null, "availability_zone": "nova", "attachments": [], "id": "96c3bda7-c82a-4f50-be73-ca7621794835", "size": 75, "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459", "os-vol-mig-status-attr:migstat": null, "metadata": {}, "status": "available", "description": null } ], "volumes_links": [ { "href": "%s/volumes/detail?marker=1", "rel": "next" }] } `, th.Server.URL) case "1": fmt.Fprintf(w, `{"volumes": []}`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) } func MockGetResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "volume": { "volume_type": "lvmdriver-1", "created_at": "2015-09-17T03:32:29.000000", "bootable": "false", "name": "vol-001", "os-vol-mig-status-attr:name_id": null, "consistencygroup_id": null, "source_volid": null, "os-volume-replication:driver_data": null, "multiattach": false, "snapshot_id": null, "replication_status": "disabled", "os-volume-replication:extended_status": null, "encrypted": false, "os-vol-host-attr:host": null, "availability_zone": "nova", "attachments": [{ "server_id": "83ec2e3b-4321-422b-8706-a84185f52a0a", "attachment_id": "05551600-a936-4d4a-ba42-79a037c1-c91a", "attached_at": "2016-08-06T14:48:20.000000", "host_name": "foobar", "volume_id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", "device": "/dev/vdc", "id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75" }], "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "size": 75, "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459", "os-vol-mig-status-attr:migstat": null, "metadata": {}, "status": "available", "description": null } } `) }) } func MockCreateResponse(t *testing.T) { th.Mux.HandleFunc("/volumes", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "volume": { "name": "vol-001", "size": 75 } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, ` { "volume": { "size": 75, "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "metadata": {}, "created_at": "2015-09-17T03:32:29.044216", "encrypted": false, "bootable": "false", "availability_zone": "nova", "attachments": [], "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", "status": "creating", "description": null, "volume_type": "lvmdriver-1", "name": "vol-001", "replication_status": "disabled", "consistencygroup_id": null, "source_volid": null, "snapshot_id": null, "multiattach": false } } `) }) } func MockDeleteResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusAccepted) }) } func MockUpdateResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "volume": { "name": "vol-002" } } `) }) } requests_test.go000066400000000000000000000166651335005366400371210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/volumes/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumetenants" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListWithExtensions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) count := 0 volumes.List(client.ServiceClient(), &volumes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := volumes.ExtractVolumes(page) if err != nil { t.Errorf("Failed to extract volumes: %v", err) return false, err } expected := []volumes.Volume{ { ID: "289da7f8-6440-407c-9fb4-7db01ec49164", Name: "vol-001", Attachments: []volumes.Attachment{{ ServerID: "83ec2e3b-4321-422b-8706-a84185f52a0a", AttachmentID: "05551600-a936-4d4a-ba42-79a037c1-c91a", AttachedAt: time.Date(2016, 8, 6, 14, 48, 20, 0, time.UTC), HostName: "foobar", VolumeID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", Device: "/dev/vdc", ID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", }}, AvailabilityZone: "nova", Bootable: "false", ConsistencyGroupID: "", CreatedAt: time.Date(2015, 9, 17, 3, 35, 3, 0, time.UTC), Description: "", Encrypted: false, Metadata: map[string]string{"foo": "bar"}, Multiattach: false, //TenantID: "304dc00909ac4d0da6c62d816bcb3459", //ReplicationDriverData: "", //ReplicationExtendedStatus: "", ReplicationStatus: "disabled", Size: 75, SnapshotID: "", SourceVolID: "", Status: "available", UserID: "ff1ce52c03ab433aaba9108c2e3ef541", VolumeType: "lvmdriver-1", }, { ID: "96c3bda7-c82a-4f50-be73-ca7621794835", Name: "vol-002", Attachments: []volumes.Attachment{}, AvailabilityZone: "nova", Bootable: "false", ConsistencyGroupID: "", CreatedAt: time.Date(2015, 9, 17, 3, 32, 29, 0, time.UTC), Description: "", Encrypted: false, Metadata: map[string]string{}, Multiattach: false, //TenantID: "304dc00909ac4d0da6c62d816bcb3459", //ReplicationDriverData: "", //ReplicationExtendedStatus: "", ReplicationStatus: "disabled", Size: 75, SnapshotID: "", SourceVolID: "", Status: "available", UserID: "ff1ce52c03ab433aaba9108c2e3ef541", VolumeType: "lvmdriver-1", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestListAllWithExtensions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) type VolumeWithExt struct { volumes.Volume volumetenants.VolumeTenantExt } allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() th.AssertNoErr(t, err) var actual []VolumeWithExt err = volumes.ExtractVolumesInto(allPages, &actual) th.AssertNoErr(t, err) th.AssertEquals(t, 2, len(actual)) th.AssertEquals(t, "304dc00909ac4d0da6c62d816bcb3459", actual[0].TenantID) } func TestListAll(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := volumes.ExtractVolumes(allPages) th.AssertNoErr(t, err) expected := []volumes.Volume{ { ID: "289da7f8-6440-407c-9fb4-7db01ec49164", Name: "vol-001", Attachments: []volumes.Attachment{{ ServerID: "83ec2e3b-4321-422b-8706-a84185f52a0a", AttachmentID: "05551600-a936-4d4a-ba42-79a037c1-c91a", AttachedAt: time.Date(2016, 8, 6, 14, 48, 20, 0, time.UTC), HostName: "foobar", VolumeID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", Device: "/dev/vdc", ID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", }}, AvailabilityZone: "nova", Bootable: "false", ConsistencyGroupID: "", CreatedAt: time.Date(2015, 9, 17, 3, 35, 3, 0, time.UTC), Description: "", Encrypted: false, Metadata: map[string]string{"foo": "bar"}, Multiattach: false, //TenantID: "304dc00909ac4d0da6c62d816bcb3459", //ReplicationDriverData: "", //ReplicationExtendedStatus: "", ReplicationStatus: "disabled", Size: 75, SnapshotID: "", SourceVolID: "", Status: "available", UserID: "ff1ce52c03ab433aaba9108c2e3ef541", VolumeType: "lvmdriver-1", }, { ID: "96c3bda7-c82a-4f50-be73-ca7621794835", Name: "vol-002", Attachments: []volumes.Attachment{}, AvailabilityZone: "nova", Bootable: "false", ConsistencyGroupID: "", CreatedAt: time.Date(2015, 9, 17, 3, 32, 29, 0, time.UTC), Description: "", Encrypted: false, Metadata: map[string]string{}, Multiattach: false, //TenantID: "304dc00909ac4d0da6c62d816bcb3459", //ReplicationDriverData: "", //ReplicationExtendedStatus: "", ReplicationStatus: "disabled", Size: 75, SnapshotID: "", SourceVolID: "", Status: "available", UserID: "ff1ce52c03ab433aaba9108c2e3ef541", VolumeType: "lvmdriver-1", }, } th.CheckDeepEquals(t, expected, actual) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetResponse(t) v, err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, v.Name, "vol-001") th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockCreateResponse(t) options := &volumes.CreateOpts{Size: 75, Name: "vol-001"} n, err := volumes.Create(client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Size, 75) th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockDeleteResponse(t) res := volumes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUpdateResponse(t) options := volumes.UpdateOpts{Name: "vol-002"} v, err := volumes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() th.AssertNoErr(t, err) th.CheckEquals(t, "vol-002", v.Name) } func TestGetWithExtensions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetResponse(t) var s struct { volumes.Volume volumetenants.VolumeTenantExt } err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "304dc00909ac4d0da6c62d816bcb3459", s.TenantID) err = volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(s) if err == nil { t.Errorf("Expected error when providing non-pointer struct") } } urls.go000066400000000000000000000010261335005366400335000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/volumespackage volumes import "github.com/gophercloud/gophercloud" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("volumes") } func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("volumes", "detail") } func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("volumes", id) } func getURL(c *gophercloud.ServiceClient, id string) string { return deleteURL(c, id) } func updateURL(c *gophercloud.ServiceClient, id string) string { return deleteURL(c, id) } util.go000066400000000000000000000010051335005366400334650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/volumespackage volumes import ( "github.com/gophercloud/gophercloud" ) // WaitForStatus will continually poll the resource, checking for a particular // status. It will do this for the amount of seconds defined. func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { current, err := Get(c, id).Extract() if err != nil { return false, err } if current.Status == status { return true, nil } return false, nil }) } volumetypes/000077500000000000000000000000001335005366400330675ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3doc.go000066400000000000000000000026451335005366400341720ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/volumetypes/* Package volumetypes provides information and interaction with volume types in the OpenStack Block Storage service. A volume type is a collection of specs used to define the volume capabilities. Example to list Volume Types allPages, err := volumetypes.List(client, volumetypes.ListOpts{}).AllPages() if err != nil{ panic(err) } volumeTypes, err := volumetypes.ExtractVolumeTypes(allPages) if err != nil{ panic(err) } for _,vt := range volumeTypes{ fmt.Println(vt) } Example to show a Volume Type typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" volumeType, err := volumetypes.Get(client, typeID).Extract() if err != nil{ panic(err) } fmt.Println(volumeType) Example to create a Volume Type volumeType, err := volumetypes.Create(client, volumetypes.CreateOpts{ Name:"volume_type_001", IsPublic:true, Description:"description_001", }).Extract() if err != nil{ panic(err) } fmt.Println(volumeType) Example to delete a Volume Type typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" err := volumetypes.Delete(client, typeID).ExtractErr() if err != nil{ panic(err) } Example to update a Volume Type typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" volumetype, err = volumetypes.Update(client, typeID, volumetypes.UpdateOpts{ Name: "volume_type_002", Description:"description_002", IsPublic:false, }).Extract() if err != nil{ panic(err) } fmt.Println(volumetype) */ package volumetypes requests.go000066400000000000000000000110621335005366400352710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/volumetypespackage volumetypes import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToVolumeTypeCreateMap() (map[string]interface{}, error) } // CreateOpts contains options for creating a Volume Type. This object is passed to // the volumetypes.Create function. For more information about these parameters, // see the Volume Type object. type CreateOpts struct { // The name of the volume type Name string `json:"name" required:"true"` // The volume type description Description string `json:"description,omitempty"` // the ID of the existing volume snapshot IsPublic *bool `json:"os-volume-type-access:is_public,omitempty"` // Extra spec key-value pairs defined by the user. ExtraSpecs map[string]string `json:"extra_specs"` } // ToVolumeTypeCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToVolumeTypeCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "volume_type") } // Create will create a new Volume Type based on the values in CreateOpts. To extract // the Volume Type object from the response, call the Extract method on the // CreateResult. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToVolumeTypeCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete will delete the existing Volume Type with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // Get retrieves the Volume Type with the provided ID. To extract the Volume Type object // from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { ToVolumeTypeListQuery() (string, error) } // ListOpts holds options for listing Volume Types. It is passed to the volumetypes.List // function. type ListOpts struct { // Comma-separated list of sort keys and optional sort directions in the // form of [:]. Sort string `q:"sort"` // Requests a page size of items. Limit int `q:"limit"` // Used in conjunction with limit to return a slice of items. Offset int `q:"offset"` // The ID of the last-seen item. Marker string `q:"marker"` } // ToVolumeTypeListQuery formats a ListOpts into a query string. func (opts ListOpts) ToVolumeTypeListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns Volume types. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToVolumeTypeListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return VolumeTypePage{pagination.LinkedPageBase{PageResult: r}} }) } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToVolumeTypeUpdateMap() (map[string]interface{}, error) } // UpdateOpts contain options for updating an existing Volume Type. This object is passed // to the volumetypes.Update function. For more information about the parameters, see // the Volume Type object. type UpdateOpts struct { Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` IsPublic *bool `json:"is_public,omitempty"` } // ToVolumeUpdateMap assembles a request body based on the contents of an // UpdateOpts. func (opts UpdateOpts) ToVolumeTypeUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "volume_type") } // Update will update the Volume Type with provided information. To extract the updated // Volume Type from the response, call the Extract method on the UpdateResult. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToVolumeTypeUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000053551335005366400351270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/volumetypespackage volumetypes import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Volume Type contains all the information associated with an OpenStack Volume Type. type VolumeType struct { // Unique identifier for the volume type. ID string `json:"id"` // Human-readable display name for the volume type. Name string `json:"name"` // Human-readable description for the volume type. Description string `json:"description"` // Arbitrary key-value pairs defined by the user. ExtraSpecs map[string]string `json:"extra_specs"` // Whether the volume type is publicly visible. IsPublic bool `json:"is_public"` // Qos Spec ID QosSpecID string `json:"qos_specs_id"` // Volume Type access public attribute PublicAccess bool `json:"os-volume-type-access:is_public"` } // VolumeTypePage is a pagination.pager that is returned from a call to the List function. type VolumeTypePage struct { pagination.LinkedPageBase } // IsEmpty returns true if a ListResult contains no Volume Types. func (r VolumeTypePage) IsEmpty() (bool, error) { volumetypes, err := ExtractVolumeTypes(r) return len(volumetypes) == 0, err } func (page VolumeTypePage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"volume_type_links"` } err := page.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // ExtractVolumeTypes extracts and returns Volumes. It is used while iterating over a volumetypes.List call. func ExtractVolumeTypes(r pagination.Page) ([]VolumeType, error) { var s []VolumeType err := ExtractVolumeTypesInto(r, &s) return s, err } type commonResult struct { gophercloud.Result } // Extract will get the Volume Type object out of the commonResult object. func (r commonResult) Extract() (*VolumeType, error) { var s VolumeType err := r.ExtractInto(&s) return &s, err } // ExtractInto converts our response data into a volume type struct func (r commonResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "volume_type") } // ExtractVolumesInto similar to ExtractInto but operates on a `list` of volume types func ExtractVolumeTypesInto(r pagination.Page, v interface{}) error { return r.(VolumeTypePage).Result.ExtractIntoSlicePtr(v, "volume_types") } // GetResult contains the response body and error from a Get request. type GetResult struct { commonResult } // CreateResult contains the response body and error from a Create request. type CreateResult struct { commonResult } // DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { gophercloud.ErrResult } // UpdateResult contains the response body and error from an Update request. type UpdateResult struct { commonResult } testing/000077500000000000000000000000001335005366400345445ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/volumetypesdoc.go000066400000000000000000000000401335005366400356320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/volumetypes/testing// volume_types package testing fixtures.go000066400000000000000000000077001335005366400367500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/volumetypes/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func MockListResponse(t *testing.T) { th.Mux.HandleFunc("/types", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, ` { "volume_types": [ { "name": "SSD", "qos_specs_id": null, "os-volume-type-access:is_public": true, "extra_specs": { "volume_backend_name": "lvmdriver-1" }, "is_public": true, "id": "6685584b-1eac-4da6-b5c3-555430cf68ff", "description": null }, { "name": "SATA", "qos_specs_id": null, "os-volume-type-access:is_public": true, "extra_specs": { "volume_backend_name": "lvmdriver-1" }, "is_public": true, "id": "8eb69a46-df97-4e41-9586-9a40a7533803", "description": null } ], "volume_type_links": [ { "href": "%s/types?marker=1", "rel": "next" } ] } `, th.Server.URL) case "1": fmt.Fprintf(w, `{"volume_types": []}`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) } func MockGetResponse(t *testing.T) { th.Mux.HandleFunc("/types/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "volume_type": { "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "name": "vol-type-001", "os-volume-type-access:is_public": true, "qos_specs_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "description": "volume type 001", "is_public": true, "extra_specs": { "capabilities": "gpu" } } } `) }) } func MockCreateResponse(t *testing.T) { th.Mux.HandleFunc("/types", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "volume_type": { "name": "test_type", "os-volume-type-access:is_public": true, "description": "test_type_desc", "extra_specs": { "capabilities": "gpu" } } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "volume_type": { "name": "test_type", "extra_specs": {}, "is_public": true, "os-volume-type-access:is_public": true, "id": "6d0ff92a-0007-4780-9ece-acfe5876966a", "description": "test_type_desc", "extra_specs": { "capabilities": "gpu" } } } `) }) } func MockDeleteResponse(t *testing.T) { th.Mux.HandleFunc("/types/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusAccepted) }) } func MockUpdateResponse(t *testing.T) { th.Mux.HandleFunc("/types/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "volume_type": { "name": "vol-type-002", "description": "volume type 0001", "is_public": true, "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22" } }`) }) } requests_test.go000066400000000000000000000061001335005366400400020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/volumetypes/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListAll(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) pages := 0 err := volumetypes.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := volumetypes.ExtractVolumeTypes(page) if err != nil { return false, err } expected := []volumetypes.VolumeType{ { ID: "6685584b-1eac-4da6-b5c3-555430cf68ff", Name: "SSD", ExtraSpecs: map[string]string{"volume_backend_name": "lvmdriver-1"}, IsPublic: true, Description: "", QosSpecID: "", PublicAccess: true, }, { ID: "8eb69a46-df97-4e41-9586-9a40a7533803", Name: "SATA", ExtraSpecs: map[string]string{"volume_backend_name": "lvmdriver-1"}, IsPublic: true, Description: "", QosSpecID: "", PublicAccess: true, }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, pages, 1) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetResponse(t) v, err := volumetypes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, v.Name, "vol-type-001") th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertEquals(t, v.ExtraSpecs["capabilities"], "gpu") th.AssertEquals(t, v.QosSpecID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertEquals(t, v.PublicAccess, true) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockCreateResponse(t) var isPublic = true options := &volumetypes.CreateOpts{ Name: "test_type", IsPublic: &isPublic, Description: "test_type_desc", ExtraSpecs: map[string]string{"capabilities": "gpu"}, } n, err := volumetypes.Create(client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, "test_type") th.AssertEquals(t, n.Description, "test_type_desc") th.AssertEquals(t, n.IsPublic, true) th.AssertEquals(t, n.PublicAccess, true) th.AssertEquals(t, n.ID, "6d0ff92a-0007-4780-9ece-acfe5876966a") th.AssertEquals(t, n.ExtraSpecs["capabilities"], "gpu") } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockDeleteResponse(t) res := volumetypes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUpdateResponse(t) var isPublic = true options := volumetypes.UpdateOpts{ Name: "vol-type-002", IsPublic: &isPublic, } v, err := volumetypes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() th.AssertNoErr(t, err) th.CheckEquals(t, "vol-type-002", v.Name) th.CheckEquals(t, true, v.IsPublic) } urls.go000066400000000000000000000010341335005366400344010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/blockstorage/v3/volumetypespackage volumetypes import "github.com/gophercloud/gophercloud" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("types") } func getURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("types", id) } func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("types") } func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("types", id) } func updateURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("types", id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/000077500000000000000000000000001335005366400263075ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/000077500000000000000000000000001335005366400266355ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/base/000077500000000000000000000000001335005366400275475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/base/doc.go000066400000000000000000000003251335005366400306430ustar00rootroot00000000000000// Package base provides information and interaction with the base API // resource in the OpenStack CDN service. This API resource allows for // retrieving the Home Document and pinging the root URL. package base golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/base/requests.go000066400000000000000000000007641335005366400317600ustar00rootroot00000000000000package base import "github.com/gophercloud/gophercloud" // Get retrieves the home document, allowing the user to discover the // entire API. func Get(c *gophercloud.ServiceClient) (r GetResult) { _, r.Err = c.Get(getURL(c), &r.Body, nil) return } // Ping retrieves a ping to the server. func Ping(c *gophercloud.ServiceClient) (r PingResult) { _, r.Err = c.Get(pingURL(c), nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, MoreHeaders: map[string]string{"Accept": ""}, }) return } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/base/results.go000066400000000000000000000011221335005366400315730ustar00rootroot00000000000000package base import "github.com/gophercloud/gophercloud" // HomeDocument is a resource that contains all the resources for the CDN API. type HomeDocument map[string]interface{} // GetResult represents the result of a Get operation. type GetResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a home document resource. func (r GetResult) Extract() (*HomeDocument, error) { var s HomeDocument err := r.ExtractInto(&s) return &s, err } // PingResult represents the result of a Ping operation. type PingResult struct { gophercloud.ErrResult } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/base/testing/000077500000000000000000000000001335005366400312245ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/base/testing/doc.go000066400000000000000000000000371335005366400323200ustar00rootroot00000000000000// cdn_base_v1 package testing fixtures.go000066400000000000000000000027211335005366400333470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/base/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) // HandleGetSuccessfully creates an HTTP handler at `/` on the test handler mux // that responds with a `Get` response. func HandleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "resources": { "rel/cdn": { "href-template": "services{?marker,limit}", "href-vars": { "marker": "param/marker", "limit": "param/limit" }, "hints": { "allow": [ "GET" ], "formats": { "application/json": {} } } } } } `) }) } // HandlePingSuccessfully creates an HTTP handler at `/ping` on the test handler // mux that responds with a `Ping` response. func HandlePingSuccessfully(t *testing.T) { th.Mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000020761335005366400344130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/base/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/cdn/v1/base" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestGetHomeDocument(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t) actual, err := base.Get(fake.ServiceClient()).Extract() th.CheckNoErr(t, err) expected := base.HomeDocument{ "resources": map[string]interface{}{ "rel/cdn": map[string]interface{}{ "href-template": "services{?marker,limit}", "href-vars": map[string]interface{}{ "marker": "param/marker", "limit": "param/limit", }, "hints": map[string]interface{}{ "allow": []interface{}{"GET"}, "formats": map[string]interface{}{ "application/json": map[string]interface{}{}, }, }, }, }, } th.CheckDeepEquals(t, expected, *actual) } func TestPing(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePingSuccessfully(t) err := base.Ping(fake.ServiceClient()).ExtractErr() th.CheckNoErr(t, err) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/base/urls.go000066400000000000000000000003331335005366400310620ustar00rootroot00000000000000package base import "github.com/gophercloud/gophercloud" func getURL(c *gophercloud.ServiceClient) string { return c.ServiceURL() } func pingURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("ping") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/flavors/000077500000000000000000000000001335005366400303115ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/flavors/doc.go000066400000000000000000000004261335005366400314070ustar00rootroot00000000000000// Package flavors provides information and interaction with the flavors API // resource in the OpenStack CDN service. This API resource allows for // listing flavors and retrieving a specific flavor. // // A flavor is a mapping configuration to a CDN provider. package flavors golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/flavors/requests.go000066400000000000000000000010471335005366400325150ustar00rootroot00000000000000package flavors import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List returns a single page of CDN flavors. func List(c *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(c, listURL(c), func(r pagination.PageResult) pagination.Page { return FlavorPage{pagination.SinglePageBase(r)} }) } // Get retrieves a specific flavor based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(getURL(c, id), &r.Body, nil) return } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/flavors/results.go000066400000000000000000000034461335005366400323500ustar00rootroot00000000000000package flavors import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Provider represents a provider for a particular flavor. type Provider struct { // Specifies the name of the provider. The name must not exceed 64 bytes in // length and is limited to unicode, digits, underscores, and hyphens. Provider string `json:"provider"` // Specifies a list with an href where rel is provider_url. Links []gophercloud.Link `json:"links"` } // Flavor represents a mapping configuration to a CDN provider. type Flavor struct { // Specifies the name of the flavor. The name must not exceed 64 bytes in // length and is limited to unicode, digits, underscores, and hyphens. ID string `json:"id"` // Specifies the list of providers mapped to this flavor. Providers []Provider `json:"providers"` // Specifies the self-navigating JSON document paths. Links []gophercloud.Link `json:"links"` } // FlavorPage is the page returned by a pager when traversing over a // collection of CDN flavors. type FlavorPage struct { pagination.SinglePageBase } // IsEmpty returns true if a FlavorPage contains no Flavors. func (r FlavorPage) IsEmpty() (bool, error) { flavors, err := ExtractFlavors(r) return len(flavors) == 0, err } // ExtractFlavors extracts and returns Flavors. It is used while iterating over // a flavors.List call. func ExtractFlavors(r pagination.Page) ([]Flavor, error) { var s struct { Flavors []Flavor `json:"flavors"` } err := (r.(FlavorPage)).ExtractInto(&s) return s.Flavors, err } // GetResult represents the result of a get operation. type GetResult struct { gophercloud.Result } // Extract is a function that extracts a flavor from a GetResult. func (r GetResult) Extract() (*Flavor, error) { var s *Flavor err := r.ExtractInto(&s) return s, err } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/flavors/testing/000077500000000000000000000000001335005366400317665ustar00rootroot00000000000000doc.go000066400000000000000000000000421335005366400327770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/flavors/testing// cdn_flavors_v1 package testing fixtures.go000066400000000000000000000044451335005366400341160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/flavors/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) // HandleListCDNFlavorsSuccessfully creates an HTTP handler at `/flavors` on the test handler mux // that responds with a `List` response. func HandleListCDNFlavorsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/flavors", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "flavors": [ { "id": "europe", "providers": [ { "provider": "Fastly", "links": [ { "href": "http://www.fastly.com", "rel": "provider_url" } ] } ], "links": [ { "href": "https://www.poppycdn.io/v1.0/flavors/europe", "rel": "self" } ] } ] } `) }) } // HandleGetCDNFlavorSuccessfully creates an HTTP handler at `/flavors/{id}` on the test handler mux // that responds with a `Get` response. func HandleGetCDNFlavorSuccessfully(t *testing.T) { th.Mux.HandleFunc("/flavors/asia", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "id" : "asia", "providers" : [ { "provider" : "ChinaCache", "links": [ { "href": "http://www.chinacache.com", "rel": "provider_url" } ] } ], "links": [ { "href": "https://www.poppycdn.io/v1.0/flavors/asia", "rel": "self" } ] } `) }) } requests_test.go000066400000000000000000000035251335005366400351550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/flavors/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/cdn/v1/flavors" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListCDNFlavorsSuccessfully(t) count := 0 err := flavors.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := flavors.ExtractFlavors(page) if err != nil { t.Errorf("Failed to extract flavors: %v", err) return false, err } expected := []flavors.Flavor{ { ID: "europe", Providers: []flavors.Provider{ { Provider: "Fastly", Links: []gophercloud.Link{ gophercloud.Link{ Href: "http://www.fastly.com", Rel: "provider_url", }, }, }, }, Links: []gophercloud.Link{ gophercloud.Link{ Href: "https://www.poppycdn.io/v1.0/flavors/europe", Rel: "self", }, }, }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, 1, count) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetCDNFlavorSuccessfully(t) expected := &flavors.Flavor{ ID: "asia", Providers: []flavors.Provider{ { Provider: "ChinaCache", Links: []gophercloud.Link{ gophercloud.Link{ Href: "http://www.chinacache.com", Rel: "provider_url", }, }, }, }, Links: []gophercloud.Link{ gophercloud.Link{ Href: "https://www.poppycdn.io/v1.0/flavors/asia", Rel: "self", }, }, } actual, err := flavors.Get(fake.ServiceClient(), "asia").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, actual) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/flavors/urls.go000066400000000000000000000003711335005366400316260ustar00rootroot00000000000000package flavors import "github.com/gophercloud/gophercloud" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("flavors") } func getURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("flavors", id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/serviceassets/000077500000000000000000000000001335005366400315205ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/serviceassets/doc.go000066400000000000000000000005611335005366400326160ustar00rootroot00000000000000// Package serviceassets provides information and interaction with the // serviceassets API resource in the OpenStack CDN service. This API resource // allows for deleting cached assets. // // A service distributes assets across the network. Service assets let you // interrogate properties about these assets and perform certain actions on them. package serviceassets requests.go000066400000000000000000000026551335005366400336530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/serviceassetspackage serviceassets import ( "strings" "github.com/gophercloud/gophercloud" ) // DeleteOptsBuilder allows extensions to add additional parameters to the Delete // request. type DeleteOptsBuilder interface { ToCDNAssetDeleteParams() (string, error) } // DeleteOpts is a structure that holds options for deleting CDN service assets. type DeleteOpts struct { // If all is set to true, specifies that the delete occurs against all of the // assets for the service. All bool `q:"all"` // Specifies the relative URL of the asset to be deleted. URL string `q:"url"` } // ToCDNAssetDeleteParams formats a DeleteOpts into a query string. func (opts DeleteOpts) ToCDNAssetDeleteParams() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // Delete accepts a unique service ID or URL and deletes the CDN service asset associated with // it. For example, both "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" and // "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" // are valid options for idOrURL. func Delete(c *gophercloud.ServiceClient, idOrURL string, opts DeleteOptsBuilder) (r DeleteResult) { var url string if strings.Contains(idOrURL, "/") { url = idOrURL } else { url = deleteURL(c, idOrURL) } if opts != nil { q, err := opts.ToCDNAssetDeleteParams() if err != nil { r.Err = err return } url += q } _, r.Err = c.Delete(url, nil) return } results.go000066400000000000000000000002651335005366400334740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/serviceassetspackage serviceassets import "github.com/gophercloud/gophercloud" // DeleteResult represents the result of a Delete operation. type DeleteResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400331165ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/serviceassetsdoc.go000066400000000000000000000000501335005366400342050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/serviceassets/testing// cdn_serviceassets_v1 package testing fixtures.go000066400000000000000000000011601335005366400353140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/serviceassets/testingpackage testing import ( "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) // HandleDeleteCDNAssetSuccessfully creates an HTTP handler at `/services/{id}/assets` on the test handler mux // that responds with a `Delete` response. func HandleDeleteCDNAssetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0/assets", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000007261335005366400363640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/serviceassets/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/cdn/v1/serviceassets" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteCDNAssetSuccessfully(t) err := serviceassets.Delete(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", nil).ExtractErr() th.AssertNoErr(t, err) } urls.go000066400000000000000000000002661335005366400327610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/serviceassetspackage serviceassets import "github.com/gophercloud/gophercloud" func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("services", id, "assets") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/services/000077500000000000000000000000001335005366400304605ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/services/doc.go000066400000000000000000000005061335005366400315550ustar00rootroot00000000000000// Package services provides information and interaction with the services API // resource in the OpenStack CDN service. This API resource allows for // listing, creating, updating, retrieving, and deleting services. // // A service represents an application that has its content cached to the edge // nodes. package services golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/services/errors.go000066400000000000000000000001741335005366400323250ustar00rootroot00000000000000package services import "fmt" func no(str string) error { return fmt.Errorf("Required parameter %s not provided", str) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/services/requests.go000066400000000000000000000223751335005366400326730ustar00rootroot00000000000000package services import ( "fmt" "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToCDNServiceListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Marker and Limit are used for pagination. type ListOpts struct { Marker string `q:"marker"` Limit int `q:"limit"` } // ToCDNServiceListQuery formats a ListOpts into a query string. func (opts ListOpts) ToCDNServiceListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // CDN services. It accepts a ListOpts struct, which allows for pagination via // marker and limit. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(c) if opts != nil { query, err := opts.ToCDNServiceListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { p := ServicePage{pagination.MarkerPageBase{PageResult: r}} p.MarkerPageBase.Owner = p return p }) } // CreateOptsBuilder is the interface options structs have to satisfy in order // to be used in the main Create operation in this package. Since many // extensions decorate or modify the common logic, it is useful for them to // satisfy a basic interface in order for them to be used. type CreateOptsBuilder interface { ToCDNServiceCreateMap() (map[string]interface{}, error) } // CreateOpts is the common options struct used in this package's Create // operation. type CreateOpts struct { // Specifies the name of the service. The minimum length for name is // 3. The maximum length is 256. Name string `json:"name" required:"true"` // Specifies a list of domains used by users to access their website. Domains []Domain `json:"domains" required:"true"` // Specifies a list of origin domains or IP addresses where the // original assets are stored. Origins []Origin `json:"origins" required:"true"` // Specifies the CDN provider flavor ID to use. For a list of // flavors, see the operation to list the available flavors. The minimum // length for flavor_id is 1. The maximum length is 256. FlavorID string `json:"flavor_id" required:"true"` // Specifies the TTL rules for the assets under this service. Supports wildcards for fine-grained control. Caching []CacheRule `json:"caching,omitempty"` // Specifies the restrictions that define who can access assets (content from the CDN cache). Restrictions []Restriction `json:"restrictions,omitempty"` } // ToCDNServiceCreateMap casts a CreateOpts struct to a map. func (opts CreateOpts) ToCDNServiceCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // Create accepts a CreateOpts struct and creates a new CDN service using the // values provided. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToCDNServiceCreateMap() if err != nil { r.Err = err return r } resp, err := c.Post(createURL(c), &b, nil, nil) r.Header = resp.Header r.Err = err return } // Get retrieves a specific service based on its URL or its unique ID. For // example, both "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" and // "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" // are valid options for idOrURL. func Get(c *gophercloud.ServiceClient, idOrURL string) (r GetResult) { var url string if strings.Contains(idOrURL, "/") { url = idOrURL } else { url = getURL(c, idOrURL) } _, r.Err = c.Get(url, &r.Body, nil) return } // Path is a JSON pointer location that indicates which service parameter is being added, replaced, // or removed. type Path struct { baseElement string } func (p Path) renderRoot() string { return "/" + p.baseElement } func (p Path) renderDash() string { return fmt.Sprintf("/%s/-", p.baseElement) } func (p Path) renderIndex(index int64) string { return fmt.Sprintf("/%s/%d", p.baseElement, index) } var ( // PathDomains indicates that an update operation is to be performed on a Domain. PathDomains = Path{baseElement: "domains"} // PathOrigins indicates that an update operation is to be performed on an Origin. PathOrigins = Path{baseElement: "origins"} // PathCaching indicates that an update operation is to be performed on a CacheRule. PathCaching = Path{baseElement: "caching"} ) type value interface { toPatchValue() interface{} appropriatePath() Path renderRootOr(func(p Path) string) string } // Patch represents a single update to an existing Service. Multiple updates to a service can be // submitted at the same time. type Patch interface { ToCDNServiceUpdateMap() map[string]interface{} } // Insertion is a Patch that requests the addition of a value (Domain, Origin, or CacheRule) to // a Service at a fixed index. Use an Append instead to append the new value to the end of its // collection. Pass it to the Update function as part of the Patch slice. type Insertion struct { Index int64 Value value } // ToCDNServiceUpdateMap converts an Insertion into a request body fragment suitable for the // Update call. func (opts Insertion) ToCDNServiceUpdateMap() map[string]interface{} { return map[string]interface{}{ "op": "add", "path": opts.Value.renderRootOr(func(p Path) string { return p.renderIndex(opts.Index) }), "value": opts.Value.toPatchValue(), } } // Append is a Patch that requests the addition of a value (Domain, Origin, or CacheRule) to a // Service at the end of its respective collection. Use an Insertion instead to insert the value // at a fixed index within the collection. Pass this to the Update function as part of its // Patch slice. type Append struct { Value value } // ToCDNServiceUpdateMap converts an Append into a request body fragment suitable for the // Update call. func (a Append) ToCDNServiceUpdateMap() map[string]interface{} { return map[string]interface{}{ "op": "add", "path": a.Value.renderRootOr(func(p Path) string { return p.renderDash() }), "value": a.Value.toPatchValue(), } } // Replacement is a Patch that alters a specific service parameter (Domain, Origin, or CacheRule) // in-place by index. Pass it to the Update function as part of the Patch slice. type Replacement struct { Value value Index int64 } // ToCDNServiceUpdateMap converts a Replacement into a request body fragment suitable for the // Update call. func (r Replacement) ToCDNServiceUpdateMap() map[string]interface{} { return map[string]interface{}{ "op": "replace", "path": r.Value.renderRootOr(func(p Path) string { return p.renderIndex(r.Index) }), "value": r.Value.toPatchValue(), } } // NameReplacement specifically updates the Service name. Pass it to the Update function as part // of the Patch slice. type NameReplacement struct { NewName string } // ToCDNServiceUpdateMap converts a NameReplacement into a request body fragment suitable for the // Update call. func (r NameReplacement) ToCDNServiceUpdateMap() map[string]interface{} { return map[string]interface{}{ "op": "replace", "path": "/name", "value": r.NewName, } } // Removal is a Patch that requests the removal of a service parameter (Domain, Origin, or // CacheRule) by index. Pass it to the Update function as part of the Patch slice. type Removal struct { Path Path Index int64 All bool } // ToCDNServiceUpdateMap converts a Removal into a request body fragment suitable for the // Update call. func (opts Removal) ToCDNServiceUpdateMap() map[string]interface{} { b := map[string]interface{}{"op": "remove"} if opts.All { b["path"] = opts.Path.renderRoot() } else { b["path"] = opts.Path.renderIndex(opts.Index) } return b } // UpdateOpts is a slice of Patches used to update a CDN service type UpdateOpts []Patch // Update accepts a slice of Patch operations (Insertion, Append, Replacement or Removal) and // updates an existing CDN service using the values provided. idOrURL can be either the service's // URL or its ID. For example, both "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" and // "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" // are valid options for idOrURL. func Update(c *gophercloud.ServiceClient, idOrURL string, opts UpdateOpts) (r UpdateResult) { var url string if strings.Contains(idOrURL, "/") { url = idOrURL } else { url = updateURL(c, idOrURL) } b := make([]map[string]interface{}, len(opts)) for i, patch := range opts { b[i] = patch.ToCDNServiceUpdateMap() } resp, err := c.Request("PATCH", url, &gophercloud.RequestOpts{ JSONBody: &b, OkCodes: []int{202}, }) r.Header = resp.Header r.Err = err return } // Delete accepts a service's ID or its URL and deletes the CDN service // associated with it. For example, both "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" and // "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" // are valid options for idOrURL. func Delete(c *gophercloud.ServiceClient, idOrURL string) (r DeleteResult) { var url string if strings.Contains(idOrURL, "/") { url = idOrURL } else { url = deleteURL(c, idOrURL) } _, r.Err = c.Delete(url, nil) return } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/services/results.go000066400000000000000000000223541335005366400325160ustar00rootroot00000000000000package services import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Domain represents a domain used by users to access their website. type Domain struct { // Specifies the domain used to access the assets on their website, for which // a CNAME is given to the CDN provider. Domain string `json:"domain" required:"true"` // Specifies the protocol used to access the assets on this domain. Only "http" // or "https" are currently allowed. The default is "http". Protocol string `json:"protocol,omitempty"` } func (d Domain) toPatchValue() interface{} { r := make(map[string]interface{}) r["domain"] = d.Domain if d.Protocol != "" { r["protocol"] = d.Protocol } return r } func (d Domain) appropriatePath() Path { return PathDomains } func (d Domain) renderRootOr(render func(p Path) string) string { return render(d.appropriatePath()) } // DomainList provides a useful way to perform bulk operations in a single Patch. type DomainList []Domain func (list DomainList) toPatchValue() interface{} { r := make([]interface{}, len(list)) for i, domain := range list { r[i] = domain.toPatchValue() } return r } func (list DomainList) appropriatePath() Path { return PathDomains } func (list DomainList) renderRootOr(_ func(p Path) string) string { return list.appropriatePath().renderRoot() } // OriginRule represents a rule that defines when an origin should be accessed. type OriginRule struct { // Specifies the name of this rule. Name string `json:"name" required:"true"` // Specifies the request URL this rule should match for this origin to be used. Regex is supported. RequestURL string `json:"request_url" required:"true"` } // Origin specifies a list of origin domains or IP addresses where the original assets are stored. type Origin struct { // Specifies the URL or IP address to pull origin content from. Origin string `json:"origin" required:"true"` // Specifies the port used to access the origin. The default is port 80. Port int `json:"port,omitempty"` // Specifies whether or not to use HTTPS to access the origin. The default // is false. SSL bool `json:"ssl"` // Specifies a collection of rules that define the conditions when this origin // should be accessed. If there is more than one origin, the rules parameter is required. Rules []OriginRule `json:"rules,omitempty"` } func (o Origin) toPatchValue() interface{} { r := make(map[string]interface{}) r["origin"] = o.Origin r["port"] = o.Port r["ssl"] = o.SSL if len(o.Rules) > 0 { r["rules"] = make([]map[string]interface{}, len(o.Rules)) for index, rule := range o.Rules { submap := r["rules"].([]map[string]interface{})[index] submap["name"] = rule.Name submap["request_url"] = rule.RequestURL } } return r } func (o Origin) appropriatePath() Path { return PathOrigins } func (o Origin) renderRootOr(render func(p Path) string) string { return render(o.appropriatePath()) } // OriginList provides a useful way to perform bulk operations in a single Patch. type OriginList []Origin func (list OriginList) toPatchValue() interface{} { r := make([]interface{}, len(list)) for i, origin := range list { r[i] = origin.toPatchValue() } return r } func (list OriginList) appropriatePath() Path { return PathOrigins } func (list OriginList) renderRootOr(_ func(p Path) string) string { return list.appropriatePath().renderRoot() } // TTLRule specifies a rule that determines if a TTL should be applied to an asset. type TTLRule struct { // Specifies the name of this rule. Name string `json:"name" required:"true"` // Specifies the request URL this rule should match for this TTL to be used. Regex is supported. RequestURL string `json:"request_url" required:"true"` } // CacheRule specifies the TTL rules for the assets under this service. type CacheRule struct { // Specifies the name of this caching rule. Note: 'default' is a reserved name used for the default TTL setting. Name string `json:"name" required:"true"` // Specifies the TTL to apply. TTL int `json:"ttl,omitempty"` // Specifies a collection of rules that determine if this TTL should be applied to an asset. Rules []TTLRule `json:"rules,omitempty"` } func (c CacheRule) toPatchValue() interface{} { r := make(map[string]interface{}) r["name"] = c.Name r["ttl"] = c.TTL r["rules"] = make([]map[string]interface{}, len(c.Rules)) for index, rule := range c.Rules { submap := r["rules"].([]map[string]interface{})[index] submap["name"] = rule.Name submap["request_url"] = rule.RequestURL } return r } func (c CacheRule) appropriatePath() Path { return PathCaching } func (c CacheRule) renderRootOr(render func(p Path) string) string { return render(c.appropriatePath()) } // CacheRuleList provides a useful way to perform bulk operations in a single Patch. type CacheRuleList []CacheRule func (list CacheRuleList) toPatchValue() interface{} { r := make([]interface{}, len(list)) for i, rule := range list { r[i] = rule.toPatchValue() } return r } func (list CacheRuleList) appropriatePath() Path { return PathCaching } func (list CacheRuleList) renderRootOr(_ func(p Path) string) string { return list.appropriatePath().renderRoot() } // RestrictionRule specifies a rule that determines if this restriction should be applied to an asset. type RestrictionRule struct { // Specifies the name of this rule. Name string `json:"name" required:"true"` // Specifies the http host that requests must come from. Referrer string `json:"referrer,omitempty"` } // Restriction specifies a restriction that defines who can access assets (content from the CDN cache). type Restriction struct { // Specifies the name of this restriction. Name string `json:"name" required:"true"` // Specifies a collection of rules that determine if this TTL should be applied to an asset. Rules []RestrictionRule `json:"rules,omitempty"` } // Error specifies an error that occurred during the previous service action. type Error struct { // Specifies an error message detailing why there is an error. Message string `json:"message"` } // Service represents a CDN service resource. type Service struct { // Specifies the service ID that represents distributed content. The value is // a UUID, such as 96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0, that is generated by the server. ID string `json:"id"` // Specifies the name of the service. Name string `json:"name"` // Specifies a list of domains used by users to access their website. Domains []Domain `json:"domains"` // Specifies a list of origin domains or IP addresses where the original assets are stored. Origins []Origin `json:"origins"` // Specifies the TTL rules for the assets under this service. Supports wildcards for fine grained control. Caching []CacheRule `json:"caching"` // Specifies the restrictions that define who can access assets (content from the CDN cache). Restrictions []Restriction `json:"restrictions"` // Specifies the CDN provider flavor ID to use. For a list of flavors, see the operation to list the available flavors. FlavorID string `json:"flavor_id"` // Specifies the current status of the service. Status string `json:"status"` // Specifies the list of errors that occurred during the previous service action. Errors []Error `json:"errors"` // Specifies the self-navigating JSON document paths. Links []gophercloud.Link `json:"links"` } // ServicePage is the page returned by a pager when traversing over a // collection of CDN services. type ServicePage struct { pagination.MarkerPageBase } // IsEmpty returns true if a ListResult contains no services. func (r ServicePage) IsEmpty() (bool, error) { services, err := ExtractServices(r) return len(services) == 0, err } // LastMarker returns the last service in a ListResult. func (r ServicePage) LastMarker() (string, error) { services, err := ExtractServices(r) if err != nil { return "", err } if len(services) == 0 { return "", nil } return (services[len(services)-1]).ID, nil } // ExtractServices is a function that takes a ListResult and returns the services' information. func ExtractServices(r pagination.Page) ([]Service, error) { var s struct { Services []Service `json:"services"` } err := (r.(ServicePage)).ExtractInto(&s) return s.Services, err } // CreateResult represents the result of a Create operation. type CreateResult struct { gophercloud.Result } // Extract is a method that extracts the location of a newly created service. func (r CreateResult) Extract() (string, error) { if r.Err != nil { return "", r.Err } if l, ok := r.Header["Location"]; ok && len(l) > 0 { return l[0], nil } return "", nil } // GetResult represents the result of a get operation. type GetResult struct { gophercloud.Result } // Extract is a function that extracts a service from a GetResult. func (r GetResult) Extract() (*Service, error) { var s Service err := r.ExtractInto(&s) return &s, err } // UpdateResult represents the result of a Update operation. type UpdateResult struct { gophercloud.Result } // Extract is a method that extracts the location of an updated service. func (r UpdateResult) Extract() (string, error) { if r.Err != nil { return "", r.Err } if l, ok := r.Header["Location"]; ok && len(l) > 0 { return l[0], nil } return "", nil } // DeleteResult represents the result of a Delete operation. type DeleteResult struct { gophercloud.ErrResult } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/services/testing/000077500000000000000000000000001335005366400321355ustar00rootroot00000000000000doc.go000066400000000000000000000000431335005366400331470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/services/testing// cdn_services_v1 package testing fixtures.go000066400000000000000000000257461335005366400342740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/services/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) // HandleListCDNServiceSuccessfully creates an HTTP handler at `/services` on the test handler mux // that responds with a `List` response. func HandleListCDNServiceSuccessfully(t *testing.T) { th.Mux.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, ` { "links": [ { "rel": "next", "href": "https://www.poppycdn.io/v1.0/services?marker=96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0&limit=20" } ], "services": [ { "id": "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", "name": "mywebsite.com", "domains": [ { "domain": "www.mywebsite.com" } ], "origins": [ { "origin": "mywebsite.com", "port": 80, "ssl": false } ], "caching": [ { "name": "default", "ttl": 3600 }, { "name": "home", "ttl": 17200, "rules": [ { "name": "index", "request_url": "/index.htm" } ] }, { "name": "images", "ttl": 12800, "rules": [ { "name": "images", "request_url": "*.png" } ] } ], "restrictions": [ { "name": "website only", "rules": [ { "name": "mywebsite.com", "referrer": "www.mywebsite.com" } ] } ], "flavor_id": "asia", "status": "deployed", "errors" : [], "links": [ { "href": "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", "rel": "self" }, { "href": "mywebsite.com.cdn123.poppycdn.net", "rel": "access_url" }, { "href": "https://www.poppycdn.io/v1.0/flavors/asia", "rel": "flavor" } ] }, { "id": "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1", "name": "myothersite.com", "domains": [ { "domain": "www.myothersite.com" } ], "origins": [ { "origin": "44.33.22.11", "port": 80, "ssl": false }, { "origin": "77.66.55.44", "port": 80, "ssl": false, "rules": [ { "name": "videos", "request_url": "^/videos/*.m3u" } ] } ], "caching": [ { "name": "default", "ttl": 3600 } ], "restrictions": [ {} ], "flavor_id": "europe", "status": "deployed", "links": [ { "href": "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1", "rel": "self" }, { "href": "myothersite.com.poppycdn.net", "rel": "access_url" }, { "href": "https://www.poppycdn.io/v1.0/flavors/europe", "rel": "flavor" } ] } ] } `) case "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1": fmt.Fprintf(w, `{ "services": [] }`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) } // HandleCreateCDNServiceSuccessfully creates an HTTP handler at `/services` on the test handler mux // that responds with a `Create` response. func HandleCreateCDNServiceSuccessfully(t *testing.T) { th.Mux.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, ` { "name": "mywebsite.com", "domains": [ { "domain": "www.mywebsite.com" }, { "domain": "blog.mywebsite.com" } ], "origins": [ { "origin": "mywebsite.com", "port": 80, "ssl": false } ], "restrictions": [ { "name": "website only", "rules": [ { "name": "mywebsite.com", "referrer": "www.mywebsite.com" } ] } ], "caching": [ { "name": "default", "ttl": 3600 } ], "flavor_id": "cdn" } `) w.Header().Add("Location", "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0") w.WriteHeader(http.StatusAccepted) }) } // HandleGetCDNServiceSuccessfully creates an HTTP handler at `/services/{id}` on the test handler mux // that responds with a `Get` response. func HandleGetCDNServiceSuccessfully(t *testing.T) { th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "id": "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", "name": "mywebsite.com", "domains": [ { "domain": "www.mywebsite.com", "protocol": "http" } ], "origins": [ { "origin": "mywebsite.com", "port": 80, "ssl": false } ], "caching": [ { "name": "default", "ttl": 3600 }, { "name": "home", "ttl": 17200, "rules": [ { "name": "index", "request_url": "/index.htm" } ] }, { "name": "images", "ttl": 12800, "rules": [ { "name": "images", "request_url": "*.png" } ] } ], "restrictions": [ { "name": "website only", "rules": [ { "name": "mywebsite.com", "referrer": "www.mywebsite.com" } ] } ], "flavor_id": "cdn", "status": "deployed", "errors" : [], "links": [ { "href": "https://global.cdn.api.rackspacecloud.com/v1.0/110011/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", "rel": "self" }, { "href": "blog.mywebsite.com.cdn1.raxcdn.com", "rel": "access_url" }, { "href": "https://global.cdn.api.rackspacecloud.com/v1.0/110011/flavors/cdn", "rel": "flavor" } ] } `) }) } // HandleUpdateCDNServiceSuccessfully creates an HTTP handler at `/services/{id}` on the test handler mux // that responds with a `Update` response. func HandleUpdateCDNServiceSuccessfully(t *testing.T) { th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, ` [ { "op": "add", "path": "/domains/-", "value": {"domain": "appended.mocksite4.com"} }, { "op": "add", "path": "/domains/4", "value": {"domain": "inserted.mocksite4.com"} }, { "op": "add", "path": "/domains", "value": [ {"domain": "bulkadded1.mocksite4.com"}, {"domain": "bulkadded2.mocksite4.com"} ] }, { "op": "replace", "path": "/origins/2", "value": {"origin": "44.33.22.11", "port": 80, "ssl": false} }, { "op": "replace", "path": "/origins", "value": [ {"origin": "44.33.22.11", "port": 80, "ssl": false}, {"origin": "55.44.33.22", "port": 443, "ssl": true} ] }, { "op": "remove", "path": "/caching/8" }, { "op": "remove", "path": "/caching" }, { "op": "replace", "path": "/name", "value": "differentServiceName" } ] `) w.Header().Add("Location", "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0") w.WriteHeader(http.StatusAccepted) }) } // HandleDeleteCDNServiceSuccessfully creates an HTTP handler at `/services/{id}` on the test handler mux // that responds with a `Delete` response. func HandleDeleteCDNServiceSuccessfully(t *testing.T) { th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000171311335005366400353220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/services/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/cdn/v1/services" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListCDNServiceSuccessfully(t) count := 0 err := services.List(fake.ServiceClient(), &services.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := services.ExtractServices(page) if err != nil { t.Errorf("Failed to extract services: %v", err) return false, err } expected := []services.Service{ { ID: "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", Name: "mywebsite.com", Domains: []services.Domain{ { Domain: "www.mywebsite.com", }, }, Origins: []services.Origin{ { Origin: "mywebsite.com", Port: 80, SSL: false, }, }, Caching: []services.CacheRule{ { Name: "default", TTL: 3600, }, { Name: "home", TTL: 17200, Rules: []services.TTLRule{ { Name: "index", RequestURL: "/index.htm", }, }, }, { Name: "images", TTL: 12800, Rules: []services.TTLRule{ { Name: "images", RequestURL: "*.png", }, }, }, }, Restrictions: []services.Restriction{ { Name: "website only", Rules: []services.RestrictionRule{ { Name: "mywebsite.com", Referrer: "www.mywebsite.com", }, }, }, }, FlavorID: "asia", Status: "deployed", Errors: []services.Error{}, Links: []gophercloud.Link{ { Href: "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", Rel: "self", }, { Href: "mywebsite.com.cdn123.poppycdn.net", Rel: "access_url", }, { Href: "https://www.poppycdn.io/v1.0/flavors/asia", Rel: "flavor", }, }, }, { ID: "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1", Name: "myothersite.com", Domains: []services.Domain{ { Domain: "www.myothersite.com", }, }, Origins: []services.Origin{ { Origin: "44.33.22.11", Port: 80, SSL: false, }, { Origin: "77.66.55.44", Port: 80, SSL: false, Rules: []services.OriginRule{ { Name: "videos", RequestURL: "^/videos/*.m3u", }, }, }, }, Caching: []services.CacheRule{ { Name: "default", TTL: 3600, }, }, Restrictions: []services.Restriction{}, FlavorID: "europe", Status: "deployed", Links: []gophercloud.Link{ gophercloud.Link{ Href: "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1", Rel: "self", }, gophercloud.Link{ Href: "myothersite.com.poppycdn.net", Rel: "access_url", }, gophercloud.Link{ Href: "https://www.poppycdn.io/v1.0/flavors/europe", Rel: "flavor", }, }, }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateCDNServiceSuccessfully(t) createOpts := services.CreateOpts{ Name: "mywebsite.com", Domains: []services.Domain{ { Domain: "www.mywebsite.com", }, { Domain: "blog.mywebsite.com", }, }, Origins: []services.Origin{ { Origin: "mywebsite.com", Port: 80, SSL: false, }, }, Restrictions: []services.Restriction{ { Name: "website only", Rules: []services.RestrictionRule{ { Name: "mywebsite.com", Referrer: "www.mywebsite.com", }, }, }, }, Caching: []services.CacheRule{ { Name: "default", TTL: 3600, }, }, FlavorID: "cdn", } expected := "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" actual, err := services.Create(fake.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, expected, actual) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetCDNServiceSuccessfully(t) expected := &services.Service{ ID: "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", Name: "mywebsite.com", Domains: []services.Domain{ { Domain: "www.mywebsite.com", Protocol: "http", }, }, Origins: []services.Origin{ { Origin: "mywebsite.com", Port: 80, SSL: false, }, }, Caching: []services.CacheRule{ { Name: "default", TTL: 3600, }, { Name: "home", TTL: 17200, Rules: []services.TTLRule{ { Name: "index", RequestURL: "/index.htm", }, }, }, { Name: "images", TTL: 12800, Rules: []services.TTLRule{ { Name: "images", RequestURL: "*.png", }, }, }, }, Restrictions: []services.Restriction{ { Name: "website only", Rules: []services.RestrictionRule{ { Name: "mywebsite.com", Referrer: "www.mywebsite.com", }, }, }, }, FlavorID: "cdn", Status: "deployed", Errors: []services.Error{}, Links: []gophercloud.Link{ { Href: "https://global.cdn.api.rackspacecloud.com/v1.0/110011/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", Rel: "self", }, { Href: "blog.mywebsite.com.cdn1.raxcdn.com", Rel: "access_url", }, { Href: "https://global.cdn.api.rackspacecloud.com/v1.0/110011/flavors/cdn", Rel: "flavor", }, }, } actual, err := services.Get(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, actual) } func TestSuccessfulUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateCDNServiceSuccessfully(t) expected := "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" ops := services.UpdateOpts{ // Append a single Domain services.Append{Value: services.Domain{Domain: "appended.mocksite4.com"}}, // Insert a single Domain services.Insertion{ Index: 4, Value: services.Domain{Domain: "inserted.mocksite4.com"}, }, // Bulk addition services.Append{ Value: services.DomainList{ {Domain: "bulkadded1.mocksite4.com"}, {Domain: "bulkadded2.mocksite4.com"}, }, }, // Replace a single Origin services.Replacement{ Index: 2, Value: services.Origin{Origin: "44.33.22.11", Port: 80, SSL: false}, }, // Bulk replace Origins services.Replacement{ Index: 0, // Ignored Value: services.OriginList{ {Origin: "44.33.22.11", Port: 80, SSL: false}, {Origin: "55.44.33.22", Port: 443, SSL: true}, }, }, // Remove a single CacheRule services.Removal{ Index: 8, Path: services.PathCaching, }, // Bulk removal services.Removal{ All: true, Path: services.PathCaching, }, // Service name replacement services.NameReplacement{ NewName: "differentServiceName", }, } actual, err := services.Update(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", ops).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, expected, actual) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteCDNServiceSuccessfully(t) err := services.Delete(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0").ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/cdn/v1/services/urls.go000066400000000000000000000007741335005366400320040ustar00rootroot00000000000000package services import "github.com/gophercloud/gophercloud" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("services") } func createURL(c *gophercloud.ServiceClient) string { return listURL(c) } func getURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("services", id) } func updateURL(c *gophercloud.ServiceClient, id string) string { return getURL(c, id) } func deleteURL(c *gophercloud.ServiceClient, id string) string { return getURL(c, id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/client.go000066400000000000000000000336051335005366400273570ustar00rootroot00000000000000package openstack import ( "fmt" "reflect" "github.com/gophercloud/gophercloud" tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/openstack/utils" ) const ( // v2 represents Keystone v2. // It should never increase beyond 2.0. v2 = "v2.0" // v3 represents Keystone v3. // The version can be anything from v3 to v3.x. v3 = "v3" ) /* NewClient prepares an unauthenticated ProviderClient instance. Most users will probably prefer using the AuthenticatedClient function instead. This is useful if you wish to explicitly control the version of the identity service that's used for authentication explicitly, for example. A basic example of using this would be: ao, err := openstack.AuthOptionsFromEnv() provider, err := openstack.NewClient(ao.IdentityEndpoint) client, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{}) */ func NewClient(endpoint string) (*gophercloud.ProviderClient, error) { base, err := utils.BaseEndpoint(endpoint) if err != nil { return nil, err } endpoint = gophercloud.NormalizeURL(endpoint) base = gophercloud.NormalizeURL(base) p := new(gophercloud.ProviderClient) p.IdentityBase = base p.IdentityEndpoint = endpoint p.UseTokenLock() return p, nil } /* AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint specified by the options, acquires a token, and returns a Provider Client instance that's ready to operate. If the full path to a versioned identity endpoint was specified (example: http://example.com:5000/v3), that path will be used as the endpoint to query. If a versionless endpoint was specified (example: http://example.com:5000/), the endpoint will be queried to determine which versions of the identity service are available, then chooses the most recent or most supported version. Example: ao, err := openstack.AuthOptionsFromEnv() provider, err := openstack.AuthenticatedClient(ao) client, err := openstack.NewNetworkV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) */ func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) { client, err := NewClient(options.IdentityEndpoint) if err != nil { return nil, err } err = Authenticate(client, options) if err != nil { return nil, err } return client, nil } // Authenticate or re-authenticate against the most recent identity service // supported at the provided endpoint. func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error { versions := []*utils.Version{ {ID: v2, Priority: 20, Suffix: "/v2.0/"}, {ID: v3, Priority: 30, Suffix: "/v3/"}, } chosen, endpoint, err := utils.ChooseVersion(client, versions) if err != nil { return err } switch chosen.ID { case v2: return v2auth(client, endpoint, options, gophercloud.EndpointOpts{}) case v3: return v3auth(client, endpoint, &options, gophercloud.EndpointOpts{}) default: // The switch statement must be out of date from the versions list. return fmt.Errorf("Unrecognized identity version: %s", chosen.ID) } } // AuthenticateV2 explicitly authenticates against the identity v2 endpoint. func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { return v2auth(client, "", options, eo) } func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { v2Client, err := NewIdentityV2(client, eo) if err != nil { return err } if endpoint != "" { v2Client.Endpoint = endpoint } v2Opts := tokens2.AuthOptions{ IdentityEndpoint: options.IdentityEndpoint, Username: options.Username, Password: options.Password, TenantID: options.TenantID, TenantName: options.TenantName, AllowReauth: options.AllowReauth, TokenID: options.TokenID, } result := tokens2.Create(v2Client, v2Opts) token, err := result.ExtractToken() if err != nil { return err } catalog, err := result.ExtractServiceCatalog() if err != nil { return err } if options.AllowReauth { // here we're creating a throw-away client (tac). it's a copy of the user's provider client, but // with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`, // this should retry authentication only once tac := *client tac.ReauthFunc = nil tac.TokenID = "" tao := options tao.AllowReauth = false client.ReauthFunc = func() error { err := v2auth(&tac, endpoint, tao, eo) if err != nil { return err } client.TokenID = tac.TokenID return nil } } client.TokenID = token.ID client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) { return V2EndpointURL(catalog, opts) } return nil } // AuthenticateV3 explicitly authenticates against the identity v3 service. func AuthenticateV3(client *gophercloud.ProviderClient, options tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { return v3auth(client, "", options, eo) } func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { // Override the generated service endpoint with the one returned by the version endpoint. v3Client, err := NewIdentityV3(client, eo) if err != nil { return err } if endpoint != "" { v3Client.Endpoint = endpoint } result := tokens3.Create(v3Client, opts) token, err := result.ExtractToken() if err != nil { return err } catalog, err := result.ExtractServiceCatalog() if err != nil { return err } client.TokenID = token.ID if opts.CanReauth() { // here we're creating a throw-away client (tac). it's a copy of the user's provider client, but // with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`, // this should retry authentication only once tac := *client tac.ReauthFunc = nil tac.TokenID = "" var tao tokens3.AuthOptionsBuilder switch ot := opts.(type) { case *gophercloud.AuthOptions: o := *ot o.AllowReauth = false tao = &o case *tokens3.AuthOptions: o := *ot o.AllowReauth = false tao = &o default: tao = opts } client.ReauthFunc = func() error { err := v3auth(&tac, endpoint, tao, eo) if err != nil { return err } client.TokenID = tac.TokenID return nil } } client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) { return V3EndpointURL(catalog, opts) } return nil } // NewIdentityV2 creates a ServiceClient that may be used to interact with the // v2 identity service. func NewIdentityV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { endpoint := client.IdentityBase + "v2.0/" clientType := "identity" var err error if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) { eo.ApplyDefaults(clientType) endpoint, err = client.EndpointLocator(eo) if err != nil { return nil, err } } return &gophercloud.ServiceClient{ ProviderClient: client, Endpoint: endpoint, Type: clientType, }, nil } // NewIdentityV3 creates a ServiceClient that may be used to access the v3 // identity service. func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { endpoint := client.IdentityBase + "v3/" clientType := "identity" var err error if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) { eo.ApplyDefaults(clientType) endpoint, err = client.EndpointLocator(eo) if err != nil { return nil, err } } // Ensure endpoint still has a suffix of v3. // This is because EndpointLocator might have found a versionless // endpoint or the published endpoint is still /v2.0. In both // cases, we need to fix the endpoint to point to /v3. base, err := utils.BaseEndpoint(endpoint) if err != nil { return nil, err } base = gophercloud.NormalizeURL(base) endpoint = base + "v3/" return &gophercloud.ServiceClient{ ProviderClient: client, Endpoint: endpoint, Type: clientType, }, nil } func initClientOpts(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts, clientType string) (*gophercloud.ServiceClient, error) { sc := new(gophercloud.ServiceClient) eo.ApplyDefaults(clientType) url, err := client.EndpointLocator(eo) if err != nil { return sc, err } sc.ProviderClient = client sc.Endpoint = url sc.Type = clientType return sc, nil } // NewObjectStorageV1 creates a ServiceClient that may be used with the v1 // object storage package. func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "object-store") } // NewComputeV2 creates a ServiceClient that may be used with the v2 compute // package. func NewComputeV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "compute") } // NewNetworkV2 creates a ServiceClient that may be used with the v2 network // package. func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { sc, err := initClientOpts(client, eo, "network") sc.ResourceBase = sc.Endpoint + "v2.0/" return sc, err } // NewBlockStorageV1 creates a ServiceClient that may be used to access the v1 // block storage service. func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "volume") } // NewBlockStorageV2 creates a ServiceClient that may be used to access the v2 // block storage service. func NewBlockStorageV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "volumev2") } // NewBlockStorageV3 creates a ServiceClient that may be used to access the v3 block storage service. func NewBlockStorageV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "volumev3") } // NewSharedFileSystemV2 creates a ServiceClient that may be used to access the v2 shared file system service. func NewSharedFileSystemV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "sharev2") } // NewCDNV1 creates a ServiceClient that may be used to access the OpenStack v1 // CDN service. func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "cdn") } // NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 // orchestration service. func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "orchestration") } // NewDBV1 creates a ServiceClient that may be used to access the v1 DB service. func NewDBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "database") } // NewDNSV2 creates a ServiceClient that may be used to access the v2 DNS // service. func NewDNSV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { sc, err := initClientOpts(client, eo, "dns") sc.ResourceBase = sc.Endpoint + "v2/" return sc, err } // NewImageServiceV2 creates a ServiceClient that may be used to access the v2 // image service. func NewImageServiceV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { sc, err := initClientOpts(client, eo, "image") sc.ResourceBase = sc.Endpoint + "v2/" return sc, err } // NewLoadBalancerV2 creates a ServiceClient that may be used to access the v2 // load balancer service. func NewLoadBalancerV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { sc, err := initClientOpts(client, eo, "load-balancer") sc.ResourceBase = sc.Endpoint + "v2.0/" return sc, err } // NewClusteringV1 creates a ServiceClient that may be used with the v1 clustering // package. func NewClusteringV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "clustering") } // NewMessagingV2 creates a ServiceClient that may be used with the v2 messaging // service. func NewMessagingV2(client *gophercloud.ProviderClient, clientID string, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { sc, err := initClientOpts(client, eo, "messaging") sc.MoreHeaders = map[string]string{"Client-ID": clientID} return sc, err } // NewContainerV1 creates a ServiceClient that may be used with v1 container package func NewContainerV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "container") } // NewKeyManagerV1 creates a ServiceClient that may be used with the v1 key // manager service. func NewKeyManagerV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { sc, err := initClientOpts(client, eo, "key-manager") sc.ResourceBase = sc.Endpoint + "v1/" return sc, err } // NewContainerInfraV1 creates a ServiceClient that may be used with the v1 container infra management // package. func NewContainerInfraV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "container-infra") } // NewWorkflowV2 creates a ServiceClient that may be used with the v2 workflow management package. func NewWorkflowV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "workflowv2") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/000077500000000000000000000000001335005366400277225ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/000077500000000000000000000000001335005366400302505ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/actions/000077500000000000000000000000001335005366400317105ustar00rootroot00000000000000doc.go000066400000000000000000000012751335005366400327320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/actions/* Package actions provides listing and retrieving of senlin actions for the OpenStack Clustering Service. Example to List Actions opts := actions.ListOpts{ Limit: 5, } err = actions.List(serviceClient, opts).EachPage(func(page pagination.Page) (bool, error) { actionInfos, err := actions.ExtractActions(page) if err != nil { return false, err } for _, actionInfo := range actionInfos { fmt.Println("%+v\n", actionInfo) } return true, nil }) Example to Get an Action actionID := "edce3528-864f-41fb-8759-f4707925cc09" action, err := actions.Get(serviceClient, actionID).Extract() if err != nil { panic(err) } fmt.Printf("Action %+v: ", action) */ package actions requests.go000066400000000000000000000027571335005366400340460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/actionspackage actions import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToActionListQuery() (string, error) } // ListOpts represents options used to list actions. type ListOpts struct { Limit int `q:"limit"` Marker string `q:"marker"` Sort string `q:"sort"` GlobalProject *bool `q:"global_project"` Name string `q:"name"` Target string `q:"target"` Action string `q:"action"` Status string `q:"status"` } // ToClusterListQuery builds a query string from ListOpts. func (opts ListOpts) ToActionListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List instructs OpenStack to provide a list of actions. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToActionListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ActionPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves details of a single action. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) return } results.go000066400000000000000000000054521335005366400336670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/actionspackage actions import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Action represents a detailed Action. type Action struct { Action string `json:"action"` Cause string `json:"cause"` CreatedAt time.Time `json:"-"` Data map[string]interface{} `json:"data"` DependedBy []string `json:"depended_by"` DependsOn []string `json:"depends_on"` StartTime float64 `json:"start_time"` EndTime float64 `json:"end_time"` ID string `json:"id"` Inputs map[string]interface{} `json:"inputs"` Interval int `json:"interval"` Name string `json:"name"` Outputs map[string]interface{} `json:"outputs"` Owner string `json:"owner"` Project string `json:"project"` Status string `json:"status"` StatusReason string `json:"status_reason"` Target string `json:"target"` Timeout int `json:"timeout"` UpdatedAt time.Time `json:"-"` User string `json:"user"` } func (r *Action) UnmarshalJSON(b []byte) error { type tmp Action var s struct { tmp CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Action(s.tmp) if s.CreatedAt != "" { r.CreatedAt, err = time.Parse(time.RFC3339, s.CreatedAt) if err != nil { return err } } if s.UpdatedAt != "" { r.UpdatedAt, err = time.Parse(time.RFC3339, s.UpdatedAt) if err != nil { return err } } return nil } // commonResult is the response of a base result. type commonResult struct { gophercloud.Result } // Extract interprets any commonResult-based result as an Action. func (r commonResult) Extract() (*Action, error) { var s struct { Action *Action `json:"action"` } err := r.ExtractInto(&s) return s.Action, err } // GetResult is the response of a Get operations. Call its Extract method to // interpret it as an Action. type GetResult struct { commonResult } // ActionPage contains a single page of all actions from a List call. type ActionPage struct { pagination.LinkedPageBase } // IsEmpty determines if a ActionPage contains any results. func (r ActionPage) IsEmpty() (bool, error) { actions, err := ExtractActions(r) return len(actions) == 0, err } // ExtractActions returns a slice of Actions from the List operation. func ExtractActions(r pagination.Page) ([]Action, error) { var s struct { Actions []Action `json:"actions"` } err := (r.(ActionPage)).ExtractInto(&s) return s.Actions, err } testing/000077500000000000000000000000001335005366400333065ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/actionsdoc.go000066400000000000000000000000511335005366400343760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/actions/testing// clustering_actions_v1 package testing fixtures.go000066400000000000000000000116721335005366400355150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/actions/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/clustering/v1/actions" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const ListResponse = ` { "actions": [ { "action": "NODE_DELETE", "cause": "RPC Request", "created_at": "2015-11-04T05:21:41Z", "data": {}, "depended_by": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], "depends_on": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], "end_time": 1425550000.0, "id": "edce3528-864f-41fb-8759-f4707925cc09", "inputs": {}, "interval": -1, "name": "node_delete_f0de9b9c", "outputs": {}, "owner": null, "project": "f1fe61dcda2f4618a14c10dc7abc214d", "start_time": 1425550000.0, "status": "SUCCEEDED", "status_reason": "Action completed successfully.", "target": "f0de9b9c-6d48-4a46-af21-2ca8607777fe", "timeout": 3600, "updated_at": "2016-11-04T05:21:41Z", "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" }, { "action": "NODE_DELETE", "cause": "RPC Request", "created_at": null, "data": {}, "depended_by": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], "depends_on": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], "end_time": 1425550000.0, "id": "edce3528-864f-41fb-8759-f4707925cc09", "inputs": {}, "interval": -1, "name": "node_delete_f0de9b9c", "outputs": {}, "owner": null, "project": "f1fe61dcda2f4618a14c10dc7abc214d", "start_time": 1425550000.0, "status": "SUCCEEDED", "status_reason": "Action completed successfully.", "target": "f0de9b9c-6d48-4a46-af21-2ca8607777fe", "timeout": 3600, "updated_at": "", "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" } ] } ` const GetResponse = ` { "action": { "action": "NODE_DELETE", "cause": "RPC Request", "created_at": "2015-11-04T05:21:41Z", "data": {}, "depended_by": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], "depends_on": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], "end_time": 1425550000.0, "id": "edce3528-864f-41fb-8759-f4707925cc09", "inputs": {}, "interval": -1, "name": "node_delete_f0de9b9c", "outputs": {}, "owner": null, "project": "f1fe61dcda2f4618a14c10dc7abc214d", "start_time": 1425550000.0, "status": "SUCCEEDED", "status_reason": "Action completed successfully.", "target": "f0de9b9c-6d48-4a46-af21-2ca8607777fe", "timeout": 3600, "updated_at": "2016-11-04T05:21:41Z", "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" } } ` var ExpectedAction1 = actions.Action{ Action: "NODE_DELETE", Cause: "RPC Request", CreatedAt: time.Date(2015, 11, 4, 5, 21, 41, 0, time.UTC), Data: map[string]interface{}{}, DependedBy: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, DependsOn: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, EndTime: 1425550000.0, ID: "edce3528-864f-41fb-8759-f4707925cc09", Inputs: make(map[string]interface{}), Interval: -1, Name: "node_delete_f0de9b9c", Outputs: make(map[string]interface{}), Owner: "", Project: "f1fe61dcda2f4618a14c10dc7abc214d", StartTime: 1425550000.0, Status: "SUCCEEDED", StatusReason: "Action completed successfully.", Target: "f0de9b9c-6d48-4a46-af21-2ca8607777fe", Timeout: 3600, UpdatedAt: time.Date(2016, 11, 4, 5, 21, 41, 0, time.UTC), User: "8bcd2cdca7684c02afc9e4f2fc0f0c79", } var ExpectedAction2 = actions.Action{ Action: "NODE_DELETE", Cause: "RPC Request", CreatedAt: time.Time{}, Data: map[string]interface{}{}, DependedBy: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, DependsOn: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, EndTime: 1425550000.0, ID: "edce3528-864f-41fb-8759-f4707925cc09", Inputs: make(map[string]interface{}), Interval: -1, Name: "node_delete_f0de9b9c", Outputs: make(map[string]interface{}), Owner: "", Project: "f1fe61dcda2f4618a14c10dc7abc214d", StartTime: 1425550000.0, Status: "SUCCEEDED", StatusReason: "Action completed successfully.", Target: "f0de9b9c-6d48-4a46-af21-2ca8607777fe", Timeout: 3600, UpdatedAt: time.Time{}, User: "8bcd2cdca7684c02afc9e4f2fc0f0c79", } var ExpectedActions = []actions.Action{ExpectedAction1, ExpectedAction2} func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/actions", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListResponse) }) } func HandleGetSuccessfully(t *testing.T, id string) { th.Mux.HandleFunc("/v1/actions/"+id, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetResponse) }) } requests_test.go000066400000000000000000000020151335005366400365450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/actions/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/clustering/v1/actions" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListActions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t) pageCount := 0 err := actions.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { pageCount++ actual, err := actions.ExtractActions(page) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedActions, actual) return true, nil }) th.AssertNoErr(t, err) if pageCount != 1 { t.Errorf("Expected 1 page, got %d", pageCount) } } func TestGetAction(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t, ExpectedAction1.ID) actual, err := actions.Get(fake.ServiceClient(), ExpectedAction1.ID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedAction1, *actual) } urls.go000066400000000000000000000010071335005366400331430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/actionspackage actions import "github.com/gophercloud/gophercloud" var apiVersion = "v1" var apiName = "actions" func commonURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName) } func listURL(client *gophercloud.ServiceClient) string { return commonURL(client) } func idURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiVersion, apiName, id) } func getURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/clusters/000077500000000000000000000000001335005366400321145ustar00rootroot00000000000000doc.go000066400000000000000000000122651335005366400331370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/clusters/* Package clusters provides information and interaction with the clusters through the OpenStack Clustering service. Example to Create a Cluster createOpts := clusters.CreateOpts{ Name: "test-cluster", DesiredCapacity: 1, ProfileID: "b7b870ee-d3c5-4a93-b9d7-846c53b2c2da", } cluster, err := clusters.Create(serviceClient, createOpts).Extract() if err != nil { panic(err) } Example to Get a Cluster clusterName := "cluster123" cluster, err := clusters.Get(serviceClient, clusterName).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", cluster) Example to List Clusters listOpts := clusters.ListOpts{ Name: "testcluster", } allPages, err := clusters.ListDetail(serviceClient, listOpts).AllPages() if err != nil { panic(err) } allClusters, err := clusters.ExtractClusters(allPages) if err != nil { panic(err) } for _, cluster := range allClusters { fmt.Printf("%+v\n", cluster) } Example to Update a Cluster updateOpts := clusters.UpdateOpts{ Name: "testcluster", ProfileID: "b7b870ee-d3c5-4a93-b9d7-846c53b2c2da", } clusterID := "7d85f602-a948-4a30-afd4-e84f47471c15" cluster, err := clusters.Update(serviceClient, clusterName, opts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", cluster) Example to Delete a Cluster clusterID := "dc6d336e3fc4c0a951b5698cd1236ee" err := clusters.Delete(serviceClient, clusterID).ExtractErr() if err != nil { panic(err) } Example to Resize a Cluster number := 1 maxSize := 5 minSize := 1 minStep := 1 strict := true resizeOpts := clusters.ResizeOpts{ AdjustmentType: clusters.ChangeInCapacityAdjustment, Number: number, MaxSize: &maxSize, MinSize: &minSize, MinStep: &minStep, Strict: &strict, } actionID, err := clusters.Resize(client, clusterName, resizeOpts).Extract() if err != nil { t.Fatalf("Unable to resize cluster: %v", err) } fmt.Println("Resize actionID", actionID) Example to ScaleIn a Cluster count := 2 scaleInOpts := clusters.ScaleInOpts{ Count: &count, } clusterID: "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" action, err := clusters.ScaleIn(computeClient, clusterID, scaleInOpts).Extract() if err != nil { panic(err) } Example to ScaleOut a cluster scaleOutOpts := clusters.ScaleOutOpts{ Count: 2, } clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" actionID, err := clusters.ScaleOut(computeClient, clusterID, scaleOutOpts).Extract() if err != nil { panic(err) } Example to List Policies for a Cluster clusterID := "7d85f602-a948-4a30-afd4-e84f47471c15" allPages, err := clusters.ListPolicies(serviceClient, clusterID, nil).AllPages() if err != nil { panic(err) } allClusterPolicies, err := clusters.ExtractClusterPolicies(allPages) if err != nil { panic(err) } for _, clusterPolicy := range allClusterPolicies { fmt.Printf("%+v\n", clusterPolicy) } Example to Get a Cluster Policy clusterID := "7d85f602-a948-4a30-afd4-e84f47471c15" profileID := "714fe676-a08f-4196-b7af-61d52eeded15" clusterPolicy, err := clusterpolicies.Get(serviceCLient, clusterID, profileID).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", clusterPolicy) Example to Attach a Policy to a Cluster enabled := true attachPolicyOpts := clusters.AttachPolicyOpts{ PolicyID: "policy-123", Enabled: &enabled, } clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" actionID, err := clusters.AttachPolicy(serviceClient, clusterID, attachPolicyOpts).Extract() if err != nil { panic(err) } fmt.Println("Attach Policy actionID", actionID) Example to Detach a Policy to Cluster detachpolicyOpts := clusters.DetachPolicyOpts{ PolicyID: "policy-123", } clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" actionID, err := clusters.DetachPolicy(serviceClient, clusterID, detachpolicyOpts).Extract() if err != nil { panic(err) } fmt.Println("Update Policy actionID", actionID) Example to Update a Policy to a Cluster enabled := true updatePolicyOpts := clusters.UpdatePolicyOpts{ PolicyID: "policy-123", Enabled: &enabled, } clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" actionID, err := clusters.UpdatePolicy(serviceClient, clusterID, updatePolicyOpts).Extract() if err != nil { panic(err) } fmt.Println("Attach Policy actionID", actionID) Example to Recover a Cluster check := true checkCapacity := true recoverOpts := clusters.RecoverOpts{ Operation: clusters.RebuildRecovery, Check: &check, CheckCapacity: &checkCapacity, } clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" actionID, err := clusters.Recover(computeClient, clusterID, recoverOpts).Extract() if err != nil { panic(err) } fmt.Println("action=", actionID) Example to Check a Cluster clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" action, err := clusters.Check(computeClient, clusterID).Extract() if err != nil { panic(err) } Example to Complete Life Cycle clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" lifecycleOpts := clusters.CompleteLifecycleOpts{LifecycleActionTokenID: "2b827124-69e1-496e-9484-33ca769fe4df"} action, err := clusters.CompleteLifecycle(computeClient, clusterID, lifecycleOpts).Extract() if err != nil { panic(err) } */ package clusters requests.go000066400000000000000000000370521335005366400342460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/clusterspackage clusters import ( "fmt" "net/http" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // AdjustmentType represents valid values for resizing a cluster. type AdjustmentType string const ( ExactCapacityAdjustment AdjustmentType = "EXACT_CAPACITY" ChangeInCapacityAdjustment AdjustmentType = "CHANGE_IN_CAPACITY" ChangeInPercentageAdjustment AdjustmentType = "CHANGE_IN_PERCENTAGE" ) // RecoveryAction represents valid values for recovering a cluster. type RecoveryAction string const ( RebootRecovery RecoveryAction = "REBOOT" RebuildRecovery RecoveryAction = "REBUILD" RecreateRecovery RecoveryAction = "RECREATE" ) // CreateOptsBuilder allows extensions to add additional parameters // to the Create request. type CreateOptsBuilder interface { ToClusterCreateMap() (map[string]interface{}, error) } // CreateOpts represents options used to create a cluster. type CreateOpts struct { Name string `json:"name" required:"true"` DesiredCapacity int `json:"desired_capacity"` ProfileID string `json:"profile_id" required:"true"` MinSize *int `json:"min_size,omitempty"` Timeout int `json:"timeout,omitempty"` MaxSize int `json:"max_size,omitempty"` Metadata map[string]interface{} `json:"metadata,omitempty"` Config map[string]interface{} `json:"config,omitempty"` } // ToClusterCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToClusterCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "cluster") } // Create requests the creation of a new cluster. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToClusterCreateMap() if err != nil { r.Err = err return } var result *http.Response result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) if r.Err == nil { r.Header = result.Header } return } // Get retrieves details of a single cluster. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { var result *http.Response result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) if r.Err == nil { r.Header = result.Header } return } // ListOptsBuilder allows extensions to add additional parameters to // the List request. type ListOptsBuilder interface { ToClusterListQuery() (string, error) } // ListOpts represents options to list clusters. type ListOpts struct { Limit int `q:"limit"` Marker string `q:"marker"` Sort string `q:"sort"` GlobalProject *bool `q:"global_project"` Name string `q:"name,omitempty"` Status string `q:"status,omitempty"` } // ToClusterListQuery formats a ListOpts into a query string. func (opts ListOpts) ToClusterListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List instructs OpenStack to provide a list of clusters. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToClusterListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ClusterPage{pagination.LinkedPageBase{PageResult: r}} }) } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToClusterUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents options to update a cluster. type UpdateOpts struct { Config string `json:"config,omitempty"` Name string `json:"name,omitempty"` ProfileID string `json:"profile_id,omitempty"` Timeout *int `json:"timeout,omitempty"` Metadata map[string]interface{} `json:"metadata,omitempty"` ProfileOnly *bool `json:"profile_only,omitempty"` } // ToClusterUpdateMap assembles a request body based on the contents of // UpdateOpts. func (opts UpdateOpts) ToClusterUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "cluster") if err != nil { return nil, err } return b, nil } // Update will update an existing cluster. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToClusterUpdateMap() if err != nil { r.Err = err return r } var result *http.Response result, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) if r.Err == nil { r.Header = result.Header } return } // Delete deletes the specified cluster ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { var result *http.Response result, r.Err = client.Delete(deleteURL(client, id), nil) if r.Err == nil { r.Header = result.Header } return } // ResizeOptsBuilder allows extensions to add additional parameters to the // resize request. type ResizeOptsBuilder interface { ToClusterResizeMap() (map[string]interface{}, error) } // ResizeOpts represents options for resizing a cluster. type ResizeOpts struct { AdjustmentType AdjustmentType `json:"adjustment_type,omitempty"` Number interface{} `json:"number,omitempty"` MinSize *int `json:"min_size,omitempty"` MaxSize *int `json:"max_size,omitempty"` MinStep *int `json:"min_step,omitempty"` Strict *bool `json:"strict,omitempty"` } // ToClusterResizeMap constructs a request body from ResizeOpts. func (opts ResizeOpts) ToClusterResizeMap() (map[string]interface{}, error) { if opts.AdjustmentType != "" && opts.Number == nil { return nil, fmt.Errorf("Number field MUST NOT be empty when AdjustmentType field used") } switch opts.Number.(type) { case nil, int, int32, int64: // Valid type. Always allow case float32, float64: if opts.AdjustmentType != ChangeInPercentageAdjustment { return nil, fmt.Errorf("Only ChangeInPercentageAdjustment allows float value for Number field") } default: return nil, fmt.Errorf("Number field must be either int, float, or omitted") } return gophercloud.BuildRequestBody(opts, "resize") } func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ActionResult) { b, err := opts.ToClusterResizeMap() if err != nil { r.Err = err return } var result *http.Response result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) if r.Err == nil { r.Header = result.Header } return } // ScaleInOptsBuilder allows extensions to add additional parameters to the // ScaleIn request. type ScaleInOptsBuilder interface { ToClusterScaleInMap() (map[string]interface{}, error) } // ScaleInOpts represents options used to scale-in a cluster. type ScaleInOpts struct { Count *int `json:"count,omitempty"` } // ToClusterScaleInMap constructs a request body from ScaleInOpts. func (opts ScaleInOpts) ToClusterScaleInMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "scale_in") } // ScaleIn will reduce the capacity of a cluster. func ScaleIn(client *gophercloud.ServiceClient, id string, opts ScaleInOptsBuilder) (r ActionResult) { b, err := opts.ToClusterScaleInMap() if err != nil { r.Err = err return } var result *http.Response result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) if r.Err == nil { r.Header = result.Header } return } // ScaleOutOptsBuilder allows extensions to add additional parameters to the // ScaleOut request. type ScaleOutOptsBuilder interface { ToClusterScaleOutMap() (map[string]interface{}, error) } // ScaleOutOpts represents options used to scale-out a cluster. type ScaleOutOpts struct { Count int `json:"count,omitempty"` } // ToClusterScaleOutMap constructs a request body from ScaleOutOpts. func (opts ScaleOutOpts) ToClusterScaleOutMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "scale_out") } // ScaleOut will increase the capacity of a cluster. func ScaleOut(client *gophercloud.ServiceClient, id string, opts ScaleOutOptsBuilder) (r ActionResult) { b, err := opts.ToClusterScaleOutMap() if err != nil { r.Err = err return } var result *http.Response result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) if r.Err == nil { r.Header = result.Header } return } // AttachPolicyOptsBuilder allows extensions to add additional parameters to the // AttachPolicy request. type AttachPolicyOptsBuilder interface { ToClusterAttachPolicyMap() (map[string]interface{}, error) } // PolicyOpts params type AttachPolicyOpts struct { PolicyID string `json:"policy_id" required:"true"` Enabled *bool `json:"enabled,omitempty"` } // ToClusterAttachPolicyMap constructs a request body from AttachPolicyOpts. func (opts AttachPolicyOpts) ToClusterAttachPolicyMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "policy_attach") } // Attach Policy will attach a policy to a cluster. func AttachPolicy(client *gophercloud.ServiceClient, id string, opts AttachPolicyOptsBuilder) (r ActionResult) { b, err := opts.ToClusterAttachPolicyMap() if err != nil { r.Err = err return } var result *http.Response result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) if r.Err == nil { r.Header = result.Header } return } // UpdatePolicyOptsBuilder allows extensions to add additional parameters to the // UpdatePolicy request. type UpdatePolicyOptsBuilder interface { ToClusterUpdatePolicyMap() (map[string]interface{}, error) } // UpdatePolicyOpts represents options used to update a cluster policy. type UpdatePolicyOpts struct { PolicyID string `json:"policy_id" required:"true"` Enabled *bool `json:"enabled,omitempty" required:"true"` } // ToClusterUpdatePolicyMap constructs a request body from UpdatePolicyOpts. func (opts UpdatePolicyOpts) ToClusterUpdatePolicyMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "policy_update") } // UpdatePolicy will update a cluster's policy. func UpdatePolicy(client *gophercloud.ServiceClient, id string, opts UpdatePolicyOptsBuilder) (r ActionResult) { b, err := opts.ToClusterUpdatePolicyMap() if err != nil { r.Err = err return } var result *http.Response result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) if r.Err == nil { r.Header = result.Header } return } // DetachPolicyOptsBuilder allows extensions to add additional parameters to the // DetachPolicy request. type DetachPolicyOptsBuilder interface { ToClusterDetachPolicyMap() (map[string]interface{}, error) } // DetachPolicyOpts represents options used to detach a policy from a cluster. type DetachPolicyOpts struct { PolicyID string `json:"policy_id" required:"true"` } // ToClusterDetachPolicyMap constructs a request body from DetachPolicyOpts. func (opts DetachPolicyOpts) ToClusterDetachPolicyMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "policy_detach") } // DetachPolicy will detach a policy from a cluster. func DetachPolicy(client *gophercloud.ServiceClient, id string, opts DetachPolicyOptsBuilder) (r ActionResult) { b, err := opts.ToClusterDetachPolicyMap() if err != nil { r.Err = err return } var result *http.Response result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) if r.Err == nil { r.Header = result.Header } return } // ListPolicyOptsBuilder allows extensions to add additional parameters to the // ListPolicies request. type ListPoliciesOptsBuilder interface { ToClusterPoliciesListQuery() (string, error) } // ListPoliciesOpts represents options to list a cluster's policies. type ListPoliciesOpts struct { Enabled *bool `q:"enabled"` Name string `q:"policy_name"` Type string `q:"policy_type"` Sort string `q:"sort"` } // ToClusterPoliciesListQuery formats a ListOpts into a query string. func (opts ListPoliciesOpts) ToClusterPoliciesListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // ListPolicies instructs OpenStack to provide a list of policies for a cluster. func ListPolicies(client *gophercloud.ServiceClient, clusterID string, opts ListPoliciesOptsBuilder) pagination.Pager { url := listPoliciesURL(client, clusterID) if opts != nil { query, err := opts.ToClusterPoliciesListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ClusterPolicyPage{pagination.SinglePageBase(r)} }) } // GetPolicy retrieves details of a cluster policy. func GetPolicy(client *gophercloud.ServiceClient, clusterID string, policyID string) (r GetPolicyResult) { _, r.Err = client.Get(getPolicyURL(client, clusterID, policyID), &r.Body, nil) return } // RecoverOptsBuilder allows extensions to add additional parameters to the // Recover request. type RecoverOptsBuilder interface { ToClusterRecoverMap() (map[string]interface{}, error) } // RecoverOpts represents options used to recover a cluster. type RecoverOpts struct { Operation RecoveryAction `json:"operation,omitempty"` Check *bool `json:"check,omitempty"` CheckCapacity *bool `json:"check_capacity,omitempty"` } // ToClusterRecovermap constructs a request body from RecoverOpts. func (opts RecoverOpts) ToClusterRecoverMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "recover") } // Recover implements cluster recover request. func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOptsBuilder) (r ActionResult) { b, err := opts.ToClusterRecoverMap() if err != nil { r.Err = err return } var result *http.Response result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) if r.Err == nil { r.Header = result.Header } return } // Check will perform a health check on a cluster. func Check(client *gophercloud.ServiceClient, id string) (r ActionResult) { b := map[string]interface{}{ "check": map[string]interface{}{}, } var result *http.Response result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) if r.Err == nil { r.Header = result.Header } return } // ToClusterCompleteLifecycleMap constructs a request body from CompleteLifecycleOpts. func (opts CompleteLifecycleOpts) ToClusterCompleteLifecycleMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "complete_lifecycle") } type CompleteLifecycleOpts struct { LifecycleActionTokenID string `json:"lifecycle_action_token" required:"true"` } func CompleteLifecycle(client *gophercloud.ServiceClient, id string, opts CompleteLifecycleOpts) (r ActionResult) { b, err := opts.ToClusterCompleteLifecycleMap() if err != nil { r.Err = err return } var result *http.Response result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) if r.Err == nil { r.Header = result.Header } return } results.go000066400000000000000000000131441335005366400340700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/clusterspackage clusters import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Cluster represents an OpenStack Clustering cluster. type Cluster struct { Config map[string]interface{} `json:"config"` CreatedAt time.Time `json:"-"` Data map[string]interface{} `json:"data"` Dependents map[string]interface{} `json:"dependents"` DesiredCapacity int `json:"desired_capacity"` Domain string `json:"domain"` ID string `json:"id"` InitAt time.Time `json:"-"` MaxSize int `json:"max_size"` Metadata map[string]interface{} `json:"metadata"` MinSize int `json:"min_size"` Name string `json:"name"` Nodes []string `json:"nodes"` Policies []string `json:"policies"` ProfileID string `json:"profile_id"` ProfileName string `json:"profile_name"` Project string `json:"project"` Status string `json:"status"` StatusReason string `json:"status_reason"` Timeout int `json:"timeout"` UpdatedAt time.Time `json:"-"` User string `json:"user"` } func (r *Cluster) UnmarshalJSON(b []byte) error { type tmp Cluster var s struct { tmp CreatedAt string `json:"created_at"` InitAt string `json:"init_at"` UpdatedAt string `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Cluster(s.tmp) if s.CreatedAt != "" { r.CreatedAt, err = time.Parse(gophercloud.RFC3339Milli, s.CreatedAt) if err != nil { return err } } if s.InitAt != "" { r.InitAt, err = time.Parse(gophercloud.RFC3339Milli, s.InitAt) if err != nil { return err } } if s.UpdatedAt != "" { r.UpdatedAt, err = time.Parse(gophercloud.RFC3339Milli, s.UpdatedAt) if err != nil { return err } } return nil } // ClusterPolicy represents and OpenStack Clustering cluster policy. type ClusterPolicy struct { ClusterID string `json:"cluster_id"` ClusterName string `json:"cluster_name"` Enabled bool `json:"enabled"` ID string `json:"id"` PolicyID string `json:"policy_id"` PolicyName string `json:"policy_name"` PolicyType string `json:"policy_type"` } // Action represents an OpenStack Clustering action. type Action struct { Action string `json:"action"` } // commonResult is the response of a base result. type commonResult struct { gophercloud.Result } // Extract interprets any commonResult-based result as a Cluster. func (r commonResult) Extract() (*Cluster, error) { var s struct { Cluster *Cluster `json:"cluster"` } err := r.ExtractInto(&s) return s.Cluster, err } // CreateResult is the response of a Create operations. Call its Extract method // to interpret it as a Cluster. type CreateResult struct { commonResult } // GetResult is the response of a Get operations. Call its Extract method to // interpret it as a Cluster. type GetResult struct { commonResult } // UpdateResult is the response of a Update operations. Call its Extract method // to interpret it as a Cluster. type UpdateResult struct { commonResult } // GetPolicyResult is the response of a Get operations. Call its Extract method // to interpret it as a ClusterPolicy. type GetPolicyResult struct { gophercloud.Result } // Extract interprets a GetPolicyResult as a ClusterPolicy. func (r GetPolicyResult) Extract() (*ClusterPolicy, error) { var s struct { ClusterPolicy *ClusterPolicy `json:"cluster_policy"` } err := r.ExtractInto(&s) return s.ClusterPolicy, err } // DeleteResult is the result from a Delete operation. Call its ExtractErr // method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // ClusterPage contains a single page of all clusters from a List call. type ClusterPage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a page of Clusters contains any results. func (page ClusterPage) IsEmpty() (bool, error) { clusters, err := ExtractClusters(page) return len(clusters) == 0, err } // ClusterPolicyPage contains a single page of all policies from a List call type ClusterPolicyPage struct { pagination.SinglePageBase } // IsEmpty determines whether or not a page of ClusterPolicies contains any // results. func (page ClusterPolicyPage) IsEmpty() (bool, error) { clusterPolicies, err := ExtractClusterPolicies(page) return len(clusterPolicies) == 0, err } // ActionResult is the response of Senlin actions. Call its Extract method to // obtain the Action ID of the action. type ActionResult struct { gophercloud.Result } // Extract interprets any Action result as an Action. func (r ActionResult) Extract() (string, error) { var s struct { Action string `json:"action"` } err := r.ExtractInto(&s) return s.Action, err } // ExtractClusters returns a slice of Clusters from the List operation. func ExtractClusters(r pagination.Page) ([]Cluster, error) { var s struct { Clusters []Cluster `json:"clusters"` } err := (r.(ClusterPage)).ExtractInto(&s) return s.Clusters, err } // ExtractClusterPolicies returns a slice of ClusterPolicies from the // ListClusterPolicies operation. func ExtractClusterPolicies(r pagination.Page) ([]ClusterPolicy, error) { var s struct { ClusterPolicies []ClusterPolicy `json:"cluster_policies"` } err := (r.(ClusterPolicyPage)).ExtractInto(&s) return s.ClusterPolicies, err } testing/000077500000000000000000000000001335005366400335125ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/clustersdoc.go000066400000000000000000000000521335005366400346030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/clusters/testing// clustering_clusters_v1 package testing fixtures.go000066400000000000000000000446731335005366400357300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/clusters/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/clustering/v1/clusters" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const ClusterResponse = ` { "cluster": { "config": {}, "created_at": "2015-02-10T14:26:14Z", "data": {}, "dependents": {}, "desired_capacity": 3, "domain": null, "id": "7d85f602-a948-4a30-afd4-e84f47471c15", "init_at": "2015-02-10T15:26:14Z", "max_size": 20, "metadata": {}, "min_size": 1, "name": "cluster1", "nodes": [ "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", "da1e9c87-e584-4626-a120-022da5062dac" ], "policies": [], "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", "profile_name": "mystack", "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", "status": "ACTIVE", "status_reason": "Cluster scale-in succeeded", "timeout": 3600, "updated_at": "2015-02-10T16:26:14Z", "user": "5e5bf8027826429c96af157f68dc9072" } }` var ExpectedCluster = clusters.Cluster{ Config: map[string]interface{}{}, CreatedAt: time.Date(2015, 2, 10, 14, 26, 14, 0, time.UTC), Data: map[string]interface{}{}, Dependents: map[string]interface{}{}, DesiredCapacity: 3, Domain: "", ID: "7d85f602-a948-4a30-afd4-e84f47471c15", InitAt: time.Date(2015, 2, 10, 15, 26, 14, 0, time.UTC), MaxSize: 20, Metadata: map[string]interface{}{}, MinSize: 1, Name: "cluster1", Nodes: []string{ "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", "da1e9c87-e584-4626-a120-022da5062dac", }, Policies: []string{}, ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", ProfileName: "mystack", Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", Status: "ACTIVE", StatusReason: "Cluster scale-in succeeded", Timeout: 3600, UpdatedAt: time.Date(2015, 2, 10, 16, 26, 14, 0, time.UTC), User: "5e5bf8027826429c96af157f68dc9072", } const ClusterResponse_EmptyTime = ` { "cluster": { "config": {}, "created_at": null, "data": {}, "dependents": {}, "desired_capacity": 3, "domain": null, "id": "7d85f602-a948-4a30-afd4-e84f47471c15", "init_at": null, "max_size": 20, "metadata": {}, "min_size": 1, "name": "cluster1", "nodes": [ "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", "da1e9c87-e584-4626-a120-022da5062dac" ], "policies": [], "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", "profile_name": "mystack", "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", "status": "ACTIVE", "status_reason": "Cluster scale-in succeeded", "timeout": 3600, "updated_at": null, "user": "5e5bf8027826429c96af157f68dc9072" } }` var ExpectedCluster_EmptyTime = clusters.Cluster{ Config: map[string]interface{}{}, Data: map[string]interface{}{}, Dependents: map[string]interface{}{}, DesiredCapacity: 3, Domain: "", ID: "7d85f602-a948-4a30-afd4-e84f47471c15", MaxSize: 20, Metadata: map[string]interface{}{}, MinSize: 1, Name: "cluster1", Nodes: []string{ "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", "da1e9c87-e584-4626-a120-022da5062dac", }, Policies: []string{}, ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", ProfileName: "mystack", Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", Status: "ACTIVE", StatusReason: "Cluster scale-in succeeded", Timeout: 3600, User: "5e5bf8027826429c96af157f68dc9072", } const ClusterResponse_Metadata = ` { "cluster": { "config": {}, "created_at": "2015-02-10T14:26:14Z", "data": {}, "dependents": {}, "desired_capacity": 3, "domain": null, "id": "7d85f602-a948-4a30-afd4-e84f47471c15", "init_at": "2015-02-10T15:26:14Z", "max_size": 20, "metadata": { "test": { "nil_interface": null, "bool_value": false, "string_value": "test_string", "float_value": 123.3 }, "foo": "bar" }, "min_size": 1, "name": "cluster1", "nodes": [ "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", "da1e9c87-e584-4626-a120-022da5062dac" ], "policies": [], "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", "profile_name": "mystack", "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", "status": "ACTIVE", "status_reason": "Cluster scale-in succeeded", "timeout": 3600, "updated_at": "2015-02-10T16:26:14Z", "user": "5e5bf8027826429c96af157f68dc9072" } }` var ExpectedCluster_Metadata = clusters.Cluster{ Config: map[string]interface{}{}, CreatedAt: time.Date(2015, 2, 10, 14, 26, 14, 0, time.UTC), Data: map[string]interface{}{}, Dependents: map[string]interface{}{}, DesiredCapacity: 3, Domain: "", ID: "7d85f602-a948-4a30-afd4-e84f47471c15", InitAt: time.Date(2015, 2, 10, 15, 26, 14, 0, time.UTC), MaxSize: 20, MinSize: 1, Metadata: map[string]interface{}{ "foo": "bar", "test": map[string]interface{}{ "nil_interface": interface{}(nil), "float_value": float64(123.3), "string_value": "test_string", "bool_value": false, }, }, Name: "cluster1", Nodes: []string{ "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", "da1e9c87-e584-4626-a120-022da5062dac", }, Policies: []string{}, ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", ProfileName: "mystack", Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", Status: "ACTIVE", StatusReason: "Cluster scale-in succeeded", Timeout: 3600, UpdatedAt: time.Date(2015, 2, 10, 16, 26, 14, 0, time.UTC), User: "5e5bf8027826429c96af157f68dc9072", } const ListResponse = ` { "clusters": [ { "config": {}, "created_at": "2015-02-10T14:26:14Z", "data": {}, "dependents": {}, "desired_capacity": 3, "domain": null, "id": "7d85f602-a948-4a30-afd4-e84f47471c15", "init_at": "2015-02-10T15:26:14Z", "max_size": 20, "min_size": 1, "metadata": {}, "name": "cluster1", "nodes": [ "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", "da1e9c87-e584-4626-a120-022da5062dac" ], "policies": [], "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", "profile_name": "mystack", "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", "status": "ACTIVE", "status_reason": "Cluster scale-in succeeded", "timeout": 3600, "updated_at": "2015-02-10T16:26:14Z", "user": "5e5bf8027826429c96af157f68dc9072" }, { "config": {}, "created_at": null, "data": {}, "dependents": {}, "desired_capacity": 3, "domain": null, "id": "7d85f602-a948-4a30-afd4-e84f47471c15", "init_at": null, "max_size": 20, "metadata": {}, "min_size": 1, "name": "cluster1", "nodes": [ "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", "da1e9c87-e584-4626-a120-022da5062dac" ], "policies": [], "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", "profile_name": "mystack", "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", "status": "ACTIVE", "status_reason": "Cluster scale-in succeeded", "timeout": 3600, "updated_at": null, "user": "5e5bf8027826429c96af157f68dc9072" } ] }` var ExpectedClusters = []clusters.Cluster{ExpectedCluster, ExpectedCluster_EmptyTime} const UpdateResponse = ` { "cluster": { "config": {}, "created_at": "2015-02-10T14:26:14Z", "data": {}, "dependents": {}, "desired_capacity": 4, "domain": null, "id": "7d85f602-a948-4a30-afd4-e84f47471c15", "init_at": "2015-02-10T15:26:14Z", "max_size": -1, "metadata": {}, "min_size": 0, "name": "cluster1", "nodes": [ "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", "da1e9c87-e584-4626-a120-022da5062dac" ], "policies": [], "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", "profile_name": "profile1", "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", "status": "ACTIVE", "status_reason": "Cluster scale-in succeeded", "timeout": 3600, "updated_at": "2015-02-10T16:26:14Z", "user": "5e5bf8027826429c96af157f68dc9072" } }` const UpdateResponse_EmptyTime = ` { "cluster": { "config": {}, "created_at": null, "data": {}, "dependents": {}, "desired_capacity": 3, "domain": null, "id": "7d85f602-a948-4a30-afd4-e84f47471c15", "init_at": null, "max_size": 20, "metadata": {}, "min_size": 1, "name": "cluster1", "nodes": [ "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", "da1e9c87-e584-4626-a120-022da5062dac" ], "policies": [], "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", "profile_name": "mystack", "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", "status": "ACTIVE", "status_reason": "Cluster scale-in succeeded", "timeout": 3600, "updated_at": null, "user": "5e5bf8027826429c96af157f68dc9072" } }` const ActionResponse = ` { "action": "2a0ff107-e789-4660-a122-3816c43af703" }` const ExpectedActionID = "2a0ff107-e789-4660-a122-3816c43af703" const ListPoliciesResult = `{ "cluster_policies": [ { "cluster_id": "7d85f602-a948-4a30-afd4-e84f47471c15", "cluster_name": "cluster4", "enabled": true, "id": "06be3a1f-b238-4a96-a737-ceec5714087e", "policy_id": "714fe676-a08f-4196-b7af-61d52eeded15", "policy_name": "dp01", "policy_type": "senlin.policy.deletion-1.0" } ] }` var ExpectedClusterPolicy = clusters.ClusterPolicy{ ClusterID: "7d85f602-a948-4a30-afd4-e84f47471c15", ClusterName: "cluster4", Enabled: true, ID: "06be3a1f-b238-4a96-a737-ceec5714087e", PolicyID: "714fe676-a08f-4196-b7af-61d52eeded15", PolicyName: "dp01", PolicyType: "senlin.policy.deletion-1.0", } var ExpectedListPolicies = []clusters.ClusterPolicy{ExpectedClusterPolicy} const GetPolicyResponse = ` { "cluster_policy": { "cluster_id": "7d85f602-a948-4a30-afd4-e84f47471c15", "cluster_name": "cluster4", "enabled": true, "id": "06be3a1f-b238-4a96-a737-ceec5714087e", "policy_id": "714fe676-a08f-4196-b7af-61d52eeded15", "policy_name": "dp01", "policy_type": "senlin.policy.deletion-1.0" } }` func HandleCreateClusterSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.Header().Add("X-OpenStack-Request-ID", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") w.Header().Add("Location", "http://senlin.cloud.blizzard.net:8778/v1/actions/625628cd-f877-44be-bde0-fec79f84e13d") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ClusterResponse) }) } func HandleCreateClusterEmptyTimeSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ClusterResponse_EmptyTime) }) } func HandleCreateClusterMetadataSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ClusterResponse_Metadata) }) } func HandleGetClusterSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ClusterResponse) }) } func HandleGetClusterEmptyTimeSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ClusterResponse_EmptyTime) }) } func HandleListClusterSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ListResponse) }) } func HandleUpdateClusterSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+ExpectedCluster.ID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ClusterResponse) }) } func HandleUpdateClusterEmptyTimeSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+ExpectedCluster_EmptyTime.ID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, UpdateResponse_EmptyTime) }) } func HandleDeleteClusterSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/6dc6d336e3fc4c0a951b5698cd1236ee", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNoContent) }) } func HandleResizeSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ActionResponse) }) } func HandleScaleInSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ActionResponse) }) } func HandleScaleOutSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ActionResponse) }) } func HandleListPoliciesSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/policies", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ListPoliciesResult) }) } func HandleGetPolicySuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/policies/714fe676-a08f-4196-b7af-61d52eeded15", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, GetPolicyResponse) }) } func HandleRecoverSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ActionResponse) }) } func HandleAttachPolicySuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ActionResponse) }) } func HandleDetachPolicySuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ActionResponse) }) } func HandleUpdatePolicySuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ActionResponse) }) } func HandleCheckSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ActionResponse) }) } func HandleLifecycleSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.Header().Add("X-OpenStack-Request-ID", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") w.Header().Add("Location", "http://senlin.cloud.blizzard.net:8778/v1/actions/2a0ff107-e789-4660-a122-3816c43af703") w.WriteHeader(http.StatusAccepted) fmt.Fprint(w, ActionResponse) }) } requests_test.go000066400000000000000000000257231335005366400367640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/clusters/testingpackage testing import ( "strings" "testing" "github.com/gophercloud/gophercloud/openstack/clustering/v1/clusters" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreateCluster(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateClusterSuccessfully(t) minSize := 1 opts := clusters.CreateOpts{ Name: "cluster1", DesiredCapacity: 3, ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", MinSize: &minSize, MaxSize: 20, Timeout: 3600, Metadata: map[string]interface{}{}, Config: map[string]interface{}{}, } res := clusters.Create(fake.ServiceClient(), opts) th.AssertNoErr(t, res.Err) location := res.Header.Get("Location") th.AssertEquals(t, "http://senlin.cloud.blizzard.net:8778/v1/actions/625628cd-f877-44be-bde0-fec79f84e13d", location) locationFields := strings.Split(location, "actions/") actionID := locationFields[1] th.AssertEquals(t, "625628cd-f877-44be-bde0-fec79f84e13d", actionID) actual, err := res.Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCluster, *actual) } func TestCreateClusterEmptyTime(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateClusterEmptyTimeSuccessfully(t) minSize := 1 opts := clusters.CreateOpts{ Name: "cluster1", DesiredCapacity: 3, ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", MinSize: &minSize, MaxSize: 20, Timeout: 3600, Metadata: map[string]interface{}{}, Config: map[string]interface{}{}, } actual, err := clusters.Create(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCluster_EmptyTime, *actual) } func TestCreateClusterMetadata(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateClusterMetadataSuccessfully(t) minSize := 1 opts := clusters.CreateOpts{ Name: "cluster1", DesiredCapacity: 3, ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", MinSize: &minSize, MaxSize: 20, Timeout: 3600, Metadata: map[string]interface{}{ "foo": "bar", "test": map[string]interface{}{ "nil_interface": interface{}(nil), "float_value": float64(123.3), "string_value": "test_string", "bool_value": false, }, }, Config: map[string]interface{}{}, } actual, err := clusters.Create(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCluster_Metadata, *actual) } func TestGetCluster(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetClusterSuccessfully(t) actual, err := clusters.Get(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCluster, *actual) } func TestGetClusterEmptyTime(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetClusterEmptyTimeSuccessfully(t) actual, err := clusters.Get(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCluster_EmptyTime, *actual) } func TestListClusters(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListClusterSuccessfully(t) count := 0 clusters.List(fake.ServiceClient(), clusters.ListOpts{GlobalProject: new(bool)}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := clusters.ExtractClusters(page) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedClusters, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestUpdateCluster(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateClusterSuccessfully(t) updateOpts := clusters.UpdateOpts{ Name: "cluster1", ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", } actual, err := clusters.Update(fake.ServiceClient(), ExpectedCluster.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCluster, *actual) } func TestUpdateClusterEmptyTime(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateClusterEmptyTimeSuccessfully(t) updateOpts := clusters.UpdateOpts{ Name: "cluster1", ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", } actual, err := clusters.Update(fake.ServiceClient(), ExpectedCluster_EmptyTime.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCluster_EmptyTime, *actual) } func TestDeleteCluster(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteClusterSuccessfully(t) err := clusters.Delete(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee").ExtractErr() th.AssertNoErr(t, err) } func TestResizeCluster(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleResizeSuccessfully(t) maxSize := 5 minSize := 1 number := -2 strict := true opts := clusters.ResizeOpts{ AdjustmentType: "CHANGE_IN_CAPACITY", MaxSize: &maxSize, MinSize: &minSize, Number: number, Strict: &strict, } actionID, err := clusters.Resize(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } // Test case for Number field having a float value func TestResizeClusterNumberFloat(t *testing.T) { maxSize := 5 minSize := 1 number := 100.0 strict := true opts := clusters.ResizeOpts{ AdjustmentType: "CHANGE_IN_PERCENTAGE", MaxSize: &maxSize, MinSize: &minSize, Number: number, Strict: &strict, } _, err := opts.ToClusterResizeMap() th.AssertNoErr(t, err) } // Test case for missing Number field. func TestResizeClusterMissingNumber(t *testing.T) { maxSize := 5 minSize := 1 strict := true opts := clusters.ResizeOpts{ MaxSize: &maxSize, MinSize: &minSize, Strict: &strict, } _, err := opts.ToClusterResizeMap() th.AssertNoErr(t, err) } // Test case for missing Number field which is required when AdjustmentType is specified func TestResizeClusterInvalidParamsMissingNumber(t *testing.T) { maxSize := 5 minSize := 1 strict := true opts := clusters.ResizeOpts{ AdjustmentType: "CHANGE_IN_CAPACITY", MaxSize: &maxSize, MinSize: &minSize, Strict: &strict, } _, err := opts.ToClusterResizeMap() isValid := err == nil th.AssertEquals(t, false, isValid) } // Test case for float Number field which is only valid for CHANGE_IN_PERCENTAGE. func TestResizeClusterInvalidParamsNumberFloat(t *testing.T) { maxSize := 5 minSize := 1 number := 100.0 strict := true opts := clusters.ResizeOpts{ AdjustmentType: "CHANGE_IN_CAPACITY", MaxSize: &maxSize, MinSize: &minSize, Number: number, Strict: &strict, } _, err := opts.ToClusterResizeMap() isValid := err == nil th.AssertEquals(t, false, isValid) } func TestClusterScaleIn(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleScaleInSuccessfully(t) count := 5 scaleOpts := clusters.ScaleInOpts{ Count: &count, } actionID, err := clusters.ScaleIn(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", scaleOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } func TestListClusterPolicies(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListPoliciesSuccessfully(t) pageCount := 0 err := clusters.ListPolicies(fake.ServiceClient(), ExpectedClusterPolicy.ClusterID, clusters.ListPoliciesOpts{Name: "Test"}).EachPage(func(page pagination.Page) (bool, error) { pageCount++ actual, err := clusters.ExtractClusterPolicies(page) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedListPolicies, actual) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, pageCount, 1) } func TestGetClusterPolicies(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetPolicySuccessfully(t) actual, err := clusters.GetPolicy(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", "714fe676-a08f-4196-b7af-61d52eeded15").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedClusterPolicy, *actual) } func TestClusterRecover(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleRecoverSuccessfully(t) recoverOpts := clusters.RecoverOpts{ Operation: clusters.RebuildRecovery, Check: new(bool), CheckCapacity: new(bool), } actionID, err := clusters.Recover(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", recoverOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } func TestAttachPolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAttachPolicySuccessfully(t) enabled := true opts := clusters.AttachPolicyOpts{ PolicyID: "policy1", Enabled: &enabled, } actionID, err := clusters.AttachPolicy(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } func TestDetachPolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDetachPolicySuccessfully(t) opts := clusters.DetachPolicyOpts{ PolicyID: "policy1", } actionID, err := clusters.DetachPolicy(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } func TestUpdatePolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdatePolicySuccessfully(t) enabled := true opts := clusters.UpdatePolicyOpts{ PolicyID: "policy1", Enabled: &enabled, } actionID, err := clusters.UpdatePolicy(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } func TestClusterScaleOut(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleScaleOutSuccessfully(t) scaleOutOpts := clusters.ScaleOutOpts{ Count: 5, } actionID, err := clusters.ScaleOut(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", scaleOutOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } func TestClusterCheck(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCheckSuccessfully(t) actionID, err := clusters.Check(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } func TestLifecycle(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleLifecycleSuccessfully(t) opts := clusters.CompleteLifecycleOpts{ LifecycleActionTokenID: "976528c6-dcf6-4d8d-9f4c-588f4e675f29", } res := clusters.CompleteLifecycle(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", opts) location := res.Header.Get("Location") th.AssertEquals(t, "http://senlin.cloud.blizzard.net:8778/v1/actions/2a0ff107-e789-4660-a122-3816c43af703", location) actionID, err := res.Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "2a0ff107-e789-4660-a122-3816c43af703", actionID) } urls.go000066400000000000000000000023761335005366400333610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/clusterspackage clusters import "github.com/gophercloud/gophercloud" var apiVersion = "v1" var apiName = "clusters" func commonURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName) } func idURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiVersion, apiName, id) } func actionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiVersion, apiName, id, "actions") } func createURL(client *gophercloud.ServiceClient) string { return commonURL(client) } func getURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } func listURL(client *gophercloud.ServiceClient) string { return commonURL(client) } func updateURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } func deleteURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } func listPoliciesURL(client *gophercloud.ServiceClient, clusterID string) string { return client.ServiceURL(apiVersion, apiName, clusterID, "policies") } func getPolicyURL(client *gophercloud.ServiceClient, clusterID string, policyID string) string { return client.ServiceURL(apiVersion, apiName, clusterID, "policies", policyID) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/nodes/000077500000000000000000000000001335005366400313605ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/nodes/doc.go000066400000000000000000000025771335005366400324670ustar00rootroot00000000000000/* Package nodes provides information and interaction with the nodes through the OpenStack Clustering service. Example to Create a Node opts := nodes.CreateOpts{ ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", Metadata: map[string]interface{}{}, Name: "node-e395be1e-002", ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", Role: "", } node, err := nodes.Create(serviceClient, opts).Extract() if err != nil { panic(err) } fmt.Printf("node", node) Example to List Nodes listOpts := nodes.ListOpts{ Name: "testnode", } allPages, err := nodes.List(serviceClient, listOpts).AllPages() if err != nil { panic(err) } allNodes, err := nodes.ExtractNodes(allPages) if err != nil { panic(err) } for _, node := range allNodes { fmt.Printf("%+v\n", node) } Example to Update a Node opts := nodes.UpdateOpts{ Name: "new-node-name", } nodeID := "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1" node, err := nodes.Update(serviceClient, nodeID, opts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", node) Example to Delete a Node nodeID := "6dc6d336e3fc4c0a951b5698cd1236ee" err := nodes.Delete(serviceClient, nodeID).ExtractErr() if err != nil { panic(err) } Example to Get a Node nodeID := "node123" node, err := nodes.Get(serviceClient, nodeID).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", node) */ package nodes requests.go000066400000000000000000000101511335005366400335010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/nodespackage nodes import ( "net/http" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToNodeCreateMap() (map[string]interface{}, error) } // CreateOpts represents options used to create a Node. type CreateOpts struct { Role string `json:"role,omitempty"` ProfileID string `json:"profile_id" required:"true"` ClusterID string `json:"cluster_id,omitempty"` Name string `json:"name" required:"true"` Metadata map[string]interface{} `json:"metadata,omitempty"` } // ToNodeCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToNodeCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "node") } // Create requests the creation of a new node. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToNodeCreateMap() if err != nil { r.Err = err return } var result *http.Response result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) if r.Err == nil { r.Header = result.Header } return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToNodeUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents options used to update a Node. type UpdateOpts struct { Name string `json:"name,omitempty"` ProfileID string `json:"profile_id,omitempty"` Role string `json:"role,omitempty"` Metadata map[string]interface{} `json:"metadata,omitempty"` } // ToClusterUpdateMap constructs a request body from UpdateOpts. func (opts UpdateOpts) ToNodeUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "node") } // Update requests the update of a node. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToNodeUpdateMap() if err != nil { r.Err = err return } var result *http.Response result, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) if r.Err == nil { r.Header = result.Header } return } // ListOptsBuilder allows extensions to add additional parmeters to the // List request. type ListOptsBuilder interface { ToNodeListQuery() (string, error) } // ListOpts represents options used to list nodes. type ListOpts struct { Limit int `q:"limit"` Marker string `q:"marker"` Sort string `q:"sort"` GlobalProject *bool `q:"global_project"` ClusterID string `q:"cluster_id"` Name string `q:"name"` Status string `q:"status"` } // ToNodeListQuery formats a ListOpts into a query string. func (opts ListOpts) ToNodeListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List instructs OpenStack to provide a list of nodes. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToNodeListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return NodePage{pagination.LinkedPageBase{PageResult: r}} }) } // Delete deletes the specified node. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { var result *http.Response result, r.Err = client.Delete(deleteURL(client, id), nil) if r.Err == nil { r.Header = result.Header } return } // Get makes a request against senlin to get a details of a node type func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { var result *http.Response result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) if r.Err == nil { r.Header = result.Header } return } results.go000066400000000000000000000064061335005366400333370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/nodespackage nodes import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Node represents an OpenStack clustering node. type Node struct { ClusterID string `json:"cluster_id"` CreatedAt time.Time `json:"-"` Data map[string]interface{} `json:"data"` Dependents map[string]interface{} `json:"dependents"` Domain string `json:"domain"` ID string `json:"id"` Index int `json:"index"` InitAt time.Time `json:"-"` Metadata map[string]interface{} `json:"metadata"` Name string `json:"name"` PhysicalID string `json:"physical_id"` ProfileID string `json:"profile_id"` ProfileName string `json:"profile_name"` Project string `json:"project"` Role string `json:"role"` Status string `json:"status"` StatusReason string `json:"status_reason"` UpdatedAt time.Time `json:"-"` User string `json:"user"` } func (r *Node) UnmarshalJSON(b []byte) error { type tmp Node var s struct { tmp CreatedAt string `json:"created_at"` InitAt string `json:"init_at"` UpdatedAt string `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Node(s.tmp) if s.CreatedAt != "" { r.CreatedAt, err = time.Parse(time.RFC3339, s.CreatedAt) if err != nil { return err } } if s.InitAt != "" { r.InitAt, err = time.Parse(time.RFC3339, s.InitAt) if err != nil { return err } } if s.UpdatedAt != "" { r.UpdatedAt, err = time.Parse(time.RFC3339, s.UpdatedAt) if err != nil { return err } } return nil } // commonResult is the response of a base result. type commonResult struct { gophercloud.Result } // Extract interprets any commonResult-based result as a Node. func (r commonResult) Extract() (*Node, error) { var s struct { Node *Node `json:"node"` } err := r.ExtractInto(&s) return s.Node, err } // CreateResult is the result of a Create operation. Call its Extract // method to intepret it as a Node. type CreateResult struct { commonResult } // GetResult is the result of a Get operation. Call its Extract method to // interpret it as a Node. type GetResult struct { commonResult } // DeleteResult is the result from a Delete operation. Call ExtractErr // method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // UpdateResult is the result of an Update operation. Call its Extract method // to interpet it as a Node. type UpdateResult struct { commonResult } // NodePage contains a single page of all nodes from a List call. type NodePage struct { pagination.LinkedPageBase } // IsEmpty determines if a NodePage contains any results. func (page NodePage) IsEmpty() (bool, error) { nodes, err := ExtractNodes(page) return len(nodes) == 0, err } // ExtractNodes returns a slice of Nodes from the List operation. func ExtractNodes(r pagination.Page) ([]Node, error) { var s struct { Nodes []Node `json:"nodes"` } err := (r.(NodePage)).ExtractInto(&s) return s.Nodes, err } testing/000077500000000000000000000000001335005366400327565ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/nodesdoc.go000066400000000000000000000000471335005366400340530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/nodes/testing// clustering_nodes_v1 package testing fixtures.go000066400000000000000000000234301335005366400351600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/nodes/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const CreateResponse = `{ "node": { "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", "created_at": "2016-05-13T07:02:20Z", "data": { "internal_ports": [ { "network_id": "847e4f65-1ff1-42b1-9e74-74e6a109ad11", "security_group_ids": ["8db277ab-1d98-4148-ba72-724721789427"], "fixed_ips": [ { "subnet_id": "863b20c0-c011-4650-85c2-ad531f4570a4", "ip_address": "10.63.177.162" } ], "id": "43aa53d7-a70b-4f40-812f-4feecb687018", "remove": true } ], "placement": { "zone": "nova" } }, "dependents": {}, "domain": "1235be1e-8d8e-43bb-bd6c-943eccf76a6d", "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", "index": 2, "init_at": "2016-05-13T08:02:04Z", "metadata": { "test": { "nil_interface": null, "bool_value": false, "string_value": "test_string", "float_value": 123.3 }, "foo": "bar" }, "name": "node-e395be1e-002", "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", "profile_name": "pcirros", "project": "eee0b7c083e84501bdd50fb269d2a10e", "role": "", "status": "ACTIVE", "status_reason": "Creation succeeded", "updated_at": null, "user": "ab79b9647d074e46ac223a8fa297b846" } }` var ExpectedCreate = nodes.Node{ ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", CreatedAt: time.Date(2016, 5, 13, 7, 2, 20, 0, time.UTC), Data: map[string]interface{}{ "internal_ports": []map[string]interface{}{ { "network_id": "847e4f65-1ff1-42b1-9e74-74e6a109ad11", "security_group_ids": []interface{}{ "8db277ab-1d98-4148-ba72-724721789427", }, "fixed_ips": []interface{}{ map[string]interface{}{ "subnet_id": "863b20c0-c011-4650-85c2-ad531f4570a4", "ip_address": "10.63.177.162", }, }, "id": "43aa53d7-a70b-4f40-812f-4feecb687018", "remove": true, }, }, "placement": map[string]interface{}{ "zone": "nova", }, }, Dependents: map[string]interface{}{}, Domain: "1235be1e-8d8e-43bb-bd6c-943eccf76a6d", ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", Index: 2, InitAt: time.Date(2016, 5, 13, 8, 2, 4, 0, time.UTC), Metadata: map[string]interface{}{ "foo": "bar", "test": map[string]interface{}{ "nil_interface": interface{}(nil), "float_value": float64(123.3), "string_value": "test_string", "bool_value": false, }, }, Name: "node-e395be1e-002", PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", ProfileName: "pcirros", Project: "eee0b7c083e84501bdd50fb269d2a10e", Role: "", Status: "ACTIVE", StatusReason: "Creation succeeded", User: "ab79b9647d074e46ac223a8fa297b846", } const ListResponse = ` { "nodes": [ { "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", "created_at": "2016-05-13T07:02:20Z", "data": {}, "dependents": {}, "domain": null, "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", "index": 2, "init_at": "2016-05-13T08:02:04Z", "metadata": {}, "name": "node-e395be1e-002", "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", "profile_name": "pcirros", "project": "eee0b7c083e84501bdd50fb269d2a10e", "role": "", "status": "ACTIVE", "status_reason": "Creation succeeded", "updated_at": "2016-05-13T09:02:04Z", "user": "ab79b9647d074e46ac223a8fa297b846" } ] }` var ExpectedList1 = nodes.Node{ ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", CreatedAt: time.Date(2016, 5, 13, 7, 2, 20, 0, time.UTC), Data: map[string]interface{}{}, Dependents: map[string]interface{}{}, Domain: "", ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", Index: 2, InitAt: time.Date(2016, 5, 13, 8, 2, 4, 0, time.UTC), Metadata: map[string]interface{}{}, Name: "node-e395be1e-002", PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", ProfileName: "pcirros", Project: "eee0b7c083e84501bdd50fb269d2a10e", Role: "", Status: "ACTIVE", StatusReason: "Creation succeeded", UpdatedAt: time.Date(2016, 5, 13, 9, 2, 4, 0, time.UTC), User: "ab79b9647d074e46ac223a8fa297b846", } var ExpectedList = []nodes.Node{ExpectedList1} const GetResponse = ` { "node": { "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", "created_at": "2016-05-13T07:02:20Z", "data": {}, "dependents": {}, "domain": null, "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", "index": 2, "init_at": "2016-05-13T07:02:04Z", "metadata": {"foo": "bar"}, "name": "node-e395be1e-002", "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", "profile_name": "pcirros", "project": "eee0b7c083e84501bdd50fb269d2a10e", "role": "", "status": "ACTIVE", "status_reason": "Creation succeeded", "updated_at": "2016-05-13T07:02:20Z", "user": "ab79b9647d074e46ac223a8fa297b846" } }` var ExpectedGet = nodes.Node{ ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", CreatedAt: time.Date(2016, 5, 13, 7, 2, 20, 0, time.UTC), Data: map[string]interface{}{}, Dependents: map[string]interface{}{}, Domain: "", ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", Index: 2, InitAt: time.Date(2016, 5, 13, 7, 2, 4, 0, time.UTC), Metadata: map[string]interface{}{"foo": "bar"}, Name: "node-e395be1e-002", PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", ProfileName: "pcirros", Project: "eee0b7c083e84501bdd50fb269d2a10e", Role: "", Status: "ACTIVE", StatusReason: "Creation succeeded", UpdatedAt: time.Date(2016, 5, 13, 7, 2, 20, 0, time.UTC), User: "ab79b9647d074e46ac223a8fa297b846", } const UpdateResponse = ` { "node": { "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", "created_at": "2016-05-13T07:02:20Z", "data": {}, "dependents": {}, "domain": null, "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", "index": 2, "init_at": "2016-05-13T08:02:04Z", "metadata": {"foo":"bar"}, "name": "node-e395be1e-002", "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", "profile_name": "pcirros", "project": "eee0b7c083e84501bdd50fb269d2a10e", "role": "", "status": "ACTIVE", "status_reason": "Creation succeeded", "updated_at": "2016-05-13T09:02:04Z", "user": "ab79b9647d074e46ac223a8fa297b846" } }` var ExpectedUpdate = nodes.Node{ ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", CreatedAt: time.Date(2016, 5, 13, 7, 2, 20, 0, time.UTC), Data: map[string]interface{}{}, Dependents: map[string]interface{}{}, Domain: "", ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", Index: 2, InitAt: time.Date(2016, 5, 13, 8, 2, 4, 0, time.UTC), Metadata: map[string]interface{}{"foo": "bar"}, Name: "node-e395be1e-002", PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", ProfileName: "pcirros", Project: "eee0b7c083e84501bdd50fb269d2a10e", Role: "", Status: "ACTIVE", StatusReason: "Creation succeeded", UpdatedAt: time.Date(2016, 5, 13, 9, 2, 4, 0, time.UTC), User: "ab79b9647d074e46ac223a8fa297b846", } func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/nodes", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.Header().Add("X-OpenStack-Request-ID", "req-3791a089-9d46-4671-a3f9-55e95e55d2b4") w.Header().Add("Location", "http://senlin.cloud.blizzard.net:8778/v1/actions/ffd94dd8-6266-4887-9a8c-5b78b72136da") w.WriteHeader(http.StatusOK) fmt.Fprint(w, CreateResponse) }) } func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/nodes", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ListResponse) }) } func HandleDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/nodes/6dc6d336e3fc4c0a951b5698cd1236ee", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNoContent) }) } func HandleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/nodes/573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, GetResponse) }) } func HandleUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/nodes/82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, UpdateResponse) }) } requests_test.go000066400000000000000000000052571335005366400362300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/nodes/testingpackage testing import ( "strings" "testing" "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreateNode(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateSuccessfully(t) createOpts := nodes.CreateOpts{ ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", Metadata: map[string]interface{}{ "foo": "bar", "test": map[string]interface{}{ "nil_interface": interface{}(nil), "float_value": float64(123.3), "string_value": "test_string", "bool_value": false, }, }, Name: "node-e395be1e-002", ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", Role: "", } res := nodes.Create(fake.ServiceClient(), createOpts) th.AssertNoErr(t, res.Err) requestID := res.Header.Get("X-Openstack-Request-Id") th.AssertEquals(t, "req-3791a089-9d46-4671-a3f9-55e95e55d2b4", requestID) location := res.Header.Get("Location") th.AssertEquals(t, "http://senlin.cloud.blizzard.net:8778/v1/actions/ffd94dd8-6266-4887-9a8c-5b78b72136da", location) locationFields := strings.Split(location, "actions/") actionID := locationFields[1] th.AssertEquals(t, "ffd94dd8-6266-4887-9a8c-5b78b72136da", actionID) actual, err := res.Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCreate, *actual) } func TestListNodes(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t) count := 0 err := nodes.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { actual, err := nodes.ExtractNodes(page) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedList, actual) count++ return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, count, 1) } func TestDeleteNode(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSuccessfully(t) deleteResult := nodes.Delete(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") th.AssertNoErr(t, deleteResult.ExtractErr()) } func TestGetNode(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t) actual, err := nodes.Get(fake.ServiceClient(), "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedGet, *actual) } func TestUpdateNode(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateSuccessfully(t) nodeOpts := nodes.UpdateOpts{ Name: "node-e395be1e-002", } actual, err := nodes.Update(fake.ServiceClient(), ExpectedUpdate.ID, nodeOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedUpdate, *actual) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/nodes/urls.go000066400000000000000000000014411335005366400326740ustar00rootroot00000000000000package nodes import "github.com/gophercloud/gophercloud" var apiVersion = "v1" var apiName = "nodes" func commonURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName) } func createURL(client *gophercloud.ServiceClient) string { return commonURL(client) } func listURL(client *gophercloud.ServiceClient) string { return commonURL(client) } func idURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiVersion, apiName, id) } func deleteURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } func getURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } func updateURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/policies/000077500000000000000000000000001335005366400320575ustar00rootroot00000000000000doc.go000066400000000000000000000035601335005366400331000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/policies/* Package policies provides information and interaction with the policies through the OpenStack Clustering service. Example to List Policies listOpts := policies.ListOpts{ Limit: 2, } allPages, err := policies.List(clusteringClient, listOpts).AllPages() if err != nil { panic(err) } allPolicies, err := policies.ExtractPolicies(allPages) if err != nil { panic(err) } for _, policy := range allPolicies { fmt.Printf("%+v\n", policy) } Example to Create a Policy opts := policies.CreateOpts{ Name: "new_policy", Spec: policies.Spec{ Description: "new policy description", Properties: map[string]interface{}{ "hooks": map[string]interface{}{ "type": "zaqar", "params": map[string]interface{}{ "queue": "my_zaqar_queue", }, "timeout": 10, }, }, Type: "senlin.policy.deletion", Version: "1.1", }, } createdPolicy, err := policies.Create(client, opts).Extract() if err != nil { panic(err) } Example to Get a Policy policyName := "get_policy" policyDetail, err := policies.Get(clusteringClient, policyName).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", policyDetail) Example to Update a Policy opts := policies.UpdateOpts{ Name: "update_policy", } updatePolicy, err := policies.Update(client, opts).Extract() if err != nil { panic(err) } Example to Validate a Policy opts := policies.ValidateOpts{ Spec: policies.Spec{ Description: "new policy description", Properties: map[string]interface{}{ "hooks": map[string]interface{}{ "type": "zaqar", "params": map[string]interface{}{ "queue": "my_zaqar_queue", }, "timeout": 10, }, }, Type: "senlin.policy.deletion", Version: "1.1", }, } validatePolicy, err := policies.Validate(client, opts).Extract() if err != nil { panic(err) } */ package policies requests.go000066400000000000000000000122751335005366400342110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/policiespackage policies import ( "net/http" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToPolicyListQuery() (string, error) } // ListOpts represents options used to list policies. type ListOpts struct { // Limit limits the number of Policies to return. Limit int `q:"limit"` // Marker and Limit control paging. Marker instructs List where to start // listing from. Marker string `q:"marker"` // Sorts the response by one or more attribute and optional sort direction // combinations. Sort string `q:"sort"` // GlobalProject indicates whether to include resources for all projects or // resources for the current project. GlobalProject *bool `q:"global_project"` // Name to filter the response by the specified name property of the object. Name string `q:"name"` // Filter the response by the specified type property of the object. Type string `q:"type"` } // ToPolicyListQuery formats a ListOpts into a query string. func (opts ListOpts) ToPolicyListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List instructs OpenStack to retrieve a list of policies. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := policyListURL(client) if opts != nil { query, err := opts.ToPolicyListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { p := PolicyPage{pagination.MarkerPageBase{PageResult: r}} p.MarkerPageBase.Owner = p return p }) } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToPolicyCreateMap() (map[string]interface{}, error) } // CreateOpts represents options used to create a policy. type CreateOpts struct { Name string `json:"name"` Spec Spec `json:"spec"` } // ToPolicyCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } return map[string]interface{}{"policy": b}, nil } // Create makes a request against the API to create a policy func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToPolicyCreateMap() if err != nil { r.Err = err return } var result *http.Response result, r.Err = client.Post(policyCreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) if r.Err == nil { r.Header = result.Header } return } // Delete makes a request against the API to delete a policy. func Delete(client *gophercloud.ServiceClient, policyID string) (r DeleteResult) { var result *http.Response result, r.Err = client.Delete(policyDeleteURL(client, policyID), &gophercloud.RequestOpts{ OkCodes: []int{204}, }) if r.Err == nil { r.Header = result.Header } return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToPolicyUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents options to update a policy. type UpdateOpts struct { Name string `json:"name,omitempty"` } // ToPolicyUpdateMap constructs a request body from UpdateOpts. func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "policy") } // Update updates a specified policy. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToPolicyUpdateMap() if err != nil { r.Err = err return } var result *http.Response result, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) if r.Err == nil { r.Header = result.Header } return } // ValidateOptsBuilder allows extensions to add additional parameters to the // Validate request. type ValidateOptsBuilder interface { ToPolicyValidateMap() (map[string]interface{}, error) } // ValidateOpts represents options used to validate a policy. type ValidateOpts struct { Spec Spec `json:"spec"` } // ToPolicyValidateMap formats a CreateOpts into a body map. func (opts ValidateOpts) ToPolicyValidateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "policy") } // Validate policy will validate a specified policy. func Validate(client *gophercloud.ServiceClient, opts ValidateOptsBuilder) (r ValidateResult) { b, err := opts.ToPolicyValidateMap() if err != nil { r.Err = err return } var result *http.Response result, r.Err = client.Post(validateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) if r.Err == nil { r.Header = result.Header } return } // Get makes a request against the API to get details for a policy. func Get(client *gophercloud.ServiceClient, policyTypeName string) (r GetResult) { url := policyGetURL(client, policyTypeName) _, r.Err = client.Get(url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000100201335005366400340210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/policiespackage policies import ( "encoding/json" "fmt" "strconv" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Policy represents a clustering policy in the Openstack cloud. type Policy struct { CreatedAt time.Time `json:"-"` Data map[string]interface{} `json:"data"` Domain string `json:"domain"` ID string `json:"id"` Name string `json:"name"` Project string `json:"project"` Spec Spec `json:"spec"` Type string `json:"type"` UpdatedAt time.Time `json:"-"` User string `json:"user"` } func (r *Policy) UnmarshalJSON(b []byte) error { type tmp Policy var s struct { tmp CreatedAt string `json:"created_at,omitempty"` UpdatedAt string `json:"updated_at,omitempty"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Policy(s.tmp) if s.CreatedAt != "" { r.CreatedAt, err = time.Parse(gophercloud.RFC3339MilliNoZ, s.CreatedAt) if err != nil { r.CreatedAt, err = time.Parse(time.RFC3339, s.CreatedAt) if err != nil { return err } } } if s.UpdatedAt != "" { r.UpdatedAt, err = time.Parse(gophercloud.RFC3339MilliNoZ, s.UpdatedAt) if err != nil { r.UpdatedAt, err = time.Parse(time.RFC3339, s.UpdatedAt) if err != nil { return err } } } return nil } // Spec represents an OpenStack clustering policy spec. type Spec struct { Description string `json:"description"` Properties map[string]interface{} `json:"properties"` Type string `json:"type"` Version string `json:"-"` } func (r *Spec) UnmarshalJSON(b []byte) error { type tmp Spec var s struct { tmp Version interface{} `json:"version"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Spec(s.tmp) switch t := s.Version.(type) { case float64: if t == 1 { r.Version = fmt.Sprintf("%.1f", t) } else { r.Version = strconv.FormatFloat(t, 'f', -1, 64) } case string: r.Version = t } return nil } // policyResult is the resposne of a base Policy result. type policyResult struct { gophercloud.Result } // Extract interpets any policyResult-base result as a Policy. func (r policyResult) Extract() (*Policy, error) { var s struct { Policy *Policy `json:"policy"` } err := r.ExtractInto(&s) return s.Policy, err } // CreateResult is the result of an Update operation. Call its Extract // method to interpret it as a Policy. type CreateResult struct { policyResult } // GetResult is the result of a Get operation. Call its Extract method to // interpret it as a Policy. type GetResult struct { policyResult } // UpdateResult is the result of an Update operation. Call its Extract // method to interpret it as a Policy. type UpdateResult struct { policyResult } // ValidateResult is the result of a Validate operation. Call its Extract // method to interpret it as a Policy. type ValidateResult struct { policyResult } // DeleteResult is the result of a Delete operation. Call its Extract // method to interpret it as a DeleteHeader. type DeleteResult struct { gophercloud.ErrResult } // PolicyPage contains a list page of all policies from a List call. type PolicyPage struct { pagination.MarkerPageBase } // IsEmpty determines if a PolicyPage contains any results. func (page PolicyPage) IsEmpty() (bool, error) { policies, err := ExtractPolicies(page) return len(policies) == 0, err } // LastMarker returns the last policy ID in a ListResult. func (r PolicyPage) LastMarker() (string, error) { policies, err := ExtractPolicies(r) if err != nil { return "", err } if len(policies) == 0 { return "", nil } return policies[len(policies)-1].ID, nil } // ExtractPolicies returns a slice of Policies from the List operation. func ExtractPolicies(r pagination.Page) ([]Policy, error) { var s struct { Policies []Policy `json:"policies"` } err := (r.(PolicyPage)).ExtractInto(&s) return s.Policies, err } testing/000077500000000000000000000000001335005366400334555ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/policiesdoc.go000066400000000000000000000000521335005366400345460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/policies/testing// clustering_policies_v1 package testing fixtures.go000066400000000000000000000334701335005366400356640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/policies/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/clustering/v1/policies" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const PolicyListBody1 = ` { "policies": [ { "created_at": "2018-04-02T21:43:30.000000", "data": {}, "domain": null, "id": "PolicyListBodyID1", "name": "delpol", "project": "018cd0909fb44cd5bc9b7a3cd664920e", "spec": { "description": "A policy for choosing victim node(s) from a cluster for deletion.", "properties": { "criteria": "OLDEST_FIRST", "destroy_after_deletion": true, "grace_period": 60, "reduce_desired_capacity": false }, "type": "senlin.policy.deletion", "version": 1 }, "type": "senlin.policy.deletion-1.0", "updated_at": "2018-04-02T00:19:12Z", "user": "fe43e41739154b72818565e0d2580819" } ] } ` const PolicyListBody2 = ` { "policies": [ { "created_at": "2018-04-02T22:29:36.000000", "data": {}, "domain": null, "id": "PolicyListBodyID2", "name": "delpol2", "project": "018cd0909fb44cd5bc9b7a3cd664920e", "spec": { "description": "A policy for choosing victim node(s) from a cluster for deletion.", "properties": { "criteria": "OLDEST_FIRST", "destroy_after_deletion": true, "grace_period": 60, "reduce_desired_capacity": false }, "type": "senlin.policy.deletion", "version": "1.0" }, "type": "senlin.policy.deletion-1.0", "updated_at": "2018-04-02T23:15:11.000000", "user": "fe43e41739154b72818565e0d2580819" } ] } ` const PolicyCreateBody = ` { "policy": { "created_at": "2018-04-04T00:18:36Z", "data": {}, "domain": null, "id": "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", "name": "delpol4", "project": "018cd0909fb44cd5bc9b7a3cd664920e", "spec": { "description": "A policy for choosing victim node(s) from a cluster for deletion.", "properties": { "hooks": { "params": { "queue": "zaqar_queue_name" }, "timeout": 180, "type": "zaqar" } }, "type": "senlin.policy.deletion", "version": 1.1 }, "type": "senlin.policy.deletion-1.1", "updated_at": null, "user": "fe43e41739154b72818565e0d2580819" } } ` const PolicyGetBody = ` { "policy": { "created_at": "2018-04-02T21:43:30.000000", "data": {}, "domain": null, "id": "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", "name": "delpol", "project": "018cd0909fb44cd5bc9b7a3cd664920e", "spec": { "description": "A policy for choosing victim node(s) from a cluster for deletion.", "properties": { "criteria": "OLDEST_FIRST", "destroy_after_deletion": true, "grace_period": 60, "reduce_desired_capacity": false }, "type": "senlin.policy.deletion", "version": 1 }, "type": "senlin.policy.deletion-1.0", "updated_at": "2018-04-02T00:19:12Z", "user": "fe43e41739154b72818565e0d2580819" } } ` const PolicyUpdateBody = ` { "policy": { "created_at": "2018-04-02T21:43:30.000000", "data": {}, "domain": null, "id": "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", "name": "delpol4", "project": "018cd0909fb44cd5bc9b7a3cd664920e", "spec": { "description": "A policy for choosing victim node(s) from a cluster for deletion.", "properties": { "hooks": { "params": { "queue": "zaqar_queue_name" }, "timeout": 180, "type": "zaqar" } }, "type": "senlin.policy.deletion", "version": 1.1 }, "type": "senlin.policy.deletion-1.1", "updated_at": null, "user": "fe43e41739154b72818565e0d2580819" } } ` const PolicyBadUpdateBody = ` { "policy": { "created_at": "invalid", "data": {}, "domain": null, "id": "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", "name": "delpol4", "project": "018cd0909fb44cd5bc9b7a3cd664920e", "spec": { "description": "A policy for choosing victim node(s) from a cluster for deletion.", "properties": { "hooks": { "params": { "queue": "zaqar_queue_name" }, "timeout": 180, "type": "zaqar" } }, "type": "senlin.policy.deletion", "version": 1.1 }, "type": "invalid", "updated_at": null, "user": "fe43e41739154b72818565e0d2580819" } } ` const PolicyValidateBody = ` { "policy": { "created_at": "2018-04-02T21:43:30.000000", "data": {}, "domain": null, "id": "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", "name": "delpol4", "project": "018cd0909fb44cd5bc9b7a3cd664920e", "spec": { "description": "A policy for choosing victim node(s) from a cluster for deletion.", "properties": { "hooks": { "params": { "queue": "zaqar_queue_name" }, "timeout": 180, "type": "zaqar" } }, "type": "senlin.policy.deletion", "version": 1.1 }, "type": "senlin.policy.deletion-1.1", "updated_at": null, "user": "fe43e41739154b72818565e0d2580819" } } ` const PolicyBadValidateBody = ` { "policy": { "created_at": "invalid", "data": {}, "domain": null, "id": "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", "name": "delpol4", "project": "018cd0909fb44cd5bc9b7a3cd664920e", "spec": { "description": "A policy for choosing victim node(s) from a cluster for deletion.", "properties": { "hooks": { "params": { "queue": "zaqar_queue_name" }, "timeout": 180, "type": "zaqar" } }, "type": "senlin.policy.deletion", "version": 1.1 }, "type": "invalid", "updated_at": null, "user": "fe43e41739154b72818565e0d2580819" } } ` const PolicyDeleteRequestID = "req-7328d1b0-9945-456f-b2cd-5166b77d14a8" const PolicyIDtoUpdate = "b99b3ab4-3aa6-4fba-b827-69b88b9c544a" const PolicyIDtoGet = "b99b3ab4-3aa6-4fba-b827-69b88b9c544a" const PolicyIDtoDelete = "1" var ExpectedPolicy1 = policies.Policy{ CreatedAt: time.Date(2018, 4, 2, 21, 43, 30, 0, time.UTC), Data: map[string]interface{}{}, Domain: "", ID: "PolicyListBodyID1", Name: "delpol", Project: "018cd0909fb44cd5bc9b7a3cd664920e", Spec: policies.Spec{ Description: "A policy for choosing victim node(s) from a cluster for deletion.", Properties: map[string]interface{}{ "criteria": "OLDEST_FIRST", "destroy_after_deletion": true, "grace_period": float64(60), "reduce_desired_capacity": false, }, Type: "senlin.policy.deletion", Version: "1.0", }, Type: "senlin.policy.deletion-1.0", User: "fe43e41739154b72818565e0d2580819", UpdatedAt: time.Date(2018, 4, 2, 0, 19, 12, 0, time.UTC), } var ExpectedPolicy2 = policies.Policy{ CreatedAt: time.Date(2018, 4, 2, 22, 29, 36, 0, time.UTC), Data: map[string]interface{}{}, Domain: "", ID: "PolicyListBodyID2", Name: "delpol2", Project: "018cd0909fb44cd5bc9b7a3cd664920e", Spec: policies.Spec{ Description: "A policy for choosing victim node(s) from a cluster for deletion.", Properties: map[string]interface{}{ "criteria": "OLDEST_FIRST", "destroy_after_deletion": true, "grace_period": float64(60), "reduce_desired_capacity": false, }, Type: "senlin.policy.deletion", Version: "1.0", }, Type: "senlin.policy.deletion-1.0", User: "fe43e41739154b72818565e0d2580819", UpdatedAt: time.Date(2018, 4, 2, 23, 15, 11, 0, time.UTC), } var ExpectedPolicies = [][]policies.Policy{ []policies.Policy{ExpectedPolicy1}, []policies.Policy{ExpectedPolicy2}, } var ExpectedCreatePolicy = policies.Policy{ CreatedAt: time.Date(2018, 4, 4, 0, 18, 36, 0, time.UTC), Data: map[string]interface{}{}, Domain: "", ID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", Name: "delpol4", Project: "018cd0909fb44cd5bc9b7a3cd664920e", Spec: policies.Spec{ Description: "A policy for choosing victim node(s) from a cluster for deletion.", Properties: map[string]interface{}{ "hooks": map[string]interface{}{ "params": map[string]interface{}{ "queue": "zaqar_queue_name", }, "timeout": float64(180), "type": "zaqar", }, }, Type: "senlin.policy.deletion", Version: "1.1", }, Type: "senlin.policy.deletion-1.1", User: "fe43e41739154b72818565e0d2580819", } var ExpectedGetPolicy = policies.Policy{ CreatedAt: time.Date(2018, 4, 2, 21, 43, 30, 0, time.UTC), Data: map[string]interface{}{}, Domain: "", ID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", Name: "delpol", Project: "018cd0909fb44cd5bc9b7a3cd664920e", Spec: policies.Spec{ Description: "A policy for choosing victim node(s) from a cluster for deletion.", Properties: map[string]interface{}{ "criteria": "OLDEST_FIRST", "destroy_after_deletion": true, "grace_period": float64(60), "reduce_desired_capacity": false, }, Type: "senlin.policy.deletion", Version: "1.0", }, Type: "senlin.policy.deletion-1.0", User: "fe43e41739154b72818565e0d2580819", UpdatedAt: time.Date(2018, 4, 2, 0, 19, 12, 0, time.UTC), } var ExpectedUpdatePolicy = policies.Policy{ CreatedAt: time.Date(2018, 4, 2, 21, 43, 30, 0, time.UTC), Data: map[string]interface{}{}, Domain: "", ID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", Name: "delpol4", Project: "018cd0909fb44cd5bc9b7a3cd664920e", Spec: policies.Spec{ Description: "A policy for choosing victim node(s) from a cluster for deletion.", Properties: map[string]interface{}{ "hooks": map[string]interface{}{ "params": map[string]interface{}{ "queue": "zaqar_queue_name", }, "timeout": float64(180), "type": "zaqar", }, }, Type: "senlin.policy.deletion", Version: "1.1", }, Type: "senlin.policy.deletion-1.1", User: "fe43e41739154b72818565e0d2580819", } var ExpectedValidatePolicy = policies.Policy{ CreatedAt: time.Date(2018, 4, 2, 21, 43, 30, 0, time.UTC), Data: map[string]interface{}{}, Domain: "", ID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", Name: "delpol4", Project: "018cd0909fb44cd5bc9b7a3cd664920e", Spec: policies.Spec{ Description: "A policy for choosing victim node(s) from a cluster for deletion.", Properties: map[string]interface{}{ "hooks": map[string]interface{}{ "params": map[string]interface{}{ "queue": "zaqar_queue_name", }, "timeout": float64(180), "type": "zaqar", }, }, Type: "senlin.policy.deletion", Version: "1.1", }, Type: "senlin.policy.deletion-1.1", User: "fe43e41739154b72818565e0d2580819", } func HandlePolicyList(t *testing.T) { th.Mux.HandleFunc("/v1/policies", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, PolicyListBody1) case "PolicyListBodyID1": fmt.Fprintf(w, PolicyListBody2) case "PolicyListBodyID2": fmt.Fprintf(w, `{"policies":[]}`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) } func HandlePolicyCreate(t *testing.T) { th.Mux.HandleFunc("/v1/policies", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, PolicyCreateBody) }) } func HandlePolicyDelete(t *testing.T) { th.Mux.HandleFunc("/v1/policies/"+PolicyIDtoDelete, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("X-OpenStack-Request-Id", PolicyDeleteRequestID) w.WriteHeader(http.StatusNoContent) }) } func HandlePolicyGet(t *testing.T) { th.Mux.HandleFunc("/v1/policies/"+PolicyIDtoGet, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, PolicyGetBody) }) } func HandlePolicyUpdate(t *testing.T) { th.Mux.HandleFunc("/v1/policies/"+PolicyIDtoUpdate, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, PolicyUpdateBody) }) } func HandleBadPolicyUpdate(t *testing.T) { th.Mux.HandleFunc("/v1/policies/"+PolicyIDtoUpdate, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, PolicyBadUpdateBody) }) } func HandlePolicyValidate(t *testing.T) { th.Mux.HandleFunc("/v1/policies/validate", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, PolicyValidateBody) }) } func HandleBadPolicyValidate(t *testing.T) { th.Mux.HandleFunc("/v1/policies/validate", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, PolicyBadValidateBody) }) } requests_test.go000066400000000000000000000061211335005366400367160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/policies/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/clustering/v1/policies" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListPolicies(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePolicyList(t) listOpts := policies.ListOpts{ Limit: 1, } count := 0 err := policies.List(fake.ServiceClient(), listOpts).EachPage(func(page pagination.Page) (bool, error) { actual, err := policies.ExtractPolicies(page) if err != nil { t.Errorf("Failed to extract policies: %v", err) return false, err } th.AssertDeepEquals(t, ExpectedPolicies[count], actual) count++ return true, nil }) th.AssertNoErr(t, err) if count != 2 { t.Errorf("Expected 2 pages, got %d", count) } } func TestCreatePolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePolicyCreate(t) expected := ExpectedCreatePolicy opts := policies.CreateOpts{ Name: ExpectedCreatePolicy.Name, Spec: ExpectedCreatePolicy.Spec, } actual, err := policies.Create(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) } func TestDeletePolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePolicyDelete(t) res := policies.Delete(fake.ServiceClient(), PolicyIDtoDelete) th.AssertNoErr(t, res.ExtractErr()) requestID := res.Header["X-Openstack-Request-Id"][0] th.AssertEquals(t, PolicyDeleteRequestID, requestID) } func TestUpdatePolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePolicyUpdate(t) expected := ExpectedUpdatePolicy opts := policies.UpdateOpts{ Name: ExpectedUpdatePolicy.Name, } actual, err := policies.Update(fake.ServiceClient(), PolicyIDtoUpdate, opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) } func TestBadUpdatePolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleBadPolicyUpdate(t) opts := policies.UpdateOpts{ Name: ExpectedUpdatePolicy.Name, } _, err := policies.Update(fake.ServiceClient(), PolicyIDtoUpdate, opts).Extract() th.AssertEquals(t, false, err == nil) } func TestValidatePolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePolicyValidate(t) expected := ExpectedValidatePolicy opts := policies.ValidateOpts{ Spec: ExpectedValidatePolicy.Spec, } actual, err := policies.Validate(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) } func TestBadValidatePolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleBadPolicyValidate(t) opts := policies.ValidateOpts{ Spec: ExpectedValidatePolicy.Spec, } _, err := policies.Validate(fake.ServiceClient(), opts).Extract() th.AssertEquals(t, false, err == nil) } func TestGetPolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePolicyGet(t) actual, err := policies.Get(fake.ServiceClient(), PolicyIDtoGet).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedGetPolicy, *actual) } urls.go000066400000000000000000000015651335005366400333230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/policiespackage policies import "github.com/gophercloud/gophercloud" const ( apiVersion = "v1" apiName = "policies" ) func policyListURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName) } func policyCreateURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName) } func policyDeleteURL(client *gophercloud.ServiceClient, policyID string) string { return client.ServiceURL(apiVersion, apiName, policyID) } func policyGetURL(client *gophercloud.ServiceClient, policyID string) string { return client.ServiceURL(apiVersion, apiName, policyID) } func updateURL(client *gophercloud.ServiceClient, policyID string) string { return client.ServiceURL(apiVersion, apiName, policyID) } func validateURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName, "validate") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/policytypes/000077500000000000000000000000001335005366400326345ustar00rootroot00000000000000doc.go000066400000000000000000000013571335005366400336570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/policytypes/* Package policytypes lists all policy types and shows details for a policy type from the OpenStack Clustering Service. Example to List Policy Types allPages, err := policytypes.List(clusteringClient).AllPages() if err != nil { panic(err) } allPolicyTypes, err := actions.ExtractPolicyTypes(allPages) if err != nil { panic(err) } for _, policyType := range allPolicyTypes { fmt.Printf("%+v\n", policyType) } Example to Get a Policy Type policyTypeName := "senlin.policy.affinity-1.0" policyTypeDetail, err := policyTypes.Get(clusteringClient, policyTypeName).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", policyTypeDetail) */ package policytypes requests.go000066400000000000000000000013421335005366400347570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/policytypespackage policytypes import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List makes a request against the API to list policy types. func List(client *gophercloud.ServiceClient) pagination.Pager { url := policyTypeListURL(client) return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return PolicyTypePage{pagination.SinglePageBase(r)} }) } // Get makes a request against the API to get details for a policy type. func Get(client *gophercloud.ServiceClient, policyTypeName string) (r GetResult) { url := policyTypeGetURL(client, policyTypeName) _, r.Err = client.Get(url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000040731335005366400346110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/policytypespackage policytypes import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // PolicyType represents a clustering policy type in the Openstack cloud. type PolicyType struct { Name string `json:"name"` Version string `json:"version"` SupportStatus map[string][]SupportStatusType `json:"support_status"` } // SupportStatusType represents the support status information for a // clustering policy type. type SupportStatusType struct { Status string `json:"status"` Since string `json:"since"` } // PolicyTypeDetail represents the detailed policy type information for a // clustering policy type. type PolicyTypeDetail struct { Name string `json:"name"` Schema map[string]interface{} `json:"schema"` SupportStatus map[string][]SupportStatusType `json:"support_status,omitempty"` } // policyTypeResult is the base result of a Policy Type operation. type policyTypeResult struct { gophercloud.Result } // Extract interprets any policyTypeResult result as a PolicyTypeDetail. func (r policyTypeResult) Extract() (*PolicyTypeDetail, error) { var s struct { PolicyType *PolicyTypeDetail `json:"policy_type"` } err := r.ExtractInto(&s) return s.PolicyType, err } // GetResult is the result of a Get operation. Call its Extract method to // interpret it as a PolicyTypeDetail. type GetResult struct { policyTypeResult } // PolicyTypePage contains a single page of all policy types from a List call. type PolicyTypePage struct { pagination.SinglePageBase } // IsEmpty determines if a PolicyType contains any results. func (page PolicyTypePage) IsEmpty() (bool, error) { policyTypes, err := ExtractPolicyTypes(page) return len(policyTypes) == 0, err } // ExtractPolicyTypes returns a slice of PolicyTypes from a List operation. func ExtractPolicyTypes(r pagination.Page) ([]PolicyType, error) { var s struct { PolicyTypes []PolicyType `json:"policy_types"` } err := (r.(PolicyTypePage)).ExtractInto(&s) return s.PolicyTypes, err } testing/000077500000000000000000000000001335005366400342325ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/policytypesdoc.go000066400000000000000000000000551335005366400353260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/policytypes/testing// clustering_policytypes_v1 package testing fixtures.go000066400000000000000000000114151335005366400364340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/policytypes/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/clustering/v1/policytypes" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const FakePolicyTypetoGet = "fake-policytype" const PolicyTypeBody = ` { "policy_types": [ { "name": "senlin.policy.affinity", "version": "1.0", "support_status": { "1.0": [ { "status": "SUPPORTED", "since": "2016.10" } ] } }, { "name": "senlin.policy.health", "version": "1.0", "support_status": { "1.0": [ { "status": "EXPERIMENTAL", "since": "2016.10" } ] } }, { "name": "senlin.policy.scaling", "version": "1.0", "support_status": { "1.0": [ { "status": "SUPPORTED", "since": "2016.04" } ] } }, { "name": "senlin.policy.region_placement", "version": "1.0", "support_status": { "1.0": [ { "status": "EXPERIMENTAL", "since": "2016.04" }, { "status": "SUPPORTED", "since": "2016.10" } ] } } ] } ` const PolicyTypeDetailBody = ` { "policy_type": { "name": "senlin.policy.batch-1.0", "schema": { "max_batch_size": { "default": -1, "description": "Maximum number of nodes that will be updated in parallel.", "required": false, "type": "Integer", "updatable": false }, "min_in_service": { "default": 1, "description": "Minimum number of nodes in service when performing updates.", "required": false, "type": "Integer", "updatable": false }, "pause_time": { "default": 60, "description": "Interval in seconds between update batches if any.", "required": false, "type": "Integer", "updatable": false } }, "support_status": { "1.0": [ { "status": "EXPERIMENTAL", "since": "2017.02" } ] } } } ` var ExpectedPolicyType1 = policytypes.PolicyType{ Name: "senlin.policy.affinity", Version: "1.0", SupportStatus: map[string][]policytypes.SupportStatusType{ "1.0": { { Status: "SUPPORTED", Since: "2016.10", }, }, }, } var ExpectedPolicyType2 = policytypes.PolicyType{ Name: "senlin.policy.health", Version: "1.0", SupportStatus: map[string][]policytypes.SupportStatusType{ "1.0": { { Status: "EXPERIMENTAL", Since: "2016.10", }, }, }, } var ExpectedPolicyType3 = policytypes.PolicyType{ Name: "senlin.policy.scaling", Version: "1.0", SupportStatus: map[string][]policytypes.SupportStatusType{ "1.0": { { Status: "SUPPORTED", Since: "2016.04", }, }, }, } var ExpectedPolicyType4 = policytypes.PolicyType{ Name: "senlin.policy.region_placement", Version: "1.0", SupportStatus: map[string][]policytypes.SupportStatusType{ "1.0": { { Status: "EXPERIMENTAL", Since: "2016.04", }, { Status: "SUPPORTED", Since: "2016.10", }, }, }, } var ExpectedPolicyTypes = []policytypes.PolicyType{ ExpectedPolicyType1, ExpectedPolicyType2, ExpectedPolicyType3, ExpectedPolicyType4, } var ExpectedPolicyTypeDetail = &policytypes.PolicyTypeDetail{ Name: "senlin.policy.batch-1.0", Schema: map[string]interface{}{ "max_batch_size": map[string]interface{}{ "default": float64(-1), "description": "Maximum number of nodes that will be updated in parallel.", "required": false, "type": "Integer", "updatable": false, }, "min_in_service": map[string]interface{}{ "default": float64(1), "description": "Minimum number of nodes in service when performing updates.", "required": false, "type": "Integer", "updatable": false, }, "pause_time": map[string]interface{}{ "default": float64(60), "description": "Interval in seconds between update batches if any.", "required": false, "type": "Integer", "updatable": false, }, }, SupportStatus: map[string][]policytypes.SupportStatusType{ "1.0": []policytypes.SupportStatusType{ { Status: "EXPERIMENTAL", Since: "2017.02", }, }, }, } func HandlePolicyTypeList(t *testing.T) { th.Mux.HandleFunc("/v1/policy-types", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, PolicyTypeBody) }) } func HandlePolicyTypeGet(t *testing.T) { th.Mux.HandleFunc("/v1/policy-types/"+FakePolicyTypetoGet, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, PolicyTypeDetailBody) }) } requests_test.go000066400000000000000000000021241335005366400374720ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/policytypes/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/clustering/v1/policytypes" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListPolicyTypes(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePolicyTypeList(t) count := 0 err := policytypes.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := policytypes.ExtractPolicyTypes(page) if err != nil { t.Errorf("Failed to extract policy types: %v", err) return false, err } th.AssertDeepEquals(t, ExpectedPolicyTypes, actual) return true, nil }) th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestGetPolicyType(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePolicyTypeGet(t) actual, err := policytypes.Get(fake.ServiceClient(), FakePolicyTypetoGet).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedPolicyTypeDetail, actual) } urls.go000066400000000000000000000006141335005366400340720ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/policytypespackage policytypes import "github.com/gophercloud/gophercloud" const ( apiVersion = "v1" apiName = "policy-types" ) func policyTypeListURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName) } func policyTypeGetURL(client *gophercloud.ServiceClient, policyTypeName string) string { return client.ServiceURL(apiVersion, apiName, policyTypeName) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/profiles/000077500000000000000000000000001335005366400320735ustar00rootroot00000000000000doc.go000066400000000000000000000032401335005366400331070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/profiles/* Package profiles provides information and interaction with profiles through the OpenStack Clustering service. Example to Create a Profile networks := []map[string]interface{} { {"network": "test-network"}, } props := map[string]interface{}{ "name": "test_gophercloud_profile", "flavor": "t2.micro", "image": "centos7.3-latest", "networks": networks, "security_groups": "", } createOpts := profiles.CreateOpts { Name: "test_profile", Spec: profiles.Spec{ Type: "os.nova.server", Version: "1.0", Properties: props, }, } profile, err := profiles.Create(serviceClient, createOpts).Extract() if err != nil { panic(err) } fmt.Println("Profile", profile) Example to Get a Profile profile, err := profiles.Get(serviceClient, "profile-name").Extract() if err != nil { panic(err) } fmt.Print("profile", profile) Example to List Profiles listOpts := profiles.ListOpts{ Limit: 2, } profiles.List(serviceClient, listOpts).EachPage(func(page pagination.Page) (bool, error) { allProfiles, err := profiles.ExtractProfiles(page) if err != nil { panic(err) } for _, profile := range allProfiles { fmt.Printf("%+v\n", profile) } return true, nil }) Example to Update a Profile updateOpts := profiles.UpdateOpts{ Name: "new-name", } profile, err := profiles.Update(serviceClient, profileName, updateOpts).Extract() if err != nil { panic(err) } fmt.Print("profile", profile) Example to Delete a Profile profileID := "6dc6d336e3fc4c0a951b5698cd1236ee" err := profiles.Delete(serviceClient, profileID).ExtractErr() if err != nil { panic(err) } */ package profiles requests.go000066400000000000000000000075551335005366400342320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/profilespackage profiles import ( "net/http" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToProfileCreateMap() (map[string]interface{}, error) } // CreateOpts represents options used for creating a profile. type CreateOpts struct { Name string `json:"name" required:"true"` Metadata map[string]interface{} `json:"metadata,omitempty"` Spec Spec `json:"spec" required:"true"` } // ToProfileCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToProfileCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "profile") } // Create requests the creation of a new profile on the server. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToProfileCreateMap() if err != nil { r.Err = err return } var result *http.Response result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) if r.Err == nil { r.Header = result.Header } return } // Get retrieves detail of a single profile. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { var result *http.Response result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) if r.Err == nil { r.Header = result.Header } return } // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToProfileListQuery() (string, error) } // ListOpts represents options used to list profiles. type ListOpts struct { GlobalProject *bool `q:"global_project"` Limit int `q:"limit"` Marker string `q:"marker"` Name string `q:"name"` Sort string `q:"sort"` Type string `q:"type"` } // ToProfileListQuery formats a ListOpts into a query string. func (opts ListOpts) ToProfileListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List instructs OpenStack to provide a list of profiles. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToProfileListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ProfilePage{pagination.LinkedPageBase{PageResult: r}} }) } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToProfileUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents options used to update a profile. type UpdateOpts struct { Metadata map[string]interface{} `json:"metadata,omitempty"` Name string `json:"name,omitempty"` } // ToProfileUpdateMap constructs a request body from UpdateOpts. func (opts UpdateOpts) ToProfileUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "profile") } // Update updates a profile. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToProfileUpdateMap() if err != nil { r.Err = err return r } var result *http.Response result, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) if r.Err == nil { r.Header = result.Header } return } // Delete deletes the specified profile via profile id. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { var result *http.Response result, r.Err = client.Delete(deleteURL(client, id), nil) if r.Err == nil { r.Header = result.Header } return } results.go000066400000000000000000000065351335005366400340550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/profilespackage profiles import ( "encoding/json" "fmt" "strconv" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Profile represent a detailed profile. type Profile struct { CreatedAt time.Time `json:"-"` Domain string `json:"domain"` ID string `json:"id"` Metadata map[string]interface{} `json:"metadata"` Name string `json:"name"` Project string `json:"project"` Spec Spec `json:"spec"` Type string `json:"type"` UpdatedAt time.Time `json:"-"` User string `json:"user"` } func (r *Profile) UnmarshalJSON(b []byte) error { type tmp Profile var s struct { tmp CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Profile(s.tmp) if s.CreatedAt != "" { r.CreatedAt, err = time.Parse(time.RFC3339, s.CreatedAt) if err != nil { return err } } if s.UpdatedAt != "" { r.UpdatedAt, err = time.Parse(time.RFC3339, s.UpdatedAt) if err != nil { return err } } return nil } // Spec represents a profile spec. type Spec struct { Type string `json:"type"` Version string `json:"-"` Properties map[string]interface{} `json:"properties"` } func (r *Spec) UnmarshalJSON(b []byte) error { type tmp Spec var s struct { tmp Version interface{} `json:"version"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Spec(s.tmp) switch t := s.Version.(type) { case float64: if t == 1 { r.Version = fmt.Sprintf("%.1f", t) } else { r.Version = strconv.FormatFloat(t, 'f', -1, 64) } case string: r.Version = t } return nil } // commonResult is the base result of a Profile operation. type commonResult struct { gophercloud.Result } // Extract provides access to Profile returned by the Get and Create functions. func (r commonResult) Extract() (*Profile, error) { var s struct { Profile *Profile `json:"profile"` } err := r.ExtractInto(&s) return s.Profile, err } // CreateResult is the result of a Create operation. Call its Extract // method to interpret it as a Profile. type CreateResult struct { commonResult } // GetResult is the result of a Get operations. Call its Extract // method to interpret it as a Profile. type GetResult struct { commonResult } // UpdateResult is the result of a Update operations. Call its Extract // method to interpret it as a Profile. type UpdateResult struct { commonResult } // DeleteResult is the result from a Delete operation. Call its ExtractErr // method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // ProfilePage contains a single page of all profiles from a List operation. type ProfilePage struct { pagination.LinkedPageBase } // IsEmpty determines if a ProfilePage contains any results. func (page ProfilePage) IsEmpty() (bool, error) { profiles, err := ExtractProfiles(page) return len(profiles) == 0, err } // ExtractProfiles returns a slice of Profiles from the List operation. func ExtractProfiles(r pagination.Page) ([]Profile, error) { var s struct { Profiles []Profile `json:"profiles"` } err := (r.(ProfilePage)).ExtractInto(&s) return s.Profiles, err } testing/000077500000000000000000000000001335005366400334715ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/profilesdoc.go000066400000000000000000000000521335005366400345620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/profiles/testing// clustering_profiles_v1 package testing fixtures.go000066400000000000000000000244001335005366400356710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/profiles/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiles" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const CreateResponse = ` { "profile": { "created_at": "2016-01-03T16:22:23Z", "domain": null, "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", "metadata": {}, "name": "test-profile", "project": "42d9e9663331431f97b75e25136307ff", "spec": { "properties": { "flavor": "t2.small", "image": "centos7.3-latest", "name": "centos_server", "networks": [ { "network": "private-network" } ] }, "type": "os.nova.server", "version": "1.0" }, "type": "os.nova.server-1.0", "updated_at": "2016-01-03T17:22:23Z", "user": "5e5bf8027826429c96af157f68dc9072" } }` var ExpectedCreate = profiles.Profile{ CreatedAt: time.Date(2016, 1, 3, 16, 22, 23, 0, time.UTC), Domain: "", ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", Metadata: map[string]interface{}{}, Name: "test-profile", Project: "42d9e9663331431f97b75e25136307ff", Spec: profiles.Spec{ Properties: map[string]interface{}{ "flavor": "t2.small", "image": "centos7.3-latest", "name": "centos_server", "networks": []interface{}{ map[string]interface{}{"network": "private-network"}, }, }, Type: "os.nova.server", Version: "1.0", }, Type: "os.nova.server-1.0", UpdatedAt: time.Date(2016, 1, 3, 17, 22, 23, 0, time.UTC), User: "5e5bf8027826429c96af157f68dc9072", } const GetResponse = ` { "profile": { "created_at": "2016-01-03T16:22:23Z", "domain": null, "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", "metadata": {}, "name": "pserver", "project": "42d9e9663331431f97b75e25136307ff", "spec": { "properties": { "flavor": 1, "image": "cirros-0.3.4-x86_64-uec", "key_name": "oskey", "name": "cirros_server", "networks": [ { "network": "private" } ] }, "type": "os.nova.server", "version": "1.0" }, "type": "os.nova.server-1.0", "updated_at": null, "user": "5e5bf8027826429c96af157f68dc9072" } }` var ExpectedGet = profiles.Profile{ CreatedAt: time.Date(2016, 1, 3, 16, 22, 23, 0, time.UTC), Domain: "", ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", Metadata: map[string]interface{}{}, Name: "pserver", Project: "42d9e9663331431f97b75e25136307ff", Spec: profiles.Spec{ Properties: map[string]interface{}{ "flavor": float64(1), "image": "cirros-0.3.4-x86_64-uec", "key_name": "oskey", "name": "cirros_server", "networks": []interface{}{ map[string]interface{}{"network": "private"}, }, }, Type: "os.nova.server", Version: "1.0", }, Type: "os.nova.server-1.0", User: "5e5bf8027826429c96af157f68dc9072", } const ListResponse = ` { "profiles": [ { "created_at": "2016-01-03T16:22:23Z", "domain": null, "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", "metadata": {}, "name": "pserver", "project": "42d9e9663331431f97b75e25136307ff", "spec": { "properties": { "flavor": "t2.small", "image": "cirros-0.3.4-x86_64-uec", "key_name": "oskey", "name": "cirros_server", "networks": [ { "network": "private" } ] }, "type": "os.nova.server", "version": 1.0 }, "type": "os.nova.server-1.0", "updated_at": "2016-01-03T17:22:23Z", "user": "5e5bf8027826429c96af157f68dc9072" }, { "created_at": null, "domain": null, "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", "metadata": {}, "name": "pserver", "project": "42d9e9663331431f97b75e25136307ff", "spec": { "properties": { "flavor": "t2.small", "image": "cirros-0.3.4-x86_64-uec", "key_name": "oskey", "name": "cirros_server", "networks": [ { "network": "private" } ] }, "type": "os.nova.server", "version": 1.0 }, "type": "os.nova.server-1.0", "updated_at": null, "user": "5e5bf8027826429c96af157f68dc9072" }, { "created_at": "", "domain": null, "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", "metadata": {}, "name": "pserver", "project": "42d9e9663331431f97b75e25136307ff", "spec": { "properties": { "flavor": "t2.small", "image": "cirros-0.3.4-x86_64-uec", "key_name": "oskey", "name": "cirros_server", "networks": [ { "network": "private" } ] }, "type": "os.nova.server", "version": "1.0" }, "type": "os.nova.server-1.0", "updated_at": "", "user": "5e5bf8027826429c96af157f68dc9072" } ] }` var ExpectedListProfile1 = profiles.Profile{ CreatedAt: time.Date(2016, 1, 3, 16, 22, 23, 0, time.UTC), Domain: "", ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", Metadata: map[string]interface{}{}, Name: "pserver", Project: "42d9e9663331431f97b75e25136307ff", Spec: profiles.Spec{ Properties: map[string]interface{}{ "flavor": "t2.small", "image": "cirros-0.3.4-x86_64-uec", "key_name": "oskey", "name": "cirros_server", "networks": []interface{}{ map[string]interface{}{"network": "private"}, }, }, Type: "os.nova.server", Version: "1.0", }, Type: "os.nova.server-1.0", UpdatedAt: time.Date(2016, 1, 3, 17, 22, 23, 0, time.UTC), User: "5e5bf8027826429c96af157f68dc9072", } var ExpectedListProfile2 = profiles.Profile{ Domain: "", ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", Metadata: map[string]interface{}{}, Name: "pserver", Project: "42d9e9663331431f97b75e25136307ff", Spec: profiles.Spec{ Properties: map[string]interface{}{ "flavor": "t2.small", "image": "cirros-0.3.4-x86_64-uec", "key_name": "oskey", "name": "cirros_server", "networks": []interface{}{ map[string]interface{}{"network": "private"}, }, }, Type: "os.nova.server", Version: "1.0", }, Type: "os.nova.server-1.0", User: "5e5bf8027826429c96af157f68dc9072", } var ExpectedListProfile3 = profiles.Profile{ Domain: "", ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", Metadata: map[string]interface{}{}, Name: "pserver", Project: "42d9e9663331431f97b75e25136307ff", Spec: profiles.Spec{ Properties: map[string]interface{}{ "flavor": "t2.small", "image": "cirros-0.3.4-x86_64-uec", "key_name": "oskey", "name": "cirros_server", "networks": []interface{}{ map[string]interface{}{"network": "private"}, }, }, Type: "os.nova.server", Version: "1.0", }, Type: "os.nova.server-1.0", User: "5e5bf8027826429c96af157f68dc9072", } var ExpectedList = []profiles.Profile{ ExpectedListProfile1, ExpectedListProfile2, ExpectedListProfile3, } const UpdateResponse = ` { "profile": { "created_at": "2016-01-03T16:22:23Z", "domain": null, "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", "metadata": { "foo": "bar" }, "name": "pserver", "project": "42d9e9663331431f97b75e25136307ff", "spec": { "properties": { "flavor": 1, "image": "cirros-0.3.4-x86_64-uec", "key_name": "oskey", "name": "cirros_server", "networks": [ { "network": "private" } ] }, "type": "os.nova.server", "version": "1.0" }, "type": "os.nova.server-1.0", "updated_at": "2016-01-03T17:22:23Z", "user": "5e5bf8027826429c96af157f68dc9072" } }` var ExpectedUpdate = profiles.Profile{ CreatedAt: time.Date(2016, 1, 3, 16, 22, 23, 0, time.UTC), Domain: "", ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", Metadata: map[string]interface{}{"foo": "bar"}, Name: "pserver", Project: "42d9e9663331431f97b75e25136307ff", Spec: profiles.Spec{ Properties: map[string]interface{}{ "flavor": float64(1), "image": "cirros-0.3.4-x86_64-uec", "key_name": "oskey", "name": "cirros_server", "networks": []interface{}{ map[string]interface{}{"network": "private"}, }, }, Type: "os.nova.server", Version: "1.0", }, Type: "os.nova.server-1.0", UpdatedAt: time.Date(2016, 1, 3, 17, 22, 23, 0, time.UTC), User: "5e5bf8027826429c96af157f68dc9072", } func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/profiles", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, CreateResponse) }) } func HandleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/profiles/9e1c6f42-acf5-4688-be2c-8ce954ef0f23", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, GetResponse) }) } func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/profiles", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListResponse) }) } func HandleUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/profiles/9e1c6f42-acf5-4688-be2c-8ce954ef0f23", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, UpdateResponse) }) } func HandleDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/profiles/6dc6d336e3fc4c0a951b5698cd1236ee", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000046521335005366400367410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/profiles/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiles" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreateProfile(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateSuccessfully(t) networks := []map[string]interface{}{ {"network": "private-network"}, } props := map[string]interface{}{ "name": "test_gopher_cloud_profile", "flavor": "t2.small", "image": "centos7.3-latest", "networks": networks, "security_groups": "", } createOpts := &profiles.CreateOpts{ Name: "TestProfile", Spec: profiles.Spec{ Type: "os.nova.server", Version: "1.0", Properties: props, }, } profile, err := profiles.Create(fake.ServiceClient(), createOpts).Extract() if err != nil { t.Errorf("Failed to extract profile: %v", err) } th.AssertDeepEquals(t, ExpectedCreate, *profile) } func TestGetProfile(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t) actual, err := profiles.Get(fake.ServiceClient(), ExpectedGet.ID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedGet, *actual) } func TestListProfiles(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t) var iFalse bool listOpts := profiles.ListOpts{ GlobalProject: &iFalse, } count := 0 err := profiles.List(fake.ServiceClient(), listOpts).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := profiles.ExtractProfiles(page) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedList, actual) return true, nil }) th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page of profiles, got %d pages instead", count) } } func TestUpdateProfile(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateSuccessfully(t) updateOpts := profiles.UpdateOpts{ Name: "pserver", } actual, err := profiles.Update(fake.ServiceClient(), ExpectedUpdate.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedUpdate, *actual) } func TestDeleteProfile(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSuccessfully(t) deleteResult := profiles.Delete(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") th.AssertNoErr(t, deleteResult.ExtractErr()) } urls.go000066400000000000000000000014471335005366400333360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/profilespackage profiles import "github.com/gophercloud/gophercloud" var apiVersion = "v1" var apiName = "profiles" func commonURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName) } func createURL(client *gophercloud.ServiceClient) string { return commonURL(client) } func idURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiVersion, apiName, id) } func getURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } func listURL(client *gophercloud.ServiceClient) string { return commonURL(client) } func updateURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } func deleteURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/profiletypes/000077500000000000000000000000001335005366400327755ustar00rootroot00000000000000doc.go000066400000000000000000000021021335005366400340050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/profiletypes/* Package profiletypes lists all profile types and shows details for a profile type from the OpenStack Clustering Service. Example to List ProfileType err = profiletypes.List(serviceClient).EachPage(func(page pagination.Page) (bool, error) { profileTypes, err := profiletypes.ExtractProfileTypes(page) if err != nil { return false, err } for _, profileType := range profileTypes { fmt.Println("%+v\n", profileType) } return true, nil }) Example to Get a ProfileType profileTypeName := "os.nova.server" profileType, err := profiletypes.Get(clusteringClient, profileTypeName).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", profileType) Example of list operations supported by a profile type serviceClient.Microversion = "1.5" profileTypeName := "os.nova.server-1.0" allPages, err := profiletypes.ListOps(serviceClient, profileTypeName).AllPages() if err != nil { panic(err) } ops, err := profiletypes.ExtractOps(allPages) if err != nil { panic(err) } for _, op := range ops { fmt.Printf("%+v\n", op) } */ package profiletypes requests.go000066400000000000000000000015541335005366400351250ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/profiletypespackage profiletypes import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) return } // List makes a request against the API to list profile types. func List(client *gophercloud.ServiceClient) pagination.Pager { url := listURL(client) return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ProfileTypePage{pagination.LinkedPageBase{PageResult: r}} }) } func ListOps(client *gophercloud.ServiceClient, id string) pagination.Pager { url := listOpsURL(client, id) return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return OperationPage{pagination.SinglePageBase(r)} }) } results.go000066400000000000000000000036031335005366400347500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/profiletypespackage profiletypes import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // commonResult is the response of a base result. type commonResult struct { gophercloud.Result } // GetResult is the response of a Get operations. type GetResult struct { commonResult } type Schema map[string]interface{} type SupportStatus map[string]interface{} type ProfileType struct { Name string `json:"name"` Schema map[string]Schema `json:"schema"` SupportStatus map[string][]SupportStatus `json:"support_status"` } func (r commonResult) Extract() (*ProfileType, error) { var s struct { ProfileType *ProfileType `json:"profile_type"` } err := r.ExtractInto(&s) return s.ProfileType, err } // ExtractProfileTypes provides access to the list of profiles in a page acquired from the List operation. func ExtractProfileTypes(r pagination.Page) ([]ProfileType, error) { var s struct { ProfileTypes []ProfileType `json:"profile_types"` } err := (r.(ProfileTypePage)).ExtractInto(&s) return s.ProfileTypes, err } // ProfileTypePage contains a single page of all profiles from a ListDetails call. type ProfileTypePage struct { pagination.LinkedPageBase } // IsEmpty determines if ExtractProfileTypes contains any results. func (page ProfileTypePage) IsEmpty() (bool, error) { profileTypes, err := ExtractProfileTypes(page) return len(profileTypes) == 0, err } // OperationPage contains a single page of all profile type operations from a ListOps call. type OperationPage struct { pagination.SinglePageBase } // ExtractOps provides access to the list of operations in a page acquired from the ListOps operation. func ExtractOps(r pagination.Page) (map[string]interface{}, error) { var s struct { Operations map[string]interface{} `json:"operations"` } err := (r.(OperationPage)).ExtractInto(&s) return s.Operations, err } testing/000077500000000000000000000000001335005366400343735ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/profiletypesdoc.go000066400000000000000000000000561335005366400354700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/profiletypes/testing// clustering_profiletypes_v1 package testing fixtures.go000066400000000000000000000162261335005366400366020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/profiletypes/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiletypes" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const ProfileTypeRequestID = "req-7328d1b0-9945-456f-b2cd-5166b77d14a8" const ListResponse = ` { "profile_types": [ { "name": "os.nova.server-1.0", "schema": { "context": { "description": "Customized security context for operating containers.", "required": false, "type": "Map", "updatable": false }, "name": { "description": "The name of the container.", "required": false, "type": "Map", "updatable": false } } }, { "name": "os.heat.stack-1.0", "schema": { "context": { "default": {}, "description": "A dictionary for specifying the customized context for stack operations", "required": false, "type": "Map", "updatable": false }, "disable_rollback": { "default": true, "description": "A boolean specifying whether a stack operation can be rolled back.", "required": false, "type": "Boolean", "updatable": true }, "environment": { "default": {}, "description": "A map that specifies the environment used for stack operations.", "required": false, "type": "Map", "updatable": true }, "files": { "default": {}, "description": "Contents of files referenced by the template, if any.", "required": false, "type": "Map", "updatable": true } }, "support_status": { "1.0": [ { "status": "SUPPORTED", "since": "2016.04" } ] } } ] } ` const GetResponse1 = ` { "profile_type": { "name": "os.nova.server-1.0", "schema": { "context": { "description": "Customized security context for operating containers.", "required": false, "type": "Map", "updatable": false }, "name": { "description": "The name of the container.", "required": false, "type": "Map", "updatable": false } } } } ` const GetResponse15 = ` { "profile_type": { "name": "os.heat.stack-1.0", "schema": { "context": { "default": {}, "description": "A dictionary for specifying the customized context for stack operations", "required": false, "type": "Map", "updatable": false }, "disable_rollback": { "default": true, "description": "A boolean specifying whether a stack operation can be rolled back.", "required": false, "type": "Boolean", "updatable": true }, "environment": { "default": {}, "description": "A map that specifies the environment used for stack operations.", "required": false, "type": "Map", "updatable": true }, "files": { "default": {}, "description": "Contents of files referenced by the template, if any.", "required": false, "type": "Map", "updatable": true } }, "support_status": { "1.0": [ { "status": "SUPPORTED", "since": "2016.04" } ] } } } ` var ExpectedProfileType1 = profiletypes.ProfileType{ Name: "os.nova.server-1.0", Schema: map[string]profiletypes.Schema{ "context": { "description": "Customized security context for operating containers.", "required": false, "type": "Map", "updatable": false, }, "name": { "description": "The name of the container.", "required": false, "type": "Map", "updatable": false, }, }, } var ExpectedProfileType15 = profiletypes.ProfileType{ Name: "os.heat.stack-1.0", Schema: map[string]profiletypes.Schema{ "context": { "default": map[string]interface{}{}, "description": "A dictionary for specifying the customized context for stack operations", "required": false, "type": "Map", "updatable": false, }, "disable_rollback": { "default": true, "description": "A boolean specifying whether a stack operation can be rolled back.", "required": false, "type": "Boolean", "updatable": true, }, "environment": { "default": map[string]interface{}{}, "description": "A map that specifies the environment used for stack operations.", "required": false, "type": "Map", "updatable": true, }, "files": { "default": map[string]interface{}{}, "description": "Contents of files referenced by the template, if any.", "required": false, "type": "Map", "updatable": true, }, }, SupportStatus: map[string][]profiletypes.SupportStatus{ "1.0": { { "status": "SUPPORTED", "since": "2016.04", }, }, }, } var ExpectedProfileTypes = []profiletypes.ProfileType{ExpectedProfileType1, ExpectedProfileType15} func HandleList1Successfully(t *testing.T) { th.Mux.HandleFunc("/v1/profile-types", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListResponse) }) } func HandleGet1Successfully(t *testing.T, id string) { th.Mux.HandleFunc("/v1/profile-types/"+id, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.Header().Add("X-OpenStack-Request-ID", ProfileTypeRequestID) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetResponse1) }) } func HandleGet15Successfully(t *testing.T, id string) { th.Mux.HandleFunc("/v1/profile-types/"+id, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.Header().Add("X-OpenStack-Request-ID", ProfileTypeRequestID) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetResponse15) }) } const ProfileTypeName = "os.nova.server-1.0" const ListOpsResponse = ` { "operations": { "pause": { "description": "Pause the server from running.", "parameter": null }, "change_password": { "description": "Change the administrator password.", "parameters": { "admin_pass": { "description": "New password for the administrator.", "required": false, "type": "String" } } } } } ` var ExpectedOps = map[string]interface{}{ "change_password": map[string]interface{}{ "description": "Change the administrator password.", "parameters": map[string]interface{}{ "admin_pass": map[string]interface{}{ "description": "New password for the administrator.", "required": false, "type": "String", }, }, }, "pause": map[string]interface{}{ "description": "Pause the server from running.", "parameter": nil, }, } func HandleListOpsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/profile-types/"+ProfileTypeName+"/ops", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.Header().Add("X-OpenStack-Request-ID", ProfileTypeRequestID) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListOpsResponse) }) } requests_test.go000066400000000000000000000035301335005366400376350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/profiletypes/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiletypes" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListProfileTypes(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleList1Successfully(t) pageCount := 0 err := profiletypes.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { pageCount++ actual, err := profiletypes.ExtractProfileTypes(page) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedProfileTypes, actual) return true, nil }) th.AssertNoErr(t, err) if pageCount != 1 { t.Errorf("Expected 1 page, got %d", pageCount) } } func TestGetProfileType10(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGet1Successfully(t, ExpectedProfileType1.Name) actual, err := profiletypes.Get(fake.ServiceClient(), ExpectedProfileType1.Name).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedProfileType1, *actual) } func TestGetProfileType15(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGet15Successfully(t, ExpectedProfileType15.Name) actual, err := profiletypes.Get(fake.ServiceClient(), ExpectedProfileType15.Name).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedProfileType15, *actual) } func TestListProfileTypesOps(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListOpsSuccessfully(t) allPages, err := profiletypes.ListOps(fake.ServiceClient(), ProfileTypeName).AllPages() th.AssertNoErr(t, err) allPolicyTypes, err := profiletypes.ExtractOps(allPages) th.AssertNoErr(t, err) for k, v := range allPolicyTypes { tools.PrintResource(t, k) tools.PrintResource(t, v) } } urls.go000066400000000000000000000012571335005366400342370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/profiletypespackage profiletypes import "github.com/gophercloud/gophercloud" const ( apiVersion = "v1" apiName = "profile-types" ) func commonURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName) } func profileTypeURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiVersion, apiName, id) } func getURL(client *gophercloud.ServiceClient, id string) string { return profileTypeURL(client, id) } func listURL(client *gophercloud.ServiceClient) string { return commonURL(client) } func listOpsURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiVersion, apiName, id, "ops") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/receivers/000077500000000000000000000000001335005366400322375ustar00rootroot00000000000000doc.go000066400000000000000000000030001335005366400332450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/receivers/* Package receivers provides information and interaction with the receivers through the OpenStack Clustering service. Example to Create a Receiver createOpts := receivers.CreateOpts{ Action: "CLUSTER_DEL_NODES", ClusterID: "b7b870ee-d3c5-4a93-b9d7-846c53b2c2dc", Name: "test_receiver", Type: receivers.WebhookReceiver, } receiver, err := receivers.Create(serviceClient, createOpts).Extract() if err != nil { panic(err) } fmt.Printf("%v\n", receiver) Example to Get a Receiver receiver, err := receivers.Get(serviceClient, "receiver-name").Extract() if err != nil { panic(err) } fmt.Printf("%v\n", receiver) Example to Delete receiver receiverID := "6dc6d336e3fc4c0a951b5698cd1236ee" err := receivers.Delete(serviceClient, receiverID).ExtractErr() if err != nil { panic(err) } fmt.Printf("%v\n", receiver) Example to Update Receiver updateOpts := receivers.UpdateOpts{ Name: "new-name", } receiverID := "6dc6d336e3fc4c0a951b5698cd1236ee" receiver, err := receivers.Update(serviceClient, receiverID, updateOpts).Extract() if err != nil { panic(err) } fmt.Printf("%v\n", receiver) Example to List Receivers listOpts := receivers.ListOpts{ Limit: 2, } receivers.List(serviceClient, listOpts).EachPage(func(page pagination.Page) (bool, error) { allReceivers, err := receivers.ExtractReceivers(page) if err != nil { panic(err) } for _, receiver := range allReceivers { fmt.Printf("%+v\n", receiver) } return true, nil }) */ package receivers requests.go000066400000000000000000000104141335005366400343620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/receiverspackage receivers import ( "net/http" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ReceiverType represents a valid type of receiver type ReceiverType string const ( WebhookReceiver ReceiverType = "webhook" MessageReceiver ReceiverType = "message" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToReceiverCreateMap() (map[string]interface{}, error) } // CreatOpts represents options used to create a receiver. type CreateOpts struct { Name string `json:"name" required:"true"` ClusterID string `json:"cluster_id,omitempty"` Type ReceiverType `json:"type" required:"true"` Action string `json:"action,omitempty"` Actor map[string]interface{} `json:"actor,omitempty"` Params map[string]interface{} `json:"params,omitempty"` } // ToReceiverCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToReceiverCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "receiver") } // Create requests the creation of a new receiver. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToReceiverCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToReceiverUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents options used to update a receiver. type UpdateOpts struct { Name string `json:"name,omitempty"` Action string `json:"action,omitempty"` Params map[string]interface{} `json:"params,omitempty"` } // ToReceiverUpdateMap constructs a request body from UpdateOpts. func (opts UpdateOpts) ToReceiverUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "receiver") } // Update requests the update of a receiver. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToReceiverUpdateMap() if err != nil { r.Err = err return } var result *http.Response result, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) if r.Err == nil { r.Header = result.Header } return } // Get retrieves details of a single receiver. Use Extract to convert its result into a Receiver. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { var result *http.Response result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) if r.Err == nil { r.Header = result.Header } return } // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToReceiverListQuery() (string, error) } // ListOpts represents options used to list recievers. type ListOpts struct { Limit int `q:"limit"` Marker string `q:"marker"` Sort string `q:"sort"` GlobalProject *bool `q:"global_project"` Name string `q:"name"` Type string `q:"type"` ClusterID string `q:"cluster_id"` Action string `q:"action"` User string `q:"user"` } // ToReceiverListQuery formats a ListOpts into a query string. func (opts ListOpts) ToReceiverListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List instructs OpenStack to provide a list of cluster. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToReceiverListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ReceiverPage{pagination.LinkedPageBase{PageResult: r}} }) } // Delete deletes the specified receiver ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } results.go000066400000000000000000000055651335005366400342230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/receiverspackage receivers import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Receiver represent a detailed receiver type Receiver struct { Action string `json:"action"` Actor map[string]interface{} `json:"actor"` Channel map[string]interface{} `json:"channel"` ClusterID string `json:"cluster_id"` CreatedAt time.Time `json:"-"` Domain string `json:"domain"` ID string `json:"id"` Name string `json:"name"` Params map[string]interface{} `json:"params"` Project string `json:"project"` Type string `json:"type"` UpdatedAt time.Time `json:"-"` User string `json:"user"` } func (r *Receiver) UnmarshalJSON(b []byte) error { type tmp Receiver var s struct { tmp CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Receiver(s.tmp) if s.CreatedAt != "" { r.CreatedAt, err = time.Parse(time.RFC3339, s.CreatedAt) if err != nil { return err } } if s.UpdatedAt != "" { r.UpdatedAt, err = time.Parse(time.RFC3339, s.UpdatedAt) if err != nil { return err } } return nil } // commonResult is the response of a base result. type commonResult struct { gophercloud.Result } // Extract interprets any commonResult-based result as a Receiver. func (r commonResult) Extract() (*Receiver, error) { var s struct { Receiver *Receiver `json:"receiver"` } err := r.ExtractInto(&s) return s.Receiver, err } // CreateResult is the result of a Create operation. Call its Extract method // to interpret it as a Receiver. type CreateResult struct { commonResult } // GetResult is the result for of a Get operation. Call its Extract method // to interpret it as a Receiver. type GetResult struct { commonResult } // UpdateResult is the result of a Update operation. Call its Extract method // to interpret it as a Receiver. type UpdateResult struct { commonResult } // DeleteResult is the result from a Delete operation. Call its ExtractErr // method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // ReceiverPage contains a single page of all nodes from a List operation. type ReceiverPage struct { pagination.LinkedPageBase } // IsEmpty determines if a ReceiverPage contains any results. func (page ReceiverPage) IsEmpty() (bool, error) { receivers, err := ExtractReceivers(page) return len(receivers) == 0, err } // ExtractReceivers returns a slice of Receivers from the List operation. func ExtractReceivers(r pagination.Page) ([]Receiver, error) { var s struct { Receivers []Receiver `json:"receivers"` } err := (r.(ReceiverPage)).ExtractInto(&s) return s.Receivers, err } testing/000077500000000000000000000000001335005366400336355ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/receiversdoc.go000066400000000000000000000000531335005366400347270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/receivers/testing// clustering_receivers_v1 package testing fixtures.go000066400000000000000000000142661335005366400360460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/receivers/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/clustering/v1/receivers" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const CreateResponse = ` { "receiver": { "action": "CLUSTER_SCALE_OUT", "actor": { "trust_id": [ "6dc6d336e3fc4c0a951b5698cd1236d9" ] }, "channel": { "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" }, "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", "created_at": "2015-11-04T05:21:41Z", "domain": "Default", "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", "name": "cluster_inflate", "params": { "count": "1" }, "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", "type": "webhook", "updated_at": "2016-11-04T05:21:41Z", "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" } }` var ExpectedReceiver = receivers.Receiver{ Action: "CLUSTER_SCALE_OUT", Actor: map[string]interface{}{ "trust_id": []string{ "6dc6d336e3fc4c0a951b5698cd1236d9", }, }, Channel: map[string]interface{}{ "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1", }, ClusterID: "ae63a10b-4a90-452c-aef1-113a0b255ee3", CreatedAt: time.Date(2015, 11, 4, 5, 21, 41, 0, time.UTC), Domain: "Default", ID: "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", Name: "cluster_inflate", Params: map[string]interface{}{ "count": "1", }, Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", Type: "webhook", UpdatedAt: time.Date(2016, 11, 4, 5, 21, 41, 0, time.UTC), User: "b4ad2d6e18cc2b9c48049f6dbe8a5b3c", } const GetResponse = ` { "receiver": { "action": "CLUSTER_SCALE_OUT", "actor": { "trust_id": [ "6dc6d336e3fc4c0a951b5698cd1236d9" ] }, "channel": { "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" }, "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", "created_at": "2015-11-04T05:21:41Z", "domain": "Default", "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", "name": "cluster_inflate", "params": { "count": "1" }, "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", "type": "webhook", "updated_at": "2016-11-04T05:21:41Z", "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" } }` const UpdateResponse = ` { "receiver": { "action": "CLUSTER_SCALE_OUT", "actor": { "trust_id": [ "6dc6d336e3fc4c0a951b5698cd1236d9" ] }, "channel": { "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" }, "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", "created_at": "2015-06-27T05:09:43Z", "domain": "Default", "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", "name": "cluster_inflate", "params": { "count": "1" }, "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", "type": "webhook", "updated_at": null, "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" } }` var ExpectedUpdateReceiver = receivers.Receiver{ Action: "CLUSTER_SCALE_OUT", Actor: map[string]interface{}{ "trust_id": []string{ "6dc6d336e3fc4c0a951b5698cd1236d9", }, }, Channel: map[string]interface{}{ "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1", }, ClusterID: "ae63a10b-4a90-452c-aef1-113a0b255ee3", CreatedAt: time.Date(2015, 6, 27, 5, 9, 43, 0, time.UTC), Domain: "Default", ID: "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", Name: "cluster_inflate", Params: map[string]interface{}{ "count": "1", }, Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", Type: "webhook", User: "b4ad2d6e18cc2b9c48049f6dbe8a5b3c", } const ListResponse = ` { "receivers": [ { "action": "CLUSTER_SCALE_OUT", "actor": { "trust_id": [ "6dc6d336e3fc4c0a951b5698cd1236d9" ] }, "channel": { "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" }, "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", "created_at": "2015-06-27T05:09:43Z", "domain": "Default", "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", "name": "cluster_inflate", "params": { "count": "1" }, "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", "type": "webhook", "updated_at": null, "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" } ] }` var ExpectedReceiversList = []receivers.Receiver{ExpectedUpdateReceiver} func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/receivers", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, CreateResponse) }) } func HandleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/receivers/573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, GetResponse) }) } func HandleUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/receivers/6dc6d336e3fc4c0a951b5698cd1236ee", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, UpdateResponse) }) } func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/receivers", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestFormValues(t, r, map[string]string{"limit": "2", "sort": "name:asc,status:desc"}) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ListResponse) }) } func HandleDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/receivers/6dc6d336e3fc4c0a951b5698cd1236ee", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000043671335005366400371100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/receivers/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/clustering/v1/receivers" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreateReceiver(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateSuccessfully(t) opts := receivers.CreateOpts{ Name: "cluster_inflate", ClusterID: "ae63a10b-4a90-452c-aef1-113a0b255ee3", Type: receivers.WebhookReceiver, Action: "CLUSTER_SCALE_OUT", Actor: map[string]interface{}{}, Params: map[string]interface{}{}, } actual, err := receivers.Create(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedReceiver, *actual) } func TestGetReceivers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t) actual, err := receivers.Get(fake.ServiceClient(), "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedReceiver, *actual) } func TestUpdateReceiver(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateSuccessfully(t) opts := receivers.UpdateOpts{ Name: "cluster_inflate", Action: "CLUSTER_SCALE_OUT", Params: map[string]interface{}{ "count": "2", }, } actual, err := receivers.Update(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee", opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedUpdateReceiver, *actual) } func TestListReceivers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t) opts := receivers.ListOpts{ Limit: 2, Sort: "name:asc,status:desc", } count := 0 receivers.List(fake.ServiceClient(), opts).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := receivers.ExtractReceivers(page) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedReceiversList, actual) return true, nil }) th.AssertEquals(t, count, 1) } func TestDeleteReceiver(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSuccessfully(t) deleteResult := receivers.Delete(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") th.AssertNoErr(t, deleteResult.ExtractErr()) } urls.go000066400000000000000000000014511335005366400334750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/receiverspackage receivers import "github.com/gophercloud/gophercloud" var apiVersion = "v1" var apiName = "receivers" func commonURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName) } func createURL(client *gophercloud.ServiceClient) string { return commonURL(client) } func idURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiVersion, apiName, id) } func getURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } func updateURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } func listURL(client *gophercloud.ServiceClient) string { return commonURL(client) } func deleteURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/webhooks/000077500000000000000000000000001335005366400320715ustar00rootroot00000000000000doc.go000066400000000000000000000005721335005366400331120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/webhooks/* Package webhooks provides the ability to trigger an action represented by a webhook from the OpenStack Clustering Service. Example to Trigger webhook action result, err := webhooks.Trigger(serviceClient(), "f93f83f6-762b-41b6-b757-80507834d394", webhooks.TriggerOpts{V: "1"}).Extract() if err != nil { panic(err) } fmt.Println("result", result) */ package webhooks requests.go000066400000000000000000000022421335005366400342140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/webhookspackage webhooks import ( "fmt" "net/url" "github.com/gophercloud/gophercloud" ) // TriggerOpts represents options used for triggering an action type TriggerOpts struct { V string `q:"V" required:"true"` Params map[string]string } // TriggerOptsBuilder Query string builder interface for webhooks type TriggerOptsBuilder interface { ToWebhookTriggerQuery() (string, error) } // Query string builder for webhooks func (opts TriggerOpts) ToWebhookTriggerQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) params := q.Query() for k, v := range opts.Params { params.Add(k, v) } q = &url.URL{RawQuery: params.Encode()} return q.String(), err } // Trigger an action represented by a webhook. func Trigger(client *gophercloud.ServiceClient, id string, opts TriggerOptsBuilder) (r TriggerResult) { url := triggerURL(client, id) if opts != nil { query, err := opts.ToWebhookTriggerQuery() if err != nil { r.Err = err return } url += query } else { r.Err = fmt.Errorf("Must contain V for TriggerOpt") return } _, r.Err = client.Post(url, nil, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) return } results.go000066400000000000000000000005431335005366400340440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/webhookspackage webhooks import ( "github.com/gophercloud/gophercloud" ) type commonResult struct { gophercloud.Result } type TriggerResult struct { commonResult } // Extract retrieves the response action func (r commonResult) Extract() (string, error) { var s struct { Action string `json:"action"` } err := r.ExtractInto(&s) return s.Action, err } testing/000077500000000000000000000000001335005366400334675ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/webhooksdoc.go000066400000000000000000000000521335005366400345600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/webhooks/testing// clustering_webhooks_v1 package testing requests_test.go000066400000000000000000000066131335005366400367360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/webhooks/testingpackage testing import ( "fmt" "net/http" "testing" "encoding/json" "github.com/gophercloud/gophercloud/openstack/clustering/v1/webhooks" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestWebhookTrigger(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "action": "290c44fa-c60f-4d75-a0eb-87433ba982a3" }`) }) triggerOpts := webhooks.TriggerOpts{ V: "1", Params: map[string]string{ "foo": "bar", "bar": "baz", }, } result, err := webhooks.Trigger(fake.ServiceClient(), "f93f83f6-762b-41b6-b757-80507834d394", triggerOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, result, "290c44fa-c60f-4d75-a0eb-87433ba982a3") } // Test webhook with params that generates query strings func TestWebhookParams(t *testing.T) { triggerOpts := webhooks.TriggerOpts{ V: "1", Params: map[string]string{ "foo": "bar", "bar": "baz", }, } expected := "?V=1&bar=baz&foo=bar" actual, err := triggerOpts.ToWebhookTriggerQuery() th.AssertNoErr(t, err) th.AssertEquals(t, actual, expected) } // Nagative test case for returning invalid type (integer) for action id func TestWebhooksInvalidAction(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "action": 123 }`) }) triggerOpts := webhooks.TriggerOpts{ V: "1", Params: map[string]string{ "foo": "bar", "bar": "baz", }, } _, err := webhooks.Trigger(fake.ServiceClient(), "f93f83f6-762b-41b6-b757-80507834d394", triggerOpts).Extract() isValid := err.(*json.UnmarshalTypeError) == nil th.AssertEquals(t, false, isValid) } // Negative test case for passing empty TriggerOpt func TestWebhookTriggerInvalidEmptyOpt(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "action": "290c44fa-c60f-4d75-a0eb-87433ba982a3" }`) }) _, err := webhooks.Trigger(fake.ServiceClient(), "f93f83f6-762b-41b6-b757-80507834d394", webhooks.TriggerOpts{}).Extract() if err == nil { t.Errorf("Expected error without V param") } } // Negative test case for passing in nil for TriggerOpt func TestWebhookTriggerInvalidNilOpt(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "action": "290c44fa-c60f-4d75-a0eb-87433ba982a3" }`) }) _, err := webhooks.Trigger(fake.ServiceClient(), "f93f83f6-762b-41b6-b757-80507834d394", nil).Extract() if err == nil { t.Errorf("Expected error with nil param") } } urls.go000066400000000000000000000003031335005366400333220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/clustering/v1/webhookspackage webhooks import "github.com/gophercloud/gophercloud" func triggerURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("v1", "webhooks", id, "trigger") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/common/000077500000000000000000000000001335005366400270335ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/common/extensions/000077500000000000000000000000001335005366400312325ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/common/extensions/doc.go000066400000000000000000000034571335005366400323370ustar00rootroot00000000000000/* Package extensions provides information and interaction with the different extensions available for an OpenStack service. The purpose of OpenStack API extensions is to: - Introduce new features in the API without requiring a version change. - Introduce vendor-specific niche functionality. - Act as a proving ground for experimental functionalities that might be included in a future version of the API. Extensions usually have tags that prevent conflicts with other extensions that define attributes or resources with the same names, and with core resources and attributes. Because an extension might not be supported by all plug-ins, its availability varies with deployments and the specific plug-in. The results of this package vary depending on the type of Service Client used. In the following examples, note how the only difference is the creation of the Service Client. Example of Retrieving Compute Extensions ao, err := openstack.AuthOptionsFromEnv() provider, err := openstack.AuthenticatedClient(ao) computeClient, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) allPages, err := extensions.List(computeClient).Allpages() allExtensions, err := extensions.ExtractExtensions(allPages) for _, extension := range allExtensions{ fmt.Println("%+v\n", extension) } Example of Retrieving Network Extensions ao, err := openstack.AuthOptionsFromEnv() provider, err := openstack.AuthenticatedClient(ao) networkClient, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) allPages, err := extensions.List(networkClient).Allpages() allExtensions, err := extensions.ExtractExtensions(allPages) for _, extension := range allExtensions{ fmt.Println("%+v\n", extension) } */ package extensions requests.go000077500000000000000000000012441335005366400333610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/common/extensionspackage extensions import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Get retrieves information for a specific extension using its alias. func Get(c *gophercloud.ServiceClient, alias string) (r GetResult) { _, r.Err = c.Get(ExtensionURL(c, alias), &r.Body, nil) return } // List returns a Pager which allows you to iterate over the full collection of extensions. // It does not accept query parameters. func List(c *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(c, ListExtensionURL(c), func(r pagination.PageResult) pagination.Page { return ExtensionPage{pagination.SinglePageBase(r)} }) } results.go000066400000000000000000000031041335005366400332010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/common/extensionspackage extensions import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // GetResult temporarily stores the result of a Get call. // Use its Extract() method to interpret it as an Extension. type GetResult struct { gophercloud.Result } // Extract interprets a GetResult as an Extension. func (r GetResult) Extract() (*Extension, error) { var s struct { Extension *Extension `json:"extension"` } err := r.ExtractInto(&s) return s.Extension, err } // Extension is a struct that represents an OpenStack extension. type Extension struct { Updated string `json:"updated"` Name string `json:"name"` Links []interface{} `json:"links"` Namespace string `json:"namespace"` Alias string `json:"alias"` Description string `json:"description"` } // ExtensionPage is the page returned by a pager when traversing over a collection of extensions. type ExtensionPage struct { pagination.SinglePageBase } // IsEmpty checks whether an ExtensionPage struct is empty. func (r ExtensionPage) IsEmpty() (bool, error) { is, err := ExtractExtensions(r) return len(is) == 0, err } // ExtractExtensions accepts a Page struct, specifically an ExtensionPage // struct, and extracts the elements into a slice of Extension structs. // In other words, a generic collection is mapped into a relevant slice. func ExtractExtensions(r pagination.Page) ([]Extension, error) { var s struct { Extensions []Extension `json:"extensions"` } err := (r.(ExtensionPage)).ExtractInto(&s) return s.Extensions, err } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/common/extensions/testing/000077500000000000000000000000001335005366400327075ustar00rootroot00000000000000doc.go000066400000000000000000000000601335005366400337200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/common/extensions/testing// common extensions unit tests package testing fixtures.go000066400000000000000000000054611335005366400350360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/common/extensions/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/common/extensions" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListOutput provides a single page of Extension results. const ListOutput = ` { "extensions": [ { "updated": "2013-01-20T00:00:00-00:00", "name": "Neutron Service Type Management", "links": [], "namespace": "http://docs.openstack.org/ext/neutron/service-type/api/v1.0", "alias": "service-type", "description": "API for retrieving service providers for Neutron advanced services" } ] }` // GetOutput provides a single Extension result. const GetOutput = ` { "extension": { "updated": "2013-02-03T10:00:00-00:00", "name": "agent", "links": [], "namespace": "http://docs.openstack.org/ext/agent/api/v2.0", "alias": "agent", "description": "The agent management extension." } } ` // ListedExtension is the Extension that should be parsed from ListOutput. var ListedExtension = extensions.Extension{ Updated: "2013-01-20T00:00:00-00:00", Name: "Neutron Service Type Management", Links: []interface{}{}, Namespace: "http://docs.openstack.org/ext/neutron/service-type/api/v1.0", Alias: "service-type", Description: "API for retrieving service providers for Neutron advanced services", } // ExpectedExtensions is a slice containing the Extension that should be parsed from ListOutput. var ExpectedExtensions = []extensions.Extension{ListedExtension} // SingleExtension is the Extension that should be parsed from GetOutput. var SingleExtension = &extensions.Extension{ Updated: "2013-02-03T10:00:00-00:00", Name: "agent", Links: []interface{}{}, Namespace: "http://docs.openstack.org/ext/agent/api/v2.0", Alias: "agent", Description: "The agent management extension.", } // HandleListExtensionsSuccessfully creates an HTTP handler at `/extensions` on the test handler // mux that response with a list containing a single tenant. func HandleListExtensionsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/extensions", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ListOutput) }) } // HandleGetExtensionSuccessfully creates an HTTP handler at `/extensions/agent` that responds with // a JSON payload corresponding to SingleExtension. func HandleGetExtensionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/extensions/agent", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetOutput) }) } requests_test.go000066400000000000000000000016421335005366400360740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/common/extensions/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/common/extensions" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListExtensionsSuccessfully(t) count := 0 extensions.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := extensions.ExtractExtensions(page) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedExtensions, actual) return true, nil }) th.CheckEquals(t, 1, count) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetExtensionSuccessfully(t) actual, err := extensions.Get(client.ServiceClient(), "agent").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SingleExtension, actual) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/common/extensions/urls.go000066400000000000000000000006501335005366400325470ustar00rootroot00000000000000package extensions import "github.com/gophercloud/gophercloud" // ExtensionURL generates the URL for an extension resource by name. func ExtensionURL(c *gophercloud.ServiceClient, name string) string { return c.ServiceURL("extensions", name) } // ListExtensionURL generates the URL for the extensions resource collection. func ListExtensionURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("extensions") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/000077500000000000000000000000001335005366400272175ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/000077500000000000000000000000001335005366400275465ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/000077500000000000000000000000001335005366400317455ustar00rootroot00000000000000aggregates/000077500000000000000000000000001335005366400337775ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000041111335005366400350700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/aggregates/* Package aggregates manages information about the host aggregates in the OpenStack cloud. Example of Create Aggregate opts := aggregates.CreateOpts{ Name: "name", AvailabilityZone: "london", } aggregate, err := aggregates.Create(computeClient, opts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", aggregate) Example of Show Aggregate Details aggregateID := 42 aggregate, err := aggregates.Get(computeClient, aggregateID).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", aggregate) Example of Delete Aggregate aggregateID := 32 err := aggregates.Delete(computeClient, aggregateID).ExtractErr() if err != nil { panic(err) } Example of Update Aggregate aggregateID := 42 opts := aggregates.UpdateOpts{ Name: "new_name", AvailabilityZone: "nova2", } aggregate, err := aggregates.Update(computeClient, aggregateID, opts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", aggregate) Example of Retrieving list of all aggregates allPages, err := aggregates.List(computeClient).AllPages() if err != nil { panic(err) } allAggregates, err := aggregates.ExtractAggregates(allPages) if err != nil { panic(err) } for _, aggregate := range allAggregates { fmt.Printf("%+v\n", aggregate) } Example of Add Host aggregateID := 22 opts := aggregates.AddHostOpts{ Host: "newhost-cmp1", } aggregate, err := aggregates.AddHost(computeClient, aggregateID, opts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", aggregate) Example of Remove Host aggregateID := 22 opts := aggregates.RemoveHostOpts{ Host: "newhost-cmp1", } aggregate, err := aggregates.RemoveHost(computeClient, aggregateID, opts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", aggregate) Example of Create or Update Metadata aggregateID := 22 opts := aggregates.SetMetadata{ Metadata: map[string]string{"key": "value"}, } aggregate, err := aggregates.SetMetadata(computeClient, aggregateID, opts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", aggregate) */ package aggregates requests.go000066400000000000000000000115401335005366400362020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/aggregatespackage aggregates import ( "strconv" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List makes a request against the API to list aggregates. func List(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, aggregatesListURL(client), func(r pagination.PageResult) pagination.Page { return AggregatesPage{pagination.SinglePageBase(r)} }) } type CreateOpts struct { // The name of the host aggregate. Name string `json:"name" required:"true"` // The availability zone of the host aggregate. // You should use a custom availability zone rather than // the default returned by the os-availability-zone API. // The availability zone must not include ‘:’ in its name. AvailabilityZone string `json:"availability_zone,omitempty"` } func (opts CreateOpts) ToAggregatesCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "aggregate") } // Create makes a request against the API to create an aggregate. func Create(client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { b, err := opts.ToAggregatesCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(aggregatesCreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete makes a request against the API to delete an aggregate. func Delete(client *gophercloud.ServiceClient, aggregateID int) (r DeleteResult) { v := strconv.Itoa(aggregateID) _, r.Err = client.Delete(aggregatesDeleteURL(client, v), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Get makes a request against the API to get details for a specific aggregate. func Get(client *gophercloud.ServiceClient, aggregateID int) (r GetResult) { v := strconv.Itoa(aggregateID) _, r.Err = client.Get(aggregatesGetURL(client, v), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } type UpdateOpts struct { // The name of the host aggregate. Name string `json:"name,omitempty"` // The availability zone of the host aggregate. // You should use a custom availability zone rather than // the default returned by the os-availability-zone API. // The availability zone must not include ‘:’ in its name. AvailabilityZone string `json:"availability_zone,omitempty"` } func (opts UpdateOpts) ToAggregatesUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "aggregate") } // Update makes a request against the API to update a specific aggregate. func Update(client *gophercloud.ServiceClient, aggregateID int, opts UpdateOpts) (r UpdateResult) { v := strconv.Itoa(aggregateID) b, err := opts.ToAggregatesUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Put(aggregatesUpdateURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } type AddHostOpts struct { // The name of the host. Host string `json:"host" required:"true"` } func (opts AddHostOpts) ToAggregatesAddHostMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "add_host") } // AddHost makes a request against the API to add host to a specific aggregate. func AddHost(client *gophercloud.ServiceClient, aggregateID int, opts AddHostOpts) (r ActionResult) { v := strconv.Itoa(aggregateID) b, err := opts.ToAggregatesAddHostMap() if err != nil { r.Err = err return } _, r.Err = client.Post(aggregatesAddHostURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } type RemoveHostOpts struct { // The name of the host. Host string `json:"host" required:"true"` } func (opts RemoveHostOpts) ToAggregatesRemoveHostMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "remove_host") } // RemoveHost makes a request against the API to remove host from a specific aggregate. func RemoveHost(client *gophercloud.ServiceClient, aggregateID int, opts RemoveHostOpts) (r ActionResult) { v := strconv.Itoa(aggregateID) b, err := opts.ToAggregatesRemoveHostMap() if err != nil { r.Err = err return } _, r.Err = client.Post(aggregatesRemoveHostURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } type SetMetadataOpts struct { Metadata map[string]interface{} `json:"metadata" required:"true"` } func (opts SetMetadataOpts) ToSetMetadataMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "set_metadata") } // SetMetadata makes a request against the API to set metadata to a specific aggregate. func SetMetadata(client *gophercloud.ServiceClient, aggregateID int, opts SetMetadataOpts) (r ActionResult) { v := strconv.Itoa(aggregateID) b, err := opts.ToSetMetadataMap() if err != nil { r.Err = err return } _, r.Err = client.Post(aggregatesSetMetadataURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000054161335005366400360350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/aggregatespackage aggregates import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Aggregate represents a host aggregate in the OpenStack cloud. type Aggregate struct { // The availability zone of the host aggregate. AvailabilityZone string `json:"availability_zone"` // A list of host ids in this aggregate. Hosts []string `json:"hosts"` // The ID of the host aggregate. ID int `json:"id"` // Metadata key and value pairs associate with the aggregate. Metadata map[string]string `json:"metadata"` // Name of the aggregate. Name string `json:"name"` // The date and time when the resource was created. CreatedAt time.Time `json:"-"` // The date and time when the resource was updated, // if the resource has not been updated, this field will show as null. UpdatedAt time.Time `json:"-"` // The date and time when the resource was deleted, // if the resource has not been deleted yet, this field will be null. DeletedAt time.Time `json:"-"` // A boolean indicates whether this aggregate is deleted or not, // if it has not been deleted, false will appear. Deleted bool `json:"deleted"` } // UnmarshalJSON to override default func (r *Aggregate) UnmarshalJSON(b []byte) error { type tmp Aggregate var s struct { tmp CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` DeletedAt gophercloud.JSONRFC3339MilliNoZ `json:"deleted_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Aggregate(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) r.UpdatedAt = time.Time(s.UpdatedAt) r.DeletedAt = time.Time(s.DeletedAt) return nil } // AggregatesPage represents a single page of all Aggregates from a List // request. type AggregatesPage struct { pagination.SinglePageBase } // IsEmpty determines whether or not a page of Aggregates contains any results. func (page AggregatesPage) IsEmpty() (bool, error) { aggregates, err := ExtractAggregates(page) return len(aggregates) == 0, err } // ExtractAggregates interprets a page of results as a slice of Aggregates. func ExtractAggregates(p pagination.Page) ([]Aggregate, error) { var a struct { Aggregates []Aggregate `json:"aggregates"` } err := (p.(AggregatesPage)).ExtractInto(&a) return a.Aggregates, err } type aggregatesResult struct { gophercloud.Result } func (r aggregatesResult) Extract() (*Aggregate, error) { var s struct { Aggregate *Aggregate `json:"aggregate"` } err := r.ExtractInto(&s) return s.Aggregate, err } type CreateResult struct { aggregatesResult } type GetResult struct { aggregatesResult } type DeleteResult struct { gophercloud.ErrResult } type UpdateResult struct { aggregatesResult } type ActionResult struct { aggregatesResult } testing/000077500000000000000000000000001335005366400354545ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/aggregatesfixtures.go000066400000000000000000000232711335005366400376610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/aggregates/testingpackage testing import ( "fmt" "net/http" "strconv" "testing" "time" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // AggregateListBody is sample response to the List call const AggregateListBody = ` { "aggregates": [ { "name": "test-aggregate1", "availability_zone": null, "deleted": false, "created_at": "2017-12-22T10:12:06.000000", "updated_at": null, "hosts": [], "deleted_at": null, "id": 1, "metadata": {} }, { "name": "test-aggregate2", "availability_zone": "test-az", "deleted": false, "created_at": "2017-12-22T10:16:07.000000", "updated_at": null, "hosts": [ "cmp0" ], "deleted_at": null, "id": 4, "metadata": { "availability_zone": "test-az" } } ] } ` const AggregateCreateBody = ` { "aggregate": { "availability_zone": "london", "created_at": "2016-12-27T22:51:32.000000", "deleted": false, "deleted_at": null, "id": 32, "name": "name", "updated_at": null } } ` const AggregateGetBody = ` { "aggregate": { "name": "test-aggregate2", "availability_zone": "test-az", "deleted": false, "created_at": "2017-12-22T10:16:07.000000", "updated_at": null, "hosts": [ "cmp0" ], "deleted_at": null, "id": 4, "metadata": { "availability_zone": "test-az" } } } ` const AggregateUpdateBody = ` { "aggregate": { "name": "test-aggregate2", "availability_zone": "nova2", "deleted": false, "created_at": "2017-12-22T10:12:06.000000", "updated_at": "2017-12-23T10:18:00.000000", "hosts": [], "deleted_at": null, "id": 1, "metadata": { "availability_zone": "nova2" } } } ` const AggregateAddHostBody = ` { "aggregate": { "name": "test-aggregate2", "availability_zone": "test-az", "deleted": false, "created_at": "2017-12-22T10:16:07.000000", "updated_at": null, "hosts": [ "cmp0", "cmp1" ], "deleted_at": null, "id": 4, "metadata": { "availability_zone": "test-az" } } } ` const AggregateRemoveHostBody = ` { "aggregate": { "name": "test-aggregate2", "availability_zone": "nova2", "deleted": false, "created_at": "2017-12-22T10:12:06.000000", "updated_at": "2017-12-23T10:18:00.000000", "hosts": [], "deleted_at": null, "id": 1, "metadata": { "availability_zone": "nova2" } } } ` const AggregateSetMetadataBody = ` { "aggregate": { "name": "test-aggregate2", "availability_zone": "test-az", "deleted": false, "created_at": "2017-12-22T10:16:07.000000", "updated_at": "2017-12-23T10:18:00.000000", "hosts": [ "cmp0" ], "deleted_at": null, "id": 4, "metadata": { "availability_zone": "test-az", "key": "value" } } } ` var ( // First aggregate from the AggregateListBody FirstFakeAggregate = aggregates.Aggregate{ AvailabilityZone: "", Hosts: []string{}, ID: 1, Metadata: map[string]string{}, Name: "test-aggregate1", CreatedAt: time.Date(2017, 12, 22, 10, 12, 6, 0, time.UTC), UpdatedAt: time.Time{}, DeletedAt: time.Time{}, Deleted: false, } // Second aggregate from the AggregateListBody SecondFakeAggregate = aggregates.Aggregate{ AvailabilityZone: "test-az", Hosts: []string{"cmp0"}, ID: 4, Metadata: map[string]string{"availability_zone": "test-az"}, Name: "test-aggregate2", CreatedAt: time.Date(2017, 12, 22, 10, 16, 7, 0, time.UTC), UpdatedAt: time.Time{}, DeletedAt: time.Time{}, Deleted: false, } // Aggregate from the AggregateCreateBody CreatedAggregate = aggregates.Aggregate{ AvailabilityZone: "london", Hosts: nil, ID: 32, Metadata: nil, Name: "name", CreatedAt: time.Date(2016, 12, 27, 22, 51, 32, 0, time.UTC), UpdatedAt: time.Time{}, DeletedAt: time.Time{}, Deleted: false, } // Aggregate ID to delete AggregateIDtoDelete = 1 // Aggregate ID to get, from the AggregateGetBody AggregateIDtoGet = SecondFakeAggregate.ID // Aggregate ID to update AggregateIDtoUpdate = FirstFakeAggregate.ID // Updated aggregate UpdatedAggregate = aggregates.Aggregate{ AvailabilityZone: "nova2", Hosts: []string{}, ID: 1, Metadata: map[string]string{"availability_zone": "nova2"}, Name: "test-aggregate2", CreatedAt: time.Date(2017, 12, 22, 10, 12, 6, 0, time.UTC), UpdatedAt: time.Date(2017, 12, 23, 10, 18, 0, 0, time.UTC), DeletedAt: time.Time{}, Deleted: false, } AggregateWithAddedHost = aggregates.Aggregate{ AvailabilityZone: "test-az", Hosts: []string{"cmp0", "cmp1"}, ID: 4, Metadata: map[string]string{"availability_zone": "test-az"}, Name: "test-aggregate2", CreatedAt: time.Date(2017, 12, 22, 10, 16, 7, 0, time.UTC), UpdatedAt: time.Time{}, DeletedAt: time.Time{}, Deleted: false, } AggregateWithRemovedHost = aggregates.Aggregate{ AvailabilityZone: "nova2", Hosts: []string{}, ID: 1, Metadata: map[string]string{"availability_zone": "nova2"}, Name: "test-aggregate2", CreatedAt: time.Date(2017, 12, 22, 10, 12, 6, 0, time.UTC), UpdatedAt: time.Date(2017, 12, 23, 10, 18, 0, 0, time.UTC), DeletedAt: time.Time{}, Deleted: false, } AggregateWithUpdatedMetadata = aggregates.Aggregate{ AvailabilityZone: "test-az", Hosts: []string{"cmp0"}, ID: 4, Metadata: map[string]string{"availability_zone": "test-az", "key": "value"}, Name: "test-aggregate2", CreatedAt: time.Date(2017, 12, 22, 10, 16, 7, 0, time.UTC), UpdatedAt: time.Date(2017, 12, 23, 10, 18, 0, 0, time.UTC), DeletedAt: time.Time{}, Deleted: false, } ) // HandleListSuccessfully configures the test server to respond to a List request. func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-aggregates", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, AggregateListBody) }) } func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-aggregates", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, AggregateCreateBody) }) } func HandleDeleteSuccessfully(t *testing.T) { v := strconv.Itoa(AggregateIDtoDelete) th.Mux.HandleFunc("/os-aggregates/"+v, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusOK) }) } func HandleGetSuccessfully(t *testing.T) { v := strconv.Itoa(AggregateIDtoGet) th.Mux.HandleFunc("/os-aggregates/"+v, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, AggregateGetBody) }) } func HandleUpdateSuccessfully(t *testing.T) { v := strconv.Itoa(AggregateIDtoUpdate) th.Mux.HandleFunc("/os-aggregates/"+v, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, AggregateUpdateBody) }) } func HandleAddHostSuccessfully(t *testing.T) { v := strconv.Itoa(AggregateWithAddedHost.ID) th.Mux.HandleFunc("/os-aggregates/"+v+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, AggregateAddHostBody) }) } func HandleRemoveHostSuccessfully(t *testing.T) { v := strconv.Itoa(AggregateWithRemovedHost.ID) th.Mux.HandleFunc("/os-aggregates/"+v+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, AggregateRemoveHostBody) }) } func HandleSetMetadataSuccessfully(t *testing.T) { v := strconv.Itoa(AggregateWithUpdatedMetadata.ID) th.Mux.HandleFunc("/os-aggregates/"+v+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, AggregateSetMetadataBody) }) } requests_test.go000066400000000000000000000064761335005366400407320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/aggregates/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListAggregates(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t) pages := 0 err := aggregates.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := aggregates.ExtractAggregates(page) if err != nil { return false, err } if len(actual) != 2 { t.Fatalf("Expected 2 aggregates, got %d", len(actual)) } th.CheckDeepEquals(t, FirstFakeAggregate, actual[0]) th.CheckDeepEquals(t, SecondFakeAggregate, actual[1]) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestCreateAggregates(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateSuccessfully(t) expected := CreatedAggregate opts := aggregates.CreateOpts{ Name: "name", AvailabilityZone: "london", } actual, err := aggregates.Create(client.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) } func TestDeleteAggregates(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSuccessfully(t) err := aggregates.Delete(client.ServiceClient(), AggregateIDtoDelete).ExtractErr() th.AssertNoErr(t, err) } func TestGetAggregates(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t) expected := SecondFakeAggregate actual, err := aggregates.Get(client.ServiceClient(), AggregateIDtoGet).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) } func TestUpdateAggregate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateSuccessfully(t) expected := UpdatedAggregate opts := aggregates.UpdateOpts{ Name: "test-aggregates2", AvailabilityZone: "nova2", } actual, err := aggregates.Update(client.ServiceClient(), expected.ID, opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) } func TestAddHostAggregate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAddHostSuccessfully(t) expected := AggregateWithAddedHost opts := aggregates.AddHostOpts{ Host: "cmp1", } actual, err := aggregates.AddHost(client.ServiceClient(), expected.ID, opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) } func TestRemoveHostAggregate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleRemoveHostSuccessfully(t) expected := AggregateWithRemovedHost opts := aggregates.RemoveHostOpts{ Host: "cmp1", } actual, err := aggregates.RemoveHost(client.ServiceClient(), expected.ID, opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) } func TestSetMetadataAggregate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleSetMetadataSuccessfully(t) expected := AggregateWithUpdatedMetadata opts := aggregates.SetMetadataOpts{ Metadata: map[string]interface{}{"key": "value"}, } actual, err := aggregates.SetMetadata(client.ServiceClient(), expected.ID, opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) } urls.go000066400000000000000000000021611335005366400353130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/aggregatespackage aggregates import "github.com/gophercloud/gophercloud" func aggregatesListURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-aggregates") } func aggregatesCreateURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-aggregates") } func aggregatesDeleteURL(c *gophercloud.ServiceClient, aggregateID string) string { return c.ServiceURL("os-aggregates", aggregateID) } func aggregatesGetURL(c *gophercloud.ServiceClient, aggregateID string) string { return c.ServiceURL("os-aggregates", aggregateID) } func aggregatesUpdateURL(c *gophercloud.ServiceClient, aggregateID string) string { return c.ServiceURL("os-aggregates", aggregateID) } func aggregatesAddHostURL(c *gophercloud.ServiceClient, aggregateID string) string { return c.ServiceURL("os-aggregates", aggregateID, "action") } func aggregatesRemoveHostURL(c *gophercloud.ServiceClient, aggregateID string) string { return c.ServiceURL("os-aggregates", aggregateID, "action") } func aggregatesSetMetadataURL(c *gophercloud.ServiceClient, aggregateID string) string { return c.ServiceURL("os-aggregates", aggregateID, "action") } attachinterfaces/000077500000000000000000000000001335005366400351765ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000025761335005366400363040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/attachinterfaces/* Package attachinterfaces provides the ability to retrieve and manage network interfaces through Nova. Example of Listing a Server's Interfaces serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" allPages, err := attachinterfaces.List(computeClient, serverID).AllPages() if err != nil { panic(err) } allInterfaces, err := attachinterfaces.ExtractInterfaces(allPages) if err != nil { panic(err) } for _, interface := range allInterfaces { fmt.Printf("%+v\n", interface) } Example to Get a Server's Interface portID = "0dde1598-b374-474e-986f-5b8dd1df1d4e" serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" interface, err := attachinterfaces.Get(computeClient, serverID, portID).Extract() if err != nil { panic(err) } Example to Create a new Interface attachment on the Server networkID := "8a5fe506-7e9f-4091-899b-96336909d93c" serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" attachOpts := attachinterfaces.CreateOpts{ NetworkID: networkID, } interface, err := attachinterfaces.Create(computeClient, serverID, attachOpts).Extract() if err != nil { panic(err) } Example to Delete an Interface attachment from the Server portID = "0dde1598-b374-474e-986f-5b8dd1df1d4e" serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" err := attachinterfaces.Delete(computeClient, serverID, portID).ExtractErr() if err != nil { panic(err) } */ package attachinterfaces requests.go000066400000000000000000000056611335005366400374100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/attachinterfacespackage attachinterfaces import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List makes a request against the nova API to list the server's interfaces. func List(client *gophercloud.ServiceClient, serverID string) pagination.Pager { return pagination.NewPager(client, listInterfaceURL(client, serverID), func(r pagination.PageResult) pagination.Page { return InterfacePage{pagination.SinglePageBase(r)} }) } // Get requests details on a single interface attachment by the server and port IDs. func Get(client *gophercloud.ServiceClient, serverID, portID string) (r GetResult) { _, r.Err = client.Get(getInterfaceURL(client, serverID, portID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToAttachInterfacesCreateMap() (map[string]interface{}, error) } // CreateOpts specifies parameters of a new interface attachment. type CreateOpts struct { // PortID is the ID of the port for which you want to create an interface. // The NetworkID and PortID parameters are mutually exclusive. // If you do not specify the PortID parameter, the OpenStack Networking API // v2.0 allocates a port and creates an interface for it on the network. PortID string `json:"port_id,omitempty"` // NetworkID is the ID of the network for which you want to create an interface. // The NetworkID and PortID parameters are mutually exclusive. // If you do not specify the NetworkID parameter, the OpenStack Networking // API v2.0 uses the network information cache that is associated with the instance. NetworkID string `json:"net_id,omitempty"` // Slice of FixedIPs. If you request a specific FixedIP address without a // NetworkID, the request returns a Bad Request (400) response code. // Note: this uses the FixedIP struct, but only the IPAddress field can be used. FixedIPs []FixedIP `json:"fixed_ips,omitempty"` } // ToAttachInterfacesCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToAttachInterfacesCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "interfaceAttachment") } // Create requests the creation of a new interface attachment on the server. func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToAttachInterfacesCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createInterfaceURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete makes a request against the nova API to detach a single interface from the server. // It needs server and port IDs to make a such request. func Delete(client *gophercloud.ServiceClient, serverID, portID string) (r DeleteResult) { _, r.Err = client.Delete(deleteInterfaceURL(client, serverID, portID), nil) return } results.go000066400000000000000000000045061335005366400372330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/attachinterfacespackage attachinterfaces import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type attachInterfaceResult struct { gophercloud.Result } // Extract interprets any attachInterfaceResult as an Interface, if possible. func (r attachInterfaceResult) Extract() (*Interface, error) { var s struct { Interface *Interface `json:"interfaceAttachment"` } err := r.ExtractInto(&s) return s.Interface, err } // GetResult is the response from a Get operation. Call its Extract // method to interpret it as an Interface. type GetResult struct { attachInterfaceResult } // CreateResult is the response from a Create operation. Call its Extract // method to interpret it as an Interface. type CreateResult struct { attachInterfaceResult } // DeleteResult is the response from a Delete operation. Call its ExtractErr // method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // FixedIP represents a Fixed IP Address. // This struct is also used when creating an attachment, // but it is not possible to specify a SubnetID. type FixedIP struct { SubnetID string `json:"subnet_id,omitempty"` IPAddress string `json:"ip_address"` } // Interface represents a network interface on a server. type Interface struct { PortState string `json:"port_state"` FixedIPs []FixedIP `json:"fixed_ips"` PortID string `json:"port_id"` NetID string `json:"net_id"` MACAddr string `json:"mac_addr"` } // InterfacePage abstracts the raw results of making a List() request against // the API. // // As OpenStack extensions may freely alter the response bodies of structures // returned to the client, you may only safely access the data provided through // the ExtractInterfaces call. type InterfacePage struct { pagination.SinglePageBase } // IsEmpty returns true if an InterfacePage contains no interfaces. func (r InterfacePage) IsEmpty() (bool, error) { interfaces, err := ExtractInterfaces(r) return len(interfaces) == 0, err } // ExtractInterfaces interprets the results of a single page from a List() call, // producing a slice of Interface structs. func ExtractInterfaces(r pagination.Page) ([]Interface, error) { var s struct { Interfaces []Interface `json:"interfaceAttachments"` } err := (r.(InterfacePage)).ExtractInto(&s) return s.Interfaces, err } testing/000077500000000000000000000000001335005366400366535ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/attachinterfacesdoc.go000066400000000000000000000000571335005366400377510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/attachinterfaces/testing// attachinterfaces unit tests package testing fixtures.go000066400000000000000000000116101335005366400410520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/attachinterfaces/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListInterfacesExpected represents an expected repsonse from a ListInterfaces request. var ListInterfacesExpected = []attachinterfaces.Interface{ { PortState: "ACTIVE", FixedIPs: []attachinterfaces.FixedIP{ { SubnetID: "d7906db4-a566-4546-b1f4-5c7fa70f0bf3", IPAddress: "10.0.0.7", }, { SubnetID: "45906d64-a548-4276-h1f8-kcffa80fjbnl", IPAddress: "10.0.0.8", }, }, PortID: "0dde1598-b374-474e-986f-5b8dd1df1d4e", NetID: "8a5fe506-7e9f-4091-899b-96336909d93c", MACAddr: "fa:16:3e:38:2d:80", }, } // GetInterfaceExpected represents an expected repsonse from a GetInterface request. var GetInterfaceExpected = attachinterfaces.Interface{ PortState: "ACTIVE", FixedIPs: []attachinterfaces.FixedIP{ { SubnetID: "d7906db4-a566-4546-b1f4-5c7fa70f0bf3", IPAddress: "10.0.0.7", }, { SubnetID: "45906d64-a548-4276-h1f8-kcffa80fjbnl", IPAddress: "10.0.0.8", }, }, PortID: "0dde1598-b374-474e-986f-5b8dd1df1d4e", NetID: "8a5fe506-7e9f-4091-899b-96336909d93c", MACAddr: "fa:16:3e:38:2d:80", } // CreateInterfacesExpected represents an expected repsonse from a CreateInterface request. var CreateInterfacesExpected = attachinterfaces.Interface{ PortState: "ACTIVE", FixedIPs: []attachinterfaces.FixedIP{ { SubnetID: "d7906db4-a566-4546-b1f4-5c7fa70f0bf3", IPAddress: "10.0.0.7", }, }, PortID: "0dde1598-b374-474e-986f-5b8dd1df1d4e", NetID: "8a5fe506-7e9f-4091-899b-96336909d93c", MACAddr: "fa:16:3e:38:2d:80", } // HandleInterfaceListSuccessfully sets up the test server to respond to a ListInterfaces request. func HandleInterfaceListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/b07e7a3b-d951-4efc-a4f9-ac9f001afb7f/os-interface", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "interfaceAttachments": [ { "port_state":"ACTIVE", "fixed_ips": [ { "subnet_id": "d7906db4-a566-4546-b1f4-5c7fa70f0bf3", "ip_address": "10.0.0.7" }, { "subnet_id": "45906d64-a548-4276-h1f8-kcffa80fjbnl", "ip_address": "10.0.0.8" } ], "port_id": "0dde1598-b374-474e-986f-5b8dd1df1d4e", "net_id": "8a5fe506-7e9f-4091-899b-96336909d93c", "mac_addr": "fa:16:3e:38:2d:80" } ] }`) }) } // HandleInterfaceGetSuccessfully sets up the test server to respond to a GetInterface request. func HandleInterfaceGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/b07e7a3b-d951-4efc-a4f9-ac9f001afb7f/os-interface/0dde1598-b374-474e-986f-5b8dd1df1d4e", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "interfaceAttachment": { "port_state":"ACTIVE", "fixed_ips": [ { "subnet_id": "d7906db4-a566-4546-b1f4-5c7fa70f0bf3", "ip_address": "10.0.0.7" }, { "subnet_id": "45906d64-a548-4276-h1f8-kcffa80fjbnl", "ip_address": "10.0.0.8" } ], "port_id": "0dde1598-b374-474e-986f-5b8dd1df1d4e", "net_id": "8a5fe506-7e9f-4091-899b-96336909d93c", "mac_addr": "fa:16:3e:38:2d:80" } }`) }) } // HandleInterfaceCreateSuccessfully sets up the test server to respond to a CreateInterface request. func HandleInterfaceCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/b07e7a3b-d951-4efc-a4f9-ac9f001afb7f/os-interface", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "interfaceAttachment": { "net_id": "8a5fe506-7e9f-4091-899b-96336909d93c" } }`) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "interfaceAttachment": { "port_state":"ACTIVE", "fixed_ips": [ { "subnet_id": "d7906db4-a566-4546-b1f4-5c7fa70f0bf3", "ip_address": "10.0.0.7" } ], "port_id": "0dde1598-b374-474e-986f-5b8dd1df1d4e", "net_id": "8a5fe506-7e9f-4091-899b-96336909d93c", "mac_addr": "fa:16:3e:38:2d:80" } }`) }) } // HandleInterfaceDeleteSuccessfully sets up the test server to respond to a DeleteInterface request. func HandleInterfaceDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/b07e7a3b-d951-4efc-a4f9-ac9f001afb7f/os-interface/0dde1598-b374-474e-986f-5b8dd1df1d4e", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000046571335005366400421300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/attachinterfaces/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListInterface(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleInterfaceListSuccessfully(t) expected := ListInterfacesExpected pages := 0 err := attachinterfaces.List(client.ServiceClient(), "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f").EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := attachinterfaces.ExtractInterfaces(page) th.AssertNoErr(t, err) if len(actual) != 1 { t.Fatalf("Expected 1 interface, got %d", len(actual)) } th.CheckDeepEquals(t, expected, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, 1, pages) } func TestListInterfacesAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleInterfaceListSuccessfully(t) allPages, err := attachinterfaces.List(client.ServiceClient(), "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f").AllPages() th.AssertNoErr(t, err) _, err = attachinterfaces.ExtractInterfaces(allPages) th.AssertNoErr(t, err) } func TestGetInterface(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleInterfaceGetSuccessfully(t) expected := GetInterfaceExpected serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" interfaceID := "0dde1598-b374-474e-986f-5b8dd1df1d4e" actual, err := attachinterfaces.Get(client.ServiceClient(), serverID, interfaceID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, actual) } func TestCreateInterface(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleInterfaceCreateSuccessfully(t) expected := CreateInterfacesExpected serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" networkID := "8a5fe506-7e9f-4091-899b-96336909d93c" actual, err := attachinterfaces.Create(client.ServiceClient(), serverID, attachinterfaces.CreateOpts{ NetworkID: networkID, }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, actual) } func TestDeleteInterface(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleInterfaceDeleteSuccessfully(t) serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" portID := "0dde1598-b374-474e-986f-5b8dd1df1d4e" err := attachinterfaces.Delete(client.ServiceClient(), serverID, portID).ExtractErr() th.AssertNoErr(t, err) } urls.go000066400000000000000000000012741335005366400365160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/attachinterfacespackage attachinterfaces import "github.com/gophercloud/gophercloud" func listInterfaceURL(client *gophercloud.ServiceClient, serverID string) string { return client.ServiceURL("servers", serverID, "os-interface") } func getInterfaceURL(client *gophercloud.ServiceClient, serverID, portID string) string { return client.ServiceURL("servers", serverID, "os-interface", portID) } func createInterfaceURL(client *gophercloud.ServiceClient, serverID string) string { return client.ServiceURL("servers", serverID, "os-interface") } func deleteInterfaceURL(client *gophercloud.ServiceClient, serverID, portID string) string { return client.ServiceURL("servers", serverID, "os-interface", portID) } availabilityzones/000077500000000000000000000000001335005366400354175ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000026411335005366400365160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/availabilityzones/* Package availabilityzones provides the ability to get lists and detailed availability zone information and to extend a server result with availability zone information. Example of Extend server result with Availability Zone Information: type ServerWithAZ struct { servers.Server availabilityzones.ServerAvailabilityZoneExt } var allServers []ServerWithAZ allPages, err := servers.List(client, nil).AllPages() if err != nil { panic("Unable to retrieve servers: %s", err) } err = servers.ExtractServersInto(allPages, &allServers) if err != nil { panic("Unable to extract servers: %s", err) } for _, server := range allServers { fmt.Println(server.AvailabilityZone) } Example of Get Availability Zone Information allPages, err := availabilityzones.List(computeClient).AllPages() if err != nil { panic(err) } availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) if err != nil { panic(err) } for _, zoneInfo := range availabilityZoneInfo { fmt.Printf("%+v\n", zoneInfo) } Example of Get Detailed Availability Zone Information allPages, err := availabilityzones.ListDetail(computeClient).AllPages() if err != nil { panic(err) } availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) if err != nil { panic(err) } for _, zoneInfo := range availabilityZoneInfo { fmt.Printf("%+v\n", zoneInfo) } */ package availabilityzones requests.go000066400000000000000000000013431335005366400376220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/availabilityzonespackage availabilityzones import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List will return the existing availability zones. func List(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { return AvailabilityZonePage{pagination.SinglePageBase(r)} }) } // ListDetail will return the existing availability zones with detailed information. func ListDetail(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, listDetailURL(client), func(r pagination.PageResult) pagination.Page { return AvailabilityZonePage{pagination.SinglePageBase(r)} }) } results.go000066400000000000000000000040441335005366400374510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/availabilityzonespackage availabilityzones import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ServerAvailabilityZoneExt is an extension to the base Server object. type ServerAvailabilityZoneExt struct { // AvailabilityZone is the availabilty zone the server is in. AvailabilityZone string `json:"OS-EXT-AZ:availability_zone"` } // ServiceState represents the state of a service in an AvailabilityZone. type ServiceState struct { Active bool `json:"active"` Available bool `json:"available"` UpdatedAt time.Time `json:"-"` } // UnmarshalJSON to override default func (r *ServiceState) UnmarshalJSON(b []byte) error { type tmp ServiceState var s struct { tmp UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = ServiceState(s.tmp) r.UpdatedAt = time.Time(s.UpdatedAt) return nil } // Services is a map of services contained in an AvailabilityZone. type Services map[string]ServiceState // Hosts is map of hosts/nodes contained in an AvailabilityZone. // Each host can have multiple services. type Hosts map[string]Services // ZoneState represents the current state of the availability zone. type ZoneState struct { // Returns true if the availability zone is available Available bool `json:"available"` } // AvailabilityZone contains all the information associated with an OpenStack // AvailabilityZone. type AvailabilityZone struct { Hosts Hosts `json:"hosts"` // The availability zone name ZoneName string `json:"zoneName"` ZoneState ZoneState `json:"zoneState"` } type AvailabilityZonePage struct { pagination.SinglePageBase } // ExtractAvailabilityZones returns a slice of AvailabilityZones contained in a // single page of results. func ExtractAvailabilityZones(r pagination.Page) ([]AvailabilityZone, error) { var s struct { AvailabilityZoneInfo []AvailabilityZone `json:"availabilityZoneInfo"` } err := (r.(AvailabilityZonePage)).ExtractInto(&s) return s.AvailabilityZoneInfo, err } testing/000077500000000000000000000000001335005366400370745ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/availabilityzonesdoc.go000066400000000000000000000000571335005366400401720ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/availabilityzones/testing// availabilityzones unittests package testing fixtures.go000066400000000000000000000132051335005366400412750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/availabilityzones/testingpackage testing import ( "fmt" "net/http" "testing" "time" az "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const GetOutput = ` { "availabilityZoneInfo": [ { "hosts": null, "zoneName": "nova", "zoneState": { "available": true } } ] } ` const GetDetailOutput = ` { "availabilityZoneInfo": [ { "hosts": { "localhost": { "nova-cert": { "active": true, "available": false, "updated_at": "2017-10-14T17:03:39.000000" }, "nova-conductor": { "active": true, "available": false, "updated_at": "2017-10-14T17:04:09.000000" }, "nova-consoleauth": { "active": true, "available": false, "updated_at": "2017-10-14T17:04:18.000000" }, "nova-scheduler": { "active": true, "available": false, "updated_at": "2017-10-14T17:04:30.000000" } }, "openstack-acc-tests.novalocal": { "nova-cert": { "active": true, "available": true, "updated_at": "2018-01-04T04:11:19.000000" }, "nova-conductor": { "active": true, "available": true, "updated_at": "2018-01-04T04:11:22.000000" }, "nova-consoleauth": { "active": true, "available": true, "updated_at": "2018-01-04T04:11:20.000000" }, "nova-scheduler": { "active": true, "available": true, "updated_at": "2018-01-04T04:11:23.000000" } } }, "zoneName": "internal", "zoneState": { "available": true } }, { "hosts": { "openstack-acc-tests.novalocal": { "nova-compute": { "active": true, "available": true, "updated_at": "2018-01-04T04:11:23.000000" } } }, "zoneName": "nova", "zoneState": { "available": true } } ] }` var AZResult = []az.AvailabilityZone{ { Hosts: nil, ZoneName: "nova", ZoneState: az.ZoneState{Available: true}, }, } var AZDetailResult = []az.AvailabilityZone{ { Hosts: az.Hosts{ "localhost": az.Services{ "nova-cert": az.ServiceState{ Active: true, Available: false, UpdatedAt: time.Date(2017, 10, 14, 17, 3, 39, 0, time.UTC), }, "nova-conductor": az.ServiceState{ Active: true, Available: false, UpdatedAt: time.Date(2017, 10, 14, 17, 4, 9, 0, time.UTC), }, "nova-consoleauth": az.ServiceState{ Active: true, Available: false, UpdatedAt: time.Date(2017, 10, 14, 17, 4, 18, 0, time.UTC), }, "nova-scheduler": az.ServiceState{ Active: true, Available: false, UpdatedAt: time.Date(2017, 10, 14, 17, 4, 30, 0, time.UTC), }, }, "openstack-acc-tests.novalocal": az.Services{ "nova-cert": az.ServiceState{ Active: true, Available: true, UpdatedAt: time.Date(2018, 1, 4, 4, 11, 19, 0, time.UTC), }, "nova-conductor": az.ServiceState{ Active: true, Available: true, UpdatedAt: time.Date(2018, 1, 4, 4, 11, 22, 0, time.UTC), }, "nova-consoleauth": az.ServiceState{ Active: true, Available: true, UpdatedAt: time.Date(2018, 1, 4, 4, 11, 20, 0, time.UTC), }, "nova-scheduler": az.ServiceState{ Active: true, Available: true, UpdatedAt: time.Date(2018, 1, 4, 4, 11, 23, 0, time.UTC), }, }, }, ZoneName: "internal", ZoneState: az.ZoneState{Available: true}, }, { Hosts: az.Hosts{ "openstack-acc-tests.novalocal": az.Services{ "nova-compute": az.ServiceState{ Active: true, Available: true, UpdatedAt: time.Date(2018, 1, 4, 4, 11, 23, 0, time.UTC), }, }, }, ZoneName: "nova", ZoneState: az.ZoneState{Available: true}, }, } // HandleGetSuccessfully configures the test server to respond to a Get request // for availability zone information. func HandleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-availability-zone", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, GetOutput) }) } // HandleGetDetailSuccessfully configures the test server to respond to a Get request // for detailed availability zone information. func HandleGetDetailSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-availability-zone/detail", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, GetDetailOutput) }) } requests_test.go000066400000000000000000000017601335005366400423410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/availabilityzones/testingpackage testing import ( "testing" az "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // Verifies that availability zones can be listed correctly func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t) allPages, err := az.List(client.ServiceClient()).AllPages() th.AssertNoErr(t, err) actual, err := az.ExtractAvailabilityZones(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, AZResult, actual) } // Verifies that detailed availability zones can be listed correctly func TestListDetail(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetDetailSuccessfully(t) allPages, err := az.ListDetail(client.ServiceClient()).AllPages() th.AssertNoErr(t, err) actual, err := az.ExtractAvailabilityZones(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, AZDetailResult, actual) } urls.go000066400000000000000000000004371335005366400367370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/availabilityzonespackage availabilityzones import "github.com/gophercloud/gophercloud" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-availability-zone") } func listDetailURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-availability-zone", "detail") } bootfromvolume/000077500000000000000000000000001335005366400347455ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000104311335005366400360400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/bootfromvolume/* Package bootfromvolume extends a server create request with the ability to specify block device options. This can be used to boot a server from a block storage volume as well as specify multiple ephemeral disks upon creation. It is recommended to refer to the Block Device Mapping documentation to see all possible ways to configure a server's block devices at creation time: https://docs.openstack.org/nova/latest/user/block-device-mapping.html Note that this package implements `block_device_mapping_v2`. Example of Creating a Server From an Image This example will boot a server from an image and use a standard ephemeral disk as the server's root disk. This is virtually no different than creating a server without using block device mappings. blockDevices := []bootfromvolume.BlockDevice{ bootfromvolume.BlockDevice{ BootIndex: 0, DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationLocal, SourceType: bootfromvolume.SourceImage, UUID: "image-uuid", }, } serverCreateOpts := servers.CreateOpts{ Name: "server_name", FlavorRef: "flavor-uuid", ImageRef: "image-uuid", } createOpts := bootfromvolume.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, BlockDevice: blockDevices, } server, err := bootfromvolume.Create(client, createOpts).Extract() if err != nil { panic(err) } Example of Creating a Server From a New Volume This example will create a block storage volume based on the given Image. The server will use this volume as its root disk. blockDevices := []bootfromvolume.BlockDevice{ bootfromvolume.BlockDevice{ DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationVolume, SourceType: bootfromvolume.SourceImage, UUID: "image-uuid", VolumeSize: 2, }, } serverCreateOpts := servers.CreateOpts{ Name: "server_name", FlavorRef: "flavor-uuid", } createOpts := bootfromvolume.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, BlockDevice: blockDevices, } server, err := bootfromvolume.Create(client, createOpts).Extract() if err != nil { panic(err) } Example of Creating a Server From an Existing Volume This example will create a server with an existing volume as its root disk. blockDevices := []bootfromvolume.BlockDevice{ bootfromvolume.BlockDevice{ DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationVolume, SourceType: bootfromvolume.SourceVolume, UUID: "volume-uuid", }, } serverCreateOpts := servers.CreateOpts{ Name: "server_name", FlavorRef: "flavor-uuid", } createOpts := bootfromvolume.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, BlockDevice: blockDevices, } server, err := bootfromvolume.Create(client, createOpts).Extract() if err != nil { panic(err) } Example of Creating a Server with Multiple Ephemeral Disks This example will create a server with multiple ephemeral disks. The first block device will be based off of an existing Image. Each additional ephemeral disks must have an index of -1. blockDevices := []bootfromvolume.BlockDevice{ bootfromvolume.BlockDevice{ BootIndex: 0, DestinationType: bootfromvolume.DestinationLocal, DeleteOnTermination: true, SourceType: bootfromvolume.SourceImage, UUID: "image-uuid", VolumeSize: 5, }, bootfromvolume.BlockDevice{ BootIndex: -1, DestinationType: bootfromvolume.DestinationLocal, DeleteOnTermination: true, GuestFormat: "ext4", SourceType: bootfromvolume.SourceBlank, VolumeSize: 1, }, bootfromvolume.BlockDevice{ BootIndex: -1, DestinationType: bootfromvolume.DestinationLocal, DeleteOnTermination: true, GuestFormat: "ext4", SourceType: bootfromvolume.SourceBlank, VolumeSize: 1, }, } serverCreateOpts := servers.CreateOpts{ Name: "server_name", FlavorRef: "flavor-uuid", ImageRef: "image-uuid", } createOpts := bootfromvolume.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, BlockDevice: blockDevices, } server, err := bootfromvolume.Create(client, createOpts).Extract() if err != nil { panic(err) } */ package bootfromvolume requests.go000066400000000000000000000101541335005366400371500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/bootfromvolumepackage bootfromvolume import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" ) type ( // DestinationType represents the type of medium being used as the // destination of the bootable device. DestinationType string // SourceType represents the type of medium being used as the source of the // bootable device. SourceType string ) const ( // DestinationLocal DestinationType is for using an ephemeral disk as the // destination. DestinationLocal DestinationType = "local" // DestinationVolume DestinationType is for using a volume as the destination. DestinationVolume DestinationType = "volume" // SourceBlank SourceType is for a "blank" or empty source. SourceBlank SourceType = "blank" // SourceImage SourceType is for using images as the source of a block device. SourceImage SourceType = "image" // SourceSnapshot SourceType is for using a volume snapshot as the source of // a block device. SourceSnapshot SourceType = "snapshot" // SourceVolume SourceType is for using a volume as the source of block // device. SourceVolume SourceType = "volume" ) // BlockDevice is a structure with options for creating block devices in a // server. The block device may be created from an image, snapshot, new volume, // or existing volume. The destination may be a new volume, existing volume // which will be attached to the instance, ephemeral disk, or boot device. type BlockDevice struct { // SourceType must be one of: "volume", "snapshot", "image", or "blank". SourceType SourceType `json:"source_type" required:"true"` // UUID is the unique identifier for the existing volume, snapshot, or // image (see above). UUID string `json:"uuid,omitempty"` // BootIndex is the boot index. It defaults to 0. BootIndex int `json:"boot_index"` // DeleteOnTermination specifies whether or not to delete the attached volume // when the server is deleted. Defaults to `false`. DeleteOnTermination bool `json:"delete_on_termination"` // DestinationType is the type that gets created. Possible values are "volume" // and "local". DestinationType DestinationType `json:"destination_type,omitempty"` // GuestFormat specifies the format of the block device. GuestFormat string `json:"guest_format,omitempty"` // VolumeSize is the size of the volume to create (in gigabytes). This can be // omitted for existing volumes. VolumeSize int `json:"volume_size,omitempty"` // DeviceType specifies the device type of the block devices. // Examples of this are disk, cdrom, floppy, lun, etc. DeviceType string `json:"device_type,omitempty"` // DiskBus is the bus type of the block devices. // Examples of this are ide, usb, virtio, scsi, etc. DiskBus string `json:"disk_bus,omitempty"` } // CreateOptsExt is a structure that extends the server `CreateOpts` structure // by allowing for a block device mapping. type CreateOptsExt struct { servers.CreateOptsBuilder BlockDevice []BlockDevice `json:"block_device_mapping_v2,omitempty"` } // ToServerCreateMap adds the block device mapping option to the base server // creation options. func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) { base, err := opts.CreateOptsBuilder.ToServerCreateMap() if err != nil { return nil, err } if len(opts.BlockDevice) == 0 { err := gophercloud.ErrMissingInput{} err.Argument = "bootfromvolume.CreateOptsExt.BlockDevice" return nil, err } serverMap := base["server"].(map[string]interface{}) blockDevice := make([]map[string]interface{}, len(opts.BlockDevice)) for i, bd := range opts.BlockDevice { b, err := gophercloud.BuildRequestBody(bd, "") if err != nil { return nil, err } blockDevice[i] = b } serverMap["block_device_mapping_v2"] = blockDevice return base, nil } // Create requests the creation of a server from the given block device mapping. func Create(client *gophercloud.ServiceClient, opts servers.CreateOptsBuilder) (r servers.CreateResult) { b, err := opts.ToServerCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) return } results.go000066400000000000000000000005331335005366400367760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/bootfromvolumepackage bootfromvolume import ( os "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" ) // CreateResult temporarily contains the response from a Create call. // It embeds the standard servers.CreateResults type and so can be used the // same way as a standard server request result. type CreateResult struct { os.CreateResult } testing/000077500000000000000000000000001335005366400364225ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/bootfromvolumedoc.go000066400000000000000000000000551335005366400375160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/bootfromvolume/testing// bootfromvolume unit tests package testing fixtures.go000066400000000000000000000142331335005366400406250ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/bootfromvolume/testingpackage testing import ( "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" ) var BaseCreateOpts = servers.CreateOpts{ Name: "createdserver", FlavorRef: "performance1-1", } var BaseCreateOptsWithImageRef = servers.CreateOpts{ Name: "createdserver", FlavorRef: "performance1-1", ImageRef: "asdfasdfasdf", } const ExpectedNewVolumeRequest = ` { "server": { "name":"createdserver", "flavorRef":"performance1-1", "imageRef":"", "block_device_mapping_v2":[ { "uuid":"123456", "source_type":"image", "destination_type":"volume", "boot_index": 0, "delete_on_termination": true, "volume_size": 10 } ] } } ` var NewVolumeRequest = bootfromvolume.CreateOptsExt{ CreateOptsBuilder: BaseCreateOpts, BlockDevice: []bootfromvolume.BlockDevice{ { UUID: "123456", SourceType: bootfromvolume.SourceImage, DestinationType: bootfromvolume.DestinationVolume, VolumeSize: 10, DeleteOnTermination: true, }, }, } const ExpectedExistingVolumeRequest = ` { "server": { "name":"createdserver", "flavorRef":"performance1-1", "imageRef":"", "block_device_mapping_v2":[ { "uuid":"123456", "source_type":"volume", "destination_type":"volume", "boot_index": 0, "delete_on_termination": true } ] } } ` var ExistingVolumeRequest = bootfromvolume.CreateOptsExt{ CreateOptsBuilder: BaseCreateOpts, BlockDevice: []bootfromvolume.BlockDevice{ { UUID: "123456", SourceType: bootfromvolume.SourceVolume, DestinationType: bootfromvolume.DestinationVolume, DeleteOnTermination: true, }, }, } const ExpectedImageRequest = ` { "server": { "name": "createdserver", "imageRef": "asdfasdfasdf", "flavorRef": "performance1-1", "block_device_mapping_v2":[ { "boot_index": 0, "delete_on_termination": true, "destination_type":"local", "source_type":"image", "uuid":"asdfasdfasdf" } ] } } ` var ImageRequest = bootfromvolume.CreateOptsExt{ CreateOptsBuilder: BaseCreateOptsWithImageRef, BlockDevice: []bootfromvolume.BlockDevice{ { BootIndex: 0, DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationLocal, SourceType: bootfromvolume.SourceImage, UUID: "asdfasdfasdf", }, }, } const ExpectedMultiEphemeralRequest = ` { "server": { "name": "createdserver", "imageRef": "asdfasdfasdf", "flavorRef": "performance1-1", "block_device_mapping_v2":[ { "boot_index": 0, "delete_on_termination": true, "destination_type":"local", "source_type":"image", "uuid":"asdfasdfasdf" }, { "boot_index": -1, "delete_on_termination": true, "destination_type":"local", "guest_format":"ext4", "source_type":"blank", "volume_size": 1 }, { "boot_index": -1, "delete_on_termination": true, "destination_type":"local", "guest_format":"ext4", "source_type":"blank", "volume_size": 1 } ] } } ` var MultiEphemeralRequest = bootfromvolume.CreateOptsExt{ CreateOptsBuilder: BaseCreateOptsWithImageRef, BlockDevice: []bootfromvolume.BlockDevice{ { BootIndex: 0, DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationLocal, SourceType: bootfromvolume.SourceImage, UUID: "asdfasdfasdf", }, { BootIndex: -1, DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationLocal, GuestFormat: "ext4", SourceType: bootfromvolume.SourceBlank, VolumeSize: 1, }, { BootIndex: -1, DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationLocal, GuestFormat: "ext4", SourceType: bootfromvolume.SourceBlank, VolumeSize: 1, }, }, } const ExpectedImageAndNewVolumeRequest = ` { "server": { "name": "createdserver", "imageRef": "asdfasdfasdf", "flavorRef": "performance1-1", "block_device_mapping_v2":[ { "boot_index": 0, "delete_on_termination": true, "destination_type":"local", "source_type":"image", "uuid":"asdfasdfasdf" }, { "boot_index": 1, "delete_on_termination": true, "destination_type":"volume", "source_type":"blank", "volume_size": 1, "device_type": "disk", "disk_bus": "scsi" } ] } } ` var ImageAndNewVolumeRequest = bootfromvolume.CreateOptsExt{ CreateOptsBuilder: BaseCreateOptsWithImageRef, BlockDevice: []bootfromvolume.BlockDevice{ { BootIndex: 0, DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationLocal, SourceType: bootfromvolume.SourceImage, UUID: "asdfasdfasdf", }, { BootIndex: 1, DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationVolume, SourceType: bootfromvolume.SourceBlank, VolumeSize: 1, DeviceType: "disk", DiskBus: "scsi", }, }, } const ExpectedImageAndExistingVolumeRequest = ` { "server": { "name": "createdserver", "imageRef": "asdfasdfasdf", "flavorRef": "performance1-1", "block_device_mapping_v2":[ { "boot_index": 0, "delete_on_termination": true, "destination_type":"local", "source_type":"image", "uuid":"asdfasdfasdf" }, { "boot_index": 1, "delete_on_termination": true, "destination_type":"volume", "source_type":"volume", "uuid":"123456", "volume_size": 1 } ] } } ` var ImageAndExistingVolumeRequest = bootfromvolume.CreateOptsExt{ CreateOptsBuilder: BaseCreateOptsWithImageRef, BlockDevice: []bootfromvolume.BlockDevice{ { BootIndex: 0, DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationLocal, SourceType: bootfromvolume.SourceImage, UUID: "asdfasdfasdf", }, { BootIndex: 1, DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationVolume, SourceType: bootfromvolume.SourceVolume, UUID: "123456", VolumeSize: 1, }, }, } requests_test.go000066400000000000000000000023241335005366400416640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/bootfromvolume/testingpackage testing import ( "testing" th "github.com/gophercloud/gophercloud/testhelper" ) func TestBootFromNewVolume(t *testing.T) { actual, err := NewVolumeRequest.ToServerCreateMap() th.AssertNoErr(t, err) th.CheckJSONEquals(t, ExpectedNewVolumeRequest, actual) } func TestBootFromExistingVolume(t *testing.T) { actual, err := ExistingVolumeRequest.ToServerCreateMap() th.AssertNoErr(t, err) th.CheckJSONEquals(t, ExpectedExistingVolumeRequest, actual) } func TestBootFromImage(t *testing.T) { actual, err := ImageRequest.ToServerCreateMap() th.AssertNoErr(t, err) th.CheckJSONEquals(t, ExpectedImageRequest, actual) } func TestCreateMultiEphemeralOpts(t *testing.T) { actual, err := MultiEphemeralRequest.ToServerCreateMap() th.AssertNoErr(t, err) th.CheckJSONEquals(t, ExpectedMultiEphemeralRequest, actual) } func TestAttachNewVolume(t *testing.T) { actual, err := ImageAndNewVolumeRequest.ToServerCreateMap() th.AssertNoErr(t, err) th.CheckJSONEquals(t, ExpectedImageAndNewVolumeRequest, actual) } func TestAttachExistingVolume(t *testing.T) { actual, err := ImageAndExistingVolumeRequest.ToServerCreateMap() th.AssertNoErr(t, err) th.CheckJSONEquals(t, ExpectedImageAndExistingVolumeRequest, actual) } urls.go000066400000000000000000000002451335005366400362620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/bootfromvolumepackage bootfromvolume import "github.com/gophercloud/gophercloud" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-volumes_boot") } defsecrules/000077500000000000000000000000001335005366400341725ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000023611335005366400352700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/defsecrules/* Package defsecrules enables management of default security group rules. Default security group rules are rules that are managed in the "default" security group. This is only applicable in environments running nova-network. This package will not work if the OpenStack environment is running the OpenStack Networking (Neutron) service. Example of Listing Default Security Group Rules allPages, err := defsecrules.List(computeClient).AllPages() if err != nil { panic(err) } allDefaultRules, err := defsecrules.ExtractDefaultRules(allPages) if err != nil { panic(err) } for _, df := range allDefaultRules { fmt.Printf("%+v\n", df) } Example of Retrieving a Default Security Group Rule rule, err := defsecrules.Get(computeClient, "rule-id").Extract() if err != nil { panic(err) } Example of Creating a Default Security Group Rule createOpts := defsecrules.CreateOpts{ IPProtocol: "TCP", FromPort: 80, ToPort: 80, CIDR: "10.10.12.0/24", } rule, err := defsecrules.Create(computeClient, createOpts).Extract() if err != nil { panic(err) } Example of Deleting a Default Security Group Rule err := defsecrules.Delete(computeClient, "rule-id").ExtractErr() if err != nil { panic(err) } */ package defsecrules requests.go000066400000000000000000000047411335005366400364020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/defsecrulespackage defsecrules import ( "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List will return a collection of default rules. func List(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, rootURL(client), func(r pagination.PageResult) pagination.Page { return DefaultRulePage{pagination.SinglePageBase(r)} }) } // CreateOpts represents the configuration for adding a new default rule. type CreateOpts struct { // The lower bound of the port range that will be opened. FromPort int `json:"from_port"` // The upper bound of the port range that will be opened. ToPort int `json:"to_port"` // The protocol type that will be allowed, e.g. TCP. IPProtocol string `json:"ip_protocol" required:"true"` // ONLY required if FromGroupID is blank. This represents the IP range that // will be the source of network traffic to your security group. // // Use 0.0.0.0/0 to allow all IPv4 addresses. // Use ::/0 to allow all IPv6 addresses. CIDR string `json:"cidr,omitempty"` } // CreateOptsBuilder builds the create rule options into a serializable format. type CreateOptsBuilder interface { ToRuleCreateMap() (map[string]interface{}, error) } // ToRuleCreateMap builds the create rule options into a serializable format. func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) { if opts.FromPort == 0 && strings.ToUpper(opts.IPProtocol) != "ICMP" { return nil, gophercloud.ErrMissingInput{Argument: "FromPort"} } if opts.ToPort == 0 && strings.ToUpper(opts.IPProtocol) != "ICMP" { return nil, gophercloud.ErrMissingInput{Argument: "ToPort"} } return gophercloud.BuildRequestBody(opts, "security_group_default_rule") } // Create is the operation responsible for creating a new default rule. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToRuleCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Get will return details for a particular default rule. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(resourceURL(client, id), &r.Body, nil) return } // Delete will permanently delete a rule the project's default security group. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(resourceURL(client, id), nil) return } results.go000066400000000000000000000036351335005366400362310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/defsecrulespackage defsecrules import ( "encoding/json" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups" "github.com/gophercloud/gophercloud/pagination" ) // DefaultRule represents a rule belonging to the "default" security group. // It is identical to an openstack/compute/v2/extensions/secgroups.Rule. type DefaultRule secgroups.Rule func (r *DefaultRule) UnmarshalJSON(b []byte) error { var s secgroups.Rule err := json.Unmarshal(b, &s) if err != nil { return err } *r = DefaultRule(s) return nil } // DefaultRulePage is a single page of a DefaultRule collection. type DefaultRulePage struct { pagination.SinglePageBase } // IsEmpty determines whether or not a page of default rules contains any results. func (page DefaultRulePage) IsEmpty() (bool, error) { users, err := ExtractDefaultRules(page) return len(users) == 0, err } // ExtractDefaultRules returns a slice of DefaultRules contained in a single // page of results. func ExtractDefaultRules(r pagination.Page) ([]DefaultRule, error) { var s struct { DefaultRules []DefaultRule `json:"security_group_default_rules"` } err := (r.(DefaultRulePage)).ExtractInto(&s) return s.DefaultRules, err } type commonResult struct { gophercloud.Result } // CreateResult represents the result of a create operation. type CreateResult struct { commonResult } // GetResult represents the result of a get operation. type GetResult struct { commonResult } // Extract will extract a DefaultRule struct from a Create or Get response. func (r commonResult) Extract() (*DefaultRule, error) { var s struct { DefaultRule DefaultRule `json:"security_group_default_rule"` } err := r.ExtractInto(&s) return &s.DefaultRule, err } // DeleteResult is the response from a delete operation. Call its ExtractErr // method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400356475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/defsecrulesdoc.go000066400000000000000000000000521335005366400367400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/defsecrules/testing// defsecrules unit tests package testing fixtures.go000066400000000000000000000056651335005366400400630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/defsecrules/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const rootPath = "/os-security-group-default-rules" func mockListRulesResponse(t *testing.T) { th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "security_group_default_rules": [ { "from_port": 80, "id": "{ruleID}", "ip_protocol": "TCP", "ip_range": { "cidr": "10.10.10.0/24" }, "to_port": 80 } ] } `) }) } func mockCreateRuleResponse(t *testing.T) { th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, ` { "security_group_default_rule": { "ip_protocol": "TCP", "from_port": 80, "to_port": 80, "cidr": "10.10.12.0/24" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "security_group_default_rule": { "from_port": 80, "id": "{ruleID}", "ip_protocol": "TCP", "ip_range": { "cidr": "10.10.12.0/24" }, "to_port": 80 } } `) }) } func mockCreateRuleResponseICMPZero(t *testing.T) { th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, ` { "security_group_default_rule": { "ip_protocol": "ICMP", "from_port": 0, "to_port": 0, "cidr": "10.10.12.0/24" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "security_group_default_rule": { "from_port": 0, "id": "{ruleID}", "ip_protocol": "ICMP", "ip_range": { "cidr": "10.10.12.0/24" }, "to_port": 0 } } `) }) } func mockGetRuleResponse(t *testing.T, ruleID string) { url := rootPath + "/" + ruleID th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "security_group_default_rule": { "id": "{ruleID}", "from_port": 80, "to_port": 80, "ip_protocol": "TCP", "ip_range": { "cidr": "10.10.12.0/24" } } } `) }) } func mockDeleteRuleResponse(t *testing.T, ruleID string) { url := rootPath + "/" + ruleID th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000053051335005366400411130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/defsecrules/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/defsecrules" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const ruleID = "{ruleID}" func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockListRulesResponse(t) count := 0 err := defsecrules.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := defsecrules.ExtractDefaultRules(page) th.AssertNoErr(t, err) expected := []defsecrules.DefaultRule{ { FromPort: 80, ID: ruleID, IPProtocol: "TCP", IPRange: secgroups.IPRange{CIDR: "10.10.10.0/24"}, ToPort: 80, }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, count) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockCreateRuleResponse(t) opts := defsecrules.CreateOpts{ IPProtocol: "TCP", FromPort: 80, ToPort: 80, CIDR: "10.10.12.0/24", } group, err := defsecrules.Create(client.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) expected := &defsecrules.DefaultRule{ ID: ruleID, FromPort: 80, ToPort: 80, IPProtocol: "TCP", IPRange: secgroups.IPRange{CIDR: "10.10.12.0/24"}, } th.AssertDeepEquals(t, expected, group) } func TestCreateICMPZero(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockCreateRuleResponseICMPZero(t) opts := defsecrules.CreateOpts{ IPProtocol: "ICMP", FromPort: 0, ToPort: 0, CIDR: "10.10.12.0/24", } group, err := defsecrules.Create(client.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) expected := &defsecrules.DefaultRule{ ID: ruleID, FromPort: 0, ToPort: 0, IPProtocol: "ICMP", IPRange: secgroups.IPRange{CIDR: "10.10.12.0/24"}, } th.AssertDeepEquals(t, expected, group) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockGetRuleResponse(t, ruleID) group, err := defsecrules.Get(client.ServiceClient(), ruleID).Extract() th.AssertNoErr(t, err) expected := &defsecrules.DefaultRule{ ID: ruleID, FromPort: 80, ToPort: 80, IPProtocol: "TCP", IPRange: secgroups.IPRange{CIDR: "10.10.12.0/24"}, } th.AssertDeepEquals(t, expected, group) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockDeleteRuleResponse(t, ruleID) err := defsecrules.Delete(client.ServiceClient(), ruleID).ExtractErr() th.AssertNoErr(t, err) } urls.go000066400000000000000000000004641335005366400355120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/defsecrulespackage defsecrules import "github.com/gophercloud/gophercloud" const rulepath = "os-security-group-default-rules" func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rulepath, id) } func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rulepath) } delegate.go000066400000000000000000000013751335005366400337750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionspackage extensions import ( "github.com/gophercloud/gophercloud" common "github.com/gophercloud/gophercloud/openstack/common/extensions" "github.com/gophercloud/gophercloud/pagination" ) // ExtractExtensions interprets a Page as a slice of Extensions. func ExtractExtensions(page pagination.Page) ([]common.Extension, error) { return common.ExtractExtensions(page) } // Get retrieves information for a specific extension using its alias. func Get(c *gophercloud.ServiceClient, alias string) common.GetResult { return common.Get(c, alias) } // List returns a Pager which allows you to iterate over the full collection of extensions. // It does not accept query parameters. func List(c *gophercloud.ServiceClient) pagination.Pager { return common.List(c) } diskconfig/000077500000000000000000000000001335005366400340065ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000020751335005366400351060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/diskconfig/* Package diskconfig provides information and interaction with the Disk Config extension that works with the OpenStack Compute service. Example of Obtaining the Disk Config of a Server type ServerWithDiskConfig { servers.Server diskconfig.ServerDiskConfigExt } var allServers []ServerWithDiskConfig allPages, err := servers.List(client, nil).AllPages() if err != nil { panic("Unable to retrieve servers: %s", err) } err = servers.ExtractServersInto(allPages, &allServers) if err != nil { panic("Unable to extract servers: %s", err) } for _, server := range allServers { fmt.Println(server.DiskConfig) } Example of Creating a Server with Disk Config serverCreateOpts := servers.CreateOpts{ Name: "server_name", ImageRef: "image-uuid", FlavorRef: "flavor-uuid", } createOpts := diskconfig.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, DiskConfig: diskconfig.Manual, } server, err := servers.Create(computeClient, createOpts).Extract() if err != nil { panic("Unable to create server: %s", err) } */ package diskconfig requests.go000066400000000000000000000066631335005366400362230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/diskconfigpackage diskconfig import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" ) // DiskConfig represents one of the two possible settings for the DiskConfig // option when creating, rebuilding, or resizing servers: Auto or Manual. type DiskConfig string const ( // Auto builds a server with a single partition the size of the target flavor // disk and automatically adjusts the filesystem to fit the entire partition. // Auto may only be used with images and servers that use a single EXT3 // partition. Auto DiskConfig = "AUTO" // Manual builds a server using whatever partition scheme and filesystem are // present in the source image. If the target flavor disk is larger, the // remaining space is left unpartitioned. This enables images to have non-EXT3 // filesystems, multiple partitions, and so on, and enables you to manage the // disk configuration. It also results in slightly shorter boot times. Manual DiskConfig = "MANUAL" ) // CreateOptsExt adds a DiskConfig option to the base CreateOpts. type CreateOptsExt struct { servers.CreateOptsBuilder // DiskConfig [optional] controls how the created server's disk is partitioned. DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"` } // ToServerCreateMap adds the diskconfig option to the base server creation options. func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) { base, err := opts.CreateOptsBuilder.ToServerCreateMap() if err != nil { return nil, err } if string(opts.DiskConfig) == "" { return base, nil } serverMap := base["server"].(map[string]interface{}) serverMap["OS-DCF:diskConfig"] = string(opts.DiskConfig) return base, nil } // RebuildOptsExt adds a DiskConfig option to the base RebuildOpts. type RebuildOptsExt struct { servers.RebuildOptsBuilder // DiskConfig controls how the rebuilt server's disk is partitioned. DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"` } // ToServerRebuildMap adds the diskconfig option to the base server rebuild options. func (opts RebuildOptsExt) ToServerRebuildMap() (map[string]interface{}, error) { if opts.DiskConfig != Auto && opts.DiskConfig != Manual { err := gophercloud.ErrInvalidInput{} err.Argument = "diskconfig.RebuildOptsExt.DiskConfig" err.Info = "Must be either diskconfig.Auto or diskconfig.Manual" return nil, err } base, err := opts.RebuildOptsBuilder.ToServerRebuildMap() if err != nil { return nil, err } serverMap := base["rebuild"].(map[string]interface{}) serverMap["OS-DCF:diskConfig"] = string(opts.DiskConfig) return base, nil } // ResizeOptsExt adds a DiskConfig option to the base server resize options. type ResizeOptsExt struct { servers.ResizeOptsBuilder // DiskConfig [optional] controls how the resized server's disk is partitioned. DiskConfig DiskConfig } // ToServerResizeMap adds the diskconfig option to the base server creation options. func (opts ResizeOptsExt) ToServerResizeMap() (map[string]interface{}, error) { if opts.DiskConfig != Auto && opts.DiskConfig != Manual { err := gophercloud.ErrInvalidInput{} err.Argument = "diskconfig.ResizeOptsExt.DiskConfig" err.Info = "Must be either diskconfig.Auto or diskconfig.Manual" return nil, err } base, err := opts.ResizeOptsBuilder.ToServerResizeMap() if err != nil { return nil, err } serverMap := base["resize"].(map[string]interface{}) serverMap["OS-DCF:diskConfig"] = string(opts.DiskConfig) return base, nil } results.go000066400000000000000000000002421335005366400360340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/diskconfigpackage diskconfig type ServerDiskConfigExt struct { // DiskConfig is the disk configuration of the server. DiskConfig DiskConfig `json:"OS-DCF:diskConfig"` } testing/000077500000000000000000000000001335005366400354635ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/diskconfigdoc.go000066400000000000000000000000511335005366400365530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/diskconfig/testing// diskconfig unit tests package testing requests_test.go000066400000000000000000000033331335005366400407260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/diskconfig/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/diskconfig" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" ) func TestCreateOpts(t *testing.T) { base := servers.CreateOpts{ Name: "createdserver", ImageRef: "asdfasdfasdf", FlavorRef: "performance1-1", } ext := diskconfig.CreateOptsExt{ CreateOptsBuilder: base, DiskConfig: diskconfig.Manual, } expected := ` { "server": { "name": "createdserver", "imageRef": "asdfasdfasdf", "flavorRef": "performance1-1", "OS-DCF:diskConfig": "MANUAL" } } ` actual, err := ext.ToServerCreateMap() th.AssertNoErr(t, err) th.CheckJSONEquals(t, expected, actual) } func TestRebuildOpts(t *testing.T) { base := servers.RebuildOpts{ Name: "rebuiltserver", AdminPass: "swordfish", ImageID: "asdfasdfasdf", } ext := diskconfig.RebuildOptsExt{ RebuildOptsBuilder: base, DiskConfig: diskconfig.Auto, } actual, err := ext.ToServerRebuildMap() th.AssertNoErr(t, err) expected := ` { "rebuild": { "name": "rebuiltserver", "imageRef": "asdfasdfasdf", "adminPass": "swordfish", "OS-DCF:diskConfig": "AUTO" } } ` th.CheckJSONEquals(t, expected, actual) } func TestResizeOpts(t *testing.T) { base := servers.ResizeOpts{ FlavorRef: "performance1-8", } ext := diskconfig.ResizeOptsExt{ ResizeOptsBuilder: base, DiskConfig: diskconfig.Auto, } actual, err := ext.ToServerResizeMap() th.AssertNoErr(t, err) expected := ` { "resize": { "flavorRef": "performance1-8", "OS-DCF:diskConfig": "AUTO" } } ` th.CheckJSONEquals(t, expected, actual) } doc.go000066400000000000000000000002341335005366400327610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions// Package extensions provides information and interaction with the // different extensions available for the OpenStack Compute service. package extensions evacuate/000077500000000000000000000000001335005366400334635ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000006131335005366400345570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/evacuate/* Package evacuate provides functionality to evacuates servers that have been provisioned by the OpenStack Compute service from a failed host to a new host. Example to Evacuate a Server from a Host serverID := "b16ba811-199d-4ffd-8839-ba96c1185a67" err := evacuate.Evacuate(computeClient, serverID, evacuate.EvacuateOpts{}).ExtractErr() if err != nil { panic(err) } */ package evacuate requests.go000066400000000000000000000022751335005366400356730ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/evacuatepackage evacuate import ( "github.com/gophercloud/gophercloud" ) // EvacuateOptsBuilder allows extensions to add additional parameters to the // the Evacuate request. type EvacuateOptsBuilder interface { ToEvacuateMap() (map[string]interface{}, error) } // EvacuateOpts specifies Evacuate action parameters. type EvacuateOpts struct { // The name of the host to which the server is evacuated Host string `json:"host,omitempty"` // Indicates whether server is on shared storage OnSharedStorage bool `json:"onSharedStorage"` // An administrative password to access the evacuated server AdminPass string `json:"adminPass,omitempty"` } // ToServerGroupCreateMap constructs a request body from CreateOpts. func (opts EvacuateOpts) ToEvacuateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "evacuate") } // Evacuate will Evacuate a failed instance to another host. func Evacuate(client *gophercloud.ServiceClient, id string, opts EvacuateOptsBuilder) (r EvacuateResult) { b, err := opts.ToEvacuateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000011311335005366400355070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/evacuatepackage evacuate import ( "github.com/gophercloud/gophercloud" ) // EvacuateResult is the response from an Evacuate operation. //Call its ExtractAdminPass method to retrieve the admin password of the instance. //The admin password will be an empty string if the cloud is not configured to inject admin passwords.. type EvacuateResult struct { gophercloud.Result } func (r EvacuateResult) ExtractAdminPass() (string, error) { var s struct { AdminPass string `json:"adminPass"` } err := r.ExtractInto(&s) if err != nil && err.Error() == "EOF" { return "", nil } return s.AdminPass, err } testing/000077500000000000000000000000001335005366400351405ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/evacuatedoc.go000066400000000000000000000000621335005366400362320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/evacuate/testing// compute_extensions_evacuate_v2 package testing fixtures.go000066400000000000000000000035551335005366400373500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/evacuate/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func mockEvacuateResponse(t *testing.T, id string) { th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, ` { "evacuate": { "adminPass": "MySecretPass", "host": "derp", "onSharedStorage": false } } `) w.WriteHeader(http.StatusOK) }) } func mockEvacuateResponseWithHost(t *testing.T, id string) { th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, ` { "evacuate": { "host": "derp", "onSharedStorage": false } } `) w.WriteHeader(http.StatusOK) }) } func mockEvacuateResponseWithNoOpts(t *testing.T, id string) { th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, ` { "evacuate": { "onSharedStorage": false } } `) w.WriteHeader(http.StatusOK) }) } const EvacuateResponse = ` { "adminPass": "MySecretPass" } ` func mockEvacuateAdminpassResponse(t *testing.T, id string) { th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, ` { "evacuate": { "onSharedStorage": false } } `) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, EvacuateResponse) }) } requests_test.go000066400000000000000000000031711335005366400404030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/evacuate/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/evacuate" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestEvacuate(t *testing.T) { const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" th.SetupHTTP() defer th.TeardownHTTP() mockEvacuateResponse(t, serverID) _, err := evacuate.Evacuate(client.ServiceClient(), serverID, evacuate.EvacuateOpts{ Host: "derp", AdminPass: "MySecretPass", OnSharedStorage: false, }).ExtractAdminPass() th.AssertNoErr(t, err) } func TestEvacuateWithHost(t *testing.T) { const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" th.SetupHTTP() defer th.TeardownHTTP() mockEvacuateResponseWithHost(t, serverID) _, err := evacuate.Evacuate(client.ServiceClient(), serverID, evacuate.EvacuateOpts{ Host: "derp", }).ExtractAdminPass() th.AssertNoErr(t, err) } func TestEvacuateWithNoOpts(t *testing.T) { const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" th.SetupHTTP() defer th.TeardownHTTP() mockEvacuateResponseWithNoOpts(t, serverID) _, err := evacuate.Evacuate(client.ServiceClient(), serverID, evacuate.EvacuateOpts{}).ExtractAdminPass() th.AssertNoErr(t, err) } func TestEvacuateAdminpassResponse(t *testing.T) { const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" th.SetupHTTP() defer th.TeardownHTTP() mockEvacuateAdminpassResponse(t, serverID) actual, err := evacuate.Evacuate(client.ServiceClient(), serverID, evacuate.EvacuateOpts{}).ExtractAdminPass() th.CheckEquals(t, "MySecretPass", actual) th.AssertNoErr(t, err) } urls.go000066400000000000000000000002771335005366400350050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/evacuatepackage evacuate import ( "github.com/gophercloud/gophercloud" ) func actionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id, "action") } extendedserverattributes/000077500000000000000000000000001335005366400370245ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000010541335005366400401200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/extendedserverattributes/* Package extendedserverattributes provides the ability to extend a server result with the extended usage information. Example to Get an extended information: type serverAttributesExt struct { servers.Server extendedserverattributes.ServerAttributesExt } var serverWithAttributesExt serverAttributesExt err := servers.Get(computeClient, "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithAttributesExt) if err != nil { panic(err) } fmt.Printf("%+v\n", serverWithAttributesExt) */ package extendedserverattributes results.go000066400000000000000000000012711335005366400410550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/extendedserverattributespackage extendedserverattributes // ServerAttributesExt represents OS-EXT-SRV-ATTR server response fields. // // Following fields will be added after implementing full API microversion // support in the Gophercloud: // // - OS-EXT-SRV-ATTR:reservation_id" // - OS-EXT-SRV-ATTR:launch_index" // - OS-EXT-SRV-ATTR:hostname" // - OS-EXT-SRV-ATTR:kernel_id" // - OS-EXT-SRV-ATTR:ramdisk_id" // - OS-EXT-SRV-ATTR:root_device_name" // - OS-EXT-SRV-ATTR:user_data" type ServerAttributesExt struct { Host string `json:"OS-EXT-SRV-ATTR:host"` InstanceName string `json:"OS-EXT-SRV-ATTR:instance_name"` HypervisorHostname string `json:"OS-EXT-SRV-ATTR:hypervisor_hostname"` } testing/000077500000000000000000000000001335005366400405015ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/extendedserverattributesdoc.go000066400000000000000000000000201335005366400415650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/extendedserverattributes/testingpackage testing fixtures.go000066400000000000000000000020531335005366400427010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/extendedserverattributes/testingpackage testing // ServerWithAttributesExtResult represents a raw server response from the // Compute API with OS-EXT-SRV-ATTR data. // Most of the actual fields were deleted from the response. const ServerWithAttributesExtResult = ` { "server": { "OS-EXT-SRV-ATTR:user_data": "", "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", "OS-EXT-SRV-ATTR:root_device_name": "/dev/sda", "OS-EXT-SRV-ATTR:hostname": "test00", "OS-EXT-SRV-ATTR:reservation_id": "r-ky9gim1l", "OS-EXT-SRV-ATTR:ramdisk_id": "", "OS-EXT-SRV-ATTR:host": "compute01", "OS-EXT-SRV-ATTR:kernel_id": "", "OS-EXT-SRV-ATTR:hypervisor_hostname": "compute01", "OS-EXT-SRV-ATTR:launch_index": 0, "created": "2018-07-27T09:15:48Z", "updated": "2018-07-27T09:15:55Z", "id": "d650a0ce-17c3-497d-961a-43c4af80998a", "name": "test_instance", "status": "ACTIVE", "user_id": "0f2f3822679e4b3ea073e5d1c6ed5f02", "tenant_id": "424e7cf0243c468ca61732ba45973b3e" } } ` requests_test.go000066400000000000000000000023301335005366400437400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/extendedserverattributes/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/extendedserverattributes" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestServerWithUsageExt(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/d650a0ce-17c3-497d-961a-43c4af80998a", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, ServerWithAttributesExtResult) }) type serverAttributesExt struct { servers.Server extendedserverattributes.ServerAttributesExt } var serverWithAttributesExt serverAttributesExt err := servers.Get(fake.ServiceClient(), "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithAttributesExt) th.AssertNoErr(t, err) th.AssertEquals(t, serverWithAttributesExt.Host, "compute01") th.AssertEquals(t, serverWithAttributesExt.InstanceName, "instance-00000001") th.AssertEquals(t, serverWithAttributesExt.HypervisorHostname, "compute01") } extendedstatus/000077500000000000000000000000001335005366400347325ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000012151335005366400360250ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/extendedstatus/* Package extendedstatus provides the ability to extend a server result with the extended status information. Example: type ServerWithExt struct { servers.Server extendedstatus.ServerExtendedStatusExt } var allServers []ServerWithExt allPages, err := servers.List(client, nil).AllPages() if err != nil { panic("Unable to retrieve servers: %s", err) } err = servers.ExtractServersInto(allPages, &allServers) if err != nil { panic("Unable to extract servers: %s", err) } for _, server := range allServers { fmt.Println(server.TaskState) fmt.Println(server.VmState) fmt.Println(server.PowerState) } */ package extendedstatus results.go000066400000000000000000000012421335005366400367610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/extendedstatuspackage extendedstatus type PowerState int type ServerExtendedStatusExt struct { TaskState string `json:"OS-EXT-STS:task_state"` VmState string `json:"OS-EXT-STS:vm_state"` PowerState PowerState `json:"OS-EXT-STS:power_state"` } const ( NOSTATE = iota RUNNING _UNUSED1 PAUSED SHUTDOWN _UNUSED2 CRASHED SUSPENDED ) func (r PowerState) String() string { switch r { case NOSTATE: return "NOSTATE" case RUNNING: return "RUNNING" case PAUSED: return "PAUSED" case SHUTDOWN: return "SHUTDOWN" case CRASHED: return "CRASHED" case SUSPENDED: return "SUSPENDED" case _UNUSED1, _UNUSED2: return "_UNUSED" default: return "N/A" } } floatingips/000077500000000000000000000000001335005366400342055ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000027621335005366400353100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/floatingips/* Package floatingips provides the ability to manage floating ips through the Nova API. This API has been deprecated and will be removed from a future release of the Nova API service. For environements that support this extension, this package can be used regardless of if either Neutron or nova-network is used as the cloud's network service. Example to List Floating IPs allPages, err := floatingips.List(computeClient).AllPages() if err != nil { panic(err) } allFloatingIPs, err := floatingips.ExtractFloatingIPs(allPages) if err != nil { panic(err) } for _, fip := range allFloatingIPs { fmt.Printf("%+v\n", fip) } Example to Create a Floating IP createOpts := floatingips.CreateOpts{ Pool: "nova", } fip, err := floatingips.Create(computeClient, createOpts).Extract() if err != nil { panic(err) } Example to Delete a Floating IP err := floatingips.Delete(computeClient, "floatingip-id").ExtractErr() if err != nil { panic(err) } Example to Associate a Floating IP With a Server associateOpts := floatingips.AssociateOpts{ FloatingIP: "10.10.10.2", } err := floatingips.AssociateInstance(computeClient, "server-id", associateOpts).ExtractErr() if err != nil { panic(err) } Example to Disassociate a Floating IP From a Server disassociateOpts := floatingips.DisassociateOpts{ FloatingIP: "10.10.10.2", } err := floatingips.DisassociateInstance(computeClient, "server-id", disassociateOpts).ExtractErr() if err != nil { panic(err) } */ package floatingips requests.go000066400000000000000000000075171335005366400364210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/floatingipspackage floatingips import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List returns a Pager that allows you to iterate over a collection of FloatingIPs. func List(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { return FloatingIPPage{pagination.SinglePageBase(r)} }) } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToFloatingIPCreateMap() (map[string]interface{}, error) } // CreateOpts specifies a Floating IP allocation request. type CreateOpts struct { // Pool is the pool of Floating IPs to allocate one from. Pool string `json:"pool" required:"true"` } // ToFloatingIPCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // Create requests the creation of a new Floating IP. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToFloatingIPCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Get returns data about a previously created Floating IP. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // Delete requests the deletion of a previous allocated Floating IP. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // AssociateOptsBuilder allows extensions to add additional parameters to the // Associate request. type AssociateOptsBuilder interface { ToFloatingIPAssociateMap() (map[string]interface{}, error) } // AssociateOpts specifies the required information to associate a Floating IP with an instance type AssociateOpts struct { // FloatingIP is the Floating IP to associate with an instance. FloatingIP string `json:"address" required:"true"` // FixedIP is an optional fixed IP address of the server. FixedIP string `json:"fixed_address,omitempty"` } // ToFloatingIPAssociateMap constructs a request body from AssociateOpts. func (opts AssociateOpts) ToFloatingIPAssociateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "addFloatingIp") } // AssociateInstance pairs an allocated Floating IP with a server. func AssociateInstance(client *gophercloud.ServiceClient, serverID string, opts AssociateOptsBuilder) (r AssociateResult) { b, err := opts.ToFloatingIPAssociateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(associateURL(client, serverID), b, nil, nil) return } // DisassociateOptsBuilder allows extensions to add additional parameters to // the Disassociate request. type DisassociateOptsBuilder interface { ToFloatingIPDisassociateMap() (map[string]interface{}, error) } // DisassociateOpts specifies the required information to disassociate a // Floating IP with a server. type DisassociateOpts struct { FloatingIP string `json:"address" required:"true"` } // ToFloatingIPDisassociateMap constructs a request body from DisassociateOpts. func (opts DisassociateOpts) ToFloatingIPDisassociateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "removeFloatingIp") } // DisassociateInstance decouples an allocated Floating IP from an instance func DisassociateInstance(client *gophercloud.ServiceClient, serverID string, opts DisassociateOptsBuilder) (r DisassociateResult) { b, err := opts.ToFloatingIPDisassociateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(disassociateURL(client, serverID), b, nil, nil) return } results.go000066400000000000000000000056001335005366400362360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/floatingipspackage floatingips import ( "encoding/json" "strconv" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // A FloatingIP is an IP that can be associated with a server. type FloatingIP struct { // ID is a unique ID of the Floating IP ID string `json:"-"` // FixedIP is a specific IP on the server to pair the Floating IP with. FixedIP string `json:"fixed_ip,omitempty"` // InstanceID is the ID of the server that is using the Floating IP. InstanceID string `json:"instance_id"` // IP is the actual Floating IP. IP string `json:"ip"` // Pool is the pool of Floating IPs that this Floating IP belongs to. Pool string `json:"pool"` } func (r *FloatingIP) UnmarshalJSON(b []byte) error { type tmp FloatingIP var s struct { tmp ID interface{} `json:"id"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = FloatingIP(s.tmp) switch t := s.ID.(type) { case float64: r.ID = strconv.FormatFloat(t, 'f', -1, 64) case string: r.ID = t } return err } // FloatingIPPage stores a single page of FloatingIPs from a List call. type FloatingIPPage struct { pagination.SinglePageBase } // IsEmpty determines whether or not a FloatingIPsPage is empty. func (page FloatingIPPage) IsEmpty() (bool, error) { va, err := ExtractFloatingIPs(page) return len(va) == 0, err } // ExtractFloatingIPs interprets a page of results as a slice of FloatingIPs. func ExtractFloatingIPs(r pagination.Page) ([]FloatingIP, error) { var s struct { FloatingIPs []FloatingIP `json:"floating_ips"` } err := (r.(FloatingIPPage)).ExtractInto(&s) return s.FloatingIPs, err } // FloatingIPResult is the raw result from a FloatingIP request. type FloatingIPResult struct { gophercloud.Result } // Extract is a method that attempts to interpret any FloatingIP resource // response as a FloatingIP struct. func (r FloatingIPResult) Extract() (*FloatingIP, error) { var s struct { FloatingIP *FloatingIP `json:"floating_ip"` } err := r.ExtractInto(&s) return s.FloatingIP, err } // CreateResult is the response from a Create operation. Call its Extract method // to interpret it as a FloatingIP. type CreateResult struct { FloatingIPResult } // GetResult is the response from a Get operation. Call its Extract method to // interpret it as a FloatingIP. type GetResult struct { FloatingIPResult } // DeleteResult is the response from a Delete operation. Call its ExtractErr // method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // AssociateResult is the response from a Delete operation. Call its ExtractErr // method to determine if the call succeeded or failed. type AssociateResult struct { gophercloud.ErrResult } // DisassociateResult is the response from a Delete operation. Call its // ExtractErr method to determine if the call succeeded or failed. type DisassociateResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400356625ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/floatingipsdoc.go000066400000000000000000000000521335005366400367530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/floatingips/testing// floatingips unit tests package testing fixtures.go000066400000000000000000000137451335005366400400740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/floatingips/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListOutput is a sample response to a List call. const ListOutput = ` { "floating_ips": [ { "fixed_ip": null, "id": "1", "instance_id": null, "ip": "10.10.10.1", "pool": "nova" }, { "fixed_ip": "166.78.185.201", "id": "2", "instance_id": "4d8c3732-a248-40ed-bebc-539a6ffd25c0", "ip": "10.10.10.2", "pool": "nova" } ] } ` // GetOutput is a sample response to a Get call. const GetOutput = ` { "floating_ip": { "fixed_ip": "166.78.185.201", "id": "2", "instance_id": "4d8c3732-a248-40ed-bebc-539a6ffd25c0", "ip": "10.10.10.2", "pool": "nova" } } ` // CreateOutput is a sample response to a Post call const CreateOutput = ` { "floating_ip": { "fixed_ip": null, "id": "1", "instance_id": null, "ip": "10.10.10.1", "pool": "nova" } } ` // CreateOutputWithNumericID is a sample response to a Post call // with a legacy nova-network-based numeric ID. const CreateOutputWithNumericID = ` { "floating_ip": { "fixed_ip": null, "id": 1, "instance_id": null, "ip": "10.10.10.1", "pool": "nova" } } ` // FirstFloatingIP is the first result in ListOutput. var FirstFloatingIP = floatingips.FloatingIP{ ID: "1", IP: "10.10.10.1", Pool: "nova", } // SecondFloatingIP is the first result in ListOutput. var SecondFloatingIP = floatingips.FloatingIP{ FixedIP: "166.78.185.201", ID: "2", InstanceID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0", IP: "10.10.10.2", Pool: "nova", } // ExpectedFloatingIPsSlice is the slice of results that should be parsed // from ListOutput, in the expected order. var ExpectedFloatingIPsSlice = []floatingips.FloatingIP{FirstFloatingIP, SecondFloatingIP} // CreatedFloatingIP is the parsed result from CreateOutput. var CreatedFloatingIP = floatingips.FloatingIP{ ID: "1", IP: "10.10.10.1", Pool: "nova", } // HandleListSuccessfully configures the test server to respond to a List request. func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-floating-ips", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ListOutput) }) } // HandleGetSuccessfully configures the test server to respond to a Get request // for an existing floating ip func HandleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-floating-ips/2", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, GetOutput) }) } // HandleCreateSuccessfully configures the test server to respond to a Create request // for a new floating ip func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-floating-ips", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, ` { "pool": "nova" } `) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, CreateOutput) }) } // HandleCreateWithNumericIDSuccessfully configures the test server to respond to a Create request // for a new floating ip func HandleCreateWithNumericIDSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-floating-ips", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, ` { "pool": "nova" } `) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, CreateOutputWithNumericID) }) } // HandleDeleteSuccessfully configures the test server to respond to a Delete request for a // an existing floating ip func HandleDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-floating-ips/1", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusAccepted) }) } // HandleAssociateSuccessfully configures the test server to respond to a Post request // to associate an allocated floating IP func HandleAssociateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, ` { "addFloatingIp": { "address": "10.10.10.2" } } `) w.WriteHeader(http.StatusAccepted) }) } // HandleFixedAssociateSucessfully configures the test server to respond to a Post request // to associate an allocated floating IP with a specific fixed IP address func HandleAssociateFixedSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, ` { "addFloatingIp": { "address": "10.10.10.2", "fixed_address": "166.78.185.201" } } `) w.WriteHeader(http.StatusAccepted) }) } // HandleDisassociateSuccessfully configures the test server to respond to a Post request // to disassociate an allocated floating IP func HandleDisassociateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, ` { "removeFloatingIp": { "address": "10.10.10.2" } } `) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000054711335005366400411320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/floatingips/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t) count := 0 err := floatingips.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := floatingips.ExtractFloatingIPs(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedFloatingIPsSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, 1, count) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateSuccessfully(t) actual, err := floatingips.Create(client.ServiceClient(), floatingips.CreateOpts{ Pool: "nova", }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &CreatedFloatingIP, actual) } func TestCreateWithNumericID(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateWithNumericIDSuccessfully(t) actual, err := floatingips.Create(client.ServiceClient(), floatingips.CreateOpts{ Pool: "nova", }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &CreatedFloatingIP, actual) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t) actual, err := floatingips.Get(client.ServiceClient(), "2").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &SecondFloatingIP, actual) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSuccessfully(t) err := floatingips.Delete(client.ServiceClient(), "1").ExtractErr() th.AssertNoErr(t, err) } func TestAssociate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAssociateSuccessfully(t) associateOpts := floatingips.AssociateOpts{ FloatingIP: "10.10.10.2", } err := floatingips.AssociateInstance(client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0", associateOpts).ExtractErr() th.AssertNoErr(t, err) } func TestAssociateFixed(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAssociateFixedSuccessfully(t) associateOpts := floatingips.AssociateOpts{ FloatingIP: "10.10.10.2", FixedIP: "166.78.185.201", } err := floatingips.AssociateInstance(client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0", associateOpts).ExtractErr() th.AssertNoErr(t, err) } func TestDisassociateInstance(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDisassociateSuccessfully(t) disassociateOpts := floatingips.DisassociateOpts{ FloatingIP: "10.10.10.2", } err := floatingips.DisassociateInstance(client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0", disassociateOpts).ExtractErr() th.AssertNoErr(t, err) } urls.go000066400000000000000000000016041335005366400355220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/floatingipspackage floatingips import "github.com/gophercloud/gophercloud" const resourcePath = "os-floating-ips" func resourceURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } func listURL(c *gophercloud.ServiceClient) string { return resourceURL(c) } func createURL(c *gophercloud.ServiceClient) string { return resourceURL(c) } func getURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id) } func deleteURL(c *gophercloud.ServiceClient, id string) string { return getURL(c, id) } func serverURL(c *gophercloud.ServiceClient, serverID string) string { return c.ServiceURL("servers/" + serverID + "/action") } func associateURL(c *gophercloud.ServiceClient, serverID string) string { return serverURL(c, serverID) } func disassociateURL(c *gophercloud.ServiceClient, serverID string) string { return serverURL(c, serverID) } hypervisors/000077500000000000000000000000001335005366400342635ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000021751335005366400353640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/hypervisors/* Package hypervisors returns details about list of hypervisors, shows details for a hypervisor and shows summary statistics for all hypervisors over all compute nodes in the OpenStack cloud. Example of Show Hypervisor Details hypervisorID := 42 hypervisor, err := hypervisors.Get(computeClient, 42).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", hypervisor) Example of Retrieving Details of All Hypervisors allPages, err := hypervisors.List(computeClient).AllPages() if err != nil { panic(err) } allHypervisors, err := hypervisors.ExtractHypervisors(allPages) if err != nil { panic(err) } for _, hypervisor := range allHypervisors { fmt.Printf("%+v\n", hypervisor) } Example of Show Hypervisor Statistics hypervisorsStatistics, err := hypervisors.GetStatistics(computeClient).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", hypervisorsStatistics) Example of Show Hypervisor Uptime hypervisorID := 42 hypervisorUptime, err := hypervisors.GetUptime(computeClient, hypervisorID).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", hypervisorUptime) */ package hypervisors requests.go000066400000000000000000000025371335005366400364740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/hypervisorspackage hypervisors import ( "strconv" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List makes a request against the API to list hypervisors. func List(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, hypervisorsListDetailURL(client), func(r pagination.PageResult) pagination.Page { return HypervisorPage{pagination.SinglePageBase(r)} }) } // Statistics makes a request against the API to get hypervisors statistics. func GetStatistics(client *gophercloud.ServiceClient) (r StatisticsResult) { _, r.Err = client.Get(hypervisorsStatisticsURL(client), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Get makes a request against the API to get details for specific hypervisor. func Get(client *gophercloud.ServiceClient, hypervisorID int) (r HypervisorResult) { v := strconv.Itoa(hypervisorID) _, r.Err = client.Get(hypervisorsGetURL(client, v), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // GetUptime makes a request against the API to get uptime for specific hypervisor. func GetUptime(client *gophercloud.ServiceClient, hypervisorID int) (r UptimeResult) { v := strconv.Itoa(hypervisorID) _, r.Err = client.Get(hypervisorsUptimeURL(client, v), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000173161335005366400363230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/hypervisorspackage hypervisors import ( "encoding/json" "fmt" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Topology represents a CPU Topology. type Topology struct { Sockets int `json:"sockets"` Cores int `json:"cores"` Threads int `json:"threads"` } // CPUInfo represents CPU information of the hypervisor. type CPUInfo struct { Vendor string `json:"vendor"` Arch string `json:"arch"` Model string `json:"model"` Features []string `json:"features"` Topology Topology `json:"topology"` } // Service represents a Compute service running on the hypervisor. type Service struct { Host string `json:"host"` ID int `json:"id"` DisabledReason string `json:"disabled_reason"` } // Hypervisor represents a hypervisor in the OpenStack cloud. type Hypervisor struct { // A structure that contains cpu information like arch, model, vendor, // features and topology. CPUInfo CPUInfo `json:"-"` // The current_workload is the number of tasks the hypervisor is responsible // for. This will be equal or greater than the number of active VMs on the // system (it can be greater when VMs are being deleted and the hypervisor is // still cleaning up). CurrentWorkload int `json:"current_workload"` // Status of the hypervisor, either "enabled" or "disabled". Status string `json:"status"` // State of the hypervisor, either "up" or "down". State string `json:"state"` // DiskAvailableLeast is the actual free disk on this hypervisor, // measured in GB. DiskAvailableLeast int `json:"disk_available_least"` // HostIP is the hypervisor's IP address. HostIP string `json:"host_ip"` // FreeDiskGB is the free disk remaining on the hypervisor, measured in GB. FreeDiskGB int `json:"-"` // FreeRAMMB is the free RAM in the hypervisor, measured in MB. FreeRamMB int `json:"free_ram_mb"` // HypervisorHostname is the hostname of the hypervisor. HypervisorHostname string `json:"hypervisor_hostname"` // HypervisorType is the type of hypervisor. HypervisorType string `json:"hypervisor_type"` // HypervisorVersion is the version of the hypervisor. HypervisorVersion int `json:"-"` // ID is the unique ID of the hypervisor. ID int `json:"id"` // LocalGB is the disk space in the hypervisor, measured in GB. LocalGB int `json:"-"` // LocalGBUsed is the used disk space of the hypervisor, measured in GB. LocalGBUsed int `json:"local_gb_used"` // MemoryMB is the total memory of the hypervisor, measured in MB. MemoryMB int `json:"memory_mb"` // MemoryMBUsed is the used memory of the hypervisor, measured in MB. MemoryMBUsed int `json:"memory_mb_used"` // RunningVMs is the The number of running vms on the hypervisor. RunningVMs int `json:"running_vms"` // Service is the service this hypervisor represents. Service Service `json:"service"` // VCPUs is the total number of vcpus on the hypervisor. VCPUs int `json:"vcpus"` // VCPUsUsed is the number of used vcpus on the hypervisor. VCPUsUsed int `json:"vcpus_used"` } func (r *Hypervisor) UnmarshalJSON(b []byte) error { type tmp Hypervisor var s struct { tmp CPUInfo interface{} `json:"cpu_info"` HypervisorVersion interface{} `json:"hypervisor_version"` FreeDiskGB interface{} `json:"free_disk_gb"` LocalGB interface{} `json:"local_gb"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Hypervisor(s.tmp) // Newer versions return the CPU info as the correct type. // Older versions return the CPU info as a string and need to be // unmarshalled by the json parser. var tmpb []byte switch t := s.CPUInfo.(type) { case string: tmpb = []byte(t) case map[string]interface{}: tmpb, err = json.Marshal(t) if err != nil { return err } default: return fmt.Errorf("CPUInfo has unexpected type: %T", t) } err = json.Unmarshal(tmpb, &r.CPUInfo) if err != nil { return err } // These fields may be returned as a scientific notation, so they need // converted to int. switch t := s.HypervisorVersion.(type) { case int: r.HypervisorVersion = t case float64: r.HypervisorVersion = int(t) default: return fmt.Errorf("Hypervisor version of unexpected type") } switch t := s.FreeDiskGB.(type) { case int: r.FreeDiskGB = t case float64: r.FreeDiskGB = int(t) default: return fmt.Errorf("Free disk GB of unexpected type") } switch t := s.LocalGB.(type) { case int: r.LocalGB = t case float64: r.LocalGB = int(t) default: return fmt.Errorf("Local GB of unexpected type") } return nil } // HypervisorPage represents a single page of all Hypervisors from a List // request. type HypervisorPage struct { pagination.SinglePageBase } // IsEmpty determines whether or not a HypervisorPage is empty. func (page HypervisorPage) IsEmpty() (bool, error) { va, err := ExtractHypervisors(page) return len(va) == 0, err } // ExtractHypervisors interprets a page of results as a slice of Hypervisors. func ExtractHypervisors(p pagination.Page) ([]Hypervisor, error) { var h struct { Hypervisors []Hypervisor `json:"hypervisors"` } err := (p.(HypervisorPage)).ExtractInto(&h) return h.Hypervisors, err } type HypervisorResult struct { gophercloud.Result } // Extract interprets any HypervisorResult as a Hypervisor, if possible. func (r HypervisorResult) Extract() (*Hypervisor, error) { var s struct { Hypervisor Hypervisor `json:"hypervisor"` } err := r.ExtractInto(&s) return &s.Hypervisor, err } // Statistics represents a summary statistics for all enabled // hypervisors over all compute nodes in the OpenStack cloud. type Statistics struct { // The number of hypervisors. Count int `json:"count"` // The current_workload is the number of tasks the hypervisor is responsible for CurrentWorkload int `json:"current_workload"` // The actual free disk on this hypervisor(in GB). DiskAvailableLeast int `json:"disk_available_least"` // The free disk remaining on this hypervisor(in GB). FreeDiskGB int `json:"free_disk_gb"` // The free RAM in this hypervisor(in MB). FreeRamMB int `json:"free_ram_mb"` // The disk in this hypervisor(in GB). LocalGB int `json:"local_gb"` // The disk used in this hypervisor(in GB). LocalGBUsed int `json:"local_gb_used"` // The memory of this hypervisor(in MB). MemoryMB int `json:"memory_mb"` // The memory used in this hypervisor(in MB). MemoryMBUsed int `json:"memory_mb_used"` // The total number of running vms on all hypervisors. RunningVMs int `json:"running_vms"` // The number of vcpu in this hypervisor. VCPUs int `json:"vcpus"` // The number of vcpu used in this hypervisor. VCPUsUsed int `json:"vcpus_used"` } type StatisticsResult struct { gophercloud.Result } // Extract interprets any StatisticsResult as a Statistics, if possible. func (r StatisticsResult) Extract() (*Statistics, error) { var s struct { Stats Statistics `json:"hypervisor_statistics"` } err := r.ExtractInto(&s) return &s.Stats, err } // Uptime represents uptime and additional info for a specific hypervisor. type Uptime struct { // The hypervisor host name provided by the Nova virt driver. // For the Ironic driver, it is the Ironic node uuid. HypervisorHostname string `json:"hypervisor_hostname"` // The id of the hypervisor. ID int `json:"id"` // The state of the hypervisor. One of up or down. State string `json:"state"` // The status of the hypervisor. One of enabled or disabled. Status string `json:"status"` // The total uptime of the hypervisor and information about average load. Uptime string `json:"uptime"` } type UptimeResult struct { gophercloud.Result } // Extract interprets any UptimeResult as a Uptime, if possible. func (r UptimeResult) Extract() (*Uptime, error) { var s struct { Uptime Uptime `json:"hypervisor"` } err := r.ExtractInto(&s) return &s.Uptime, err } testing/000077500000000000000000000000001335005366400357405ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/hypervisorsfixtures.go000066400000000000000000000163701335005366400401470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/hypervisors/testingpackage testing import ( "fmt" "net/http" "strconv" "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/hypervisors" "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // The first hypervisor represents what the specification says (~Newton) // The second is exactly the same, but what you can get off a real system (~Kilo) const HypervisorListBody = ` { "hypervisors": [ { "cpu_info": { "arch": "x86_64", "model": "Nehalem", "vendor": "Intel", "features": [ "pge", "clflush" ], "topology": { "cores": 1, "threads": 1, "sockets": 4 } }, "current_workload": 0, "status": "enabled", "state": "up", "disk_available_least": 0, "host_ip": "1.1.1.1", "free_disk_gb": 1028, "free_ram_mb": 7680, "hypervisor_hostname": "fake-mini", "hypervisor_type": "fake", "hypervisor_version": 2002000, "id": 1, "local_gb": 1028, "local_gb_used": 0, "memory_mb": 8192, "memory_mb_used": 512, "running_vms": 0, "service": { "host": "e6a37ee802d74863ab8b91ade8f12a67", "id": 2, "disabled_reason": null }, "vcpus": 1, "vcpus_used": 0 }, { "cpu_info": "{\"arch\": \"x86_64\", \"model\": \"Nehalem\", \"vendor\": \"Intel\", \"features\": [\"pge\", \"clflush\"], \"topology\": {\"cores\": 1, \"threads\": 1, \"sockets\": 4}}", "current_workload": 0, "status": "enabled", "state": "up", "disk_available_least": 0, "host_ip": "1.1.1.1", "free_disk_gb": 1028, "free_ram_mb": 7680, "hypervisor_hostname": "fake-mini", "hypervisor_type": "fake", "hypervisor_version": 2.002e+06, "id": 1, "local_gb": 1028, "local_gb_used": 0, "memory_mb": 8192, "memory_mb_used": 512, "running_vms": 0, "service": { "host": "e6a37ee802d74863ab8b91ade8f12a67", "id": 2, "disabled_reason": null }, "vcpus": 1, "vcpus_used": 0 } ] }` const HypervisorsStatisticsBody = ` { "hypervisor_statistics": { "count": 1, "current_workload": 0, "disk_available_least": 0, "free_disk_gb": 1028, "free_ram_mb": 7680, "local_gb": 1028, "local_gb_used": 0, "memory_mb": 8192, "memory_mb_used": 512, "running_vms": 0, "vcpus": 2, "vcpus_used": 0 } } ` const HypervisorGetBody = ` { "hypervisor":{ "cpu_info":{ "arch":"x86_64", "model":"Nehalem", "vendor":"Intel", "features":[ "pge", "clflush" ], "topology":{ "cores":1, "threads":1, "sockets":4 } }, "current_workload":0, "status":"enabled", "state":"up", "disk_available_least":0, "host_ip":"1.1.1.1", "free_disk_gb":1028, "free_ram_mb":7680, "hypervisor_hostname":"fake-mini", "hypervisor_type":"fake", "hypervisor_version":2002000, "id":1, "local_gb":1028, "local_gb_used":0, "memory_mb":8192, "memory_mb_used":512, "running_vms":0, "service":{ "host":"e6a37ee802d74863ab8b91ade8f12a67", "id":2, "disabled_reason":null }, "vcpus":1, "vcpus_used":0 } } ` const HypervisorUptimeBody = ` { "hypervisor": { "hypervisor_hostname": "fake-mini", "id": 1, "state": "up", "status": "enabled", "uptime": " 08:32:11 up 93 days, 18:25, 12 users, load average: 0.20, 0.12, 0.14" } } ` var ( HypervisorFake = hypervisors.Hypervisor{ CPUInfo: hypervisors.CPUInfo{ Arch: "x86_64", Model: "Nehalem", Vendor: "Intel", Features: []string{ "pge", "clflush", }, Topology: hypervisors.Topology{ Cores: 1, Threads: 1, Sockets: 4, }, }, CurrentWorkload: 0, Status: "enabled", State: "up", DiskAvailableLeast: 0, HostIP: "1.1.1.1", FreeDiskGB: 1028, FreeRamMB: 7680, HypervisorHostname: "fake-mini", HypervisorType: "fake", HypervisorVersion: 2002000, ID: 1, LocalGB: 1028, LocalGBUsed: 0, MemoryMB: 8192, MemoryMBUsed: 512, RunningVMs: 0, Service: hypervisors.Service{ Host: "e6a37ee802d74863ab8b91ade8f12a67", ID: 2, DisabledReason: "", }, VCPUs: 1, VCPUsUsed: 0, } HypervisorsStatisticsExpected = hypervisors.Statistics{ Count: 1, CurrentWorkload: 0, DiskAvailableLeast: 0, FreeDiskGB: 1028, FreeRamMB: 7680, LocalGB: 1028, LocalGBUsed: 0, MemoryMB: 8192, MemoryMBUsed: 512, RunningVMs: 0, VCPUs: 2, VCPUsUsed: 0, } HypervisorUptimeExpected = hypervisors.Uptime{ HypervisorHostname: "fake-mini", ID: 1, State: "up", Status: "enabled", Uptime: " 08:32:11 up 93 days, 18:25, 12 users, load average: 0.20, 0.12, 0.14", } ) func HandleHypervisorsStatisticsSuccessfully(t *testing.T) { testhelper.Mux.HandleFunc("/os-hypervisors/statistics", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, HypervisorsStatisticsBody) }) } func HandleHypervisorListSuccessfully(t *testing.T) { testhelper.Mux.HandleFunc("/os-hypervisors/detail", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, HypervisorListBody) }) } func HandleHypervisorGetSuccessfully(t *testing.T) { v := strconv.Itoa(HypervisorFake.ID) testhelper.Mux.HandleFunc("/os-hypervisors/"+v, func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, HypervisorGetBody) }) } func HandleHypervisorUptimeSuccessfully(t *testing.T) { v := strconv.Itoa(HypervisorFake.ID) testhelper.Mux.HandleFunc("/os-hypervisors/"+v+"/uptime", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, HypervisorUptimeBody) }) } requests_test.go000066400000000000000000000046211335005366400412040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/hypervisors/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/hypervisors" "github.com/gophercloud/gophercloud/pagination" "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListHypervisors(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() HandleHypervisorListSuccessfully(t) pages := 0 err := hypervisors.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := hypervisors.ExtractHypervisors(page) if err != nil { return false, err } if len(actual) != 2 { t.Fatalf("Expected 2 hypervisors, got %d", len(actual)) } testhelper.CheckDeepEquals(t, HypervisorFake, actual[0]) testhelper.CheckDeepEquals(t, HypervisorFake, actual[1]) return true, nil }) testhelper.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListAllHypervisors(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() HandleHypervisorListSuccessfully(t) allPages, err := hypervisors.List(client.ServiceClient()).AllPages() testhelper.AssertNoErr(t, err) actual, err := hypervisors.ExtractHypervisors(allPages) testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, HypervisorFake, actual[0]) testhelper.CheckDeepEquals(t, HypervisorFake, actual[1]) } func TestHypervisorsStatistics(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() HandleHypervisorsStatisticsSuccessfully(t) expected := HypervisorsStatisticsExpected actual, err := hypervisors.GetStatistics(client.ServiceClient()).Extract() testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, &expected, actual) } func TestGetHypervisor(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() HandleHypervisorGetSuccessfully(t) expected := HypervisorFake actual, err := hypervisors.Get(client.ServiceClient(), expected.ID).Extract() testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, &expected, actual) } func TestHypervisorsUptime(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() HandleHypervisorUptimeSuccessfully(t) expected := HypervisorUptimeExpected actual, err := hypervisors.GetUptime(client.ServiceClient(), HypervisorFake.ID).Extract() testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, &expected, actual) } urls.go000066400000000000000000000011321335005366400355740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/hypervisorspackage hypervisors import "github.com/gophercloud/gophercloud" func hypervisorsListDetailURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-hypervisors", "detail") } func hypervisorsStatisticsURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-hypervisors", "statistics") } func hypervisorsGetURL(c *gophercloud.ServiceClient, hypervisorID string) string { return c.ServiceURL("os-hypervisors", hypervisorID) } func hypervisorsUptimeURL(c *gophercloud.ServiceClient, hypervisorID string) string { return c.ServiceURL("os-hypervisors", hypervisorID, "uptime") } keypairs/000077500000000000000000000000001335005366400335155ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000025551335005366400346200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/keypairs/* Package keypairs provides the ability to manage key pairs as well as create servers with a specified key pair. Example to List Key Pairs allPages, err := keypairs.List(computeClient).AllPages() if err != nil { panic(err) } allKeyPairs, err := keypairs.ExtractKeyPairs(allPages) if err != nil { panic(err) } for _, kp := range allKeyPairs { fmt.Printf("%+v\n", kp) } Example to Create a Key Pair createOpts := keypairs.CreateOpts{ Name: "keypair-name", } keypair, err := keypairs.Create(computeClient, createOpts).Extract() if err != nil { panic(err) } fmt.Printf("%+v", keypair) Example to Import a Key Pair createOpts := keypairs.CreateOpts{ Name: "keypair-name", PublicKey: "public-key", } keypair, err := keypairs.Create(computeClient, createOpts).Extract() if err != nil { panic(err) } Example to Delete a Key Pair err := keypairs.Delete(computeClient, "keypair-name").ExtractErr() if err != nil { panic(err) } Example to Create a Server With a Key Pair serverCreateOpts := servers.CreateOpts{ Name: "server_name", ImageRef: "image-uuid", FlavorRef: "flavor-uuid", } createOpts := keypairs.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, KeyName: "keypair-name", } server, err := servers.Create(computeClient, createOpts).Extract() if err != nil { panic(err) } */ package keypairs requests.go000066400000000000000000000052421335005366400357220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/keypairspackage keypairs import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsExt adds a KeyPair option to the base CreateOpts. type CreateOptsExt struct { servers.CreateOptsBuilder // KeyName is the name of the key pair. KeyName string `json:"key_name,omitempty"` } // ToServerCreateMap adds the key_name to the base server creation options. func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) { base, err := opts.CreateOptsBuilder.ToServerCreateMap() if err != nil { return nil, err } if opts.KeyName == "" { return base, nil } serverMap := base["server"].(map[string]interface{}) serverMap["key_name"] = opts.KeyName return base, nil } // List returns a Pager that allows you to iterate over a collection of KeyPairs. func List(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { return KeyPairPage{pagination.SinglePageBase(r)} }) } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToKeyPairCreateMap() (map[string]interface{}, error) } // CreateOpts specifies KeyPair creation or import parameters. type CreateOpts struct { // Name is a friendly name to refer to this KeyPair in other services. Name string `json:"name" required:"true"` // PublicKey [optional] is a pregenerated OpenSSH-formatted public key. // If provided, this key will be imported and no new key will be created. PublicKey string `json:"public_key,omitempty"` } // ToKeyPairCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "keypair") } // Create requests the creation of a new KeyPair on the server, or to import a // pre-existing keypair. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToKeyPairCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Get returns public data about a previously uploaded KeyPair. func Get(client *gophercloud.ServiceClient, name string) (r GetResult) { _, r.Err = client.Get(getURL(client, name), &r.Body, nil) return } // Delete requests the deletion of a previous stored KeyPair from the server. func Delete(client *gophercloud.ServiceClient, name string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, name), nil) return } results.go000066400000000000000000000050301335005366400355430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/keypairspackage keypairs import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // KeyPair is an SSH key known to the OpenStack Cloud that is available to be // injected into servers. type KeyPair struct { // Name is used to refer to this keypair from other services within this // region. Name string `json:"name"` // Fingerprint is a short sequence of bytes that can be used to authenticate // or validate a longer public key. Fingerprint string `json:"fingerprint"` // PublicKey is the public key from this pair, in OpenSSH format. // "ssh-rsa AAAAB3Nz..." PublicKey string `json:"public_key"` // PrivateKey is the private key from this pair, in PEM format. // "-----BEGIN RSA PRIVATE KEY-----\nMIICXA..." // It is only present if this KeyPair was just returned from a Create call. PrivateKey string `json:"private_key"` // UserID is the user who owns this KeyPair. UserID string `json:"user_id"` } // KeyPairPage stores a single page of all KeyPair results from a List call. // Use the ExtractKeyPairs function to convert the results to a slice of // KeyPairs. type KeyPairPage struct { pagination.SinglePageBase } // IsEmpty determines whether or not a KeyPairPage is empty. func (page KeyPairPage) IsEmpty() (bool, error) { ks, err := ExtractKeyPairs(page) return len(ks) == 0, err } // ExtractKeyPairs interprets a page of results as a slice of KeyPairs. func ExtractKeyPairs(r pagination.Page) ([]KeyPair, error) { type pair struct { KeyPair KeyPair `json:"keypair"` } var s struct { KeyPairs []pair `json:"keypairs"` } err := (r.(KeyPairPage)).ExtractInto(&s) results := make([]KeyPair, len(s.KeyPairs)) for i, pair := range s.KeyPairs { results[i] = pair.KeyPair } return results, err } type keyPairResult struct { gophercloud.Result } // Extract is a method that attempts to interpret any KeyPair resource response // as a KeyPair struct. func (r keyPairResult) Extract() (*KeyPair, error) { var s struct { KeyPair *KeyPair `json:"keypair"` } err := r.ExtractInto(&s) return s.KeyPair, err } // CreateResult is the response from a Create operation. Call its Extract method // to interpret it as a KeyPair. type CreateResult struct { keyPairResult } // GetResult is the response from a Get operation. Call its Extract method to // interpret it as a KeyPair. type GetResult struct { keyPairResult } // DeleteResult is the response from a Delete operation. Call its ExtractErr // method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400351725ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/keypairsdoc.go000066400000000000000000000000471335005366400362670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/keypairs/testing// keypairs unit tests package testing fixtures.go000066400000000000000000000211321335005366400373710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/keypairs/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListOutput is a sample response to a List call. const ListOutput = ` { "keypairs": [ { "keypair": { "fingerprint": "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a", "name": "firstkey", "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n" } }, { "keypair": { "fingerprint": "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8", "name": "secondkey", "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n" } } ] } ` // GetOutput is a sample response to a Get call. const GetOutput = ` { "keypair": { "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n", "name": "firstkey", "fingerprint": "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a" } } ` // CreateOutput is a sample response to a Create call. const CreateOutput = ` { "keypair": { "fingerprint": "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8", "name": "createdkey", "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7\nDUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ\n9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5QIDAQAB\nAoGAE5XO1mDhORy9COvsg+kYPUhB1GsCYxh+v88wG7HeFDKBY6KUc/Kxo6yoGn5T\nTjRjekyi2KoDZHz4VlIzyZPwFS4I1bf3oCunVoAKzgLdmnTtvRNMC5jFOGc2vUgP\n9bSyRj3S1R4ClVk2g0IDeagko/jc8zzLEYuIK+fbkds79YECQQDt3vcevgegnkga\ntF4NsDmmBPRkcSHCqrANP/7vFcBQN3czxeYYWX3DK07alu6GhH1Y4sHbdm616uU0\nll7xbDzxAkEAzAtN2IyftNygV2EGiaGgqLyo/tD9+Vui2qCQplqe4jvWh/5Sparl\nOjmKo+uAW+hLrLVMnHzRWxbWU8hirH5FNQJATO+ZxCK4etXXAnQmG41NCAqANWB2\nB+2HJbH2NcQ2QHvAHUm741JGn/KI/aBlo7KEjFRDWUVUB5ji64BbUwCsMQJBAIku\nLGcjnBf/oLk+XSPZC2eGd2Ph5G5qYmH0Q2vkTx+wtTn3DV+eNsDfgMtWAJVJ5t61\ngU1QSXyhLPVlKpnnxuUCQC+xvvWjWtsLaFtAsZywJiqLxQzHts8XLGZptYJ5tLWV\nrtmYtBcJCN48RrgQHry/xWYeA4K/AFQpXfNPgprQ96Q=\n-----END RSA PRIVATE KEY-----\n", "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n", "user_id": "fake" } } ` // ImportOutput is a sample response to a Create call that provides its own public key. const ImportOutput = ` { "keypair": { "fingerprint": "1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c", "name": "importedkey", "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova", "user_id": "fake" } } ` // FirstKeyPair is the first result in ListOutput. var FirstKeyPair = keypairs.KeyPair{ Name: "firstkey", Fingerprint: "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a", PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n", } // SecondKeyPair is the second result in ListOutput. var SecondKeyPair = keypairs.KeyPair{ Name: "secondkey", Fingerprint: "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8", PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n", } // ExpectedKeyPairSlice is the slice of results that should be parsed from ListOutput, in the expected // order. var ExpectedKeyPairSlice = []keypairs.KeyPair{FirstKeyPair, SecondKeyPair} // CreatedKeyPair is the parsed result from CreatedOutput. var CreatedKeyPair = keypairs.KeyPair{ Name: "createdkey", Fingerprint: "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8", PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n", PrivateKey: "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7\nDUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ\n9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5QIDAQAB\nAoGAE5XO1mDhORy9COvsg+kYPUhB1GsCYxh+v88wG7HeFDKBY6KUc/Kxo6yoGn5T\nTjRjekyi2KoDZHz4VlIzyZPwFS4I1bf3oCunVoAKzgLdmnTtvRNMC5jFOGc2vUgP\n9bSyRj3S1R4ClVk2g0IDeagko/jc8zzLEYuIK+fbkds79YECQQDt3vcevgegnkga\ntF4NsDmmBPRkcSHCqrANP/7vFcBQN3czxeYYWX3DK07alu6GhH1Y4sHbdm616uU0\nll7xbDzxAkEAzAtN2IyftNygV2EGiaGgqLyo/tD9+Vui2qCQplqe4jvWh/5Sparl\nOjmKo+uAW+hLrLVMnHzRWxbWU8hirH5FNQJATO+ZxCK4etXXAnQmG41NCAqANWB2\nB+2HJbH2NcQ2QHvAHUm741JGn/KI/aBlo7KEjFRDWUVUB5ji64BbUwCsMQJBAIku\nLGcjnBf/oLk+XSPZC2eGd2Ph5G5qYmH0Q2vkTx+wtTn3DV+eNsDfgMtWAJVJ5t61\ngU1QSXyhLPVlKpnnxuUCQC+xvvWjWtsLaFtAsZywJiqLxQzHts8XLGZptYJ5tLWV\nrtmYtBcJCN48RrgQHry/xWYeA4K/AFQpXfNPgprQ96Q=\n-----END RSA PRIVATE KEY-----\n", UserID: "fake", } // ImportedKeyPair is the parsed result from ImportOutput. var ImportedKeyPair = keypairs.KeyPair{ Name: "importedkey", Fingerprint: "1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c", PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova", UserID: "fake", } // HandleListSuccessfully configures the test server to respond to a List request. func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-keypairs", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ListOutput) }) } // HandleGetSuccessfully configures the test server to respond to a Get request for "firstkey". func HandleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-keypairs/firstkey", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, GetOutput) }) } // HandleCreateSuccessfully configures the test server to respond to a Create request for a new // keypair called "createdkey". func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-keypairs", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "keypair": { "name": "createdkey" } }`) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, CreateOutput) }) } // HandleImportSuccessfully configures the test server to respond to an Import request for an // existing keypair called "importedkey". func HandleImportSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-keypairs", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, ` { "keypair": { "name": "importedkey", "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova" } } `) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ImportOutput) }) } // HandleDeleteSuccessfully configures the test server to respond to a Delete request for a // keypair called "deletedkey". func HandleDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-keypairs/deletedkey", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000037041335005366400404370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/keypairs/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t) count := 0 err := keypairs.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := keypairs.ExtractKeyPairs(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedKeyPairSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, 1, count) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateSuccessfully(t) actual, err := keypairs.Create(client.ServiceClient(), keypairs.CreateOpts{ Name: "createdkey", }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &CreatedKeyPair, actual) } func TestImport(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleImportSuccessfully(t) actual, err := keypairs.Create(client.ServiceClient(), keypairs.CreateOpts{ Name: "importedkey", PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova", }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &ImportedKeyPair, actual) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t) actual, err := keypairs.Get(client.ServiceClient(), "firstkey").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstKeyPair, actual) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSuccessfully(t) err := keypairs.Delete(client.ServiceClient(), "deletedkey").ExtractErr() th.AssertNoErr(t, err) } urls.go000066400000000000000000000010501335005366400350250ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/keypairspackage keypairs import "github.com/gophercloud/gophercloud" const resourcePath = "os-keypairs" func resourceURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } func listURL(c *gophercloud.ServiceClient) string { return resourceURL(c) } func createURL(c *gophercloud.ServiceClient) string { return resourceURL(c) } func getURL(c *gophercloud.ServiceClient, name string) string { return c.ServiceURL(resourcePath, name) } func deleteURL(c *gophercloud.ServiceClient, name string) string { return getURL(c, name) } limits/000077500000000000000000000000001335005366400331675ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000004721335005366400342660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/limits/* Package limits shows rate and limit information for a tenant/project. Example to Retrieve Limits for a Tenant getOpts := limits.GetOpts{ TenantID: "tenant-id", } limits, err := limits.Get(computeClient, getOpts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", limits) */ package limits requests.go000066400000000000000000000016061335005366400353740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/limitspackage limits import ( "github.com/gophercloud/gophercloud" ) // GetOptsBuilder allows extensions to add additional parameters to the // Get request. type GetOptsBuilder interface { ToLimitsQuery() (string, error) } // GetOpts enables retrieving limits by a specific tenant. type GetOpts struct { // The tenant ID to retrieve limits for. TenantID string `q:"tenant_id"` } // ToLimitsQuery formats a GetOpts into a query string. func (opts GetOpts) ToLimitsQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // Get returns the limits about the currently scoped tenant. func Get(client *gophercloud.ServiceClient, opts GetOptsBuilder) (r GetResult) { url := getURL(client) if opts != nil { query, err := opts.ToLimitsQuery() if err != nil { r.Err = err return } url += query } _, r.Err = client.Get(url, &r.Body, nil) return } results.go000066400000000000000000000060521335005366400352220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/limitspackage limits import ( "github.com/gophercloud/gophercloud" ) // Limits is a struct that contains the response of a limit query. type Limits struct { // Absolute contains the limits and usage information. Absolute Absolute `json:"absolute"` } // Usage is a struct that contains the current resource usage and limits // of a tenant. type Absolute struct { // MaxTotalCores is the number of cores available to a tenant. MaxTotalCores int `json:"maxTotalCores"` // MaxImageMeta is the amount of image metadata available to a tenant. MaxImageMeta int `json:"maxImageMeta"` // MaxServerMeta is the amount of server metadata available to a tenant. MaxServerMeta int `json:"maxServerMeta"` // MaxPersonality is the amount of personality/files available to a tenant. MaxPersonality int `json:"maxPersonality"` // MaxPersonalitySize is the personality file size available to a tenant. MaxPersonalitySize int `json:"maxPersonalitySize"` // MaxTotalKeypairs is the total keypairs available to a tenant. MaxTotalKeypairs int `json:"maxTotalKeypairs"` // MaxSecurityGroups is the number of security groups available to a tenant. MaxSecurityGroups int `json:"maxSecurityGroups"` // MaxSecurityGroupRules is the number of security group rules available to // a tenant. MaxSecurityGroupRules int `json:"maxSecurityGroupRules"` // MaxServerGroups is the number of server groups available to a tenant. MaxServerGroups int `json:"maxServerGroups"` // MaxServerGroupMembers is the number of server group members available // to a tenant. MaxServerGroupMembers int `json:"maxServerGroupMembers"` // MaxTotalFloatingIps is the number of floating IPs available to a tenant. MaxTotalFloatingIps int `json:"maxTotalFloatingIps"` // MaxTotalInstances is the number of instances/servers available to a tenant. MaxTotalInstances int `json:"maxTotalInstances"` // MaxTotalRAMSize is the total amount of RAM available to a tenant measured // in megabytes (MB). MaxTotalRAMSize int `json:"maxTotalRAMSize"` // TotalCoresUsed is the number of cores currently in use. TotalCoresUsed int `json:"totalCoresUsed"` // TotalInstancesUsed is the number of instances/servers in use. TotalInstancesUsed int `json:"totalInstancesUsed"` // TotalFloatingIpsUsed is the number of floating IPs in use. TotalFloatingIpsUsed int `json:"totalFloatingIpsUsed"` // TotalRAMUsed is the total RAM/memory in use measured in megabytes (MB). TotalRAMUsed int `json:"totalRAMUsed"` // TotalSecurityGroupsUsed is the total number of security groups in use. TotalSecurityGroupsUsed int `json:"totalSecurityGroupsUsed"` // TotalServerGroupsUsed is the total number of server groups in use. TotalServerGroupsUsed int `json:"totalServerGroupsUsed"` } // Extract interprets a limits result as a Limits. func (r GetResult) Extract() (*Limits, error) { var s struct { Limits *Limits `json:"limits"` } err := r.ExtractInto(&s) return s.Limits, err } // GetResult is the response from a Get operation. Call its Extract // method to interpret it as an Absolute. type GetResult struct { gophercloud.Result } testing/000077500000000000000000000000001335005366400346445ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/limitsfixtures.go000066400000000000000000000043231335005366400370460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/limits/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/limits" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // GetOutput is a sample response to a Get call. const GetOutput = ` { "limits": { "rate": [], "absolute": { "maxServerMeta": 128, "maxPersonality": 5, "totalServerGroupsUsed": 0, "maxImageMeta": 128, "maxPersonalitySize": 10240, "maxTotalKeypairs": 100, "maxSecurityGroupRules": 20, "maxServerGroups": 10, "totalCoresUsed": 1, "totalRAMUsed": 2048, "totalInstancesUsed": 1, "maxSecurityGroups": 10, "totalFloatingIpsUsed": 0, "maxTotalCores": 20, "maxServerGroupMembers": 10, "maxTotalFloatingIps": 10, "totalSecurityGroupsUsed": 1, "maxTotalInstances": 10, "maxTotalRAMSize": 51200 } } } ` // LimitsResult is the result of the limits in GetOutput. var LimitsResult = limits.Limits{ Absolute: limits.Absolute{ MaxServerMeta: 128, MaxPersonality: 5, TotalServerGroupsUsed: 0, MaxImageMeta: 128, MaxPersonalitySize: 10240, MaxTotalKeypairs: 100, MaxSecurityGroupRules: 20, MaxServerGroups: 10, TotalCoresUsed: 1, TotalRAMUsed: 2048, TotalInstancesUsed: 1, MaxSecurityGroups: 10, TotalFloatingIpsUsed: 0, MaxTotalCores: 20, MaxServerGroupMembers: 10, MaxTotalFloatingIps: 10, TotalSecurityGroupsUsed: 1, MaxTotalInstances: 10, MaxTotalRAMSize: 51200, }, } const TenantID = "555544443333222211110000ffffeeee" // HandleGetSuccessfully configures the test server to respond to a Get request // for a limit. func HandleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/limits", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, GetOutput) }) } requests_test.go000066400000000000000000000010071335005366400401030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/limits/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/limits" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t) getOpts := limits.GetOpts{ TenantID: TenantID, } actual, err := limits.Get(client.ServiceClient(), getOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &LimitsResult, actual) } urls.go000066400000000000000000000002711335005366400345030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/limitspackage limits import ( "github.com/gophercloud/gophercloud" ) const resourcePath = "limits" func getURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } lockunlock/000077500000000000000000000000001335005366400340325ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000006661335005366400351360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/lockunlock/* Package lockunlock provides functionality to lock and unlock servers that have been provisioned by the OpenStack Compute service. Example to Lock and Unlock a Server serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" err := lockunlock.Lock(computeClient, serverID).ExtractErr() if err != nil { panic(err) } err = lockunlock.Unlock(computeClient, serverID).ExtractErr() if err != nil { panic(err) } */ package lockunlock requests.go000066400000000000000000000012601335005366400362330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/lockunlockpackage lockunlock import "github.com/gophercloud/gophercloud" func actionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id, "action") } // Lock is the operation responsible for locking a Compute server. func Lock(client *gophercloud.ServiceClient, id string) (r LockResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"lock": nil}, nil, nil) return } // Unlock is the operation responsible for unlocking a Compute server. func Unlock(client *gophercloud.ServiceClient, id string) (r UnlockResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"unlock": nil}, nil, nil) return } results.go000066400000000000000000000005431335005366400360640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/lockunlockpackage lockunlock import ( "github.com/gophercloud/gophercloud" ) // LockResult and UnlockResult are the responses from a Lock and Unlock // operations respectively. Call their ExtractErr methods to determine if the // requests suceeded or failed. type LockResult struct { gophercloud.ErrResult } type UnlockResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400355075ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/lockunlockdoc.go000066400000000000000000000000511335005366400365770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/lockunlock/testing// unlocklock unit tests package testing fixtures.go000066400000000000000000000014431335005366400377110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/lockunlock/testingpackage testing import ( "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func mockStartServerResponse(t *testing.T, id string) { th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{"lock": null}`) w.WriteHeader(http.StatusAccepted) }) } func mockStopServerResponse(t *testing.T, id string) { th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{"unlock": null}`) w.WriteHeader(http.StatusAccepted) }) } request_test.go000066400000000000000000000012511335005366400405640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/lockunlock/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/lockunlock" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const serverID = "{serverId}" func TestLock(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockStartServerResponse(t, serverID) err := lockunlock.Lock(client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } func TestUnlock(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockStopServerResponse(t, serverID) err := lockunlock.Unlock(client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } migrate/000077500000000000000000000000001335005366400333165ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000013241335005366400344120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/migrate/* Package migrate provides functionality to migrate servers that have been provisioned by the OpenStack Compute service. Example of Migrate Server (migrate Action) serverID := "b16ba811-199d-4ffd-8839-ba96c1185a67" err := migrate.Migrate(computeClient, serverID).ExtractErr() if err != nil { panic(err) } Example of Live-Migrate Server (os-migrateLive Action) serverID := "b16ba811-199d-4ffd-8839-ba96c1185a67" host := "01c0cadef72d47e28a672a76060d492c" blockMigration := false migrationOpts := migrate.LiveMigrateOpts{ Host: &host, BlockMigration: &blockMigration, } err := migrate.LiveMigrate(computeClient, serverID, migrationOpts).ExtractErr() if err != nil { panic(err) } */ package migrate requests.go000066400000000000000000000034411335005366400355220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/migratepackage migrate import ( "github.com/gophercloud/gophercloud" ) // Migrate will initiate a migration of the instance to another host. func Migrate(client *gophercloud.ServiceClient, id string) (r MigrateResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"migrate": nil}, nil, nil) return } // LiveMigrateOptsBuilder allows extensions to add additional parameters to the // LiveMigrate request. type LiveMigrateOptsBuilder interface { ToLiveMigrateMap() (map[string]interface{}, error) } // LiveMigrateOpts specifies parameters of live migrate action. type LiveMigrateOpts struct { // The host to which to migrate the server. // If this parameter is None, the scheduler chooses a host. Host *string `json:"host"` // Set to True to migrate local disks by using block migration. // If the source or destination host uses shared storage and you set // this value to True, the live migration fails. BlockMigration *bool `json:"block_migration,omitempty"` // Set to True to enable over commit when the destination host is checked // for available disk space. Set to False to disable over commit. This setting // affects only the libvirt virt driver. DiskOverCommit *bool `json:"disk_over_commit,omitempty"` } // ToLiveMigrateMap constructs a request body from LiveMigrateOpts. func (opts LiveMigrateOpts) ToLiveMigrateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "os-migrateLive") } // LiveMigrate will initiate a live-migration (without rebooting) of the instance to another host. func LiveMigrate(client *gophercloud.ServiceClient, id string, opts LiveMigrateOptsBuilder) (r MigrateResult) { b, err := opts.ToLiveMigrateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(actionURL(client, id), b, nil, nil) return } results.go000066400000000000000000000004011335005366400353410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/migratepackage migrate import ( "github.com/gophercloud/gophercloud" ) // MigrateResult is the response from a Migrate operation. Call its ExtractErr // method to determine if the request suceeded or failed. type MigrateResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400347735ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/migratedoc.go000066400000000000000000000000631335005366400360660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/migrate/testing// compute_extensions_startstop_v2 package testing fixtures.go000066400000000000000000000016371335005366400372020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/migrate/testingpackage testing import ( "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func mockMigrateResponse(t *testing.T, id string) { th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{"migrate": null}`) w.WriteHeader(http.StatusAccepted) }) } func mockLiveMigrateResponse(t *testing.T, id string) { th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "os-migrateLive": { "host": "01c0cadef72d47e28a672a76060d492c", "block_migration": false, "disk_over_commit": true } }`) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000017021335005366400402340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/migrate/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/migrate" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" func TestMigrate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockMigrateResponse(t, serverID) err := migrate.Migrate(client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } func TestLiveMigrate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockLiveMigrateResponse(t, serverID) host := "01c0cadef72d47e28a672a76060d492c" blockMigration := false diskOverCommit := true migrationOpts := migrate.LiveMigrateOpts{ Host: &host, BlockMigration: &blockMigration, DiskOverCommit: &diskOverCommit, } err := migrate.LiveMigrate(client.ServiceClient(), serverID, migrationOpts).ExtractErr() th.AssertNoErr(t, err) } urls.go000066400000000000000000000002761335005366400346370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/migratepackage migrate import ( "github.com/gophercloud/gophercloud" ) func actionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id, "action") } networks/000077500000000000000000000000001335005366400335425ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000007651335005366400346460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/networks/* Package networks provides the ability to create and manage networks in cloud environments using nova-network. This package can also be used to retrieve network details of Neutron-based networks. Example to List Networks allPages, err := networks.List(computeClient).AllPages() if err != nil { panic(err) } allNetworks, err := networks.ExtractNetworks(allPages) if err != nil { panic(err) } for _, network := range allNetworks { fmt.Printf("%+v\n", network) } */ package networks requests.go000066400000000000000000000011471335005366400357470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/networkspackage networks import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List returns a Pager that allows you to iterate over a collection of Network. func List(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { return NetworkPage{pagination.SinglePageBase(r)} }) } // Get returns data about a previously created Network. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } results.go000066400000000000000000000072561335005366400356040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/networkspackage networks import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // A Network represents a network in an OpenStack cloud. type Network struct { // The Bridge that VIFs on this network are connected to Bridge string `json:"bridge"` // BridgeInterface is what interface is connected to the Bridge BridgeInterface string `json:"bridge_interface"` // The Broadcast address of the network. Broadcast string `json:"broadcast"` // CIDR is the IPv4 subnet. CIDR string `json:"cidr"` // CIDRv6 is the IPv6 subnet. CIDRv6 string `json:"cidr_v6"` // CreatedAt is when the network was created.. CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at,omitempty"` // Deleted shows if the network has been deleted. Deleted bool `json:"deleted"` // DeletedAt is the time when the network was deleted. DeletedAt gophercloud.JSONRFC3339MilliNoZ `json:"deleted_at,omitempty"` // DHCPStart is the start of the DHCP address range. DHCPStart string `json:"dhcp_start"` // DNS1 is the first DNS server to use through DHCP. DNS1 string `json:"dns_1"` // DNS2 is the first DNS server to use through DHCP. DNS2 string `json:"dns_2"` // Gateway is the network gateway. Gateway string `json:"gateway"` // Gatewayv6 is the IPv6 network gateway. Gatewayv6 string `json:"gateway_v6"` // Host is the host that the network service is running on. Host string `json:"host"` // ID is the UUID of the network. ID string `json:"id"` // Injected determines if network information is injected into the host. Injected bool `json:"injected"` // Label is the common name that the network has.. Label string `json:"label"` // MultiHost is if multi-host networking is enablec.. MultiHost bool `json:"multi_host"` // Netmask is the network netmask. Netmask string `json:"netmask"` // Netmaskv6 is the IPv6 netmask. Netmaskv6 string `json:"netmask_v6"` // Priority is the network interface priority. Priority int `json:"priority"` // ProjectID is the project associated with this network. ProjectID string `json:"project_id"` // RXTXBase configures bandwidth entitlement. RXTXBase int `json:"rxtx_base"` // UpdatedAt is the time when the network was last updated. UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at,omitempty"` // VLAN is the vlan this network runs on. VLAN int `json:"vlan"` // VPNPrivateAddress is the private address of the CloudPipe VPN. VPNPrivateAddress string `json:"vpn_private_address"` // VPNPublicAddress is the public address of the CloudPipe VPN. VPNPublicAddress string `json:"vpn_public_address"` // VPNPublicPort is the port of the CloudPipe VPN. VPNPublicPort int `json:"vpn_public_port"` } // NetworkPage stores a single page of all Network results from a List call. type NetworkPage struct { pagination.SinglePageBase } // IsEmpty determines whether or not a NetworkPage is empty. func (page NetworkPage) IsEmpty() (bool, error) { va, err := ExtractNetworks(page) return len(va) == 0, err } // ExtractNetworks interprets a page of results as a slice of Networks. func ExtractNetworks(r pagination.Page) ([]Network, error) { var s struct { Networks []Network `json:"networks"` } err := (r.(NetworkPage)).ExtractInto(&s) return s.Networks, err } type NetworkResult struct { gophercloud.Result } // Extract is a method that attempts to interpret any Network resource // response as a Network struct. func (r NetworkResult) Extract() (*Network, error) { var s struct { Network *Network `json:"network"` } err := r.ExtractInto(&s) return s.Network, err } // GetResult is the response from a Get operation. Call its Extract method to // interpret it as a Network. type GetResult struct { NetworkResult } testing/000077500000000000000000000000001335005366400352175ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/networksdoc.go000066400000000000000000000000471335005366400363140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/networks/testing// networks unit tests package testing fixtures.go000066400000000000000000000142611335005366400374230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/networks/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/networks" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListOutput is a sample response to a List call. const ListOutput = ` { "networks": [ { "bridge": "br100", "bridge_interface": "eth0", "broadcast": "10.0.0.7", "cidr": "10.0.0.0/29", "cidr_v6": null, "created_at": "2011-08-15T06:19:19.387525", "deleted": false, "dhcp_start": "10.0.0.3", "dns1": null, "dns2": null, "gateway": "10.0.0.1", "gateway_v6": null, "host": "nsokolov-desktop", "id": "20c8acc0-f747-4d71-a389-46d078ebf047", "injected": false, "label": "mynet_0", "multi_host": false, "netmask": "255.255.255.248", "netmask_v6": null, "priority": null, "project_id": "1234", "rxtx_base": null, "updated_at": "2011-08-16T09:26:13.048257", "vlan": 100, "vpn_private_address": "10.0.0.2", "vpn_public_address": "127.0.0.1", "vpn_public_port": 1000 }, { "bridge": "br101", "bridge_interface": "eth0", "broadcast": "10.0.0.15", "cidr": "10.0.0.10/29", "cidr_v6": null, "created_at": "2011-08-15T06:19:19.387525", "deleted": false, "dhcp_start": "10.0.0.11", "dns1": null, "dns2": null, "gateway": "10.0.0.9", "gateway_v6": null, "host": null, "id": "20c8acc0-f747-4d71-a389-46d078ebf000", "injected": false, "label": "mynet_1", "multi_host": false, "netmask": "255.255.255.248", "netmask_v6": null, "priority": null, "project_id": null, "rxtx_base": null, "vlan": 101, "vpn_private_address": "10.0.0.10", "vpn_public_address": null, "vpn_public_port": 1001 } ] } ` // GetOutput is a sample response to a Get call. const GetOutput = ` { "network": { "bridge": "br101", "bridge_interface": "eth0", "broadcast": "10.0.0.15", "cidr": "10.0.0.10/29", "cidr_v6": null, "created_at": "2011-08-15T06:19:19.387525", "deleted": false, "dhcp_start": "10.0.0.11", "dns1": null, "dns2": null, "gateway": "10.0.0.9", "gateway_v6": null, "host": null, "id": "20c8acc0-f747-4d71-a389-46d078ebf000", "injected": false, "label": "mynet_1", "multi_host": false, "netmask": "255.255.255.248", "netmask_v6": null, "priority": null, "project_id": null, "rxtx_base": null, "vlan": 101, "vpn_private_address": "10.0.0.10", "vpn_public_address": null, "vpn_public_port": 1001 } } ` // FirstNetwork is the first result in ListOutput. var nilTime time.Time var FirstNetwork = networks.Network{ Bridge: "br100", BridgeInterface: "eth0", Broadcast: "10.0.0.7", CIDR: "10.0.0.0/29", CIDRv6: "", CreatedAt: gophercloud.JSONRFC3339MilliNoZ(time.Date(2011, 8, 15, 6, 19, 19, 387525000, time.UTC)), Deleted: false, DeletedAt: gophercloud.JSONRFC3339MilliNoZ(nilTime), DHCPStart: "10.0.0.3", DNS1: "", DNS2: "", Gateway: "10.0.0.1", Gatewayv6: "", Host: "nsokolov-desktop", ID: "20c8acc0-f747-4d71-a389-46d078ebf047", Injected: false, Label: "mynet_0", MultiHost: false, Netmask: "255.255.255.248", Netmaskv6: "", Priority: 0, ProjectID: "1234", RXTXBase: 0, UpdatedAt: gophercloud.JSONRFC3339MilliNoZ(time.Date(2011, 8, 16, 9, 26, 13, 48257000, time.UTC)), VLAN: 100, VPNPrivateAddress: "10.0.0.2", VPNPublicAddress: "127.0.0.1", VPNPublicPort: 1000, } // SecondNetwork is the second result in ListOutput. var SecondNetwork = networks.Network{ Bridge: "br101", BridgeInterface: "eth0", Broadcast: "10.0.0.15", CIDR: "10.0.0.10/29", CIDRv6: "", CreatedAt: gophercloud.JSONRFC3339MilliNoZ(time.Date(2011, 8, 15, 6, 19, 19, 387525000, time.UTC)), Deleted: false, DeletedAt: gophercloud.JSONRFC3339MilliNoZ(nilTime), DHCPStart: "10.0.0.11", DNS1: "", DNS2: "", Gateway: "10.0.0.9", Gatewayv6: "", Host: "", ID: "20c8acc0-f747-4d71-a389-46d078ebf000", Injected: false, Label: "mynet_1", MultiHost: false, Netmask: "255.255.255.248", Netmaskv6: "", Priority: 0, ProjectID: "", RXTXBase: 0, UpdatedAt: gophercloud.JSONRFC3339MilliNoZ(nilTime), VLAN: 101, VPNPrivateAddress: "10.0.0.10", VPNPublicAddress: "", VPNPublicPort: 1001, } // ExpectedNetworkSlice is the slice of results that should be parsed // from ListOutput, in the expected order. var ExpectedNetworkSlice = []networks.Network{FirstNetwork, SecondNetwork} // HandleListSuccessfully configures the test server to respond to a List request. func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-networks", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ListOutput) }) } // HandleGetSuccessfully configures the test server to respond to a Get request // for an existing network. func HandleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-networks/20c8acc0-f747-4d71-a389-46d078ebf000", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, GetOutput) }) } requests_test.go000066400000000000000000000017201335005366400404600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/networks/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/networks" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t) count := 0 err := networks.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := networks.ExtractNetworks(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedNetworkSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, 1, count) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t) actual, err := networks.Get(client.ServiceClient(), "20c8acc0-f747-4d71-a389-46d078ebf000").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &SecondNetwork, actual) } urls.go000066400000000000000000000005661335005366400350650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/networkspackage networks import "github.com/gophercloud/gophercloud" const resourcePath = "os-networks" func resourceURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } func listURL(c *gophercloud.ServiceClient) string { return resourceURL(c) } func getURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id) } pauseunpause/000077500000000000000000000000001335005366400344045ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000007031335005366400355000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/pauseunpause/* Package pauseunpause provides functionality to pause and unpause servers that have been provisioned by the OpenStack Compute service. Example to Pause and Unpause a Server serverID := "32c8baf7-1cdb-4cc2-bc31-c3a55b89f56b" err := pauseunpause.Pause(computeClient, serverID).ExtractErr() if err != nil { panic(err) } err = pauseunpause.Unpause(computeClient, serverID).ExtractErr() if err != nil { panic(err) } */ package pauseunpause requests.go000066400000000000000000000012721335005366400366100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/pauseunpausepackage pauseunpause import "github.com/gophercloud/gophercloud" func actionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id, "action") } // Pause is the operation responsible for pausing a Compute server. func Pause(client *gophercloud.ServiceClient, id string) (r PauseResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"pause": nil}, nil, nil) return } // Unpause is the operation responsible for unpausing a Compute server. func Unpause(client *gophercloud.ServiceClient, id string) (r UnpauseResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"unpause": nil}, nil, nil) return } results.go000066400000000000000000000006751335005366400364440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/pauseunpausepackage pauseunpause import "github.com/gophercloud/gophercloud" // PauseResult is the response from a Pause operation. Call its ExtractErr // method to determine if the request succeeded or failed. type PauseResult struct { gophercloud.ErrResult } // UnpauseResult is the response from an Unpause operation. Call its ExtractErr // method to determine if the request succeeded or failed. type UnpauseResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400360615ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/pauseunpausedoc.go000066400000000000000000000000531335005366400371530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/pauseunpause/testing// pauseunpause unit tests package testing fixtures.go000066400000000000000000000014501335005366400402610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/pauseunpause/testingpackage testing import ( "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func mockPauseServerResponse(t *testing.T, id string) { th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{"pause": null}`) w.WriteHeader(http.StatusAccepted) }) } func mockUnpauseServerResponse(t *testing.T, id string) { th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{"unpause": null}`) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000012661335005366400413270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/pauseunpause/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/pauseunpause" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const serverID = "{serverId}" func TestPause(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockPauseServerResponse(t, serverID) err := pauseunpause.Pause(client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } func TestUnpause(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockUnpauseServerResponse(t, serverID) err := pauseunpause.Unpause(client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } quotasets/000077500000000000000000000000001335005366400337165ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000013451335005366400350150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/quotasets/* Package quotasets enables retrieving and managing Compute quotas. Example to Get a Quota Set quotaset, err := quotasets.Get(computeClient, "tenant-id").Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", quotaset) Example to Get a Detailed Quota Set quotaset, err := quotasets.GetDetail(computeClient, "tenant-id").Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", quotaset) Example to Update a Quota Set updateOpts := quotasets.UpdateOpts{ FixedIPs: gophercloud.IntToPointer(100), Cores: gophercloud.IntToPointer(64), } quotaset, err := quotasets.Update(computeClient, "tenant-id", updateOpts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", quotaset) */ package quotasets requests.go000066400000000000000000000070671335005366400361320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/quotasetspackage quotasets import ( "github.com/gophercloud/gophercloud" ) // Get returns public data about a previously created QuotaSet. func Get(client *gophercloud.ServiceClient, tenantID string) (r GetResult) { _, r.Err = client.Get(getURL(client, tenantID), &r.Body, nil) return } // GetDetail returns detailed public data about a previously created QuotaSet. func GetDetail(client *gophercloud.ServiceClient, tenantID string) (r GetDetailResult) { _, r.Err = client.Get(getDetailURL(client, tenantID), &r.Body, nil) return } // Updates the quotas for the given tenantID and returns the new QuotaSet. func Update(client *gophercloud.ServiceClient, tenantID string, opts UpdateOptsBuilder) (r UpdateResult) { reqBody, err := opts.ToComputeQuotaUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Put(updateURL(client, tenantID), reqBody, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) return } // Resets the quotas for the given tenant to their default values. func Delete(client *gophercloud.ServiceClient, tenantID string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, tenantID), nil) return } // Options for Updating the quotas of a Tenant. // All int-values are pointers so they can be nil if they are not needed. // You can use gopercloud.IntToPointer() for convenience type UpdateOpts struct { // FixedIPs is number of fixed ips alloted this quota_set. FixedIPs *int `json:"fixed_ips,omitempty"` // FloatingIPs is number of floating ips alloted this quota_set. FloatingIPs *int `json:"floating_ips,omitempty"` // InjectedFileContentBytes is content bytes allowed for each injected file. InjectedFileContentBytes *int `json:"injected_file_content_bytes,omitempty"` // InjectedFilePathBytes is allowed bytes for each injected file path. InjectedFilePathBytes *int `json:"injected_file_path_bytes,omitempty"` // InjectedFiles is injected files allowed for each project. InjectedFiles *int `json:"injected_files,omitempty"` // KeyPairs is number of ssh keypairs. KeyPairs *int `json:"key_pairs,omitempty"` // MetadataItems is number of metadata items allowed for each instance. MetadataItems *int `json:"metadata_items,omitempty"` // RAM is megabytes allowed for each instance. RAM *int `json:"ram,omitempty"` // SecurityGroupRules is rules allowed for each security group. SecurityGroupRules *int `json:"security_group_rules,omitempty"` // SecurityGroups security groups allowed for each project. SecurityGroups *int `json:"security_groups,omitempty"` // Cores is number of instance cores allowed for each project. Cores *int `json:"cores,omitempty"` // Instances is number of instances allowed for each project. Instances *int `json:"instances,omitempty"` // Number of ServerGroups allowed for the project. ServerGroups *int `json:"server_groups,omitempty"` // Max number of Members for each ServerGroup. ServerGroupMembers *int `json:"server_group_members,omitempty"` // Force will update the quotaset even if the quota has already been used // and the reserved quota exceeds the new quota. Force bool `json:"force,omitempty"` } // UpdateOptsBuilder enables extensins to add parameters to the update request. type UpdateOptsBuilder interface { // Extra specific name to prevent collisions with interfaces for other quotas // (e.g. neutron) ToComputeQuotaUpdateMap() (map[string]interface{}, error) } // ToComputeQuotaUpdateMap builds the update options into a serializable // format. func (opts UpdateOpts) ToComputeQuotaUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "quota_set") } results.go000066400000000000000000000141601335005366400357500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/quotasetspackage quotasets import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // QuotaSet is a set of operational limits that allow for control of compute // usage. type QuotaSet struct { // ID is tenant associated with this QuotaSet. ID string `json:"id"` // FixedIPs is number of fixed ips alloted this QuotaSet. FixedIPs int `json:"fixed_ips"` // FloatingIPs is number of floating ips alloted this QuotaSet. FloatingIPs int `json:"floating_ips"` // InjectedFileContentBytes is the allowed bytes for each injected file. InjectedFileContentBytes int `json:"injected_file_content_bytes"` // InjectedFilePathBytes is allowed bytes for each injected file path. InjectedFilePathBytes int `json:"injected_file_path_bytes"` // InjectedFiles is the number of injected files allowed for each project. InjectedFiles int `json:"injected_files"` // KeyPairs is number of ssh keypairs. KeyPairs int `json:"key_pairs"` // MetadataItems is number of metadata items allowed for each instance. MetadataItems int `json:"metadata_items"` // RAM is megabytes allowed for each instance. RAM int `json:"ram"` // SecurityGroupRules is number of security group rules allowed for each // security group. SecurityGroupRules int `json:"security_group_rules"` // SecurityGroups is the number of security groups allowed for each project. SecurityGroups int `json:"security_groups"` // Cores is number of instance cores allowed for each project. Cores int `json:"cores"` // Instances is number of instances allowed for each project. Instances int `json:"instances"` // ServerGroups is the number of ServerGroups allowed for the project. ServerGroups int `json:"server_groups"` // ServerGroupMembers is the number of members for each ServerGroup. ServerGroupMembers int `json:"server_group_members"` } // QuotaDetailSet represents details of both operational limits of compute // resources and the current usage of those resources. type QuotaDetailSet struct { // ID is the tenant ID associated with this QuotaDetailSet. ID string `json:"id"` // FixedIPs is number of fixed ips alloted this QuotaDetailSet. FixedIPs QuotaDetail `json:"fixed_ips"` // FloatingIPs is number of floating ips alloted this QuotaDetailSet. FloatingIPs QuotaDetail `json:"floating_ips"` // InjectedFileContentBytes is the allowed bytes for each injected file. InjectedFileContentBytes QuotaDetail `json:"injected_file_content_bytes"` // InjectedFilePathBytes is allowed bytes for each injected file path. InjectedFilePathBytes QuotaDetail `json:"injected_file_path_bytes"` // InjectedFiles is the number of injected files allowed for each project. InjectedFiles QuotaDetail `json:"injected_files"` // KeyPairs is number of ssh keypairs. KeyPairs QuotaDetail `json:"key_pairs"` // MetadataItems is number of metadata items allowed for each instance. MetadataItems QuotaDetail `json:"metadata_items"` // RAM is megabytes allowed for each instance. RAM QuotaDetail `json:"ram"` // SecurityGroupRules is number of security group rules allowed for each // security group. SecurityGroupRules QuotaDetail `json:"security_group_rules"` // SecurityGroups is the number of security groups allowed for each project. SecurityGroups QuotaDetail `json:"security_groups"` // Cores is number of instance cores allowed for each project. Cores QuotaDetail `json:"cores"` // Instances is number of instances allowed for each project. Instances QuotaDetail `json:"instances"` // ServerGroups is the number of ServerGroups allowed for the project. ServerGroups QuotaDetail `json:"server_groups"` // ServerGroupMembers is the number of members for each ServerGroup. ServerGroupMembers QuotaDetail `json:"server_group_members"` } // QuotaDetail is a set of details about a single operational limit that allows // for control of compute usage. type QuotaDetail struct { // InUse is the current number of provisioned/allocated resources of the // given type. InUse int `json:"in_use"` // Reserved is a transitional state when a claim against quota has been made // but the resource is not yet fully online. Reserved int `json:"reserved"` // Limit is the maximum number of a given resource that can be // allocated/provisioned. This is what "quota" usually refers to. Limit int `json:"limit"` } // QuotaSetPage stores a single page of all QuotaSet results from a List call. type QuotaSetPage struct { pagination.SinglePageBase } // IsEmpty determines whether or not a QuotaSetsetPage is empty. func (page QuotaSetPage) IsEmpty() (bool, error) { ks, err := ExtractQuotaSets(page) return len(ks) == 0, err } // ExtractQuotaSets interprets a page of results as a slice of QuotaSets. func ExtractQuotaSets(r pagination.Page) ([]QuotaSet, error) { var s struct { QuotaSets []QuotaSet `json:"quotas"` } err := (r.(QuotaSetPage)).ExtractInto(&s) return s.QuotaSets, err } type quotaResult struct { gophercloud.Result } // Extract is a method that attempts to interpret any QuotaSet resource response // as a QuotaSet struct. func (r quotaResult) Extract() (*QuotaSet, error) { var s struct { QuotaSet *QuotaSet `json:"quota_set"` } err := r.ExtractInto(&s) return s.QuotaSet, err } // GetResult is the response from a Get operation. Call its Extract method to // interpret it as a QuotaSet. type GetResult struct { quotaResult } // UpdateResult is the response from a Update operation. Call its Extract method // to interpret it as a QuotaSet. type UpdateResult struct { quotaResult } // DeleteResult is the response from a Delete operation. Call its Extract method // to interpret it as a QuotaSet. type DeleteResult struct { quotaResult } type quotaDetailResult struct { gophercloud.Result } // GetDetailResult is the response from a Get operation. Call its Extract // method to interpret it as a QuotaSet. type GetDetailResult struct { quotaDetailResult } // Extract is a method that attempts to interpret any QuotaDetailSet // resource response as a set of QuotaDetailSet structs. func (r quotaDetailResult) Extract() (QuotaDetailSet, error) { var s struct { QuotaData QuotaDetailSet `json:"quota_set"` } err := r.ExtractInto(&s) return s.QuotaData, err } testing/000077500000000000000000000000001335005366400353735ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/quotasetsdoc.go000066400000000000000000000000501335005366400364620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/quotasets/testing// quotasets unit tests package testing fixtures.go000066400000000000000000000167151335005366400376050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/quotasets/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // GetOutput is a sample response to a Get call. const GetOutput = ` { "quota_set" : { "instances" : 25, "security_groups" : 10, "security_group_rules" : 20, "cores" : 200, "injected_file_content_bytes" : 10240, "injected_files" : 5, "metadata_items" : 128, "ram" : 200000, "key_pairs" : 10, "injected_file_path_bytes" : 255, "server_groups" : 2, "server_group_members" : 3 } } ` // GetDetailsOutput is a sample response to a Get call with the detailed option. const GetDetailsOutput = ` { "quota_set" : { "id": "555544443333222211110000ffffeeee", "instances" : { "in_use": 0, "limit": 25, "reserved": 0 }, "security_groups" : { "in_use": 0, "limit": 10, "reserved": 0 }, "security_group_rules" : { "in_use": 0, "limit": 20, "reserved": 0 }, "cores" : { "in_use": 0, "limit": 200, "reserved": 0 }, "injected_file_content_bytes" : { "in_use": 0, "limit": 10240, "reserved": 0 }, "injected_files" : { "in_use": 0, "limit": 5, "reserved": 0 }, "metadata_items" : { "in_use": 0, "limit": 128, "reserved": 0 }, "ram" : { "in_use": 0, "limit": 200000, "reserved": 0 }, "key_pairs" : { "in_use": 0, "limit": 10, "reserved": 0 }, "injected_file_path_bytes" : { "in_use": 0, "limit": 255, "reserved": 0 }, "server_groups" : { "in_use": 0, "limit": 2, "reserved": 0 }, "server_group_members" : { "in_use": 0, "limit": 3, "reserved": 0 } } } ` const FirstTenantID = "555544443333222211110000ffffeeee" // FirstQuotaSet is the first result in ListOutput. var FirstQuotaSet = quotasets.QuotaSet{ FixedIPs: 0, FloatingIPs: 0, InjectedFileContentBytes: 10240, InjectedFilePathBytes: 255, InjectedFiles: 5, KeyPairs: 10, MetadataItems: 128, RAM: 200000, SecurityGroupRules: 20, SecurityGroups: 10, Cores: 200, Instances: 25, ServerGroups: 2, ServerGroupMembers: 3, } // FirstQuotaDetailsSet is the first result in ListOutput. var FirstQuotaDetailsSet = quotasets.QuotaDetailSet{ ID: FirstTenantID, InjectedFileContentBytes: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 10240}, InjectedFilePathBytes: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 255}, InjectedFiles: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 5}, KeyPairs: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 10}, MetadataItems: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 128}, RAM: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 200000}, SecurityGroupRules: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 20}, SecurityGroups: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 10}, Cores: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 200}, Instances: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 25}, ServerGroups: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 2}, ServerGroupMembers: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 3}, } //The expected update Body. Is also returned by PUT request const UpdateOutput = `{"quota_set":{"cores":200,"fixed_ips":0,"floating_ips":0,"injected_file_content_bytes":10240,"injected_file_path_bytes":255,"injected_files":5,"instances":25,"key_pairs":10,"metadata_items":128,"ram":200000,"security_group_rules":20,"security_groups":10,"server_groups":2,"server_group_members":3}}` //The expected partialupdate Body. Is also returned by PUT request const PartialUpdateBody = `{"quota_set":{"cores":200, "force":true}}` //Result of Quota-update var UpdatedQuotaSet = quotasets.UpdateOpts{ FixedIPs: gophercloud.IntToPointer(0), FloatingIPs: gophercloud.IntToPointer(0), InjectedFileContentBytes: gophercloud.IntToPointer(10240), InjectedFilePathBytes: gophercloud.IntToPointer(255), InjectedFiles: gophercloud.IntToPointer(5), KeyPairs: gophercloud.IntToPointer(10), MetadataItems: gophercloud.IntToPointer(128), RAM: gophercloud.IntToPointer(200000), SecurityGroupRules: gophercloud.IntToPointer(20), SecurityGroups: gophercloud.IntToPointer(10), Cores: gophercloud.IntToPointer(200), Instances: gophercloud.IntToPointer(25), ServerGroups: gophercloud.IntToPointer(2), ServerGroupMembers: gophercloud.IntToPointer(3), } // HandleGetSuccessfully configures the test server to respond to a Get request for sample tenant func HandleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, GetOutput) }) } // HandleGetDetailSuccessfully configures the test server to respond to a Get Details request for sample tenant func HandleGetDetailSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID+"/detail", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, GetDetailsOutput) }) } // HandlePutSuccessfully configures the test server to respond to a Put request for sample tenant func HandlePutSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, UpdateOutput) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, UpdateOutput) }) } // HandlePartialPutSuccessfully configures the test server to respond to a Put request for sample tenant that only containes specific values func HandlePartialPutSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, PartialUpdateBody) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, UpdateOutput) }) } // HandleDeleteSuccessfully configures the test server to respond to a Delete request for sample tenant func HandleDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestBody(t, r, "") w.Header().Add("Content-Type", "application/json") w.WriteHeader(202) }) } requests_test.go000066400000000000000000000040661335005366400406420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/quotasets/testingpackage testing import ( "errors" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t) actual, err := quotasets.Get(client.ServiceClient(), FirstTenantID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstQuotaSet, actual) } func TestGetDetail(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetDetailSuccessfully(t) actual, err := quotasets.GetDetail(client.ServiceClient(), FirstTenantID).Extract() th.CheckDeepEquals(t, FirstQuotaDetailsSet, actual) th.AssertNoErr(t, err) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePutSuccessfully(t) actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, UpdatedQuotaSet).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstQuotaSet, actual) } func TestPartialUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePartialPutSuccessfully(t) opts := quotasets.UpdateOpts{Cores: gophercloud.IntToPointer(200), Force: true} actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, opts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstQuotaSet, actual) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSuccessfully(t) _, err := quotasets.Delete(client.ServiceClient(), FirstTenantID).Extract() th.AssertNoErr(t, err) } type ErrorUpdateOpts quotasets.UpdateOpts func (opts ErrorUpdateOpts) ToComputeQuotaUpdateMap() (map[string]interface{}, error) { return nil, errors.New("This is an error") } func TestErrorInToComputeQuotaUpdateMap(t *testing.T) { opts := &ErrorUpdateOpts{} th.SetupHTTP() defer th.TeardownHTTP() HandlePutSuccessfully(t) _, err := quotasets.Update(client.ServiceClient(), FirstTenantID, opts).Extract() if err == nil { t.Fatal("Error handling failed") } } urls.go000066400000000000000000000012071335005366400352320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/quotasetspackage quotasets import "github.com/gophercloud/gophercloud" const resourcePath = "os-quota-sets" func resourceURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } func getURL(c *gophercloud.ServiceClient, tenantID string) string { return c.ServiceURL(resourcePath, tenantID) } func getDetailURL(c *gophercloud.ServiceClient, tenantID string) string { return c.ServiceURL(resourcePath, tenantID, "detail") } func updateURL(c *gophercloud.ServiceClient, tenantID string) string { return getURL(c, tenantID) } func deleteURL(c *gophercloud.ServiceClient, tenantID string) string { return getURL(c, tenantID) } rescueunrescue/000077500000000000000000000000001335005366400347265ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000013621335005366400360240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/rescueunrescue/* Package rescueunrescue provides the ability to place a server into rescue mode and to return it back. Example to Rescue a server rescueOpts := rescueunrescue.RescueOpts{ AdminPass: "aUPtawPzE9NU", RescueImageRef: "115e5c5b-72f0-4a0a-9067-60706545248c", } serverID := "3f54d05f-3430-4d80-aa07-63e6af9e2488" adminPass, err := rescueunrescue.Rescue(computeClient, serverID, rescueOpts).Extract() if err != nil { panic(err) } fmt.Printf("adminPass of the rescued server %s: %s\n", serverID, adminPass) Example to Unrescue a server serverID := "3f54d05f-3430-4d80-aa07-63e6af9e2488" if err := rescueunrescue.Unrescue(computeClient, serverID).ExtractErr(); err != nil { panic(err) } */ package rescueunrescue requests.go000066400000000000000000000032531335005366400371330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/rescueunrescuepackage rescueunrescue import "github.com/gophercloud/gophercloud" // RescueOptsBuilder is an interface that allows extensions to override the // default structure of a Rescue request. type RescueOptsBuilder interface { ToServerRescueMap() (map[string]interface{}, error) } // RescueOpts represents the configuration options used to control a Rescue // option. type RescueOpts struct { // AdminPass is the desired administrative password for the instance in // RESCUE mode. // If it's left blank, the server will generate a password. AdminPass string `json:"adminPass,omitempty"` // RescueImageRef contains reference on an image that needs to be used as // rescue image. // If it's left blank, the server will be rescued with the default image. RescueImageRef string `json:"rescue_image_ref,omitempty"` } // ToServerRescueMap formats a RescueOpts as a map that can be used as a JSON // request body for the Rescue request. func (opts RescueOpts) ToServerRescueMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "rescue") } // Rescue instructs the provider to place the server into RESCUE mode. func Rescue(client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder) (r RescueResult) { b, err := opts.ToServerRescueMap() if err != nil { r.Err = err return } _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Unrescue instructs the provider to return the server from RESCUE mode. func Unrescue(client *gophercloud.ServiceClient, id string) (r UnrescueResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"unrescue": nil}, nil, nil) return } results.go000066400000000000000000000013111335005366400367520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/rescueunrescuepackage rescueunrescue import "github.com/gophercloud/gophercloud" type commonResult struct { gophercloud.Result } // RescueResult is the response from a Rescue operation. Call its Extract // method to retrieve adminPass for a rescued server. type RescueResult struct { commonResult } // UnrescueResult is the response from an UnRescue operation. Call its ExtractErr // method to determine if the call succeeded or failed. type UnrescueResult struct { gophercloud.ErrResult } // Extract interprets any RescueResult as an AdminPass, if possible. func (r RescueResult) Extract() (string, error) { var s struct { AdminPass string `json:"adminPass"` } err := r.ExtractInto(&s) return s.AdminPass, err } testing/000077500000000000000000000000001335005366400364035ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/rescueunrescuedoc.go000066400000000000000000000000201335005366400374670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/rescueunrescue/testingpackage testing fixtures.go000066400000000000000000000007271335005366400406110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/rescueunrescue/testingpackage testing // RescueRequest represents request to rescue a server. const RescueRequest = ` { "rescue": { "adminPass": "aUPtawPzE9NU", "rescue_image_ref": "115e5c5b-72f0-4a0a-9067-60706545248c" } } ` // RescueResult represents a raw server response to a RescueRequest. const RescueResult = ` { "adminPass": "aUPtawPzE9NU" } ` // UnrescueRequest represents request to unrescue a server. const UnrescueRequest = ` { "unrescue": null } ` requests_test.go000066400000000000000000000026411335005366400416470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/rescueunrescue/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/rescueunrescue" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestRescue(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/3f54d05f-3430-4d80-aa07-63e6af9e2488/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, RescueRequest) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, RescueResult) }) s, err := rescueunrescue.Rescue(fake.ServiceClient(), "3f54d05f-3430-4d80-aa07-63e6af9e2488", rescueunrescue.RescueOpts{ AdminPass: "aUPtawPzE9NU", RescueImageRef: "115e5c5b-72f0-4a0a-9067-60706545248c", }).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "aUPtawPzE9NU", s) } func TestUnrescue(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/3f54d05f-3430-4d80-aa07-63e6af9e2488/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, UnrescueRequest) w.WriteHeader(http.StatusAccepted) }) err := rescueunrescue.Unrescue(fake.ServiceClient(), "3f54d05f-3430-4d80-aa07-63e6af9e2488").ExtractErr() th.AssertNoErr(t, err) } urls.go000066400000000000000000000003001335005366400362330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/rescueunrescuepackage rescueunrescue import "github.com/gophercloud/gophercloud" func actionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id, "action") } resetstate/000077500000000000000000000000001335005366400340515ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000005361335005366400351510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/resetstate/* Package resetstate provides functionality to reset the state of a server that has been provisioned by the OpenStack Compute service. Example to Reset a Server serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" err := resetstate.ResetState(client, id, resetstate.StateActive).ExtractErr() if err != nil { panic(err) } */ package resetstate requests.go000066400000000000000000000012371335005366400362560ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/resetstatepackage resetstate import ( "github.com/gophercloud/gophercloud" ) // ServerState refers to the states usable in ResetState Action type ServerState string const ( // StateActive returns the state of the server as active StateActive ServerState = "active" // StateError returns the state of the server as error StateError ServerState = "error" ) // ResetState will reset the state of a server func ResetState(client *gophercloud.ServiceClient, id string, state ServerState) (r ResetResult) { stateMap := map[string]interface{}{"state": state} _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-resetState": stateMap}, nil, nil) return } results.go000066400000000000000000000004011335005366400360740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/resetstatepackage resetstate import ( "github.com/gophercloud/gophercloud" ) // ResetResult is the response of a ResetState operation. Call its ExtractErr // method to determine if the request suceeded or failed. type ResetResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400355265ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/resetstatedoc.go000066400000000000000000000000201335005366400366120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/resetstate/testingpackage testing fixtures.go000066400000000000000000000010351335005366400377250ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/resetstate/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func mockResetStateResponse(t *testing.T, id string, state string) { th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, fmt.Sprintf(`{"os-resetState": {"state": "%s"}}`, state)) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000010161335005366400407650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/resetstate/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/resetstate" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" func TestResetState(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockResetStateResponse(t, serverID, "active") err := resetstate.ResetState(client.ServiceClient(), serverID, "active").ExtractErr() th.AssertNoErr(t, err) } urls.go000066400000000000000000000003011335005366400353570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/resetstatepackage resetstate import ( "github.com/gophercloud/gophercloud" ) func actionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id, "action") } schedulerhints/000077500000000000000000000000001335005366400347125ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000032641335005366400360130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/schedulerhints/* Package schedulerhints extends the server create request with the ability to specify additional parameters which determine where the server will be created in the OpenStack cloud. Example to Add a Server to a Server Group schedulerHints := schedulerhints.SchedulerHints{ Group: "servergroup-uuid", } serverCreateOpts := servers.CreateOpts{ Name: "server_name", ImageRef: "image-uuid", FlavorRef: "flavor-uuid", } createOpts := schedulerhints.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, SchedulerHints: schedulerHints, } server, err := servers.Create(computeClient, createOpts).Extract() if err != nil { panic(err) } Example to Place Server B on a Different Host than Server A schedulerHints := schedulerhints.SchedulerHints{ DifferentHost: []string{ "server-a-uuid", } } serverCreateOpts := servers.CreateOpts{ Name: "server_b", ImageRef: "image-uuid", FlavorRef: "flavor-uuid", } createOpts := schedulerhints.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, SchedulerHints: schedulerHints, } server, err := servers.Create(computeClient, createOpts).Extract() if err != nil { panic(err) } Example to Place Server B on the Same Host as Server A schedulerHints := schedulerhints.SchedulerHints{ SameHost: []string{ "server-a-uuid", } } serverCreateOpts := servers.CreateOpts{ Name: "server_b", ImageRef: "image-uuid", FlavorRef: "flavor-uuid", } createOpts := schedulerhints.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, SchedulerHints: schedulerHints, } server, err := servers.Create(computeClient, createOpts).Extract() if err != nil { panic(err) } */ package schedulerhints requests.go000066400000000000000000000107771335005366400371300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/schedulerhintspackage schedulerhints import ( "net" "regexp" "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" ) // SchedulerHints represents a set of scheduling hints that are passed to the // OpenStack scheduler. type SchedulerHints struct { // Group specifies a Server Group to place the instance in. Group string // DifferentHost will place the instance on a compute node that does not // host the given instances. DifferentHost []string // SameHost will place the instance on a compute node that hosts the given // instances. SameHost []string // Query is a conditional statement that results in compute nodes able to // host the instance. Query []interface{} // TargetCell specifies a cell name where the instance will be placed. TargetCell string `json:"target_cell,omitempty"` // BuildNearHostIP specifies a subnet of compute nodes to host the instance. BuildNearHostIP string // AdditionalProperies are arbitrary key/values that are not validated by nova. AdditionalProperties map[string]interface{} } // CreateOptsBuilder builds the scheduler hints into a serializable format. type CreateOptsBuilder interface { ToServerSchedulerHintsCreateMap() (map[string]interface{}, error) } // ToServerSchedulerHintsMap builds the scheduler hints into a serializable format. func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interface{}, error) { sh := make(map[string]interface{}) uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$") if opts.Group != "" { if !uuidRegex.MatchString(opts.Group) { err := gophercloud.ErrInvalidInput{} err.Argument = "schedulerhints.SchedulerHints.Group" err.Value = opts.Group err.Info = "Group must be a UUID" return nil, err } sh["group"] = opts.Group } if len(opts.DifferentHost) > 0 { for _, diffHost := range opts.DifferentHost { if !uuidRegex.MatchString(diffHost) { err := gophercloud.ErrInvalidInput{} err.Argument = "schedulerhints.SchedulerHints.DifferentHost" err.Value = opts.DifferentHost err.Info = "The hosts must be in UUID format." return nil, err } } sh["different_host"] = opts.DifferentHost } if len(opts.SameHost) > 0 { for _, sameHost := range opts.SameHost { if !uuidRegex.MatchString(sameHost) { err := gophercloud.ErrInvalidInput{} err.Argument = "schedulerhints.SchedulerHints.SameHost" err.Value = opts.SameHost err.Info = "The hosts must be in UUID format." return nil, err } } sh["same_host"] = opts.SameHost } /* Query can be something simple like: [">=", "$free_ram_mb", 1024] Or more complex like: ['and', ['>=', '$free_ram_mb', 1024], ['>=', '$free_disk_mb', 200 * 1024] ] Because of the possible complexity, just make sure the length is a minimum of 3. */ if len(opts.Query) > 0 { if len(opts.Query) < 3 { err := gophercloud.ErrInvalidInput{} err.Argument = "schedulerhints.SchedulerHints.Query" err.Value = opts.Query err.Info = "Must be a conditional statement in the format of [op,variable,value]" return nil, err } sh["query"] = opts.Query } if opts.TargetCell != "" { sh["target_cell"] = opts.TargetCell } if opts.BuildNearHostIP != "" { if _, _, err := net.ParseCIDR(opts.BuildNearHostIP); err != nil { err := gophercloud.ErrInvalidInput{} err.Argument = "schedulerhints.SchedulerHints.BuildNearHostIP" err.Value = opts.BuildNearHostIP err.Info = "Must be a valid subnet in the form 192.168.1.1/24" return nil, err } ipParts := strings.Split(opts.BuildNearHostIP, "/") sh["build_near_host_ip"] = ipParts[0] sh["cidr"] = "/" + ipParts[1] } if opts.AdditionalProperties != nil { for k, v := range opts.AdditionalProperties { sh[k] = v } } return sh, nil } // CreateOptsExt adds a SchedulerHints option to the base CreateOpts. type CreateOptsExt struct { servers.CreateOptsBuilder // SchedulerHints provides a set of hints to the scheduler. SchedulerHints CreateOptsBuilder } // ToServerCreateMap adds the SchedulerHints option to the base server creation options. func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) { base, err := opts.CreateOptsBuilder.ToServerCreateMap() if err != nil { return nil, err } schedulerHints, err := opts.SchedulerHints.ToServerSchedulerHintsCreateMap() if err != nil { return nil, err } if len(schedulerHints) == 0 { return base, nil } base["os:scheduler_hints"] = schedulerHints return base, nil } testing/000077500000000000000000000000001335005366400363675ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/schedulerhintsdoc.go000066400000000000000000000000551335005366400374630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/schedulerhints/testing// schedulerhints unit tests package testing requests_test.go000066400000000000000000000070361335005366400416360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/schedulerhints/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" ) func TestCreateOpts(t *testing.T) { base := servers.CreateOpts{ Name: "createdserver", ImageRef: "asdfasdfasdf", FlavorRef: "performance1-1", } schedulerHints := schedulerhints.SchedulerHints{ Group: "101aed42-22d9-4a3e-9ba1-21103b0d1aba", DifferentHost: []string{ "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287", }, SameHost: []string{ "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287", }, Query: []interface{}{">=", "$free_ram_mb", "1024"}, TargetCell: "foobar", BuildNearHostIP: "192.168.1.1/24", AdditionalProperties: map[string]interface{}{"reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, } ext := schedulerhints.CreateOptsExt{ CreateOptsBuilder: base, SchedulerHints: schedulerHints, } expected := ` { "server": { "name": "createdserver", "imageRef": "asdfasdfasdf", "flavorRef": "performance1-1" }, "os:scheduler_hints": { "group": "101aed42-22d9-4a3e-9ba1-21103b0d1aba", "different_host": [ "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287" ], "same_host": [ "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287" ], "query": [ ">=", "$free_ram_mb", "1024" ], "target_cell": "foobar", "build_near_host_ip": "192.168.1.1", "cidr": "/24", "reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1" } } ` actual, err := ext.ToServerCreateMap() th.AssertNoErr(t, err) th.CheckJSONEquals(t, expected, actual) } func TestCreateOptsWithComplexQuery(t *testing.T) { base := servers.CreateOpts{ Name: "createdserver", ImageRef: "asdfasdfasdf", FlavorRef: "performance1-1", } schedulerHints := schedulerhints.SchedulerHints{ Group: "101aed42-22d9-4a3e-9ba1-21103b0d1aba", DifferentHost: []string{ "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287", }, SameHost: []string{ "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287", }, Query: []interface{}{"and", []string{">=", "$free_ram_mb", "1024"}, []string{">=", "$free_disk_mb", "204800"}}, TargetCell: "foobar", BuildNearHostIP: "192.168.1.1/24", AdditionalProperties: map[string]interface{}{"reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, } ext := schedulerhints.CreateOptsExt{ CreateOptsBuilder: base, SchedulerHints: schedulerHints, } expected := ` { "server": { "name": "createdserver", "imageRef": "asdfasdfasdf", "flavorRef": "performance1-1" }, "os:scheduler_hints": { "group": "101aed42-22d9-4a3e-9ba1-21103b0d1aba", "different_host": [ "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287" ], "same_host": [ "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287" ], "query": [ "and", [">=", "$free_ram_mb", "1024"], [">=", "$free_disk_mb", "204800"] ], "target_cell": "foobar", "build_near_host_ip": "192.168.1.1", "cidr": "/24", "reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1" } } ` actual, err := ext.ToServerCreateMap() th.AssertNoErr(t, err) th.CheckJSONEquals(t, expected, actual) } secgroups/000077500000000000000000000000001335005366400337005ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000047361335005366400350060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/secgroups/* Package secgroups provides the ability to manage security groups through the Nova API. This API has been deprecated and will be removed from a future release of the Nova API service. For environments that support this extension, this package can be used regardless of if either Neutron or nova-network is used as the cloud's network service. Example to List Security Groups allPages, err := secroups.List(computeClient).AllPages() if err != nil { panic(err) } allSecurityGroups, err := secgroups.ExtractSecurityGroups(allPages) if err != nil { panic(err) } for _, sg := range allSecurityGroups { fmt.Printf("%+v\n", sg) } Example to List Security Groups by Server serverID := "aab3ad01-9956-4623-a29b-24afc89a7d36" allPages, err := secroups.ListByServer(computeClient, serverID).AllPages() if err != nil { panic(err) } allSecurityGroups, err := secgroups.ExtractSecurityGroups(allPages) if err != nil { panic(err) } for _, sg := range allSecurityGroups { fmt.Printf("%+v\n", sg) } Example to Create a Security Group createOpts := secgroups.CreateOpts{ Name: "group_name", Description: "A Security Group", } sg, err := secgroups.Create(computeClient, createOpts).Extract() if err != nil { panic(err) } Example to Create a Security Group Rule sgID := "37d94f8a-d136-465c-ae46-144f0d8ef141" createOpts := secgroups.CreateRuleOpts{ ParentGroupID: sgID, FromPort: 22, ToPort: 22, IPProtocol: "tcp", CIDR: "0.0.0.0/0", } rule, err := secgroups.CreateRule(computeClient, createOpts).Extract() if err != nil { panic(err) } Example to Add a Security Group to a Server serverID := "aab3ad01-9956-4623-a29b-24afc89a7d36" sgID := "37d94f8a-d136-465c-ae46-144f0d8ef141" err := secgroups.AddServer(computeClient, serverID, sgID).ExtractErr() if err != nil { panic(err) } Example to Remove a Security Group from a Server serverID := "aab3ad01-9956-4623-a29b-24afc89a7d36" sgID := "37d94f8a-d136-465c-ae46-144f0d8ef141" err := secgroups.RemoveServer(computeClient, serverID, sgID).ExtractErr() if err != nil { panic(err) } Example to Delete a Security Group sgID := "37d94f8a-d136-465c-ae46-144f0d8ef141" err := secgroups.Delete(computeClient, sgID).ExtractErr() if err != nil { panic(err) } Example to Delete a Security Group Rule ruleID := "6221fe3e-383d-46c9-a3a6-845e66c1e8b4" err := secgroups.DeleteRule(computeClient, ruleID).ExtractErr() if err != nil { panic(err) } */ package secgroups requests.go000066400000000000000000000150001335005366400360760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/secgroupspackage secgroups import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) func commonList(client *gophercloud.ServiceClient, url string) pagination.Pager { return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return SecurityGroupPage{pagination.SinglePageBase(r)} }) } // List will return a collection of all the security groups for a particular // tenant. func List(client *gophercloud.ServiceClient) pagination.Pager { return commonList(client, rootURL(client)) } // ListByServer will return a collection of all the security groups which are // associated with a particular server. func ListByServer(client *gophercloud.ServiceClient, serverID string) pagination.Pager { return commonList(client, listByServerURL(client, serverID)) } // GroupOpts is the underlying struct responsible for creating or updating // security groups. It therefore represents the mutable attributes of a // security group. type GroupOpts struct { // the name of your security group. Name string `json:"name" required:"true"` // the description of your security group. Description string `json:"description" required:"true"` } // CreateOpts is the struct responsible for creating a security group. type CreateOpts GroupOpts // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToSecGroupCreateMap() (map[string]interface{}, error) } // ToSecGroupCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToSecGroupCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "security_group") } // Create will create a new security group. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSecGroupCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // UpdateOpts is the struct responsible for updating an existing security group. type UpdateOpts GroupOpts // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToSecGroupUpdateMap() (map[string]interface{}, error) } // ToSecGroupUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "security_group") } // Update will modify the mutable properties of a security group, notably its // name and description. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToSecGroupUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Put(resourceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Get will return details for a particular security group. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(resourceURL(client, id), &r.Body, nil) return } // Delete will permanently delete a security group from the project. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(resourceURL(client, id), nil) return } // CreateRuleOpts represents the configuration for adding a new rule to an // existing security group. type CreateRuleOpts struct { // ID is the ID of the group that this rule will be added to. ParentGroupID string `json:"parent_group_id" required:"true"` // FromPort is the lower bound of the port range that will be opened. // Use -1 to allow all ICMP traffic. FromPort int `json:"from_port"` // ToPort is the upper bound of the port range that will be opened. // Use -1 to allow all ICMP traffic. ToPort int `json:"to_port"` // IPProtocol the protocol type that will be allowed, e.g. TCP. IPProtocol string `json:"ip_protocol" required:"true"` // CIDR is the network CIDR to allow traffic from. // This is ONLY required if FromGroupID is blank. This represents the IP // range that will be the source of network traffic to your security group. // Use 0.0.0.0/0 to allow all IP addresses. CIDR string `json:"cidr,omitempty" or:"FromGroupID"` // FromGroupID represents another security group to allow access. // This is ONLY required if CIDR is blank. This value represents the ID of a // group that forwards traffic to the parent group. So, instead of accepting // network traffic from an entire IP range, you can instead refine the // inbound source by an existing security group. FromGroupID string `json:"group_id,omitempty" or:"CIDR"` } // CreateRuleOptsBuilder allows extensions to add additional parameters to the // CreateRule request. type CreateRuleOptsBuilder interface { ToRuleCreateMap() (map[string]interface{}, error) } // ToRuleCreateMap builds a request body from CreateRuleOpts. func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "security_group_rule") } // CreateRule will add a new rule to an existing security group (whose ID is // specified in CreateRuleOpts). You have the option of controlling inbound // traffic from either an IP range (CIDR) or from another security group. func CreateRule(client *gophercloud.ServiceClient, opts CreateRuleOptsBuilder) (r CreateRuleResult) { b, err := opts.ToRuleCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(rootRuleURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // DeleteRule will permanently delete a rule from a security group. func DeleteRule(client *gophercloud.ServiceClient, id string) (r DeleteRuleResult) { _, r.Err = client.Delete(resourceRuleURL(client, id), nil) return } func actionMap(prefix, groupName string) map[string]map[string]string { return map[string]map[string]string{ prefix + "SecurityGroup": map[string]string{"name": groupName}, } } // AddServer will associate a server and a security group, enforcing the // rules of the group on the server. func AddServer(client *gophercloud.ServiceClient, serverID, groupName string) (r AddServerResult) { _, r.Err = client.Post(serverActionURL(client, serverID), actionMap("add", groupName), nil, nil) return } // RemoveServer will disassociate a server from a security group. func RemoveServer(client *gophercloud.ServiceClient, serverID, groupName string) (r RemoveServerResult) { _, r.Err = client.Post(serverActionURL(client, serverID), actionMap("remove", groupName), nil, nil) return } results.go000066400000000000000000000130221335005366400357260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/secgroupspackage secgroups import ( "encoding/json" "strconv" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // SecurityGroup represents a security group. type SecurityGroup struct { // The unique ID of the group. If Neutron is installed, this ID will be // represented as a string UUID; if Neutron is not installed, it will be a // numeric ID. For the sake of consistency, we always cast it to a string. ID string `json:"-"` // The human-readable name of the group, which needs to be unique. Name string `json:"name"` // The human-readable description of the group. Description string `json:"description"` // The rules which determine how this security group operates. Rules []Rule `json:"rules"` // The ID of the tenant to which this security group belongs. TenantID string `json:"tenant_id"` } func (r *SecurityGroup) UnmarshalJSON(b []byte) error { type tmp SecurityGroup var s struct { tmp ID interface{} `json:"id"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = SecurityGroup(s.tmp) switch t := s.ID.(type) { case float64: r.ID = strconv.FormatFloat(t, 'f', -1, 64) case string: r.ID = t } return err } // Rule represents a security group rule, a policy which determines how a // security group operates and what inbound traffic it allows in. type Rule struct { // The unique ID. If Neutron is installed, this ID will be // represented as a string UUID; if Neutron is not installed, it will be a // numeric ID. For the sake of consistency, we always cast it to a string. ID string `json:"-"` // The lower bound of the port range which this security group should open up. FromPort int `json:"from_port"` // The upper bound of the port range which this security group should open up. ToPort int `json:"to_port"` // The IP protocol (e.g. TCP) which the security group accepts. IPProtocol string `json:"ip_protocol"` // The CIDR IP range whose traffic can be received. IPRange IPRange `json:"ip_range"` // The security group ID to which this rule belongs. ParentGroupID string `json:"-"` // Not documented. Group Group } func (r *Rule) UnmarshalJSON(b []byte) error { type tmp Rule var s struct { tmp ID interface{} `json:"id"` ParentGroupID interface{} `json:"parent_group_id"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Rule(s.tmp) switch t := s.ID.(type) { case float64: r.ID = strconv.FormatFloat(t, 'f', -1, 64) case string: r.ID = t } switch t := s.ParentGroupID.(type) { case float64: r.ParentGroupID = strconv.FormatFloat(t, 'f', -1, 64) case string: r.ParentGroupID = t } return err } // IPRange represents the IP range whose traffic will be accepted by the // security group. type IPRange struct { CIDR string } // Group represents a group. type Group struct { TenantID string `json:"tenant_id"` Name string } // SecurityGroupPage is a single page of a SecurityGroup collection. type SecurityGroupPage struct { pagination.SinglePageBase } // IsEmpty determines whether or not a page of Security Groups contains any // results. func (page SecurityGroupPage) IsEmpty() (bool, error) { users, err := ExtractSecurityGroups(page) return len(users) == 0, err } // ExtractSecurityGroups returns a slice of SecurityGroups contained in a // single page of results. func ExtractSecurityGroups(r pagination.Page) ([]SecurityGroup, error) { var s struct { SecurityGroups []SecurityGroup `json:"security_groups"` } err := (r.(SecurityGroupPage)).ExtractInto(&s) return s.SecurityGroups, err } type commonResult struct { gophercloud.Result } // CreateResult represents the result of a create operation. Call its Extract // method to interpret the result as a SecurityGroup. type CreateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret the result as a SecurityGroup. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret the result as a SecurityGroup. type UpdateResult struct { commonResult } // Extract will extract a SecurityGroup struct from most responses. func (r commonResult) Extract() (*SecurityGroup, error) { var s struct { SecurityGroup *SecurityGroup `json:"security_group"` } err := r.ExtractInto(&s) return s.SecurityGroup, err } // CreateRuleResult represents the result when adding rules to a security group. // Call its Extract method to interpret the result as a Rule. type CreateRuleResult struct { gophercloud.Result } // Extract will extract a Rule struct from a CreateRuleResult. func (r CreateRuleResult) Extract() (*Rule, error) { var s struct { Rule *Rule `json:"security_group_rule"` } err := r.ExtractInto(&s) return s.Rule, err } // DeleteResult is the response from delete operation. Call its ExtractErr // method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // DeleteRuleResult is the response from a DeleteRule operation. Call its // ExtractErr method to determine if the request succeeded or failed. type DeleteRuleResult struct { gophercloud.ErrResult } // AddServerResult is the response from an AddServer operation. Call its // ExtractErr method to determine if the request succeeded or failed. type AddServerResult struct { gophercloud.ErrResult } // RemoveServerResult is the response from a RemoveServer operation. Call its // ExtractErr method to determine if the request succeeded or failed. type RemoveServerResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400353555ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/secgroupsdoc.go000066400000000000000000000000501335005366400364440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/secgroups/testing// secgroups unit tests package testing fixtures.go000066400000000000000000000162601335005366400375620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/secgroups/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const rootPath = "/os-security-groups" const listGroupsJSON = ` { "security_groups": [ { "description": "default", "id": "{groupID}", "name": "default", "rules": [], "tenant_id": "openstack" } ] } ` func mockListGroupsResponse(t *testing.T) { th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, listGroupsJSON) }) } func mockListGroupsByServerResponse(t *testing.T, serverID string) { url := fmt.Sprintf("/servers/%s%s", serverID, rootPath) th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, listGroupsJSON) }) } func mockCreateGroupResponse(t *testing.T) { th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, ` { "security_group": { "name": "test", "description": "something" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "security_group": { "description": "something", "id": "{groupID}", "name": "test", "rules": [], "tenant_id": "openstack" } } `) }) } func mockUpdateGroupResponse(t *testing.T, groupID string) { url := fmt.Sprintf("%s/%s", rootPath, groupID) th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, ` { "security_group": { "name": "new_name", "description": "new_desc" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "security_group": { "description": "something", "id": "{groupID}", "name": "new_name", "rules": [], "tenant_id": "openstack" } } `) }) } func mockGetGroupsResponse(t *testing.T, groupID string) { url := fmt.Sprintf("%s/%s", rootPath, groupID) th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "security_group": { "description": "default", "id": "{groupID}", "name": "default", "rules": [ { "from_port": 80, "group": { "tenant_id": "openstack", "name": "default" }, "ip_protocol": "TCP", "to_port": 85, "parent_group_id": "{groupID}", "ip_range": { "cidr": "0.0.0.0" }, "id": "{ruleID}" } ], "tenant_id": "openstack" } } `) }) } func mockGetNumericIDGroupResponse(t *testing.T, groupID int) { url := fmt.Sprintf("%s/%d", rootPath, groupID) th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "security_group": { "id": %d } } `, groupID) }) } func mockGetNumericIDGroupRuleResponse(t *testing.T, groupID int) { url := fmt.Sprintf("%s/%d", rootPath, groupID) th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "security_group": { "id": %d, "rules": [ { "parent_group_id": %d, "id": %d } ] } } `, groupID, groupID, groupID) }) } func mockDeleteGroupResponse(t *testing.T, groupID string) { url := fmt.Sprintf("%s/%s", rootPath, groupID) th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) }) } func mockAddRuleResponse(t *testing.T) { th.Mux.HandleFunc("/os-security-group-rules", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, ` { "security_group_rule": { "from_port": 22, "ip_protocol": "TCP", "to_port": 22, "parent_group_id": "{groupID}", "cidr": "0.0.0.0/0" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "security_group_rule": { "from_port": 22, "group": {}, "ip_protocol": "TCP", "to_port": 22, "parent_group_id": "{groupID}", "ip_range": { "cidr": "0.0.0.0/0" }, "id": "{ruleID}" } }`) }) } func mockAddRuleResponseICMPZero(t *testing.T) { th.Mux.HandleFunc("/os-security-group-rules", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, ` { "security_group_rule": { "from_port": 0, "ip_protocol": "ICMP", "to_port": 0, "parent_group_id": "{groupID}", "cidr": "0.0.0.0/0" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "security_group_rule": { "from_port": 0, "group": {}, "ip_protocol": "ICMP", "to_port": 0, "parent_group_id": "{groupID}", "ip_range": { "cidr": "0.0.0.0/0" }, "id": "{ruleID}" } }`) }) } func mockDeleteRuleResponse(t *testing.T, ruleID string) { url := fmt.Sprintf("/os-security-group-rules/%s", ruleID) th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) }) } func mockAddServerToGroupResponse(t *testing.T, serverID string) { url := fmt.Sprintf("/servers/%s/action", serverID) th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, ` { "addSecurityGroup": { "name": "test" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) }) } func mockRemoveServerFromGroupResponse(t *testing.T, serverID string) { url := fmt.Sprintf("/servers/%s/action", serverID) th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, ` { "removeSecurityGroup": { "name": "test" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000146361335005366400406300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/secgroups/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const ( serverID = "{serverID}" groupID = "{groupID}" ruleID = "{ruleID}" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockListGroupsResponse(t) count := 0 err := secgroups.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := secgroups.ExtractSecurityGroups(page) if err != nil { t.Errorf("Failed to extract users: %v", err) return false, err } expected := []secgroups.SecurityGroup{ { ID: groupID, Description: "default", Name: "default", Rules: []secgroups.Rule{}, TenantID: "openstack", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, count) } func TestListByServer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockListGroupsByServerResponse(t, serverID) count := 0 err := secgroups.ListByServer(client.ServiceClient(), serverID).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := secgroups.ExtractSecurityGroups(page) if err != nil { t.Errorf("Failed to extract users: %v", err) return false, err } expected := []secgroups.SecurityGroup{ { ID: groupID, Description: "default", Name: "default", Rules: []secgroups.Rule{}, TenantID: "openstack", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, count) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockCreateGroupResponse(t) opts := secgroups.CreateOpts{ Name: "test", Description: "something", } group, err := secgroups.Create(client.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) expected := &secgroups.SecurityGroup{ ID: groupID, Name: "test", Description: "something", TenantID: "openstack", Rules: []secgroups.Rule{}, } th.AssertDeepEquals(t, expected, group) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockUpdateGroupResponse(t, groupID) opts := secgroups.UpdateOpts{ Name: "new_name", Description: "new_desc", } group, err := secgroups.Update(client.ServiceClient(), groupID, opts).Extract() th.AssertNoErr(t, err) expected := &secgroups.SecurityGroup{ ID: groupID, Name: "new_name", Description: "something", TenantID: "openstack", Rules: []secgroups.Rule{}, } th.AssertDeepEquals(t, expected, group) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockGetGroupsResponse(t, groupID) group, err := secgroups.Get(client.ServiceClient(), groupID).Extract() th.AssertNoErr(t, err) expected := &secgroups.SecurityGroup{ ID: groupID, Description: "default", Name: "default", TenantID: "openstack", Rules: []secgroups.Rule{ { FromPort: 80, ToPort: 85, IPProtocol: "TCP", IPRange: secgroups.IPRange{CIDR: "0.0.0.0"}, Group: secgroups.Group{TenantID: "openstack", Name: "default"}, ParentGroupID: groupID, ID: ruleID, }, }, } th.AssertDeepEquals(t, expected, group) } func TestGetNumericID(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() numericGroupID := 12345 mockGetNumericIDGroupResponse(t, numericGroupID) group, err := secgroups.Get(client.ServiceClient(), "12345").Extract() th.AssertNoErr(t, err) expected := &secgroups.SecurityGroup{ID: "12345"} th.AssertDeepEquals(t, expected, group) } func TestGetNumericRuleID(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() numericGroupID := 12345 mockGetNumericIDGroupRuleResponse(t, numericGroupID) group, err := secgroups.Get(client.ServiceClient(), "12345").Extract() th.AssertNoErr(t, err) expected := &secgroups.SecurityGroup{ ID: "12345", Rules: []secgroups.Rule{ { ParentGroupID: "12345", ID: "12345", }, }, } th.AssertDeepEquals(t, expected, group) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockDeleteGroupResponse(t, groupID) err := secgroups.Delete(client.ServiceClient(), groupID).ExtractErr() th.AssertNoErr(t, err) } func TestAddRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockAddRuleResponse(t) opts := secgroups.CreateRuleOpts{ ParentGroupID: groupID, FromPort: 22, ToPort: 22, IPProtocol: "TCP", CIDR: "0.0.0.0/0", } rule, err := secgroups.CreateRule(client.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) expected := &secgroups.Rule{ FromPort: 22, ToPort: 22, Group: secgroups.Group{}, IPProtocol: "TCP", ParentGroupID: groupID, IPRange: secgroups.IPRange{CIDR: "0.0.0.0/0"}, ID: ruleID, } th.AssertDeepEquals(t, expected, rule) } func TestAddRuleICMPZero(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockAddRuleResponseICMPZero(t) opts := secgroups.CreateRuleOpts{ ParentGroupID: groupID, FromPort: 0, ToPort: 0, IPProtocol: "ICMP", CIDR: "0.0.0.0/0", } rule, err := secgroups.CreateRule(client.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) expected := &secgroups.Rule{ FromPort: 0, ToPort: 0, Group: secgroups.Group{}, IPProtocol: "ICMP", ParentGroupID: groupID, IPRange: secgroups.IPRange{CIDR: "0.0.0.0/0"}, ID: ruleID, } th.AssertDeepEquals(t, expected, rule) } func TestDeleteRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockDeleteRuleResponse(t, ruleID) err := secgroups.DeleteRule(client.ServiceClient(), ruleID).ExtractErr() th.AssertNoErr(t, err) } func TestAddServer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockAddServerToGroupResponse(t, serverID) err := secgroups.AddServer(client.ServiceClient(), serverID, "test").ExtractErr() th.AssertNoErr(t, err) } func TestRemoveServer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockRemoveServerFromGroupResponse(t, serverID) err := secgroups.RemoveServer(client.ServiceClient(), serverID, "test").ExtractErr() th.AssertNoErr(t, err) } urls.go000066400000000000000000000014471335005366400352220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/secgroupspackage secgroups import "github.com/gophercloud/gophercloud" const ( secgrouppath = "os-security-groups" rulepath = "os-security-group-rules" ) func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(secgrouppath, id) } func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(secgrouppath) } func listByServerURL(c *gophercloud.ServiceClient, serverID string) string { return c.ServiceURL("servers", serverID, secgrouppath) } func rootRuleURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rulepath) } func resourceRuleURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rulepath, id) } func serverActionURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("servers", id, "action") } servergroups/000077500000000000000000000000001335005366400344345ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000014521335005366400355320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/servergroups/* Package servergroups provides the ability to manage server groups. Example to List Server Groups allpages, err := servergroups.List(computeClient).AllPages() if err != nil { panic(err) } allServerGroups, err := servergroups.ExtractServerGroups(allPages) if err != nil { panic(err) } for _, sg := range allServerGroups { fmt.Printf("%#v\n", sg) } Example to Create a Server Group createOpts := servergroups.CreateOpts{ Name: "my_sg", Policies: []string{"anti-affinity"}, } sg, err := servergroups.Create(computeClient, createOpts).Extract() if err != nil { panic(err) } Example to Delete a Server Group sgID := "7a6f29ad-e34d-4368-951a-58a08f11cfb7" err := servergroups.Delete(computeClient, sgID).ExtractErr() if err != nil { panic(err) } */ package servergroups requests.go000066400000000000000000000035121335005366400366370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/servergroupspackage servergroups import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List returns a Pager that allows you to iterate over a collection of // ServerGroups. func List(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { return ServerGroupPage{pagination.SinglePageBase(r)} }) } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToServerGroupCreateMap() (map[string]interface{}, error) } // CreateOpts specifies Server Group creation parameters. type CreateOpts struct { // Name is the name of the server group Name string `json:"name" required:"true"` // Policies are the server group policies Policies []string `json:"policies" required:"true"` } // ToServerGroupCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToServerGroupCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "server_group") } // Create requests the creation of a new Server Group. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToServerGroupCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Get returns data about a previously created ServerGroup. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // Delete requests the deletion of a previously allocated ServerGroup. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } results.go000066400000000000000000000045371335005366400364750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/servergroupspackage servergroups import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // A ServerGroup creates a policy for instance placement in the cloud. type ServerGroup struct { // ID is the unique ID of the Server Group. ID string `json:"id"` // Name is the common name of the server group. Name string `json:"name"` // Polices are the group policies. // // Normally a single policy is applied: // // "affinity" will place all servers within the server group on the // same compute node. // // "anti-affinity" will place servers within the server group on different // compute nodes. Policies []string `json:"policies"` // Members are the members of the server group. Members []string `json:"members"` // Metadata includes a list of all user-specified key-value pairs attached // to the Server Group. Metadata map[string]interface{} } // ServerGroupPage stores a single page of all ServerGroups results from a // List call. type ServerGroupPage struct { pagination.SinglePageBase } // IsEmpty determines whether or not a ServerGroupsPage is empty. func (page ServerGroupPage) IsEmpty() (bool, error) { va, err := ExtractServerGroups(page) return len(va) == 0, err } // ExtractServerGroups interprets a page of results as a slice of // ServerGroups. func ExtractServerGroups(r pagination.Page) ([]ServerGroup, error) { var s struct { ServerGroups []ServerGroup `json:"server_groups"` } err := (r.(ServerGroupPage)).ExtractInto(&s) return s.ServerGroups, err } type ServerGroupResult struct { gophercloud.Result } // Extract is a method that attempts to interpret any Server Group resource // response as a ServerGroup struct. func (r ServerGroupResult) Extract() (*ServerGroup, error) { var s struct { ServerGroup *ServerGroup `json:"server_group"` } err := r.ExtractInto(&s) return s.ServerGroup, err } // CreateResult is the response from a Create operation. Call its Extract method // to interpret it as a ServerGroup. type CreateResult struct { ServerGroupResult } // GetResult is the response from a Get operation. Call its Extract method to // interpret it as a ServerGroup. type GetResult struct { ServerGroupResult } // DeleteResult is the response from a Delete operation. Call its ExtractErr // method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400361115ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/servergroupsdoc.go000066400000000000000000000000531335005366400372030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/servergroups/testing// servergroups unit tests package testing fixtures.go000066400000000000000000000102611335005366400403110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/servergroups/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListOutput is a sample response to a List call. const ListOutput = ` { "server_groups": [ { "id": "616fb98f-46ca-475e-917e-2563e5a8cd19", "name": "test", "policies": [ "anti-affinity" ], "members": [], "metadata": {} }, { "id": "4d8c3732-a248-40ed-bebc-539a6ffd25c0", "name": "test2", "policies": [ "affinity" ], "members": [], "metadata": {} } ] } ` // GetOutput is a sample response to a Get call. const GetOutput = ` { "server_group": { "id": "616fb98f-46ca-475e-917e-2563e5a8cd19", "name": "test", "policies": [ "anti-affinity" ], "members": [], "metadata": {} } } ` // CreateOutput is a sample response to a Post call const CreateOutput = ` { "server_group": { "id": "616fb98f-46ca-475e-917e-2563e5a8cd19", "name": "test", "policies": [ "anti-affinity" ], "members": [], "metadata": {} } } ` // FirstServerGroup is the first result in ListOutput. var FirstServerGroup = servergroups.ServerGroup{ ID: "616fb98f-46ca-475e-917e-2563e5a8cd19", Name: "test", Policies: []string{ "anti-affinity", }, Members: []string{}, Metadata: map[string]interface{}{}, } // SecondServerGroup is the second result in ListOutput. var SecondServerGroup = servergroups.ServerGroup{ ID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0", Name: "test2", Policies: []string{ "affinity", }, Members: []string{}, Metadata: map[string]interface{}{}, } // ExpectedServerGroupSlice is the slice of results that should be parsed // from ListOutput, in the expected order. var ExpectedServerGroupSlice = []servergroups.ServerGroup{FirstServerGroup, SecondServerGroup} // CreatedServerGroup is the parsed result from CreateOutput. var CreatedServerGroup = servergroups.ServerGroup{ ID: "616fb98f-46ca-475e-917e-2563e5a8cd19", Name: "test", Policies: []string{ "anti-affinity", }, Members: []string{}, Metadata: map[string]interface{}{}, } // HandleListSuccessfully configures the test server to respond to a List request. func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-server-groups", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ListOutput) }) } // HandleGetSuccessfully configures the test server to respond to a Get request // for an existing server group func HandleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-server-groups/4d8c3732-a248-40ed-bebc-539a6ffd25c0", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, GetOutput) }) } // HandleCreateSuccessfully configures the test server to respond to a Create request // for a new server group func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-server-groups", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, ` { "server_group": { "name": "test", "policies": [ "anti-affinity" ] } } `) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, CreateOutput) }) } // HandleDeleteSuccessfully configures the test server to respond to a Delete request for a // an existing server group func HandleDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-server-groups/616fb98f-46ca-475e-917e-2563e5a8cd19", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000030521335005366400413520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/servergroups/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t) count := 0 err := servergroups.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := servergroups.ExtractServerGroups(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedServerGroupSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, 1, count) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateSuccessfully(t) actual, err := servergroups.Create(client.ServiceClient(), servergroups.CreateOpts{ Name: "test", Policies: []string{"anti-affinity"}, }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &CreatedServerGroup, actual) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t) actual, err := servergroups.Get(client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstServerGroup, actual) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSuccessfully(t) err := servergroups.Delete(client.ServiceClient(), "616fb98f-46ca-475e-917e-2563e5a8cd19").ExtractErr() th.AssertNoErr(t, err) } urls.go000066400000000000000000000010511335005366400357450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/servergroupspackage servergroups import "github.com/gophercloud/gophercloud" const resourcePath = "os-server-groups" func resourceURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } func listURL(c *gophercloud.ServiceClient) string { return resourceURL(c) } func createURL(c *gophercloud.ServiceClient) string { return resourceURL(c) } func getURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id) } func deleteURL(c *gophercloud.ServiceClient, id string) string { return getURL(c, id) } serverusage/000077500000000000000000000000001335005366400342215ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000007551335005366400353240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/serverusage/* Package serverusage provides the ability the ability to extend a server result with the extended usage information. Example to Get an extended information: type serverUsageExt struct { servers.Server serverusage.UsageExt } var serverWithUsageExt serverUsageExt err := servers.Get(computeClient, "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithUsageExt) if err != nil { panic(err) } fmt.Printf("%+v\n", serverWithUsageExt) */ package serverusage results.go000066400000000000000000000013731335005366400362550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/serverusagepackage serverusage import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" ) // UsageExt represents OS-SRV-USG server response fields. type UsageExt struct { LaunchedAt time.Time `json:"-"` TerminatedAt time.Time `json:"-"` } // UnmarshalJSON helps to unmarshal UsageExt fields into needed values. func (r *UsageExt) UnmarshalJSON(b []byte) error { type tmp UsageExt var s struct { tmp LaunchedAt gophercloud.JSONRFC3339MilliNoZ `json:"OS-SRV-USG:launched_at"` TerminatedAt gophercloud.JSONRFC3339MilliNoZ `json:"OS-SRV-USG:terminated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = UsageExt(s.tmp) r.LaunchedAt = time.Time(s.LaunchedAt) r.TerminatedAt = time.Time(s.TerminatedAt) return nil } testing/000077500000000000000000000000001335005366400356765ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/serverusagedoc.go000066400000000000000000000000201335005366400367620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/serverusage/testingpackage testing fixtures.go000066400000000000000000000012321335005366400400740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/serverusage/testingpackage testing // ServerWithUsageExtResult represents a raw server response from the Compute API // with OS-SRV-USG data. // Most of the actual fields were deleted from the response. const ServerWithUsageExtResult = ` { "server": { "OS-SRV-USG:launched_at": "2018-07-27T09:15:55.000000", "OS-SRV-USG:terminated_at": null, "created": "2018-07-27T09:15:48Z", "updated": "2018-07-27T09:15:55Z", "id": "d650a0ce-17c3-497d-961a-43c4af80998a", "name": "test_instance", "status": "ACTIVE", "user_id": "0f2f3822679e4b3ea073e5d1c6ed5f02", "tenant_id": "424e7cf0243c468ca61732ba45973b3e" } } ` requests_test.go000066400000000000000000000032411335005366400411370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/serverusage/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/serverusage" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestServerWithUsageExt(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/d650a0ce-17c3-497d-961a-43c4af80998a", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, ServerWithUsageExtResult) }) type serverUsageExt struct { servers.Server serverusage.UsageExt } var serverWithUsageExt serverUsageExt err := servers.Get(fake.ServiceClient(), "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithUsageExt) th.AssertNoErr(t, err) th.AssertEquals(t, serverWithUsageExt.LaunchedAt, time.Date(2018, 07, 27, 9, 15, 55, 0, time.UTC)) th.AssertEquals(t, serverWithUsageExt.TerminatedAt, time.Time{}) th.AssertEquals(t, serverWithUsageExt.Created, time.Date(2018, 07, 27, 9, 15, 48, 0, time.UTC)) th.AssertEquals(t, serverWithUsageExt.Updated, time.Date(2018, 07, 27, 9, 15, 55, 0, time.UTC)) th.AssertEquals(t, serverWithUsageExt.ID, "d650a0ce-17c3-497d-961a-43c4af80998a") th.AssertEquals(t, serverWithUsageExt.Name, "test_instance") th.AssertEquals(t, serverWithUsageExt.Status, "ACTIVE") th.AssertEquals(t, serverWithUsageExt.UserID, "0f2f3822679e4b3ea073e5d1c6ed5f02") th.AssertEquals(t, serverWithUsageExt.TenantID, "424e7cf0243c468ca61732ba45973b3e") } services/000077500000000000000000000000001335005366400335115ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000006341335005366400346100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/services/* Package services returns information about the compute services in the OpenStack cloud. Example of Retrieving list of all services allPages, err := services.List(computeClient).AllPages() if err != nil { panic(err) } allServices, err := services.ExtractServices(allPages) if err != nil { panic(err) } for _, service := range allServices { fmt.Printf("%+v\n", service) } */ package services requests.go000066400000000000000000000006151335005366400357150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/servicespackage services import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List makes a request against the API to list services. func List(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { return ServicePage{pagination.SinglePageBase(r)} }) } results.go000066400000000000000000000031741335005366400355460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/servicespackage services import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Service represents a Compute service in the OpenStack cloud. type Service struct { // The binary name of the service. Binary string `json:"binary"` // The reason for disabling a service. DisabledReason string `json:"disabled_reason"` // The name of the host. Host string `json:"host"` // The id of the service. ID int `json:"id"` // The state of the service. One of up or down. State string `json:"state"` // The status of the service. One of enabled or disabled. Status string `json:"status"` // The date and time when the resource was updated. UpdatedAt time.Time `json:"-"` // The availability zone name. Zone string `json:"zone"` } // UnmarshalJSON to override default func (r *Service) UnmarshalJSON(b []byte) error { type tmp Service var s struct { tmp UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Service(s.tmp) r.UpdatedAt = time.Time(s.UpdatedAt) return nil } // ServicePage represents a single page of all Services from a List request. type ServicePage struct { pagination.SinglePageBase } // IsEmpty determines whether or not a page of Services contains any results. func (page ServicePage) IsEmpty() (bool, error) { services, err := ExtractServices(page) return len(services) == 0, err } func ExtractServices(r pagination.Page) ([]Service, error) { var s struct { Service []Service `json:"services"` } err := (r.(ServicePage)).ExtractInto(&s) return s.Service, err } testing/000077500000000000000000000000001335005366400351665ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/servicesfixtures.go000066400000000000000000000065331335005366400373750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/services/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/services" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ServiceListBody is sample response to the List call const ServiceListBody = ` { "services": [ { "id": 1, "binary": "nova-scheduler", "disabled_reason": "test1", "host": "host1", "state": "up", "status": "disabled", "updated_at": "2012-10-29T13:42:02.000000", "forced_down": false, "zone": "internal" }, { "id": 2, "binary": "nova-compute", "disabled_reason": "test2", "host": "host1", "state": "up", "status": "disabled", "updated_at": "2012-10-29T13:42:05.000000", "forced_down": false, "zone": "nova" }, { "id": 3, "binary": "nova-scheduler", "disabled_reason": null, "host": "host2", "state": "down", "status": "enabled", "updated_at": "2012-09-19T06:55:34.000000", "forced_down": false, "zone": "internal" }, { "id": 4, "binary": "nova-compute", "disabled_reason": "test4", "host": "host2", "state": "down", "status": "disabled", "updated_at": "2012-09-18T08:03:38.000000", "forced_down": false, "zone": "nova" } ] } ` // First service from the ServiceListBody var FirstFakeService = services.Service{ Binary: "nova-scheduler", DisabledReason: "test1", Host: "host1", ID: 1, State: "up", Status: "disabled", UpdatedAt: time.Date(2012, 10, 29, 13, 42, 2, 0, time.UTC), Zone: "internal", } // Second service from the ServiceListBody var SecondFakeService = services.Service{ Binary: "nova-compute", DisabledReason: "test2", Host: "host1", ID: 2, State: "up", Status: "disabled", UpdatedAt: time.Date(2012, 10, 29, 13, 42, 5, 0, time.UTC), Zone: "nova", } // Third service from the ServiceListBody var ThirdFakeService = services.Service{ Binary: "nova-scheduler", DisabledReason: "", Host: "host2", ID: 3, State: "down", Status: "enabled", UpdatedAt: time.Date(2012, 9, 19, 6, 55, 34, 0, time.UTC), Zone: "internal", } // Fourth service from the ServiceListBody var FourthFakeService = services.Service{ Binary: "nova-compute", DisabledReason: "test4", Host: "host2", ID: 4, State: "down", Status: "disabled", UpdatedAt: time.Date(2012, 9, 18, 8, 3, 38, 0, time.UTC), Zone: "nova", } // HandleListSuccessfully configures the test server to respond to a List request. func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-services", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ServiceListBody) }) } requests_test.go000066400000000000000000000020511335005366400404250ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/services/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/services" "github.com/gophercloud/gophercloud/pagination" "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListServices(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() HandleListSuccessfully(t) pages := 0 err := services.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := services.ExtractServices(page) if err != nil { return false, err } if len(actual) != 4 { t.Fatalf("Expected 4 services, got %d", len(actual)) } testhelper.CheckDeepEquals(t, FirstFakeService, actual[0]) testhelper.CheckDeepEquals(t, SecondFakeService, actual[1]) testhelper.CheckDeepEquals(t, ThirdFakeService, actual[2]) testhelper.CheckDeepEquals(t, FourthFakeService, actual[3]) return true, nil }) testhelper.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } urls.go000066400000000000000000000002311335005366400350210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/servicespackage services import "github.com/gophercloud/gophercloud" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-services") } startstop/000077500000000000000000000000001335005366400337315ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000006601335005366400350270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/startstop/* Package startstop provides functionality to start and stop servers that have been provisioned by the OpenStack Compute service. Example to Stop and Start a Server serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" err := startstop.Stop(computeClient, serverID).ExtractErr() if err != nil { panic(err) } err := startstop.Start(computeClient, serverID).ExtractErr() if err != nil { panic(err) } */ package startstop requests.go000066400000000000000000000012611335005366400361330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/startstoppackage startstop import "github.com/gophercloud/gophercloud" func actionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id, "action") } // Start is the operation responsible for starting a Compute server. func Start(client *gophercloud.ServiceClient, id string) (r StartResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-start": nil}, nil, nil) return } // Stop is the operation responsible for stopping a Compute server. func Stop(client *gophercloud.ServiceClient, id string) (r StopResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-stop": nil}, nil, nil) return } results.go000066400000000000000000000006561335005366400357700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/startstoppackage startstop import "github.com/gophercloud/gophercloud" // StartResult is the response from a Start operation. Call its ExtractErr // method to determine if the request succeeded or failed. type StartResult struct { gophercloud.ErrResult } // StopResult is the response from Stop operation. Call its ExtractErr // method to determine if the request succeeded or failed. type StopResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400354065ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/startstopdoc.go000066400000000000000000000000501335005366400364750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/startstop/testing// startstop unit tests package testing fixtures.go000066400000000000000000000014501335005366400376060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/startstop/testingpackage testing import ( "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func mockStartServerResponse(t *testing.T, id string) { th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{"os-start": null}`) w.WriteHeader(http.StatusAccepted) }) } func mockStopServerResponse(t *testing.T, id string) { th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{"os-stop": null}`) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000012441335005366400406500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/startstop/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/startstop" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const serverID = "{serverId}" func TestStart(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockStartServerResponse(t, serverID) err := startstop.Start(client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } func TestStop(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockStopServerResponse(t, serverID) err := startstop.Stop(client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } suspendresume/000077500000000000000000000000001335005366400345705ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000007141335005366400356660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/suspendresume/* Package suspendresume provides functionality to suspend and resume servers that have been provisioned by the OpenStack Compute service. Example to Suspend and Resume a Server serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" err := suspendresume.Suspend(computeClient, serverID).ExtractErr() if err != nil { panic(err) } err := suspendresume.Resume(computeClient, serverID).ExtractErr() if err != nil { panic(err) } */ package suspendresume requests.go000066400000000000000000000013041335005366400367700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/suspendresumepackage suspendresume import "github.com/gophercloud/gophercloud" func actionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id, "action") } // Suspend is the operation responsible for suspending a Compute server. func Suspend(client *gophercloud.ServiceClient, id string) (r SuspendResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"suspend": nil}, nil, nil) return } // Resume is the operation responsible for resuming a Compute server. func Resume(client *gophercloud.ServiceClient, id string) (r UnsuspendResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"resume": nil}, nil, nil) return } results.go000066400000000000000000000007121335005366400366200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/suspendresumepackage suspendresume import "github.com/gophercloud/gophercloud" // SuspendResult is the response from a Suspend operation. Call its // ExtractErr method to determine if the request succeeded or failed. type SuspendResult struct { gophercloud.ErrResult } // UnsuspendResult is the response from an Unsuspend operation. Call // its ExtractErr method to determine if the request succeeded or failed. type UnsuspendResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400362455ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/suspendresumedoc.go000066400000000000000000000000541335005366400373400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/suspendresume/testing// suspendresume unit tests package testing fixtures.go000066400000000000000000000014521335005366400404470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/suspendresume/testingpackage testing import ( "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func mockSuspendServerResponse(t *testing.T, id string) { th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{"suspend": null}`) w.WriteHeader(http.StatusAccepted) }) } func mockResumeServerResponse(t *testing.T, id string) { th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{"resume": null}`) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000012741335005366400415120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/suspendresume/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/suspendresume" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const serverID = "{serverId}" func TestSuspend(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockSuspendServerResponse(t, serverID) err := suspendresume.Suspend(client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } func TestResume(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockResumeServerResponse(t, serverID) err := suspendresume.Resume(client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } tenantnetworks/000077500000000000000000000000001335005366400347545ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000011621335005366400360500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/tenantnetworks/* Package tenantnetworks provides the ability for tenants to see information about the networks they have access to. This is a deprecated API and will be removed from the Nova API service in a future version. This API works in both Neutron and nova-network based OpenStack clouds. Example to List Networks Available to a Tenant allPages, err := tenantnetworks.List(computeClient).AllPages() if err != nil { panic(err) } allNetworks, err := tenantnetworks.ExtractNetworks(allPages) if err != nil { panic(err) } for _, network := range allNetworks { fmt.Printf("%+v\n", network) } */ package tenantnetworks requests.go000066400000000000000000000011561335005366400371610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/tenantnetworkspackage tenantnetworks import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List returns a Pager that allows you to iterate over a collection of Networks. func List(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { return NetworkPage{pagination.SinglePageBase(r)} }) } // Get returns data about a previously created Network. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } results.go000066400000000000000000000026361335005366400370130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/tenantnetworkspackage tenantnetworks import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // A Network represents a network that a server communicates on. type Network struct { // CIDR is the IPv4 subnet. CIDR string `json:"cidr"` // ID is the UUID of the network. ID string `json:"id"` // Name is the common name that the network has. Name string `json:"label"` } // NetworkPage stores a single page of all Networks results from a List call. type NetworkPage struct { pagination.SinglePageBase } // IsEmpty determines whether or not a NetworkPage is empty. func (page NetworkPage) IsEmpty() (bool, error) { va, err := ExtractNetworks(page) return len(va) == 0, err } // ExtractNetworks interprets a page of results as a slice of Network. func ExtractNetworks(r pagination.Page) ([]Network, error) { var s struct { Networks []Network `json:"networks"` } err := (r.(NetworkPage)).ExtractInto(&s) return s.Networks, err } type NetworkResult struct { gophercloud.Result } // Extract is a method that attempts to interpret any Network resource response // as a Network struct. func (r NetworkResult) Extract() (*Network, error) { var s struct { Network *Network `json:"network"` } err := r.ExtractInto(&s) return s.Network, err } // GetResult is the response from a Get operation. Call its Extract method to // interpret it as a Network. type GetResult struct { NetworkResult } testing/000077500000000000000000000000001335005366400364315ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/tenantnetworksdoc.go000066400000000000000000000000551335005366400375250ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/tenantnetworks/testing// tenantnetworks unit tests package testing fixtures.go000066400000000000000000000043431335005366400406350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/tenantnetworks/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListOutput is a sample response to a List call. const ListOutput = ` { "networks": [ { "cidr": "10.0.0.0/29", "id": "20c8acc0-f747-4d71-a389-46d078ebf047", "label": "mynet_0" }, { "cidr": "10.0.0.10/29", "id": "20c8acc0-f747-4d71-a389-46d078ebf000", "label": "mynet_1" } ] } ` // GetOutput is a sample response to a Get call. const GetOutput = ` { "network": { "cidr": "10.0.0.10/29", "id": "20c8acc0-f747-4d71-a389-46d078ebf000", "label": "mynet_1" } } ` // FirstNetwork is the first result in ListOutput. var nilTime time.Time var FirstNetwork = tenantnetworks.Network{ CIDR: "10.0.0.0/29", ID: "20c8acc0-f747-4d71-a389-46d078ebf047", Name: "mynet_0", } // SecondNetwork is the second result in ListOutput. var SecondNetwork = tenantnetworks.Network{ CIDR: "10.0.0.10/29", ID: "20c8acc0-f747-4d71-a389-46d078ebf000", Name: "mynet_1", } // ExpectedNetworkSlice is the slice of results that should be parsed // from ListOutput, in the expected order. var ExpectedNetworkSlice = []tenantnetworks.Network{FirstNetwork, SecondNetwork} // HandleListSuccessfully configures the test server to respond to a List request. func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-tenant-networks", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ListOutput) }) } // HandleGetSuccessfully configures the test server to respond to a Get request // for an existing network. func HandleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-tenant-networks/20c8acc0-f747-4d71-a389-46d078ebf000", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, GetOutput) }) } requests_test.go000066400000000000000000000017501335005366400416750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/tenantnetworks/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t) count := 0 err := tenantnetworks.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := tenantnetworks.ExtractNetworks(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedNetworkSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, 1, count) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t) actual, err := tenantnetworks.Get(client.ServiceClient(), "20c8acc0-f747-4d71-a389-46d078ebf000").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &SecondNetwork, actual) } urls.go000066400000000000000000000006031335005366400362670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/tenantnetworkspackage tenantnetworks import "github.com/gophercloud/gophercloud" const resourcePath = "os-tenant-networks" func resourceURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } func listURL(c *gophercloud.ServiceClient) string { return resourceURL(c) } func getURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id) } testing/000077500000000000000000000000001335005366400333435ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdelegate_test.go000066400000000000000000000031521335005366400365040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/testingpackage testing import ( "testing" common "github.com/gophercloud/gophercloud/openstack/common/extensions" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListExtensionsSuccessfully(t) count := 0 extensions.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := extensions.ExtractExtensions(page) th.AssertNoErr(t, err) expected := []common.Extension{ common.Extension{ Updated: "2013-01-20T00:00:00-00:00", Name: "Neutron Service Type Management", Links: []interface{}{}, Namespace: "http://docs.openstack.org/ext/neutron/service-type/api/v1.0", Alias: "service-type", Description: "API for retrieving service providers for Neutron advanced services", }, } th.AssertDeepEquals(t, expected, actual) return true, nil }) th.CheckEquals(t, 1, count) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetExtensionsSuccessfully(t) ext, err := extensions.Get(client.ServiceClient(), "agent").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ext.Updated, "2013-02-03T10:00:00-00:00") th.AssertEquals(t, ext.Name, "agent") th.AssertEquals(t, ext.Namespace, "http://docs.openstack.org/ext/agent/api/v2.0") th.AssertEquals(t, ext.Alias, "agent") th.AssertEquals(t, ext.Description, "The agent management extension.") } doc.go000066400000000000000000000000511335005366400344330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/testing// extensions unit tests package testing fixtures.go000066400000000000000000000026061335005366400355470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func HandleListExtensionsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/extensions", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ` { "extensions": [ { "updated": "2013-01-20T00:00:00-00:00", "name": "Neutron Service Type Management", "links": [], "namespace": "http://docs.openstack.org/ext/neutron/service-type/api/v1.0", "alias": "service-type", "description": "API for retrieving service providers for Neutron advanced services" } ] } `) }) } func HandleGetExtensionsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/extensions/agent", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "extension": { "updated": "2013-02-03T10:00:00-00:00", "name": "agent", "links": [], "namespace": "http://docs.openstack.org/ext/agent/api/v2.0", "alias": "agent", "description": "The agent management extension." } } `) }) } usage/000077500000000000000000000000001335005366400327725ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000026121335005366400340670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/usage/* Package usage provides information and interaction with the SimpleTenantUsage extension for the OpenStack Compute service. Due to the way the API responses are formatted, it is not recommended to query by using the AllPages convenience method. Instead, use the EachPage method to view each result page-by-page. This is because the usage calculations are done _per page_ and not as an aggregated total of the entire usage set. Example to Retrieve Usage for a Single Tenant: start := time.Date(2017, 01, 21, 10, 4, 20, 0, time.UTC) end := time.Date(2017, 01, 21, 10, 4, 20, 0, time.UTC) singleTenantOpts := usage.SingleTenantOpts{ Start: &start, End: &end, } err := usage.SingleTenant(computeClient, tenantID, singleTenantOpts).EachPage(func(page pagination.Page) (bool, error) { tenantUsage, err := usage.ExtractSingleTenant(page) if err != nil { return false, err } fmt.Printf("%+v\n", tenantUsage) return true, nil }) if err != nil { panic(err) } Example to Retrieve Usage for All Tenants: allTenantsOpts := usage.AllTenantsOpts{ Detailed: true, } err := usage.AllTenants(computeClient, allTenantsOpts).EachPage(func(page pagination.Page) (bool, error) { allTenantsUsage, err := usage.ExtractAllTenants(page) if err != nil { return false, err } fmt.Printf("%+v\n", allTenantsUsage) return true, nil }) if err != nil { panic(err) } */ package usage requests.go000066400000000000000000000061341335005366400352000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/usagepackage usage import ( "net/url" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // SingleTenantOpts are options for fetching usage of a single tenant. type SingleTenantOpts struct { // The ending time to calculate usage statistics on compute and storage resources. End *time.Time `q:"end"` // The beginning time to calculate usage statistics on compute and storage resources. Start *time.Time `q:"start"` } // SingleTenantOptsBuilder allows extensions to add additional parameters to the // SingleTenant request. type SingleTenantOptsBuilder interface { ToUsageSingleTenantQuery() (string, error) } // ToUsageSingleTenantQuery formats a SingleTenantOpts into a query string. func (opts SingleTenantOpts) ToUsageSingleTenantQuery() (string, error) { params := make(url.Values) if opts.Start != nil { params.Add("start", opts.Start.Format(gophercloud.RFC3339MilliNoZ)) } if opts.End != nil { params.Add("end", opts.End.Format(gophercloud.RFC3339MilliNoZ)) } q := &url.URL{RawQuery: params.Encode()} return q.String(), nil } // SingleTenant returns usage data about a single tenant. func SingleTenant(client *gophercloud.ServiceClient, tenantID string, opts SingleTenantOptsBuilder) pagination.Pager { url := getTenantURL(client, tenantID) if opts != nil { query, err := opts.ToUsageSingleTenantQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return SingleTenantPage{pagination.LinkedPageBase{PageResult: r}} }) } // AllTenantsOpts are options for fetching usage of all tenants. type AllTenantsOpts struct { // Detailed will return detailed results. Detailed bool // The ending time to calculate usage statistics on compute and storage resources. End *time.Time `q:"end"` // The beginning time to calculate usage statistics on compute and storage resources. Start *time.Time `q:"start"` } // AllTenantsOptsBuilder allows extensions to add additional parameters to the // AllTenants request. type AllTenantsOptsBuilder interface { ToUsageAllTenantsQuery() (string, error) } // ToUsageAllTenantsQuery formats a AllTenantsOpts into a query string. func (opts AllTenantsOpts) ToUsageAllTenantsQuery() (string, error) { params := make(url.Values) if opts.Start != nil { params.Add("start", opts.Start.Format(gophercloud.RFC3339MilliNoZ)) } if opts.End != nil { params.Add("end", opts.End.Format(gophercloud.RFC3339MilliNoZ)) } if opts.Detailed == true { params.Add("detailed", "1") } q := &url.URL{RawQuery: params.Encode()} return q.String(), nil } // AllTenants returns usage data about all tenants. func AllTenants(client *gophercloud.ServiceClient, opts AllTenantsOptsBuilder) pagination.Pager { url := allTenantsURL(client) if opts != nil { query, err := opts.ToUsageAllTenantsQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return AllTenantsPage{pagination.LinkedPageBase{PageResult: r}} }) } results.go000066400000000000000000000125001335005366400350200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/usagepackage usage import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // TenantUsage is a set of usage information about a tenant over the sampling window type TenantUsage struct { // ServerUsages is an array of ServerUsage maps ServerUsages []ServerUsage `json:"server_usages"` // Start is the beginning time to calculate usage statistics on compute and storage resources Start time.Time `json:"-"` // Stop is the ending time to calculate usage statistics on compute and storage resources Stop time.Time `json:"-"` // TenantID is the ID of the tenant whose usage is being reported on TenantID string `json:"tenant_id"` // TotalHours is the total duration that servers exist (in hours) TotalHours float64 `json:"total_hours"` // TotalLocalGBUsage multiplies the server disk size (in GiB) by hours the server exists, and then adding that all together for each server TotalLocalGBUsage float64 `json:"total_local_gb_usage"` // TotalMemoryMBUsage multiplies the server memory size (in MB) by hours the server exists, and then adding that all together for each server TotalMemoryMBUsage float64 `json:"total_memory_mb_usage"` // TotalVCPUsUsage multiplies the number of virtual CPUs of the server by hours the server exists, and then adding that all together for each server TotalVCPUsUsage float64 `json:"total_vcpus_usage"` } // UnmarshalJSON sets *u to a copy of data. func (u *TenantUsage) UnmarshalJSON(b []byte) error { type tmp TenantUsage var s struct { tmp Start gophercloud.JSONRFC3339MilliNoZ `json:"start"` Stop gophercloud.JSONRFC3339MilliNoZ `json:"stop"` } if err := json.Unmarshal(b, &s); err != nil { return err } *u = TenantUsage(s.tmp) u.Start = time.Time(s.Start) u.Stop = time.Time(s.Stop) return nil } // ServerUsage is a detailed set of information about a specific instance inside a tenant type ServerUsage struct { // EndedAt is the date and time when the server was deleted EndedAt time.Time `json:"-"` // Flavor is the display name of a flavor Flavor string `json:"flavor"` // Hours is the duration that the server exists in hours Hours float64 `json:"hours"` // InstanceID is the UUID of the instance InstanceID string `json:"instance_id"` // LocalGB is the sum of the root disk size of the server and the ephemeral disk size of it (in GiB) LocalGB int `json:"local_gb"` // MemoryMB is the memory size of the server (in MB) MemoryMB int `json:"memory_mb"` // Name is the name assigned to the server when it was created Name string `json:"name"` // StartedAt is the date and time when the server was started StartedAt time.Time `json:"-"` // State is the VM power state State string `json:"state"` // TenantID is the UUID of the tenant in a multi-tenancy cloud TenantID string `json:"tenant_id"` // Uptime is the uptime of the server in seconds Uptime int `json:"uptime"` // VCPUs is the number of virtual CPUs that the server uses VCPUs int `json:"vcpus"` } // UnmarshalJSON sets *u to a copy of data. func (u *ServerUsage) UnmarshalJSON(b []byte) error { type tmp ServerUsage var s struct { tmp EndedAt gophercloud.JSONRFC3339MilliNoZ `json:"ended_at"` StartedAt gophercloud.JSONRFC3339MilliNoZ `json:"started_at"` } if err := json.Unmarshal(b, &s); err != nil { return err } *u = ServerUsage(s.tmp) u.EndedAt = time.Time(s.EndedAt) u.StartedAt = time.Time(s.StartedAt) return nil } // SingleTenantPage stores a single, only page of TenantUsage results from a // SingleTenant call. type SingleTenantPage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a SingleTenantPage is empty. func (r SingleTenantPage) IsEmpty() (bool, error) { ks, err := ExtractSingleTenant(r) return ks == nil, err } // NextPageURL uses the response's embedded link reference to navigate to the // next page of results. func (r SingleTenantPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"tenant_usage_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // ExtractSingleTenant interprets a SingleTenantPage as a TenantUsage result. func ExtractSingleTenant(page pagination.Page) (*TenantUsage, error) { var s struct { TenantUsage *TenantUsage `json:"tenant_usage"` } err := (page.(SingleTenantPage)).ExtractInto(&s) return s.TenantUsage, err } // AllTenantsPage stores a single, only page of TenantUsage results from a // AllTenants call. type AllTenantsPage struct { pagination.LinkedPageBase } // ExtractAllTenants interprets a AllTenantsPage as a TenantUsage result. func ExtractAllTenants(page pagination.Page) ([]TenantUsage, error) { var s struct { TenantUsages []TenantUsage `json:"tenant_usages"` } err := (page.(AllTenantsPage)).ExtractInto(&s) return s.TenantUsages, err } // IsEmpty determines whether or not an AllTenantsPage is empty. func (r AllTenantsPage) IsEmpty() (bool, error) { usages, err := ExtractAllTenants(r) return len(usages) == 0, err } // NextPageURL uses the response's embedded link reference to navigate to the // next page of results. func (r AllTenantsPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"tenant_usages_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } testing/000077500000000000000000000000001335005366400344475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/usagedoc.go000066400000000000000000000000621335005366400355410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/usage/testing// simple tenant usage unit tests package testing fixtures.go000066400000000000000000000241031335005366400366470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/usage/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/usage" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const FirstTenantID = "aabbccddeeff112233445566" const SecondTenantID = "665544332211ffeeddccbbaa" // GetSingleTenant holds the fixtures for the content of the request for a // single tenant. const GetSingleTenant = `{ "tenant_usage": { "server_usages": [ { "ended_at": null, "flavor": "m1.tiny", "hours": 0.021675453333333334, "instance_id": "a70096fd-8196-406b-86c4-045840f53ad7", "local_gb": 1, "memory_mb": 512, "name": "jttest", "started_at": "2017-11-30T03:23:43.000000", "state": "active", "tenant_id": "aabbccddeeff112233445566", "uptime": 78, "vcpus": 1 }, { "ended_at": "2017-11-21T04:10:11.000000", "flavor": "m1.acctest", "hours": 0.33444444444444443, "instance_id": "c04e38f2-dcee-4ca8-9466-7708d0a9b6dd", "local_gb": 15, "memory_mb": 512, "name": "basic", "started_at": "2017-11-21T03:50:07.000000", "state": "terminated", "tenant_id": "aabbccddeeff112233445566", "uptime": 1204, "vcpus": 1 }, { "ended_at": "2017-11-30T03:21:21.000000", "flavor": "m1.acctest", "hours": 0.004166666666666667, "instance_id": "ceb654fa-e0e8-44fb-8942-e4d0bfad3941", "local_gb": 15, "memory_mb": 512, "name": "ACPTTESTJSxbPQAC34lTnBE1", "started_at": "2017-11-30T03:21:06.000000", "state": "terminated", "tenant_id": "aabbccddeeff112233445566", "uptime": 15, "vcpus": 1 } ], "start": "2017-11-02T03:25:01.000000", "stop": "2017-11-30T03:25:01.000000", "tenant_id": "aabbccddeeff112233445566", "total_hours": 1.25834212, "total_local_gb_usage": 18.571675453333334, "total_memory_mb_usage": 644.27116544, "total_vcpus_usage": 1.25834212 } }` // HandleGetSingleTenantSuccessfully configures the test server to respond to a // Get request for a single tenant func HandleGetSingleTenantSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-simple-tenant-usage/"+FirstTenantID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprint(w, GetSingleTenant) }) } // SingleTenantUsageResults is the code fixture for GetSingleTenant. var SingleTenantUsageResults = usage.TenantUsage{ ServerUsages: []usage.ServerUsage{ { Flavor: "m1.tiny", Hours: 0.021675453333333334, InstanceID: "a70096fd-8196-406b-86c4-045840f53ad7", LocalGB: 1, MemoryMB: 512, Name: "jttest", StartedAt: time.Date(2017, 11, 30, 3, 23, 43, 0, time.UTC), State: "active", TenantID: "aabbccddeeff112233445566", Uptime: 78, VCPUs: 1, }, { Flavor: "m1.acctest", Hours: 0.33444444444444443, InstanceID: "c04e38f2-dcee-4ca8-9466-7708d0a9b6dd", LocalGB: 15, MemoryMB: 512, Name: "basic", StartedAt: time.Date(2017, 11, 21, 3, 50, 7, 0, time.UTC), EndedAt: time.Date(2017, 11, 21, 4, 10, 11, 0, time.UTC), State: "terminated", TenantID: "aabbccddeeff112233445566", Uptime: 1204, VCPUs: 1, }, { Flavor: "m1.acctest", Hours: 0.004166666666666667, InstanceID: "ceb654fa-e0e8-44fb-8942-e4d0bfad3941", LocalGB: 15, MemoryMB: 512, Name: "ACPTTESTJSxbPQAC34lTnBE1", StartedAt: time.Date(2017, 11, 30, 3, 21, 6, 0, time.UTC), EndedAt: time.Date(2017, 11, 30, 3, 21, 21, 0, time.UTC), State: "terminated", TenantID: "aabbccddeeff112233445566", Uptime: 15, VCPUs: 1, }, }, Start: time.Date(2017, 11, 2, 3, 25, 1, 0, time.UTC), Stop: time.Date(2017, 11, 30, 3, 25, 1, 0, time.UTC), TenantID: "aabbccddeeff112233445566", TotalHours: 1.25834212, TotalLocalGBUsage: 18.571675453333334, TotalMemoryMBUsage: 644.27116544, TotalVCPUsUsage: 1.25834212, } // GetAllTenants holds the fixtures for the content of the request for // all tenants. const GetAllTenants = `{ "tenant_usages": [ { "server_usages": [ { "ended_at": null, "flavor": "m1.tiny", "hours": 0.021675453333333334, "instance_id": "a70096fd-8196-406b-86c4-045840f53ad7", "local_gb": 1, "memory_mb": 512, "name": "jttest", "started_at": "2017-11-30T03:23:43.000000", "state": "active", "tenant_id": "aabbccddeeff112233445566", "uptime": 78, "vcpus": 1 }, { "ended_at": "2017-11-21T04:10:11.000000", "flavor": "m1.acctest", "hours": 0.33444444444444443, "instance_id": "c04e38f2-dcee-4ca8-9466-7708d0a9b6dd", "local_gb": 15, "memory_mb": 512, "name": "basic", "started_at": "2017-11-21T03:50:07.000000", "state": "terminated", "tenant_id": "aabbccddeeff112233445566", "uptime": 1204, "vcpus": 1 }, { "ended_at": "2017-11-30T03:21:21.000000", "flavor": "m1.acctest", "hours": 0.004166666666666667, "instance_id": "ceb654fa-e0e8-44fb-8942-e4d0bfad3941", "local_gb": 15, "memory_mb": 512, "name": "ACPTTESTJSxbPQAC34lTnBE1", "started_at": "2017-11-30T03:21:06.000000", "state": "terminated", "tenant_id": "aabbccddeeff112233445566", "uptime": 15, "vcpus": 1 } ], "start": "2017-11-02T03:25:01.000000", "stop": "2017-11-30T03:25:01.000000", "tenant_id": "aabbccddeeff112233445566", "total_hours": 1.25834212, "total_local_gb_usage": 18.571675453333334, "total_memory_mb_usage": 644.27116544, "total_vcpus_usage": 1.25834212 }, { "server_usages": [ { "ended_at": null, "flavor": "m1.tiny", "hours": 0.021675453333333334, "instance_id": "a70096fd-8196-406b-86c4-045840f53ad7", "local_gb": 1, "memory_mb": 512, "name": "test", "started_at": "2017-11-30T03:23:43.000000", "state": "active", "tenant_id": "665544332211ffeeddccbbaa", "uptime": 78, "vcpus": 1 } ], "start": "2017-11-02T03:25:01.000000", "stop": "2017-11-30T03:25:01.000000", "tenant_id": "665544332211ffeeddccbbaa", "total_hours": 0.021675453333333334, "total_local_gb_usage": 18.571675453333334, "total_memory_mb_usage": 644.27116544, "total_vcpus_usage": 1.25834212 } ] }` // HandleGetAllTenantsSuccessfully configures the test server to respond to a // Get request for all tenants. func HandleGetAllTenantsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-simple-tenant-usage", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprint(w, GetAllTenants) }) } // AllTenantsUsageResult is the code fixture for GetAllTenants. var AllTenantsUsageResult = []usage.TenantUsage{ { ServerUsages: []usage.ServerUsage{ { Flavor: "m1.tiny", Hours: 0.021675453333333334, InstanceID: "a70096fd-8196-406b-86c4-045840f53ad7", LocalGB: 1, MemoryMB: 512, Name: "jttest", StartedAt: time.Date(2017, 11, 30, 3, 23, 43, 0, time.UTC), State: "active", TenantID: "aabbccddeeff112233445566", Uptime: 78, VCPUs: 1, }, { Flavor: "m1.acctest", Hours: 0.33444444444444443, InstanceID: "c04e38f2-dcee-4ca8-9466-7708d0a9b6dd", LocalGB: 15, MemoryMB: 512, Name: "basic", StartedAt: time.Date(2017, 11, 21, 3, 50, 7, 0, time.UTC), EndedAt: time.Date(2017, 11, 21, 4, 10, 11, 0, time.UTC), State: "terminated", TenantID: "aabbccddeeff112233445566", Uptime: 1204, VCPUs: 1, }, { Flavor: "m1.acctest", Hours: 0.004166666666666667, InstanceID: "ceb654fa-e0e8-44fb-8942-e4d0bfad3941", LocalGB: 15, MemoryMB: 512, Name: "ACPTTESTJSxbPQAC34lTnBE1", StartedAt: time.Date(2017, 11, 30, 3, 21, 6, 0, time.UTC), EndedAt: time.Date(2017, 11, 30, 3, 21, 21, 0, time.UTC), State: "terminated", TenantID: "aabbccddeeff112233445566", Uptime: 15, VCPUs: 1, }, }, Start: time.Date(2017, 11, 2, 3, 25, 1, 0, time.UTC), Stop: time.Date(2017, 11, 30, 3, 25, 1, 0, time.UTC), TenantID: "aabbccddeeff112233445566", TotalHours: 1.25834212, TotalLocalGBUsage: 18.571675453333334, TotalMemoryMBUsage: 644.27116544, TotalVCPUsUsage: 1.25834212, }, { ServerUsages: []usage.ServerUsage{ { Flavor: "m1.tiny", Hours: 0.021675453333333334, InstanceID: "a70096fd-8196-406b-86c4-045840f53ad7", LocalGB: 1, MemoryMB: 512, Name: "test", StartedAt: time.Date(2017, 11, 30, 3, 23, 43, 0, time.UTC), State: "active", TenantID: "665544332211ffeeddccbbaa", Uptime: 78, VCPUs: 1, }, }, Start: time.Date(2017, 11, 2, 3, 25, 1, 0, time.UTC), Stop: time.Date(2017, 11, 30, 3, 25, 1, 0, time.UTC), TenantID: "665544332211ffeeddccbbaa", TotalHours: 0.021675453333333334, TotalLocalGBUsage: 18.571675453333334, TotalMemoryMBUsage: 644.27116544, TotalVCPUsUsage: 1.25834212, }, } requests_test.go000066400000000000000000000023551335005366400377150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/usage/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/usage" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestGetTenant(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSingleTenantSuccessfully(t) count := 0 err := usage.SingleTenant(client.ServiceClient(), FirstTenantID, nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := usage.ExtractSingleTenant(page) th.AssertNoErr(t, err) th.AssertDeepEquals(t, &SingleTenantUsageResults, actual) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, count, 1) } func TestAllTenants(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetAllTenantsSuccessfully(t) getOpts := usage.AllTenantsOpts{ Detailed: true, } count := 0 err := usage.AllTenants(client.ServiceClient(), getOpts).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := usage.ExtractAllTenants(page) th.AssertNoErr(t, err) th.AssertDeepEquals(t, AllTenantsUsageResult, actual) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, count, 1) } urls.go000066400000000000000000000005301335005366400343040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/usagepackage usage import "github.com/gophercloud/gophercloud" const resourcePath = "os-simple-tenant-usage" func allTenantsURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(resourcePath) } func getTenantURL(client *gophercloud.ServiceClient, tenantID string) string { return client.ServiceURL(resourcePath, tenantID) } volumeattach/000077500000000000000000000000001335005366400343625ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensionsdoc.go000066400000000000000000000012771335005366400354650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/volumeattach/* Package volumeattach provides the ability to attach and detach volumes from servers. Example to Attach a Volume serverID := "7ac8686c-de71-4acb-9600-ec18b1a1ed6d" volumeID := "87463836-f0e2-4029-abf6-20c8892a3103" createOpts := volumeattach.CreateOpts{ Device: "/dev/vdc", VolumeID: volumeID, } result, err := volumeattach.Create(computeClient, serverID, createOpts).Extract() if err != nil { panic(err) } Example to Detach a Volume serverID := "7ac8686c-de71-4acb-9600-ec18b1a1ed6d" attachmentID := "ed081613-1c9b-4231-aa5e-ebfd4d87f983" err := volumeattach.Delete(computeClient, serverID, attachmentID).ExtractErr() if err != nil { panic(err) } */ package volumeattach requests.go000066400000000000000000000041531335005366400365670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/volumeattachpackage volumeattach import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List returns a Pager that allows you to iterate over a collection of // VolumeAttachments. func List(client *gophercloud.ServiceClient, serverID string) pagination.Pager { return pagination.NewPager(client, listURL(client, serverID), func(r pagination.PageResult) pagination.Page { return VolumeAttachmentPage{pagination.SinglePageBase(r)} }) } // CreateOptsBuilder allows extensions to add parameters to the Create request. type CreateOptsBuilder interface { ToVolumeAttachmentCreateMap() (map[string]interface{}, error) } // CreateOpts specifies volume attachment creation or import parameters. type CreateOpts struct { // Device is the device that the volume will attach to the instance as. // Omit for "auto". Device string `json:"device,omitempty"` // VolumeID is the ID of the volume to attach to the instance. VolumeID string `json:"volumeId" required:"true"` } // ToVolumeAttachmentCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToVolumeAttachmentCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "volumeAttachment") } // Create requests the creation of a new volume attachment on the server. func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToVolumeAttachmentCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Get returns public data about a previously created VolumeAttachment. func Get(client *gophercloud.ServiceClient, serverID, attachmentID string) (r GetResult) { _, r.Err = client.Get(getURL(client, serverID, attachmentID), &r.Body, nil) return } // Delete requests the deletion of a previous stored VolumeAttachment from // the server. func Delete(client *gophercloud.ServiceClient, serverID, attachmentID string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, serverID, attachmentID), nil) return } results.go000066400000000000000000000043551335005366400364210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/volumeattachpackage volumeattach import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // VolumeAttachment contains attachment information between a volume // and server. type VolumeAttachment struct { // ID is a unique id of the attachment. ID string `json:"id"` // Device is what device the volume is attached as. Device string `json:"device"` // VolumeID is the ID of the attached volume. VolumeID string `json:"volumeId"` // ServerID is the ID of the instance that has the volume attached. ServerID string `json:"serverId"` } // VolumeAttachmentPage stores a single page all of VolumeAttachment // results from a List call. type VolumeAttachmentPage struct { pagination.SinglePageBase } // IsEmpty determines whether or not a VolumeAttachmentPage is empty. func (page VolumeAttachmentPage) IsEmpty() (bool, error) { va, err := ExtractVolumeAttachments(page) return len(va) == 0, err } // ExtractVolumeAttachments interprets a page of results as a slice of // VolumeAttachment. func ExtractVolumeAttachments(r pagination.Page) ([]VolumeAttachment, error) { var s struct { VolumeAttachments []VolumeAttachment `json:"volumeAttachments"` } err := (r.(VolumeAttachmentPage)).ExtractInto(&s) return s.VolumeAttachments, err } // VolumeAttachmentResult is the result from a volume attachment operation. type VolumeAttachmentResult struct { gophercloud.Result } // Extract is a method that attempts to interpret any VolumeAttachment resource // response as a VolumeAttachment struct. func (r VolumeAttachmentResult) Extract() (*VolumeAttachment, error) { var s struct { VolumeAttachment *VolumeAttachment `json:"volumeAttachment"` } err := r.ExtractInto(&s) return s.VolumeAttachment, err } // CreateResult is the response from a Create operation. Call its Extract method // to interpret it as a VolumeAttachment. type CreateResult struct { VolumeAttachmentResult } // GetResult is the response from a Get operation. Call its Extract method to // interpret it as a VolumeAttachment. type GetResult struct { VolumeAttachmentResult } // DeleteResult is the response from a Delete operation. Call its ExtractErr // method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400360375ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/volumeattachdoc.go000066400000000000000000000000531335005366400371310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/volumeattach/testing// volumeattach unit tests package testing fixtures.go000066400000000000000000000063441335005366400402460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/volumeattach/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListOutput is a sample response to a List call. const ListOutput = ` { "volumeAttachments": [ { "device": "/dev/vdd", "id": "a26887c6-c47b-4654-abb5-dfadf7d3f803", "serverId": "4d8c3732-a248-40ed-bebc-539a6ffd25c0", "volumeId": "a26887c6-c47b-4654-abb5-dfadf7d3f803" }, { "device": "/dev/vdc", "id": "a26887c6-c47b-4654-abb5-dfadf7d3f804", "serverId": "4d8c3732-a248-40ed-bebc-539a6ffd25c0", "volumeId": "a26887c6-c47b-4654-abb5-dfadf7d3f804" } ] } ` // GetOutput is a sample response to a Get call. const GetOutput = ` { "volumeAttachment": { "device": "/dev/vdc", "id": "a26887c6-c47b-4654-abb5-dfadf7d3f804", "serverId": "4d8c3732-a248-40ed-bebc-539a6ffd25c0", "volumeId": "a26887c6-c47b-4654-abb5-dfadf7d3f804" } } ` // CreateOutput is a sample response to a Create call. const CreateOutput = ` { "volumeAttachment": { "device": "/dev/vdc", "id": "a26887c6-c47b-4654-abb5-dfadf7d3f804", "serverId": "4d8c3732-a248-40ed-bebc-539a6ffd25c0", "volumeId": "a26887c6-c47b-4654-abb5-dfadf7d3f804" } } ` // HandleListSuccessfully configures the test server to respond to a List request. func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/os-volume_attachments", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ListOutput) }) } // HandleGetSuccessfully configures the test server to respond to a Get request // for an existing attachment func HandleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/os-volume_attachments/a26887c6-c47b-4654-abb5-dfadf7d3f804", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, GetOutput) }) } // HandleCreateSuccessfully configures the test server to respond to a Create request // for a new attachment func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/os-volume_attachments", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, ` { "volumeAttachment": { "volumeId": "a26887c6-c47b-4654-abb5-dfadf7d3f804", "device": "/dev/vdc" } } `) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, CreateOutput) }) } // HandleDeleteSuccessfully configures the test server to respond to a Delete request for a // an existing attachment func HandleDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/os-volume_attachments/a26887c6-c47b-4654-abb5-dfadf7d3f804", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000057371335005366400413140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/volumeattach/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // FirstVolumeAttachment is the first result in ListOutput. var FirstVolumeAttachment = volumeattach.VolumeAttachment{ Device: "/dev/vdd", ID: "a26887c6-c47b-4654-abb5-dfadf7d3f803", ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0", VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f803", } // SecondVolumeAttachment is the first result in ListOutput. var SecondVolumeAttachment = volumeattach.VolumeAttachment{ Device: "/dev/vdc", ID: "a26887c6-c47b-4654-abb5-dfadf7d3f804", ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0", VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804", } // ExpectedVolumeAttachmentSlide is the slice of results that should be parsed // from ListOutput, in the expected order. var ExpectedVolumeAttachmentSlice = []volumeattach.VolumeAttachment{FirstVolumeAttachment, SecondVolumeAttachment} //CreatedVolumeAttachment is the parsed result from CreatedOutput. var CreatedVolumeAttachment = volumeattach.VolumeAttachment{ Device: "/dev/vdc", ID: "a26887c6-c47b-4654-abb5-dfadf7d3f804", ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0", VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804", } func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t) serverID := "4d8c3732-a248-40ed-bebc-539a6ffd25c0" count := 0 err := volumeattach.List(client.ServiceClient(), serverID).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := volumeattach.ExtractVolumeAttachments(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedVolumeAttachmentSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, 1, count) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateSuccessfully(t) serverID := "4d8c3732-a248-40ed-bebc-539a6ffd25c0" actual, err := volumeattach.Create(client.ServiceClient(), serverID, volumeattach.CreateOpts{ Device: "/dev/vdc", VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804", }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &CreatedVolumeAttachment, actual) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t) aID := "a26887c6-c47b-4654-abb5-dfadf7d3f804" serverID := "4d8c3732-a248-40ed-bebc-539a6ffd25c0" actual, err := volumeattach.Get(client.ServiceClient(), serverID, aID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &SecondVolumeAttachment, actual) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSuccessfully(t) aID := "a26887c6-c47b-4654-abb5-dfadf7d3f804" serverID := "4d8c3732-a248-40ed-bebc-539a6ffd25c0" err := volumeattach.Delete(client.ServiceClient(), serverID, aID).ExtractErr() th.AssertNoErr(t, err) } urls.go000066400000000000000000000013011335005366400356710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/extensions/volumeattachpackage volumeattach import "github.com/gophercloud/gophercloud" const resourcePath = "os-volume_attachments" func resourceURL(c *gophercloud.ServiceClient, serverID string) string { return c.ServiceURL("servers", serverID, resourcePath) } func listURL(c *gophercloud.ServiceClient, serverID string) string { return resourceURL(c, serverID) } func createURL(c *gophercloud.ServiceClient, serverID string) string { return resourceURL(c, serverID) } func getURL(c *gophercloud.ServiceClient, serverID, aID string) string { return c.ServiceURL("servers", serverID, resourcePath, aID) } func deleteURL(c *gophercloud.ServiceClient, serverID, aID string) string { return getURL(c, serverID, aID) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/flavors/000077500000000000000000000000001335005366400312225ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/flavors/doc.go000066400000000000000000000061121335005366400323160ustar00rootroot00000000000000/* Package flavors provides information and interaction with the flavor API in the OpenStack Compute service. A flavor is an available hardware configuration for a server. Each flavor has a unique combination of disk space, memory capacity and priority for CPU time. Example to List Flavors listOpts := flavors.ListOpts{ AccessType: flavors.PublicAccess, } allPages, err := flavors.ListDetail(computeClient, listOpts).AllPages() if err != nil { panic(err) } allFlavors, err := flavors.ExtractFlavors(allPages) if err != nil { panic(err) } for _, flavor := range allFlavors { fmt.Printf("%+v\n", flavor) } Example to Create a Flavor createOpts := flavors.CreateOpts{ ID: "1", Name: "m1.tiny", Disk: gophercloud.IntToPointer(1), RAM: 512, VCPUs: 1, RxTxFactor: 1.0, } flavor, err := flavors.Create(computeClient, createOpts).Extract() if err != nil { panic(err) } Example to List Flavor Access flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" allPages, err := flavors.ListAccesses(computeClient, flavorID).AllPages() if err != nil { panic(err) } allAccesses, err := flavors.ExtractAccesses(allPages) if err != nil { panic(err) } for _, access := range allAccesses { fmt.Printf("%+v", access) } Example to Grant Access to a Flavor flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" accessOpts := flavors.AddAccessOpts{ Tenant: "15153a0979884b59b0592248ef947921", } accessList, err := flavors.AddAccess(computeClient, flavor.ID, accessOpts).Extract() if err != nil { panic(err) } Example to Remove/Revoke Access to a Flavor flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" accessOpts := flavors.RemoveAccessOpts{ Tenant: "15153a0979884b59b0592248ef947921", } accessList, err := flavors.RemoveAccess(computeClient, flavor.ID, accessOpts).Extract() if err != nil { panic(err) } Example to Create Extra Specs for a Flavor flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" createOpts := flavors.ExtraSpecsOpts{ "hw:cpu_policy": "CPU-POLICY", "hw:cpu_thread_policy": "CPU-THREAD-POLICY", } createdExtraSpecs, err := flavors.CreateExtraSpecs(computeClient, flavorID, createOpts).Extract() if err != nil { panic(err) } fmt.Printf("%+v", createdExtraSpecs) Example to Get Extra Specs for a Flavor flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" extraSpecs, err := flavors.ListExtraSpecs(computeClient, flavorID).Extract() if err != nil { panic(err) } fmt.Printf("%+v", extraSpecs) Example to Update Extra Specs for a Flavor flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" updateOpts := flavors.ExtraSpecsOpts{ "hw:cpu_thread_policy": "CPU-THREAD-POLICY-UPDATED", } updatedExtraSpec, err := flavors.UpdateExtraSpec(computeClient, flavorID, updateOpts).Extract() if err != nil { panic(err) } fmt.Printf("%+v", updatedExtraSpec) Example to Delete an Extra Spec for a Flavor flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" err := flavors.DeleteExtraSpec(computeClient, flavorID, "hw:cpu_thread_policy").ExtractErr() if err != nil { panic(err) } */ package flavors requests.go000066400000000000000000000256541335005366400333610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/flavorspackage flavors import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToFlavorListQuery() (string, error) } /* AccessType maps to OpenStack's Flavor.is_public field. Although the is_public field is boolean, the request options are ternary, which is why AccessType is a string. The following values are allowed: The AccessType arguement is optional, and if it is not supplied, OpenStack returns the PublicAccess flavors. */ type AccessType string const ( // PublicAccess returns public flavors and private flavors associated with // that project. PublicAccess AccessType = "true" // PrivateAccess (admin only) returns private flavors, across all projects. PrivateAccess AccessType = "false" // AllAccess (admin only) returns public and private flavors across all // projects. AllAccess AccessType = "None" ) /* ListOpts filters the results returned by the List() function. For example, a flavor with a minDisk field of 10 will not be returned if you specify MinDisk set to 20. Typically, software will use the last ID of the previous call to List to set the Marker for the current call. */ type ListOpts struct { // ChangesSince, if provided, instructs List to return only those things which // have changed since the timestamp provided. ChangesSince string `q:"changes-since"` // MinDisk and MinRAM, if provided, elides flavors which do not meet your // criteria. MinDisk int `q:"minDisk"` MinRAM int `q:"minRam"` // SortDir allows to select sort direction. // It can be "asc" or "desc" (default). SortDir string `q:"sort_dir"` // SortKey allows to sort by one of the flavors attributes. // Default is flavorid. SortKey string `q:"sort_key"` // Marker and Limit control paging. // Marker instructs List where to start listing from. Marker string `q:"marker"` // Limit instructs List to refrain from sending excessively large lists of // flavors. Limit int `q:"limit"` // AccessType, if provided, instructs List which set of flavors to return. // If IsPublic not provided, flavors for the current project are returned. AccessType AccessType `q:"is_public"` } // ToFlavorListQuery formats a ListOpts into a query string. func (opts ListOpts) ToFlavorListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // ListDetail instructs OpenStack to provide a list of flavors. // You may provide criteria by which List curtails its results for easier // processing. func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToFlavorListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return FlavorPage{pagination.LinkedPageBase{PageResult: r}} }) } type CreateOptsBuilder interface { ToFlavorCreateMap() (map[string]interface{}, error) } // CreateOpts specifies parameters used for creating a flavor. type CreateOpts struct { // Name is the name of the flavor. Name string `json:"name" required:"true"` // RAM is the memory of the flavor, measured in MB. RAM int `json:"ram" required:"true"` // VCPUs is the number of vcpus for the flavor. VCPUs int `json:"vcpus" required:"true"` // Disk the amount of root disk space, measured in GB. Disk *int `json:"disk" required:"true"` // ID is a unique ID for the flavor. ID string `json:"id,omitempty"` // Swap is the amount of swap space for the flavor, measured in MB. Swap *int `json:"swap,omitempty"` // RxTxFactor alters the network bandwidth of a flavor. RxTxFactor float64 `json:"rxtx_factor,omitempty"` // IsPublic flags a flavor as being available to all projects or not. IsPublic *bool `json:"os-flavor-access:is_public,omitempty"` // Ephemeral is the amount of ephemeral disk space, measured in GB. Ephemeral *int `json:"OS-FLV-EXT-DATA:ephemeral,omitempty"` } // ToFlavorCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToFlavorCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "flavor") } // Create requests the creation of a new flavor. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToFlavorCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) return } // Get retrieves details of a single flavor. Use ExtractFlavor to convert its // result into a Flavor. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // Delete deletes the specified flavor ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // ListAccesses retrieves the tenants which have access to a flavor. func ListAccesses(client *gophercloud.ServiceClient, id string) pagination.Pager { url := accessURL(client, id) return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return AccessPage{pagination.SinglePageBase(r)} }) } // AddAccessOptsBuilder allows extensions to add additional parameters to the // AddAccess requests. type AddAccessOptsBuilder interface { ToFlavorAddAccessMap() (map[string]interface{}, error) } // AddAccessOpts represents options for adding access to a flavor. type AddAccessOpts struct { // Tenant is the project/tenant ID to grant access. Tenant string `json:"tenant"` } // ToFlavorAddAccessMap constructs a request body from AddAccessOpts. func (opts AddAccessOpts) ToFlavorAddAccessMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "addTenantAccess") } // AddAccess grants a tenant/project access to a flavor. func AddAccess(client *gophercloud.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) { b, err := opts.ToFlavorAddAccessMap() if err != nil { r.Err = err return } _, r.Err = client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // RemoveAccessOptsBuilder allows extensions to add additional parameters to the // RemoveAccess requests. type RemoveAccessOptsBuilder interface { ToFlavorRemoveAccessMap() (map[string]interface{}, error) } // RemoveAccessOpts represents options for removing access to a flavor. type RemoveAccessOpts struct { // Tenant is the project/tenant ID to grant access. Tenant string `json:"tenant"` } // ToFlavorRemoveAccessMap constructs a request body from RemoveAccessOpts. func (opts RemoveAccessOpts) ToFlavorRemoveAccessMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "removeTenantAccess") } // RemoveAccess removes/revokes a tenant/project access to a flavor. func RemoveAccess(client *gophercloud.ServiceClient, id string, opts RemoveAccessOptsBuilder) (r RemoveAccessResult) { b, err := opts.ToFlavorRemoveAccessMap() if err != nil { r.Err = err return } _, r.Err = client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // ExtraSpecs requests all the extra-specs for the given flavor ID. func ListExtraSpecs(client *gophercloud.ServiceClient, flavorID string) (r ListExtraSpecsResult) { _, r.Err = client.Get(extraSpecsListURL(client, flavorID), &r.Body, nil) return } func GetExtraSpec(client *gophercloud.ServiceClient, flavorID string, key string) (r GetExtraSpecResult) { _, r.Err = client.Get(extraSpecsGetURL(client, flavorID, key), &r.Body, nil) return } // CreateExtraSpecsOptsBuilder allows extensions to add additional parameters to the // CreateExtraSpecs requests. type CreateExtraSpecsOptsBuilder interface { ToFlavorExtraSpecsCreateMap() (map[string]interface{}, error) } // ExtraSpecsOpts is a map that contains key-value pairs. type ExtraSpecsOpts map[string]string // ToFlavorExtraSpecsCreateMap assembles a body for a Create request based on // the contents of ExtraSpecsOpts. func (opts ExtraSpecsOpts) ToFlavorExtraSpecsCreateMap() (map[string]interface{}, error) { return map[string]interface{}{"extra_specs": opts}, nil } // CreateExtraSpecs will create or update the extra-specs key-value pairs for // the specified Flavor. func CreateExtraSpecs(client *gophercloud.ServiceClient, flavorID string, opts CreateExtraSpecsOptsBuilder) (r CreateExtraSpecsResult) { b, err := opts.ToFlavorExtraSpecsCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(extraSpecsCreateURL(client, flavorID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // UpdateExtraSpecOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateExtraSpecOptsBuilder interface { ToFlavorExtraSpecUpdateMap() (map[string]string, string, error) } // ToFlavorExtraSpecUpdateMap assembles a body for an Update request based on // the contents of a ExtraSpecOpts. func (opts ExtraSpecsOpts) ToFlavorExtraSpecUpdateMap() (map[string]string, string, error) { if len(opts) != 1 { err := gophercloud.ErrInvalidInput{} err.Argument = "flavors.ExtraSpecOpts" err.Info = "Must have 1 and only one key-value pair" return nil, "", err } var key string for k := range opts { key = k } return opts, key, nil } // UpdateExtraSpec will updates the value of the specified flavor's extra spec // for the key in opts. func UpdateExtraSpec(client *gophercloud.ServiceClient, flavorID string, opts UpdateExtraSpecOptsBuilder) (r UpdateExtraSpecResult) { b, key, err := opts.ToFlavorExtraSpecUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Put(extraSpecUpdateURL(client, flavorID, key), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // DeleteExtraSpec will delete the key-value pair with the given key for the given // flavor ID. func DeleteExtraSpec(client *gophercloud.ServiceClient, flavorID, key string) (r DeleteExtraSpecResult) { _, r.Err = client.Delete(extraSpecDeleteURL(client, flavorID, key), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // IDFromName is a convienience function that returns a flavor's ID given its // name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" allPages, err := ListDetail(client, nil).AllPages() if err != nil { return "", err } all, err := ExtractFlavors(allPages) if err != nil { return "", err } for _, f := range all { if f.Name == name { count++ id = f.ID } } switch count { case 0: err := &gophercloud.ErrResourceNotFound{} err.ResourceType = "flavor" err.Name = name return "", err case 1: return id, nil default: err := &gophercloud.ErrMultipleResourcesFound{} err.ResourceType = "flavor" err.Name = name err.Count = count return "", err } } results.go000066400000000000000000000147351335005366400332050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/flavorspackage flavors import ( "encoding/json" "strconv" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { gophercloud.Result } // CreateResult is the response of a Get operations. Call its Extract method to // interpret it as a Flavor. type CreateResult struct { commonResult } // GetResult is the response of a Get operations. Call its Extract method to // interpret it as a Flavor. type GetResult struct { commonResult } // DeleteResult is the result from a Delete operation. Call its ExtractErr // method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // Extract provides access to the individual Flavor returned by the Get and // Create functions. func (r commonResult) Extract() (*Flavor, error) { var s struct { Flavor *Flavor `json:"flavor"` } err := r.ExtractInto(&s) return s.Flavor, err } // Flavor represent (virtual) hardware configurations for server resources // in a region. type Flavor struct { // ID is the flavor's unique ID. ID string `json:"id"` // Disk is the amount of root disk, measured in GB. Disk int `json:"disk"` // RAM is the amount of memory, measured in MB. RAM int `json:"ram"` // Name is the name of the flavor. Name string `json:"name"` // RxTxFactor describes bandwidth alterations of the flavor. RxTxFactor float64 `json:"rxtx_factor"` // Swap is the amount of swap space, measured in MB. Swap int `json:"-"` // VCPUs indicates how many (virtual) CPUs are available for this flavor. VCPUs int `json:"vcpus"` // IsPublic indicates whether the flavor is public. IsPublic bool `json:"os-flavor-access:is_public"` // Ephemeral is the amount of ephemeral disk space, measured in GB. Ephemeral int `json:"OS-FLV-EXT-DATA:ephemeral"` } func (r *Flavor) UnmarshalJSON(b []byte) error { type tmp Flavor var s struct { tmp Swap interface{} `json:"swap"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Flavor(s.tmp) switch t := s.Swap.(type) { case float64: r.Swap = int(t) case string: switch t { case "": r.Swap = 0 default: swap, err := strconv.ParseFloat(t, 64) if err != nil { return err } r.Swap = int(swap) } } return nil } // FlavorPage contains a single page of all flavors from a ListDetails call. type FlavorPage struct { pagination.LinkedPageBase } // IsEmpty determines if a FlavorPage contains any results. func (page FlavorPage) IsEmpty() (bool, error) { flavors, err := ExtractFlavors(page) return len(flavors) == 0, err } // NextPageURL uses the response's embedded link reference to navigate to the // next page of results. func (page FlavorPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"flavors_links"` } err := page.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // ExtractFlavors provides access to the list of flavors in a page acquired // from the ListDetail operation. func ExtractFlavors(r pagination.Page) ([]Flavor, error) { var s struct { Flavors []Flavor `json:"flavors"` } err := (r.(FlavorPage)).ExtractInto(&s) return s.Flavors, err } // AccessPage contains a single page of all FlavorAccess entries for a flavor. type AccessPage struct { pagination.SinglePageBase } // IsEmpty indicates whether an AccessPage is empty. func (page AccessPage) IsEmpty() (bool, error) { v, err := ExtractAccesses(page) return len(v) == 0, err } // ExtractAccesses interprets a page of results as a slice of FlavorAccess. func ExtractAccesses(r pagination.Page) ([]FlavorAccess, error) { var s struct { FlavorAccesses []FlavorAccess `json:"flavor_access"` } err := (r.(AccessPage)).ExtractInto(&s) return s.FlavorAccesses, err } type accessResult struct { gophercloud.Result } // AddAccessResult is the response of an AddAccess operation. Call its // Extract method to interpret it as a slice of FlavorAccess. type AddAccessResult struct { accessResult } // RemoveAccessResult is the response of a RemoveAccess operation. Call its // Extract method to interpret it as a slice of FlavorAccess. type RemoveAccessResult struct { accessResult } // Extract provides access to the result of an access create or delete. // The result will be all accesses that the flavor has. func (r accessResult) Extract() ([]FlavorAccess, error) { var s struct { FlavorAccesses []FlavorAccess `json:"flavor_access"` } err := r.ExtractInto(&s) return s.FlavorAccesses, err } // FlavorAccess represents an ACL of tenant access to a specific Flavor. type FlavorAccess struct { // FlavorID is the unique ID of the flavor. FlavorID string `json:"flavor_id"` // TenantID is the unique ID of the tenant. TenantID string `json:"tenant_id"` } // Extract interprets any extraSpecsResult as ExtraSpecs, if possible. func (r extraSpecsResult) Extract() (map[string]string, error) { var s struct { ExtraSpecs map[string]string `json:"extra_specs"` } err := r.ExtractInto(&s) return s.ExtraSpecs, err } // extraSpecsResult contains the result of a call for (potentially) multiple // key-value pairs. Call its Extract method to interpret it as a // map[string]interface. type extraSpecsResult struct { gophercloud.Result } // ListExtraSpecsResult contains the result of a Get operation. Call its Extract // method to interpret it as a map[string]interface. type ListExtraSpecsResult struct { extraSpecsResult } // CreateExtraSpecResult contains the result of a Create operation. Call its // Extract method to interpret it as a map[string]interface. type CreateExtraSpecsResult struct { extraSpecsResult } // extraSpecResult contains the result of a call for individual a single // key-value pair. type extraSpecResult struct { gophercloud.Result } // GetExtraSpecResult contains the result of a Get operation. Call its Extract // method to interpret it as a map[string]interface. type GetExtraSpecResult struct { extraSpecResult } // UpdateExtraSpecResult contains the result of an Update operation. Call its // Extract method to interpret it as a map[string]interface. type UpdateExtraSpecResult struct { extraSpecResult } // DeleteExtraSpecResult contains the result of a Delete operation. Call its // ExtractErr method to determine if the call succeeded or failed. type DeleteExtraSpecResult struct { gophercloud.ErrResult } // Extract interprets any extraSpecResult as an ExtraSpec, if possible. func (r extraSpecResult) Extract() (map[string]string, error) { var s map[string]string err := r.ExtractInto(&s) return s, err } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/flavors/testing/000077500000000000000000000000001335005366400326775ustar00rootroot00000000000000doc.go000066400000000000000000000000461335005366400337140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/flavors/testing// flavors unit tests package testing fixtures.go000066400000000000000000000065711335005366400350310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/flavors/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) // ExtraSpecsGetBody provides a GET result of the extra_specs for a flavor const ExtraSpecsGetBody = ` { "extra_specs" : { "hw:cpu_policy": "CPU-POLICY", "hw:cpu_thread_policy": "CPU-THREAD-POLICY" } } ` // GetExtraSpecBody provides a GET result of a particular extra_spec for a flavor const GetExtraSpecBody = ` { "hw:cpu_policy": "CPU-POLICY" } ` // UpdatedExtraSpecBody provides an PUT result of a particular updated extra_spec for a flavor const UpdatedExtraSpecBody = ` { "hw:cpu_policy": "CPU-POLICY-2" } ` // ExtraSpecs is the expected extra_specs returned from GET on a flavor's extra_specs var ExtraSpecs = map[string]string{ "hw:cpu_policy": "CPU-POLICY", "hw:cpu_thread_policy": "CPU-THREAD-POLICY", } // ExtraSpec is the expected extra_spec returned from GET on a flavor's extra_specs var ExtraSpec = map[string]string{ "hw:cpu_policy": "CPU-POLICY", } // UpdatedExtraSpec is the expected extra_spec returned from PUT on a flavor's extra_specs var UpdatedExtraSpec = map[string]string{ "hw:cpu_policy": "CPU-POLICY-2", } func HandleExtraSpecsListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/flavors/1/os-extra_specs", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ExtraSpecsGetBody) }) } func HandleExtraSpecGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/flavors/1/os-extra_specs/hw:cpu_policy", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetExtraSpecBody) }) } func HandleExtraSpecsCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/flavors/1/os-extra_specs", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, `{ "extra_specs": { "hw:cpu_policy": "CPU-POLICY", "hw:cpu_thread_policy": "CPU-THREAD-POLICY" } }`) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ExtraSpecsGetBody) }) } func HandleExtraSpecUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/flavors/1/os-extra_specs/hw:cpu_policy", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, `{ "hw:cpu_policy": "CPU-POLICY-2" }`) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, UpdatedExtraSpecBody) }) } func HandleExtraSpecDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc("/flavors/1/os-extra_specs/hw:cpu_policy", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) }) } requests_test.go000066400000000000000000000226101335005366400360620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/flavors/testingpackage testing import ( "fmt" "net/http" "reflect" "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const tokenID = "blerb" func TestListFlavors(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/flavors/detail", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, ` { "flavors": [ { "id": "1", "name": "m1.tiny", "vcpus": 1, "disk": 1, "ram": 512, "swap":"", "os-flavor-access:is_public": true, "OS-FLV-EXT-DATA:ephemeral": 10 }, { "id": "2", "name": "m1.small", "vcpus": 1, "disk": 20, "ram": 2048, "swap": 1000, "os-flavor-access:is_public": true, "OS-FLV-EXT-DATA:ephemeral": 0 }, { "id": "3", "name": "m1.medium", "vcpus": 2, "disk": 40, "ram": 4096, "swap": 1000, "os-flavor-access:is_public": false, "OS-FLV-EXT-DATA:ephemeral": 0 } ], "flavors_links": [ { "href": "%s/flavors/detail?marker=2", "rel": "next" } ] } `, th.Server.URL) case "2": fmt.Fprintf(w, `{ "flavors": [] }`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) pages := 0 // Get public and private flavors err := flavors.ListDetail(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := flavors.ExtractFlavors(page) if err != nil { return false, err } expected := []flavors.Flavor{ {ID: "1", Name: "m1.tiny", VCPUs: 1, Disk: 1, RAM: 512, Swap: 0, IsPublic: true, Ephemeral: 10}, {ID: "2", Name: "m1.small", VCPUs: 1, Disk: 20, RAM: 2048, Swap: 1000, IsPublic: true, Ephemeral: 0}, {ID: "3", Name: "m1.medium", VCPUs: 2, Disk: 40, RAM: 4096, Swap: 1000, IsPublic: false, Ephemeral: 0}, } if !reflect.DeepEqual(expected, actual) { t.Errorf("Expected %#v, but was %#v", expected, actual) } return true, nil }) if err != nil { t.Fatal(err) } if pages != 1 { t.Errorf("Expected one page, got %d", pages) } } func TestGetFlavor(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/flavors/12345", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ` { "flavor": { "id": "1", "name": "m1.tiny", "disk": 1, "ram": 512, "vcpus": 1, "rxtx_factor": 1, "swap": "" } } `) }) actual, err := flavors.Get(fake.ServiceClient(), "12345").Extract() if err != nil { t.Fatalf("Unable to get flavor: %v", err) } expected := &flavors.Flavor{ ID: "1", Name: "m1.tiny", Disk: 1, RAM: 512, VCPUs: 1, RxTxFactor: 1, Swap: 0, } if !reflect.DeepEqual(expected, actual) { t.Errorf("Expected %#v, but was %#v", expected, actual) } } func TestCreateFlavor(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/flavors", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ` { "flavor": { "id": "1", "name": "m1.tiny", "disk": 1, "ram": 512, "vcpus": 1, "rxtx_factor": 1, "swap": "" } } `) }) disk := 1 opts := &flavors.CreateOpts{ ID: "1", Name: "m1.tiny", Disk: &disk, RAM: 512, VCPUs: 1, RxTxFactor: 1.0, } actual, err := flavors.Create(fake.ServiceClient(), opts).Extract() if err != nil { t.Fatalf("Unable to create flavor: %v", err) } expected := &flavors.Flavor{ ID: "1", Name: "m1.tiny", Disk: 1, RAM: 512, VCPUs: 1, RxTxFactor: 1, Swap: 0, } if !reflect.DeepEqual(expected, actual) { t.Errorf("Expected %#v, but was %#v", expected, actual) } } func TestDeleteFlavor(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/flavors/12345678", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusAccepted) }) res := flavors.Delete(fake.ServiceClient(), "12345678") th.AssertNoErr(t, res.Err) } func TestFlavorAccessesList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/flavors/12345678/os-flavor-access", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ` { "flavor_access": [ { "flavor_id": "12345678", "tenant_id": "2f954bcf047c4ee9b09a37d49ae6db54" } ] } `) }) expected := []flavors.FlavorAccess{ flavors.FlavorAccess{ FlavorID: "12345678", TenantID: "2f954bcf047c4ee9b09a37d49ae6db54", }, } allPages, err := flavors.ListAccesses(fake.ServiceClient(), "12345678").AllPages() th.AssertNoErr(t, err) actual, err := flavors.ExtractAccesses(allPages) th.AssertNoErr(t, err) if !reflect.DeepEqual(expected, actual) { t.Errorf("Expected %#v, but was %#v", expected, actual) } } func TestFlavorAccessAdd(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/flavors/12345678/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "accept", "application/json") th.TestJSONRequest(t, r, ` { "addTenantAccess": { "tenant": "2f954bcf047c4ee9b09a37d49ae6db54" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "flavor_access": [ { "flavor_id": "12345678", "tenant_id": "2f954bcf047c4ee9b09a37d49ae6db54" } ] } `) }) expected := []flavors.FlavorAccess{ flavors.FlavorAccess{ FlavorID: "12345678", TenantID: "2f954bcf047c4ee9b09a37d49ae6db54", }, } addAccessOpts := flavors.AddAccessOpts{ Tenant: "2f954bcf047c4ee9b09a37d49ae6db54", } actual, err := flavors.AddAccess(fake.ServiceClient(), "12345678", addAccessOpts).Extract() th.AssertNoErr(t, err) if !reflect.DeepEqual(expected, actual) { t.Errorf("Expected %#v, but was %#v", expected, actual) } } func TestFlavorAccessRemove(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/flavors/12345678/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "accept", "application/json") th.TestJSONRequest(t, r, ` { "removeTenantAccess": { "tenant": "2f954bcf047c4ee9b09a37d49ae6db54" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "flavor_access": [] } `) }) expected := []flavors.FlavorAccess{} removeAccessOpts := flavors.RemoveAccessOpts{ Tenant: "2f954bcf047c4ee9b09a37d49ae6db54", } actual, err := flavors.RemoveAccess(fake.ServiceClient(), "12345678", removeAccessOpts).Extract() th.AssertNoErr(t, err) if !reflect.DeepEqual(expected, actual) { t.Errorf("Expected %#v, but was %#v", expected, actual) } } func TestFlavorExtraSpecsList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleExtraSpecsListSuccessfully(t) expected := ExtraSpecs actual, err := flavors.ListExtraSpecs(fake.ServiceClient(), "1").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } func TestFlavorExtraSpecGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleExtraSpecGetSuccessfully(t) expected := ExtraSpec actual, err := flavors.GetExtraSpec(fake.ServiceClient(), "1", "hw:cpu_policy").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } func TestFlavorExtraSpecsCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleExtraSpecsCreateSuccessfully(t) createOpts := flavors.ExtraSpecsOpts{ "hw:cpu_policy": "CPU-POLICY", "hw:cpu_thread_policy": "CPU-THREAD-POLICY", } expected := ExtraSpecs actual, err := flavors.CreateExtraSpecs(fake.ServiceClient(), "1", createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } func TestFlavorExtraSpecUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleExtraSpecUpdateSuccessfully(t) updateOpts := flavors.ExtraSpecsOpts{ "hw:cpu_policy": "CPU-POLICY-2", } expected := UpdatedExtraSpec actual, err := flavors.UpdateExtraSpec(fake.ServiceClient(), "1", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } func TestFlavorExtraSpecDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleExtraSpecDeleteSuccessfully(t) res := flavors.DeleteExtraSpec(fake.ServiceClient(), "1", "hw:cpu_policy") th.AssertNoErr(t, res.Err) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/flavors/urls.go000066400000000000000000000027261335005366400325450ustar00rootroot00000000000000package flavors import ( "github.com/gophercloud/gophercloud" ) func getURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("flavors", id) } func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("flavors", "detail") } func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("flavors") } func deleteURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("flavors", id) } func accessURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("flavors", id, "os-flavor-access") } func accessActionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("flavors", id, "action") } func extraSpecsListURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("flavors", id, "os-extra_specs") } func extraSpecsGetURL(client *gophercloud.ServiceClient, id, key string) string { return client.ServiceURL("flavors", id, "os-extra_specs", key) } func extraSpecsCreateURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("flavors", id, "os-extra_specs") } func extraSpecUpdateURL(client *gophercloud.ServiceClient, id, key string) string { return client.ServiceURL("flavors", id, "os-extra_specs", key) } func extraSpecDeleteURL(client *gophercloud.ServiceClient, id, key string) string { return client.ServiceURL("flavors", id, "os-extra_specs", key) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/images/000077500000000000000000000000001335005366400310135ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/images/doc.go000066400000000000000000000013601335005366400321070ustar00rootroot00000000000000/* Package images provides information and interaction with the images through the OpenStack Compute service. This API is deprecated and will be removed from a future version of the Nova API service. An image is a collection of files used to create or rebuild a server. Operators provide a number of pre-built OS images by default. You may also create custom images from cloud servers you have launched. Example to List Images listOpts := images.ListOpts{ Limit: 2, } allPages, err := images.ListDetail(computeClient, listOpts).AllPages() if err != nil { panic(err) } allImages, err := images.ExtractImages(allPages) if err != nil { panic(err) } for _, image := range allImages { fmt.Printf("%+v\n", image) } */ package images requests.go000066400000000000000000000052761335005366400331500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/imagespackage images import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // ListDetail request. type ListOptsBuilder interface { ToImageListQuery() (string, error) } // ListOpts contain options filtering Images returned from a call to ListDetail. type ListOpts struct { // ChangesSince filters Images based on the last changed status (in date-time // format). ChangesSince string `q:"changes-since"` // Limit limits the number of Images to return. Limit int `q:"limit"` // Mark is an Image UUID at which to set a marker. Marker string `q:"marker"` // Name is the name of the Image. Name string `q:"name"` // Server is the name of the Server (in URL format). Server string `q:"server"` // Status is the current status of the Image. Status string `q:"status"` // Type is the type of image (e.g. BASE, SERVER, ALL). Type string `q:"type"` } // ToImageListQuery formats a ListOpts into a query string. func (opts ListOpts) ToImageListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // ListDetail enumerates the available images. func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listDetailURL(client) if opts != nil { query, err := opts.ToImageListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ImagePage{pagination.LinkedPageBase{PageResult: r}} }) } // Get returns data about a specific image by its ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // Delete deletes the specified image ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // IDFromName is a convienience function that returns an image's ID given its // name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" allPages, err := ListDetail(client, nil).AllPages() if err != nil { return "", err } all, err := ExtractImages(allPages) if err != nil { return "", err } for _, f := range all { if f.Name == name { count++ id = f.ID } } switch count { case 0: err := &gophercloud.ErrResourceNotFound{} err.ResourceType = "image" err.Name = name return "", err case 1: return id, nil default: err := &gophercloud.ErrMultipleResourcesFound{} err.ResourceType = "image" err.Name = name err.Count = count return "", err } } results.go000066400000000000000000000047431335005366400327740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/imagespackage images import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // GetResult is the response from a Get operation. Call its Extract method to // interpret it as an Image. type GetResult struct { gophercloud.Result } // DeleteResult is the result from a Delete operation. Call its ExtractErr // method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // Extract interprets a GetResult as an Image. func (r GetResult) Extract() (*Image, error) { var s struct { Image *Image `json:"image"` } err := r.ExtractInto(&s) return s.Image, err } // Image represents an Image returned by the Compute API. type Image struct { // ID is the unique ID of an image. ID string // Created is the date when the image was created. Created string // MinDisk is the minimum amount of disk a flavor must have to be able // to create a server based on the image, measured in GB. MinDisk int // MinRAM is the minimum amount of RAM a flavor must have to be able // to create a server based on the image, measured in MB. MinRAM int // Name provides a human-readable moniker for the OS image. Name string // The Progress and Status fields indicate image-creation status. Progress int // Status is the current status of the image. Status string // Update is the date when the image was updated. Updated string // Metadata provides free-form key/value pairs that further describe the // image. Metadata map[string]interface{} } // ImagePage contains a single page of all Images returne from a ListDetail // operation. Use ExtractImages to convert it into a slice of usable structs. type ImagePage struct { pagination.LinkedPageBase } // IsEmpty returns true if an ImagePage contains no Image results. func (page ImagePage) IsEmpty() (bool, error) { images, err := ExtractImages(page) return len(images) == 0, err } // NextPageURL uses the response's embedded link reference to navigate to the // next page of results. func (page ImagePage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"images_links"` } err := page.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // ExtractImages converts a page of List results into a slice of usable Image // structs. func ExtractImages(r pagination.Page) ([]Image, error) { var s struct { Images []Image `json:"images"` } err := (r.(ImagePage)).ExtractInto(&s) return s.Images, err } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/images/testing/000077500000000000000000000000001335005366400324705ustar00rootroot00000000000000doc.go000066400000000000000000000000451335005366400335040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/images/testing// images unit tests package testing requests_test.go000066400000000000000000000133201335005366400356510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/images/testingpackage testing import ( "encoding/json" "fmt" "net/http" "reflect" "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/images" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListImages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/images/detail", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, ` { "images": [ { "status": "ACTIVE", "updated": "2014-09-23T12:54:56Z", "id": "f3e4a95d-1f4f-4989-97ce-f3a1fb8c04d7", "OS-EXT-IMG-SIZE:size": 476704768, "name": "F17-x86_64-cfntools", "created": "2014-09-23T12:54:52Z", "minDisk": 0, "progress": 100, "minRam": 0, "metadata": { "architecture": "x86_64", "block_device_mapping": { "guest_format": null, "boot_index": 0, "device_name": "/dev/vda", "delete_on_termination": false } } }, { "status": "ACTIVE", "updated": "2014-09-23T12:51:43Z", "id": "f90f6034-2570-4974-8351-6b49732ef2eb", "OS-EXT-IMG-SIZE:size": 13167616, "name": "cirros-0.3.2-x86_64-disk", "created": "2014-09-23T12:51:42Z", "minDisk": 0, "progress": 100, "minRam": 0 } ] } `) case "2": fmt.Fprintf(w, `{ "images": [] }`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) pages := 0 options := &images.ListOpts{Limit: 2} err := images.ListDetail(fake.ServiceClient(), options).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := images.ExtractImages(page) if err != nil { return false, err } expected := []images.Image{ { ID: "f3e4a95d-1f4f-4989-97ce-f3a1fb8c04d7", Name: "F17-x86_64-cfntools", Created: "2014-09-23T12:54:52Z", Updated: "2014-09-23T12:54:56Z", MinDisk: 0, MinRAM: 0, Progress: 100, Status: "ACTIVE", Metadata: map[string]interface{}{ "architecture": "x86_64", "block_device_mapping": map[string]interface{}{ "guest_format": interface{}(nil), "boot_index": float64(0), "device_name": "/dev/vda", "delete_on_termination": false, }, }, }, { ID: "f90f6034-2570-4974-8351-6b49732ef2eb", Name: "cirros-0.3.2-x86_64-disk", Created: "2014-09-23T12:51:42Z", Updated: "2014-09-23T12:51:43Z", MinDisk: 0, MinRAM: 0, Progress: 100, Status: "ACTIVE", }, } if !reflect.DeepEqual(expected, actual) { t.Errorf("Unexpected page contents: expected %#v, got %#v", expected, actual) } return false, nil }) if err != nil { t.Fatalf("EachPage error: %v", err) } if pages != 1 { t.Errorf("Expected one page, got %d", pages) } } func TestGetImage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/images/12345678", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ` { "image": { "status": "ACTIVE", "updated": "2014-09-23T12:54:56Z", "id": "f3e4a95d-1f4f-4989-97ce-f3a1fb8c04d7", "OS-EXT-IMG-SIZE:size": 476704768, "name": "F17-x86_64-cfntools", "created": "2014-09-23T12:54:52Z", "minDisk": 0, "progress": 100, "minRam": 0, "metadata": { "architecture": "x86_64", "block_device_mapping": { "guest_format": null, "boot_index": 0, "device_name": "/dev/vda", "delete_on_termination": false } } } } `) }) actual, err := images.Get(fake.ServiceClient(), "12345678").Extract() if err != nil { t.Fatalf("Unexpected error from Get: %v", err) } expected := &images.Image{ Status: "ACTIVE", Updated: "2014-09-23T12:54:56Z", ID: "f3e4a95d-1f4f-4989-97ce-f3a1fb8c04d7", Name: "F17-x86_64-cfntools", Created: "2014-09-23T12:54:52Z", MinDisk: 0, Progress: 100, MinRAM: 0, Metadata: map[string]interface{}{ "architecture": "x86_64", "block_device_mapping": map[string]interface{}{ "guest_format": interface{}(nil), "boot_index": float64(0), "device_name": "/dev/vda", "delete_on_termination": false, }, }, } if !reflect.DeepEqual(expected, actual) { t.Errorf("Expected %#v, but got %#v", expected, actual) } } func TestNextPageURL(t *testing.T) { var page images.ImagePage var body map[string]interface{} bodyString := []byte(`{"images":{"links":[{"href":"http://192.154.23.87/12345/images/image3","rel":"bookmark"}]}, "images_links":[{"href":"http://192.154.23.87/12345/images/image4","rel":"next"}]}`) err := json.Unmarshal(bodyString, &body) if err != nil { t.Fatalf("Error unmarshaling data into page body: %v", err) } page.Body = body expected := "http://192.154.23.87/12345/images/image4" actual, err := page.NextPageURL() th.AssertNoErr(t, err) th.CheckEquals(t, expected, actual) } // Test Image delete func TestDeleteImage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/images/12345678", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := images.Delete(fake.ServiceClient(), "12345678") th.AssertNoErr(t, res.Err) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/images/urls.go000066400000000000000000000006131335005366400323270ustar00rootroot00000000000000package images import "github.com/gophercloud/gophercloud" func listDetailURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("images", "detail") } func getURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("images", id) } func deleteURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("images", id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/servers/000077500000000000000000000000001335005366400312375ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/servers/doc.go000066400000000000000000000045431335005366400323410ustar00rootroot00000000000000/* Package servers provides information and interaction with the server API resource in the OpenStack Compute service. A server is a virtual machine instance in the compute system. In order for one to be provisioned, a valid flavor and image are required. Example to List Servers listOpts := servers.ListOpts{ AllTenants: true, } allPages, err := servers.List(computeClient, listOpts).AllPages() if err != nil { panic(err) } allServers, err := servers.ExtractServers(allPages) if err != nil { panic(err) } for _, server := range allServers { fmt.Printf("%+v\n", server) } Example to Create a Server createOpts := servers.CreateOpts{ Name: "server_name", ImageRef: "image-uuid", FlavorRef: "flavor-uuid", } server, err := servers.Create(computeClient, createOpts).Extract() if err != nil { panic(err) } Example to Delete a Server serverID := "d9072956-1560-487c-97f2-18bdf65ec749" err := servers.Delete(computeClient, serverID).ExtractErr() if err != nil { panic(err) } Example to Force Delete a Server serverID := "d9072956-1560-487c-97f2-18bdf65ec749" err := servers.ForceDelete(computeClient, serverID).ExtractErr() if err != nil { panic(err) } Example to Reboot a Server rebootOpts := servers.RebootOpts{ Type: servers.SoftReboot, } serverID := "d9072956-1560-487c-97f2-18bdf65ec749" err := servers.Reboot(computeClient, serverID, rebootOpts).ExtractErr() if err != nil { panic(err) } Example to Rebuild a Server rebuildOpts := servers.RebuildOpts{ Name: "new_name", ImageID: "image-uuid", } serverID := "d9072956-1560-487c-97f2-18bdf65ec749" server, err := servers.Rebuilt(computeClient, serverID, rebuildOpts).Extract() if err != nil { panic(err) } Example to Resize a Server resizeOpts := servers.ResizeOpts{ FlavorRef: "flavor-uuid", } serverID := "d9072956-1560-487c-97f2-18bdf65ec749" err := servers.Resize(computeClient, serverID, resizeOpts).ExtractErr() if err != nil { panic(err) } err = servers.ConfirmResize(computeClient, serverID).ExtractErr() if err != nil { panic(err) } Example to Snapshot a Server snapshotOpts := servers.CreateImageOpts{ Name: "snapshot_name", } serverID := "d9072956-1560-487c-97f2-18bdf65ec749" image, err := servers.CreateImage(computeClient, serverID, snapshotOpts).ExtractImageID() if err != nil { panic(err) } */ package servers errors.go000066400000000000000000000045361335005366400330330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/serverspackage servers import ( "fmt" "github.com/gophercloud/gophercloud" ) // ErrNeitherImageIDNorImageNameProvided is the error when neither the image // ID nor the image name is provided for a server operation type ErrNeitherImageIDNorImageNameProvided struct{ gophercloud.ErrMissingInput } func (e ErrNeitherImageIDNorImageNameProvided) Error() string { return "One and only one of the image ID and the image name must be provided." } // ErrNeitherFlavorIDNorFlavorNameProvided is the error when neither the flavor // ID nor the flavor name is provided for a server operation type ErrNeitherFlavorIDNorFlavorNameProvided struct{ gophercloud.ErrMissingInput } func (e ErrNeitherFlavorIDNorFlavorNameProvided) Error() string { return "One and only one of the flavor ID and the flavor name must be provided." } type ErrNoClientProvidedForIDByName struct{ gophercloud.ErrMissingInput } func (e ErrNoClientProvidedForIDByName) Error() string { return "A service client must be provided to find a resource ID by name." } // ErrInvalidHowParameterProvided is the error when an unknown value is given // for the `how` argument type ErrInvalidHowParameterProvided struct{ gophercloud.ErrInvalidInput } // ErrNoAdminPassProvided is the error when an administrative password isn't // provided for a server operation type ErrNoAdminPassProvided struct{ gophercloud.ErrMissingInput } // ErrNoImageIDProvided is the error when an image ID isn't provided for a server // operation type ErrNoImageIDProvided struct{ gophercloud.ErrMissingInput } // ErrNoIDProvided is the error when a server ID isn't provided for a server // operation type ErrNoIDProvided struct{ gophercloud.ErrMissingInput } // ErrServer is a generic error type for servers HTTP operations. type ErrServer struct { gophercloud.ErrUnexpectedResponseCode ID string } func (se ErrServer) Error() string { return fmt.Sprintf("Error while executing HTTP request for server [%s]", se.ID) } // Error404 overrides the generic 404 error message. func (se ErrServer) Error404(e gophercloud.ErrUnexpectedResponseCode) error { se.ErrUnexpectedResponseCode = e return &ErrServerNotFound{se} } // ErrServerNotFound is the error when a 404 is received during server HTTP // operations. type ErrServerNotFound struct { ErrServer } func (e ErrServerNotFound) Error() string { return fmt.Sprintf("I couldn't find server [%s]", e.ID) } requests.go000066400000000000000000000633321335005366400333710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/serverspackage servers import ( "encoding/base64" "encoding/json" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" "github.com/gophercloud/gophercloud/openstack/compute/v2/images" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToServerListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the server attributes you want to see returned. Marker and Limit are used // for pagination. type ListOpts struct { // ChangesSince is a time/date stamp for when the server last changed status. ChangesSince string `q:"changes-since"` // Image is the name of the image in URL format. Image string `q:"image"` // Flavor is the name of the flavor in URL format. Flavor string `q:"flavor"` // Name of the server as a string; can be queried with regular expressions. // Realize that ?name=bob returns both bob and bobb. If you need to match bob // only, you can use a regular expression matching the syntax of the // underlying database server implemented for Compute. Name string `q:"name"` // Status is the value of the status of the server so that you can filter on // "ACTIVE" for example. Status string `q:"status"` // Host is the name of the host as a string. Host string `q:"host"` // Marker is a UUID of the server at which you want to set a marker. Marker string `q:"marker"` // Limit is an integer value for the limit of values to return. Limit int `q:"limit"` // AllTenants is a bool to show all tenants. AllTenants bool `q:"all_tenants"` // TenantID lists servers for a particular tenant. // Setting "AllTenants = true" is required. TenantID string `q:"tenant_id"` } // ToServerListQuery formats a ListOpts into a query string. func (opts ListOpts) ToServerListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List makes a request against the API to list servers accessible to you. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listDetailURL(client) if opts != nil { query, err := opts.ToServerListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ServerPage{pagination.LinkedPageBase{PageResult: r}} }) } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToServerCreateMap() (map[string]interface{}, error) } // Network is used within CreateOpts to control a new server's network // attachments. type Network struct { // UUID of a network to attach to the newly provisioned server. // Required unless Port is provided. UUID string // Port of a neutron network to attach to the newly provisioned server. // Required unless UUID is provided. Port string // FixedIP specifies a fixed IPv4 address to be used on this network. FixedIP string } // Personality is an array of files that are injected into the server at launch. type Personality []*File // File is used within CreateOpts and RebuildOpts to inject a file into the // server at launch. // File implements the json.Marshaler interface, so when a Create or Rebuild // operation is requested, json.Marshal will call File's MarshalJSON method. type File struct { // Path of the file. Path string // Contents of the file. Maximum content size is 255 bytes. Contents []byte } // MarshalJSON marshals the escaped file, base64 encoding the contents. func (f *File) MarshalJSON() ([]byte, error) { file := struct { Path string `json:"path"` Contents string `json:"contents"` }{ Path: f.Path, Contents: base64.StdEncoding.EncodeToString(f.Contents), } return json.Marshal(file) } // CreateOpts specifies server creation parameters. type CreateOpts struct { // Name is the name to assign to the newly launched server. Name string `json:"name" required:"true"` // ImageRef [optional; required if ImageName is not provided] is the ID or // full URL to the image that contains the server's OS and initial state. // Also optional if using the boot-from-volume extension. ImageRef string `json:"imageRef"` // ImageName [optional; required if ImageRef is not provided] is the name of // the image that contains the server's OS and initial state. // Also optional if using the boot-from-volume extension. ImageName string `json:"-"` // FlavorRef [optional; required if FlavorName is not provided] is the ID or // full URL to the flavor that describes the server's specs. FlavorRef string `json:"flavorRef"` // FlavorName [optional; required if FlavorRef is not provided] is the name of // the flavor that describes the server's specs. FlavorName string `json:"-"` // SecurityGroups lists the names of the security groups to which this server // should belong. SecurityGroups []string `json:"-"` // UserData contains configuration information or scripts to use upon launch. // Create will base64-encode it for you, if it isn't already. UserData []byte `json:"-"` // AvailabilityZone in which to launch the server. AvailabilityZone string `json:"availability_zone,omitempty"` // Networks dictates how this server will be attached to available networks. // By default, the server will be attached to all isolated networks for the // tenant. Networks []Network `json:"-"` // Metadata contains key-value pairs (up to 255 bytes each) to attach to the // server. Metadata map[string]string `json:"metadata,omitempty"` // Personality includes files to inject into the server at launch. // Create will base64-encode file contents for you. Personality Personality `json:"personality,omitempty"` // ConfigDrive enables metadata injection through a configuration drive. ConfigDrive *bool `json:"config_drive,omitempty"` // AdminPass sets the root user password. If not set, a randomly-generated // password will be created and returned in the response. AdminPass string `json:"adminPass,omitempty"` // AccessIPv4 specifies an IPv4 address for the instance. AccessIPv4 string `json:"accessIPv4,omitempty"` // AccessIPv6 pecifies an IPv6 address for the instance. AccessIPv6 string `json:"accessIPv6,omitempty"` // ServiceClient will allow calls to be made to retrieve an image or // flavor ID by name. ServiceClient *gophercloud.ServiceClient `json:"-"` } // ToServerCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { sc := opts.ServiceClient opts.ServiceClient = nil b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } if opts.UserData != nil { var userData string if _, err := base64.StdEncoding.DecodeString(string(opts.UserData)); err != nil { userData = base64.StdEncoding.EncodeToString(opts.UserData) } else { userData = string(opts.UserData) } b["user_data"] = &userData } if len(opts.SecurityGroups) > 0 { securityGroups := make([]map[string]interface{}, len(opts.SecurityGroups)) for i, groupName := range opts.SecurityGroups { securityGroups[i] = map[string]interface{}{"name": groupName} } b["security_groups"] = securityGroups } if len(opts.Networks) > 0 { networks := make([]map[string]interface{}, len(opts.Networks)) for i, net := range opts.Networks { networks[i] = make(map[string]interface{}) if net.UUID != "" { networks[i]["uuid"] = net.UUID } if net.Port != "" { networks[i]["port"] = net.Port } if net.FixedIP != "" { networks[i]["fixed_ip"] = net.FixedIP } } b["networks"] = networks } // If ImageRef isn't provided, check if ImageName was provided to ascertain // the image ID. if opts.ImageRef == "" { if opts.ImageName != "" { if sc == nil { err := ErrNoClientProvidedForIDByName{} err.Argument = "ServiceClient" return nil, err } imageID, err := images.IDFromName(sc, opts.ImageName) if err != nil { return nil, err } b["imageRef"] = imageID } } // If FlavorRef isn't provided, use FlavorName to ascertain the flavor ID. if opts.FlavorRef == "" { if opts.FlavorName == "" { err := ErrNeitherFlavorIDNorFlavorNameProvided{} err.Argument = "FlavorRef/FlavorName" return nil, err } if sc == nil { err := ErrNoClientProvidedForIDByName{} err.Argument = "ServiceClient" return nil, err } flavorID, err := flavors.IDFromName(sc, opts.FlavorName) if err != nil { return nil, err } b["flavorRef"] = flavorID } return map[string]interface{}{"server": b}, nil } // Create requests a server to be provisioned to the user in the current tenant. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { reqBody, err := opts.ToServerCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(listURL(client), reqBody, &r.Body, nil) return } // Delete requests that a server previously provisioned be removed from your // account. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // ForceDelete forces the deletion of a server. func ForceDelete(client *gophercloud.ServiceClient, id string) (r ActionResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"forceDelete": ""}, nil, nil) return } // Get requests details on a single server, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, }) return } // UpdateOptsBuilder allows extensions to add additional attributes to the // Update request. type UpdateOptsBuilder interface { ToServerUpdateMap() (map[string]interface{}, error) } // UpdateOpts specifies the base attributes that may be updated on an existing // server. type UpdateOpts struct { // Name changes the displayed name of the server. // The server host name will *not* change. // Server names are not constrained to be unique, even within the same tenant. Name string `json:"name,omitempty"` // AccessIPv4 provides a new IPv4 address for the instance. AccessIPv4 string `json:"accessIPv4,omitempty"` // AccessIPv6 provides a new IPv6 address for the instance. AccessIPv6 string `json:"accessIPv6,omitempty"` } // ToServerUpdateMap formats an UpdateOpts structure into a request body. func (opts UpdateOpts) ToServerUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "server") } // Update requests that various attributes of the indicated server be changed. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToServerUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // ChangeAdminPassword alters the administrator or root password for a specified // server. func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword string) (r ActionResult) { b := map[string]interface{}{ "changePassword": map[string]string{ "adminPass": newPassword, }, } _, r.Err = client.Post(actionURL(client, id), b, nil, nil) return } // RebootMethod describes the mechanisms by which a server reboot can be requested. type RebootMethod string // These constants determine how a server should be rebooted. // See the Reboot() function for further details. const ( SoftReboot RebootMethod = "SOFT" HardReboot RebootMethod = "HARD" OSReboot = SoftReboot PowerCycle = HardReboot ) // RebootOptsBuilder allows extensions to add additional parameters to the // reboot request. type RebootOptsBuilder interface { ToServerRebootMap() (map[string]interface{}, error) } // RebootOpts provides options to the reboot request. type RebootOpts struct { // Type is the type of reboot to perform on the server. Type RebootMethod `json:"type" required:"true"` } // ToServerRebootMap builds a body for the reboot request. func (opts RebootOpts) ToServerRebootMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "reboot") } /* Reboot requests that a given server reboot. Two methods exist for rebooting a server: HardReboot (aka PowerCycle) starts the server instance by physically cutting power to the machine, or if a VM, terminating it at the hypervisor level. It's done. Caput. Full stop. Then, after a brief while, power is rtored or the VM instance restarted. SoftReboot (aka OSReboot) simply tells the OS to restart under its own procedure. E.g., in Linux, asking it to enter runlevel 6, or executing "sudo shutdown -r now", or by asking Windows to rtart the machine. */ func Reboot(client *gophercloud.ServiceClient, id string, opts RebootOptsBuilder) (r ActionResult) { b, err := opts.ToServerRebootMap() if err != nil { r.Err = err return } _, r.Err = client.Post(actionURL(client, id), b, nil, nil) return } // RebuildOptsBuilder allows extensions to provide additional parameters to the // rebuild request. type RebuildOptsBuilder interface { ToServerRebuildMap() (map[string]interface{}, error) } // RebuildOpts represents the configuration options used in a server rebuild // operation. type RebuildOpts struct { // AdminPass is the server's admin password AdminPass string `json:"adminPass,omitempty"` // ImageID is the ID of the image you want your server to be provisioned on. ImageID string `json:"imageRef"` // ImageName is readable name of an image. ImageName string `json:"-"` // Name to set the server to Name string `json:"name,omitempty"` // AccessIPv4 [optional] provides a new IPv4 address for the instance. AccessIPv4 string `json:"accessIPv4,omitempty"` // AccessIPv6 [optional] provides a new IPv6 address for the instance. AccessIPv6 string `json:"accessIPv6,omitempty"` // Metadata [optional] contains key-value pairs (up to 255 bytes each) // to attach to the server. Metadata map[string]string `json:"metadata,omitempty"` // Personality [optional] includes files to inject into the server at launch. // Rebuild will base64-encode file contents for you. Personality Personality `json:"personality,omitempty"` // ServiceClient will allow calls to be made to retrieve an image or // flavor ID by name. ServiceClient *gophercloud.ServiceClient `json:"-"` } // ToServerRebuildMap formats a RebuildOpts struct into a map for use in JSON func (opts RebuildOpts) ToServerRebuildMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } // If ImageRef isn't provided, check if ImageName was provided to ascertain // the image ID. if opts.ImageID == "" { if opts.ImageName != "" { if opts.ServiceClient == nil { err := ErrNoClientProvidedForIDByName{} err.Argument = "ServiceClient" return nil, err } imageID, err := images.IDFromName(opts.ServiceClient, opts.ImageName) if err != nil { return nil, err } b["imageRef"] = imageID } } return map[string]interface{}{"rebuild": b}, nil } // Rebuild will reprovision the server according to the configuration options // provided in the RebuildOpts struct. func Rebuild(client *gophercloud.ServiceClient, id string, opts RebuildOptsBuilder) (r RebuildResult) { b, err := opts.ToServerRebuildMap() if err != nil { r.Err = err return } _, r.Err = client.Post(actionURL(client, id), b, &r.Body, nil) return } // ResizeOptsBuilder allows extensions to add additional parameters to the // resize request. type ResizeOptsBuilder interface { ToServerResizeMap() (map[string]interface{}, error) } // ResizeOpts represents the configuration options used to control a Resize // operation. type ResizeOpts struct { // FlavorRef is the ID of the flavor you wish your server to become. FlavorRef string `json:"flavorRef" required:"true"` } // ToServerResizeMap formats a ResizeOpts as a map that can be used as a JSON // request body for the Resize request. func (opts ResizeOpts) ToServerResizeMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "resize") } // Resize instructs the provider to change the flavor of the server. // // Note that this implies rebuilding it. // // Unfortunately, one cannot pass rebuild parameters to the resize function. // When the resize completes, the server will be in VERIFY_RESIZE state. // While in this state, you can explore the use of the new server's // configuration. If you like it, call ConfirmResize() to commit the resize // permanently. Otherwise, call RevertResize() to restore the old configuration. func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ActionResult) { b, err := opts.ToServerResizeMap() if err != nil { r.Err = err return } _, r.Err = client.Post(actionURL(client, id), b, nil, nil) return } // ConfirmResize confirms a previous resize operation on a server. // See Resize() for more details. func ConfirmResize(client *gophercloud.ServiceClient, id string) (r ActionResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"confirmResize": nil}, nil, &gophercloud.RequestOpts{ OkCodes: []int{201, 202, 204}, }) return } // RevertResize cancels a previous resize operation on a server. // See Resize() for more details. func RevertResize(client *gophercloud.ServiceClient, id string) (r ActionResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"revertResize": nil}, nil, nil) return } // ResetMetadataOptsBuilder allows extensions to add additional parameters to // the Reset request. type ResetMetadataOptsBuilder interface { ToMetadataResetMap() (map[string]interface{}, error) } // MetadataOpts is a map that contains key-value pairs. type MetadataOpts map[string]string // ToMetadataResetMap assembles a body for a Reset request based on the contents // of a MetadataOpts. func (opts MetadataOpts) ToMetadataResetMap() (map[string]interface{}, error) { return map[string]interface{}{"metadata": opts}, nil } // ToMetadataUpdateMap assembles a body for an Update request based on the // contents of a MetadataOpts. func (opts MetadataOpts) ToMetadataUpdateMap() (map[string]interface{}, error) { return map[string]interface{}{"metadata": opts}, nil } // ResetMetadata will create multiple new key-value pairs for the given server // ID. // Note: Using this operation will erase any already-existing metadata and // create the new metadata provided. To keep any already-existing metadata, // use the UpdateMetadatas or UpdateMetadata function. func ResetMetadata(client *gophercloud.ServiceClient, id string, opts ResetMetadataOptsBuilder) (r ResetMetadataResult) { b, err := opts.ToMetadataResetMap() if err != nil { r.Err = err return } _, r.Err = client.Put(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Metadata requests all the metadata for the given server ID. func Metadata(client *gophercloud.ServiceClient, id string) (r GetMetadataResult) { _, r.Err = client.Get(metadataURL(client, id), &r.Body, nil) return } // UpdateMetadataOptsBuilder allows extensions to add additional parameters to // the Create request. type UpdateMetadataOptsBuilder interface { ToMetadataUpdateMap() (map[string]interface{}, error) } // UpdateMetadata updates (or creates) all the metadata specified by opts for // the given server ID. This operation does not affect already-existing metadata // that is not specified by opts. func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { b, err := opts.ToMetadataUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // MetadatumOptsBuilder allows extensions to add additional parameters to the // Create request. type MetadatumOptsBuilder interface { ToMetadatumCreateMap() (map[string]interface{}, string, error) } // MetadatumOpts is a map of length one that contains a key-value pair. type MetadatumOpts map[string]string // ToMetadatumCreateMap assembles a body for a Create request based on the // contents of a MetadataumOpts. func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]interface{}, string, error) { if len(opts) != 1 { err := gophercloud.ErrInvalidInput{} err.Argument = "servers.MetadatumOpts" err.Info = "Must have 1 and only 1 key-value pair" return nil, "", err } metadatum := map[string]interface{}{"meta": opts} var key string for k := range metadatum["meta"].(MetadatumOpts) { key = k } return metadatum, key, nil } // CreateMetadatum will create or update the key-value pair with the given key // for the given server ID. func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts MetadatumOptsBuilder) (r CreateMetadatumResult) { b, key, err := opts.ToMetadatumCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Put(metadatumURL(client, id, key), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Metadatum requests the key-value pair with the given key for the given // server ID. func Metadatum(client *gophercloud.ServiceClient, id, key string) (r GetMetadatumResult) { _, r.Err = client.Get(metadatumURL(client, id, key), &r.Body, nil) return } // DeleteMetadatum will delete the key-value pair with the given key for the // given server ID. func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) (r DeleteMetadatumResult) { _, r.Err = client.Delete(metadatumURL(client, id, key), nil) return } // ListAddresses makes a request against the API to list the servers IP // addresses. func ListAddresses(client *gophercloud.ServiceClient, id string) pagination.Pager { return pagination.NewPager(client, listAddressesURL(client, id), func(r pagination.PageResult) pagination.Page { return AddressPage{pagination.SinglePageBase(r)} }) } // ListAddressesByNetwork makes a request against the API to list the servers IP // addresses for the given network. func ListAddressesByNetwork(client *gophercloud.ServiceClient, id, network string) pagination.Pager { return pagination.NewPager(client, listAddressesByNetworkURL(client, id, network), func(r pagination.PageResult) pagination.Page { return NetworkAddressPage{pagination.SinglePageBase(r)} }) } // CreateImageOptsBuilder allows extensions to add additional parameters to the // CreateImage request. type CreateImageOptsBuilder interface { ToServerCreateImageMap() (map[string]interface{}, error) } // CreateImageOpts provides options to pass to the CreateImage request. type CreateImageOpts struct { // Name of the image/snapshot. Name string `json:"name" required:"true"` // Metadata contains key-value pairs (up to 255 bytes each) to attach to // the created image. Metadata map[string]string `json:"metadata,omitempty"` } // ToServerCreateImageMap formats a CreateImageOpts structure into a request // body. func (opts CreateImageOpts) ToServerCreateImageMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "createImage") } // CreateImage makes a request against the nova API to schedule an image to be // created of the server func CreateImage(client *gophercloud.ServiceClient, id string, opts CreateImageOptsBuilder) (r CreateImageResult) { b, err := opts.ToServerCreateImageMap() if err != nil { r.Err = err return } resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) r.Err = err r.Header = resp.Header return } // IDFromName is a convienience function that returns a server's ID given its // name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" listOpts := ListOpts{ Name: name, } allPages, err := List(client, listOpts).AllPages() if err != nil { return "", err } all, err := ExtractServers(allPages) if err != nil { return "", err } for _, f := range all { if f.Name == name { count++ id = f.ID } } switch count { case 0: return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "server"} case 1: return id, nil default: return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "server"} } } // GetPassword makes a request against the nova API to get the encrypted // administrative password. func GetPassword(client *gophercloud.ServiceClient, serverId string) (r GetPasswordResult) { _, r.Err = client.Get(passwordURL(client, serverId), &r.Body, nil) return } // ShowConsoleOutputOptsBuilder is the interface types must satisfy in order to be // used as ShowConsoleOutput options type ShowConsoleOutputOptsBuilder interface { ToServerShowConsoleOutputMap() (map[string]interface{}, error) } // ShowConsoleOutputOpts satisfies the ShowConsoleOutputOptsBuilder type ShowConsoleOutputOpts struct { // The number of lines to fetch from the end of console log. // All lines will be returned if this is not specified. Length int `json:"length,omitempty"` } // ToServerShowConsoleOutputMap formats a ShowConsoleOutputOpts structure into a request body. func (opts ShowConsoleOutputOpts) ToServerShowConsoleOutputMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "os-getConsoleOutput") } // ShowConsoleOutput makes a request against the nova API to get console log from the server func ShowConsoleOutput(client *gophercloud.ServiceClient, id string, opts ShowConsoleOutputOptsBuilder) (r ShowConsoleOutputResult) { b, err := opts.ToServerShowConsoleOutputMap() if err != nil { r.Err = err return } _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000301561335005366400332150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/serverspackage servers import ( "crypto/rsa" "encoding/base64" "encoding/json" "fmt" "net/url" "path" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type serverResult struct { gophercloud.Result } // Extract interprets any serverResult as a Server, if possible. func (r serverResult) Extract() (*Server, error) { var s Server err := r.ExtractInto(&s) return &s, err } func (r serverResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "server") } func ExtractServersInto(r pagination.Page, v interface{}) error { return r.(ServerPage).Result.ExtractIntoSlicePtr(v, "servers") } // CreateResult is the response from a Create operation. Call its Extract // method to interpret it as a Server. type CreateResult struct { serverResult } // GetResult is the response from a Get operation. Call its Extract // method to interpret it as a Server. type GetResult struct { serverResult } // UpdateResult is the response from an Update operation. Call its Extract // method to interpret it as a Server. type UpdateResult struct { serverResult } // DeleteResult is the response from a Delete operation. Call its ExtractErr // method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // RebuildResult is the response from a Rebuild operation. Call its Extract // method to interpret it as a Server. type RebuildResult struct { serverResult } // ActionResult represents the result of server action operations, like reboot. // Call its ExtractErr method to determine if the action succeeded or failed. type ActionResult struct { gophercloud.ErrResult } // CreateImageResult is the response from a CreateImage operation. Call its // ExtractImageID method to retrieve the ID of the newly created image. type CreateImageResult struct { gophercloud.Result } // ShowConsoleOutputResult represents the result of console output from a server type ShowConsoleOutputResult struct { gophercloud.Result } // Extract will return the console output from a ShowConsoleOutput request. func (r ShowConsoleOutputResult) Extract() (string, error) { var s struct { Output string `json:"output"` } err := r.ExtractInto(&s) return s.Output, err } // GetPasswordResult represent the result of a get os-server-password operation. // Call its ExtractPassword method to retrieve the password. type GetPasswordResult struct { gophercloud.Result } // ExtractPassword gets the encrypted password. // If privateKey != nil the password is decrypted with the private key. // If privateKey == nil the encrypted password is returned and can be decrypted // with: // echo '' | base64 -D | openssl rsautl -decrypt -inkey func (r GetPasswordResult) ExtractPassword(privateKey *rsa.PrivateKey) (string, error) { var s struct { Password string `json:"password"` } err := r.ExtractInto(&s) if err == nil && privateKey != nil && s.Password != "" { return decryptPassword(s.Password, privateKey) } return s.Password, err } func decryptPassword(encryptedPassword string, privateKey *rsa.PrivateKey) (string, error) { b64EncryptedPassword := make([]byte, base64.StdEncoding.DecodedLen(len(encryptedPassword))) n, err := base64.StdEncoding.Decode(b64EncryptedPassword, []byte(encryptedPassword)) if err != nil { return "", fmt.Errorf("Failed to base64 decode encrypted password: %s", err) } password, err := rsa.DecryptPKCS1v15(nil, privateKey, b64EncryptedPassword[0:n]) if err != nil { return "", fmt.Errorf("Failed to decrypt password: %s", err) } return string(password), nil } // ExtractImageID gets the ID of the newly created server image from the header. func (r CreateImageResult) ExtractImageID() (string, error) { if r.Err != nil { return "", r.Err } // Get the image id from the header u, err := url.ParseRequestURI(r.Header.Get("Location")) if err != nil { return "", err } imageID := path.Base(u.Path) if imageID == "." || imageID == "/" { return "", fmt.Errorf("Failed to parse the ID of newly created image: %s", u) } return imageID, nil } // Server represents a server/instance in the OpenStack cloud. type Server struct { // ID uniquely identifies this server amongst all other servers, // including those not accessible to the current tenant. ID string `json:"id"` // TenantID identifies the tenant owning this server resource. TenantID string `json:"tenant_id"` // UserID uniquely identifies the user account owning the tenant. UserID string `json:"user_id"` // Name contains the human-readable name for the server. Name string `json:"name"` // Updated and Created contain ISO-8601 timestamps of when the state of the // server last changed, and when it was created. Updated time.Time `json:"updated"` Created time.Time `json:"created"` // HostID is the host where the server is located in the cloud. HostID string `json:"hostid"` // Status contains the current operational status of the server, // such as IN_PROGRESS or ACTIVE. Status string `json:"status"` // Progress ranges from 0..100. // A request made against the server completes only once Progress reaches 100. Progress int `json:"progress"` // AccessIPv4 and AccessIPv6 contain the IP addresses of the server, // suitable for remote access for administration. AccessIPv4 string `json:"accessIPv4"` AccessIPv6 string `json:"accessIPv6"` // Image refers to a JSON object, which itself indicates the OS image used to // deploy the server. Image map[string]interface{} `json:"-"` // Flavor refers to a JSON object, which itself indicates the hardware // configuration of the deployed server. Flavor map[string]interface{} `json:"flavor"` // Addresses includes a list of all IP addresses assigned to the server, // keyed by pool. Addresses map[string]interface{} `json:"addresses"` // Metadata includes a list of all user-specified key-value pairs attached // to the server. Metadata map[string]string `json:"metadata"` // Links includes HTTP references to the itself, useful for passing along to // other APIs that might want a server reference. Links []interface{} `json:"links"` // KeyName indicates which public key was injected into the server on launch. KeyName string `json:"key_name"` // AdminPass will generally be empty (""). However, it will contain the // administrative password chosen when provisioning a new server without a // set AdminPass setting in the first place. // Note that this is the ONLY time this field will be valid. AdminPass string `json:"adminPass"` // SecurityGroups includes the security groups that this instance has applied // to it. SecurityGroups []map[string]interface{} `json:"security_groups"` // Fault contains failure information about a server. Fault Fault `json:"fault"` } type Fault struct { Code int `json:"code"` Created time.Time `json:"created"` Details string `json:"details"` Message string `json:"message"` } func (r *Server) UnmarshalJSON(b []byte) error { type tmp Server var s struct { tmp Image interface{} `json:"image"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Server(s.tmp) switch t := s.Image.(type) { case map[string]interface{}: r.Image = t case string: switch t { case "": r.Image = nil } } return err } // ServerPage abstracts the raw results of making a List() request against // the API. As OpenStack extensions may freely alter the response bodies of // structures returned to the client, you may only safely access the data // provided through the ExtractServers call. type ServerPage struct { pagination.LinkedPageBase } // IsEmpty returns true if a page contains no Server results. func (r ServerPage) IsEmpty() (bool, error) { s, err := ExtractServers(r) return len(s) == 0, err } // NextPageURL uses the response's embedded link reference to navigate to the // next page of results. func (r ServerPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"servers_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // ExtractServers interprets the results of a single page from a List() call, // producing a slice of Server entities. func ExtractServers(r pagination.Page) ([]Server, error) { var s []Server err := ExtractServersInto(r, &s) return s, err } // MetadataResult contains the result of a call for (potentially) multiple // key-value pairs. Call its Extract method to interpret it as a // map[string]interface. type MetadataResult struct { gophercloud.Result } // GetMetadataResult contains the result of a Get operation. Call its Extract // method to interpret it as a map[string]interface. type GetMetadataResult struct { MetadataResult } // ResetMetadataResult contains the result of a Reset operation. Call its // Extract method to interpret it as a map[string]interface. type ResetMetadataResult struct { MetadataResult } // UpdateMetadataResult contains the result of an Update operation. Call its // Extract method to interpret it as a map[string]interface. type UpdateMetadataResult struct { MetadataResult } // MetadatumResult contains the result of a call for individual a single // key-value pair. type MetadatumResult struct { gophercloud.Result } // GetMetadatumResult contains the result of a Get operation. Call its Extract // method to interpret it as a map[string]interface. type GetMetadatumResult struct { MetadatumResult } // CreateMetadatumResult contains the result of a Create operation. Call its // Extract method to interpret it as a map[string]interface. type CreateMetadatumResult struct { MetadatumResult } // DeleteMetadatumResult contains the result of a Delete operation. Call its // ExtractErr method to determine if the call succeeded or failed. type DeleteMetadatumResult struct { gophercloud.ErrResult } // Extract interprets any MetadataResult as a Metadata, if possible. func (r MetadataResult) Extract() (map[string]string, error) { var s struct { Metadata map[string]string `json:"metadata"` } err := r.ExtractInto(&s) return s.Metadata, err } // Extract interprets any MetadatumResult as a Metadatum, if possible. func (r MetadatumResult) Extract() (map[string]string, error) { var s struct { Metadatum map[string]string `json:"meta"` } err := r.ExtractInto(&s) return s.Metadatum, err } // Address represents an IP address. type Address struct { Version int `json:"version"` Address string `json:"addr"` } // AddressPage abstracts the raw results of making a ListAddresses() request // against the API. As OpenStack extensions may freely alter the response bodies // of structures returned to the client, you may only safely access the data // provided through the ExtractAddresses call. type AddressPage struct { pagination.SinglePageBase } // IsEmpty returns true if an AddressPage contains no networks. func (r AddressPage) IsEmpty() (bool, error) { addresses, err := ExtractAddresses(r) return len(addresses) == 0, err } // ExtractAddresses interprets the results of a single page from a // ListAddresses() call, producing a map of addresses. func ExtractAddresses(r pagination.Page) (map[string][]Address, error) { var s struct { Addresses map[string][]Address `json:"addresses"` } err := (r.(AddressPage)).ExtractInto(&s) return s.Addresses, err } // NetworkAddressPage abstracts the raw results of making a // ListAddressesByNetwork() request against the API. // As OpenStack extensions may freely alter the response bodies of structures // returned to the client, you may only safely access the data provided through // the ExtractAddresses call. type NetworkAddressPage struct { pagination.SinglePageBase } // IsEmpty returns true if a NetworkAddressPage contains no addresses. func (r NetworkAddressPage) IsEmpty() (bool, error) { addresses, err := ExtractNetworkAddresses(r) return len(addresses) == 0, err } // ExtractNetworkAddresses interprets the results of a single page from a // ListAddressesByNetwork() call, producing a slice of addresses. func ExtractNetworkAddresses(r pagination.Page) ([]Address, error) { var s map[string][]Address err := (r.(NetworkAddressPage)).ExtractInto(&s) if err != nil { return nil, err } var key string for k := range s { key = k } return s[key], err } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/servers/testing/000077500000000000000000000000001335005366400327145ustar00rootroot00000000000000doc.go000066400000000000000000000000461335005366400337310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/servers/testing// servers unit tests package testing fixtures.go000066400000000000000000001000471335005366400350370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/servers/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ServerListBody contains the canned body of a servers.List response. const ServerListBody = ` { "servers": [ { "status": "ACTIVE", "updated": "2014-09-25T13:10:10Z", "hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362", "OS-EXT-SRV-ATTR:host": "devstack", "addresses": { "private": [ { "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:7c:1b:2b", "version": 4, "addr": "10.0.0.32", "OS-EXT-IPS:type": "fixed" } ] }, "links": [ { "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5", "rel": "self" }, { "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5", "rel": "bookmark" } ], "key_name": null, "image": { "id": "f90f6034-2570-4974-8351-6b49732ef2eb", "links": [ { "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb", "rel": "bookmark" } ] }, "OS-EXT-STS:task_state": null, "OS-EXT-STS:vm_state": "active", "OS-EXT-SRV-ATTR:instance_name": "instance-0000001e", "OS-SRV-USG:launched_at": "2014-09-25T13:10:10.000000", "OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack", "flavor": { "id": "1", "links": [ { "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1", "rel": "bookmark" } ] }, "id": "ef079b0c-e610-4dfb-b1aa-b49f07ac48e5", "security_groups": [ { "name": "default" } ], "OS-SRV-USG:terminated_at": null, "OS-EXT-AZ:availability_zone": "nova", "user_id": "9349aff8be7545ac9d2f1d00999a23cd", "name": "herp", "created": "2014-09-25T13:10:02Z", "tenant_id": "fcad67a6189847c4aecfa3c81a05783b", "OS-DCF:diskConfig": "MANUAL", "os-extended-volumes:volumes_attached": [], "accessIPv4": "", "accessIPv6": "", "progress": 0, "OS-EXT-STS:power_state": 1, "config_drive": "", "metadata": {} }, { "status": "ACTIVE", "updated": "2014-09-25T13:04:49Z", "hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362", "OS-EXT-SRV-ATTR:host": "devstack", "addresses": { "private": [ { "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be", "version": 4, "addr": "10.0.0.31", "OS-EXT-IPS:type": "fixed" } ] }, "links": [ { "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", "rel": "self" }, { "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", "rel": "bookmark" } ], "key_name": null, "image": { "id": "f90f6034-2570-4974-8351-6b49732ef2eb", "links": [ { "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb", "rel": "bookmark" } ] }, "OS-EXT-STS:task_state": null, "OS-EXT-STS:vm_state": "active", "OS-EXT-SRV-ATTR:instance_name": "instance-0000001d", "OS-SRV-USG:launched_at": "2014-09-25T13:04:49.000000", "OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack", "flavor": { "id": "1", "links": [ { "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1", "rel": "bookmark" } ] }, "id": "9e5476bd-a4ec-4653-93d6-72c93aa682ba", "security_groups": [ { "name": "default" } ], "OS-SRV-USG:terminated_at": null, "OS-EXT-AZ:availability_zone": "nova", "user_id": "9349aff8be7545ac9d2f1d00999a23cd", "name": "derp", "created": "2014-09-25T13:04:41Z", "tenant_id": "fcad67a6189847c4aecfa3c81a05783b", "OS-DCF:diskConfig": "MANUAL", "os-extended-volumes:volumes_attached": [], "accessIPv4": "", "accessIPv6": "", "progress": 0, "OS-EXT-STS:power_state": 1, "config_drive": "", "metadata": {} }, { "status": "ACTIVE", "updated": "2014-09-25T13:04:49Z", "hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362", "OS-EXT-SRV-ATTR:host": "devstack", "addresses": { "private": [ { "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be", "version": 4, "addr": "10.0.0.31", "OS-EXT-IPS:type": "fixed" } ] }, "links": [ { "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", "rel": "self" }, { "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", "rel": "bookmark" } ], "key_name": null, "image": "", "OS-EXT-STS:task_state": null, "OS-EXT-STS:vm_state": "active", "OS-EXT-SRV-ATTR:instance_name": "instance-0000001d", "OS-SRV-USG:launched_at": "2014-09-25T13:04:49.000000", "OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack", "flavor": { "id": "1", "links": [ { "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1", "rel": "bookmark" } ] }, "id": "9e5476bd-a4ec-4653-93d6-72c93aa682bb", "security_groups": [ { "name": "default" } ], "OS-SRV-USG:terminated_at": null, "OS-EXT-AZ:availability_zone": "nova", "user_id": "9349aff8be7545ac9d2f1d00999a23cd", "name": "merp", "created": "2014-09-25T13:04:41Z", "tenant_id": "fcad67a6189847c4aecfa3c81a05783b", "OS-DCF:diskConfig": "MANUAL", "os-extended-volumes:volumes_attached": [], "accessIPv4": "", "accessIPv6": "", "progress": 0, "OS-EXT-STS:power_state": 1, "config_drive": "", "metadata": {} } ] } ` // SingleServerBody is the canned body of a Get request on an existing server. const SingleServerBody = ` { "server": { "status": "ACTIVE", "updated": "2014-09-25T13:04:49Z", "hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362", "OS-EXT-SRV-ATTR:host": "devstack", "addresses": { "private": [ { "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be", "version": 4, "addr": "10.0.0.31", "OS-EXT-IPS:type": "fixed" } ] }, "links": [ { "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", "rel": "self" }, { "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", "rel": "bookmark" } ], "key_name": null, "image": { "id": "f90f6034-2570-4974-8351-6b49732ef2eb", "links": [ { "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb", "rel": "bookmark" } ] }, "OS-EXT-STS:task_state": null, "OS-EXT-STS:vm_state": "active", "OS-EXT-SRV-ATTR:instance_name": "instance-0000001d", "OS-SRV-USG:launched_at": "2014-09-25T13:04:49.000000", "OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack", "flavor": { "id": "1", "links": [ { "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1", "rel": "bookmark" } ] }, "id": "9e5476bd-a4ec-4653-93d6-72c93aa682ba", "security_groups": [ { "name": "default" } ], "OS-SRV-USG:terminated_at": null, "OS-EXT-AZ:availability_zone": "nova", "user_id": "9349aff8be7545ac9d2f1d00999a23cd", "name": "derp", "created": "2014-09-25T13:04:41Z", "tenant_id": "fcad67a6189847c4aecfa3c81a05783b", "OS-DCF:diskConfig": "MANUAL", "os-extended-volumes:volumes_attached": [], "accessIPv4": "", "accessIPv6": "", "progress": 0, "OS-EXT-STS:power_state": 1, "config_drive": "", "metadata": {} } } ` // FaultyServerBody is the body of a Get request on an existing server // which has a fault/error. const FaultyServerBody = ` { "server": { "status": "ACTIVE", "updated": "2014-09-25T13:04:49Z", "hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362", "OS-EXT-SRV-ATTR:host": "devstack", "addresses": { "private": [ { "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be", "version": 4, "addr": "10.0.0.31", "OS-EXT-IPS:type": "fixed" } ] }, "links": [ { "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", "rel": "self" }, { "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", "rel": "bookmark" } ], "key_name": null, "image": { "id": "f90f6034-2570-4974-8351-6b49732ef2eb", "links": [ { "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb", "rel": "bookmark" } ] }, "OS-EXT-STS:task_state": null, "OS-EXT-STS:vm_state": "active", "OS-EXT-SRV-ATTR:instance_name": "instance-0000001d", "OS-SRV-USG:launched_at": "2014-09-25T13:04:49.000000", "OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack", "flavor": { "id": "1", "links": [ { "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1", "rel": "bookmark" } ] }, "id": "9e5476bd-a4ec-4653-93d6-72c93aa682ba", "security_groups": [ { "name": "default" } ], "OS-SRV-USG:terminated_at": null, "OS-EXT-AZ:availability_zone": "nova", "user_id": "9349aff8be7545ac9d2f1d00999a23cd", "name": "derp", "created": "2014-09-25T13:04:41Z", "tenant_id": "fcad67a6189847c4aecfa3c81a05783b", "OS-DCF:diskConfig": "MANUAL", "os-extended-volumes:volumes_attached": [], "accessIPv4": "", "accessIPv6": "", "progress": 0, "OS-EXT-STS:power_state": 1, "config_drive": "", "metadata": {}, "fault": { "message": "Conflict updating instance c2ce4dea-b73f-4d01-8633-2c6032869281. Expected: {'task_state': [u'spawning']}. Actual: {'task_state': None}", "code": 500, "created": "2017-11-11T07:58:39Z", "details": "Stock details for test" } } } ` const ServerPasswordBody = ` { "password": "xlozO3wLCBRWAa2yDjCCVx8vwNPypxnypmRYDa/zErlQ+EzPe1S/Gz6nfmC52mOlOSCRuUOmG7kqqgejPof6M7bOezS387zjq4LSvvwp28zUknzy4YzfFGhnHAdai3TxUJ26pfQCYrq8UTzmKF2Bq8ioSEtVVzM0A96pDh8W2i7BOz6MdoiVyiev/I1K2LsuipfxSJR7Wdke4zNXJjHHP2RfYsVbZ/k9ANu+Nz4iIH8/7Cacud/pphH7EjrY6a4RZNrjQskrhKYed0YERpotyjYk1eDtRe72GrSiXteqCM4biaQ5w3ruS+AcX//PXk3uJ5kC7d67fPXaVz4WaQRYMg==" } ` const ConsoleOutputBody = `{ "output": "abc" }` var ( herpTimeCreated, _ = time.Parse(time.RFC3339, "2014-09-25T13:10:02Z") herpTimeUpdated, _ = time.Parse(time.RFC3339, "2014-09-25T13:10:10Z") // ServerHerp is a Server struct that should correspond to the first result in ServerListBody. ServerHerp = servers.Server{ Status: "ACTIVE", Updated: herpTimeUpdated, HostID: "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362", Addresses: map[string]interface{}{ "private": []interface{}{ map[string]interface{}{ "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:7c:1b:2b", "version": float64(4), "addr": "10.0.0.32", "OS-EXT-IPS:type": "fixed", }, }, }, Links: []interface{}{ map[string]interface{}{ "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5", "rel": "self", }, map[string]interface{}{ "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5", "rel": "bookmark", }, }, Image: map[string]interface{}{ "id": "f90f6034-2570-4974-8351-6b49732ef2eb", "links": []interface{}{ map[string]interface{}{ "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb", "rel": "bookmark", }, }, }, Flavor: map[string]interface{}{ "id": "1", "links": []interface{}{ map[string]interface{}{ "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1", "rel": "bookmark", }, }, }, ID: "ef079b0c-e610-4dfb-b1aa-b49f07ac48e5", UserID: "9349aff8be7545ac9d2f1d00999a23cd", Name: "herp", Created: herpTimeCreated, TenantID: "fcad67a6189847c4aecfa3c81a05783b", Metadata: map[string]string{}, SecurityGroups: []map[string]interface{}{ map[string]interface{}{ "name": "default", }, }, } derpTimeCreated, _ = time.Parse(time.RFC3339, "2014-09-25T13:04:41Z") derpTimeUpdated, _ = time.Parse(time.RFC3339, "2014-09-25T13:04:49Z") // ServerDerp is a Server struct that should correspond to the second server in ServerListBody. ServerDerp = servers.Server{ Status: "ACTIVE", Updated: derpTimeUpdated, HostID: "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362", Addresses: map[string]interface{}{ "private": []interface{}{ map[string]interface{}{ "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be", "version": float64(4), "addr": "10.0.0.31", "OS-EXT-IPS:type": "fixed", }, }, }, Links: []interface{}{ map[string]interface{}{ "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", "rel": "self", }, map[string]interface{}{ "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", "rel": "bookmark", }, }, Image: map[string]interface{}{ "id": "f90f6034-2570-4974-8351-6b49732ef2eb", "links": []interface{}{ map[string]interface{}{ "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb", "rel": "bookmark", }, }, }, Flavor: map[string]interface{}{ "id": "1", "links": []interface{}{ map[string]interface{}{ "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1", "rel": "bookmark", }, }, }, ID: "9e5476bd-a4ec-4653-93d6-72c93aa682ba", UserID: "9349aff8be7545ac9d2f1d00999a23cd", Name: "derp", Created: derpTimeCreated, TenantID: "fcad67a6189847c4aecfa3c81a05783b", Metadata: map[string]string{}, SecurityGroups: []map[string]interface{}{ map[string]interface{}{ "name": "default", }, }, } ConsoleOutput = "abc" merpTimeCreated, _ = time.Parse(time.RFC3339, "2014-09-25T13:04:41Z") merpTimeUpdated, _ = time.Parse(time.RFC3339, "2014-09-25T13:04:49Z") // ServerMerp is a Server struct that should correspond to the second server in ServerListBody. ServerMerp = servers.Server{ Status: "ACTIVE", Updated: merpTimeUpdated, HostID: "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362", Addresses: map[string]interface{}{ "private": []interface{}{ map[string]interface{}{ "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be", "version": float64(4), "addr": "10.0.0.31", "OS-EXT-IPS:type": "fixed", }, }, }, Links: []interface{}{ map[string]interface{}{ "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", "rel": "self", }, map[string]interface{}{ "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", "rel": "bookmark", }, }, Image: nil, Flavor: map[string]interface{}{ "id": "1", "links": []interface{}{ map[string]interface{}{ "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1", "rel": "bookmark", }, }, }, ID: "9e5476bd-a4ec-4653-93d6-72c93aa682bb", UserID: "9349aff8be7545ac9d2f1d00999a23cd", Name: "merp", Created: merpTimeCreated, TenantID: "fcad67a6189847c4aecfa3c81a05783b", Metadata: map[string]string{}, SecurityGroups: []map[string]interface{}{ map[string]interface{}{ "name": "default", }, }, } faultTimeCreated, _ = time.Parse(time.RFC3339, "2017-11-11T07:58:39Z") DerpFault = servers.Fault{ Code: 500, Created: faultTimeCreated, Details: "Stock details for test", Message: "Conflict updating instance c2ce4dea-b73f-4d01-8633-2c6032869281. " + "Expected: {'task_state': [u'spawning']}. Actual: {'task_state': None}", } ) type CreateOptsWithCustomField struct { servers.CreateOpts Foo string `json:"foo,omitempty"` } func (opts CreateOptsWithCustomField) ToServerCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "server") } // HandleServerCreationSuccessfully sets up the test server to respond to a server creation request // with a given response. func HandleServerCreationSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/servers", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "server": { "name": "derp", "imageRef": "f90f6034-2570-4974-8351-6b49732ef2eb", "flavorRef": "1" } }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) th.Mux.HandleFunc("/images/detail", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, ` { "images": [ { "status": "ACTIVE", "updated": "2014-09-23T12:54:56Z", "id": "f3e4a95d-1f4f-4989-97ce-f3a1fb8c04d7", "OS-EXT-IMG-SIZE:size": 476704768, "name": "F17-x86_64-cfntools", "created": "2014-09-23T12:54:52Z", "minDisk": 0, "progress": 100, "minRam": 0 }, { "status": "ACTIVE", "updated": "2014-09-23T12:51:43Z", "id": "f90f6034-2570-4974-8351-6b49732ef2eb", "OS-EXT-IMG-SIZE:size": 13167616, "name": "cirros-0.3.2-x86_64-disk", "created": "2014-09-23T12:51:42Z", "minDisk": 0, "progress": 100, "minRam": 0 } ] } `) case "2": fmt.Fprintf(w, `{ "images": [] }`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) th.Mux.HandleFunc("/flavors/detail", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, ` { "flavors": [ { "id": "1", "name": "m1.tiny", "disk": 1, "ram": 512, "vcpus": 1, "swap":"" }, { "id": "2", "name": "m2.small", "disk": 10, "ram": 1024, "vcpus": 2, "swap": 1000 } ], "flavors_links": [ { "href": "%s/flavors/detail?marker=2", "rel": "next" } ] } `, th.Server.URL) case "2": fmt.Fprintf(w, `{ "flavors": [] }`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) } // HandleServerCreationWithCustomFieldSuccessfully sets up the test server to respond to a server creation request // with a given response. func HandleServerCreationWithCustomFieldSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/servers", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "server": { "name": "derp", "imageRef": "f90f6034-2570-4974-8351-6b49732ef2eb", "flavorRef": "1", "foo": "bar" } }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // HandleServerCreationWithUserdata sets up the test server to respond to a server creation request // with a given response. func HandleServerCreationWithUserdata(t *testing.T, response string) { th.Mux.HandleFunc("/servers", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "server": { "name": "derp", "imageRef": "f90f6034-2570-4974-8351-6b49732ef2eb", "flavorRef": "1", "user_data": "dXNlcmRhdGEgc3RyaW5n" } }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // HandleServerCreationWithMetadata sets up the test server to respond to a server creation request // with a given response. func HandleServerCreationWithMetadata(t *testing.T, response string) { th.Mux.HandleFunc("/servers", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "server": { "name": "derp", "imageRef": "f90f6034-2570-4974-8351-6b49732ef2eb", "flavorRef": "1", "metadata": { "abc": "def" } } }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // HandleServerListSuccessfully sets up the test server to respond to a server List request. func HandleServerListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/detail", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, ServerListBody) case "9e5476bd-a4ec-4653-93d6-72c93aa682ba": fmt.Fprintf(w, `{ "servers": [] }`) default: t.Fatalf("/servers/detail invoked with unexpected marker=[%s]", marker) } }) } // HandleServerDeletionSuccessfully sets up the test server to respond to a server deletion request. func HandleServerDeletionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/asdfasdfasdf", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleServerForceDeletionSuccessfully sets up the test server to respond to a server force deletion // request. func HandleServerForceDeletionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/asdfasdfasdf/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "forceDelete": "" }`) w.WriteHeader(http.StatusAccepted) }) } // HandleServerGetSuccessfully sets up the test server to respond to a server Get request. func HandleServerGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/1234asdf", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, SingleServerBody) }) } // HandleServerGetFaultSuccessfully sets up the test server to respond to a server Get // request which contains a fault. func HandleServerGetFaultSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/1234asdf", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, FaultyServerBody) }) } // HandleServerUpdateSuccessfully sets up the test server to respond to a server Update request. func HandleServerUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/1234asdf", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, `{ "server": { "name": "new-name" } }`) fmt.Fprintf(w, SingleServerBody) }) } // HandleAdminPasswordChangeSuccessfully sets up the test server to respond to a server password // change request. func HandleAdminPasswordChangeSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "changePassword": { "adminPass": "new-password" } }`) w.WriteHeader(http.StatusAccepted) }) } // HandleRebootSuccessfully sets up the test server to respond to a reboot request with success. func HandleRebootSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "reboot": { "type": "SOFT" } }`) w.WriteHeader(http.StatusAccepted) }) } // HandleShowConsoleOutputSuccessfully sets up the test server to respond to a os-getConsoleOutput request with success. func HandleShowConsoleOutputSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "os-getConsoleOutput": { "length": 50 } }`) w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // HandleRebuildSuccessfully sets up the test server to respond to a rebuild request with success. func HandleRebuildSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, ` { "rebuild": { "name": "new-name", "adminPass": "swordfish", "imageRef": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb", "accessIPv4": "1.2.3.4" } } `) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // HandleMetadatumGetSuccessfully sets up the test server to respond to a metadatum Get request. func HandleMetadatumGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/1234asdf/metadata/foo", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") w.Write([]byte(`{ "meta": {"foo":"bar"}}`)) }) } // HandleMetadatumCreateSuccessfully sets up the test server to respond to a metadatum Create request. func HandleMetadatumCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/1234asdf/metadata/foo", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "meta": { "foo": "bar" } }`) w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") w.Write([]byte(`{ "meta": {"foo":"bar"}}`)) }) } // HandleMetadatumDeleteSuccessfully sets up the test server to respond to a metadatum Delete request. func HandleMetadatumDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/1234asdf/metadata/foo", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleMetadataGetSuccessfully sets up the test server to respond to a metadata Get request. func HandleMetadataGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/1234asdf/metadata", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.WriteHeader(http.StatusOK) w.Write([]byte(`{ "metadata": {"foo":"bar", "this":"that"}}`)) }) } // HandleMetadataResetSuccessfully sets up the test server to respond to a metadata Create request. func HandleMetadataResetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/1234asdf/metadata", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "metadata": { "foo": "bar", "this": "that" } }`) w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") w.Write([]byte(`{ "metadata": {"foo":"bar", "this":"that"}}`)) }) } // HandleMetadataUpdateSuccessfully sets up the test server to respond to a metadata Update request. func HandleMetadataUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/1234asdf/metadata", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "metadata": { "foo": "baz", "this": "those" } }`) w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") w.Write([]byte(`{ "metadata": {"foo":"baz", "this":"those"}}`)) }) } // ListAddressesExpected represents an expected repsonse from a ListAddresses request. var ListAddressesExpected = map[string][]servers.Address{ "public": []servers.Address{ { Version: 4, Address: "50.56.176.35", }, { Version: 6, Address: "2001:4800:790e:510:be76:4eff:fe04:84a8", }, }, "private": []servers.Address{ { Version: 4, Address: "10.180.3.155", }, }, } // HandleAddressListSuccessfully sets up the test server to respond to a ListAddresses request. func HandleAddressListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/asdfasdfasdf/ips", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "addresses": { "public": [ { "version": 4, "addr": "50.56.176.35" }, { "version": 6, "addr": "2001:4800:790e:510:be76:4eff:fe04:84a8" } ], "private": [ { "version": 4, "addr": "10.180.3.155" } ] } }`) }) } // ListNetworkAddressesExpected represents an expected repsonse from a ListAddressesByNetwork request. var ListNetworkAddressesExpected = []servers.Address{ { Version: 4, Address: "50.56.176.35", }, { Version: 6, Address: "2001:4800:790e:510:be76:4eff:fe04:84a8", }, } // HandleNetworkAddressListSuccessfully sets up the test server to respond to a ListAddressesByNetwork request. func HandleNetworkAddressListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/asdfasdfasdf/ips/public", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "public": [ { "version": 4, "addr": "50.56.176.35" }, { "version": 6, "addr": "2001:4800:790e:510:be76:4eff:fe04:84a8" } ] }`) }) } // HandleCreateServerImageSuccessfully sets up the test server to respond to a TestCreateServerImage request. func HandleCreateServerImageSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/serverimage/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Location", "https://0.0.0.0/images/xxxx-xxxxx-xxxxx-xxxx") w.WriteHeader(http.StatusAccepted) }) } // HandlePasswordGetSuccessfully sets up the test server to respond to a password Get request. func HandlePasswordGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/1234asdf/os-server-password", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, ServerPasswordBody) }) } requests_test.go000066400000000000000000000345011335005366400361010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/servers/testingpackage testing import ( "encoding/base64" "encoding/json" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/diskconfig" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/extendedstatus" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListServers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleServerListSuccessfully(t) pages := 0 err := servers.List(client.ServiceClient(), servers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := servers.ExtractServers(page) if err != nil { return false, err } if len(actual) != 3 { t.Fatalf("Expected 3 servers, got %d", len(actual)) } th.CheckDeepEquals(t, ServerHerp, actual[0]) th.CheckDeepEquals(t, ServerDerp, actual[1]) th.CheckDeepEquals(t, ServerMerp, actual[2]) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListAllServers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleServerListSuccessfully(t) allPages, err := servers.List(client.ServiceClient(), servers.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := servers.ExtractServers(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ServerHerp, actual[0]) th.CheckDeepEquals(t, ServerDerp, actual[1]) } func TestListAllServersWithExtensions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleServerListSuccessfully(t) type ServerWithExt struct { servers.Server availabilityzones.ServerAvailabilityZoneExt extendedstatus.ServerExtendedStatusExt diskconfig.ServerDiskConfigExt } allPages, err := servers.List(client.ServiceClient(), servers.ListOpts{}).AllPages() th.AssertNoErr(t, err) var actual []ServerWithExt err = servers.ExtractServersInto(allPages, &actual) th.AssertNoErr(t, err) th.AssertEquals(t, 3, len(actual)) th.AssertEquals(t, "nova", actual[0].AvailabilityZone) th.AssertEquals(t, "RUNNING", actual[0].PowerState.String()) th.AssertEquals(t, "", actual[0].TaskState) th.AssertEquals(t, "active", actual[0].VmState) th.AssertEquals(t, diskconfig.Manual, actual[0].DiskConfig) } func TestCreateServer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleServerCreationSuccessfully(t, SingleServerBody) actual, err := servers.Create(client.ServiceClient(), servers.CreateOpts{ Name: "derp", ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", FlavorRef: "1", }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ServerDerp, *actual) } func TestCreateServerWithCustomField(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleServerCreationWithCustomFieldSuccessfully(t, SingleServerBody) actual, err := servers.Create(client.ServiceClient(), CreateOptsWithCustomField{ CreateOpts: servers.CreateOpts{ Name: "derp", ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", FlavorRef: "1", }, Foo: "bar", }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ServerDerp, *actual) } func TestCreateServerWithMetadata(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleServerCreationWithMetadata(t, SingleServerBody) actual, err := servers.Create(client.ServiceClient(), servers.CreateOpts{ Name: "derp", ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", FlavorRef: "1", Metadata: map[string]string{ "abc": "def", }, }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ServerDerp, *actual) } func TestCreateServerWithUserdataString(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleServerCreationWithUserdata(t, SingleServerBody) actual, err := servers.Create(client.ServiceClient(), servers.CreateOpts{ Name: "derp", ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", FlavorRef: "1", UserData: []byte("userdata string"), }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ServerDerp, *actual) } func TestCreateServerWithUserdataEncoded(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleServerCreationWithUserdata(t, SingleServerBody) encoded := base64.StdEncoding.EncodeToString([]byte("userdata string")) actual, err := servers.Create(client.ServiceClient(), servers.CreateOpts{ Name: "derp", ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", FlavorRef: "1", UserData: []byte(encoded), }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ServerDerp, *actual) } func TestCreateServerWithImageNameAndFlavorName(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleServerCreationSuccessfully(t, SingleServerBody) actual, err := servers.Create(client.ServiceClient(), servers.CreateOpts{ Name: "derp", ImageName: "cirros-0.3.2-x86_64-disk", FlavorName: "m1.tiny", ServiceClient: client.ServiceClient(), }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ServerDerp, *actual) } func TestDeleteServer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleServerDeletionSuccessfully(t) res := servers.Delete(client.ServiceClient(), "asdfasdfasdf") th.AssertNoErr(t, res.Err) } func TestForceDeleteServer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleServerForceDeletionSuccessfully(t) res := servers.ForceDelete(client.ServiceClient(), "asdfasdfasdf") th.AssertNoErr(t, res.Err) } func TestGetServer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleServerGetSuccessfully(t) client := client.ServiceClient() actual, err := servers.Get(client, "1234asdf").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, ServerDerp, *actual) } func TestGetFaultyServer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleServerGetFaultSuccessfully(t) client := client.ServiceClient() actual, err := servers.Get(client, "1234asdf").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } FaultyServer := ServerDerp FaultyServer.Fault = DerpFault th.CheckDeepEquals(t, FaultyServer, *actual) } func TestGetServerWithExtensions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleServerGetSuccessfully(t) var s struct { servers.Server availabilityzones.ServerAvailabilityZoneExt extendedstatus.ServerExtendedStatusExt diskconfig.ServerDiskConfigExt } err := servers.Get(client.ServiceClient(), "1234asdf").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "nova", s.AvailabilityZone) th.AssertEquals(t, "RUNNING", s.PowerState.String()) th.AssertEquals(t, "", s.TaskState) th.AssertEquals(t, "active", s.VmState) th.AssertEquals(t, diskconfig.Manual, s.DiskConfig) err = servers.Get(client.ServiceClient(), "1234asdf").ExtractInto(s) if err == nil { t.Errorf("Expected error when providing non-pointer struct") } } func TestUpdateServer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleServerUpdateSuccessfully(t) client := client.ServiceClient() actual, err := servers.Update(client, "1234asdf", servers.UpdateOpts{Name: "new-name"}).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, ServerDerp, *actual) } func TestChangeServerAdminPassword(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAdminPasswordChangeSuccessfully(t) res := servers.ChangeAdminPassword(client.ServiceClient(), "1234asdf", "new-password") th.AssertNoErr(t, res.Err) } func TestShowConsoleOutput(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleShowConsoleOutputSuccessfully(t, ConsoleOutputBody) outputOpts := &servers.ShowConsoleOutputOpts{ Length: 50, } actual, err := servers.ShowConsoleOutput(client.ServiceClient(), "1234asdf", outputOpts).Extract() th.AssertNoErr(t, err) th.AssertByteArrayEquals(t, []byte(ConsoleOutput), []byte(actual)) } func TestGetPassword(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePasswordGetSuccessfully(t) res := servers.GetPassword(client.ServiceClient(), "1234asdf") th.AssertNoErr(t, res.Err) } func TestRebootServer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleRebootSuccessfully(t) res := servers.Reboot(client.ServiceClient(), "1234asdf", servers.RebootOpts{ Type: servers.SoftReboot, }) th.AssertNoErr(t, res.Err) } func TestRebuildServer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleRebuildSuccessfully(t, SingleServerBody) opts := servers.RebuildOpts{ Name: "new-name", AdminPass: "swordfish", ImageID: "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb", AccessIPv4: "1.2.3.4", } actual, err := servers.Rebuild(client.ServiceClient(), "1234asdf", opts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ServerDerp, *actual) } func TestResizeServer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "resize": { "flavorRef": "2" } }`) w.WriteHeader(http.StatusAccepted) }) res := servers.Resize(client.ServiceClient(), "1234asdf", servers.ResizeOpts{FlavorRef: "2"}) th.AssertNoErr(t, res.Err) } func TestConfirmResize(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "confirmResize": null }`) w.WriteHeader(http.StatusNoContent) }) res := servers.ConfirmResize(client.ServiceClient(), "1234asdf") th.AssertNoErr(t, res.Err) } func TestRevertResize(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "revertResize": null }`) w.WriteHeader(http.StatusAccepted) }) res := servers.RevertResize(client.ServiceClient(), "1234asdf") th.AssertNoErr(t, res.Err) } func TestGetMetadatum(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMetadatumGetSuccessfully(t) expected := map[string]string{"foo": "bar"} actual, err := servers.Metadatum(client.ServiceClient(), "1234asdf", "foo").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, actual) } func TestCreateMetadatum(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMetadatumCreateSuccessfully(t) expected := map[string]string{"foo": "bar"} actual, err := servers.CreateMetadatum(client.ServiceClient(), "1234asdf", servers.MetadatumOpts{"foo": "bar"}).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, actual) } func TestDeleteMetadatum(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMetadatumDeleteSuccessfully(t) err := servers.DeleteMetadatum(client.ServiceClient(), "1234asdf", "foo").ExtractErr() th.AssertNoErr(t, err) } func TestGetMetadata(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMetadataGetSuccessfully(t) expected := map[string]string{"foo": "bar", "this": "that"} actual, err := servers.Metadata(client.ServiceClient(), "1234asdf").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, actual) } func TestResetMetadata(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMetadataResetSuccessfully(t) expected := map[string]string{"foo": "bar", "this": "that"} actual, err := servers.ResetMetadata(client.ServiceClient(), "1234asdf", servers.MetadataOpts{ "foo": "bar", "this": "that", }).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, actual) } func TestUpdateMetadata(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMetadataUpdateSuccessfully(t) expected := map[string]string{"foo": "baz", "this": "those"} actual, err := servers.UpdateMetadata(client.ServiceClient(), "1234asdf", servers.MetadataOpts{ "foo": "baz", "this": "those", }).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, actual) } func TestListAddresses(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAddressListSuccessfully(t) expected := ListAddressesExpected pages := 0 err := servers.ListAddresses(client.ServiceClient(), "asdfasdfasdf").EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := servers.ExtractAddresses(page) th.AssertNoErr(t, err) if len(actual) != 2 { t.Fatalf("Expected 2 networks, got %d", len(actual)) } th.CheckDeepEquals(t, expected, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, 1, pages) } func TestListAddressesByNetwork(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleNetworkAddressListSuccessfully(t) expected := ListNetworkAddressesExpected pages := 0 err := servers.ListAddressesByNetwork(client.ServiceClient(), "asdfasdfasdf", "public").EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := servers.ExtractNetworkAddresses(page) th.AssertNoErr(t, err) if len(actual) != 2 { t.Fatalf("Expected 2 addresses, got %d", len(actual)) } th.CheckDeepEquals(t, expected, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, 1, pages) } func TestCreateServerImage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateServerImageSuccessfully(t) _, err := servers.CreateImage(client.ServiceClient(), "serverimage", servers.CreateImageOpts{Name: "test"}).ExtractImageID() th.AssertNoErr(t, err) } func TestMarshalPersonality(t *testing.T) { name := "/etc/test" contents := []byte("asdfasdf") personality := servers.Personality{ &servers.File{ Path: name, Contents: contents, }, } data, err := json.Marshal(personality) if err != nil { t.Fatal(err) } var actual []map[string]string err = json.Unmarshal(data, &actual) if err != nil { t.Fatal(err) } if len(actual) != 1 { t.Fatal("expected personality length 1") } if actual[0]["path"] != name { t.Fatal("file path incorrect") } if actual[0]["contents"] != base64.StdEncoding.EncodeToString(contents) { t.Fatal("file contents incorrect") } } results_test.go000066400000000000000000000114751335005366400357340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/servers/testingpackage testing import ( "crypto/rsa" "encoding/json" "fmt" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" "golang.org/x/crypto/ssh" ) // Fail - No password in JSON. func TestExtractPassword_no_pwd_data(t *testing.T) { var dejson interface{} err := json.Unmarshal([]byte(`{ "Crappy data": ".-.-." }`), &dejson) if err != nil { t.Fatalf("%s", err) } resp := servers.GetPasswordResult{Result: gophercloud.Result{Body: dejson}} pwd, err := resp.ExtractPassword(nil) th.AssertEquals(t, pwd, "") } // Ok - return encrypted password when no private key is given. func TestExtractPassword_encrypted_pwd(t *testing.T) { var dejson interface{} sejson := []byte(`{"password":"PP8EnwPO9DhEc8+O/6CKAkPF379mKsUsfFY6yyw0734XXvKsSdV9KbiHQ2hrBvzeZxtGMrlFaikVunCRizyLLWLMuOi4hoH+qy9F9sQid61gQIGkxwDAt85d/7Eau2/KzorFnZhgxArl7IiqJ67X6xjKkR3zur+Yp3V/mtVIehpPYIaAvPbcp2t4mQXl1I9J8yrQfEZOctLL1L4heDEVXnxvNihVLK6pivlVggp6SZCtjj9cduZGrYGsxsOCso1dqJQr7GCojfwvuLOoG0OYwEGuWVTZppxWxi/q1QgeHFhGKA5QUXlz7pS71oqpjYsTeViuHnfvlqb5TVYZpQ1haw=="}`) err := json.Unmarshal(sejson, &dejson) fmt.Printf("%v\n", dejson) if err != nil { t.Fatalf("%s", err) } resp := servers.GetPasswordResult{Result: gophercloud.Result{Body: dejson}} pwd, err := resp.ExtractPassword(nil) th.AssertNoErr(t, err) th.AssertEquals(t, "PP8EnwPO9DhEc8+O/6CKAkPF379mKsUsfFY6yyw0734XXvKsSdV9KbiHQ2hrBvzeZxtGMrlFaikVunCRizyLLWLMuOi4hoH+qy9F9sQid61gQIGkxwDAt85d/7Eau2/KzorFnZhgxArl7IiqJ67X6xjKkR3zur+Yp3V/mtVIehpPYIaAvPbcp2t4mQXl1I9J8yrQfEZOctLL1L4heDEVXnxvNihVLK6pivlVggp6SZCtjj9cduZGrYGsxsOCso1dqJQr7GCojfwvuLOoG0OYwEGuWVTZppxWxi/q1QgeHFhGKA5QUXlz7pS71oqpjYsTeViuHnfvlqb5TVYZpQ1haw==", pwd) } // Ok - return decrypted password when private key is given. // Decrytion can be verified by: // echo "" | base64 -D | openssl rsautl -decrypt -inkey func TestExtractPassword_decrypted_pwd(t *testing.T) { privateKey, err := ssh.ParseRawPrivateKey([]byte(` -----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAo1ODZgwMVdTJYim9UYuYhowoPMhGEuV5IRZjcJ315r7RBSC+ yEiBb1V+jhf+P8fzAyU35lkBzZGDr7E3jxSesbOuYT8cItQS4ErUnI1LGuqvMxwv X3GMyE/HmOcaiODF1XZN3Ur5pMJdVknnmczgUsW0hT98Udrh3MQn9WSuh/6LRy6+ x1QsKHOCLFPnkhWa3LKyxmpQq/Gvhz+6NLe+gt8MFullA5mKQxBJ/K6laVHeaMlw JG3GCX0EZhRlvzoV8koIBKZtbKFolFr8ZtxBm3R5LvnyrtOvp22sa+xeItUT5kG1 ZnbGNdK87oYW+VigEUfzT/+8R1i6E2QIXoeZiQIDAQABAoIBAQCVZ70IqbbTAW8j RAlyQh/J3Qal65LmkFJJKUDX8TfT1/Q/G6BKeMEmxm+Zrmsfj1pHI1HKftt+YEG1 g4jOc09kQXkgbmnfll6aHPn3J+1vdwXD3GGdjrL5PrnYrngAhJWU2r8J0x8hT8ew OrUJZXhDX6XuSpAAFRmOKUZgXbSmo4X+LZX76ACnarselJt5FL724ECvpWJ7xxC4 FMzvp4RqMmNFvv/Uq9lE/EmoSk4dviYyIZZ16DbDNyc9k/sGqCAMktCEwZ3EQm// S5bkNhgP6oUXjluWy53aPRgykEylgDWo5SSdSEyKnw/fciU0xdprA9JrBGIcTyHS /k2kgD4xAoGBANTkJ88Q0YrxX3fZNZVqcn00XKTxPGmxN5LRs7eV743q30AxK5Db QU8iwaAA1IKUWV5DLhgUTNsDCOPUPue4aOSBD3/sj+WEmvIhj7afDL5didkYHsqf fDnhFHq7y/3i57d428C7BwwR79pGWVyi7vH3pfu9A1iwl1aNOae+zvbVAoGBAMRm AmwQ9fJ3Qc44jysFK/yliLRGdShjkMMah5G3JlrelwfPtwPwEL2EHHhJB/C1acMs n6Q6RaoF6WNSZUY65ksQg7aPOYf2X0FTFwQJvwDJ4qlWjmq7w+tQ0AoGJG+dVUmQ zHZ/Y+HokSXzz9c4oevk4v/rMgAQ00WHrTdtIhnlAoGBALIJJ72D7CkNGHCq5qPQ xHQukPejgolFGhufYXM7YX3GmPMe67cVlTVv9Isxhoa5N0+cUPT0LR3PGOUm/4Bb eOT3hZXOqLwhvE6XgI8Rzd95bClwgXekDoh80dqeKMdmta961BQGlKskaPiacmsF G1yhZV70P9Mwwy8vpbLB4GUNAoGAbTwbjsWkNfa0qCF3J8NZoszjCvnBQfSW2J1R 1+8ZKyNwt0yFi3Ajr3TibNiZzPzp1T9lj29FvfpJxA9Y+sXZvthxmcFxizix5GB1 ha5yCNtA8VSOI7lJkAFDpL+j1lyYyjD6N9JE2KqEyKoh6J+8F7sXsqW7CqRRDfQX mKNfey0CgYEAxcEoNoADN2hRl7qY9rbQfVvQb3RkoQkdHhl9gpLFCcV32IP8R4xg 09NbQK5OmgcIuZhLVNzTmUHJbabEGeXqIFIV0DsqECAt3WzbDyKQO23VJysFD46c KSde3I0ybDz7iS2EtceKB7m4C0slYd+oBkm4efuF00rCOKDwpFq45m0= -----END RSA PRIVATE KEY----- `)) if err != nil { t.Fatalf("Error parsing private key: %s\n", err) } var dejson interface{} sejson := []byte(`{"password":"PP8EnwPO9DhEc8+O/6CKAkPF379mKsUsfFY6yyw0734XXvKsSdV9KbiHQ2hrBvzeZxtGMrlFaikVunCRizyLLWLMuOi4hoH+qy9F9sQid61gQIGkxwDAt85d/7Eau2/KzorFnZhgxArl7IiqJ67X6xjKkR3zur+Yp3V/mtVIehpPYIaAvPbcp2t4mQXl1I9J8yrQfEZOctLL1L4heDEVXnxvNihVLK6pivlVggp6SZCtjj9cduZGrYGsxsOCso1dqJQr7GCojfwvuLOoG0OYwEGuWVTZppxWxi/q1QgeHFhGKA5QUXlz7pS71oqpjYsTeViuHnfvlqb5TVYZpQ1haw=="}`) err = json.Unmarshal(sejson, &dejson) fmt.Printf("%v\n", dejson) if err != nil { t.Fatalf("%s", err) } resp := servers.GetPasswordResult{Result: gophercloud.Result{Body: dejson}} pwd, err := resp.ExtractPassword(privateKey.(*rsa.PrivateKey)) th.AssertNoErr(t, err) th.AssertEquals(t, "ruZKK0tqxRfYm5t7lSJq", pwd) } func TestListAddressesAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAddressListSuccessfully(t) allPages, err := servers.ListAddresses(client.ServiceClient(), "asdfasdfasdf").AllPages() th.AssertNoErr(t, err) _, err = servers.ExtractAddresses(allPages) th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/servers/urls.go000066400000000000000000000027201335005366400325540ustar00rootroot00000000000000package servers import "github.com/gophercloud/gophercloud" func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("servers") } func listURL(client *gophercloud.ServiceClient) string { return createURL(client) } func listDetailURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("servers", "detail") } func deleteURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id) } func getURL(client *gophercloud.ServiceClient, id string) string { return deleteURL(client, id) } func updateURL(client *gophercloud.ServiceClient, id string) string { return deleteURL(client, id) } func actionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id, "action") } func metadatumURL(client *gophercloud.ServiceClient, id, key string) string { return client.ServiceURL("servers", id, "metadata", key) } func metadataURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id, "metadata") } func listAddressesURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id, "ips") } func listAddressesByNetworkURL(client *gophercloud.ServiceClient, id, network string) string { return client.ServiceURL("servers", id, "ips", network) } func passwordURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id, "os-server-password") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/compute/v2/servers/util.go000066400000000000000000000010371335005366400325440ustar00rootroot00000000000000package servers import "github.com/gophercloud/gophercloud" // WaitForStatus will continually poll a server until it successfully // transitions to a specified status. It will do this for at most the number // of seconds specified. func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { current, err := Get(c, id).Extract() if err != nil { return false, err } if current.Status == status { return true, nil } return false, nil }) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/container/000077500000000000000000000000001335005366400275255ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/container/v1/000077500000000000000000000000001335005366400300535ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/container/v1/capsules/000077500000000000000000000000001335005366400316725ustar00rootroot00000000000000doc.go000066400000000000000000000003271335005366400327110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/container/v1/capsules// Package capsules contains functionality for working with Zun capsule // resources. A capsule is a container group, as the co-located and // co-scheduled unit, is the same like pod in Kubernetes. package capsules errors.go000066400000000000000000000003671335005366400334640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/container/v1/capsulespackage capsules import ( "fmt" "github.com/gophercloud/gophercloud" ) type ErrInvalidDataFormat struct { gophercloud.BaseError } func (e ErrInvalidDataFormat) Error() string { return fmt.Sprintf("Data in neither json nor yaml format.") } requests.go000066400000000000000000000063041335005366400340200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/container/v1/capsulespackage capsules import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder is the interface options structs have to satisfy in order // to be used in the main Create operation in this package. Since many // extensions decorate or modify the common logic, it is useful for them to // satisfy a basic interface in order for them to be used. type CreateOptsBuilder interface { ToCapsuleCreateMap() (map[string]interface{}, error) } // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToCapsuleListQuery() (string, error) } // Get requests details on a single capsule, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, }) return } // CreateOpts is the common options struct used in this package's Create // operation. type CreateOpts struct { // A structure that contains either the template file or url. Call the // associated methods to extract the information relevant to send in a create request. TemplateOpts *Template `json:"-" required:"true"` } // ToCapsuleCreateMap assembles a request body based on the contents of // a CreateOpts. func (opts CreateOpts) ToCapsuleCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } if err := opts.TemplateOpts.Parse(); err != nil { return nil, err } b["template"] = string(opts.TemplateOpts.Bin) return b, nil } // Create implements create capsule request. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToCapsuleCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{202}}) return } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the capsule attributes you want to see returned. Marker and Limit are used // for pagination. type ListOpts struct { Marker string `q:"marker"` Limit int `q:"limit"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` AllProjects bool `q:"all_projects"` } // ToCapsuleListQuery formats a ListOpts into a query string. func (opts ListOpts) ToCapsuleListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List makes a request against the API to list capsules accessible to you. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToCapsuleListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return CapsulePage{pagination.LinkedPageBase{PageResult: r}} }) } // Delete implements Capsule delete request. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } results.go000066400000000000000000000171441335005366400336520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/container/v1/capsulespackage capsules import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a capsule resource. func (r commonResult) Extract() (*Capsule, error) { var s *Capsule err := r.ExtractInto(&s) return s, err } // GetResult represents the result of a get operation. type GetResult struct { commonResult } // CreateResult is the response from a Create operation. Call its Extract // method to interpret it as a Capsule. type CreateResult struct { commonResult } // DeleteResult represents the result of a delete operation. type DeleteResult struct { gophercloud.ErrResult } type CapsulePage struct { pagination.LinkedPageBase } // Represents a Capsule type Capsule struct { // UUID for the capsule UUID string `json:"uuid"` // User ID for the capsule UserID string `json:"user_id"` // Project ID for the capsule ProjectID string `json:"project_id"` // cpu for the capsule CPU float64 `json:"cpu"` // Memory for the capsule Memory string `json:"memory"` // The name of the capsule MetaName string `json:"meta_name"` // Indicates whether capsule is currently operational. Status string `json:"status"` // Indicates whether capsule is currently operational. StatusReason string `json:"status_reason"` // The created time of the capsule. CreatedAt time.Time `json:"-"` // The updated time of the capsule. UpdatedAt time.Time `json:"-"` // Links includes HTTP references to the itself, useful for passing along to // other APIs that might want a capsule reference. Links []interface{} `json:"links"` // The capsule version CapsuleVersion string `json:"capsule_version"` // The capsule restart policy RestartPolicy string `json:"restart_policy"` // The capsule metadata labels MetaLabels map[string]string `json:"meta_labels"` // The list of containers uuids inside capsule. ContainersUUIDs []string `json:"containers_uuids"` // The capsule IP addresses Addresses map[string][]Address `json:"addresses"` // The capsule volume attached information VolumesInfo map[string][]string `json:"volumes_info"` // The container object inside capsule Containers []Container `json:"containers"` // The capsule host Host string `json:"host"` } type Container struct { // The Container IP addresses Addresses map[string][]Address `json:"addresses"` // UUID for the container UUID string `json:"uuid"` // User ID for the container UserID string `json:"user_id"` // Project ID for the container ProjectID string `json:"project_id"` // cpu for the container CPU float64 `json:"cpu"` // Memory for the container Memory string `json:"memory"` // Image for the container Image string `json:"image"` // The container container Labels map[string]string `json:"labels"` // The created time of the container CreatedAt time.Time `json:"-"` // The updated time of the container UpdatedAt time.Time `json:"-"` // The started time of the container StartedAt time.Time `json:"-"` // Name for the container Name string `json:"name"` // Links includes HTTP references to the itself, useful for passing along to // other APIs that might want a capsule reference. Links []interface{} `json:"links"` // auto remove flag token for the container AutoRemove bool `json:"auto_remove"` // Host for the container Host string `json:"host"` // Work directory for the container WorkDir string `json:"workdir"` // Disk for the container Disk int `json:"disk"` // Image pull policy for the container ImagePullPolicy string `json:"image_pull_policy"` // Task state for the container TaskState string `json:"task_state"` // Host name for the container HostName string `json:"hostname"` // Environment for the container Environment map[string]string `json:"environment"` // Status for the container Status string `json:"status"` // Auto Heal flag for the container AutoHeal bool `json:"auto_heal"` // Status details for the container StatusDetail string `json:"status_detail"` // Status reason for the container StatusReason string `json:"status_reason"` // Image driver for the container ImageDriver string `json:"image_driver"` // Command for the container Command []string `json:"command"` // Image for the container Runtime string `json:"runtime"` // Interactive flag for the container Interactive bool `json:"interactive"` // Restart Policy for the container RestartPolicy map[string]string `json:"restart_policy"` // Ports information for the container Ports []int `json:"ports"` // Security groups for the container SecurityGroups []string `json:"security_groups"` } type Address struct { PreserveOnDelete bool `json:"preserve_on_delete"` Addr string `json:"addr"` Port string `json:"port"` Version float64 `json:"version"` SubnetID string `json:"subnet_id"` } // NextPageURL is invoked when a paginated collection of capsules has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r CapsulePage) NextPageURL() (string, error) { var s struct { Next string `json:"next"` } err := r.ExtractInto(&s) if err != nil { return "", err } return s.Next, nil } // IsEmpty checks whether a CapsulePage struct is empty. func (r CapsulePage) IsEmpty() (bool, error) { is, err := ExtractCapsules(r) return len(is) == 0, err } // ExtractCapsules accepts a Page struct, specifically a CapsulePage struct, // and extracts the elements into a slice of Capsule structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractCapsules(r pagination.Page) ([]Capsule, error) { var s struct { Capsules []Capsule `json:"capsules"` } err := (r.(CapsulePage)).ExtractInto(&s) return s.Capsules, err } func (r *Capsule) UnmarshalJSON(b []byte) error { type tmp Capsule // Support for "older" zun time formats. var s1 struct { tmp CreatedAt gophercloud.JSONRFC3339ZNoT `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339ZNoT `json:"updated_at"` } err := json.Unmarshal(b, &s1) if err == nil { *r = Capsule(s1.tmp) r.CreatedAt = time.Time(s1.CreatedAt) r.UpdatedAt = time.Time(s1.UpdatedAt) return nil } // Support for "new" zun time formats. var s2 struct { tmp CreatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"updated_at"` } err = json.Unmarshal(b, &s2) if err != nil { return err } *r = Capsule(s2.tmp) r.CreatedAt = time.Time(s2.CreatedAt) r.UpdatedAt = time.Time(s2.UpdatedAt) return nil } func (r *Container) UnmarshalJSON(b []byte) error { type tmp Container // Support for "older" zun time formats. var s1 struct { tmp CreatedAt gophercloud.JSONRFC3339ZNoT `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339ZNoT `json:"updated_at"` StartedAt gophercloud.JSONRFC3339ZNoT `json:"started_at"` } err := json.Unmarshal(b, &s1) if err == nil { *r = Container(s1.tmp) r.CreatedAt = time.Time(s1.CreatedAt) r.UpdatedAt = time.Time(s1.UpdatedAt) r.StartedAt = time.Time(s1.StartedAt) return nil } // Support for "new" zun time formats. var s2 struct { tmp CreatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"updated_at"` StartedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"started_at"` } err = json.Unmarshal(b, &s2) if err != nil { return err } *r = Container(s2.tmp) r.CreatedAt = time.Time(s2.CreatedAt) r.UpdatedAt = time.Time(s2.UpdatedAt) r.StartedAt = time.Time(s2.StartedAt) return nil } template.go000066400000000000000000000013441335005366400337570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/container/v1/capsulespackage capsules import ( "encoding/json" "gopkg.in/yaml.v2" ) // Template is a structure that represents OpenStack Zun Capsule templates type Template struct { // Bin stores the contents of the template or environment. Bin []byte // Parsed contains a parsed version of Bin. Since there are 2 different // fields referring to the same value, you must be careful when accessing // this filed. Parsed map[string]interface{} } // Parse will parse the contents and then validate. The contents MUST be either JSON or YAML. func (t *Template) Parse() error { if jerr := json.Unmarshal(t.Bin, &t.Parsed); jerr != nil { if yerr := yaml.Unmarshal(t.Bin, &t.Parsed); yerr != nil { return ErrInvalidDataFormat{} } } return nil } testing/000077500000000000000000000000001335005366400332705ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/container/v1/capsulesdoc.go000066400000000000000000000000201335005366400343540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/container/v1/capsules/testingpackage testing fixtures.go000066400000000000000000000362031335005366400354740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/container/v1/capsules/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/container/v1/capsules" th "github.com/gophercloud/gophercloud/testhelper" fakeclient "github.com/gophercloud/gophercloud/testhelper/client" ) // ValidJSONTemplate is a valid OpenStack Capsule template in JSON format const ValidJSONTemplate = ` { "capsuleVersion": "beta", "kind": "capsule", "metadata": { "labels": { "app": "web", "app1": "web1" }, "name": "template" }, "spec": { "restartPolicy": "Always", "containers": [ { "command": [ "/bin/bash" ], "env": { "ENV1": "/usr/local/bin", "ENV2": "/usr/bin" }, "image": "ubuntu", "imagePullPolicy": "ifnotpresent", "ports": [ { "containerPort": 80, "hostPort": 80, "name": "nginx-port", "protocol": "TCP" } ], "resources": { "requests": { "cpu": 1, "memory": 1024 } }, "workDir": "/root" } ] } } ` // ValidYAMLTemplate is a valid OpenStack Capsule template in YAML format const ValidYAMLTemplate = ` capsuleVersion: beta kind: capsule metadata: name: template labels: app: web app1: web1 spec: restartPolicy: Always containers: - image: ubuntu command: - "/bin/bash" imagePullPolicy: ifnotpresent workDir: /root ports: - name: nginx-port containerPort: 80 hostPort: 80 protocol: TCP resources: requests: cpu: 1 memory: 1024 env: ENV1: /usr/local/bin ENV2: /usr/bin ` // ValidJSONTemplateParsed is the expected parsed version of ValidJSONTemplate var ValidJSONTemplateParsed = map[string]interface{}{ "capsuleVersion": "beta", "kind": "capsule", "metadata": map[string]interface{}{ "name": "template", "labels": map[string]string{ "app": "web", "app1": "web1", }, }, "spec": map[string]interface{}{ "restartPolicy": "Always", "containers": []map[string]interface{}{ map[string]interface{}{ "image": "ubuntu", "command": []interface{}{ "/bin/bash", }, "imagePullPolicy": "ifnotpresent", "workDir": "/root", "ports": []interface{}{ map[string]interface{}{ "name": "nginx-port", "containerPort": float64(80), "hostPort": float64(80), "protocol": "TCP", }, }, "resources": map[string]interface{}{ "requests": map[string]interface{}{ "cpu": float64(1), "memory": float64(1024), }, }, "env": map[string]interface{}{ "ENV1": "/usr/local/bin", "ENV2": "/usr/bin", }, }, }, }, } // ValidYAMLTemplateParsed is the expected parsed version of ValidYAMLTemplate var ValidYAMLTemplateParsed = map[string]interface{}{ "capsuleVersion": "beta", "kind": "capsule", "metadata": map[string]interface{}{ "name": "template", "labels": map[string]string{ "app": "web", "app1": "web1", }, }, "spec": map[interface{}]interface{}{ "restartPolicy": "Always", "containers": []map[interface{}]interface{}{ map[interface{}]interface{}{ "image": "ubuntu", "command": []interface{}{ "/bin/bash", }, "imagePullPolicy": "ifnotpresent", "workDir": "/root", "ports": []interface{}{ map[interface{}]interface{}{ "name": "nginx-port", "containerPort": 80, "hostPort": 80, "protocol": "TCP", }, }, "resources": map[interface{}]interface{}{ "requests": map[interface{}]interface{}{ "cpu": 1, "memory": 1024, }, }, "env": map[interface{}]interface{}{ "ENV1": "/usr/local/bin", "ENV2": "/usr/bin", }, }, }, }, } const CapsuleGetBody_OldTime = ` { "uuid": "cc654059-1a77-47a3-bfcf-715bde5aad9e", "status": "Running", "user_id": "d33b18c384574fd2a3299447aac285f0", "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", "cpu": 1, "memory": "1024M", "meta_name": "test", "meta_labels": {"web": "app"}, "created_at": "2018-01-12 09:37:25+00:00", "updated_at": "2018-01-12 09:37:26+00:00", "links": [ { "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", "rel": "self" }, { "href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", "rel": "bookmark" } ], "capsule_version": "beta", "restart_policy": "always", "containers_uuids": ["1739e28a-d391-4fd9-93a5-3ba3f29a4c9b"], "addresses": { "b1295212-64e1-471d-aa01-25ff46f9818d": [ { "version": 4, "preserve_on_delete": false, "addr": "172.24.4.11", "port": "8439060f-381a-4386-a518-33d5a4058636", "subnet_id": "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a" } ] }, "volumes_info": { "67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": [ "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b" ] }, "host": "test-host", "status_reason": "No reason", "containers": [ { "addresses": { "b1295212-64e1-471d-aa01-25ff46f9818d": [ { "version": 4, "preserve_on_delete": false, "addr": "172.24.4.11", "port": "8439060f-381a-4386-a518-33d5a4058636", "subnet_id": "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a" } ] }, "image": "test", "labels": {"foo": "bar"}, "created_at": "2018-01-12 09:37:25+00:00", "updated_at": "2018-01-12 09:37:26+00:00", "started_at": "2018-01-12 09:37:26+00:00", "workdir": "/root", "disk": 0, "security_groups": ["default"], "image_pull_policy": "ifnotpresent", "task_state": "Creating", "user_id": "d33b18c384574fd2a3299447aac285f0", "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", "uuid": "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", "hostname": "test-hostname", "environment": {"USER1": "test"}, "memory": "1024M", "status": "Running", "auto_remove": false, "auto_heal": false, "host": "test-host", "image_driver": "docker", "status_detail": "Just created", "status_reason": "No reason", "name": "test-demo-omicron-13", "restart_policy": { "MaximumRetryCount": "0", "Name": "always" }, "ports": [80], "command": ["testcmd"], "runtime": "runc", "cpu": 1, "interactive": true } ] }` const CapsuleGetBody_NewTime = ` { "uuid": "cc654059-1a77-47a3-bfcf-715bde5aad9e", "status": "Running", "user_id": "d33b18c384574fd2a3299447aac285f0", "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", "cpu": 1, "memory": "1024M", "meta_name": "test", "meta_labels": {"web": "app"}, "created_at": "2018-01-12 09:37:25", "updated_at": "2018-01-12 09:37:26", "links": [ { "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", "rel": "self" }, { "href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", "rel": "bookmark" } ], "capsule_version": "beta", "restart_policy": "always", "containers_uuids": ["1739e28a-d391-4fd9-93a5-3ba3f29a4c9b"], "addresses": { "b1295212-64e1-471d-aa01-25ff46f9818d": [ { "version": 4, "preserve_on_delete": false, "addr": "172.24.4.11", "port": "8439060f-381a-4386-a518-33d5a4058636", "subnet_id": "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a" } ] }, "volumes_info": { "67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": [ "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b" ] }, "host": "test-host", "status_reason": "No reason", "containers": [ { "addresses": { "b1295212-64e1-471d-aa01-25ff46f9818d": [ { "version": 4, "preserve_on_delete": false, "addr": "172.24.4.11", "port": "8439060f-381a-4386-a518-33d5a4058636", "subnet_id": "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a" } ] }, "image": "test", "labels": {"foo": "bar"}, "created_at": "2018-01-12 09:37:25", "updated_at": "2018-01-12 09:37:26", "started_at": "2018-01-12 09:37:26", "workdir": "/root", "disk": 0, "security_groups": ["default"], "image_pull_policy": "ifnotpresent", "task_state": "Creating", "user_id": "d33b18c384574fd2a3299447aac285f0", "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", "uuid": "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", "hostname": "test-hostname", "environment": {"USER1": "test"}, "memory": "1024M", "status": "Running", "auto_remove": false, "auto_heal": false, "host": "test-host", "image_driver": "docker", "status_detail": "Just created", "status_reason": "No reason", "name": "test-demo-omicron-13", "restart_policy": { "MaximumRetryCount": "0", "Name": "always" }, "ports": [80], "command": ["testcmd"], "runtime": "runc", "cpu": 1, "interactive": true } ] }` const CapsuleListBody = ` { "capsules": [ { "uuid": "cc654059-1a77-47a3-bfcf-715bde5aad9e", "status": "Running", "user_id": "d33b18c384574fd2a3299447aac285f0", "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", "cpu": 1, "memory": "1024M", "meta_name": "test", "meta_labels": {"web": "app"}, "created_at": "2018-01-12 09:37:25+00:00", "updated_at": "2018-01-12 09:37:25+01:00", "links": [ { "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", "rel": "self" }, { "href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", "rel": "bookmark" } ], "capsule_version": "beta", "restart_policy": "always", "containers_uuids": ["1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", "d1469e8d-bcbc-43fc-b163-8b9b6a740930"], "addresses": { "b1295212-64e1-471d-aa01-25ff46f9818d": [ { "version": 4, "preserve_on_delete": false, "addr": "172.24.4.11", "port": "8439060f-381a-4386-a518-33d5a4058636", "subnet_id": "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a" } ] }, "volumes_info": { "67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": [ "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b" ] }, "host": "test-host", "status_reason": "No reason" } ] }` var ExpectedContainer1 = capsules.Container{ Name: "test-demo-omicron-13", UUID: "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", UserID: "d33b18c384574fd2a3299447aac285f0", ProjectID: "6b8ffef2a0ac42ee87887b9cc98bdf68", CPU: float64(1), Memory: "1024M", Host: "test-host", Status: "Running", Image: "test", Labels: map[string]string{ "foo": "bar", }, WorkDir: "/root", Disk: 0, Command: []string{ "testcmd", }, Ports: []int{ 80, }, SecurityGroups: []string{ "default", }, ImagePullPolicy: "ifnotpresent", Runtime: "runc", TaskState: "Creating", HostName: "test-hostname", Environment: map[string]string{ "USER1": "test", }, StatusReason: "No reason", StatusDetail: "Just created", ImageDriver: "docker", Interactive: true, AutoRemove: false, AutoHeal: false, RestartPolicy: map[string]string{ "MaximumRetryCount": "0", "Name": "always", }, Addresses: map[string][]capsules.Address{ "b1295212-64e1-471d-aa01-25ff46f9818d": []capsules.Address{ { PreserveOnDelete: false, Addr: "172.24.4.11", Port: "8439060f-381a-4386-a518-33d5a4058636", Version: float64(4), SubnetID: "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a", }, }, }, } var ExpectedCapsule = capsules.Capsule{ UUID: "cc654059-1a77-47a3-bfcf-715bde5aad9e", Status: "Running", UserID: "d33b18c384574fd2a3299447aac285f0", ProjectID: "6b8ffef2a0ac42ee87887b9cc98bdf68", CPU: float64(1), Memory: "1024M", MetaName: "test", Links: []interface{}{ map[string]interface{}{ "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", "rel": "self", }, map[string]interface{}{ "href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", "rel": "bookmark", }, }, CapsuleVersion: "beta", RestartPolicy: "always", MetaLabels: map[string]string{ "web": "app", }, ContainersUUIDs: []string{ "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", }, Addresses: map[string][]capsules.Address{ "b1295212-64e1-471d-aa01-25ff46f9818d": []capsules.Address{ { PreserveOnDelete: false, Addr: "172.24.4.11", Port: "8439060f-381a-4386-a518-33d5a4058636", Version: float64(4), SubnetID: "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a", }, }, }, VolumesInfo: map[string][]string{ "67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": []string{ "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", }, }, Host: "test-host", StatusReason: "No reason", Containers: []capsules.Container{ ExpectedContainer1, }, } // HandleCapsuleGetOldTimeSuccessfully test setup func HandleCapsuleGetOldTimeSuccessfully(t *testing.T) { th.Mux.HandleFunc("/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, CapsuleGetBody_OldTime) }) } // HandleCapsuleGetNewTimeSuccessfully test setup func HandleCapsuleGetNewTimeSuccessfully(t *testing.T) { th.Mux.HandleFunc("/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, CapsuleGetBody_NewTime) }) } // HandleCapsuleCreateSuccessfully creates an HTTP handler at `/capsules` on the test handler mux // that responds with a `Create` response. func HandleCapsuleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/capsules", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, CapsuleGetBody_NewTime) }) } // HandleCapsuleListSuccessfully test setup func HandleCapsuleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/capsules/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, CapsuleListBody) }) } func HandleCapsuleDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc("/capsules/963a239d-3946-452b-be5a-055eab65a421", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000066071335005366400365420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/container/v1/capsules/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/container/v1/capsules" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fakeclient "github.com/gophercloud/gophercloud/testhelper/client" ) func TestGetCapsule_OldTime(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCapsuleGetOldTimeSuccessfully(t) createdAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:25+00:00") updatedAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:26+00:00") startedAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:26+00:00") ExpectedCapsule.CreatedAt = createdAt ExpectedCapsule.UpdatedAt = updatedAt ExpectedCapsule.Containers[0].CreatedAt = createdAt ExpectedCapsule.Containers[0].UpdatedAt = updatedAt ExpectedCapsule.Containers[0].StartedAt = startedAt actualCapsule, err := capsules.Get(fakeclient.ServiceClient(), ExpectedCapsule.UUID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &ExpectedCapsule, actualCapsule) } func TestGetCapsule_NewTime(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCapsuleGetNewTimeSuccessfully(t) createdAt, _ := time.Parse(gophercloud.RFC3339ZNoTNoZ, "2018-01-12 09:37:25") updatedAt, _ := time.Parse(gophercloud.RFC3339ZNoTNoZ, "2018-01-12 09:37:26") startedAt, _ := time.Parse(gophercloud.RFC3339ZNoTNoZ, "2018-01-12 09:37:26") ExpectedCapsule.CreatedAt = createdAt ExpectedCapsule.UpdatedAt = updatedAt ExpectedCapsule.Containers[0].CreatedAt = createdAt ExpectedCapsule.Containers[0].UpdatedAt = updatedAt ExpectedCapsule.Containers[0].StartedAt = startedAt actualCapsule, err := capsules.Get(fakeclient.ServiceClient(), ExpectedCapsule.UUID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &ExpectedCapsule, actualCapsule) } func TestCreateCapsule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCapsuleCreateSuccessfully(t) template := new(capsules.Template) template.Bin = []byte(ValidJSONTemplate) createOpts := capsules.CreateOpts{ TemplateOpts: template, } actualCapsule, err := capsules.Create(fakeclient.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &ExpectedCapsule, actualCapsule) } func TestListCapsule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCapsuleListSuccessfully(t) createdAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:25+00:00") updatedAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:25+01:00") ExpectedCapsule.CreatedAt = createdAt ExpectedCapsule.UpdatedAt = updatedAt ExpectedCapsule.Containers = nil expected := []capsules.Capsule{ExpectedCapsule} count := 0 results := capsules.List(fakeclient.ServiceClient(), nil) err := results.EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := capsules.ExtractCapsules(page) if err != nil { t.Errorf("Failed to extract capsules: %v", err) return false, err } th.CheckDeepEquals(t, expected, actual) return true, nil }) th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCapsuleDeleteSuccessfully(t) res := capsules.Delete(fakeclient.ServiceClient(), "963a239d-3946-452b-be5a-055eab65a421") th.AssertNoErr(t, res.Err) } template_test.go000066400000000000000000000014771335005366400365020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/container/v1/capsules/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/container/v1/capsules" th "github.com/gophercloud/gophercloud/testhelper" ) func TestTemplateParsing(t *testing.T) { templateJSON := new(capsules.Template) templateJSON.Bin = []byte(ValidJSONTemplate) err := templateJSON.Parse() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ValidJSONTemplateParsed, templateJSON.Parsed) templateYAML := new(capsules.Template) templateYAML.Bin = []byte(ValidYAMLTemplate) err = templateYAML.Parse() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ValidYAMLTemplateParsed, templateYAML.Parsed) templateInvalid := new(capsules.Template) templateInvalid.Bin = []byte("Keep Austin Weird") err = templateInvalid.Parse() if err == nil { t.Error("Template parsing did not catch invalid template") } } urls.go000066400000000000000000000011361335005366400331300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/container/v1/capsulespackage capsules import "github.com/gophercloud/gophercloud" func getURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("capsules", id) } func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("capsules") } // `listURL` is a pure function. `listURL(c)` is a URL for which a GET // request will respond with a list of capsules in the service `c`. func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("capsules") } func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("capsules", id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/000077500000000000000000000000001335005366400305455ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/000077500000000000000000000000001335005366400310735ustar00rootroot00000000000000certificates/000077500000000000000000000000001335005366400334615ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1doc.go000066400000000000000000000016131335005366400345560ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/certificates// Package certificates contains functionality for working with Magnum Certificate // resources. /* Package certificates provides information and interaction with the certificates through the OpenStack Container Infra service. Example to get certificates certificate, err := certificates.Get(serviceClient, "d564b18a-2890-4152-be3d-e05d784ff72").Extract() if err != nil { panic(err) } Example to create certificates opts := certificates.CreateOpts{ BayUUID: "d564b18a-2890-4152-be3d-e05d784ff727", CSR: "-----BEGIN CERTIFICATE REQUEST-----\nMIIEfzCCAmcCAQAwFDESMBAGA1UEAxMJWW91ciBOYW1lMIICIjANBgkqhkiG9w0B\n-----END CERTIFICATE REQUEST-----\n", } response, err := certificates.Create(sc, opts).Extract() if err != nil { panic(err) } Example to update certificates err := certificates.Update(client, clusterUUID).ExtractErr() if err != nil { panic(err) } */ package certificates requests.go000066400000000000000000000033131335005366400356630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/certificatespackage certificates import ( "net/http" "github.com/gophercloud/gophercloud" ) // CreateOptsBuilder allows extensions to add additional parameters // to the Create request. type CreateOptsBuilder interface { ToCertificateCreateMap() (map[string]interface{}, error) } // CreateOpts represents options used to create a certificate. type CreateOpts struct { ClusterUUID string `json:"cluster_uuid,omitempty" xor:"BayUUID"` BayUUID string `json:"bay_uuid,omitempty" xor:"ClusterUUID"` CSR string `json:"csr" required:"true"` } // ToCertificateCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToCertificateCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // Get makes a request against the API to get details for a certificate. func Get(client *gophercloud.ServiceClient, clusterID string) (r GetResult) { url := getURL(client, clusterID) _, r.Err = client.Get(url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Create requests the creation of a new certificate. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToCertificateCreateMap() if err != nil { r.Err = err return } var result *http.Response result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) if r.Err == nil { r.Header = result.Header } return } // Update will rotate the CA certificate for a cluster func Update(client *gophercloud.ServiceClient, clusterID string) (r UpdateResult) { _, r.Err = client.Patch(updateURL(client, clusterID), nil, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return } results.go000066400000000000000000000016341335005366400355150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/certificatespackage certificates import ( "github.com/gophercloud/gophercloud" ) type commonResult struct { gophercloud.Result } // GetResult is the response of a Get operations. type GetResult struct { commonResult } // CreateResult is the response of a Create operations. type CreateResult struct { commonResult } // UpdateResult is the response of an Update operations. type UpdateResult struct { gophercloud.ErrResult } // Extract is a function that accepts a result and extracts a certificate resource. func (r commonResult) Extract() (*Certificate, error) { var s *Certificate err := r.ExtractInto(&s) return s, err } // Represents a Certificate type Certificate struct { ClusterUUID string `json:"cluster_uuid"` BayUUID string `json:"bay_uuid"` Links []gophercloud.Link `json:"links"` PEM string `json:"pem"` CSR string `json:"csr"` } testing/000077500000000000000000000000001335005366400351365ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/certificatesdoc.go000066400000000000000000000000201335005366400362220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/certificates/testingpackage testing fixtures.go000066400000000000000000000073571335005366400373520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/certificates/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/certificates" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const CertificateResponse = ` { "cluster_uuid": "d564b18a-2890-4152-be3d-e05d784ff727", "bay_uuid": "d564b18a-2890-4152-be3d-e05d784ff727", "pem": "FAKE_CERTIFICATE", "links": [ { "href": "http://10.63.176.154:9511/v1/certificates/d564b18a-2890-4152-be3d-e05d784ff727", "rel": "self" }, { "href": "http://10.63.176.154:9511/certificates/d564b18a-2890-4152-be3d-e05d784ff727", "rel": "bookmark" } ] }` const CreateCertificateResponse = ` { "cluster_uuid": "d564b18a-2890-4152-be3d-e05d784ff727", "bay_uuid": "d564b18a-2890-4152-be3d-e05d784ff727", "pem": "FAKE_CERTIFICATE_PEM", "csr": "FAKE_CERTIFICATE_CSR", "links": [ { "href": "http://10.63.176.154:9511/v1/certificates/d564b18a-2890-4152-be3d-e05d784ff727", "rel": "self" }, { "href": "http://10.63.176.154:9511/certificates/d564b18a-2890-4152-be3d-e05d784ff727", "rel": "bookmark" } ] }` var ExpectedCertificate = certificates.Certificate{ ClusterUUID: "d564b18a-2890-4152-be3d-e05d784ff727", BayUUID: "d564b18a-2890-4152-be3d-e05d784ff727", PEM: "FAKE_CERTIFICATE", Links: []gophercloud.Link{ {Href: "http://10.63.176.154:9511/v1/certificates/d564b18a-2890-4152-be3d-e05d784ff727", Rel: "self"}, {Href: "http://10.63.176.154:9511/certificates/d564b18a-2890-4152-be3d-e05d784ff727", Rel: "bookmark"}, }, } var ExpectedCreateCertificateResponse = certificates.Certificate{ ClusterUUID: "d564b18a-2890-4152-be3d-e05d784ff727", BayUUID: "d564b18a-2890-4152-be3d-e05d784ff727", PEM: "FAKE_CERTIFICATE_PEM", CSR: "FAKE_CERTIFICATE_CSR", Links: []gophercloud.Link{ {Href: "http://10.63.176.154:9511/v1/certificates/d564b18a-2890-4152-be3d-e05d784ff727", Rel: "self"}, {Href: "http://10.63.176.154:9511/certificates/d564b18a-2890-4152-be3d-e05d784ff727", Rel: "bookmark"}, }, } func HandleGetCertificateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/certificates/d564b18a-2890-4152-be3d-e05d784ff72", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.Header().Add("OpenStack-API-Minimum-Version", "container-infra 1.1") w.Header().Add("OpenStack-API-Maximum-Version", "container-infra 1.6") w.Header().Add("OpenStack-API-Version", "container-infra 1.1") w.Header().Add("X-OpenStack-Request-Id", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") w.WriteHeader(http.StatusOK) fmt.Fprint(w, CertificateResponse) }) } func HandleCreateCertificateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/certificates/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.Header().Add("OpenStack-API-Minimum-Version", "container-infra 1.1") w.Header().Add("OpenStack-API-Maximum-Version", "container-infra 1.6") w.Header().Add("OpenStack-API-Version", "container-infra 1.1") w.Header().Add("X-OpenStack-Request-Id", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") w.WriteHeader(http.StatusCreated) fmt.Fprint(w, CreateCertificateResponse) }) } func HandleUpdateCertificateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/certificates/d564b18a-2890-4152-be3d-e05d784ff72", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, `{}`) }) } requests_test.go000066400000000000000000000025101335005366400403750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/certificates/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/certificates" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestGetCertificates(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetCertificateSuccessfully(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" actual, err := certificates.Get(sc, "d564b18a-2890-4152-be3d-e05d784ff72").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCertificate, *actual) } func TestCreateCertificates(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateCertificateSuccessfully(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" opts := certificates.CreateOpts{ BayUUID: "d564b18a-2890-4152-be3d-e05d784ff727", CSR: "FAKE_CERTIFICATE_CSR", } actual, err := certificates.Create(sc, opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCreateCertificateResponse, *actual) } func TestUpdateCertificates(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateCertificateSuccessfully(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" err := certificates.Update(sc, "d564b18a-2890-4152-be3d-e05d784ff72").ExtractErr() th.AssertNoErr(t, err) } urls.go000066400000000000000000000007731335005366400350040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/certificatespackage certificates import ( "github.com/gophercloud/gophercloud" ) var apiName = "certificates" func commonURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiName) } func getURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiName, id) } func createURL(client *gophercloud.ServiceClient) string { return commonURL(client) } func updateURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiName, id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/clusters/000077500000000000000000000000001335005366400327375ustar00rootroot00000000000000doc.go000066400000000000000000000034631335005366400337620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/clusters/* Package clusters contains functionality for working with Magnum Cluster resources. Example to Create a Cluster masterCount := 1 nodeCount := 1 createTimeout := 30 opts := clusters.CreateOpts{ ClusterTemplateID: "0562d357-8641-4759-8fed-8173f02c9633", CreateTimeout: &createTimeout, DiscoveryURL: "", FlavorID: "m1.small", KeyPair: "my_keypair", Labels: map[string]string{}, MasterCount: &masterCount, MasterFlavorID: "m1.small", Name: "k8s", NodeCount: &nodeCount, } cluster, err := clusters.Create(serviceClient, createOpts).Extract() if err != nil { panic(err) } Example to Get a Cluster clusterName := "cluster123" cluster, err := clusters.Get(serviceClient, clusterName).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", cluster) Example to List Clusters listOpts := clusters.ListOpts{ Limit: 20, } allPages, err := clusters.List(serviceClient, listOpts).AllPages() if err != nil { panic(err) } allClusters, err := clusters.ExtractClusters(allPages) if err != nil { panic(err) } for _, cluster := range allClusters { fmt.Printf("%+v\n", cluster) } Example to Update a Cluster updateOpts := []clusters.UpdateOptsBuilder{ clusters.UpdateOpts{ Op: clusters.ReplaceOp, Path: "/master_lb_enabled", Value: "True", }, clusters.UpdateOpts{ Op: clusters.ReplaceOp, Path: "/registry_enabled", Value: "True", }, } clusterUUID, err := clusters.Update(serviceClient, clusterUUID, updateOpts).Extract() if err != nil { panic(err) } fmt.Printf("%s\n", clusterUUID) Example to Delete a Cluster clusterUUID := "dc6d336e3fc4c0a951b5698cd1236ee" err := clusters.Delete(serviceClient, clusterUUID).ExtractErr() if err != nil { panic(err) } */ package clusters requests.go000066400000000000000000000112471335005366400350670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/clusterspackage clusters import ( "net/http" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder Builder. type CreateOptsBuilder interface { ToClusterCreateMap() (map[string]interface{}, error) } // CreateOpts params type CreateOpts struct { ClusterTemplateID string `json:"cluster_template_id" required:"true"` CreateTimeout *int `json:"create_timeout"` DiscoveryURL string `json:"discovery_url,omitempty"` DockerVolumeSize *int `json:"docker_volume_size,omitempty"` FlavorID string `json:"flavor_id,omitempty"` Keypair string `json:"keypair,omitempty"` Labels map[string]string `json:"labels,omitempty"` MasterCount *int `json:"master_count,omitempty"` MasterFlavorID string `json:"master_flavor_id,omitempty"` Name string `json:"name"` NodeCount *int `json:"node_count,omitempty"` } // ToClusterCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToClusterCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // Create requests the creation of a new cluster. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToClusterCreateMap() if err != nil { r.Err = err return } var result *http.Response result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) if r.Err == nil { r.Header = result.Header } return } // Get retrieves a specific clusters based on its unique ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { var result *http.Response result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) if r.Err == nil { r.Header = result.Header } return } // Delete deletes the specified cluster ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { var result *http.Response result, r.Err = client.Delete(deleteURL(client, id), nil) r.Header = result.Header return } // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToClustersListQuery() (string, error) } // ListOpts allows the sorting of paginated collections through // the API. SortKey allows you to sort by a particular cluster attribute. // SortDir sets the direction, and is either `asc' or `desc'. // Marker and Limit are used for pagination. type ListOpts struct { Marker string `q:"marker"` Limit int `q:"limit"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToClustersListQuery formats a ListOpts into a query string. func (opts ListOpts) ToClustersListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // clusters. It accepts a ListOptsBuilder, which allows you to sort // the returned collection for greater efficiency. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(c) if opts != nil { query, err := opts.ToClustersListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return ClusterPage{pagination.LinkedPageBase{PageResult: r}} }) } type UpdateOp string const ( AddOp UpdateOp = "add" RemoveOp UpdateOp = "remove" ReplaceOp UpdateOp = "replace" ) type UpdateOpts struct { Op UpdateOp `json:"op" required:"true"` Path string `json:"path" required:"true"` Value string `json:"value,omitempty"` } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToClustersUpdateMap() (map[string]interface{}, error) } // ToClusterUpdateMap assembles a request body based on the contents of // UpdateOpts. func (opts UpdateOpts) ToClustersUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // Update implements cluster updated request. func Update(client *gophercloud.ServiceClient, id string, opts []UpdateOptsBuilder) (r UpdateResult) { var o []map[string]interface{} for _, opt := range opts { b, err := opt.ToClustersUpdateMap() if err != nil { r.Err = err return r } o = append(o, b) } var result *http.Response result, r.Err = client.Patch(updateURL(client, id), o, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) if r.Err == nil { r.Header = result.Header } return } results.go000066400000000000000000000063011335005366400347100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/clusterspackage clusters import ( "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { gophercloud.Result } // CreateResult is the response of a Create operations. type CreateResult struct { commonResult } // DeleteResult is the result from a Delete operation. Call its Extract or ExtractErr // method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // GetResult represents the result of a get operation. type GetResult struct { commonResult } // Extract is a function that accepts a result and extracts a cluster resource. func (r commonResult) Extract() (*Cluster, error) { var s *Cluster err := r.ExtractInto(&s) return s, err } // UpdateResult is the response of a Update operations. type UpdateResult struct { commonResult } func (r CreateResult) Extract() (string, error) { var s struct { UUID string } err := r.ExtractInto(&s) return s.UUID, err } func (r UpdateResult) Extract() (string, error) { var s struct { UUID string } err := r.ExtractInto(&s) return s.UUID, err } type Cluster struct { APIAddress string `json:"api_address"` COEVersion string `json:"coe_version"` ClusterTemplateID string `json:"cluster_template_id"` ContainerVersion string `json:"container_version"` CreateTimeout int `json:"create_timeout"` CreatedAt time.Time `json:"created_at"` DiscoveryURL string `json:"discovery_url"` DockerVolumeSize int `json:"docker_volume_size"` Faults map[string]string `json:"faults"` FlavorID string `json:"flavor_id"` KeyPair string `json:"keypair"` Labels map[string]string `json:"labels"` Links []gophercloud.Link `json:"links"` MasterFlavorID string `json:"master_flavor_id"` MasterAddresses []string `json:"master_addresses"` MasterCount int `json:"master_count"` Name string `json:"name"` NodeAddresses []string `json:"node_addresses"` NodeCount int `json:"node_count"` ProjectID string `json:"project_id"` StackID string `json:"stack_id"` Status string `json:"status"` StatusReason string `json:"status_reason"` UUID string `json:"uuid"` UpdatedAt time.Time `json:"updated_at"` UserID string `json:"user_id"` } type ClusterPage struct { pagination.LinkedPageBase } func (r ClusterPage) NextPageURL() (string, error) { var s struct { Next string `json:"next"` } err := r.ExtractInto(&s) if err != nil { return "", err } return s.Next, nil } // IsEmpty checks whether a ClusterPage struct is empty. func (r ClusterPage) IsEmpty() (bool, error) { is, err := ExtractClusters(r) return len(is) == 0, err } func ExtractClusters(r pagination.Page) ([]Cluster, error) { var s struct { Clusters []Cluster `json:"clusters"` } err := (r.(ClusterPage)).ExtractInto(&s) return s.Clusters, err } testing/000077500000000000000000000000001335005366400343355ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/clustersdoc.go000066400000000000000000000000201335005366400354210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/clusters/testingpackage testing fixtures.go000066400000000000000000000167731335005366400365530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/clusters/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clusters" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const clusterUUID = "746e779a-751a-456b-a3e9-c883d734946f" const clusterUUID2 = "846e779a-751a-456b-a3e9-c883d734946f" const requestUUID = "req-781e9bdc-4163-46eb-91c9-786c53188bbb" var ClusterCreateResponse = fmt.Sprintf(` { "uuid":"%s" }`, clusterUUID) var ExpectedCluster = clusters.Cluster{ APIAddress: "https://172.24.4.6:6443", COEVersion: "v1.2.0", ClusterTemplateID: "0562d357-8641-4759-8fed-8173f02c9633", CreateTimeout: 60, CreatedAt: time.Date(2016, 8, 29, 6, 51, 31, 0, time.UTC), DiscoveryURL: "https://discovery.etcd.io/cbeb580da58915809d59ee69348a84f3", Links: []gophercloud.Link{ { Href: "http://10.164.180.104:9511/v1/clusters/746e779a-751a-456b-a3e9-c883d734946f", Rel: "self", }, { Href: "http://10.164.180.104:9511/clusters/746e779a-751a-456b-a3e9-c883d734946f", Rel: "bookmark", }, }, KeyPair: "my-keypair", MasterAddresses: []string{"172.24.4.6"}, MasterCount: 1, Name: "k8s", NodeAddresses: []string{"172.24.4.13"}, NodeCount: 1, StackID: "9c6f1169-7300-4d08-a444-d2be38758719", Status: "CREATE_COMPLETE", StatusReason: "Stack CREATE completed successfully", UpdatedAt: time.Date(2016, 8, 29, 6, 53, 24, 0, time.UTC), UUID: clusterUUID, } var ExpectedCluster2 = clusters.Cluster{ APIAddress: "https://172.24.4.6:6443", COEVersion: "v1.2.0", ClusterTemplateID: "0562d357-8641-4759-8fed-8173f02c9633", CreateTimeout: 60, CreatedAt: time.Time{}, DiscoveryURL: "https://discovery.etcd.io/cbeb580da58915809d59ee69348a84f3", Links: []gophercloud.Link{ { Href: "http://10.164.180.104:9511/v1/clusters/746e779a-751a-456b-a3e9-c883d734946f", Rel: "self", }, { Href: "http://10.164.180.104:9511/clusters/746e779a-751a-456b-a3e9-c883d734946f", Rel: "bookmark", }, }, KeyPair: "my-keypair", MasterAddresses: []string{"172.24.4.6"}, MasterCount: 1, Name: "k8s", NodeAddresses: []string{"172.24.4.13"}, NodeCount: 1, StackID: "9c6f1169-7300-4d08-a444-d2be38758719", Status: "CREATE_COMPLETE", StatusReason: "Stack CREATE completed successfully", UpdatedAt: time.Date(2016, 8, 29, 6, 53, 24, 0, time.UTC), UUID: clusterUUID2, } var ExpectedClusterUUID = clusterUUID func HandleCreateClusterSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.Header().Add("X-OpenStack-Request-Id", requestUUID) w.WriteHeader(http.StatusAccepted) fmt.Fprint(w, ClusterCreateResponse) }) } func HandleGetClusterSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ClusterGetResponse) }) } var ClusterGetResponse = fmt.Sprintf(` { "status":"CREATE_COMPLETE", "uuid":"%s", "links":[ { "href":"http://10.164.180.104:9511/v1/clusters/746e779a-751a-456b-a3e9-c883d734946f", "rel":"self" }, { "href":"http://10.164.180.104:9511/clusters/746e779a-751a-456b-a3e9-c883d734946f", "rel":"bookmark" } ], "stack_id":"9c6f1169-7300-4d08-a444-d2be38758719", "created_at":"2016-08-29T06:51:31+00:00", "api_address":"https://172.24.4.6:6443", "discovery_url":"https://discovery.etcd.io/cbeb580da58915809d59ee69348a84f3", "updated_at":"2016-08-29T06:53:24+00:00", "master_count":1, "coe_version": "v1.2.0", "keypair":"my-keypair", "cluster_template_id":"0562d357-8641-4759-8fed-8173f02c9633", "master_addresses":[ "172.24.4.6" ], "node_count":1, "node_addresses":[ "172.24.4.13" ], "status_reason":"Stack CREATE completed successfully", "create_timeout":60, "name":"k8s" }`, clusterUUID) var ClusterListResponse = fmt.Sprintf(` { "clusters": [ { "api_address":"https://172.24.4.6:6443", "cluster_template_id":"0562d357-8641-4759-8fed-8173f02c9633", "coe_version": "v1.2.0", "create_timeout":60, "created_at":"2016-08-29T06:51:31+00:00", "discovery_url":"https://discovery.etcd.io/cbeb580da58915809d59ee69348a84f3", "keypair":"my-keypair", "links":[ { "href":"http://10.164.180.104:9511/v1/clusters/746e779a-751a-456b-a3e9-c883d734946f", "rel":"self" }, { "href":"http://10.164.180.104:9511/clusters/746e779a-751a-456b-a3e9-c883d734946f", "rel":"bookmark" } ], "master_addresses":[ "172.24.4.6" ], "master_count":1, "name":"k8s", "node_addresses":[ "172.24.4.13" ], "node_count":1, "stack_id":"9c6f1169-7300-4d08-a444-d2be38758719", "status":"CREATE_COMPLETE", "status_reason":"Stack CREATE completed successfully", "updated_at":"2016-08-29T06:53:24+00:00", "uuid":"%s" }, { "api_address":"https://172.24.4.6:6443", "cluster_template_id":"0562d357-8641-4759-8fed-8173f02c9633", "coe_version": "v1.2.0", "create_timeout":60, "created_at":null, "discovery_url":"https://discovery.etcd.io/cbeb580da58915809d59ee69348a84f3", "keypair":"my-keypair", "links":[ { "href":"http://10.164.180.104:9511/v1/clusters/746e779a-751a-456b-a3e9-c883d734946f", "rel":"self" }, { "href":"http://10.164.180.104:9511/clusters/746e779a-751a-456b-a3e9-c883d734946f", "rel":"bookmark" } ], "master_addresses":[ "172.24.4.6" ], "master_count":1, "name":"k8s", "node_addresses":[ "172.24.4.13" ], "node_count":1, "stack_id":"9c6f1169-7300-4d08-a444-d2be38758719", "status":"CREATE_COMPLETE", "status_reason":"Stack CREATE completed successfully", "updated_at":null, "uuid":"%s" } ] }`, clusterUUID, clusterUUID2) var ExpectedClusters = []clusters.Cluster{ExpectedCluster} func HandleListClusterSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.Header().Add("X-OpenStack-Request-Id", requestUUID) w.WriteHeader(http.StatusOK) fmt.Fprint(w, ClusterListResponse) }) } var UpdateResponse = fmt.Sprintf(` { "uuid":"%s" }`, clusterUUID) func HandleUpdateClusterSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.Header().Add("X-OpenStack-Request-Id", requestUUID) w.WriteHeader(http.StatusOK) fmt.Fprint(w, UpdateResponse) }) } func HandleDeleteClusterSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.Header().Add("X-OpenStack-Request-Id", requestUUID) w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000067051335005366400376060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/clusters/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clusters" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreateCluster(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateClusterSuccessfully(t) masterCount := 1 nodeCount := 1 createTimeout := 30 opts := clusters.CreateOpts{ ClusterTemplateID: "0562d357-8641-4759-8fed-8173f02c9633", CreateTimeout: &createTimeout, DiscoveryURL: "", FlavorID: "m1.small", Keypair: "my_keypair", Labels: map[string]string{}, MasterCount: &masterCount, MasterFlavorID: "m1.small", Name: "k8s", NodeCount: &nodeCount, } sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" res := clusters.Create(sc, opts) th.AssertNoErr(t, res.Err) requestID := res.Header.Get("X-OpenStack-Request-Id") th.AssertEquals(t, requestUUID, requestID) actual, err := res.Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, clusterUUID, actual) } func TestGetCluster(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetClusterSuccessfully(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" actual, err := clusters.Get(sc, "746e779a-751a-456b-a3e9-c883d734946f").Extract() th.AssertNoErr(t, err) actual.CreatedAt = actual.CreatedAt.UTC() actual.UpdatedAt = actual.UpdatedAt.UTC() th.AssertDeepEquals(t, ExpectedCluster, *actual) } func TestListClusters(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListClusterSuccessfully(t) count := 0 sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" clusters.List(sc, clusters.ListOpts{Limit: 2}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := clusters.ExtractClusters(page) th.AssertNoErr(t, err) for idx := range actual { actual[idx].CreatedAt = actual[idx].CreatedAt.UTC() actual[idx].UpdatedAt = actual[idx].UpdatedAt.UTC() } th.AssertDeepEquals(t, ExpectedClusters, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestUpdateCluster(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateClusterSuccessfully(t) updateOpts := []clusters.UpdateOptsBuilder{ clusters.UpdateOpts{ Op: clusters.ReplaceOp, Path: "/master_lb_enabled", Value: "True", }, clusters.UpdateOpts{ Op: clusters.ReplaceOp, Path: "/registry_enabled", Value: "True", }, } sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" res := clusters.Update(sc, clusterUUID, updateOpts) th.AssertNoErr(t, res.Err) requestID := res.Header.Get("X-OpenStack-Request-Id") th.AssertEquals(t, requestUUID, requestID) actual, err := res.Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, clusterUUID, actual) } func TestDeleteCluster(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteClusterSuccessfully(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" r := clusters.Delete(sc, clusterUUID) err := r.ExtractErr() th.AssertNoErr(t, err) uuid := "" idKey := "X-Openstack-Request-Id" if len(r.Header[idKey]) > 0 { uuid = r.Header[idKey][0] if uuid == "" { t.Errorf("No value for header [%s]", idKey) } } else { t.Errorf("Missing header [%s]", idKey) } th.AssertEquals(t, requestUUID, uuid) } urls.go000066400000000000000000000014201335005366400341710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/clusterspackage clusters import ( "github.com/gophercloud/gophercloud" ) var apiName = "clusters" func commonURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiName) } func idURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiName, id) } func createURL(client *gophercloud.ServiceClient) string { return commonURL(client) } func deleteURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } func getURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("clusters", id) } func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("clusters") } func updateURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } clustertemplates/000077500000000000000000000000001335005366400344145ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1doc.go000066400000000000000000000045741335005366400355220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/clustertemplates// Package clustertemplates contains functionality for working with Magnum Cluster Templates // resources. /* Package clustertemplates provides information and interaction with the cluster-templates through the OpenStack Container Infra service. Example to Create Cluster Template boolFalse := false boolTrue := true createOpts := clustertemplates.CreateOpts{ Name: "test-cluster-template", Labels: map[string]string{}, FixedSubnet: "", MasterFlavorID: "", NoProxy: "10.0.0.0/8,172.0.0.0/8,192.0.0.0/8,localhost", HTTPSProxy: "http://10.164.177.169:8080", TLSDisabled: &boolFalse, KeyPairID: "kp", Public: &boolFalse, HTTPProxy: "http://10.164.177.169:8080", ServerType: "vm", ExternalNetworkID: "public", ImageID: "fedora-atomic-latest", VolumeDriver: "cinder", RegistryEnabled: &boolFalse, DockerStorageDriver: "devicemapper", NetworkDriver: "flannel", FixedNetwork: "", COE: "kubernetes", FlavorID: "m1.small", MasterLBEnabled: &boolTrue, DNSNameServer: "8.8.8.8", } clustertemplate, err := clustertemplates.Create(serviceClient, createOpts).Extract() if err != nil { panic(err) } Example to Delete Cluster Template clusterTemplateID := "dc6d336e3fc4c0a951b5698cd1236ee" err := clustertemplates.Delete(serviceClient, clusterTemplateID).ExtractErr() if err != nil { panic(err) } Example to List Clusters Templates listOpts := clustertemplates.ListOpts{ Limit: 20, } allPages, err := clustertemplates.List(serviceClient, listOpts).AllPages() if err != nil { panic(err) } allClusterTemplates, err := clusters.ExtractClusterTemplates(allPages) if err != nil { panic(err) } for _, clusterTemplate := range allClusterTemplates { fmt.Printf("%+v\n", clusterTemplate) } Example to Update Cluster Template updateOpts := []clustertemplates.UpdateOptsBuilder{ clustertemplates.UpdateOpts{ Op: clustertemplates.ReplaceOp, Path: "/master_lb_enabled", Value: "True", }, clustertemplates.UpdateOpts{ Op: clustertemplates.ReplaceOp, Path: "/registry_enabled", Value: "True", }, } clustertemplate, err := clustertemplates.Update(serviceClient, updateOpts).Extract() if err != nil { panic(err) } */ package clustertemplates requests.go000066400000000000000000000136601335005366400366240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/clustertemplatespackage clustertemplates import ( "net/http" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder Builder. type CreateOptsBuilder interface { ToClusterCreateMap() (map[string]interface{}, error) } // CreateOpts params type CreateOpts struct { APIServerPort *int `json:"apiserver_port,omitempty"` COE string `json:"coe" required:"true"` DNSNameServer string `json:"dns_nameserver,omitempty"` DockerStorageDriver string `json:"docker_storage_driver,omitempty"` DockerVolumeSize *int `json:"docker_volume_size,omitempty"` ExternalNetworkID string `json:"external_network_id,omitempty"` FixedNetwork string `json:"fixed_network,omitempty"` FixedSubnet string `json:"fixed_subnet,omitempty"` FlavorID string `json:"flavor_id,omitempty"` FloatingIPEnabled *bool `json:"floating_ip_enabled,omitempty"` HTTPProxy string `json:"http_proxy,omitempty"` HTTPSProxy string `json:"https_proxy,omitempty"` ImageID string `json:"image_id" required:"true"` InsecureRegistry string `json:"insecure_registry,omitempty"` KeyPairID string `json:"keypair_id,omitempty"` Labels map[string]string `json:"labels,omitempty"` MasterFlavorID string `json:"master_flavor_id,omitempty"` MasterLBEnabled *bool `json:"master_lb_enabled,omitempty"` Name string `json:"name,omitempty"` NetworkDriver string `json:"network_driver,omitempty"` NoProxy string `json:"no_proxy,omitempty"` Public *bool `json:"public,omitempty"` RegistryEnabled *bool `json:"registry_enabled,omitempty"` ServerType string `json:"server_type,omitempty"` TLSDisabled *bool `json:"tls_disabled,omitempty"` VolumeDriver string `json:"volume_driver,omitempty"` } // ToClusterCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToClusterCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // Create requests the creation of a new cluster. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToClusterCreateMap() if err != nil { r.Err = err return } var result *http.Response result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) if r.Err == nil { r.Header = result.Header } return } // Delete deletes the specified cluster ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { var result *http.Response result, r.Err = client.Delete(deleteURL(client, id), nil) r.Header = result.Header return } // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToClusterTemplateListQuery() (string, error) } // ListOpts allows the sorting of paginated collections through // the API. SortKey allows you to sort by a particular cluster templates attribute. // SortDir sets the direction, and is either `asc' or `desc'. // Marker and Limit are used for pagination. type ListOpts struct { Marker string `q:"marker"` Limit int `q:"limit"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToClusterTemplateListQuery formats a ListOpts into a query string. func (opts ListOpts) ToClusterTemplateListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // cluster-templates. It accepts a ListOptsBuilder, which allows you to sort // the returned collection for greater efficiency. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToClusterTemplateListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ClusterTemplatePage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves a specific cluster-template based on its unique ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { var result *http.Response result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) if r.Err == nil { r.Header = result.Header } return } type UpdateOp string const ( AddOp UpdateOp = "add" RemoveOp UpdateOp = "remove" ReplaceOp UpdateOp = "replace" ) type UpdateOpts struct { Op UpdateOp `json:"op" required:"true"` Path string `json:"path" required:"true"` Value string `json:"value,omitempty"` } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToClusterTemplateUpdateMap() (map[string]interface{}, error) } // ToClusterUpdateMap assembles a request body based on the contents of // UpdateOpts. func (opts UpdateOpts) ToClusterTemplateUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } return b, nil } // Update implements cluster updated request. func Update(client *gophercloud.ServiceClient, id string, opts []UpdateOptsBuilder) (r UpdateResult) { var o []map[string]interface{} for _, opt := range opts { b, err := opt.ToClusterTemplateUpdateMap() if err != nil { r.Err = err return r } o = append(o, b) } var result *http.Response result, r.Err = client.Patch(updateURL(client, id), o, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) if r.Err == nil { r.Header = result.Header } return } results.go000066400000000000000000000102331335005366400364430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/clustertemplatespackage clustertemplates import ( "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { gophercloud.Result } // CreateResult is the response of a Create operations. type CreateResult struct { commonResult } // DeleteResult is the result from a Delete operation. Call its ExtractErr // method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // GetResult is the response of a Get operations. type GetResult struct { commonResult } // UpdateResult is the response of a Update operations. type UpdateResult struct { commonResult } // Extract is a function that accepts a result and extracts a cluster-template resource. func (r commonResult) Extract() (*ClusterTemplate, error) { var s *ClusterTemplate err := r.ExtractInto(&s) return s, err } // Represents a template for a Cluster Template type ClusterTemplate struct { APIServerPort int `json:"apiserver_port"` COE string `json:"coe"` ClusterDistro string `json:"cluster_distro"` CreatedAt time.Time `json:"created_at"` DNSNameServer string `json:"dns_nameserver"` DockerStorageDriver string `json:"docker_storage_driver"` DockerVolumeSize int `json:"docker_volume_size"` ExternalNetworkID string `json:"external_network_id"` FixedNetwork string `json:"fixed_network"` FixedSubnet string `json:"fixed_subnet"` FlavorID string `json:"flavor_id"` FloatingIPEnabled bool `json:"floating_ip_enabled"` HTTPProxy string `json:"http_proxy"` HTTPSProxy string `json:"https_proxy"` ImageID string `json:"image_id"` InsecureRegistry string `json:"insecure_registry"` KeyPairID string `json:"keypair_id"` Labels map[string]string `json:"labels"` Links []gophercloud.Link `json:"links"` MasterFlavorID string `json:"master_flavor_id"` MasterLBEnabled bool `json:"master_lb_enabled"` Name string `json:"name"` NetworkDriver string `json:"network_driver"` NoProxy string `json:"no_proxy"` ProjectID string `json:"project_id"` Public bool `json:"public"` RegistryEnabled bool `json:"registry_enabled"` ServerType string `json:"server_type"` TLSDisabled bool `json:"tls_disabled"` UUID string `json:"uuid"` UpdatedAt time.Time `json:"updated_at"` UserID string `json:"user_id"` VolumeDriver string `json:"volume_driver"` } // ClusterTemplatePage is the page returned by a pager when traversing over a // collection of cluster-templates. type ClusterTemplatePage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of cluster template has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r ClusterTemplatePage) NextPageURL() (string, error) { var s struct { Next string `json:"next"` } err := r.ExtractInto(&s) if err != nil { return "", err } return s.Next, nil } // IsEmpty checks whether a ClusterTemplatePage struct is empty. func (r ClusterTemplatePage) IsEmpty() (bool, error) { is, err := ExtractClusterTemplates(r) return len(is) == 0, err } // ExtractClusterTemplates accepts a Page struct, specifically a ClusterTemplatePage struct, // and extracts the elements into a slice of cluster templates structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractClusterTemplates(r pagination.Page) ([]ClusterTemplate, error) { var s struct { ClusterTemplates []ClusterTemplate `json:"clustertemplates"` } err := (r.(ClusterTemplatePage)).ExtractInto(&s) return s.ClusterTemplates, err } testing/000077500000000000000000000000001335005366400360715ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/clustertemplatesdoc.go000066400000000000000000000000201335005366400371550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/clustertemplates/testingpackage testing fixtures.go000066400000000000000000000404221335005366400402730ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/clustertemplates/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clustertemplates" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const ClusterTemplateResponse = ` { "apiserver_port": 8081, "cluster_distro": "fedora-atomic", "coe": "kubernetes", "created_at": "2018-06-27T16:52:21+00:00", "dns_nameserver": "8.8.8.8", "docker_storage_driver": "devicemapper", "docker_volume_size": 3, "external_network_id": "public", "fixed_network": null, "fixed_subnet": null, "flavor_id": "m1.small", "floating_ip_enabled": true, "http_proxy": "http://10.164.177.169:8080", "https_proxy": "http://10.164.177.169:8080", "image_id": "Fedora-Atomic-27-20180212.2.x86_64", "insecure_registry": null, "keypair_id": "kp", "labels": null, "links": [ { "href": "http://10.63.176.154:9511/v1/clustertemplates/79c0f9e5-93b8-4719-8fab-063afc67bffe", "rel": "self" }, { "href": "http://10.63.176.154:9511/clustertemplates/79c0f9e5-93b8-4719-8fab-063afc67bffe", "rel": "bookmark" } ], "master_flavor_id": null, "master_lb_enabled": true, "name": "kubernetes-dev", "network_driver": "flannel", "no_proxy": "10.0.0.0/8,172.0.0.0/8,192.0.0.0/8,localhost", "project_id": "76bd201dbc1641729904ab190d3390c6", "public": false, "registry_enabled": false, "server_type": "vm", "tls_disabled": false, "updated_at": null, "user_id": "c48d66144e9c4a54ae2b164b85cfefe3", "uuid": "79c0f9e5-93b8-4719-8fab-063afc67bffe", "volume_driver": "cinder" }` const ClusterTemplateResponse_EmptyTime = ` { "apiserver_port": null, "cluster_distro": "fedora-atomic", "coe": "kubernetes", "created_at": null, "dns_nameserver": "8.8.8.8", "docker_storage_driver": null, "docker_volume_size": 5, "external_network_id": "public", "fixed_network": null, "fixed_subnet": null, "flavor_id": "m1.small", "http_proxy": null, "https_proxy": null, "image_id": "fedora-atomic-latest", "insecure_registry": null, "keypair_id": "testkey", "labels": {}, "links": [ { "href": "http://65.61.151.130:9511/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", "rel": "bookmark" }, { "href": "http://65.61.151.130:9511/v1/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", "rel": "self" } ], "master_flavor_id": null, "master_lb_enabled": false, "name": "kubernetes-dev", "network_driver": "flannel", "no_proxy": null, "public": false, "registry_enabled": false, "server_type": "vm", "tls_disabled": false, "updated_at": null, "uuid": "472807c2-f175-4946-9765-149701a5aba7", "volume_driver": null }` const ClusterTemplateListResponse = ` { "clustertemplates": [ { "apiserver_port": 8081, "cluster_distro": "fedora-atomic", "coe": "kubernetes", "created_at": "2018-06-27T16:52:21+00:00", "dns_nameserver": "8.8.8.8", "docker_storage_driver": "devicemapper", "docker_volume_size": 3, "external_network_id": "public", "fixed_network": null, "fixed_subnet": null, "flavor_id": "m1.small", "floating_ip_enabled": true, "http_proxy": "http://10.164.177.169:8080", "https_proxy": "http://10.164.177.169:8080", "image_id": "Fedora-Atomic-27-20180212.2.x86_64", "insecure_registry": null, "keypair_id": "kp", "labels": null, "links": [ { "href": "http://10.63.176.154:9511/v1/clustertemplates/79c0f9e5-93b8-4719-8fab-063afc67bffe", "rel": "self" }, { "href": "http://10.63.176.154:9511/clustertemplates/79c0f9e5-93b8-4719-8fab-063afc67bffe", "rel": "bookmark" } ], "master_flavor_id": null, "master_lb_enabled": true, "name": "kubernetes-dev", "network_driver": "flannel", "no_proxy": "10.0.0.0/8,172.0.0.0/8,192.0.0.0/8,localhost", "project_id": "76bd201dbc1641729904ab190d3390c6", "public": false, "registry_enabled": false, "server_type": "vm", "tls_disabled": false, "updated_at": null, "user_id": "c48d66144e9c4a54ae2b164b85cfefe3", "uuid": "79c0f9e5-93b8-4719-8fab-063afc67bffe", "volume_driver": "cinder" }, { "apiserver_port": null, "cluster_distro": "fedora-atomic", "coe": "kubernetes", "created_at": null, "dns_nameserver": "8.8.8.8", "docker_storage_driver": null, "docker_volume_size": 5, "external_network_id": "public", "fixed_network": null, "fixed_subnet": null, "flavor_id": "m1.small", "http_proxy": null, "https_proxy": null, "image_id": "fedora-atomic-latest", "insecure_registry": null, "keypair_id": "testkey", "labels": {}, "links": [ { "href": "http://65.61.151.130:9511/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", "rel": "bookmark" }, { "href": "http://65.61.151.130:9511/v1/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", "rel": "self" } ], "master_flavor_id": null, "master_lb_enabled": false, "name": "kubernetes-dev", "network_driver": "flannel", "no_proxy": null, "public": false, "registry_enabled": false, "server_type": "vm", "tls_disabled": false, "updated_at": null, "uuid": "472807c2-f175-4946-9765-149701a5aba7", "volume_driver": null } ] }` var ExpectedClusterTemplate = clustertemplates.ClusterTemplate{ APIServerPort: 8081, COE: "kubernetes", ClusterDistro: "fedora-atomic", CreatedAt: time.Date(2018, 6, 27, 16, 52, 21, 0, time.UTC), DNSNameServer: "8.8.8.8", DockerStorageDriver: "devicemapper", DockerVolumeSize: 3, ExternalNetworkID: "public", FixedNetwork: "", FixedSubnet: "", FlavorID: "m1.small", FloatingIPEnabled: true, HTTPProxy: "http://10.164.177.169:8080", HTTPSProxy: "http://10.164.177.169:8080", ImageID: "Fedora-Atomic-27-20180212.2.x86_64", InsecureRegistry: "", KeyPairID: "kp", Labels: map[string]string(nil), Links: []gophercloud.Link{ {Href: "http://10.63.176.154:9511/v1/clustertemplates/79c0f9e5-93b8-4719-8fab-063afc67bffe", Rel: "self"}, {Href: "http://10.63.176.154:9511/clustertemplates/79c0f9e5-93b8-4719-8fab-063afc67bffe", Rel: "bookmark"}, }, MasterFlavorID: "", MasterLBEnabled: true, Name: "kubernetes-dev", NetworkDriver: "flannel", NoProxy: "10.0.0.0/8,172.0.0.0/8,192.0.0.0/8,localhost", ProjectID: "76bd201dbc1641729904ab190d3390c6", Public: false, RegistryEnabled: false, ServerType: "vm", TLSDisabled: false, UUID: "79c0f9e5-93b8-4719-8fab-063afc67bffe", UpdatedAt: time.Time{}, UserID: "c48d66144e9c4a54ae2b164b85cfefe3", VolumeDriver: "cinder", } var ExpectedClusterTemplate_EmptyTime = clustertemplates.ClusterTemplate{ COE: "kubernetes", ClusterDistro: "fedora-atomic", CreatedAt: time.Time{}, DNSNameServer: "8.8.8.8", DockerStorageDriver: "", DockerVolumeSize: 5, ExternalNetworkID: "public", FixedNetwork: "", FixedSubnet: "", FlavorID: "m1.small", HTTPProxy: "", HTTPSProxy: "", ImageID: "fedora-atomic-latest", InsecureRegistry: "", KeyPairID: "testkey", Labels: map[string]string{}, Links: []gophercloud.Link{ {Href: "http://65.61.151.130:9511/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", Rel: "bookmark"}, {Href: "http://65.61.151.130:9511/v1/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", Rel: "self"}, }, MasterFlavorID: "", MasterLBEnabled: false, Name: "kubernetes-dev", NetworkDriver: "flannel", NoProxy: "", Public: false, RegistryEnabled: false, ServerType: "vm", TLSDisabled: false, UUID: "472807c2-f175-4946-9765-149701a5aba7", UpdatedAt: time.Time{}, VolumeDriver: "", } var ExpectedClusterTemplates = []clustertemplates.ClusterTemplate{ExpectedClusterTemplate, ExpectedClusterTemplate_EmptyTime} func HandleCreateClusterTemplateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clustertemplates", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.Header().Add("OpenStack-API-Minimum-Version", "container-infra 1.1") w.Header().Add("OpenStack-API-Maximum-Version", "container-infra 1.6") w.Header().Add("OpenStack-API-Version", "container-infra 1.1") w.Header().Add("X-OpenStack-Request-Id", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") w.WriteHeader(http.StatusCreated) fmt.Fprint(w, ClusterTemplateResponse) }) } func HandleDeleteClusterSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clustertemplates/6dc6d336e3fc4c0a951b5698cd1236ee", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.Header().Add("OpenStack-API-Minimum-Version", "container-infra 1.1") w.Header().Add("OpenStack-API-Maximum-Version", "container-infra 1.6") w.Header().Add("OpenStack-API-Version", "container-infra 1.1") w.Header().Add("X-OpenStack-Request-Id", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") w.WriteHeader(http.StatusNoContent) }) } func HandleListClusterTemplateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clustertemplates", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ClusterTemplateListResponse) }) } func HandleGetClusterTemplateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clustertemplates/7d85f602-a948-4a30-afd4-e84f47471c15", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ClusterTemplateResponse) }) } func HandleGetClusterTemplateEmptyTimeSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clustertemplates/7d85f602-a948-4a30-afd4-e84f47471c15", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ClusterTemplateResponse_EmptyTime) }) } const UpdateResponse = ` { "apiserver_port": null, "cluster_distro": "fedora-atomic", "coe": "kubernetes", "created_at": "2016-08-10T13:47:01+00:00", "dns_nameserver": "8.8.8.8", "docker_storage_driver": null, "docker_volume_size": 5, "external_network_id": "public", "fixed_network": null, "fixed_subnet": null, "flavor_id": "m1.small", "http_proxy": null, "https_proxy": null, "image_id": "fedora-atomic-latest", "insecure_registry": null, "keypair_id": "testkey", "labels": {}, "links": [ { "href": "http://65.61.151.130:9511/v1/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", "rel": "self" }, { "href": "http://65.61.151.130:9511/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", "rel": "bookmark" } ], "master_flavor_id": null, "master_lb_enabled": false, "name": "kubernetes-dev", "network_driver": "flannel", "no_proxy": null, "public": false, "registry_enabled": false, "server_type": "vm", "tls_disabled": false, "updated_at": null, "uuid": "472807c2-f175-4946-9765-149701a5aba7", "volume_driver": null }` const UpdateResponse_EmptyTime = ` { "apiserver_port": null, "cluster_distro": "fedora-atomic", "coe": "kubernetes", "created_at": null, "dns_nameserver": "8.8.8.8", "docker_storage_driver": null, "docker_volume_size": 5, "external_network_id": "public", "fixed_network": null, "fixed_subnet": null, "flavor_id": "m1.small", "http_proxy": null, "https_proxy": null, "image_id": "fedora-atomic-latest", "insecure_registry": null, "keypair_id": "testkey", "labels": {}, "links": [ { "href": "http://65.61.151.130:9511/v1/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", "rel": "self" }, { "href": "http://65.61.151.130:9511/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", "rel": "bookmark" } ], "master_flavor_id": null, "master_lb_enabled": false, "name": "kubernetes-dev", "network_driver": "flannel", "no_proxy": null, "public": false, "registry_enabled": false, "server_type": "vm", "tls_disabled": false, "updated_at": null, "uuid": "472807c2-f175-4946-9765-149701a5aba7", "volume_driver": null }` const UpdateResponse_InvalidUpdate = ` { "errors": [{\"status\": 400, \"code\": \"client\", \"links\": [], \"title\": \"'add' and 'replace' operations needs value\", \"detail\": \"'add' and 'replace' operations needs value\", \"request_id\": \"\"}] }` var ExpectedUpdateClusterTemplate = clustertemplates.ClusterTemplate{ COE: "kubernetes", ClusterDistro: "fedora-atomic", CreatedAt: time.Date(2016, 8, 10, 13, 47, 01, 0, time.UTC), DNSNameServer: "8.8.8.8", DockerStorageDriver: "", DockerVolumeSize: 5, ExternalNetworkID: "public", FixedNetwork: "", FixedSubnet: "", FlavorID: "m1.small", HTTPProxy: "", HTTPSProxy: "", ImageID: "fedora-atomic-latest", InsecureRegistry: "", KeyPairID: "testkey", Labels: map[string]string{}, Links: []gophercloud.Link{ {Href: "http://65.61.151.130:9511/v1/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", Rel: "self"}, {Href: "http://65.61.151.130:9511/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", Rel: "bookmark"}, }, MasterFlavorID: "", MasterLBEnabled: false, Name: "kubernetes-dev", NetworkDriver: "flannel", NoProxy: "", Public: false, RegistryEnabled: false, ServerType: "vm", TLSDisabled: false, UUID: "472807c2-f175-4946-9765-149701a5aba7", UpdatedAt: time.Time{}, VolumeDriver: "", } var ExpectedUpdateClusterTemplate_EmptyTime = clustertemplates.ClusterTemplate{ COE: "kubernetes", ClusterDistro: "fedora-atomic", CreatedAt: time.Time{}, DNSNameServer: "8.8.8.8", DockerStorageDriver: "", DockerVolumeSize: 5, ExternalNetworkID: "public", FixedNetwork: "", FixedSubnet: "", FlavorID: "m1.small", HTTPProxy: "", HTTPSProxy: "", ImageID: "fedora-atomic-latest", InsecureRegistry: "", KeyPairID: "testkey", Labels: map[string]string{}, Links: []gophercloud.Link{ {Href: "http://65.61.151.130:9511/v1/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", Rel: "self"}, {Href: "http://65.61.151.130:9511/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", Rel: "bookmark"}, }, MasterFlavorID: "", MasterLBEnabled: false, Name: "kubernetes-dev", NetworkDriver: "flannel", NoProxy: "", Public: false, RegistryEnabled: false, ServerType: "vm", TLSDisabled: false, UUID: "472807c2-f175-4946-9765-149701a5aba7", UpdatedAt: time.Time{}, VolumeDriver: "", } func HandleUpdateClusterTemplateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clustertemplates/7d85f602-a948-4a30-afd4-e84f47471c15", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, UpdateResponse) }) } func HandleUpdateClusterTemplateEmptyTimeSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clustertemplates/7d85f602-a948-4a30-afd4-e84f47471c15", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, UpdateResponse_EmptyTime) }) } func HandleUpdateClusterTemplateInvalidUpdate(t *testing.T) { th.Mux.HandleFunc("/v1/clustertemplates/7d85f602-a948-4a30-afd4-e84f47471c15", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) fmt.Fprint(w, UpdateResponse_EmptyTime) }) } requests_test.go000066400000000000000000000135501335005366400413360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/clustertemplates/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clustertemplates" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreateClusterTemplate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateClusterTemplateSuccessfully(t) boolFalse := false boolTrue := true dockerVolumeSize := 3 opts := clustertemplates.CreateOpts{ Name: "kubernetes-dev", Labels: map[string]string{}, FixedSubnet: "", MasterFlavorID: "", NoProxy: "10.0.0.0/8,172.0.0.0/8,192.0.0.0/8,localhost", HTTPSProxy: "http://10.164.177.169:8080", TLSDisabled: &boolFalse, KeyPairID: "kp", Public: &boolFalse, HTTPProxy: "http://10.164.177.169:8080", DockerVolumeSize: &dockerVolumeSize, ServerType: "vm", ExternalNetworkID: "public", ImageID: "Fedora-Atomic-27-20180212.2.x86_64", VolumeDriver: "cinder", RegistryEnabled: &boolFalse, DockerStorageDriver: "devicemapper", NetworkDriver: "flannel", FixedNetwork: "", COE: "kubernetes", FlavorID: "m1.small", MasterLBEnabled: &boolTrue, DNSNameServer: "8.8.8.8", } sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" res := clustertemplates.Create(sc, opts) th.AssertNoErr(t, res.Err) requestID := res.Header.Get("X-OpenStack-Request-Id") th.AssertEquals(t, "req-781e9bdc-4163-46eb-91c9-786c53188bbb", requestID) actual, err := res.Extract() th.AssertNoErr(t, err) actual.CreatedAt = actual.CreatedAt.UTC() th.AssertDeepEquals(t, ExpectedClusterTemplate, *actual) } func TestDeleteClusterTemplate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteClusterSuccessfully(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" res := clustertemplates.Delete(sc, "6dc6d336e3fc4c0a951b5698cd1236ee") th.AssertNoErr(t, res.Err) requestID := res.Header["X-Openstack-Request-Id"][0] th.AssertEquals(t, "req-781e9bdc-4163-46eb-91c9-786c53188bbb", requestID) } func TestListClusterTemplates(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListClusterTemplateSuccessfully(t) count := 0 sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" clustertemplates.List(sc, clustertemplates.ListOpts{Limit: 2}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := clustertemplates.ExtractClusterTemplates(page) th.AssertNoErr(t, err) for idx, _ := range actual { actual[idx].CreatedAt = actual[idx].CreatedAt.UTC() } th.AssertDeepEquals(t, ExpectedClusterTemplates, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestGetClusterTemplate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetClusterTemplateSuccessfully(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" actual, err := clustertemplates.Get(sc, "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() th.AssertNoErr(t, err) actual.CreatedAt = actual.CreatedAt.UTC() th.AssertDeepEquals(t, ExpectedClusterTemplate, *actual) } func TestGetClusterTemplateEmptyTime(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetClusterTemplateEmptyTimeSuccessfully(t) sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" actual, err := clustertemplates.Get(sc, "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() th.AssertNoErr(t, err) actual.CreatedAt = actual.CreatedAt.UTC() th.AssertDeepEquals(t, ExpectedClusterTemplate_EmptyTime, *actual) } func TestUpdateClusterTemplate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateClusterTemplateSuccessfully(t) updateOpts := []clustertemplates.UpdateOptsBuilder{ clustertemplates.UpdateOpts{ Path: "/master_lb_enabled", Value: "True", Op: clustertemplates.ReplaceOp, }, clustertemplates.UpdateOpts{ Path: "/registry_enabled", Value: "True", Op: clustertemplates.ReplaceOp, }, } sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" res := clustertemplates.Update(sc, "7d85f602-a948-4a30-afd4-e84f47471c15", updateOpts) th.AssertNoErr(t, res.Err) actual, err := res.Extract() th.AssertNoErr(t, err) actual.CreatedAt = actual.CreatedAt.UTC() th.AssertDeepEquals(t, ExpectedUpdateClusterTemplate, *actual) } func TestUpdateClusterTemplateEmptyTime(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateClusterTemplateEmptyTimeSuccessfully(t) updateOpts := []clustertemplates.UpdateOptsBuilder{ clustertemplates.UpdateOpts{ Op: clustertemplates.ReplaceOp, Path: "/master_lb_enabled", Value: "True", }, clustertemplates.UpdateOpts{ Op: clustertemplates.ReplaceOp, Path: "/registry_enabled", Value: "True", }, } sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" actual, err := clustertemplates.Update(sc, "7d85f602-a948-4a30-afd4-e84f47471c15", updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedUpdateClusterTemplate_EmptyTime, *actual) } func TestUpdateClusterTemplateInvalidUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateClusterTemplateInvalidUpdate(t) updateOpts := []clustertemplates.UpdateOptsBuilder{ clustertemplates.UpdateOpts{ Op: clustertemplates.ReplaceOp, Path: "/master_lb_enabled", }, clustertemplates.UpdateOpts{ Op: clustertemplates.RemoveOp, Path: "/master_lb_enabled", }, clustertemplates.UpdateOpts{ Op: clustertemplates.AddOp, Path: "/master_lb_enabled", }, } sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" _, err := clustertemplates.Update(sc, "7d85f602-a948-4a30-afd4-e84f47471c15", updateOpts).Extract() th.AssertEquals(t, true, err != nil) } urls.go000066400000000000000000000014161335005366400357320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/containerinfra/v1/clustertemplatespackage clustertemplates import ( "github.com/gophercloud/gophercloud" ) var apiName = "clustertemplates" func commonURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiName) } func idURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiName, id) } func createURL(client *gophercloud.ServiceClient) string { return commonURL(client) } func deleteURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } func listURL(client *gophercloud.ServiceClient) string { return commonURL(client) } func getURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } func updateURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/000077500000000000000000000000001335005366400261305ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/000077500000000000000000000000001335005366400264565ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/configurations/000077500000000000000000000000001335005366400315105ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/configurations/doc.go000066400000000000000000000012251335005366400326040ustar00rootroot00000000000000// Package configurations provides information and interaction with the // configuration API resource in the Rackspace Database service. // // A configuration group is a collection of key/value pairs which define how a // particular database operates. These key/value pairs are specific to each // datastore type and serve like settings. Some directives are capable of being // applied dynamically, while other directives require a server restart to take // effect. The configuration group can be applied to an instance at creation or // applied to an existing instance to modify the behavior of the running // datastore on the instance. package configurations requests.go000066400000000000000000000154261335005366400336430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/configurationspackage configurations import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/db/v1/instances" "github.com/gophercloud/gophercloud/pagination" ) // List will list all of the available configurations. func List(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, baseURL(client), func(r pagination.PageResult) pagination.Page { return ConfigPage{pagination.SinglePageBase(r)} }) } // CreateOptsBuilder is a top-level interface which renders a JSON map. type CreateOptsBuilder interface { ToConfigCreateMap() (map[string]interface{}, error) } // DatastoreOpts is the primary options struct for creating and modifying // how configuration resources are associated with datastores. type DatastoreOpts struct { // The type of datastore. Defaults to "MySQL". Type string `json:"type,omitempty"` // The specific version of a datastore. Defaults to "5.6". Version string `json:"version,omitempty"` } // CreateOpts is the struct responsible for configuring new configurations. type CreateOpts struct { // The configuration group name Name string `json:"name" required:"true"` // A map of user-defined configuration settings that will define // how each associated datastore works. Each key/value pair is specific to a // datastore type. Values map[string]interface{} `json:"values" required:"true"` // Associates the configuration group with a particular datastore. Datastore *DatastoreOpts `json:"datastore,omitempty"` // A human-readable explanation for the group. Description string `json:"description,omitempty"` } // ToConfigCreateMap casts a CreateOpts struct into a JSON map. func (opts CreateOpts) ToConfigCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "configuration") } // Create will create a new configuration group. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToConfigCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) return } // Get will retrieve the details for a specified configuration group. func Get(client *gophercloud.ServiceClient, configID string) (r GetResult) { _, r.Err = client.Get(resourceURL(client, configID), &r.Body, nil) return } // UpdateOptsBuilder is the top-level interface for casting update options into // JSON maps. type UpdateOptsBuilder interface { ToConfigUpdateMap() (map[string]interface{}, error) } // UpdateOpts is the struct responsible for modifying existing configurations. type UpdateOpts struct { // The configuration group name Name string `json:"name,omitempty"` // A map of user-defined configuration settings that will define // how each associated datastore works. Each key/value pair is specific to a // datastore type. Values map[string]interface{} `json:"values,omitempty"` // Associates the configuration group with a particular datastore. Datastore *DatastoreOpts `json:"datastore,omitempty"` // A human-readable explanation for the group. Description string `json:"description,omitempty"` } // ToConfigUpdateMap will cast an UpdateOpts struct into a JSON map. func (opts UpdateOpts) ToConfigUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "configuration") } // Update will modify an existing configuration group by performing a merge // between new and existing values. If the key already exists, the new value // will overwrite. All other keys will remain unaffected. func Update(client *gophercloud.ServiceClient, configID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToConfigUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Patch(resourceURL(client, configID), &b, nil, nil) return } // Replace will modify an existing configuration group by overwriting the // entire parameter group with the new values provided. Any existing keys not // included in UpdateOptsBuilder will be deleted. func Replace(client *gophercloud.ServiceClient, configID string, opts UpdateOptsBuilder) (r ReplaceResult) { b, err := opts.ToConfigUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Put(resourceURL(client, configID), &b, nil, nil) return } // Delete will permanently delete a configuration group. Please note that // config groups cannot be deleted whilst still attached to running instances - // you must detach and then delete them. func Delete(client *gophercloud.ServiceClient, configID string) (r DeleteResult) { _, r.Err = client.Delete(resourceURL(client, configID), nil) return } // ListInstances will list all the instances associated with a particular // configuration group. func ListInstances(client *gophercloud.ServiceClient, configID string) pagination.Pager { return pagination.NewPager(client, instancesURL(client, configID), func(r pagination.PageResult) pagination.Page { return instances.InstancePage{LinkedPageBase: pagination.LinkedPageBase{PageResult: r}} }) } // ListDatastoreParams will list all the available and supported parameters // that can be used for a particular datastore ID and a particular version. // For example, if you are wondering how you can configure a MySQL 5.6 instance, // you can use this operation (you will need to retrieve the MySQL datastore ID // by using the datastores API). func ListDatastoreParams(client *gophercloud.ServiceClient, datastoreID, versionID string) pagination.Pager { return pagination.NewPager(client, listDSParamsURL(client, datastoreID, versionID), func(r pagination.PageResult) pagination.Page { return ParamPage{pagination.SinglePageBase(r)} }) } // GetDatastoreParam will retrieve information about a specific configuration // parameter. For example, you can use this operation to understand more about // "innodb_file_per_table" configuration param for MySQL datastores. You will // need the param's ID first, which can be attained by using the ListDatastoreParams // operation. func GetDatastoreParam(client *gophercloud.ServiceClient, datastoreID, versionID, paramID string) (r ParamResult) { _, r.Err = client.Get(getDSParamURL(client, datastoreID, versionID, paramID), &r.Body, nil) return } // ListGlobalParams is similar to ListDatastoreParams but does not require a // DatastoreID. func ListGlobalParams(client *gophercloud.ServiceClient, versionID string) pagination.Pager { return pagination.NewPager(client, listGlobalParamsURL(client, versionID), func(r pagination.PageResult) pagination.Page { return ParamPage{pagination.SinglePageBase(r)} }) } // GetGlobalParam is similar to GetDatastoreParam but does not require a // DatastoreID. func GetGlobalParam(client *gophercloud.ServiceClient, versionID, paramID string) (r ParamResult) { _, r.Err = client.Get(getGlobalParamURL(client, versionID, paramID), &r.Body, nil) return } results.go000066400000000000000000000066271335005366400334740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/configurationspackage configurations import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Config represents a configuration group API resource. type Config struct { Created time.Time `json:"-"` Updated time.Time `json:"-"` DatastoreName string `json:"datastore_name"` DatastoreVersionID string `json:"datastore_version_id"` DatastoreVersionName string `json:"datastore_version_name"` Description string ID string Name string Values map[string]interface{} } func (r *Config) UnmarshalJSON(b []byte) error { type tmp Config var s struct { tmp Created gophercloud.JSONRFC3339NoZ `json:"created"` Updated gophercloud.JSONRFC3339NoZ `json:"updated"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Config(s.tmp) r.Created = time.Time(s.Created) r.Updated = time.Time(s.Updated) return nil } // ConfigPage contains a page of Config resources in a paginated collection. type ConfigPage struct { pagination.SinglePageBase } // IsEmpty indicates whether a ConfigPage is empty. func (r ConfigPage) IsEmpty() (bool, error) { is, err := ExtractConfigs(r) return len(is) == 0, err } // ExtractConfigs will retrieve a slice of Config structs from a page. func ExtractConfigs(r pagination.Page) ([]Config, error) { var s struct { Configs []Config `json:"configurations"` } err := (r.(ConfigPage)).ExtractInto(&s) return s.Configs, err } type commonResult struct { gophercloud.Result } // Extract will retrieve a Config resource from an operation result. func (r commonResult) Extract() (*Config, error) { var s struct { Config *Config `json:"configuration"` } err := r.ExtractInto(&s) return s.Config, err } // GetResult represents the result of a Get operation. type GetResult struct { commonResult } // CreateResult represents the result of a Create operation. type CreateResult struct { commonResult } // UpdateResult represents the result of an Update operation. type UpdateResult struct { gophercloud.ErrResult } // ReplaceResult represents the result of a Replace operation. type ReplaceResult struct { gophercloud.ErrResult } // DeleteResult represents the result of a Delete operation. type DeleteResult struct { gophercloud.ErrResult } // Param represents a configuration parameter API resource. type Param struct { Max float64 Min float64 Name string RestartRequired bool `json:"restart_required"` Type string } // ParamPage contains a page of Param resources in a paginated collection. type ParamPage struct { pagination.SinglePageBase } // IsEmpty indicates whether a ParamPage is empty. func (r ParamPage) IsEmpty() (bool, error) { is, err := ExtractParams(r) return len(is) == 0, err } // ExtractParams will retrieve a slice of Param structs from a page. func ExtractParams(r pagination.Page) ([]Param, error) { var s struct { Params []Param `json:"configuration-parameters"` } err := (r.(ParamPage)).ExtractInto(&s) return s.Params, err } // ParamResult represents the result of an operation which retrieves details // about a particular configuration param. type ParamResult struct { gophercloud.Result } // Extract will retrieve a param from an operation result. func (r ParamResult) Extract() (*Param, error) { var s *Param err := r.ExtractInto(&s) return s, err } testing/000077500000000000000000000000001335005366400331065ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/configurationsdoc.go000066400000000000000000000000501335005366400341750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/configurations/testing// db_configurations_v1 package testing fixtures.go000066400000000000000000000071031335005366400353070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/configurations/testingpackage testing import ( "fmt" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/db/v1/configurations" ) var ( timestamp = "2015-11-12T14:22:42" timeVal, _ = time.Parse(gophercloud.RFC3339NoZ, timestamp) ) var singleConfigJSON = ` { "created": "` + timestamp + `", "datastore_name": "mysql", "datastore_version_id": "b00000b0-00b0-0b00-00b0-000b000000bb", "datastore_version_name": "5.6", "description": "example_description", "id": "005a8bb7-a8df-40ee-b0b7-fc144641abc2", "name": "example-configuration-name", "updated": "` + timestamp + `" } ` var singleConfigWithValuesJSON = ` { "created": "` + timestamp + `", "datastore_name": "mysql", "datastore_version_id": "b00000b0-00b0-0b00-00b0-000b000000bb", "datastore_version_name": "5.6", "description": "example description", "id": "005a8bb7-a8df-40ee-b0b7-fc144641abc2", "instance_count": 0, "name": "example-configuration-name", "updated": "` + timestamp + `", "values": { "collation_server": "latin1_swedish_ci", "connect_timeout": 120 } } ` var ( ListConfigsJSON = fmt.Sprintf(`{"configurations": [%s]}`, singleConfigJSON) GetConfigJSON = fmt.Sprintf(`{"configuration": %s}`, singleConfigJSON) CreateConfigJSON = fmt.Sprintf(`{"configuration": %s}`, singleConfigWithValuesJSON) ) var CreateReq = ` { "configuration": { "datastore": { "type": "a00000a0-00a0-0a00-00a0-000a000000aa", "version": "b00000b0-00b0-0b00-00b0-000b000000bb" }, "description": "example description", "name": "example-configuration-name", "values": { "collation_server": "latin1_swedish_ci", "connect_timeout": 120 } } } ` var UpdateReq = ` { "configuration": { "values": { "connect_timeout": 300 } } } ` var ListInstancesJSON = ` { "instances": [ { "id": "d4603f69-ec7e-4e9b-803f-600b9205576f", "name": "json_rack_instance" } ] } ` var ListParamsJSON = ` { "configuration-parameters": [ { "max": 1, "min": 0, "name": "innodb_file_per_table", "restart_required": true, "type": "integer" }, { "max": 4294967296, "min": 0, "name": "key_buffer_size", "restart_required": false, "type": "integer" }, { "max": 65535, "min": 2, "name": "connect_timeout", "restart_required": false, "type": "integer" }, { "max": 4294967296, "min": 0, "name": "join_buffer_size", "restart_required": false, "type": "integer" } ] } ` var GetParamJSON = ` { "max": 1, "min": 0, "name": "innodb_file_per_table", "restart_required": true, "type": "integer" } ` var ExampleConfig = configurations.Config{ Created: timeVal, DatastoreName: "mysql", DatastoreVersionID: "b00000b0-00b0-0b00-00b0-000b000000bb", DatastoreVersionName: "5.6", Description: "example_description", ID: "005a8bb7-a8df-40ee-b0b7-fc144641abc2", Name: "example-configuration-name", Updated: timeVal, } var ExampleConfigWithValues = configurations.Config{ Created: timeVal, DatastoreName: "mysql", DatastoreVersionID: "b00000b0-00b0-0b00-00b0-000b000000bb", DatastoreVersionName: "5.6", Description: "example description", ID: "005a8bb7-a8df-40ee-b0b7-fc144641abc2", Name: "example-configuration-name", Updated: timeVal, Values: map[string]interface{}{ "collation_server": "latin1_swedish_ci", "connect_timeout": float64(120), }, } requests_test.go000066400000000000000000000153221335005366400363520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/configurations/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/db/v1/configurations" "github.com/gophercloud/gophercloud/openstack/db/v1/instances" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" "github.com/gophercloud/gophercloud/testhelper/fixture" ) var ( configID = "{configID}" _baseURL = "/configurations" resURL = _baseURL + "/" + configID dsID = "{datastoreID}" versionID = "{versionID}" paramID = "{paramID}" dsParamListURL = "/datastores/" + dsID + "/versions/" + versionID + "/parameters" dsParamGetURL = "/datastores/" + dsID + "/versions/" + versionID + "/parameters/" + paramID globalParamListURL = "/datastores/versions/" + versionID + "/parameters" globalParamGetURL = "/datastores/versions/" + versionID + "/parameters/" + paramID ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() fixture.SetupHandler(t, _baseURL, "GET", "", ListConfigsJSON, 200) count := 0 err := configurations.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := configurations.ExtractConfigs(page) th.AssertNoErr(t, err) expected := []configurations.Config{ExampleConfig} th.AssertDeepEquals(t, expected, actual) return true, nil }) th.AssertEquals(t, 1, count) th.AssertNoErr(t, err) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() fixture.SetupHandler(t, resURL, "GET", "", GetConfigJSON, 200) config, err := configurations.Get(fake.ServiceClient(), configID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &ExampleConfig, config) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() fixture.SetupHandler(t, _baseURL, "POST", CreateReq, CreateConfigJSON, 200) opts := configurations.CreateOpts{ Datastore: &configurations.DatastoreOpts{ Type: "a00000a0-00a0-0a00-00a0-000a000000aa", Version: "b00000b0-00b0-0b00-00b0-000b000000bb", }, Description: "example description", Name: "example-configuration-name", Values: map[string]interface{}{ "collation_server": "latin1_swedish_ci", "connect_timeout": 120, }, } config, err := configurations.Create(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &ExampleConfigWithValues, config) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() fixture.SetupHandler(t, resURL, "PATCH", UpdateReq, "", 200) opts := configurations.UpdateOpts{ Values: map[string]interface{}{ "connect_timeout": 300, }, } err := configurations.Update(fake.ServiceClient(), configID, opts).ExtractErr() th.AssertNoErr(t, err) } func TestReplace(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() fixture.SetupHandler(t, resURL, "PUT", UpdateReq, "", 202) opts := configurations.UpdateOpts{ Values: map[string]interface{}{ "connect_timeout": 300, }, } err := configurations.Replace(fake.ServiceClient(), configID, opts).ExtractErr() th.AssertNoErr(t, err) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() fixture.SetupHandler(t, resURL, "DELETE", "", "", 202) err := configurations.Delete(fake.ServiceClient(), configID).ExtractErr() th.AssertNoErr(t, err) } func TestListInstances(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() fixture.SetupHandler(t, resURL+"/instances", "GET", "", ListInstancesJSON, 200) expectedInstance := instances.Instance{ ID: "d4603f69-ec7e-4e9b-803f-600b9205576f", Name: "json_rack_instance", } pages := 0 err := configurations.ListInstances(fake.ServiceClient(), configID).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := instances.ExtractInstances(page) if err != nil { return false, err } th.AssertDeepEquals(t, actual, []instances.Instance{expectedInstance}) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, pages) } func TestListDSParams(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() fixture.SetupHandler(t, dsParamListURL, "GET", "", ListParamsJSON, 200) pages := 0 err := configurations.ListDatastoreParams(fake.ServiceClient(), dsID, versionID).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := configurations.ExtractParams(page) if err != nil { return false, err } expected := []configurations.Param{ {Max: 1, Min: 0, Name: "innodb_file_per_table", RestartRequired: true, Type: "integer"}, {Max: 4294967296, Min: 0, Name: "key_buffer_size", RestartRequired: false, Type: "integer"}, {Max: 65535, Min: 2, Name: "connect_timeout", RestartRequired: false, Type: "integer"}, {Max: 4294967296, Min: 0, Name: "join_buffer_size", RestartRequired: false, Type: "integer"}, } th.AssertDeepEquals(t, actual, expected) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, pages) } func TestGetDSParam(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() fixture.SetupHandler(t, dsParamGetURL, "GET", "", GetParamJSON, 200) param, err := configurations.GetDatastoreParam(fake.ServiceClient(), dsID, versionID, paramID).Extract() th.AssertNoErr(t, err) expected := &configurations.Param{ Max: 1, Min: 0, Name: "innodb_file_per_table", RestartRequired: true, Type: "integer", } th.AssertDeepEquals(t, expected, param) } func TestListGlobalParams(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() fixture.SetupHandler(t, globalParamListURL, "GET", "", ListParamsJSON, 200) pages := 0 err := configurations.ListGlobalParams(fake.ServiceClient(), versionID).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := configurations.ExtractParams(page) if err != nil { return false, err } expected := []configurations.Param{ {Max: 1, Min: 0, Name: "innodb_file_per_table", RestartRequired: true, Type: "integer"}, {Max: 4294967296, Min: 0, Name: "key_buffer_size", RestartRequired: false, Type: "integer"}, {Max: 65535, Min: 2, Name: "connect_timeout", RestartRequired: false, Type: "integer"}, {Max: 4294967296, Min: 0, Name: "join_buffer_size", RestartRequired: false, Type: "integer"}, } th.AssertDeepEquals(t, actual, expected) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, pages) } func TestGetGlobalParam(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() fixture.SetupHandler(t, globalParamGetURL, "GET", "", GetParamJSON, 200) param, err := configurations.GetGlobalParam(fake.ServiceClient(), versionID, paramID).Extract() th.AssertNoErr(t, err) expected := &configurations.Param{ Max: 1, Min: 0, Name: "innodb_file_per_table", RestartRequired: true, Type: "integer", } th.AssertDeepEquals(t, expected, param) } urls.go000066400000000000000000000021521335005366400327450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/configurationspackage configurations import "github.com/gophercloud/gophercloud" func baseURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("configurations") } func resourceURL(c *gophercloud.ServiceClient, configID string) string { return c.ServiceURL("configurations", configID) } func instancesURL(c *gophercloud.ServiceClient, configID string) string { return c.ServiceURL("configurations", configID, "instances") } func listDSParamsURL(c *gophercloud.ServiceClient, datastoreID, versionID string) string { return c.ServiceURL("datastores", datastoreID, "versions", versionID, "parameters") } func getDSParamURL(c *gophercloud.ServiceClient, datastoreID, versionID, paramID string) string { return c.ServiceURL("datastores", datastoreID, "versions", versionID, "parameters", paramID) } func listGlobalParamsURL(c *gophercloud.ServiceClient, versionID string) string { return c.ServiceURL("datastores", "versions", versionID, "parameters") } func getGlobalParamURL(c *gophercloud.ServiceClient, versionID, paramID string) string { return c.ServiceURL("datastores", "versions", versionID, "parameters", paramID) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/databases/000077500000000000000000000000001335005366400304055ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/databases/doc.go000066400000000000000000000003611335005366400315010ustar00rootroot00000000000000// Package flavors provides information and interaction with the database API // resource in the OpenStack Database service. // // A database, when referred to here, refers to the database engine running on // an instance. package databases golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/databases/requests.go000066400000000000000000000064641335005366400326210ustar00rootroot00000000000000package databases import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder builds create options type CreateOptsBuilder interface { ToDBCreateMap() (map[string]interface{}, error) } // CreateOpts is the struct responsible for configuring a database; often in // the context of an instance. type CreateOpts struct { // Specifies the name of the database. Valid names can be composed // of the following characters: letters (either case); numbers; these // characters '@', '?', '#', ' ' but NEVER beginning a name string; '_' is // permitted anywhere. Prohibited characters that are forbidden include: // single quotes, double quotes, back quotes, semicolons, commas, backslashes, // and forward slashes. Name string `json:"name" required:"true"` // Set of symbols and encodings. The default character set is // "utf8". See http://dev.mysql.com/doc/refman/5.1/en/charset-mysql.html for // supported character sets. CharSet string `json:"character_set,omitempty"` // Set of rules for comparing characters in a character set. The // default value for collate is "utf8_general_ci". See // http://dev.mysql.com/doc/refman/5.1/en/charset-mysql.html for supported // collations. Collate string `json:"collate,omitempty"` } // ToMap is a helper function to convert individual DB create opt structures // into sub-maps. func (opts CreateOpts) ToMap() (map[string]interface{}, error) { if len(opts.Name) > 64 { err := gophercloud.ErrInvalidInput{} err.Argument = "databases.CreateOpts.Name" err.Value = opts.Name err.Info = "Must be less than 64 chars long" return nil, err } return gophercloud.BuildRequestBody(opts, "") } // BatchCreateOpts allows for multiple databases to created and modified. type BatchCreateOpts []CreateOpts // ToDBCreateMap renders a JSON map for creating DBs. func (opts BatchCreateOpts) ToDBCreateMap() (map[string]interface{}, error) { dbs := make([]map[string]interface{}, len(opts)) for i, db := range opts { dbMap, err := db.ToMap() if err != nil { return nil, err } dbs[i] = dbMap } return map[string]interface{}{"databases": dbs}, nil } // Create will create a new database within the specified instance. If the // specified instance does not exist, a 404 error will be returned. func Create(client *gophercloud.ServiceClient, instanceID string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToDBCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(baseURL(client, instanceID), &b, nil, nil) return } // List will list all of the databases for a specified instance. Note: this // operation will only return user-defined databases; it will exclude system // databases like "mysql", "information_schema", "lost+found" etc. func List(client *gophercloud.ServiceClient, instanceID string) pagination.Pager { return pagination.NewPager(client, baseURL(client, instanceID), func(r pagination.PageResult) pagination.Page { return DBPage{pagination.LinkedPageBase{PageResult: r}} }) } // Delete will permanently delete the database within a specified instance. // All contained data inside the database will also be permanently deleted. func Delete(client *gophercloud.ServiceClient, instanceID, dbName string) (r DeleteResult) { _, r.Err = client.Delete(dbURL(client, instanceID, dbName), nil) return } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/databases/results.go000066400000000000000000000030221335005366400324320ustar00rootroot00000000000000package databases import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Database represents a Database API resource. type Database struct { // Specifies the name of the MySQL database. Name string // Set of symbols and encodings. The default character set is utf8. CharSet string // Set of rules for comparing characters in a character set. The default // value for collate is utf8_general_ci. Collate string } // CreateResult represents the result of a Create operation. type CreateResult struct { gophercloud.ErrResult } // DeleteResult represents the result of a Delete operation. type DeleteResult struct { gophercloud.ErrResult } // DBPage represents a single page of a paginated DB collection. type DBPage struct { pagination.LinkedPageBase } // IsEmpty checks to see whether the collection is empty. func (page DBPage) IsEmpty() (bool, error) { dbs, err := ExtractDBs(page) return len(dbs) == 0, err } // NextPageURL will retrieve the next page URL. func (page DBPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"databases_links"` } err := page.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // ExtractDBs will convert a generic pagination struct into a more // relevant slice of DB structs. func ExtractDBs(page pagination.Page) ([]Database, error) { r := page.(DBPage) var s struct { Databases []Database `json:"databases"` } err := r.ExtractInto(&s) return s.Databases, err } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/databases/testing/000077500000000000000000000000001335005366400320625ustar00rootroot00000000000000doc.go000066400000000000000000000000431335005366400330740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/databases/testing// db_databases_v1 package testing fixtures.go000066400000000000000000000015531335005366400342070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/databases/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/testhelper/fixture" ) var ( instanceID = "{instanceID}" resURL = "/instances/" + instanceID + "/databases" ) var createDBsReq = ` { "databases": [ { "character_set": "utf8", "collate": "utf8_general_ci", "name": "testingdb" }, { "name": "sampledb" } ] } ` var listDBsResp = ` { "databases": [ { "name": "anotherexampledb" }, { "name": "exampledb" }, { "name": "nextround" }, { "name": "sampledb" }, { "name": "testingdb" } ] } ` func HandleCreate(t *testing.T) { fixture.SetupHandler(t, resURL, "POST", createDBsReq, "", 202) } func HandleList(t *testing.T) { fixture.SetupHandler(t, resURL, "GET", "", listDBsResp, 200) } func HandleDelete(t *testing.T) { fixture.SetupHandler(t, resURL+"/{dbName}", "DELETE", "", "", 202) } requests_test.go000066400000000000000000000026621335005366400352520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/databases/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/db/v1/databases" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreate(t) opts := databases.BatchCreateOpts{ databases.CreateOpts{Name: "testingdb", CharSet: "utf8", Collate: "utf8_general_ci"}, databases.CreateOpts{Name: "sampledb"}, } res := databases.Create(fake.ServiceClient(), instanceID, opts) th.AssertNoErr(t, res.Err) } func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleList(t) expectedDBs := []databases.Database{ {Name: "anotherexampledb"}, {Name: "exampledb"}, {Name: "nextround"}, {Name: "sampledb"}, {Name: "testingdb"}, } pages := 0 err := databases.List(fake.ServiceClient(), instanceID).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := databases.ExtractDBs(page) if err != nil { return false, err } th.CheckDeepEquals(t, expectedDBs, actual) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDelete(t) err := databases.Delete(fake.ServiceClient(), instanceID, "{dbName}").ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/databases/urls.go000066400000000000000000000005271335005366400317250ustar00rootroot00000000000000package databases import "github.com/gophercloud/gophercloud" func baseURL(c *gophercloud.ServiceClient, instanceID string) string { return c.ServiceURL("instances", instanceID, "databases") } func dbURL(c *gophercloud.ServiceClient, instanceID, dbName string) string { return c.ServiceURL("instances", instanceID, "databases", dbName) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/datastores/000077500000000000000000000000001335005366400306275ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/datastores/doc.go000066400000000000000000000002241335005366400317210ustar00rootroot00000000000000// Package datastores provides information and interaction with the datastore // API resource in the Rackspace Database service. package datastores requests.go000066400000000000000000000023671335005366400327620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/datastorespackage datastores import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List will list all available datastore types that instances can use. func List(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, baseURL(client), func(r pagination.PageResult) pagination.Page { return DatastorePage{pagination.SinglePageBase(r)} }) } // Get will retrieve the details of a specified datastore type. func Get(client *gophercloud.ServiceClient, datastoreID string) (r GetResult) { _, r.Err = client.Get(resourceURL(client, datastoreID), &r.Body, nil) return } // ListVersions will list all of the available versions for a specified // datastore type. func ListVersions(client *gophercloud.ServiceClient, datastoreID string) pagination.Pager { return pagination.NewPager(client, versionsURL(client, datastoreID), func(r pagination.PageResult) pagination.Page { return VersionPage{pagination.SinglePageBase(r)} }) } // GetVersion will retrieve the details of a specified datastore version. func GetVersion(client *gophercloud.ServiceClient, datastoreID, versionID string) (r GetVersionResult) { _, r.Err = client.Get(versionURL(client, datastoreID, versionID), &r.Body, nil) return } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/datastores/results.go000066400000000000000000000051621335005366400326630ustar00rootroot00000000000000package datastores import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Version represents a version API resource. Multiple versions belong to a Datastore. type Version struct { ID string Links []gophercloud.Link Name string } // Datastore represents a Datastore API resource. type Datastore struct { DefaultVersion string `json:"default_version"` ID string Links []gophercloud.Link Name string Versions []Version } // DatastorePartial is a meta structure which is used in various API responses. // It is a lightweight and truncated version of a full Datastore resource, // offering details of the Version, Type and VersionID only. type DatastorePartial struct { Version string Type string VersionID string `json:"version_id"` } // GetResult represents the result of a Get operation. type GetResult struct { gophercloud.Result } // GetVersionResult represents the result of getting a version. type GetVersionResult struct { gophercloud.Result } // DatastorePage represents a page of datastore resources. type DatastorePage struct { pagination.SinglePageBase } // IsEmpty indicates whether a Datastore collection is empty. func (r DatastorePage) IsEmpty() (bool, error) { is, err := ExtractDatastores(r) return len(is) == 0, err } // ExtractDatastores retrieves a slice of datastore structs from a paginated // collection. func ExtractDatastores(r pagination.Page) ([]Datastore, error) { var s struct { Datastores []Datastore `json:"datastores"` } err := (r.(DatastorePage)).ExtractInto(&s) return s.Datastores, err } // Extract retrieves a single Datastore struct from an operation result. func (r GetResult) Extract() (*Datastore, error) { var s struct { Datastore *Datastore `json:"datastore"` } err := r.ExtractInto(&s) return s.Datastore, err } // VersionPage represents a page of version resources. type VersionPage struct { pagination.SinglePageBase } // IsEmpty indicates whether a collection of version resources is empty. func (r VersionPage) IsEmpty() (bool, error) { is, err := ExtractVersions(r) return len(is) == 0, err } // ExtractVersions retrieves a slice of versions from a paginated collection. func ExtractVersions(r pagination.Page) ([]Version, error) { var s struct { Versions []Version `json:"versions"` } err := (r.(VersionPage)).ExtractInto(&s) return s.Versions, err } // Extract retrieves a single Version struct from an operation result. func (r GetVersionResult) Extract() (*Version, error) { var s struct { Version *Version `json:"version"` } err := r.ExtractInto(&s) return s.Version, err } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/datastores/testing/000077500000000000000000000000001335005366400323045ustar00rootroot00000000000000doc.go000066400000000000000000000000441335005366400333170ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/datastores/testing// db_datastores_v1 package testing fixtures.go000066400000000000000000000056141335005366400344330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/datastores/testingpackage testing import ( "fmt" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/db/v1/datastores" ) const version1JSON = ` { "id": "b00000b0-00b0-0b00-00b0-000b000000bb", "links": [ { "href": "https://10.240.28.38:8779/v1.0/1234/datastores/versions/b00000b0-00b0-0b00-00b0-000b000000bb", "rel": "self" }, { "href": "https://10.240.28.38:8779/datastores/versions/b00000b0-00b0-0b00-00b0-000b000000bb", "rel": "bookmark" } ], "name": "5.1" } ` const version2JSON = ` { "id": "c00000b0-00c0-0c00-00c0-000b000000cc", "links": [ { "href": "https://10.240.28.38:8779/v1.0/1234/datastores/versions/c00000b0-00c0-0c00-00c0-000b000000cc", "rel": "self" }, { "href": "https://10.240.28.38:8779/datastores/versions/c00000b0-00c0-0c00-00c0-000b000000cc", "rel": "bookmark" } ], "name": "5.2" } ` var versionsJSON = fmt.Sprintf(`"versions": [%s, %s]`, version1JSON, version2JSON) var singleDSJSON = fmt.Sprintf(` { "default_version": "c00000b0-00c0-0c00-00c0-000b000000cc", "id": "10000000-0000-0000-0000-000000000001", "links": [ { "href": "https://10.240.28.38:8779/v1.0/1234/datastores/10000000-0000-0000-0000-000000000001", "rel": "self" }, { "href": "https://10.240.28.38:8779/datastores/10000000-0000-0000-0000-000000000001", "rel": "bookmark" } ], "name": "mysql", %s } `, versionsJSON) var ( ListDSResp = fmt.Sprintf(`{"datastores":[%s]}`, singleDSJSON) GetDSResp = fmt.Sprintf(`{"datastore":%s}`, singleDSJSON) ListVersionsResp = fmt.Sprintf(`{%s}`, versionsJSON) GetVersionResp = fmt.Sprintf(`{"version":%s}`, version1JSON) ) var ExampleVersion1 = datastores.Version{ ID: "b00000b0-00b0-0b00-00b0-000b000000bb", Links: []gophercloud.Link{ {Rel: "self", Href: "https://10.240.28.38:8779/v1.0/1234/datastores/versions/b00000b0-00b0-0b00-00b0-000b000000bb"}, {Rel: "bookmark", Href: "https://10.240.28.38:8779/datastores/versions/b00000b0-00b0-0b00-00b0-000b000000bb"}, }, Name: "5.1", } var exampleVersion2 = datastores.Version{ ID: "c00000b0-00c0-0c00-00c0-000b000000cc", Links: []gophercloud.Link{ {Rel: "self", Href: "https://10.240.28.38:8779/v1.0/1234/datastores/versions/c00000b0-00c0-0c00-00c0-000b000000cc"}, {Rel: "bookmark", Href: "https://10.240.28.38:8779/datastores/versions/c00000b0-00c0-0c00-00c0-000b000000cc"}, }, Name: "5.2", } var ExampleVersions = []datastores.Version{ExampleVersion1, exampleVersion2} var ExampleDatastore = datastores.Datastore{ DefaultVersion: "c00000b0-00c0-0c00-00c0-000b000000cc", ID: "10000000-0000-0000-0000-000000000001", Links: []gophercloud.Link{ {Rel: "self", Href: "https://10.240.28.38:8779/v1.0/1234/datastores/10000000-0000-0000-0000-000000000001"}, {Rel: "bookmark", Href: "https://10.240.28.38:8779/datastores/10000000-0000-0000-0000-000000000001"}, }, Name: "mysql", Versions: ExampleVersions, } requests_test.go000066400000000000000000000037231335005366400354730ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/datastores/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/db/v1/datastores" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" "github.com/gophercloud/gophercloud/testhelper/fixture" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() fixture.SetupHandler(t, "/datastores", "GET", "", ListDSResp, 200) pages := 0 err := datastores.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := datastores.ExtractDatastores(page) if err != nil { return false, err } th.CheckDeepEquals(t, []datastores.Datastore{ExampleDatastore}, actual) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, pages) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() fixture.SetupHandler(t, "/datastores/{dsID}", "GET", "", GetDSResp, 200) ds, err := datastores.Get(fake.ServiceClient(), "{dsID}").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &ExampleDatastore, ds) } func TestListVersions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() fixture.SetupHandler(t, "/datastores/{dsID}/versions", "GET", "", ListVersionsResp, 200) pages := 0 err := datastores.ListVersions(fake.ServiceClient(), "{dsID}").EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := datastores.ExtractVersions(page) if err != nil { return false, err } th.CheckDeepEquals(t, ExampleVersions, actual) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, pages) } func TestGetVersion(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() fixture.SetupHandler(t, "/datastores/{dsID}/versions/{versionID}", "GET", "", GetVersionResp, 200) ds, err := datastores.GetVersion(fake.ServiceClient(), "{dsID}", "{versionID}").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &ExampleVersion1, ds) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/datastores/urls.go000066400000000000000000000010321335005366400321370ustar00rootroot00000000000000package datastores import "github.com/gophercloud/gophercloud" func baseURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("datastores") } func resourceURL(c *gophercloud.ServiceClient, dsID string) string { return c.ServiceURL("datastores", dsID) } func versionsURL(c *gophercloud.ServiceClient, dsID string) string { return c.ServiceURL("datastores", dsID, "versions") } func versionURL(c *gophercloud.ServiceClient, dsID, versionID string) string { return c.ServiceURL("datastores", dsID, "versions", versionID) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/flavors/000077500000000000000000000000001335005366400301325ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/flavors/doc.go000066400000000000000000000004771335005366400312360ustar00rootroot00000000000000// Package flavors provides information and interaction with the flavor API // resource in the OpenStack Database service. // // A flavor is an available hardware configuration for a database instance. // Each flavor has a unique combination of disk space, memory capacity and // priority for CPU time. package flavors golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/flavors/requests.go000066400000000000000000000013351335005366400323360ustar00rootroot00000000000000package flavors import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List will list all available hardware flavors that an instance can use. The // operation is identical to the one supported by the Nova API, but without the // "disk" property. func List(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { return FlavorPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get will retrieve information for a specified hardware flavor. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/flavors/results.go000066400000000000000000000035421335005366400321660ustar00rootroot00000000000000package flavors import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // GetResult temporarily holds the response from a Get call. type GetResult struct { gophercloud.Result } // Extract provides access to the individual Flavor returned by the Get function. func (r GetResult) Extract() (*Flavor, error) { var s struct { Flavor *Flavor `json:"flavor"` } err := r.ExtractInto(&s) return s.Flavor, err } // Flavor records represent (virtual) hardware configurations for server resources in a region. type Flavor struct { // The flavor's unique identifier. // Contains 0 if the ID is not an integer. ID int `json:"id"` // The RAM capacity for the flavor. RAM int `json:"ram"` // The Name field provides a human-readable moniker for the flavor. Name string `json:"name"` // Links to access the flavor. Links []gophercloud.Link // The flavor's unique identifier as a string StrID string `json:"str_id"` } // FlavorPage contains a single page of the response from a List call. type FlavorPage struct { pagination.LinkedPageBase } // IsEmpty determines if a page contains any results. func (page FlavorPage) IsEmpty() (bool, error) { flavors, err := ExtractFlavors(page) return len(flavors) == 0, err } // NextPageURL uses the response's embedded link reference to navigate to the next page of results. func (page FlavorPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"flavors_links"` } err := page.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // ExtractFlavors provides access to the list of flavors in a page acquired from the List operation. func ExtractFlavors(r pagination.Page) ([]Flavor, error) { var s struct { Flavors []Flavor `json:"flavors"` } err := (r.(FlavorPage)).ExtractInto(&s) return s.Flavors, err } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/flavors/testing/000077500000000000000000000000001335005366400316075ustar00rootroot00000000000000doc.go000066400000000000000000000000411335005366400326170ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/flavors/testing// db_flavors_v1 package testing fixtures.go000066400000000000000000000022511335005366400337300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/flavors/testingpackage testing import ( "fmt" "testing" "github.com/gophercloud/gophercloud/testhelper/fixture" ) const flavor = ` { "id": %s, "links": [ { "href": "https://openstack.example.com/v1.0/1234/flavors/%s", "rel": "self" }, { "href": "https://openstack.example.com/flavors/%s", "rel": "bookmark" } ], "name": "%s", "ram": %d, "str_id": "%s" } ` var ( flavorID = "{flavorID}" _baseURL = "/flavors" resURL = "/flavors/" + flavorID ) var ( flavor1 = fmt.Sprintf(flavor, "1", "1", "1", "m1.tiny", 512, "1") flavor2 = fmt.Sprintf(flavor, "2", "2", "2", "m1.small", 1024, "2") flavor3 = fmt.Sprintf(flavor, "3", "3", "3", "m1.medium", 2048, "3") flavor4 = fmt.Sprintf(flavor, "4", "4", "4", "m1.large", 4096, "4") flavor5 = fmt.Sprintf(flavor, "null", "d1", "d1", "ds512M", 512, "d1") listFlavorsResp = fmt.Sprintf(`{"flavors":[%s, %s, %s, %s, %s]}`, flavor1, flavor2, flavor3, flavor4, flavor5) getFlavorResp = fmt.Sprintf(`{"flavor": %s}`, flavor1) ) func HandleList(t *testing.T) { fixture.SetupHandler(t, _baseURL, "GET", "", listFlavorsResp, 200) } func HandleGet(t *testing.T) { fixture.SetupHandler(t, resURL, "GET", "", getFlavorResp, 200) } requests_test.go000066400000000000000000000047511335005366400350000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/flavors/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/db/v1/flavors" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListFlavors(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleList(t) pages := 0 err := flavors.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := flavors.ExtractFlavors(page) if err != nil { return false, err } expected := []flavors.Flavor{ { ID: 1, Name: "m1.tiny", RAM: 512, Links: []gophercloud.Link{ {Href: "https://openstack.example.com/v1.0/1234/flavors/1", Rel: "self"}, {Href: "https://openstack.example.com/flavors/1", Rel: "bookmark"}, }, StrID: "1", }, { ID: 2, Name: "m1.small", RAM: 1024, Links: []gophercloud.Link{ {Href: "https://openstack.example.com/v1.0/1234/flavors/2", Rel: "self"}, {Href: "https://openstack.example.com/flavors/2", Rel: "bookmark"}, }, StrID: "2", }, { ID: 3, Name: "m1.medium", RAM: 2048, Links: []gophercloud.Link{ {Href: "https://openstack.example.com/v1.0/1234/flavors/3", Rel: "self"}, {Href: "https://openstack.example.com/flavors/3", Rel: "bookmark"}, }, StrID: "3", }, { ID: 4, Name: "m1.large", RAM: 4096, Links: []gophercloud.Link{ {Href: "https://openstack.example.com/v1.0/1234/flavors/4", Rel: "self"}, {Href: "https://openstack.example.com/flavors/4", Rel: "bookmark"}, }, StrID: "4", }, { ID: 0, Name: "ds512M", RAM: 512, Links: []gophercloud.Link{ {Href: "https://openstack.example.com/v1.0/1234/flavors/d1", Rel: "self"}, {Href: "https://openstack.example.com/flavors/d1", Rel: "bookmark"}, }, StrID: "d1", }, } th.AssertDeepEquals(t, expected, actual) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, pages) } func TestGetFlavor(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGet(t) actual, err := flavors.Get(fake.ServiceClient(), flavorID).Extract() th.AssertNoErr(t, err) expected := &flavors.Flavor{ ID: 1, Name: "m1.tiny", RAM: 512, Links: []gophercloud.Link{ {Href: "https://openstack.example.com/v1.0/1234/flavors/1", Rel: "self"}, }, StrID: "1", } th.AssertDeepEquals(t, expected, actual) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/flavors/urls.go000066400000000000000000000004151335005366400314460ustar00rootroot00000000000000package flavors import "github.com/gophercloud/gophercloud" func getURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("flavors", id) } func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("flavors") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/instances/000077500000000000000000000000001335005366400304455ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/instances/doc.go000066400000000000000000000004721335005366400315440ustar00rootroot00000000000000// Package instances provides information and interaction with the instance API // resource in the OpenStack Database service. // // A database instance is an isolated database environment with compute and // storage resources in a single tenant environment on a shared physical host // machine. package instances golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/instances/requests.go000066400000000000000000000201231335005366400326450ustar00rootroot00000000000000package instances import ( "github.com/gophercloud/gophercloud" db "github.com/gophercloud/gophercloud/openstack/db/v1/databases" "github.com/gophercloud/gophercloud/openstack/db/v1/users" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder is the top-level interface for create options. type CreateOptsBuilder interface { ToInstanceCreateMap() (map[string]interface{}, error) } // DatastoreOpts represents the configuration for how an instance stores data. type DatastoreOpts struct { Version string `json:"version"` Type string `json:"type"` } // ToMap converts a DatastoreOpts to a map[string]string (for a request body) func (opts DatastoreOpts) ToMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // NetworkOpts is used within CreateOpts to control a new server's network attachments. type NetworkOpts struct { // UUID of a nova-network to attach to the newly provisioned server. // Required unless Port is provided. UUID string `json:"net-id,omitempty"` // Port of a neutron network to attach to the newly provisioned server. // Required unless UUID is provided. Port string `json:"port-id,omitempty"` // V4FixedIP [optional] specifies a fixed IPv4 address to be used on this network. V4FixedIP string `json:"v4-fixed-ip,omitempty"` // V6FixedIP [optional] specifies a fixed IPv6 address to be used on this network. V6FixedIP string `json:"v6-fixed-ip,omitempty"` } // ToMap converts a NetworkOpts to a map[string]string (for a request body) func (opts NetworkOpts) ToMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // CreateOpts is the struct responsible for configuring a new database instance. type CreateOpts struct { // Either the integer UUID (in string form) of the flavor, or its URI // reference as specified in the response from the List() call. Required. FlavorRef string // Specifies the volume size in gigabytes (GB). The value must be between 1 // and 300. Required. Size int // Name of the instance to create. The length of the name is limited to // 255 characters and any characters are permitted. Optional. Name string // A slice of database information options. Databases db.CreateOptsBuilder // A slice of user information options. Users users.CreateOptsBuilder // Options to configure the type of datastore the instance will use. This is // optional, and if excluded will default to MySQL. Datastore *DatastoreOpts // Networks dictates how this server will be attached to available networks. Networks []NetworkOpts } // ToInstanceCreateMap will render a JSON map. func (opts CreateOpts) ToInstanceCreateMap() (map[string]interface{}, error) { if opts.Size > 300 || opts.Size < 1 { err := gophercloud.ErrInvalidInput{} err.Argument = "instances.CreateOpts.Size" err.Value = opts.Size err.Info = "Size (GB) must be between 1-300" return nil, err } if opts.FlavorRef == "" { return nil, gophercloud.ErrMissingInput{Argument: "instances.CreateOpts.FlavorRef"} } instance := map[string]interface{}{ "volume": map[string]int{"size": opts.Size}, "flavorRef": opts.FlavorRef, } if opts.Name != "" { instance["name"] = opts.Name } if opts.Databases != nil { dbs, err := opts.Databases.ToDBCreateMap() if err != nil { return nil, err } instance["databases"] = dbs["databases"] } if opts.Users != nil { users, err := opts.Users.ToUserCreateMap() if err != nil { return nil, err } instance["users"] = users["users"] } if opts.Datastore != nil { datastore, err := opts.Datastore.ToMap() if err != nil { return nil, err } instance["datastore"] = datastore } if len(opts.Networks) > 0 { networks := make([]map[string]interface{}, len(opts.Networks)) for i, net := range opts.Networks { var err error networks[i], err = net.ToMap() if err != nil { return nil, err } } instance["nics"] = networks } return map[string]interface{}{"instance": instance}, nil } // Create asynchronously provisions a new database instance. It requires the // user to specify a flavor and a volume size. The API service then provisions // the instance with the requested flavor and sets up a volume of the specified // size, which is the storage for the database instance. // // Although this call only allows the creation of 1 instance per request, you // can create an instance with multiple databases and users. The default // binding for a MySQL instance is port 3306. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToInstanceCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) return } // List retrieves the status and information for all database instances. func List(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, baseURL(client), func(r pagination.PageResult) pagination.Page { return InstancePage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves the status and information for a specified database instance. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(resourceURL(client, id), &r.Body, nil) return } // Delete permanently destroys the database instance. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(resourceURL(client, id), nil) return } // EnableRootUser enables the login from any host for the root user and // provides the user with a generated root password. func EnableRootUser(client *gophercloud.ServiceClient, id string) (r EnableRootUserResult) { _, r.Err = client.Post(userRootURL(client, id), nil, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) return } // IsRootEnabled checks an instance to see if root access is enabled. It returns // True if root user is enabled for the specified database instance or False // otherwise. func IsRootEnabled(client *gophercloud.ServiceClient, id string) (r IsRootEnabledResult) { _, r.Err = client.Get(userRootURL(client, id), &r.Body, nil) return } // Restart will restart only the MySQL Instance. Restarting MySQL will // erase any dynamic configuration settings that you have made within MySQL. // The MySQL service will be unavailable until the instance restarts. func Restart(client *gophercloud.ServiceClient, id string) (r ActionResult) { b := map[string]interface{}{"restart": struct{}{}} _, r.Err = client.Post(actionURL(client, id), &b, nil, nil) return } // Resize changes the memory size of the instance, assuming a valid // flavorRef is provided. It will also restart the MySQL service. func Resize(client *gophercloud.ServiceClient, id, flavorRef string) (r ActionResult) { b := map[string]interface{}{"resize": map[string]string{"flavorRef": flavorRef}} _, r.Err = client.Post(actionURL(client, id), &b, nil, nil) return } // ResizeVolume will resize the attached volume for an instance. It supports // only increasing the volume size and does not support decreasing the size. // The volume size is in gigabytes (GB) and must be an integer. func ResizeVolume(client *gophercloud.ServiceClient, id string, size int) (r ActionResult) { b := map[string]interface{}{"resize": map[string]interface{}{"volume": map[string]int{"size": size}}} _, r.Err = client.Post(actionURL(client, id), &b, nil, nil) return } // AttachConfigurationGroup will attach configuration group to the instance func AttachConfigurationGroup(client *gophercloud.ServiceClient, instanceID string, configID string) (r ConfigurationResult) { b := map[string]interface{}{"instance": map[string]interface{}{"configuration": configID}} _, r.Err = client.Put(resourceURL(client, instanceID), &b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) return } // DetachConfigurationGroup will dettach configuration group from the instance func DetachConfigurationGroup(client *gophercloud.ServiceClient, instanceID string) (r ConfigurationResult) { b := map[string]interface{}{"instance": map[string]interface{}{}} _, r.Err = client.Put(resourceURL(client, instanceID), &b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) return } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/instances/results.go000066400000000000000000000131541335005366400325010ustar00rootroot00000000000000package instances import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/db/v1/datastores" "github.com/gophercloud/gophercloud/openstack/db/v1/users" "github.com/gophercloud/gophercloud/pagination" ) // Volume represents information about an attached volume for a database instance. type Volume struct { // The size in GB of the volume Size int Used float64 } // Flavor represents (virtual) hardware configurations for server resources in a region. type Flavor struct { // The flavor's unique identifier. ID string // Links to access the flavor. Links []gophercloud.Link } // Fault describes the fault reason in more detail when a database instance has errored type Fault struct { // Indicates the time when the fault occured Created time.Time `json:"-"` // A message describing the fault reason Message string // More details about the fault, for example a stack trace. Only filled // in for admin users. Details string } func (r *Fault) UnmarshalJSON(b []byte) error { type tmp Fault var s struct { tmp Created gophercloud.JSONRFC3339NoZ `json:"created"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Fault(s.tmp) r.Created = time.Time(s.Created) return nil } // Instance represents a remote MySQL instance. type Instance struct { // Indicates the datetime that the instance was created Created time.Time `json:"-"` // Indicates the most recent datetime that the instance was updated. Updated time.Time `json:"-"` // Indicates the hardware flavor the instance uses. Flavor Flavor // A DNS-resolvable hostname associated with the database instance (rather // than an IPv4 address). Since the hostname always resolves to the correct // IP address of the database instance, this relieves the user from the task // of maintaining the mapping. Note that although the IP address may likely // change on resizing, migrating, and so forth, the hostname always resolves // to the correct database instance. Hostname string // The IP addresses associated with the database instance // Is empty if the instance has a hostname IP []string // Indicates the unique identifier for the instance resource. ID string // Exposes various links that reference the instance resource. Links []gophercloud.Link // The human-readable name of the instance. Name string // The build status of the instance. Status string // Fault information (only available when the instance has errored) Fault *Fault // Information about the attached volume of the instance. Volume Volume // Indicates how the instance stores data. Datastore datastores.DatastorePartial } func (r *Instance) UnmarshalJSON(b []byte) error { type tmp Instance var s struct { tmp Created gophercloud.JSONRFC3339NoZ `json:"created"` Updated gophercloud.JSONRFC3339NoZ `json:"updated"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Instance(s.tmp) r.Created = time.Time(s.Created) r.Updated = time.Time(s.Updated) return nil } type commonResult struct { gophercloud.Result } // CreateResult represents the result of a Create operation. type CreateResult struct { commonResult } // GetResult represents the result of a Get operation. type GetResult struct { commonResult } // DeleteResult represents the result of a Delete operation. type DeleteResult struct { gophercloud.ErrResult } // ConfigurationResult represents the result of a AttachConfigurationGroup/DetachConfigurationGroup operation. type ConfigurationResult struct { gophercloud.ErrResult } // Extract will extract an Instance from various result structs. func (r commonResult) Extract() (*Instance, error) { var s struct { Instance *Instance `json:"instance"` } err := r.ExtractInto(&s) return s.Instance, err } // InstancePage represents a single page of a paginated instance collection. type InstancePage struct { pagination.LinkedPageBase } // IsEmpty checks to see whether the collection is empty. func (page InstancePage) IsEmpty() (bool, error) { instances, err := ExtractInstances(page) return len(instances) == 0, err } // NextPageURL will retrieve the next page URL. func (page InstancePage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"instances_links"` } err := page.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // ExtractInstances will convert a generic pagination struct into a more // relevant slice of Instance structs. func ExtractInstances(r pagination.Page) ([]Instance, error) { var s struct { Instances []Instance `json:"instances"` } err := (r.(InstancePage)).ExtractInto(&s) return s.Instances, err } // EnableRootUserResult represents the result of an operation to enable the root user. type EnableRootUserResult struct { gophercloud.Result } // Extract will extract root user information from a UserRootResult. func (r EnableRootUserResult) Extract() (*users.User, error) { var s struct { User *users.User `json:"user"` } err := r.ExtractInto(&s) return s.User, err } // ActionResult represents the result of action requests, such as: restarting // an instance service, resizing its memory allocation, and resizing its // attached volume size. type ActionResult struct { gophercloud.ErrResult } // IsRootEnabledResult is the result of a call to IsRootEnabled. To see if // root is enabled, call the type's Extract method. type IsRootEnabledResult struct { gophercloud.Result } // Extract is used to extract the data from a IsRootEnabledResult. func (r IsRootEnabledResult) Extract() (bool, error) { return r.Body.(map[string]interface{})["rootEnabled"] == true, r.Err } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/instances/testing/000077500000000000000000000000001335005366400321225ustar00rootroot00000000000000doc.go000066400000000000000000000000431335005366400331340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/instances/testing// db_instances_v1 package testing fixtures.go000066400000000000000000000141301335005366400342420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/instances/testingpackage testing import ( "fmt" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/db/v1/datastores" "github.com/gophercloud/gophercloud/openstack/db/v1/instances" "github.com/gophercloud/gophercloud/testhelper/fixture" ) var ( timestamp = "2015-11-12T14:22:42" timeVal, _ = time.Parse(gophercloud.RFC3339NoZ, timestamp) ) var instance = ` { "created": "` + timestamp + `", "datastore": { "type": "mysql", "version": "5.6" }, "flavor": { "id": "1", "links": [ { "href": "https://openstack.example.com/v1.0/1234/flavors/1", "rel": "self" }, { "href": "https://openstack.example.com/v1.0/1234/flavors/1", "rel": "bookmark" } ] }, "links": [ { "href": "https://openstack.example.com/v1.0/1234/instances/1", "rel": "self" } ], "hostname": "e09ad9a3f73309469cf1f43d11e79549caf9acf2.openstack.example.com", "id": "{instanceID}", "name": "json_rack_instance", "status": "BUILD", "updated": "` + timestamp + `", "volume": { "size": 2 } } ` var createReq = ` { "instance": { "databases": [ { "character_set": "utf8", "collate": "utf8_general_ci", "name": "sampledb" }, { "name": "nextround" } ], "flavorRef": "1", "name": "json_rack_instance", "users": [ { "databases": [ { "name": "sampledb" } ], "name": "demouser", "password": "demopassword" } ], "volume": { "size": 2 } } } ` var instanceWithFault = ` { "created": "` + timestamp + `", "datastore": { "type": "mysql", "version": "5.6" }, "flavor": { "id": "1", "links": [ { "href": "https://openstack.example.com/v1.0/1234/flavors/1", "rel": "self" }, { "href": "https://openstack.example.com/v1.0/1234/flavors/1", "rel": "bookmark" } ] }, "links": [ { "href": "https://openstack.example.com/v1.0/1234/instances/1", "rel": "self" } ], "hostname": "e09ad9a3f73309469cf1f43d11e79549caf9acf2.openstack.example.com", "id": "{instanceID}", "name": "json_rack_instance", "status": "BUILD", "updated": "` + timestamp + `", "volume": { "size": 2 }, "fault": { "message": "some error message", "created": "` + timestamp + `", "details": "some details about the error" } } ` var ( instanceID = "{instanceID}" configGroupID = "00000000-0000-0000-0000-000000000000" rootURL = "/instances" resURL = rootURL + "/" + instanceID uRootURL = resURL + "/root" aURL = resURL + "/action" ) var ( restartReq = `{"restart": {}}` resizeReq = `{"resize": {"flavorRef": "2"}}` resizeVolReq = `{"resize": {"volume": {"size": 4}}}` attachConfigurationGroupReq = `{"instance": {"configuration": "00000000-0000-0000-0000-000000000000"}}` detachConfigurationGroupReq = `{"instance": {}}` ) var ( createResp = fmt.Sprintf(`{"instance": %s}`, instance) createWithFaultResp = fmt.Sprintf(`{"instance": %s}`, instanceWithFault) listInstancesResp = fmt.Sprintf(`{"instances":[%s]}`, instance) getInstanceResp = createResp enableUserResp = `{"user":{"name":"root","password":"secretsecret"}}` isUserEnabledResp = `{"rootEnabled":true}` ) var expectedInstance = instances.Instance{ Created: timeVal, Updated: timeVal, Flavor: instances.Flavor{ ID: "1", Links: []gophercloud.Link{ {Href: "https://openstack.example.com/v1.0/1234/flavors/1", Rel: "self"}, {Href: "https://openstack.example.com/v1.0/1234/flavors/1", Rel: "bookmark"}, }, }, Hostname: "e09ad9a3f73309469cf1f43d11e79549caf9acf2.openstack.example.com", ID: instanceID, Links: []gophercloud.Link{ {Href: "https://openstack.example.com/v1.0/1234/instances/1", Rel: "self"}, }, Name: "json_rack_instance", Status: "BUILD", Volume: instances.Volume{Size: 2}, Datastore: datastores.DatastorePartial{ Type: "mysql", Version: "5.6", }, } var expectedInstanceWithFault = instances.Instance{ Created: timeVal, Updated: timeVal, Flavor: instances.Flavor{ ID: "1", Links: []gophercloud.Link{ {Href: "https://openstack.example.com/v1.0/1234/flavors/1", Rel: "self"}, {Href: "https://openstack.example.com/v1.0/1234/flavors/1", Rel: "bookmark"}, }, }, Hostname: "e09ad9a3f73309469cf1f43d11e79549caf9acf2.openstack.example.com", ID: instanceID, Links: []gophercloud.Link{ {Href: "https://openstack.example.com/v1.0/1234/instances/1", Rel: "self"}, }, Name: "json_rack_instance", Status: "BUILD", Volume: instances.Volume{Size: 2}, Datastore: datastores.DatastorePartial{ Type: "mysql", Version: "5.6", }, Fault: &instances.Fault{ Created: timeVal, Message: "some error message", Details: "some details about the error", }, } func HandleCreate(t *testing.T) { fixture.SetupHandler(t, rootURL, "POST", createReq, createResp, 200) } func HandleCreateWithFault(t *testing.T) { fixture.SetupHandler(t, rootURL, "POST", createReq, createWithFaultResp, 200) } func HandleList(t *testing.T) { fixture.SetupHandler(t, rootURL, "GET", "", listInstancesResp, 200) } func HandleGet(t *testing.T) { fixture.SetupHandler(t, resURL, "GET", "", getInstanceResp, 200) } func HandleDelete(t *testing.T) { fixture.SetupHandler(t, resURL, "DELETE", "", "", 202) } func HandleEnableRoot(t *testing.T) { fixture.SetupHandler(t, uRootURL, "POST", "", enableUserResp, 200) } func HandleIsRootEnabled(t *testing.T) { fixture.SetupHandler(t, uRootURL, "GET", "", isUserEnabledResp, 200) } func HandleRestart(t *testing.T) { fixture.SetupHandler(t, aURL, "POST", restartReq, "", 202) } func HandleResize(t *testing.T) { fixture.SetupHandler(t, aURL, "POST", resizeReq, "", 202) } func HandleResizeVol(t *testing.T) { fixture.SetupHandler(t, aURL, "POST", resizeVolReq, "", 202) } func HandleAttachConfigurationGroup(t *testing.T) { fixture.SetupHandler(t, resURL, "PUT", attachConfigurationGroupReq, "", 202) } func HandleDetachConfigurationGroup(t *testing.T) { fixture.SetupHandler(t, resURL, "PUT", detachConfigurationGroupReq, "", 202) } requests_test.go000066400000000000000000000102211335005366400353000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/instances/testingpackage testing import ( "testing" db "github.com/gophercloud/gophercloud/openstack/db/v1/databases" "github.com/gophercloud/gophercloud/openstack/db/v1/instances" "github.com/gophercloud/gophercloud/openstack/db/v1/users" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreate(t) opts := instances.CreateOpts{ Name: "json_rack_instance", FlavorRef: "1", Databases: db.BatchCreateOpts{ {CharSet: "utf8", Collate: "utf8_general_ci", Name: "sampledb"}, {Name: "nextround"}, }, Users: users.BatchCreateOpts{ { Name: "demouser", Password: "demopassword", Databases: db.BatchCreateOpts{ {Name: "sampledb"}, }, }, }, Size: 2, } instance, err := instances.Create(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expectedInstance, instance) } func TestCreateWithFault(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateWithFault(t) opts := instances.CreateOpts{ Name: "json_rack_instance", FlavorRef: "1", Databases: db.BatchCreateOpts{ {CharSet: "utf8", Collate: "utf8_general_ci", Name: "sampledb"}, {Name: "nextround"}, }, Users: users.BatchCreateOpts{ { Name: "demouser", Password: "demopassword", Databases: db.BatchCreateOpts{ {Name: "sampledb"}, }, }, }, Size: 2, } instance, err := instances.Create(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expectedInstanceWithFault, instance) } func TestInstanceList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleList(t) pages := 0 err := instances.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := instances.ExtractInstances(page) if err != nil { return false, err } th.CheckDeepEquals(t, []instances.Instance{expectedInstance}, actual) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, pages) } func TestGetInstance(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGet(t) instance, err := instances.Get(fake.ServiceClient(), instanceID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expectedInstance, instance) } func TestDeleteInstance(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDelete(t) res := instances.Delete(fake.ServiceClient(), instanceID) th.AssertNoErr(t, res.Err) } func TestEnableRootUser(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleEnableRoot(t) expected := &users.User{Name: "root", Password: "secretsecret"} user, err := instances.EnableRootUser(fake.ServiceClient(), instanceID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, user) } func TestIsRootEnabled(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleIsRootEnabled(t) isEnabled, err := instances.IsRootEnabled(fake.ServiceClient(), instanceID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, true, isEnabled) } func TestRestart(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleRestart(t) res := instances.Restart(fake.ServiceClient(), instanceID) th.AssertNoErr(t, res.Err) } func TestResize(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleResize(t) res := instances.Resize(fake.ServiceClient(), instanceID, "2") th.AssertNoErr(t, res.Err) } func TestResizeVolume(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleResizeVol(t) res := instances.ResizeVolume(fake.ServiceClient(), instanceID, 4) th.AssertNoErr(t, res.Err) } func TestAttachConfigurationGroup(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAttachConfigurationGroup(t) res := instances.AttachConfigurationGroup(fake.ServiceClient(), instanceID, configGroupID) th.AssertNoErr(t, res.Err) } func TestDetachConfigurationGroup(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDetachConfigurationGroup(t) res := instances.DetachConfigurationGroup(fake.ServiceClient(), instanceID) th.AssertNoErr(t, res.Err) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/instances/urls.go000066400000000000000000000007541335005366400317670ustar00rootroot00000000000000package instances import "github.com/gophercloud/gophercloud" func baseURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("instances") } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("instances", id) } func userRootURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("instances", id, "root") } func actionURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("instances", id, "action") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/users/000077500000000000000000000000001335005366400276175ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/users/doc.go000066400000000000000000000002051335005366400307100ustar00rootroot00000000000000// Package users provides information and interaction with the user API // resource in the OpenStack Database service. package users golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/users/requests.go000066400000000000000000000071001335005366400320170ustar00rootroot00000000000000package users import ( "github.com/gophercloud/gophercloud" db "github.com/gophercloud/gophercloud/openstack/db/v1/databases" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder is the top-level interface for creating JSON maps. type CreateOptsBuilder interface { ToUserCreateMap() (map[string]interface{}, error) } // CreateOpts is the struct responsible for configuring a new user; often in the // context of an instance. type CreateOpts struct { // Specifies a name for the user. Valid names can be composed // of the following characters: letters (either case); numbers; these // characters '@', '?', '#', ' ' but NEVER beginning a name string; '_' is // permitted anywhere. Prohibited characters that are forbidden include: // single quotes, double quotes, back quotes, semicolons, commas, backslashes, // and forward slashes. Spaces at the front or end of a user name are also // not permitted. Name string `json:"name" required:"true"` // Specifies a password for the user. Password string `json:"password" required:"true"` // An array of databases that this user will connect to. The // "name" field is the only requirement for each option. Databases db.BatchCreateOpts `json:"databases,omitempty"` // Specifies the host from which a user is allowed to connect to // the database. Possible values are a string containing an IPv4 address or // "%" to allow connecting from any host. Optional; the default is "%". Host string `json:"host,omitempty"` } // ToMap is a convenience function for creating sub-maps for individual users. func (opts CreateOpts) ToMap() (map[string]interface{}, error) { if opts.Name == "root" { err := gophercloud.ErrInvalidInput{} err.Argument = "users.CreateOpts.Name" err.Value = "root" err.Info = "root is a reserved user name and cannot be used" return nil, err } return gophercloud.BuildRequestBody(opts, "") } // BatchCreateOpts allows multiple users to be created at once. type BatchCreateOpts []CreateOpts // ToUserCreateMap will generate a JSON map. func (opts BatchCreateOpts) ToUserCreateMap() (map[string]interface{}, error) { users := make([]map[string]interface{}, len(opts)) for i, opt := range opts { user, err := opt.ToMap() if err != nil { return nil, err } users[i] = user } return map[string]interface{}{"users": users}, nil } // Create asynchronously provisions a new user for the specified database // instance based on the configuration defined in CreateOpts. If databases are // assigned for a particular user, the user will be granted all privileges // for those specified databases. "root" is a reserved name and cannot be used. func Create(client *gophercloud.ServiceClient, instanceID string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToUserCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(baseURL(client, instanceID), &b, nil, nil) return } // List will list all the users associated with a specified database instance, // along with their associated databases. This operation will not return any // system users or administrators for a database. func List(client *gophercloud.ServiceClient, instanceID string) pagination.Pager { return pagination.NewPager(client, baseURL(client, instanceID), func(r pagination.PageResult) pagination.Page { return UserPage{pagination.LinkedPageBase{PageResult: r}} }) } // Delete will permanently delete a user from a specified database instance. func Delete(client *gophercloud.ServiceClient, instanceID, userName string) (r DeleteResult) { _, r.Err = client.Delete(userURL(client, instanceID, userName), nil) return } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/users/results.go000066400000000000000000000026531335005366400316550ustar00rootroot00000000000000package users import ( "github.com/gophercloud/gophercloud" db "github.com/gophercloud/gophercloud/openstack/db/v1/databases" "github.com/gophercloud/gophercloud/pagination" ) // User represents a database user type User struct { // The user name Name string // The user password Password string // The databases associated with this user Databases []db.Database } // CreateResult represents the result of a create operation. type CreateResult struct { gophercloud.ErrResult } // DeleteResult represents the result of a delete operation. type DeleteResult struct { gophercloud.ErrResult } // UserPage represents a single page of a paginated user collection. type UserPage struct { pagination.LinkedPageBase } // IsEmpty checks to see whether the collection is empty. func (page UserPage) IsEmpty() (bool, error) { users, err := ExtractUsers(page) return len(users) == 0, err } // NextPageURL will retrieve the next page URL. func (page UserPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"users_links"` } err := page.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // ExtractUsers will convert a generic pagination struct into a more // relevant slice of User structs. func ExtractUsers(r pagination.Page) ([]User, error) { var s struct { Users []User `json:"users"` } err := (r.(UserPage)).ExtractInto(&s) return s.Users, err } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/users/testing/000077500000000000000000000000001335005366400312745ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/users/testing/doc.go000066400000000000000000000000371335005366400323700ustar00rootroot00000000000000// db_users_v1 package testing fixtures.go000066400000000000000000000017031335005366400334160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/users/testingpackage testing import ( "fmt" "testing" "github.com/gophercloud/gophercloud/testhelper/fixture" ) const user1 = ` {"databases": [{"name": "databaseA"}],"name": "dbuser3"%s} ` const user2 = ` {"databases": [{"name": "databaseB"},{"name": "databaseC"}],"name": "dbuser4"%s} ` var ( instanceID = "{instanceID}" _rootURL = "/instances/" + instanceID + "/users" pUser1 = fmt.Sprintf(user1, `,"password":"secretsecret"`) pUser2 = fmt.Sprintf(user2, `,"password":"secretsecret"`) createReq = fmt.Sprintf(`{"users":[%s, %s]}`, pUser1, pUser2) listResp = fmt.Sprintf(`{"users":[%s, %s]}`, fmt.Sprintf(user1, ""), fmt.Sprintf(user2, "")) ) func HandleCreate(t *testing.T) { fixture.SetupHandler(t, _rootURL, "POST", createReq, "", 202) } func HandleList(t *testing.T) { fixture.SetupHandler(t, _rootURL, "GET", "", listResp, 200) } func HandleDelete(t *testing.T) { fixture.SetupHandler(t, _rootURL+"/{userName}", "DELETE", "", "", 202) } requests_test.go000066400000000000000000000032621335005366400344610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/users/testingpackage testing import ( "testing" db "github.com/gophercloud/gophercloud/openstack/db/v1/databases" "github.com/gophercloud/gophercloud/openstack/db/v1/users" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreate(t) opts := users.BatchCreateOpts{ { Databases: db.BatchCreateOpts{ db.CreateOpts{Name: "databaseA"}, }, Name: "dbuser3", Password: "secretsecret", }, { Databases: db.BatchCreateOpts{ {Name: "databaseB"}, {Name: "databaseC"}, }, Name: "dbuser4", Password: "secretsecret", }, } res := users.Create(fake.ServiceClient(), instanceID, opts) th.AssertNoErr(t, res.Err) } func TestUserList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleList(t) expectedUsers := []users.User{ { Databases: []db.Database{ db.Database{Name: "databaseA"}, }, Name: "dbuser3", }, { Databases: []db.Database{ {Name: "databaseB"}, {Name: "databaseC"}, }, Name: "dbuser4", }, } pages := 0 err := users.List(fake.ServiceClient(), instanceID).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := users.ExtractUsers(page) if err != nil { return false, err } th.CheckDeepEquals(t, expectedUsers, actual) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, pages) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDelete(t) res := users.Delete(fake.ServiceClient(), instanceID, "{userName}") th.AssertNoErr(t, res.Err) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/db/v1/users/urls.go000066400000000000000000000005211335005366400311310ustar00rootroot00000000000000package users import "github.com/gophercloud/gophercloud" func baseURL(c *gophercloud.ServiceClient, instanceID string) string { return c.ServiceURL("instances", instanceID, "users") } func userURL(c *gophercloud.ServiceClient, instanceID, userName string) string { return c.ServiceURL("instances", instanceID, "users", userName) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/dns/000077500000000000000000000000001335005366400263275ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/dns/v2/000077500000000000000000000000001335005366400266565ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/dns/v2/recordsets/000077500000000000000000000000001335005366400310335ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/dns/v2/recordsets/doc.go000066400000000000000000000022071335005366400321300ustar00rootroot00000000000000/* Package recordsets provides information and interaction with the zone API resource for the OpenStack DNS service. Example to List RecordSets by Zone listOpts := recordsets.ListOpts{ Type: "A", } zoneID := "fff121f5-c506-410a-a69e-2d73ef9cbdbd" allPages, err := recordsets.ListByZone(dnsClient, zoneID, listOpts).AllPages() if err != nil { panic(err) } allRRs, err := recordsets.ExtractRecordSets(allPages() if err != nil { panic(err) } for _, rr := range allRRs { fmt.Printf("%+v\n", rr) } Example to Create a RecordSet createOpts := recordsets.CreateOpts{ Name: "example.com.", Type: "A", TTL: 3600, Description: "This is a recordset.", Records: []string{"10.1.0.2"}, } zoneID := "fff121f5-c506-410a-a69e-2d73ef9cbdbd" rr, err := recordsets.Create(dnsClient, zoneID, createOpts).Extract() if err != nil { panic(err) } Example to Delete a RecordSet zoneID := "fff121f5-c506-410a-a69e-2d73ef9cbdbd" recordsetID := "d96ed01a-b439-4eb8-9b90-7a9f71017f7b" err := recordsets.Delete(dnsClient, zoneID, recordsetID).ExtractErr() if err != nil { panic(err) } */ package recordsets requests.go000066400000000000000000000115041335005366400331570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/dns/v2/recordsetspackage recordsets import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToRecordSetListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the server attributes you want to see returned. Marker and Limit are used // for pagination. // https://developer.openstack.org/api-ref/dns/ type ListOpts struct { // Integer value for the limit of values to return. Limit int `q:"limit"` // UUID of the recordset at which you want to set a marker. Marker string `q:"marker"` Data string `q:"data"` Description string `q:"description"` Name string `q:"name"` SortDir string `q:"sort_dir"` SortKey string `q:"sort_key"` Status string `q:"status"` TTL int `q:"ttl"` Type string `q:"type"` ZoneID string `q:"zone_id"` } // ToRecordSetListQuery formats a ListOpts into a query string. func (opts ListOpts) ToRecordSetListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // ListByZone implements the recordset list request. func ListByZone(client *gophercloud.ServiceClient, zoneID string, opts ListOptsBuilder) pagination.Pager { url := baseURL(client, zoneID) if opts != nil { query, err := opts.ToRecordSetListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return RecordSetPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get implements the recordset Get request. func Get(client *gophercloud.ServiceClient, zoneID string, rrsetID string) (r GetResult) { _, r.Err = client.Get(rrsetURL(client, zoneID, rrsetID), &r.Body, nil) return } // CreateOptsBuilder allows extensions to add additional attributes to the // Create request. type CreateOptsBuilder interface { ToRecordSetCreateMap() (map[string]interface{}, error) } // CreateOpts specifies the base attributes that may be used to create a // RecordSet. type CreateOpts struct { // Name is the name of the RecordSet. Name string `json:"name" required:"true"` // Description is a description of the RecordSet. Description string `json:"description,omitempty"` // Records are the DNS records of the RecordSet. Records []string `json:"records,omitempty"` // TTL is the time to live of the RecordSet. TTL int `json:"ttl,omitempty"` // Type is the RRTYPE of the RecordSet. Type string `json:"type,omitempty"` } // ToRecordSetCreateMap formats an CreateOpts structure into a request body. func (opts CreateOpts) ToRecordSetCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } return b, nil } // Create creates a recordset in a given zone. func Create(client *gophercloud.ServiceClient, zoneID string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToRecordSetCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(baseURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201, 202}, }) return } // UpdateOptsBuilder allows extensions to add additional attributes to the // Update request. type UpdateOptsBuilder interface { ToRecordSetUpdateMap() (map[string]interface{}, error) } // UpdateOpts specifies the base attributes that may be updated on an existing // RecordSet. type UpdateOpts struct { // Description is a description of the RecordSet. Description string `json:"description,omitempty"` // TTL is the time to live of the RecordSet. TTL int `json:"ttl,omitempty"` // Records are the DNS records of the RecordSet. Records []string `json:"records,omitempty"` } // ToRecordSetUpdateMap formats an UpdateOpts structure into a request body. func (opts UpdateOpts) ToRecordSetUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } if opts.TTL > 0 { b["ttl"] = opts.TTL } else { b["ttl"] = nil } return b, nil } // Update updates a recordset in a given zone func Update(client *gophercloud.ServiceClient, zoneID string, rrsetID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToRecordSetUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Put(rrsetURL(client, zoneID, rrsetID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) return } // Delete removes an existing RecordSet. func Delete(client *gophercloud.ServiceClient, zoneID string, rrsetID string) (r DeleteResult) { _, r.Err = client.Delete(rrsetURL(client, zoneID, rrsetID), &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return } results.go000066400000000000000000000072641335005366400330150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/dns/v2/recordsetspackage recordsets import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { gophercloud.Result } // Extract interprets a GetResult, CreateResult or UpdateResult as a RecordSet. // An error is returned if the original call or the extraction failed. func (r commonResult) Extract() (*RecordSet, error) { var s *RecordSet err := r.ExtractInto(&s) return s, err } // CreateResult is the result of a Create operation. Call its Extract method to // interpret the result as a RecordSet. type CreateResult struct { commonResult } // GetResult is the result of a Get operation. Call its Extract method to // interpret the result as a RecordSet. type GetResult struct { commonResult } // RecordSetPage is a single page of RecordSet results. type RecordSetPage struct { pagination.LinkedPageBase } // UpdateResult is result of an Update operation. Call its Extract method to // interpret the result as a RecordSet. type UpdateResult struct { commonResult } // DeleteResult is result of a Delete operation. Call its ExtractErr method to // determine if the operation succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // IsEmpty returns true if the page contains no results. func (r RecordSetPage) IsEmpty() (bool, error) { s, err := ExtractRecordSets(r) return len(s) == 0, err } // ExtractRecordSets extracts a slice of RecordSets from a List result. func ExtractRecordSets(r pagination.Page) ([]RecordSet, error) { var s struct { RecordSets []RecordSet `json:"recordsets"` } err := (r.(RecordSetPage)).ExtractInto(&s) return s.RecordSets, err } // RecordSet represents a DNS Record Set. type RecordSet struct { // ID is the unique ID of the recordset ID string `json:"id"` // ZoneID is the ID of the zone the recordset belongs to. ZoneID string `json:"zone_id"` // ProjectID is the ID of the project that owns the recordset. ProjectID string `json:"project_id"` // Name is the name of the recordset. Name string `json:"name"` // ZoneName is the name of the zone the recordset belongs to. ZoneName string `json:"zone_name"` // Type is the RRTYPE of the recordset. Type string `json:"type"` // Records are the DNS records of the recordset. Records []string `json:"records"` // TTL is the time to live of the recordset. TTL int `json:"ttl"` // Status is the status of the recordset. Status string `json:"status"` // Action is the current action in progress of the recordset. Action string `json:"action"` // Description is the description of the recordset. Description string `json:"description"` // Version is the revision of the recordset. Version int `json:"version"` // CreatedAt is the date when the recordset was created. CreatedAt time.Time `json:"-"` // UpdatedAt is the date when the recordset was updated. UpdatedAt time.Time `json:"-"` // Links includes HTTP references to the itself, // useful for passing along to other APIs that might want a recordset // reference. Links []gophercloud.Link `json:"-"` } func (r *RecordSet) UnmarshalJSON(b []byte) error { type tmp RecordSet var s struct { tmp CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` Links map[string]interface{} `json:"links"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = RecordSet(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) r.UpdatedAt = time.Time(s.UpdatedAt) if s.Links != nil { for rel, href := range s.Links { if v, ok := href.(string); ok { link := gophercloud.Link{ Rel: rel, Href: v, } r.Links = append(r.Links, link) } } } return err } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/dns/v2/recordsets/testing/000077500000000000000000000000001335005366400325105ustar00rootroot00000000000000doc.go000066400000000000000000000000511335005366400335210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/dns/v2/recordsets/testing// recordsets unit tests package testing fixtures.go000066400000000000000000000302311335005366400346300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/dns/v2/recordsets/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListByZoneOutput is a sample response to a ListByZone call. const ListByZoneOutput = ` { "recordsets": [ { "description": "This is an example record set.", "links": { "self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096648" }, "updated_at": null, "records": [ "10.1.0.2" ], "ttl": 3600, "id": "f7b10e9b-0cae-4a91-b162-562bc6096648", "name": "example.org.", "project_id": "4335d1f0-f793-11e2-b778-0800200c9a66", "zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f", "zone_name": "example.com.", "created_at": "2014-10-24T19:59:44.000000", "version": 1, "type": "A", "status": "PENDING", "action": "CREATE" }, { "description": "This is another example record set.", "links": { "self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/7423aeaf-b354-4bd7-8aba-2e831567b478" }, "updated_at": "2017-03-04T14:29:07.000000", "records": [ "10.1.0.3", "10.1.0.4" ], "ttl": 3600, "id": "7423aeaf-b354-4bd7-8aba-2e831567b478", "name": "foo.example.org.", "project_id": "4335d1f0-f793-11e2-b778-0800200c9a66", "zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f", "zone_name": "example.com.", "created_at": "2014-10-24T19:59:44.000000", "version": 1, "type": "A", "status": "PENDING", "action": "CREATE" } ], "links": { "self": "http://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets" }, "metadata": { "total_count": 2 } } ` // ListByZoneOutputLimited is a sample response to a ListByZone call with a requested limit. const ListByZoneOutputLimited = ` { "recordsets": [ { "description": "This is another example record set.", "links": { "self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/7423aeaf-b354-4bd7-8aba-2e831567b478" }, "updated_at": "2017-03-04T14:29:07.000000", "records": [ "10.1.0.3", "10.1.0.4" ], "ttl": 3600, "id": "7423aeaf-b354-4bd7-8aba-2e831567b478", "name": "foo.example.org.", "project_id": "4335d1f0-f793-11e2-b778-0800200c9a66", "zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f", "zone_name": "example.com.", "created_at": "2014-10-24T19:59:44.000000", "version": 1, "type": "A", "status": "PENDING", "action": "CREATE" } ], "links": { "self": "http://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets?limit=1" }, "metadata": { "total_count": 1 } } ` // GetOutput is a sample response to a Get call. const GetOutput = ` { "description": "This is an example record set.", "links": { "self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096648" }, "updated_at": null, "records": [ "10.1.0.2" ], "ttl": 3600, "id": "f7b10e9b-0cae-4a91-b162-562bc6096648", "name": "example.org.", "project_id": "4335d1f0-f793-11e2-b778-0800200c9a66", "zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f", "zone_name": "example.com.", "created_at": "2014-10-24T19:59:44.000000", "version": 1, "type": "A", "status": "PENDING", "action": "CREATE" } ` // NextPageRequest is a sample request to test pagination. const NextPageRequest = ` { "links": { "self": "http://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets?limit=1", "next": "http://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets?limit=1&marker=f7b10e9b-0cae-4a91-b162-562bc6096648" } } ` // FirstRecordSet is the first result in ListByZoneOutput var FirstRecordSetCreatedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2014-10-24T19:59:44.000000") var FirstRecordSet = recordsets.RecordSet{ ID: "f7b10e9b-0cae-4a91-b162-562bc6096648", Description: "This is an example record set.", UpdatedAt: time.Time{}, Records: []string{"10.1.0.2"}, TTL: 3600, Name: "example.org.", ProjectID: "4335d1f0-f793-11e2-b778-0800200c9a66", ZoneID: "2150b1bf-dee2-4221-9d85-11f7886fb15f", ZoneName: "example.com.", CreatedAt: FirstRecordSetCreatedAt, Version: 1, Type: "A", Status: "PENDING", Action: "CREATE", Links: []gophercloud.Link{ { Rel: "self", Href: "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096648", }, }, } // SecondRecordSet is the first result in ListByZoneOutput var SecondRecordSetCreatedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2014-10-24T19:59:44.000000") var SecondRecordSetUpdatedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2017-03-04T14:29:07.000000") var SecondRecordSet = recordsets.RecordSet{ ID: "7423aeaf-b354-4bd7-8aba-2e831567b478", Description: "This is another example record set.", UpdatedAt: SecondRecordSetUpdatedAt, Records: []string{"10.1.0.3", "10.1.0.4"}, TTL: 3600, Name: "foo.example.org.", ProjectID: "4335d1f0-f793-11e2-b778-0800200c9a66", ZoneID: "2150b1bf-dee2-4221-9d85-11f7886fb15f", ZoneName: "example.com.", CreatedAt: SecondRecordSetCreatedAt, Version: 1, Type: "A", Status: "PENDING", Action: "CREATE", Links: []gophercloud.Link{ { Rel: "self", Href: "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/7423aeaf-b354-4bd7-8aba-2e831567b478", }, }, } // ExpectedRecordSetSlice is the slice of results that should be parsed // from ListByZoneOutput, in the expected order. var ExpectedRecordSetSlice = []recordsets.RecordSet{FirstRecordSet, SecondRecordSet} // ExpectedRecordSetSliceLimited is the slice of limited results that should be parsed // from ListByZoneOutput. var ExpectedRecordSetSliceLimited = []recordsets.RecordSet{SecondRecordSet} // HandleListByZoneSuccessfully configures the test server to respond to a ListByZone request. func HandleListByZoneSuccessfully(t *testing.T) { th.Mux.HandleFunc("/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "f7b10e9b-0cae-4a91-b162-562bc6096648": fmt.Fprintf(w, ListByZoneOutputLimited) case "": fmt.Fprintf(w, ListByZoneOutput) } }) } // HandleGetSuccessfully configures the test server to respond to a Get request. func HandleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096648", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, GetOutput) }) } // CreateRecordSetRequest is a sample request to create a resource record. const CreateRecordSetRequest = ` { "name" : "example.org.", "description" : "This is an example record set.", "type" : "A", "ttl" : 3600, "records" : [ "10.1.0.2" ] } ` // CreateRecordSetResponse is a sample response to a create request. const CreateRecordSetResponse = ` { "description": "This is an example record set.", "links": { "self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096648" }, "updated_at": null, "records": [ "10.1.0.2" ], "ttl": 3600, "id": "f7b10e9b-0cae-4a91-b162-562bc6096648", "name": "example.org.", "project_id": "4335d1f0-f793-11e2-b778-0800200c9a66", "zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f", "zone_name": "example.com.", "created_at": "2014-10-24T19:59:44.000000", "version": 1, "type": "A", "status": "PENDING", "action": "CREATE" } ` // CreatedRecordSet is the expected created resource record. var CreatedRecordSet = FirstRecordSet // HandleZoneCreationSuccessfully configures the test server to respond to a Create request. func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, CreateRecordSetRequest) w.WriteHeader(http.StatusCreated) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, CreateRecordSetResponse) }) } // UpdateRecordSetRequest is a sample request to update a record set. const UpdateRecordSetRequest = ` { "description" : "Updated description", "ttl" : null, "records" : [ "10.1.0.2", "10.1.0.3" ] } ` // UpdateRecordSetResponse is a sample response to an update request. const UpdateRecordSetResponse = ` { "description": "Updated description", "links": { "self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096648" }, "updated_at": null, "records": [ "10.1.0.2", "10.1.0.3" ], "ttl": 3600, "id": "f7b10e9b-0cae-4a91-b162-562bc6096648", "name": "example.org.", "project_id": "4335d1f0-f793-11e2-b778-0800200c9a66", "zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f", "zone_name": "example.com.", "created_at": "2014-10-24T19:59:44.000000", "version": 2, "type": "A", "status": "PENDING", "action": "UPDATE" } ` // HandleUpdateSuccessfully configures the test server to respond to an Update request. func HandleUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096648", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, UpdateRecordSetRequest) w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, UpdateRecordSetResponse) }) } // DeleteRecordSetResponse is a sample response to a delete request. const DeleteRecordSetResponse = ` { "description": "Updated description", "links": { "self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096648" }, "updated_at": null, "records": [ "10.1.0.2", "10.1.0.3", ], "ttl": null, "id": "f7b10e9b-0cae-4a91-b162-562bc6096648", "name": "example.org.", "project_id": "4335d1f0-f793-11e2-b778-0800200c9a66", "zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f", "zone_name": "example.com.", "created_at": "2014-10-24T19:59:44.000000", "version": 2, "type": "A", "status": "PENDING", "action": "UPDATE" } ` // HandleDeleteSuccessfully configures the test server to respond to an Delete request. func HandleDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc("/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096648", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusAccepted) //w.Header().Add("Content-Type", "application/json") //fmt.Fprintf(w, DeleteZoneResponse) }) } requests_test.go000066400000000000000000000104121335005366400356700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/dns/v2/recordsets/testingpackage testing import ( "encoding/json" "testing" "github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListByZone(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListByZoneSuccessfully(t) count := 0 err := recordsets.ListByZone(client.ServiceClient(), "2150b1bf-dee2-4221-9d85-11f7886fb15f", nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := recordsets.ExtractRecordSets(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedRecordSetSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, 1, count) } func TestListByZoneLimited(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListByZoneSuccessfully(t) count := 0 listOpts := recordsets.ListOpts{ Limit: 1, Marker: "f7b10e9b-0cae-4a91-b162-562bc6096648", } err := recordsets.ListByZone(client.ServiceClient(), "2150b1bf-dee2-4221-9d85-11f7886fb15f", listOpts).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := recordsets.ExtractRecordSets(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedRecordSetSliceLimited, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, 1, count) } func TestListByZoneAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListByZoneSuccessfully(t) allPages, err := recordsets.ListByZone(client.ServiceClient(), "2150b1bf-dee2-4221-9d85-11f7886fb15f", nil).AllPages() th.AssertNoErr(t, err) allRecordSets, err := recordsets.ExtractRecordSets(allPages) th.AssertNoErr(t, err) th.CheckEquals(t, 2, len(allRecordSets)) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t) actual, err := recordsets.Get(client.ServiceClient(), "2150b1bf-dee2-4221-9d85-11f7886fb15f", "f7b10e9b-0cae-4a91-b162-562bc6096648").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstRecordSet, actual) } func TestNextPageURL(t *testing.T) { var page recordsets.RecordSetPage var body map[string]interface{} err := json.Unmarshal([]byte(NextPageRequest), &body) if err != nil { t.Fatalf("Error unmarshaling data into page body: %v", err) } page.Body = body expected := "http://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets?limit=1&marker=f7b10e9b-0cae-4a91-b162-562bc6096648" actual, err := page.NextPageURL() th.AssertNoErr(t, err) th.CheckEquals(t, expected, actual) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateSuccessfully(t) createOpts := recordsets.CreateOpts{ Name: "example.org.", Type: "A", TTL: 3600, Description: "This is an example record set.", Records: []string{"10.1.0.2"}, } actual, err := recordsets.Create(client.ServiceClient(), "2150b1bf-dee2-4221-9d85-11f7886fb15f", createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &CreatedRecordSet, actual) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateSuccessfully(t) updateOpts := recordsets.UpdateOpts{ TTL: 0, Description: "Updated description", Records: []string{"10.1.0.2", "10.1.0.3"}, } UpdatedRecordSet := CreatedRecordSet UpdatedRecordSet.Status = "PENDING" UpdatedRecordSet.Action = "UPDATE" UpdatedRecordSet.Description = "Updated description" UpdatedRecordSet.Records = []string{"10.1.0.2", "10.1.0.3"} UpdatedRecordSet.Version = 2 actual, err := recordsets.Update(client.ServiceClient(), UpdatedRecordSet.ZoneID, UpdatedRecordSet.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &UpdatedRecordSet, actual) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSuccessfully(t) DeletedRecordSet := CreatedRecordSet DeletedRecordSet.Status = "PENDING" DeletedRecordSet.Action = "UPDATE" DeletedRecordSet.Description = "Updated description" DeletedRecordSet.Records = []string{"10.1.0.2", "10.1.0.3"} DeletedRecordSet.Version = 2 err := recordsets.Delete(client.ServiceClient(), DeletedRecordSet.ZoneID, DeletedRecordSet.ID).ExtractErr() th.AssertNoErr(t, err) //th.CheckDeepEquals(t, &DeletedZone, actual) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/dns/v2/recordsets/urls.go000066400000000000000000000005161335005366400323510ustar00rootroot00000000000000package recordsets import "github.com/gophercloud/gophercloud" func baseURL(c *gophercloud.ServiceClient, zoneID string) string { return c.ServiceURL("zones", zoneID, "recordsets") } func rrsetURL(c *gophercloud.ServiceClient, zoneID string, rrsetID string) string { return c.ServiceURL("zones", zoneID, "recordsets", rrsetID) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/dns/v2/zones/000077500000000000000000000000001335005366400300145ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/dns/v2/zones/doc.go000066400000000000000000000016321335005366400311120ustar00rootroot00000000000000/* Package zones provides information and interaction with the zone API resource for the OpenStack DNS service. Example to List Zones listOpts := zones.ListOpts{ Email: "jdoe@example.com", } allPages, err := zones.List(dnsClient, listOpts).AllPages() if err != nil { panic(err) } allZones, err := zones.ExtractZones(allPages) if err != nil { panic(err) } for _, zone := range allZones { fmt.Printf("%+v\n", zone) } Example to Create a Zone createOpts := zones.CreateOpts{ Name: "example.com.", Email: "jdoe@example.com", Type: "PRIMARY", TTL: 7200, Description: "This is a zone.", } zone, err := zones.Create(dnsClient, createOpts).Extract() if err != nil { panic(err) } Example to Delete a Zone zoneID := "99d10f68-5623-4491-91a0-6daafa32b60e" err := zones.Delete(dnsClient, zoneID).ExtractErr() if err != nil { panic(err) } */ package zones golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/dns/v2/zones/requests.go000066400000000000000000000114321335005366400322170ustar00rootroot00000000000000package zones import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add parameters to the List request. type ListOptsBuilder interface { ToZoneListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the server attributes you want to see returned. Marker and Limit are used // for pagination. // https://developer.openstack.org/api-ref/dns/ type ListOpts struct { // Integer value for the limit of values to return. Limit int `q:"limit"` // UUID of the zone at which you want to set a marker. Marker string `q:"marker"` Description string `q:"description"` Email string `q:"email"` Name string `q:"name"` SortDir string `q:"sort_dir"` SortKey string `q:"sort_key"` Status string `q:"status"` TTL int `q:"ttl"` Type string `q:"type"` } // ToZoneListQuery formats a ListOpts into a query string. func (opts ListOpts) ToZoneListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List implements a zone List request. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := baseURL(client) if opts != nil { query, err := opts.ToZoneListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ZonePage{pagination.LinkedPageBase{PageResult: r}} }) } // Get returns information about a zone, given its ID. func Get(client *gophercloud.ServiceClient, zoneID string) (r GetResult) { _, r.Err = client.Get(zoneURL(client, zoneID), &r.Body, nil) return } // CreateOptsBuilder allows extensions to add additional attributes to the // Create request. type CreateOptsBuilder interface { ToZoneCreateMap() (map[string]interface{}, error) } // CreateOpts specifies the attributes used to create a zone. type CreateOpts struct { // Attributes are settings that supply hints and filters for the zone. Attributes map[string]string `json:"attributes,omitempty"` // Email contact of the zone. Email string `json:"email,omitempty"` // Description of the zone. Description string `json:"description,omitempty"` // Name of the zone. Name string `json:"name" required:"true"` // Masters specifies zone masters if this is a secondary zone. Masters []string `json:"masters,omitempty"` // TTL is the time to live of the zone. TTL int `json:"-"` // Type specifies if this is a primary or secondary zone. Type string `json:"type,omitempty"` } // ToZoneCreateMap formats an CreateOpts structure into a request body. func (opts CreateOpts) ToZoneCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } if opts.TTL > 0 { b["ttl"] = opts.TTL } return b, nil } // Create implements a zone create request. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToZoneCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201, 202}, }) return } // UpdateOptsBuilder allows extensions to add additional attributes to the // Update request. type UpdateOptsBuilder interface { ToZoneUpdateMap() (map[string]interface{}, error) } // UpdateOpts specifies the attributes to update a zone. type UpdateOpts struct { // Email contact of the zone. Email string `json:"email,omitempty"` // TTL is the time to live of the zone. TTL int `json:"-"` // Masters specifies zone masters if this is a secondary zone. Masters []string `json:"masters,omitempty"` // Description of the zone. Description string `json:"description,omitempty"` } // ToZoneUpdateMap formats an UpdateOpts structure into a request body. func (opts UpdateOpts) ToZoneUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } if opts.TTL > 0 { b["ttl"] = opts.TTL } return b, nil } // Update implements a zone update request. func Update(client *gophercloud.ServiceClient, zoneID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToZoneUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Patch(zoneURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) return } // Delete implements a zone delete request. func Delete(client *gophercloud.ServiceClient, zoneID string) (r DeleteResult) { _, r.Err = client.Delete(zoneURL(client, zoneID), &gophercloud.RequestOpts{ OkCodes: []int{202}, JSONResponse: &r.Body, }) return } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/dns/v2/zones/results.go000066400000000000000000000102031335005366400320400ustar00rootroot00000000000000package zones import ( "encoding/json" "strconv" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { gophercloud.Result } // Extract interprets a GetResult, CreateResult or UpdateResult as a Zone. // An error is returned if the original call or the extraction failed. func (r commonResult) Extract() (*Zone, error) { var s *Zone err := r.ExtractInto(&s) return s, err } // CreateResult is the result of a Create request. Call its Extract method // to interpret the result as a Zone. type CreateResult struct { commonResult } // GetResult is the result of a Get request. Call its Extract method // to interpret the result as a Zone. type GetResult struct { commonResult } // UpdateResult is the result of an Update request. Call its Extract method // to interpret the result as a Zone. type UpdateResult struct { commonResult } // DeleteResult is the result of a Delete request. Call its ExtractErr method // to determine if the request succeeded or failed. type DeleteResult struct { commonResult } // ZonePage is a single page of Zone results. type ZonePage struct { pagination.LinkedPageBase } // IsEmpty returns true if the page contains no results. func (r ZonePage) IsEmpty() (bool, error) { s, err := ExtractZones(r) return len(s) == 0, err } // ExtractZones extracts a slice of Zones from a List result. func ExtractZones(r pagination.Page) ([]Zone, error) { var s struct { Zones []Zone `json:"zones"` } err := (r.(ZonePage)).ExtractInto(&s) return s.Zones, err } // Zone represents a DNS zone. type Zone struct { // ID uniquely identifies this zone amongst all other zones, including those // not accessible to the current tenant. ID string `json:"id"` // PoolID is the ID for the pool hosting this zone. PoolID string `json:"pool_id"` // ProjectID identifies the project/tenant owning this resource. ProjectID string `json:"project_id"` // Name is the DNS Name for the zone. Name string `json:"name"` // Email for the zone. Used in SOA records for the zone. Email string `json:"email"` // Description for this zone. Description string `json:"description"` // TTL is the Time to Live for the zone. TTL int `json:"ttl"` // Serial is the current serial number for the zone. Serial int `json:"-"` // Status is the status of the resource. Status string `json:"status"` // Action is the current action in progress on the resource. Action string `json:"action"` // Version of the resource. Version int `json:"version"` // Attributes for the zone. Attributes map[string]string `json:"attributes"` // Type of zone. Primary is controlled by Designate. // Secondary zones are slaved from another DNS Server. // Defaults to Primary. Type string `json:"type"` // Masters is the servers for slave servers to get DNS information from. Masters []string `json:"masters"` // CreatedAt is the date when the zone was created. CreatedAt time.Time `json:"-"` // UpdatedAt is the date when the last change was made to the zone. UpdatedAt time.Time `json:"-"` // TransferredAt is the last time an update was retrieved from the // master servers. TransferredAt time.Time `json:"-"` // Links includes HTTP references to the itself, useful for passing along // to other APIs that might want a server reference. Links map[string]interface{} `json:"links"` } func (r *Zone) UnmarshalJSON(b []byte) error { type tmp Zone var s struct { tmp CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` TransferredAt gophercloud.JSONRFC3339MilliNoZ `json:"transferred_at"` Serial interface{} `json:"serial"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Zone(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) r.UpdatedAt = time.Time(s.UpdatedAt) r.TransferredAt = time.Time(s.TransferredAt) switch t := s.Serial.(type) { case float64: r.Serial = int(t) case string: switch t { case "": r.Serial = 0 default: serial, err := strconv.ParseFloat(t, 64) if err != nil { return err } r.Serial = int(serial) } } return err } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/dns/v2/zones/testing/000077500000000000000000000000001335005366400314715ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/dns/v2/zones/testing/doc.go000066400000000000000000000000441335005366400325630ustar00rootroot00000000000000// zones unit tests package testing fixtures.go000066400000000000000000000225111335005366400336130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/dns/v2/zones/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/dns/v2/zones" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // List Output is a sample response to a List call. const ListOutput = ` { "links": { "self": "http://example.com:9001/v2/zones" }, "metadata": { "total_count": 2 }, "zones": [ { "id": "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3", "pool_id": "572ba08c-d929-4c70-8e42-03824bb24ca2", "project_id": "4335d1f0-f793-11e2-b778-0800200c9a66", "name": "example.org.", "email": "joe@example.org", "ttl": 7200, "serial": 1404757531, "status": "ACTIVE", "action": "CREATE", "description": "This is an example zone.", "masters": [], "type": "PRIMARY", "transferred_at": null, "version": 1, "created_at": "2014-07-07T18:25:31.275934", "updated_at": null, "links": { "self": "https://127.0.0.1:9001/v2/zones/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3" } }, { "id": "34c4561c-9205-4386-9df5-167436f5a222", "pool_id": "572ba08c-d929-4c70-8e42-03824bb24ca2", "project_id": "4335d1f0-f793-11e2-b778-0800200c9a66", "name": "foo.example.com.", "email": "joe@foo.example.com", "ttl": 7200, "serial": 1488053571, "status": "ACTIVE", "action": "CREATE", "description": "This is another example zone.", "masters": ["example.com."], "type": "PRIMARY", "transferred_at": null, "version": 1, "created_at": "2014-07-07T18:25:31.275934", "updated_at": "2015-02-25T20:23:01.234567", "links": { "self": "https://127.0.0.1:9001/v2/zones/34c4561c-9205-4386-9df5-167436f5a222" } } ] } ` // GetOutput is a sample response to a Get call. const GetOutput = ` { "id": "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3", "pool_id": "572ba08c-d929-4c70-8e42-03824bb24ca2", "project_id": "4335d1f0-f793-11e2-b778-0800200c9a66", "name": "example.org.", "email": "joe@example.org", "ttl": 7200, "serial": 1404757531, "status": "ACTIVE", "action": "CREATE", "description": "This is an example zone.", "masters": [], "type": "PRIMARY", "transferred_at": null, "version": 1, "created_at": "2014-07-07T18:25:31.275934", "updated_at": null, "links": { "self": "https://127.0.0.1:9001/v2/zones/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3" } } ` // FirstZone is the first result in ListOutput var FirstZoneCreatedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2014-07-07T18:25:31.275934") var FirstZone = zones.Zone{ ID: "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3", PoolID: "572ba08c-d929-4c70-8e42-03824bb24ca2", ProjectID: "4335d1f0-f793-11e2-b778-0800200c9a66", Name: "example.org.", Email: "joe@example.org", TTL: 7200, Serial: 1404757531, Status: "ACTIVE", Action: "CREATE", Description: "This is an example zone.", Masters: []string{}, Type: "PRIMARY", Version: 1, CreatedAt: FirstZoneCreatedAt, Links: map[string]interface{}{ "self": "https://127.0.0.1:9001/v2/zones/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3", }, } var SecondZoneCreatedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2014-07-07T18:25:31.275934") var SecondZoneUpdatedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2015-02-25T20:23:01.234567") var SecondZone = zones.Zone{ ID: "34c4561c-9205-4386-9df5-167436f5a222", PoolID: "572ba08c-d929-4c70-8e42-03824bb24ca2", ProjectID: "4335d1f0-f793-11e2-b778-0800200c9a66", Name: "foo.example.com.", Email: "joe@foo.example.com", TTL: 7200, Serial: 1488053571, Status: "ACTIVE", Action: "CREATE", Description: "This is another example zone.", Masters: []string{"example.com."}, Type: "PRIMARY", Version: 1, CreatedAt: SecondZoneCreatedAt, UpdatedAt: SecondZoneUpdatedAt, Links: map[string]interface{}{ "self": "https://127.0.0.1:9001/v2/zones/34c4561c-9205-4386-9df5-167436f5a222", }, } // ExpectedZonesSlice is the slice of results that should be parsed // from ListOutput, in the expected order. var ExpectedZonesSlice = []zones.Zone{FirstZone, SecondZone} // HandleListSuccessfully configures the test server to respond to a List request. func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/zones", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ListOutput) }) } // HandleGetSuccessfully configures the test server to respond to a List request. func HandleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/zones/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, GetOutput) }) } // CreateZoneRequest is a sample request to create a zone. const CreateZoneRequest = ` { "name": "example.org.", "email": "joe@example.org", "type": "PRIMARY", "ttl": 7200, "description": "This is an example zone." } ` // CreateZoneResponse is a sample response to a create request. const CreateZoneResponse = ` { "id": "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3", "pool_id": "572ba08c-d929-4c70-8e42-03824bb24ca2", "project_id": "4335d1f0-f793-11e2-b778-0800200c9a66", "name": "example.org.", "email": "joe@example.org", "ttl": 7200, "serial": 1404757531, "status": "ACTIVE", "action": "CREATE", "description": "This is an example zone.", "masters": [], "type": "PRIMARY", "transferred_at": null, "version": 1, "created_at": "2014-07-07T18:25:31.275934", "updated_at": null, "links": { "self": "https://127.0.0.1:9001/v2/zones/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3" } } ` // CreatedZone is the expected created zone var CreatedZone = FirstZone // HandleZoneCreationSuccessfully configures the test server to respond to a Create request. func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/zones", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, CreateZoneRequest) w.WriteHeader(http.StatusCreated) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, CreateZoneResponse) }) } // UpdateZoneRequest is a sample request to update a zone. const UpdateZoneRequest = ` { "ttl": 600, "description": "Updated Description" } ` // UpdateZoneResponse is a sample response to update a zone. const UpdateZoneResponse = ` { "id": "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3", "pool_id": "572ba08c-d929-4c70-8e42-03824bb24ca2", "project_id": "4335d1f0-f793-11e2-b778-0800200c9a66", "name": "example.org.", "email": "joe@example.org", "ttl": 600, "serial": 1404757531, "status": "PENDING", "action": "UPDATE", "description": "Updated Description", "masters": [], "type": "PRIMARY", "transferred_at": null, "version": 1, "created_at": "2014-07-07T18:25:31.275934", "updated_at": null, "links": { "self": "https://127.0.0.1:9001/v2/zones/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3" } } ` // HandleZoneUpdateSuccessfully configures the test server to respond to an Update request. func HandleUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/zones/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, UpdateZoneRequest) w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, UpdateZoneResponse) }) } // DeleteZoneResponse is a sample response to update a zone. const DeleteZoneResponse = ` { "id": "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3", "pool_id": "572ba08c-d929-4c70-8e42-03824bb24ca2", "project_id": "4335d1f0-f793-11e2-b778-0800200c9a66", "name": "example.org.", "email": "joe@example.org", "ttl": 600, "serial": 1404757531, "status": "PENDING", "action": "DELETE", "description": "Updated Description", "masters": [], "type": "PRIMARY", "transferred_at": null, "version": 1, "created_at": "2014-07-07T18:25:31.275934", "updated_at": null, "links": { "self": "https://127.0.0.1:9001/v2/zones/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3" } } ` // HandleZoneDeleteSuccessfully configures the test server to respond to an Delete request. func HandleDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc("/zones/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, DeleteZoneResponse) }) } requests_test.go000066400000000000000000000051211335005366400346520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/dns/v2/zones/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/dns/v2/zones" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t) count := 0 err := zones.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := zones.ExtractZones(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedZonesSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, 1, count) } func TestListAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t) allPages, err := zones.List(client.ServiceClient(), nil).AllPages() th.AssertNoErr(t, err) allZones, err := zones.ExtractZones(allPages) th.AssertNoErr(t, err) th.CheckEquals(t, 2, len(allZones)) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t) actual, err := zones.Get(client.ServiceClient(), "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstZone, actual) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateSuccessfully(t) createOpts := zones.CreateOpts{ Name: "example.org.", Email: "joe@example.org", Type: "PRIMARY", TTL: 7200, Description: "This is an example zone.", } actual, err := zones.Create(client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &CreatedZone, actual) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateSuccessfully(t) updateOpts := zones.UpdateOpts{ TTL: 600, Description: "Updated Description", } UpdatedZone := CreatedZone UpdatedZone.Status = "PENDING" UpdatedZone.Action = "UPDATE" UpdatedZone.TTL = 600 UpdatedZone.Description = "Updated Description" actual, err := zones.Update(client.ServiceClient(), UpdatedZone.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &UpdatedZone, actual) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSuccessfully(t) DeletedZone := CreatedZone DeletedZone.Status = "PENDING" DeletedZone.Action = "DELETE" DeletedZone.TTL = 600 DeletedZone.Description = "Updated Description" actual, err := zones.Delete(client.ServiceClient(), DeletedZone.ID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &DeletedZone, actual) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/dns/v2/zones/urls.go000066400000000000000000000003741335005366400313340ustar00rootroot00000000000000package zones import "github.com/gophercloud/gophercloud" func baseURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("zones") } func zoneURL(c *gophercloud.ServiceClient, zoneID string) string { return c.ServiceURL("zones", zoneID) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/doc.go000066400000000000000000000007541335005366400266450ustar00rootroot00000000000000/* Package openstack contains resources for the individual OpenStack projects supported in Gophercloud. It also includes functions to authenticate to an OpenStack cloud and for provisioning various service-level clients. Example of Creating a Service Client ao, err := openstack.AuthOptionsFromEnv() provider, err := openstack.AuthenticatedClient(ao) client, err := openstack.NewNetworkV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) */ package openstack golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/endpoint_location.go000066400000000000000000000076621335005366400316150ustar00rootroot00000000000000package openstack import ( "github.com/gophercloud/gophercloud" tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" ) /* V2EndpointURL discovers the endpoint URL for a specific service from a ServiceCatalog acquired during the v2 identity service. The specified EndpointOpts are used to identify a unique, unambiguous endpoint to return. It's an error both when multiple endpoints match the provided criteria and when none do. The minimum that can be specified is a Type, but you will also often need to specify a Name and/or a Region depending on what's available on your OpenStack deployment. */ func V2EndpointURL(catalog *tokens2.ServiceCatalog, opts gophercloud.EndpointOpts) (string, error) { // Extract Endpoints from the catalog entries that match the requested Type, Name if provided, and Region if provided. var endpoints = make([]tokens2.Endpoint, 0, 1) for _, entry := range catalog.Entries { if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) { for _, endpoint := range entry.Endpoints { if opts.Region == "" || endpoint.Region == opts.Region { endpoints = append(endpoints, endpoint) } } } } // Report an error if the options were ambiguous. if len(endpoints) > 1 { err := &ErrMultipleMatchingEndpointsV2{} err.Endpoints = endpoints return "", err } // Extract the appropriate URL from the matching Endpoint. for _, endpoint := range endpoints { switch opts.Availability { case gophercloud.AvailabilityPublic: return gophercloud.NormalizeURL(endpoint.PublicURL), nil case gophercloud.AvailabilityInternal: return gophercloud.NormalizeURL(endpoint.InternalURL), nil case gophercloud.AvailabilityAdmin: return gophercloud.NormalizeURL(endpoint.AdminURL), nil default: err := &ErrInvalidAvailabilityProvided{} err.Argument = "Availability" err.Value = opts.Availability return "", err } } // Report an error if there were no matching endpoints. err := &gophercloud.ErrEndpointNotFound{} return "", err } /* V3EndpointURL discovers the endpoint URL for a specific service from a Catalog acquired during the v3 identity service. The specified EndpointOpts are used to identify a unique, unambiguous endpoint to return. It's an error both when multiple endpoints match the provided criteria and when none do. The minimum that can be specified is a Type, but you will also often need to specify a Name and/or a Region depending on what's available on your OpenStack deployment. */ func V3EndpointURL(catalog *tokens3.ServiceCatalog, opts gophercloud.EndpointOpts) (string, error) { // Extract Endpoints from the catalog entries that match the requested Type, Interface, // Name if provided, and Region if provided. var endpoints = make([]tokens3.Endpoint, 0, 1) for _, entry := range catalog.Entries { if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) { for _, endpoint := range entry.Endpoints { if opts.Availability != gophercloud.AvailabilityAdmin && opts.Availability != gophercloud.AvailabilityPublic && opts.Availability != gophercloud.AvailabilityInternal { err := &ErrInvalidAvailabilityProvided{} err.Argument = "Availability" err.Value = opts.Availability return "", err } if (opts.Availability == gophercloud.Availability(endpoint.Interface)) && (opts.Region == "" || endpoint.Region == opts.Region || endpoint.RegionID == opts.Region) { endpoints = append(endpoints, endpoint) } } } } // Report an error if the options were ambiguous. if len(endpoints) > 1 { return "", ErrMultipleMatchingEndpointsV3{Endpoints: endpoints} } // Extract the URL from the matching Endpoint. for _, endpoint := range endpoints { return gophercloud.NormalizeURL(endpoint.URL), nil } // Report an error if there were no matching endpoints. err := &gophercloud.ErrEndpointNotFound{} return "", err } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/errors.go000066400000000000000000000044201335005366400274060ustar00rootroot00000000000000package openstack import ( "fmt" "github.com/gophercloud/gophercloud" tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" ) // ErrEndpointNotFound is the error when no suitable endpoint can be found // in the user's catalog type ErrEndpointNotFound struct{ gophercloud.BaseError } func (e ErrEndpointNotFound) Error() string { return "No suitable endpoint could be found in the service catalog." } // ErrInvalidAvailabilityProvided is the error when an invalid endpoint // availability is provided type ErrInvalidAvailabilityProvided struct{ gophercloud.ErrInvalidInput } func (e ErrInvalidAvailabilityProvided) Error() string { return fmt.Sprintf("Unexpected availability in endpoint query: %s", e.Value) } // ErrMultipleMatchingEndpointsV2 is the error when more than one endpoint // for the given options is found in the v2 catalog type ErrMultipleMatchingEndpointsV2 struct { gophercloud.BaseError Endpoints []tokens2.Endpoint } func (e ErrMultipleMatchingEndpointsV2) Error() string { return fmt.Sprintf("Discovered %d matching endpoints: %#v", len(e.Endpoints), e.Endpoints) } // ErrMultipleMatchingEndpointsV3 is the error when more than one endpoint // for the given options is found in the v3 catalog type ErrMultipleMatchingEndpointsV3 struct { gophercloud.BaseError Endpoints []tokens3.Endpoint } func (e ErrMultipleMatchingEndpointsV3) Error() string { return fmt.Sprintf("Discovered %d matching endpoints: %#v", len(e.Endpoints), e.Endpoints) } // ErrNoAuthURL is the error when the OS_AUTH_URL environment variable is not // found type ErrNoAuthURL struct{ gophercloud.ErrInvalidInput } func (e ErrNoAuthURL) Error() string { return "Environment variable OS_AUTH_URL needs to be set." } // ErrNoUsername is the error when the OS_USERNAME environment variable is not // found type ErrNoUsername struct{ gophercloud.ErrInvalidInput } func (e ErrNoUsername) Error() string { return "Environment variable OS_USERNAME needs to be set." } // ErrNoPassword is the error when the OS_PASSWORD environment variable is not // found type ErrNoPassword struct{ gophercloud.ErrInvalidInput } func (e ErrNoPassword) Error() string { return "Environment variable OS_PASSWORD needs to be set." } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/000077500000000000000000000000001335005366400273745ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/000077500000000000000000000000001335005366400277235ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/extensions/000077500000000000000000000000001335005366400321225ustar00rootroot00000000000000admin/000077500000000000000000000000001335005366400331335ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/extensionsroles/000077500000000000000000000000001335005366400342575ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/extensions/admindoc.go000066400000000000000000000031031335005366400353500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/extensions/admin/roles/* Package roles provides functionality to interact with and control roles on the API. A role represents a personality that a user can assume when performing a specific set of operations. If a role includes a set of rights and privileges, a user assuming that role inherits those rights and privileges. When a token is generated, the list of roles that user can assume is returned back to them. Services that are being called by that user determine how they interpret the set of roles a user has and to which operations or resources each role grants access. It is up to individual services such as Compute or Image to assign meaning to these roles. As far as the Identity service is concerned, a role is an arbitrary name assigned by the user. Example to List Roles allPages, err := roles.List(identityClient).AllPages() if err != nil { panic(err) } allRoles, err := roles.ExtractRoles(allPages) if err != nil { panic(err) } for _, role := range allRoles { fmt.Printf("%+v\n", role) } Example to Grant a Role to a User tenantID := "a99e9b4e620e4db09a2dfb6e42a01e66" userID := "9df1a02f5eb2416a9781e8b0c022d3ae" roleID := "9fe2ff9ee4384b1894a90878d3e92bab" err := roles.AddUser(identityClient, tenantID, userID, roleID).ExtractErr() if err != nil { panic(err) } Example to Remove a Role from a User tenantID := "a99e9b4e620e4db09a2dfb6e42a01e66" userID := "9df1a02f5eb2416a9781e8b0c022d3ae" roleID := "9fe2ff9ee4384b1894a90878d3e92bab" err := roles.DeleteUser(identityClient, tenantID, userID, roleID).ExtractErr() if err != nil { panic(err) } */ package roles requests.go000066400000000000000000000023421335005366400364620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/extensions/admin/rolespackage roles import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List is the operation responsible for listing all available global roles // that a user can adopt. func List(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, rootURL(client), func(r pagination.PageResult) pagination.Page { return RolePage{pagination.SinglePageBase(r)} }) } // AddUser is the operation responsible for assigning a particular role to // a user. This is confined to the scope of the user's tenant - so the tenant // ID is a required argument. func AddUser(client *gophercloud.ServiceClient, tenantID, userID, roleID string) (r UserRoleResult) { _, r.Err = client.Put(userRoleURL(client, tenantID, userID, roleID), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) return } // DeleteUser is the operation responsible for deleting a particular role // from a user. This is confined to the scope of the user's tenant - so the // tenant ID is a required argument. func DeleteUser(client *gophercloud.ServiceClient, tenantID, userID, roleID string) (r UserRoleResult) { _, r.Err = client.Delete(userRoleURL(client, tenantID, userID, roleID), nil) return } results.go000066400000000000000000000022621335005366400363110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/extensions/admin/rolespackage roles import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Role represents an API role resource. type Role struct { // ID is the unique ID for the role. ID string // Name is the human-readable name of the role. Name string // Description is the description of the role. Description string // ServiceID is the associated service for this role. ServiceID string } // RolePage is a single page of a user Role collection. type RolePage struct { pagination.SinglePageBase } // IsEmpty determines whether or not a page of Roles contains any results. func (r RolePage) IsEmpty() (bool, error) { users, err := ExtractRoles(r) return len(users) == 0, err } // ExtractRoles returns a slice of roles contained in a single page of results. func ExtractRoles(r pagination.Page) ([]Role, error) { var s struct { Roles []Role `json:"roles"` } err := (r.(RolePage)).ExtractInto(&s) return s.Roles, err } // UserRoleResult represents the result of either an AddUserRole or // a DeleteUserRole operation. Call its ExtractErr method to determine // if the request succeeded or failed. type UserRoleResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400357345ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/extensions/admin/rolesdoc.go000066400000000000000000000000441335005366400370260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/extensions/admin/roles/testing// roles unit tests package testing fixtures.go000066400000000000000000000023541335005366400401400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/extensions/admin/roles/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func MockListRoleResponse(t *testing.T) { th.Mux.HandleFunc("/OS-KSADM/roles", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "roles": [ { "id": "123", "name": "compute:admin", "description": "Nova Administrator" } ] } `) }) } func MockAddUserRoleResponse(t *testing.T) { th.Mux.HandleFunc("/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusCreated) }) } func MockDeleteUserRoleResponse(t *testing.T) { th.Mux.HandleFunc("/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000025301335005366400411750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/extensions/admin/roles/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/identity/v2/extensions/admin/roles" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestRole(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListRoleResponse(t) count := 0 err := roles.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := roles.ExtractRoles(page) if err != nil { t.Errorf("Failed to extract users: %v", err) return false, err } expected := []roles.Role{ { ID: "123", Name: "compute:admin", Description: "Nova Administrator", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, count) } func TestAddUser(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockAddUserRoleResponse(t) err := roles.AddUser(client.ServiceClient(), "{tenant_id}", "{user_id}", "{role_id}").ExtractErr() th.AssertNoErr(t, err) } func TestDeleteUser(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockDeleteUserRoleResponse(t) err := roles.DeleteUser(client.ServiceClient(), "{tenant_id}", "{user_id}", "{role_id}").ExtractErr() th.AssertNoErr(t, err) } urls.go000066400000000000000000000010111335005366400355640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/extensions/admin/rolespackage roles import "github.com/gophercloud/gophercloud" const ( ExtPath = "OS-KSADM" RolePath = "roles" UserPath = "users" ) func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(ExtPath, RolePath, id) } func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(ExtPath, RolePath) } func userRoleURL(c *gophercloud.ServiceClient, tenantID, userID, roleID string) string { return c.ServiceURL("tenants", tenantID, UserPath, userID, RolePath, ExtPath, roleID) } delegate.go000066400000000000000000000027761335005366400341600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/extensionspackage extensions import ( "github.com/gophercloud/gophercloud" common "github.com/gophercloud/gophercloud/openstack/common/extensions" "github.com/gophercloud/gophercloud/pagination" ) // ExtensionPage is a single page of Extension results. type ExtensionPage struct { common.ExtensionPage } // IsEmpty returns true if the current page contains at least one Extension. func (page ExtensionPage) IsEmpty() (bool, error) { is, err := ExtractExtensions(page) return len(is) == 0, err } // ExtractExtensions accepts a Page struct, specifically an ExtensionPage struct, and extracts the // elements into a slice of Extension structs. func ExtractExtensions(page pagination.Page) ([]common.Extension, error) { // Identity v2 adds an intermediate "values" object. var s struct { Extensions struct { Values []common.Extension `json:"values"` } `json:"extensions"` } err := page.(ExtensionPage).ExtractInto(&s) return s.Extensions.Values, err } // Get retrieves information for a specific extension using its alias. func Get(c *gophercloud.ServiceClient, alias string) common.GetResult { return common.Get(c, alias) } // List returns a Pager which allows you to iterate over the full collection of extensions. // It does not accept query parameters. func List(c *gophercloud.ServiceClient) pagination.Pager { return common.List(c).WithPageCreator(func(r pagination.PageResult) pagination.Page { return ExtensionPage{ ExtensionPage: common.ExtensionPage{SinglePageBase: pagination.SinglePageBase(r)}, } }) } doc.go000066400000000000000000000002351335005366400331370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/extensions// Package extensions provides information and interaction with the // different extensions available for the OpenStack Identity service. package extensions testing/000077500000000000000000000000001335005366400335205ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/extensionsdelegate_test.go000066400000000000000000000020511335005366400366560ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/extensions/testingpackage testing import ( "testing" common "github.com/gophercloud/gophercloud/openstack/common/extensions/testing" "github.com/gophercloud/gophercloud/openstack/identity/v2/extensions" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListExtensionsSuccessfully(t) count := 0 err := extensions.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := extensions.ExtractExtensions(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, common.ExpectedExtensions, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, 1, count) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() common.HandleGetExtensionSuccessfully(t) actual, err := extensions.Get(client.ServiceClient(), "agent").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, common.SingleExtension, actual) } doc.go000066400000000000000000000000511335005366400346100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/extensions/testing// extensions unit tests package testing fixtures.go000066400000000000000000000027421335005366400357250ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/extensions/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListOutput provides a single Extension result. It differs from the delegated implementation // by the introduction of an intermediate "values" member. const ListOutput = ` { "extensions": { "values": [ { "updated": "2013-01-20T00:00:00-00:00", "name": "Neutron Service Type Management", "links": [], "namespace": "http://docs.openstack.org/ext/neutron/service-type/api/v1.0", "alias": "service-type", "description": "API for retrieving service providers for Neutron advanced services" } ] } } ` // HandleListExtensionsSuccessfully creates an HTTP handler that returns ListOutput for a List // call. func HandleListExtensionsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/extensions", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ` { "extensions": { "values": [ { "updated": "2013-01-20T00:00:00-00:00", "name": "Neutron Service Type Management", "links": [], "namespace": "http://docs.openstack.org/ext/neutron/service-type/api/v1.0", "alias": "service-type", "description": "API for retrieving service providers for Neutron advanced services" } ] } } `) }) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/tenants/000077500000000000000000000000001335005366400313775ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/tenants/doc.go000066400000000000000000000025711335005366400325000ustar00rootroot00000000000000/* Package tenants provides information and interaction with the tenants API resource for the OpenStack Identity service. See http://developer.openstack.org/api-ref-identity-v2.html#identity-auth-v2 and http://developer.openstack.org/api-ref-identity-v2.html#admin-tenants for more information. Example to List Tenants listOpts := tenants.ListOpts{ Limit: 2, } allPages, err := tenants.List(identityClient, listOpts).AllPages() if err != nil { panic(err) } allTenants, err := tenants.ExtractTenants(allPages) if err != nil { panic(err) } for _, tenant := range allTenants { fmt.Printf("%+v\n", tenant) } Example to Create a Tenant createOpts := tenants.CreateOpts{ Name: "tenant_name", Description: "this is a tenant", Enabled: gophercloud.Enabled, } tenant, err := tenants.Create(identityClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Tenant tenantID := "e6db6ed6277c461a853458589063b295" updateOpts := tenants.UpdateOpts{ Description: "this is a new description", Enabled: gophercloud.Disabled, } tenant, err := tenants.Update(identityClient, tenantID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Tenant tenantID := "e6db6ed6277c461a853458589063b295" err := tenants.Delete(identitYClient, tenantID).ExtractErr() if err != nil { panic(err) } */ package tenants requests.go000066400000000000000000000067521335005366400335340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/tenantspackage tenants import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOpts filters the Tenants that are returned by the List call. type ListOpts struct { // Marker is the ID of the last Tenant on the previous page. Marker string `q:"marker"` // Limit specifies the page size. Limit int `q:"limit"` } // List enumerates the Tenants to which the current token has access. func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager { url := listURL(client) if opts != nil { q, err := gophercloud.BuildQueryString(opts) if err != nil { return pagination.Pager{Err: err} } url += q.String() } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return TenantPage{pagination.LinkedPageBase{PageResult: r}} }) } // CreateOpts represents the options needed when creating new tenant. type CreateOpts struct { // Name is the name of the tenant. Name string `json:"name" required:"true"` // Description is the description of the tenant. Description string `json:"description,omitempty"` // Enabled sets the tenant status to enabled or disabled. Enabled *bool `json:"enabled,omitempty"` } // CreateOptsBuilder enables extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToTenantCreateMap() (map[string]interface{}, error) } // ToTenantCreateMap assembles a request body based on the contents of // a CreateOpts. func (opts CreateOpts) ToTenantCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "tenant") } // Create is the operation responsible for creating new tenant. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToTenantCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) return } // Get requests details on a single tenant by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToTenantUpdateMap() (map[string]interface{}, error) } // UpdateOpts specifies the base attributes that may be updated on an existing // tenant. type UpdateOpts struct { // Name is the name of the tenant. Name string `json:"name,omitempty"` // Description is the description of the tenant. Description string `json:"description,omitempty"` // Enabled sets the tenant status to enabled or disabled. Enabled *bool `json:"enabled,omitempty"` } // ToTenantUpdateMap formats an UpdateOpts structure into a request body. func (opts UpdateOpts) ToTenantUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "tenant") } // Update is the operation responsible for updating exist tenants by their TenantID. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToTenantUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Put(updateURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete is the operation responsible for permanently deleting a tenant. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } results.go000066400000000000000000000044371335005366400333600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/tenantspackage tenants import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Tenant is a grouping of users in the identity service. type Tenant struct { // ID is a unique identifier for this tenant. ID string `json:"id"` // Name is a friendlier user-facing name for this tenant. Name string `json:"name"` // Description is a human-readable explanation of this Tenant's purpose. Description string `json:"description"` // Enabled indicates whether or not a tenant is active. Enabled bool `json:"enabled"` } // TenantPage is a single page of Tenant results. type TenantPage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a page of Tenants contains any results. func (r TenantPage) IsEmpty() (bool, error) { tenants, err := ExtractTenants(r) return len(tenants) == 0, err } // NextPageURL extracts the "next" link from the tenants_links section of the result. func (r TenantPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"tenants_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // ExtractTenants returns a slice of Tenants contained in a single page of // results. func ExtractTenants(r pagination.Page) ([]Tenant, error) { var s struct { Tenants []Tenant `json:"tenants"` } err := (r.(TenantPage)).ExtractInto(&s) return s.Tenants, err } type tenantResult struct { gophercloud.Result } // Extract interprets any tenantResults as a Tenant. func (r tenantResult) Extract() (*Tenant, error) { var s struct { Tenant *Tenant `json:"tenant"` } err := r.ExtractInto(&s) return s.Tenant, err } // GetResult is the response from a Get request. Call its Extract method to // interpret it as a Tenant. type GetResult struct { tenantResult } // CreateResult is the response from a Create request. Call its Extract method // to interpret it as a Tenant. type CreateResult struct { tenantResult } // DeleteResult is the response from a Get request. Call its ExtractErr method // to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // UpdateResult is the response from a Update request. Call its Extract method // to interpret it as a Tenant. type UpdateResult struct { tenantResult } testing/000077500000000000000000000000001335005366400327755ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/tenantsdoc.go000066400000000000000000000000461335005366400340710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/tenants/testing// tenants unit tests package testing fixtures.go000066400000000000000000000071421335005366400352010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/tenants/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListOutput provides a single page of Tenant results. const ListOutput = ` { "tenants": [ { "id": "1234", "name": "Red Team", "description": "The team that is red", "enabled": true }, { "id": "9876", "name": "Blue Team", "description": "The team that is blue", "enabled": false } ] } ` // RedTeam is a Tenant fixture. var RedTeam = tenants.Tenant{ ID: "1234", Name: "Red Team", Description: "The team that is red", Enabled: true, } // BlueTeam is a Tenant fixture. var BlueTeam = tenants.Tenant{ ID: "9876", Name: "Blue Team", Description: "The team that is blue", Enabled: false, } // ExpectedTenantSlice is the slice of tenants expected to be returned from ListOutput. var ExpectedTenantSlice = []tenants.Tenant{RedTeam, BlueTeam} // HandleListTenantsSuccessfully creates an HTTP handler at `/tenants` on the test handler mux that // responds with a list of two tenants. func HandleListTenantsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/tenants", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListOutput) }) } func mockCreateTenantResponse(t *testing.T) { th.Mux.HandleFunc("/tenants", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, ` { "tenant": { "name": "new_tenant", "description": "This is new tenant", "enabled": true } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "tenant": { "name": "new_tenant", "description": "This is new tenant", "enabled": true, "id": "5c62ef576dc7444cbb73b1fe84b97648" } } `) }) } func mockDeleteTenantResponse(t *testing.T) { th.Mux.HandleFunc("/tenants/2466f69cd4714d89a548a68ed97ffcd4", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } func mockUpdateTenantResponse(t *testing.T) { th.Mux.HandleFunc("/tenants/5c62ef576dc7444cbb73b1fe84b97648", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, ` { "tenant": { "name": "new_name", "description": "This is new name", "enabled": true } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "tenant": { "name": "new_name", "description": "This is new name", "enabled": true, "id": "5c62ef576dc7444cbb73b1fe84b97648" } } `) }) } func mockGetTenantResponse(t *testing.T) { th.Mux.HandleFunc("/tenants/5c62ef576dc7444cbb73b1fe84b97648", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "tenant": { "name": "new_tenant", "description": "This is new tenant", "enabled": true, "id": "5c62ef576dc7444cbb73b1fe84b97648" } } `) }) } requests_test.go000066400000000000000000000047631335005366400362500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/tenants/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListTenants(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListTenantsSuccessfully(t) count := 0 err := tenants.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := tenants.ExtractTenants(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedTenantSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestCreateTenant(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockCreateTenantResponse(t) opts := tenants.CreateOpts{ Name: "new_tenant", Description: "This is new tenant", Enabled: gophercloud.Enabled, } tenant, err := tenants.Create(client.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) expected := &tenants.Tenant{ Name: "new_tenant", Description: "This is new tenant", Enabled: true, ID: "5c62ef576dc7444cbb73b1fe84b97648", } th.AssertDeepEquals(t, expected, tenant) } func TestDeleteTenant(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockDeleteTenantResponse(t) err := tenants.Delete(client.ServiceClient(), "2466f69cd4714d89a548a68ed97ffcd4").ExtractErr() th.AssertNoErr(t, err) } func TestUpdateTenant(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockUpdateTenantResponse(t) id := "5c62ef576dc7444cbb73b1fe84b97648" opts := tenants.UpdateOpts{ Name: "new_name", Description: "This is new name", Enabled: gophercloud.Enabled, } tenant, err := tenants.Update(client.ServiceClient(), id, opts).Extract() th.AssertNoErr(t, err) expected := &tenants.Tenant{ Name: "new_name", ID: id, Description: "This is new name", Enabled: true, } th.AssertDeepEquals(t, expected, tenant) } func TestGetTenant(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockGetTenantResponse(t) tenant, err := tenants.Get(client.ServiceClient(), "5c62ef576dc7444cbb73b1fe84b97648").Extract() th.AssertNoErr(t, err) expected := &tenants.Tenant{ Name: "new_tenant", ID: "5c62ef576dc7444cbb73b1fe84b97648", Description: "This is new tenant", Enabled: true, } th.AssertDeepEquals(t, expected, tenant) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/tenants/urls.go000066400000000000000000000011701335005366400327120ustar00rootroot00000000000000package tenants import "github.com/gophercloud/gophercloud" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("tenants") } func getURL(client *gophercloud.ServiceClient, tenantID string) string { return client.ServiceURL("tenants", tenantID) } func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("tenants") } func deleteURL(client *gophercloud.ServiceClient, tenantID string) string { return client.ServiceURL("tenants", tenantID) } func updateURL(client *gophercloud.ServiceClient, tenantID string) string { return client.ServiceURL("tenants", tenantID) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/tokens/000077500000000000000000000000001335005366400312265ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/tokens/doc.go000066400000000000000000000020321335005366400323170ustar00rootroot00000000000000/* Package tokens provides information and interaction with the token API resource for the OpenStack Identity service. For more information, see: http://developer.openstack.org/api-ref-identity-v2.html#identity-auth-v2 Example to Create an Unscoped Token from a Password authOpts := gophercloud.AuthOptions{ Username: "user", Password: "pass" } token, err := tokens.Create(identityClient, authOpts).ExtractToken() if err != nil { panic(err) } Example to Create a Token from a Tenant ID and Password authOpts := gophercloud.AuthOptions{ Username: "user", Password: "password", TenantID: "fc394f2ab2df4114bde39905f800dc57" } token, err := tokens.Create(identityClient, authOpts).ExtractToken() if err != nil { panic(err) } Example to Create a Token from a Tenant Name and Password authOpts := gophercloud.AuthOptions{ Username: "user", Password: "password", TenantName: "tenantname" } token, err := tokens.Create(identityClient, authOpts).ExtractToken() if err != nil { panic(err) } */ package tokens requests.go000066400000000000000000000067761335005366400333710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/tokenspackage tokens import "github.com/gophercloud/gophercloud" // PasswordCredentialsV2 represents the required options to authenticate // with a username and password. type PasswordCredentialsV2 struct { Username string `json:"username" required:"true"` Password string `json:"password" required:"true"` } // TokenCredentialsV2 represents the required options to authenticate // with a token. type TokenCredentialsV2 struct { ID string `json:"id,omitempty" required:"true"` } // AuthOptionsV2 wraps a gophercloud AuthOptions in order to adhere to the // AuthOptionsBuilder interface. type AuthOptionsV2 struct { PasswordCredentials *PasswordCredentialsV2 `json:"passwordCredentials,omitempty" xor:"TokenCredentials"` // The TenantID and TenantName fields are optional for the Identity V2 API. // Some providers allow you to specify a TenantName instead of the TenantId. // Some require both. Your provider's authentication policies will determine // how these fields influence authentication. TenantID string `json:"tenantId,omitempty"` TenantName string `json:"tenantName,omitempty"` // TokenCredentials allows users to authenticate (possibly as another user) // with an authentication token ID. TokenCredentials *TokenCredentialsV2 `json:"token,omitempty" xor:"PasswordCredentials"` } // AuthOptionsBuilder allows extensions to add additional parameters to the // token create request. type AuthOptionsBuilder interface { // ToTokenCreateMap assembles the Create request body, returning an error // if parameters are missing or inconsistent. ToTokenV2CreateMap() (map[string]interface{}, error) } // AuthOptions are the valid options for Openstack Identity v2 authentication. // For field descriptions, see gophercloud.AuthOptions. type AuthOptions struct { IdentityEndpoint string `json:"-"` Username string `json:"username,omitempty"` Password string `json:"password,omitempty"` TenantID string `json:"tenantId,omitempty"` TenantName string `json:"tenantName,omitempty"` AllowReauth bool `json:"-"` TokenID string } // ToTokenV2CreateMap builds a token request body from the given AuthOptions. func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { v2Opts := AuthOptionsV2{ TenantID: opts.TenantID, TenantName: opts.TenantName, } if opts.Password != "" { v2Opts.PasswordCredentials = &PasswordCredentialsV2{ Username: opts.Username, Password: opts.Password, } } else { v2Opts.TokenCredentials = &TokenCredentialsV2{ ID: opts.TokenID, } } b, err := gophercloud.BuildRequestBody(v2Opts, "auth") if err != nil { return nil, err } return b, nil } // Create authenticates to the identity service and attempts to acquire a Token. // Generally, rather than interact with this call directly, end users should // call openstack.AuthenticatedClient(), which abstracts all of the gory details // about navigating service catalogs and such. func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r CreateResult) { b, err := auth.ToTokenV2CreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, MoreHeaders: map[string]string{"X-Auth-Token": ""}, }) return } // Get validates and retrieves information for user's token. func Get(client *gophercloud.ServiceClient, token string) (r GetResult) { _, r.Err = client.Get(GetURL(client, token), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, }) return } results.go000066400000000000000000000114751335005366400332070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/tokenspackage tokens import ( "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" ) // Token provides only the most basic information related to an authentication // token. type Token struct { // ID provides the primary means of identifying a user to the OpenStack API. // OpenStack defines this field as an opaque value, so do not depend on its // content. It is safe, however, to compare for equality. ID string // ExpiresAt provides a timestamp in ISO 8601 format, indicating when the // authentication token becomes invalid. After this point in time, future // API requests made using this authentication token will respond with // errors. Either the caller will need to reauthenticate manually, or more // preferably, the caller should exploit automatic re-authentication. // See the AuthOptions structure for more details. ExpiresAt time.Time // Tenant provides information about the tenant to which this token grants // access. Tenant tenants.Tenant } // Role is a role for a user. type Role struct { Name string `json:"name"` } // User is an OpenStack user. type User struct { ID string `json:"id"` Name string `json:"name"` UserName string `json:"username"` Roles []Role `json:"roles"` } // Endpoint represents a single API endpoint offered by a service. // It provides the public and internal URLs, if supported, along with a region // specifier, again if provided. // // The significance of the Region field will depend upon your provider. // // In addition, the interface offered by the service will have version // information associated with it through the VersionId, VersionInfo, and // VersionList fields, if provided or supported. // // In all cases, fields which aren't supported by the provider and service // combined will assume a zero-value (""). type Endpoint struct { TenantID string `json:"tenantId"` PublicURL string `json:"publicURL"` InternalURL string `json:"internalURL"` AdminURL string `json:"adminURL"` Region string `json:"region"` VersionID string `json:"versionId"` VersionInfo string `json:"versionInfo"` VersionList string `json:"versionList"` } // CatalogEntry provides a type-safe interface to an Identity API V2 service // catalog listing. // // Each class of service, such as cloud DNS or block storage services, will have // a single CatalogEntry representing it. // // Note: when looking for the desired service, try, whenever possible, to key // off the type field. Otherwise, you'll tie the representation of the service // to a specific provider. type CatalogEntry struct { // Name will contain the provider-specified name for the service. Name string `json:"name"` // Type will contain a type string if OpenStack defines a type for the // service. Otherwise, for provider-specific services, the provider may assign // their own type strings. Type string `json:"type"` // Endpoints will let the caller iterate over all the different endpoints that // may exist for the service. Endpoints []Endpoint `json:"endpoints"` } // ServiceCatalog provides a view into the service catalog from a previous, // successful authentication. type ServiceCatalog struct { Entries []CatalogEntry } // CreateResult is the response from a Create request. Use ExtractToken() to // interpret it as a Token, or ExtractServiceCatalog() to interpret it as a // service catalog. type CreateResult struct { gophercloud.Result } // GetResult is the deferred response from a Get call, which is the same with a // Created token. Use ExtractUser() to interpret it as a User. type GetResult struct { CreateResult } // ExtractToken returns the just-created Token from a CreateResult. func (r CreateResult) ExtractToken() (*Token, error) { var s struct { Access struct { Token struct { Expires string `json:"expires"` ID string `json:"id"` Tenant tenants.Tenant `json:"tenant"` } `json:"token"` } `json:"access"` } err := r.ExtractInto(&s) if err != nil { return nil, err } expiresTs, err := time.Parse(gophercloud.RFC3339Milli, s.Access.Token.Expires) if err != nil { return nil, err } return &Token{ ID: s.Access.Token.ID, ExpiresAt: expiresTs, Tenant: s.Access.Token.Tenant, }, nil } // ExtractServiceCatalog returns the ServiceCatalog that was generated along // with the user's Token. func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) { var s struct { Access struct { Entries []CatalogEntry `json:"serviceCatalog"` } `json:"access"` } err := r.ExtractInto(&s) return &ServiceCatalog{Entries: s.Access.Entries}, err } // ExtractUser returns the User from a GetResult. func (r GetResult) ExtractUser() (*User, error) { var s struct { Access struct { User User `json:"user"` } `json:"access"` } err := r.ExtractInto(&s) return &s.Access.User, err } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/tokens/testing/000077500000000000000000000000001335005366400327035ustar00rootroot00000000000000doc.go000066400000000000000000000000451335005366400337170ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/tokens/testing// tokens unit tests package testing fixtures.go000066400000000000000000000123421335005366400350260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/tokens/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" th "github.com/gophercloud/gophercloud/testhelper" thclient "github.com/gophercloud/gophercloud/testhelper/client" ) // ExpectedToken is the token that should be parsed from TokenCreationResponse. var ExpectedToken = &tokens.Token{ ID: "aaaabbbbccccdddd", ExpiresAt: time.Date(2014, time.January, 31, 15, 30, 58, 0, time.UTC), Tenant: tenants.Tenant{ ID: "fc394f2ab2df4114bde39905f800dc57", Name: "test", Description: "There are many tenants. This one is yours.", Enabled: true, }, } // ExpectedServiceCatalog is the service catalog that should be parsed from TokenCreationResponse. var ExpectedServiceCatalog = &tokens.ServiceCatalog{ Entries: []tokens.CatalogEntry{ { Name: "inscrutablewalrus", Type: "something", Endpoints: []tokens.Endpoint{ { PublicURL: "http://something0:1234/v2/", Region: "region0", }, { PublicURL: "http://something1:1234/v2/", Region: "region1", }, }, }, { Name: "arbitrarypenguin", Type: "else", Endpoints: []tokens.Endpoint{ { PublicURL: "http://else0:4321/v3/", Region: "region0", }, }, }, }, } // ExpectedUser is the token that should be parsed from TokenGetResponse. var ExpectedUser = &tokens.User{ ID: "a530fefc3d594c4ba2693a4ecd6be74e", Name: "apiserver", Roles: []tokens.Role{tokens.Role{Name: "member"}, tokens.Role{Name: "service"}}, UserName: "apiserver", } // TokenCreationResponse is a JSON response that contains ExpectedToken and ExpectedServiceCatalog. const TokenCreationResponse = ` { "access": { "token": { "issued_at": "2014-01-30T15:30:58.000000Z", "expires": "2014-01-31T15:30:58Z", "id": "aaaabbbbccccdddd", "tenant": { "description": "There are many tenants. This one is yours.", "enabled": true, "id": "fc394f2ab2df4114bde39905f800dc57", "name": "test" } }, "serviceCatalog": [ { "endpoints": [ { "publicURL": "http://something0:1234/v2/", "region": "region0" }, { "publicURL": "http://something1:1234/v2/", "region": "region1" } ], "type": "something", "name": "inscrutablewalrus" }, { "endpoints": [ { "publicURL": "http://else0:4321/v3/", "region": "region0" } ], "type": "else", "name": "arbitrarypenguin" } ] } } ` // TokenGetResponse is a JSON response that contains ExpectedToken and ExpectedUser. const TokenGetResponse = ` { "access": { "token": { "issued_at": "2014-01-30T15:30:58.000000Z", "expires": "2014-01-31T15:30:58Z", "id": "aaaabbbbccccdddd", "tenant": { "description": "There are many tenants. This one is yours.", "enabled": true, "id": "fc394f2ab2df4114bde39905f800dc57", "name": "test" } }, "serviceCatalog": [], "user": { "id": "a530fefc3d594c4ba2693a4ecd6be74e", "name": "apiserver", "roles": [ { "name": "member" }, { "name": "service" } ], "roles_links": [], "username": "apiserver" } } }` // HandleTokenPost expects a POST against a /tokens handler, ensures that the request body has been // constructed properly given certain auth options, and returns the result. func HandleTokenPost(t *testing.T, requestJSON string) { th.Mux.HandleFunc("/tokens", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") if requestJSON != "" { th.TestJSONRequest(t, r, requestJSON) } w.WriteHeader(http.StatusOK) fmt.Fprintf(w, TokenCreationResponse) }) } // HandleTokenGet expects a Get against a /tokens handler, ensures that the request body has been // constructed properly given certain auth options, and returns the result. func HandleTokenGet(t *testing.T, token string) { th.Mux.HandleFunc("/tokens/"+token, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", thclient.TokenID) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, TokenGetResponse) }) } // IsSuccessful ensures that a CreateResult was successful and contains the correct token and // service catalog. func IsSuccessful(t *testing.T, result tokens.CreateResult) { token, err := result.ExtractToken() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedToken, token) serviceCatalog, err := result.ExtractServiceCatalog() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedServiceCatalog, serviceCatalog) } // GetIsSuccessful ensures that a GetResult was successful and contains the correct token and // User Info. func GetIsSuccessful(t *testing.T, result tokens.GetResult) { token, err := result.ExtractToken() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedToken, token) user, err := result.ExtractUser() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedUser, user) } requests_test.go000066400000000000000000000045021335005366400360660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/tokens/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func tokenPost(t *testing.T, options gophercloud.AuthOptions, requestJSON string) tokens.CreateResult { th.SetupHTTP() defer th.TeardownHTTP() HandleTokenPost(t, requestJSON) return tokens.Create(client.ServiceClient(), options) } func tokenPostErr(t *testing.T, options gophercloud.AuthOptions, expectedErr error) { th.SetupHTTP() defer th.TeardownHTTP() HandleTokenPost(t, "") actualErr := tokens.Create(client.ServiceClient(), options).Err th.CheckDeepEquals(t, expectedErr, actualErr) } func TestCreateWithPassword(t *testing.T) { options := gophercloud.AuthOptions{ Username: "me", Password: "swordfish", } IsSuccessful(t, tokenPost(t, options, ` { "auth": { "passwordCredentials": { "username": "me", "password": "swordfish" } } } `)) } func TestCreateTokenWithTenantID(t *testing.T) { options := gophercloud.AuthOptions{ Username: "me", Password: "opensesame", TenantID: "fc394f2ab2df4114bde39905f800dc57", } IsSuccessful(t, tokenPost(t, options, ` { "auth": { "tenantId": "fc394f2ab2df4114bde39905f800dc57", "passwordCredentials": { "username": "me", "password": "opensesame" } } } `)) } func TestCreateTokenWithTenantName(t *testing.T) { options := gophercloud.AuthOptions{ Username: "me", Password: "opensesame", TenantName: "demo", } IsSuccessful(t, tokenPost(t, options, ` { "auth": { "tenantName": "demo", "passwordCredentials": { "username": "me", "password": "opensesame" } } } `)) } func TestRequireUsername(t *testing.T) { options := gophercloud.AuthOptions{ Password: "thing", } tokenPostErr(t, options, gophercloud.ErrMissingInput{Argument: "Username"}) } func tokenGet(t *testing.T, tokenId string) tokens.GetResult { th.SetupHTTP() defer th.TeardownHTTP() HandleTokenGet(t, tokenId) return tokens.Get(client.ServiceClient(), tokenId) } func TestGetWithToken(t *testing.T) { GetIsSuccessful(t, tokenGet(t, "db22caf43c934e6c829087c41ff8d8d6")) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/tokens/urls.go000066400000000000000000000006011335005366400325370ustar00rootroot00000000000000package tokens import "github.com/gophercloud/gophercloud" // CreateURL generates the URL used to create new Tokens. func CreateURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("tokens") } // GetURL generates the URL used to Validate Tokens. func GetURL(client *gophercloud.ServiceClient, token string) string { return client.ServiceURL("tokens", token) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/users/000077500000000000000000000000001335005366400310645ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/users/doc.go000066400000000000000000000027101335005366400321600ustar00rootroot00000000000000/* Package users provides information and interaction with the users API resource for the OpenStack Identity Service. Example to List Users allPages, err := users.List(identityClient).AllPages() if err != nil { panic(err) } allUsers, err := users.ExtractUsers(allPages) if err != nil { panic(err) } for _, user := range allUsers { fmt.Printf("%+v\n", user) } Example to Create a User createOpts := users.CreateOpts{ Name: "name", TenantID: "c39e3de9be2d4c779f1dfd6abacc176d", Enabled: gophercloud.Enabled, } user, err := users.Create(identityClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a User userID := "9fe2ff9ee4384b1894a90878d3e92bab" updateOpts := users.UpdateOpts{ Name: "new_name", Enabled: gophercloud.Disabled, } user, err := users.Update(identityClient, userID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a User userID := "9fe2ff9ee4384b1894a90878d3e92bab" err := users.Delete(identityClient, userID).ExtractErr() if err != nil { panic(err) } Example to List a User's Roles tenantID := "1d8b6120dcc640fda4fc9194ffc80273" userID := "c39e3de9be2d4c779f1dfd6abacc176d" allPages, err := users.ListRoles(identityClient, tenantID, userID).AllPages() if err != nil { panic(err) } allRoles, err := users.ExtractRoles(allPages) if err != nil { panic(err) } for _, role := range allRoles { fmt.Printf("%+v\n", role) } */ package users requests.go000066400000000000000000000073671335005366400332240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/userspackage users import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List lists the existing users. func List(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, rootURL(client), func(r pagination.PageResult) pagination.Page { return UserPage{pagination.SinglePageBase(r)} }) } // CommonOpts are the parameters that are shared between CreateOpts and // UpdateOpts type CommonOpts struct { // Either a name or username is required. When provided, the value must be // unique or a 409 conflict error will be returned. If you provide a name but // omit a username, the latter will be set to the former; and vice versa. Name string `json:"name,omitempty"` Username string `json:"username,omitempty"` // TenantID is the ID of the tenant to which you want to assign this user. TenantID string `json:"tenantId,omitempty"` // Enabled indicates whether this user is enabled or not. Enabled *bool `json:"enabled,omitempty"` // Email is the email address of this user. Email string `json:"email,omitempty"` } // CreateOpts represents the options needed when creating new users. type CreateOpts CommonOpts // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToUserCreateMap() (map[string]interface{}, error) } // ToUserCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToUserCreateMap() (map[string]interface{}, error) { if opts.Name == "" && opts.Username == "" { err := gophercloud.ErrMissingInput{} err.Argument = "users.CreateOpts.Name/users.CreateOpts.Username" err.Info = "Either a Name or Username must be provided" return nil, err } return gophercloud.BuildRequestBody(opts, "user") } // Create is the operation responsible for creating new users. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToUserCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) return } // Get requests details on a single user, either by ID or Name. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(ResourceURL(client, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToUserUpdateMap() (map[string]interface{}, error) } // UpdateOpts specifies the base attributes that may be updated on an // existing server. type UpdateOpts CommonOpts // ToUserUpdateMap formats an UpdateOpts structure into a request body. func (opts UpdateOpts) ToUserUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "user") } // Update is the operation responsible for updating exist users by their ID. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToUserUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Put(ResourceURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete is the operation responsible for permanently deleting a User. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(ResourceURL(client, id), nil) return } // ListRoles lists the existing roles that can be assigned to users. func ListRoles(client *gophercloud.ServiceClient, tenantID, userID string) pagination.Pager { return pagination.NewPager(client, listRolesURL(client, tenantID, userID), func(r pagination.PageResult) pagination.Page { return RolePage{pagination.SinglePageBase(r)} }) } results.go000066400000000000000000000054611335005366400330430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/userspackage users import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // User represents a user resource that exists on the API. type User struct { // ID is the UUID for this user. ID string // Name is the human name for this user. Name string // Username is the username for this user. Username string // Enabled indicates whether the user is enabled (true) or disabled (false). Enabled bool // Email is the email address for this user. Email string // TenantID is the ID of the tenant to which this user belongs. TenantID string `json:"tenant_id"` } // Role assigns specific responsibilities to users, allowing them to accomplish // certain API operations whilst scoped to a service. type Role struct { // ID is the UUID of the role. ID string // Name is the name of the role. Name string } // UserPage is a single page of a User collection. type UserPage struct { pagination.SinglePageBase } // RolePage is a single page of a user Role collection. type RolePage struct { pagination.SinglePageBase } // IsEmpty determines whether or not a page of Users contains any results. func (r UserPage) IsEmpty() (bool, error) { users, err := ExtractUsers(r) return len(users) == 0, err } // ExtractUsers returns a slice of Users contained in a single page of results. func ExtractUsers(r pagination.Page) ([]User, error) { var s struct { Users []User `json:"users"` } err := (r.(UserPage)).ExtractInto(&s) return s.Users, err } // IsEmpty determines whether or not a page of Roles contains any results. func (r RolePage) IsEmpty() (bool, error) { users, err := ExtractRoles(r) return len(users) == 0, err } // ExtractRoles returns a slice of Roles contained in a single page of results. func ExtractRoles(r pagination.Page) ([]Role, error) { var s struct { Roles []Role `json:"roles"` } err := (r.(RolePage)).ExtractInto(&s) return s.Roles, err } type commonResult struct { gophercloud.Result } // Extract interprets any commonResult as a User, if possible. func (r commonResult) Extract() (*User, error) { var s struct { User *User `json:"user"` } err := r.ExtractInto(&s) return s.User, err } // CreateResult represents the result of a Create operation. Call its Extract // method to interpret the result as a User. type CreateResult struct { commonResult } // GetResult represents the result of a Get operation. Call its Extract method // to interpret the result as a User. type GetResult struct { commonResult } // UpdateResult represents the result of an Update operation. Call its Extract // method to interpret the result as a User. type UpdateResult struct { commonResult } // DeleteResult represents the result of a Delete operation. Call its // ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { commonResult } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/users/testing/000077500000000000000000000000001335005366400325415ustar00rootroot00000000000000doc.go000066400000000000000000000000441335005366400335540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/users/testing// users unit tests package testing fixtures.go000066400000000000000000000071361335005366400346710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/users/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func MockListUserResponse(t *testing.T) { th.Mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "users":[ { "id": "u1000", "name": "John Smith", "username": "jqsmith", "email": "john.smith@example.org", "enabled": true, "tenant_id": "12345" }, { "id": "u1001", "name": "Jane Smith", "username": "jqsmith", "email": "jane.smith@example.org", "enabled": true, "tenant_id": "12345" } ] } `) }) } func mockCreateUserResponse(t *testing.T) { th.Mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, ` { "user": { "name": "new_user", "tenantId": "12345", "enabled": false, "email": "new_user@foo.com" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "user": { "name": "new_user", "tenant_id": "12345", "enabled": false, "email": "new_user@foo.com", "id": "c39e3de9be2d4c779f1dfd6abacc176d" } } `) }) } func mockGetUserResponse(t *testing.T) { th.Mux.HandleFunc("/users/new_user", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "user": { "name": "new_user", "tenant_id": "12345", "enabled": false, "email": "new_user@foo.com", "id": "c39e3de9be2d4c779f1dfd6abacc176d" } } `) }) } func mockUpdateUserResponse(t *testing.T) { th.Mux.HandleFunc("/users/c39e3de9be2d4c779f1dfd6abacc176d", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, ` { "user": { "name": "new_name", "enabled": true, "email": "new_email@foo.com" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "user": { "name": "new_name", "tenant_id": "12345", "enabled": true, "email": "new_email@foo.com", "id": "c39e3de9be2d4c779f1dfd6abacc176d" } } `) }) } func mockDeleteUserResponse(t *testing.T) { th.Mux.HandleFunc("/users/c39e3de9be2d4c779f1dfd6abacc176d", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) } func mockListRolesResponse(t *testing.T) { th.Mux.HandleFunc("/tenants/1d8b6120dcc640fda4fc9194ffc80273/users/c39e3de9be2d4c779f1dfd6abacc176d/roles", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "roles": [ { "id": "9fe2ff9ee4384b1894a90878d3e92bab", "name": "foo_role" }, { "id": "1ea3d56793574b668e85960fbf651e13", "name": "admin" } ] } `) }) } requests_test.go000066400000000000000000000066011335005366400357260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/users/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/identity/v2/users" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListUserResponse(t) count := 0 err := users.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := users.ExtractUsers(page) th.AssertNoErr(t, err) expected := []users.User{ { ID: "u1000", Name: "John Smith", Username: "jqsmith", Email: "john.smith@example.org", Enabled: true, TenantID: "12345", }, { ID: "u1001", Name: "Jane Smith", Username: "jqsmith", Email: "jane.smith@example.org", Enabled: true, TenantID: "12345", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, count) } func TestCreateUser(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockCreateUserResponse(t) opts := users.CreateOpts{ Name: "new_user", TenantID: "12345", Enabled: gophercloud.Disabled, Email: "new_user@foo.com", } user, err := users.Create(client.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) expected := &users.User{ Name: "new_user", ID: "c39e3de9be2d4c779f1dfd6abacc176d", Email: "new_user@foo.com", Enabled: false, TenantID: "12345", } th.AssertDeepEquals(t, expected, user) } func TestGetUser(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockGetUserResponse(t) user, err := users.Get(client.ServiceClient(), "new_user").Extract() th.AssertNoErr(t, err) expected := &users.User{ Name: "new_user", ID: "c39e3de9be2d4c779f1dfd6abacc176d", Email: "new_user@foo.com", Enabled: false, TenantID: "12345", } th.AssertDeepEquals(t, expected, user) } func TestUpdateUser(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockUpdateUserResponse(t) id := "c39e3de9be2d4c779f1dfd6abacc176d" opts := users.UpdateOpts{ Name: "new_name", Enabled: gophercloud.Enabled, Email: "new_email@foo.com", } user, err := users.Update(client.ServiceClient(), id, opts).Extract() th.AssertNoErr(t, err) expected := &users.User{ Name: "new_name", ID: id, Email: "new_email@foo.com", Enabled: true, TenantID: "12345", } th.AssertDeepEquals(t, expected, user) } func TestDeleteUser(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockDeleteUserResponse(t) res := users.Delete(client.ServiceClient(), "c39e3de9be2d4c779f1dfd6abacc176d") th.AssertNoErr(t, res.Err) } func TestListingUserRoles(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() mockListRolesResponse(t) tenantID := "1d8b6120dcc640fda4fc9194ffc80273" userID := "c39e3de9be2d4c779f1dfd6abacc176d" err := users.ListRoles(client.ServiceClient(), tenantID, userID).EachPage(func(page pagination.Page) (bool, error) { actual, err := users.ExtractRoles(page) th.AssertNoErr(t, err) expected := []users.Role{ {ID: "9fe2ff9ee4384b1894a90878d3e92bab", Name: "foo_role"}, {ID: "1ea3d56793574b668e85960fbf651e13", Name: "admin"}, } th.CheckDeepEquals(t, expected, actual) return true, nil }) th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v2/users/urls.go000066400000000000000000000007451335005366400324060ustar00rootroot00000000000000package users import "github.com/gophercloud/gophercloud" const ( tenantPath = "tenants" userPath = "users" rolePath = "roles" ) func ResourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(userPath, id) } func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(userPath) } func listRolesURL(c *gophercloud.ServiceClient, tenantID, userID string) string { return c.ServiceURL(tenantPath, tenantID, userPath, userID, rolePath) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/000077500000000000000000000000001335005366400277245ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/domains/000077500000000000000000000000001335005366400313565ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/domains/doc.go000066400000000000000000000021771335005366400324610ustar00rootroot00000000000000/* Package domains manages and retrieves Domains in the OpenStack Identity Service. Example to List Domains var iTrue bool = true listOpts := domains.ListOpts{ Enabled: &iTrue, } allPages, err := domains.List(identityClient, listOpts).AllPages() if err != nil { panic(err) } allDomains, err := domains.ExtractDomains(allPages) if err != nil { panic(err) } for _, domain := range allDomains { fmt.Printf("%+v\n", domain) } Example to Create a Domain createOpts := domains.CreateOpts{ Name: "domain name", Description: "Test domain", } domain, err := domains.Create(identityClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Domain domainID := "0fe36e73809d46aeae6705c39077b1b3" var iFalse bool = false updateOpts := domains.UpdateOpts{ Enabled: &iFalse, } domain, err := domains.Update(identityClient, domainID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Domain domainID := "0fe36e73809d46aeae6705c39077b1b3" err := domains.Delete(identityClient, domainID).ExtractErr() if err != nil { panic(err) } */ package domains requests.go000066400000000000000000000072121335005366400335030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/domainspackage domains import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to // the List request type ListOptsBuilder interface { ToDomainListQuery() (string, error) } // ListOpts provides options to filter the List results. type ListOpts struct { // Enabled filters the response by enabled domains. Enabled *bool `q:"enabled"` // Name filters the response by domain name. Name string `q:"name"` } // ToDomainListQuery formats a ListOpts into a query string. func (opts ListOpts) ToDomainListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List enumerates the domains to which the current token has access. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToDomainListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return DomainPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves details on a single domain, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { ToDomainCreateMap() (map[string]interface{}, error) } // CreateOpts provides options used to create a domain. type CreateOpts struct { // Name is the name of the new domain. Name string `json:"name" required:"true"` // Description is a description of the domain. Description string `json:"description,omitempty"` // Enabled sets the domain status to enabled or disabled. Enabled *bool `json:"enabled,omitempty"` } // ToDomainCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToDomainCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "domain") } // Create creates a new Domain. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToDomainCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) return } // Delete deletes a domain. func Delete(client *gophercloud.ServiceClient, domainID string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, domainID), nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { ToDomainUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents parameters to update a domain. type UpdateOpts struct { // Name is the name of the domain. Name string `json:"name,omitempty"` // Description is the description of the domain. Description string `json:"description,omitempty"` // Enabled sets the domain status to enabled or disabled. Enabled *bool `json:"enabled,omitempty"` } // ToUpdateCreateMap formats a UpdateOpts into an update request. func (opts UpdateOpts) ToDomainUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "domain") } // Update modifies the attributes of a domain. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToDomainUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000045471335005366400333410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/domainspackage domains import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // A Domain is a collection of projects, users, and roles. type Domain struct { // Description is the description of the Domain. Description string `json:"description"` // Enabled is whether or not the domain is enabled. Enabled bool `json:"enabled"` // ID is the unique ID of the domain. ID string `json:"id"` // Links contains referencing links to the domain. Links map[string]interface{} `json:"links"` // Name is the name of the domain. Name string `json:"name"` } type domainResult struct { gophercloud.Result } // GetResult is the response from a Get operation. Call its Extract method // to interpret it as a Domain. type GetResult struct { domainResult } // CreateResult is the response from a Create operation. Call its Extract method // to interpret it as a Domain. type CreateResult struct { domainResult } // DeleteResult is the response from a Delete operation. Call its ExtractErr to // determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // UpdateResult is the result of an Update request. Call its Extract method to // interpret it as a Domain. type UpdateResult struct { domainResult } // DomainPage is a single page of Domain results. type DomainPage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a page of Domains contains any results. func (r DomainPage) IsEmpty() (bool, error) { domains, err := ExtractDomains(r) return len(domains) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (r DomainPage) NextPageURL() (string, error) { var s struct { Links struct { Next string `json:"next"` Previous string `json:"previous"` } `json:"links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return s.Links.Next, err } // ExtractDomains returns a slice of Domains contained in a single page of // results. func ExtractDomains(r pagination.Page) ([]Domain, error) { var s struct { Domains []Domain `json:"domains"` } err := (r.(DomainPage)).ExtractInto(&s) return s.Domains, err } // Extract interprets any domainResults as a Domain. func (r domainResult) Extract() (*Domain, error) { var s struct { Domain *Domain `json:"domain"` } err := r.ExtractInto(&s) return s.Domain, err } testing/000077500000000000000000000000001335005366400327545ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/domainsfixtures.go000066400000000000000000000122421335005366400351550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/domains/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/domains" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListOutput provides a single page of Domain results. const ListOutput = ` { "links": { "next": null, "previous": null, "self": "http://example.com/identity/v3/domains" }, "domains": [ { "enabled": true, "id": "2844b2a08be147a08ef58317d6471f1f", "links": { "self": "http://example.com/identity/v3/domains/2844b2a08be147a08ef58317d6471f1f" }, "name": "domain one", "description": "some description" }, { "enabled": true, "id": "9fe1d3", "links": { "self": "https://example.com/identity/v3/domains/9fe1d3" }, "name": "domain two" } ] } ` // GetOutput provides a Get result. const GetOutput = ` { "domain": { "enabled": true, "id": "9fe1d3", "links": { "self": "https://example.com/identity/v3/domains/9fe1d3" }, "name": "domain two" } } ` // CreateRequest provides the input to a Create request. const CreateRequest = ` { "domain": { "name": "domain two" } } ` // UpdateRequest provides the input to as Update request. const UpdateRequest = ` { "domain": { "description": "Staging Domain" } } ` // UpdateOutput provides an update result. const UpdateOutput = ` { "domain": { "enabled": true, "id": "9fe1d3", "links": { "self": "https://example.com/identity/v3/domains/9fe1d3" }, "name": "domain two", "description": "Staging Domain" } } ` // FirstDomain is the first domain in the List request. var FirstDomain = domains.Domain{ Enabled: true, ID: "2844b2a08be147a08ef58317d6471f1f", Links: map[string]interface{}{ "self": "http://example.com/identity/v3/domains/2844b2a08be147a08ef58317d6471f1f", }, Name: "domain one", Description: "some description", } // SecondDomain is the second domain in the List request. var SecondDomain = domains.Domain{ Enabled: true, ID: "9fe1d3", Links: map[string]interface{}{ "self": "https://example.com/identity/v3/domains/9fe1d3", }, Name: "domain two", } // SecondDomainUpdated is how SecondDomain should look after an Update. var SecondDomainUpdated = domains.Domain{ Enabled: true, ID: "9fe1d3", Links: map[string]interface{}{ "self": "https://example.com/identity/v3/domains/9fe1d3", }, Name: "domain two", Description: "Staging Domain", } // ExpectedDomainsSlice is the slice of domains expected to be returned from ListOutput. var ExpectedDomainsSlice = []domains.Domain{FirstDomain, SecondDomain} // HandleListDomainsSuccessfully creates an HTTP handler at `/domains` on the // test handler mux that responds with a list of two domains. func HandleListDomainsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/domains", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListOutput) }) } // HandleGetDomainSuccessfully creates an HTTP handler at `/domains` on the // test handler mux that responds with a single domain. func HandleGetDomainSuccessfully(t *testing.T) { th.Mux.HandleFunc("/domains/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetOutput) }) } // HandleCreateDomainSuccessfully creates an HTTP handler at `/domains` on the // test handler mux that tests domain creation. func HandleCreateDomainSuccessfully(t *testing.T) { th.Mux.HandleFunc("/domains", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, GetOutput) }) } // HandleDeleteDomainSuccessfully creates an HTTP handler at `/domains` on the // test handler mux that tests domain deletion. func HandleDeleteDomainSuccessfully(t *testing.T) { th.Mux.HandleFunc("/domains/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleUpdateDomainSuccessfully creates an HTTP handler at `/domains` on the // test handler mux that tests domain update. func HandleUpdateDomainSuccessfully(t *testing.T) { th.Mux.HandleFunc("/domains/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, UpdateOutput) }) } requests_test.go000066400000000000000000000042171335005366400362210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/domains/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/domains" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListDomains(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListDomainsSuccessfully(t) count := 0 err := domains.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := domains.ExtractDomains(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedDomainsSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListDomainsAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListDomainsSuccessfully(t) allPages, err := domains.List(client.ServiceClient(), nil).AllPages() th.AssertNoErr(t, err) actual, err := domains.ExtractDomains(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedDomainsSlice, actual) } func TestGetDomain(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetDomainSuccessfully(t) actual, err := domains.Get(client.ServiceClient(), "9fe1d3").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondDomain, *actual) } func TestCreateDomain(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateDomainSuccessfully(t) createOpts := domains.CreateOpts{ Name: "domain two", } actual, err := domains.Create(client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondDomain, *actual) } func TestDeleteDomain(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteDomainSuccessfully(t) res := domains.Delete(client.ServiceClient(), "9fe1d3") th.AssertNoErr(t, res.Err) } func TestUpdateDomain(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateDomainSuccessfully(t) updateOpts := domains.UpdateOpts{ Description: "Staging Domain", } actual, err := domains.Update(client.ServiceClient(), "9fe1d3", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondDomainUpdated, *actual) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/domains/urls.go000066400000000000000000000011701335005366400326710ustar00rootroot00000000000000package domains import "github.com/gophercloud/gophercloud" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("domains") } func getURL(client *gophercloud.ServiceClient, domainID string) string { return client.ServiceURL("domains", domainID) } func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("domains") } func deleteURL(client *gophercloud.ServiceClient, domainID string) string { return client.ServiceURL("domains", domainID) } func updateURL(client *gophercloud.ServiceClient, domainID string) string { return client.ServiceURL("domains", domainID) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/endpoints/000077500000000000000000000000001335005366400317275ustar00rootroot00000000000000doc.go000066400000000000000000000027501335005366400327500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/endpoints/* Package endpoints provides information and interaction with the service endpoints API resource in the OpenStack Identity service. For more information, see: http://developer.openstack.org/api-ref-identity-v3.html#endpoints-v3 Example to List Endpoints serviceID := "e629d6e599d9489fb3ae5d9cc12eaea3" listOpts := endpoints.ListOpts{ ServiceID: serviceID, } allPages, err := endpoints.List(identityClient, listOpts).AllPages() if err != nil { panic(err) } allEndpoints, err := endpoints.ExtractEndpoints(allPages) if err != nil { panic(err) } for _, endpoint := range allEndpoints { fmt.Printf("%+v\n", endpoint) } Example to Create an Endpoint serviceID := "e629d6e599d9489fb3ae5d9cc12eaea3" createOpts := endpoints.CreateOpts{ Availability: gophercloud.AvailabilityPublic, Name: "neutron", Region: "RegionOne", URL: "https://localhost:9696", ServiceID: serviceID, } endpoint, err := endpoints.Create(identityClient, createOpts).Extract() if err != nil { panic(err) } Example to Update an Endpoint endpointID := "ad59deeec5154d1fa0dcff518596f499" updateOpts := endpoints.UpdateOpts{ Region: "RegionTwo", } endpoint, err := endpoints.Update(identityClient, endpointID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete an Endpoint endpointID := "ad59deeec5154d1fa0dcff518596f499" err := endpoints.Delete(identityClient, endpointID).ExtractErr() if err != nil { panic(err) } */ package endpoints requests.go000066400000000000000000000107261335005366400340600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/endpointspackage endpoints import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type CreateOptsBuilder interface { ToEndpointCreateMap() (map[string]interface{}, error) } // CreateOpts contains the subset of Endpoint attributes that should be used // to create an Endpoint. type CreateOpts struct { // Availability is the interface type of the Endpoint (admin, internal, // or public), referenced by the gophercloud.Availability type. Availability gophercloud.Availability `json:"interface" required:"true"` // Name is the name of the Endpoint. Name string `json:"name" required:"true"` // Region is the region the Endpoint is located in. // This field can be omitted or left as a blank string. Region string `json:"region,omitempty"` // URL is the url of the Endpoint. URL string `json:"url" required:"true"` // ServiceID is the ID of the service the Endpoint refers to. ServiceID string `json:"service_id" required:"true"` } // ToEndpointCreateMap builds a request body from the Endpoint Create options. func (opts CreateOpts) ToEndpointCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "endpoint") } // Create inserts a new Endpoint into the service catalog. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToEndpointCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(listURL(client), &b, &r.Body, nil) return } // ListOptsBuilder allows extensions to add parameters to the List request. type ListOptsBuilder interface { ToEndpointListParams() (string, error) } // ListOpts allows finer control over the endpoints returned by a List call. // All fields are optional. type ListOpts struct { // Availability is the interface type of the Endpoint (admin, internal, // or public), referenced by the gophercloud.Availability type. Availability gophercloud.Availability `q:"interface"` // ServiceID is the ID of the service the Endpoint refers to. ServiceID string `q:"service_id"` // RegionID is the ID of the region the Endpoint refers to. RegionID int `q:"region_id"` } // ToEndpointListParams builds a list request from the List options. func (opts ListOpts) ToEndpointListParams() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List enumerates endpoints in a paginated collection, optionally filtered // by ListOpts criteria. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { u := listURL(client) if opts != nil { q, err := gophercloud.BuildQueryString(opts) if err != nil { return pagination.Pager{Err: err} } u += q.String() } return pagination.NewPager(client, u, func(r pagination.PageResult) pagination.Page { return EndpointPage{pagination.LinkedPageBase{PageResult: r}} }) } // UpdateOptsBuilder allows extensions to add parameters to the Update request. type UpdateOptsBuilder interface { ToEndpointUpdateMap() (map[string]interface{}, error) } // UpdateOpts contains the subset of Endpoint attributes that should be used to // update an Endpoint. type UpdateOpts struct { // Availability is the interface type of the Endpoint (admin, internal, // or public), referenced by the gophercloud.Availability type. Availability gophercloud.Availability `json:"interface,omitempty"` // Name is the name of the Endpoint. Name string `json:"name,omitempty"` // Region is the region the Endpoint is located in. // This field can be omitted or left as a blank string. Region string `json:"region,omitempty"` // URL is the url of the Endpoint. URL string `json:"url,omitempty"` // ServiceID is the ID of the service the Endpoint refers to. ServiceID string `json:"service_id,omitempty"` } // ToEndpointUpdateMap builds an update request body from the Update options. func (opts UpdateOpts) ToEndpointUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "endpoint") } // Update changes an existing endpoint with new data. func Update(client *gophercloud.ServiceClient, endpointID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToEndpointUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Patch(endpointURL(client, endpointID), &b, &r.Body, nil) return } // Delete removes an endpoint from the service catalog. func Delete(client *gophercloud.ServiceClient, endpointID string) (r DeleteResult) { _, r.Err = client.Delete(endpointURL(client, endpointID), nil) return } results.go000066400000000000000000000042221335005366400337000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/endpointspackage endpoints import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { gophercloud.Result } // Extract interprets a GetResult, CreateResult or UpdateResult as a concrete // Endpoint. An error is returned if the original call or the extraction failed. func (r commonResult) Extract() (*Endpoint, error) { var s struct { Endpoint *Endpoint `json:"endpoint"` } err := r.ExtractInto(&s) return s.Endpoint, err } // CreateResult is the response from a Create operation. Call its Extract // method to interpret it as an Endpoint. type CreateResult struct { commonResult } // UpdateResult is the response from an Update operation. Call its Extract // method to interpret it as an Endpoint. type UpdateResult struct { commonResult } // DeleteResult is the response from a Delete operation. Call its ExtractErr // method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // Endpoint describes the entry point for another service's API. type Endpoint struct { // ID is the unique ID of the endpoint. ID string `json:"id"` // Availability is the interface type of the Endpoint (admin, internal, // or public), referenced by the gophercloud.Availability type. Availability gophercloud.Availability `json:"interface"` // Name is the name of the Endpoint. Name string `json:"name"` // Region is the region the Endpoint is located in. Region string `json:"region"` // ServiceID is the ID of the service the Endpoint refers to. ServiceID string `json:"service_id"` // URL is the url of the Endpoint. URL string `json:"url"` } // EndpointPage is a single page of Endpoint results. type EndpointPage struct { pagination.LinkedPageBase } // IsEmpty returns true if no Endpoints were returned. func (r EndpointPage) IsEmpty() (bool, error) { es, err := ExtractEndpoints(r) return len(es) == 0, err } // ExtractEndpoints extracts an Endpoint slice from a Page. func ExtractEndpoints(r pagination.Page) ([]Endpoint, error) { var s struct { Endpoints []Endpoint `json:"endpoints"` } err := (r.(EndpointPage)).ExtractInto(&s) return s.Endpoints, err } testing/000077500000000000000000000000001335005366400333255ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/endpointsdoc.go000066400000000000000000000000501335005366400344140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/endpoints/testing// endpoints unit tests package testing requests_test.go000066400000000000000000000123511335005366400365700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/endpoints/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/identity/v3/endpoints" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreateSuccessful(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/endpoints", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, ` { "endpoint": { "interface": "public", "name": "the-endiest-of-points", "region": "underground", "url": "https://1.2.3.4:9000/", "service_id": "asdfasdfasdfasdf" } } `) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "endpoint": { "id": "12", "interface": "public", "links": { "self": "https://localhost:5000/v3/endpoints/12" }, "name": "the-endiest-of-points", "region": "underground", "service_id": "asdfasdfasdfasdf", "url": "https://1.2.3.4:9000/" } } `) }) actual, err := endpoints.Create(client.ServiceClient(), endpoints.CreateOpts{ Availability: gophercloud.AvailabilityPublic, Name: "the-endiest-of-points", Region: "underground", URL: "https://1.2.3.4:9000/", ServiceID: "asdfasdfasdfasdf", }).Extract() th.AssertNoErr(t, err) expected := &endpoints.Endpoint{ ID: "12", Availability: gophercloud.AvailabilityPublic, Name: "the-endiest-of-points", Region: "underground", ServiceID: "asdfasdfasdfasdf", URL: "https://1.2.3.4:9000/", } th.AssertDeepEquals(t, expected, actual) } func TestListEndpoints(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/endpoints", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ` { "endpoints": [ { "id": "12", "interface": "public", "links": { "self": "https://localhost:5000/v3/endpoints/12" }, "name": "the-endiest-of-points", "region": "underground", "service_id": "asdfasdfasdfasdf", "url": "https://1.2.3.4:9000/" }, { "id": "13", "interface": "internal", "links": { "self": "https://localhost:5000/v3/endpoints/13" }, "name": "shhhh", "region": "underground", "service_id": "asdfasdfasdfasdf", "url": "https://1.2.3.4:9001/" } ], "links": { "next": null, "previous": null } } `) }) count := 0 endpoints.List(client.ServiceClient(), endpoints.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := endpoints.ExtractEndpoints(page) if err != nil { t.Errorf("Failed to extract endpoints: %v", err) return false, err } expected := []endpoints.Endpoint{ { ID: "12", Availability: gophercloud.AvailabilityPublic, Name: "the-endiest-of-points", Region: "underground", ServiceID: "asdfasdfasdfasdf", URL: "https://1.2.3.4:9000/", }, { ID: "13", Availability: gophercloud.AvailabilityInternal, Name: "shhhh", Region: "underground", ServiceID: "asdfasdfasdfasdf", URL: "https://1.2.3.4:9001/", }, } th.AssertDeepEquals(t, expected, actual) return true, nil }) th.AssertEquals(t, 1, count) } func TestUpdateEndpoint(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/endpoints/12", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, ` { "endpoint": { "name": "renamed", "region": "somewhere-else" } } `) fmt.Fprintf(w, ` { "endpoint": { "id": "12", "interface": "public", "links": { "self": "https://localhost:5000/v3/endpoints/12" }, "name": "renamed", "region": "somewhere-else", "service_id": "asdfasdfasdfasdf", "url": "https://1.2.3.4:9000/" } } `) }) actual, err := endpoints.Update(client.ServiceClient(), "12", endpoints.UpdateOpts{ Name: "renamed", Region: "somewhere-else", }).Extract() if err != nil { t.Fatalf("Unexpected error from Update: %v", err) } expected := &endpoints.Endpoint{ ID: "12", Availability: gophercloud.AvailabilityPublic, Name: "renamed", Region: "somewhere-else", ServiceID: "asdfasdfasdfasdf", URL: "https://1.2.3.4:9000/", } th.AssertDeepEquals(t, expected, actual) } func TestDeleteEndpoint(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/endpoints/34", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) res := endpoints.Delete(client.ServiceClient(), "34") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000004501335005366400331630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/endpointspackage endpoints import "github.com/gophercloud/gophercloud" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("endpoints") } func endpointURL(client *gophercloud.ServiceClient, endpointID string) string { return client.ServiceURL("endpoints", endpointID) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/extensions/000077500000000000000000000000001335005366400321235ustar00rootroot00000000000000trusts/000077500000000000000000000000001335005366400334105ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/extensionsdoc.go000066400000000000000000000010041335005366400344770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/extensions/trusts/* Package trusts enables management of OpenStack Identity Trusts. Example to Create a Token with Username, Password, and Trust ID var trustToken struct { tokens.Token trusts.TokenExt } authOptions := tokens.AuthOptions{ UserID: "username", Password: "password", } createOpts := trusts.AuthOptsExt{ AuthOptionsBuilder: authOptions, TrustID: "de0945a", } err := tokens.Create(identityClient, createOpts).ExtractInto(&trustToken) if err != nil { panic(err) } */ package trusts requests.go000066400000000000000000000017671335005366400356250ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/extensions/trustspackage trusts import "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" // AuthOptsExt extends the base Identity v3 tokens AuthOpts with a TrustID. type AuthOptsExt struct { tokens.AuthOptionsBuilder // TrustID is the ID of the trust. TrustID string `json:"id"` } // ToTokenV3CreateMap builds a create request body from the AuthOpts. func (opts AuthOptsExt) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { return opts.AuthOptionsBuilder.ToTokenV3CreateMap(scope) } // ToTokenV3ScopeMap builds a scope from AuthOpts. func (opts AuthOptsExt) ToTokenV3ScopeMap() (map[string]interface{}, error) { b, err := opts.AuthOptionsBuilder.ToTokenV3ScopeMap() if err != nil { return nil, err } if opts.TrustID != "" { if b == nil { b = make(map[string]interface{}) } b["OS-TRUST:trust"] = map[string]interface{}{ "id": opts.TrustID, } } return b, nil } func (opts AuthOptsExt) CanReauth() bool { return opts.AuthOptionsBuilder.CanReauth() } results.go000066400000000000000000000014321335005366400354400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/extensions/trustspackage trusts // TrusteeUser represents the trusted user ID of a trust. type TrusteeUser struct { ID string `json:"id"` } // TrustorUser represents the trusting user ID of a trust. type TrustorUser struct { ID string `json:"id"` } // Trust represents a delegated authorization request between two // identities. type Trust struct { ID string `json:"id"` Impersonation bool `json:"impersonation"` TrusteeUser TrusteeUser `json:"trustee_user"` TrustorUser TrustorUser `json:"trustor_user"` RedelegatedTrustID string `json:"redelegated_trust_id"` RedelegationCount int `json:"redelegation_count"` } // TokenExt represents an extension of the base token result. type TokenExt struct { Trust Trust `json:"OS-TRUST:trust"` } testing/000077500000000000000000000000001335005366400350655ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/extensions/trustsdoc.go000066400000000000000000000000451335005366400361600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/extensions/trusts/testing// trusts unit tests package testing fixtures.go000066400000000000000000000040531335005366400372670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/extensions/trusts/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/testhelper" ) // HandleCreateTokenWithTrustID verifies that providing certain AuthOptions and Scope results in an expected JSON structure. func HandleCreateTokenWithTrustID(t *testing.T, options tokens.AuthOptionsBuilder, requestJSON string) { testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "POST") testhelper.TestHeader(t, r, "Content-Type", "application/json") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestJSONRequest(t, r, requestJSON) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, `{ "token": { "expires_at": "2013-02-27T18:30:59.999999Z", "issued_at": "2013-02-27T16:30:59.999999Z", "methods": [ "password" ], "OS-TRUST:trust": { "id": "fe0aef", "impersonation": false, "redelegated_trust_id": "3ba234", "redelegation_count": 2, "links": { "self": "http://example.com/identity/v3/trusts/fe0aef" }, "trustee_user": { "id": "0ca8f6", "links": { "self": "http://example.com/identity/v3/users/0ca8f6" } }, "trustor_user": { "id": "bd263c", "links": { "self": "http://example.com/identity/v3/users/bd263c" } } }, "user": { "domain": { "id": "1789d1", "links": { "self": "http://example.com/identity/v3/domains/1789d1" }, "name": "example.com" }, "email": "joe@example.com", "id": "0ca8f6", "links": { "self": "http://example.com/identity/v3/users/0ca8f6" }, "name": "Joe" } } }`) }) } requests_test.go000066400000000000000000000030121335005366400403220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/extensions/trusts/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreateUserIDPasswordTrustID(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() ao := trusts.AuthOptsExt{ TrustID: "de0945a", AuthOptionsBuilder: &tokens.AuthOptions{ UserID: "me", Password: "squirrel!", }, } HandleCreateTokenWithTrustID(t, ao, ` { "auth": { "identity": { "methods": ["password"], "password": { "user": { "id": "me", "password": "squirrel!" } } }, "scope": { "OS-TRUST:trust": { "id": "de0945a" } } } } `) var actual struct { tokens.Token trusts.TokenExt } err := tokens.Create(client.ServiceClient(), ao).ExtractInto(&actual) if err != nil { t.Errorf("Create returned an error: %v", err) } expected := struct { tokens.Token trusts.TokenExt }{ tokens.Token{ ExpiresAt: time.Date(2013, 02, 27, 18, 30, 59, 999999000, time.UTC), }, trusts.TokenExt{ Trust: trusts.Trust{ ID: "fe0aef", Impersonation: false, TrusteeUser: trusts.TrusteeUser{ ID: "0ca8f6", }, TrustorUser: trusts.TrustorUser{ ID: "bd263c", }, RedelegatedTrustID: "3ba234", RedelegationCount: 2, }, }, } th.AssertDeepEquals(t, expected, actual) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/groups/000077500000000000000000000000001335005366400312435ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/groups/doc.go000066400000000000000000000022311335005366400323350ustar00rootroot00000000000000/* Package groups manages and retrieves Groups in the OpenStack Identity Service. Example to List Groups listOpts := groups.ListOpts{ DomainID: "default", } allPages, err := groups.List(identityClient, listOpts).AllPages() if err != nil { panic(err) } allGroups, err := groups.ExtractGroups(allPages) if err != nil { panic(err) } for _, group := range allGroups { fmt.Printf("%+v\n", group) } Example to Create a Group createOpts := groups.CreateOpts{ Name: "groupname", DomainID: "default", Extra: map[string]interface{}{ "email": "groupname@example.com", } } group, err := groups.Create(identityClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Group groupID := "0fe36e73809d46aeae6705c39077b1b3" updateOpts := groups.UpdateOpts{ Description: "Updated Description for group", } group, err := groups.Update(identityClient, groupID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Group groupID := "0fe36e73809d46aeae6705c39077b1b3" err := groups.Delete(identityClient, groupID).ExtractErr() if err != nil { panic(err) } */ package groups errors.go000066400000000000000000000005461335005366400330340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/groupspackage groups import "fmt" // InvalidListFilter is returned by the ToUserListQuery method when validation of // a filter does not pass type InvalidListFilter struct { FilterName string } func (e InvalidListFilter) Error() string { s := fmt.Sprintf( "Invalid filter name [%s]: it must be in format of NAME__COMPARATOR", e.FilterName, ) return s } requests.go000066400000000000000000000112331335005366400333660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/groupspackage groups import ( "net/url" "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to // the List request type ListOptsBuilder interface { ToGroupListQuery() (string, error) } // ListOpts provides options to filter the List results. type ListOpts struct { // DomainID filters the response by a domain ID. DomainID string `q:"domain_id"` // Name filters the response by group name. Name string `q:"name"` // Filters filters the response by custom filters such as // 'name__contains=foo' Filters map[string]string `q:"-"` } // ToGroupListQuery formats a ListOpts into a query string. func (opts ListOpts) ToGroupListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return "", err } params := q.Query() for k, v := range opts.Filters { i := strings.Index(k, "__") if i > 0 && i < len(k)-2 { params.Add(k, v) } else { return "", InvalidListFilter{FilterName: k} } } q = &url.URL{RawQuery: params.Encode()} return q.String(), err } // List enumerates the Groups to which the current token has access. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToGroupListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return GroupPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves details on a single group, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { ToGroupCreateMap() (map[string]interface{}, error) } // CreateOpts provides options used to create a group. type CreateOpts struct { // Name is the name of the new group. Name string `json:"name" required:"true"` // Description is a description of the group. Description string `json:"description,omitempty"` // DomainID is the ID of the domain the group belongs to. DomainID string `json:"domain_id,omitempty"` // Extra is free-form extra key/value pairs to describe the group. Extra map[string]interface{} `json:"-"` } // ToGroupCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToGroupCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "group") if err != nil { return nil, err } if opts.Extra != nil { if v, ok := b["group"].(map[string]interface{}); ok { for key, value := range opts.Extra { v[key] = value } } } return b, nil } // Create creates a new Group. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToGroupCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) return } // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { ToGroupUpdateMap() (map[string]interface{}, error) } // UpdateOpts provides options for updating a group. type UpdateOpts struct { // Name is the name of the new group. Name string `json:"name,omitempty"` // Description is a description of the group. Description string `json:"description,omitempty"` // DomainID is the ID of the domain the group belongs to. DomainID string `json:"domain_id,omitempty"` // Extra is free-form extra key/value pairs to describe the group. Extra map[string]interface{} `json:"-"` } // ToGroupUpdateMap formats a UpdateOpts into an update request. func (opts UpdateOpts) ToGroupUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "group") if err != nil { return nil, err } if opts.Extra != nil { if v, ok := b["group"].(map[string]interface{}); ok { for key, value := range opts.Extra { v[key] = value } } } return b, nil } // Update updates an existing Group. func Update(client *gophercloud.ServiceClient, groupID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToGroupUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Patch(updateURL(client, groupID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete deletes a group. func Delete(client *gophercloud.ServiceClient, groupID string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, groupID), nil) return } results.go000066400000000000000000000060611335005366400332170ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/groupspackage groups import ( "encoding/json" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) // Group helps manage related users. type Group struct { // Description describes the group purpose. Description string `json:"description"` // DomainID is the domain ID the group belongs to. DomainID string `json:"domain_id"` // ID is the unique ID of the group. ID string `json:"id"` // Extra is a collection of miscellaneous key/values. Extra map[string]interface{} `json:"-"` // Links contains referencing links to the group. Links map[string]interface{} `json:"links"` // Name is the name of the group. Name string `json:"name"` } func (r *Group) UnmarshalJSON(b []byte) error { type tmp Group var s struct { tmp Extra map[string]interface{} `json:"extra"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Group(s.tmp) // Collect other fields and bundle them into Extra // but only if a field titled "extra" wasn't sent. if s.Extra != nil { r.Extra = s.Extra } else { var result interface{} err := json.Unmarshal(b, &result) if err != nil { return err } if resultMap, ok := result.(map[string]interface{}); ok { r.Extra = internal.RemainingKeys(Group{}, resultMap) } } return err } type groupResult struct { gophercloud.Result } // GetResult is the response from a Get operation. Call its Extract method // to interpret it as a Group. type GetResult struct { groupResult } // CreateResult is the response from a Create operation. Call its Extract method // to interpret it as a Group. type CreateResult struct { groupResult } // UpdateResult is the response from an Update operation. Call its Extract // method to interpret it as a Group. type UpdateResult struct { groupResult } // DeleteResult is the response from a Delete operation. Call its ExtractErr to // determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // GroupPage is a single page of Group results. type GroupPage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a page of Groups contains any results. func (r GroupPage) IsEmpty() (bool, error) { groups, err := ExtractGroups(r) return len(groups) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (r GroupPage) NextPageURL() (string, error) { var s struct { Links struct { Next string `json:"next"` Previous string `json:"previous"` } `json:"links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return s.Links.Next, err } // ExtractGroups returns a slice of Groups contained in a single page of results. func ExtractGroups(r pagination.Page) ([]Group, error) { var s struct { Groups []Group `json:"groups"` } err := (r.(GroupPage)).ExtractInto(&s) return s.Groups, err } // Extract interprets any group results as a Group. func (r groupResult) Extract() (*Group, error) { var s struct { Group *Group `json:"group"` } err := r.ExtractInto(&s) return s.Group, err } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/groups/testing/000077500000000000000000000000001335005366400327205ustar00rootroot00000000000000fixtures.go000066400000000000000000000140021335005366400350360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/groups/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListOutput provides a single page of Group results. const ListOutput = ` { "links": { "next": null, "previous": null, "self": "http://example.com/identity/v3/groups" }, "groups": [ { "domain_id": "default", "id": "2844b2a08be147a08ef58317d6471f1f", "description": "group for internal support users", "links": { "self": "http://example.com/identity/v3/groups/2844b2a08be147a08ef58317d6471f1f" }, "name": "internal support", "extra": { "email": "support@localhost" } }, { "domain_id": "1789d1", "id": "9fe1d3", "description": "group for support users", "links": { "self": "https://example.com/identity/v3/groups/9fe1d3" }, "name": "support", "extra": { "email": "support@example.com" } } ] } ` // GetOutput provides a Get result. const GetOutput = ` { "group": { "domain_id": "1789d1", "id": "9fe1d3", "description": "group for support users", "links": { "self": "https://example.com/identity/v3/groups/9fe1d3" }, "name": "support", "extra": { "email": "support@example.com" } } } ` // CreateRequest provides the input to a Create request. const CreateRequest = ` { "group": { "domain_id": "1789d1", "name": "support", "description": "group for support users", "email": "support@example.com" } } ` // UpdateRequest provides the input to as Update request. const UpdateRequest = ` { "group": { "description": "L2 Support Team", "email": "supportteam@example.com" } } ` // UpdateOutput provides an update result. const UpdateOutput = ` { "group": { "domain_id": "1789d1", "id": "9fe1d3", "links": { "self": "https://example.com/identity/v3/groups/9fe1d3" }, "name": "support", "description": "L2 Support Team", "extra": { "email": "supportteam@example.com" } } } ` // FirstGroup is the first group in the List request. var FirstGroup = groups.Group{ DomainID: "default", ID: "2844b2a08be147a08ef58317d6471f1f", Links: map[string]interface{}{ "self": "http://example.com/identity/v3/groups/2844b2a08be147a08ef58317d6471f1f", }, Name: "internal support", Description: "group for internal support users", Extra: map[string]interface{}{ "email": "support@localhost", }, } // SecondGroup is the second group in the List request. var SecondGroup = groups.Group{ DomainID: "1789d1", ID: "9fe1d3", Links: map[string]interface{}{ "self": "https://example.com/identity/v3/groups/9fe1d3", }, Name: "support", Description: "group for support users", Extra: map[string]interface{}{ "email": "support@example.com", }, } // SecondGroupUpdated is how SecondGroup should look after an Update. var SecondGroupUpdated = groups.Group{ DomainID: "1789d1", ID: "9fe1d3", Links: map[string]interface{}{ "self": "https://example.com/identity/v3/groups/9fe1d3", }, Name: "support", Description: "L2 Support Team", Extra: map[string]interface{}{ "email": "supportteam@example.com", }, } // ExpectedGroupsSlice is the slice of groups expected to be returned from ListOutput. var ExpectedGroupsSlice = []groups.Group{FirstGroup, SecondGroup} // HandleListGroupsSuccessfully creates an HTTP handler at `/groups` on the // test handler mux that responds with a list of two groups. func HandleListGroupsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/groups", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListOutput) }) } // HandleGetGroupSuccessfully creates an HTTP handler at `/groups` on the // test handler mux that responds with a single group. func HandleGetGroupSuccessfully(t *testing.T) { th.Mux.HandleFunc("/groups/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetOutput) }) } // HandleCreateGroupSuccessfully creates an HTTP handler at `/groups` on the // test handler mux that tests group creation. func HandleCreateGroupSuccessfully(t *testing.T) { th.Mux.HandleFunc("/groups", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, GetOutput) }) } // HandleUpdateGroupSuccessfully creates an HTTP handler at `/groups` on the // test handler mux that tests group update. func HandleUpdateGroupSuccessfully(t *testing.T) { th.Mux.HandleFunc("/groups/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, UpdateOutput) }) } // HandleDeleteGroupSuccessfully creates an HTTP handler at `/groups` on the // test handler mux that tests group deletion. func HandleDeleteGroupSuccessfully(t *testing.T) { th.Mux.HandleFunc("/groups/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000063151335005366400361070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/groups/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListGroups(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListGroupsSuccessfully(t) count := 0 err := groups.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := groups.ExtractGroups(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedGroupsSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListGroupsAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListGroupsSuccessfully(t) allPages, err := groups.List(client.ServiceClient(), nil).AllPages() th.AssertNoErr(t, err) actual, err := groups.ExtractGroups(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedGroupsSlice, actual) th.AssertEquals(t, ExpectedGroupsSlice[0].Extra["email"], "support@localhost") th.AssertEquals(t, ExpectedGroupsSlice[1].Extra["email"], "support@example.com") } func TestListGroupsFiltersCheck(t *testing.T) { type test struct { filterName string wantErr bool } tests := []test{ {"foo__contains", false}, {"foo", true}, {"foo_contains", true}, {"foo__", true}, {"__foo", true}, } var listOpts groups.ListOpts for _, _test := range tests { listOpts.Filters = map[string]string{_test.filterName: "bar"} _, err := listOpts.ToGroupListQuery() if !_test.wantErr { th.AssertNoErr(t, err) } else { switch _t := err.(type) { case nil: t.Fatal("error expected but got a nil") case groups.InvalidListFilter: default: t.Fatalf("unexpected error type: [%T]", _t) } } } } func TestGetGroup(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetGroupSuccessfully(t) actual, err := groups.Get(client.ServiceClient(), "9fe1d3").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondGroup, *actual) th.AssertEquals(t, SecondGroup.Extra["email"], "support@example.com") } func TestCreateGroup(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateGroupSuccessfully(t) createOpts := groups.CreateOpts{ Name: "support", DomainID: "1789d1", Description: "group for support users", Extra: map[string]interface{}{ "email": "support@example.com", }, } actual, err := groups.Create(client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondGroup, *actual) } func TestUpdateGroup(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateGroupSuccessfully(t) updateOpts := groups.UpdateOpts{ Description: "L2 Support Team", Extra: map[string]interface{}{ "email": "supportteam@example.com", }, } actual, err := groups.Update(client.ServiceClient(), "9fe1d3", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondGroupUpdated, *actual) } func TestDeleteGroup(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteGroupSuccessfully(t) res := groups.Delete(client.ServiceClient(), "9fe1d3") th.AssertNoErr(t, res.Err) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/groups/urls.go000066400000000000000000000011541335005366400325600ustar00rootroot00000000000000package groups import "github.com/gophercloud/gophercloud" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("groups") } func getURL(client *gophercloud.ServiceClient, groupID string) string { return client.ServiceURL("groups", groupID) } func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("groups") } func updateURL(client *gophercloud.ServiceClient, groupID string) string { return client.ServiceURL("groups", groupID) } func deleteURL(client *gophercloud.ServiceClient, groupID string) string { return client.ServiceURL("groups", groupID) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/policies/000077500000000000000000000000001335005366400315335ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/policies/doc.go000066400000000000000000000031471335005366400326340ustar00rootroot00000000000000/* Package policies provides information and interaction with the policies API resource for the OpenStack Identity service. Example to List Policies listOpts := policies.ListOpts{ Type: "application/json", } allPages, err := policies.List(identityClient, listOpts).AllPages() if err != nil { panic(err) } allPolicies, err := policies.ExtractPolicies(allPages) if err != nil { panic(err) } for _, policy := range allPolicies { fmt.Printf("%+v\n", policy) } Example to Create a Policy createOpts := policies.CreateOpts{ Type: "application/json", Blob: []byte("{'foobar_user': 'role:compute-user'}"), Extra: map[string]interface{}{ "description": "policy for foobar_user", }, } policy, err := policies.Create(identityClient, createOpts).Extract() if err != nil { panic(err) } Example to Get a Policy policyID := "0fe36e73809d46aeae6705c39077b1b3" policy, err := policies.Get(identityClient, policyID).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", policy) Example to Update a Policy policyID := "0fe36e73809d46aeae6705c39077b1b3" updateOpts := policies.UpdateOpts{ Type: "application/json", Blob: []byte("{'foobar_user': 'role:compute-user'}"), Extra: map[string]interface{}{ "description": "policy for foobar_user", }, } policy, err := policies.Update(identityClient, policyID, updateOpts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", policy) Example to Delete a Policy policyID := "0fe36e73809d46aeae6705c39077b1b3" err := policies.Delete(identityClient, policyID).ExtractErr() if err != nil { panic(err) } */ package policies errors.go000066400000000000000000000013301335005366400333140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/policiespackage policies import "fmt" // InvalidListFilter is returned by the ToPolicyListQuery method when // validation of a filter does not pass type InvalidListFilter struct { FilterName string } func (e InvalidListFilter) Error() string { s := fmt.Sprintf( "Invalid filter name [%s]: it must be in format of TYPE__COMPARATOR", e.FilterName, ) return s } // StringFieldLengthExceedsLimit is returned by the // ToPolicyCreateMap/ToPolicyUpdateMap methods when validation of // a type does not pass type StringFieldLengthExceedsLimit struct { Field string Limit int } func (e StringFieldLengthExceedsLimit) Error() string { return fmt.Sprintf("String length of field [%s] exceeds limit (%d)", e.Field, e.Limit, ) } requests.go000066400000000000000000000115731335005366400336650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/policiespackage policies import ( "net/url" "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) const policyTypeMaxLength = 255 // ListOptsBuilder allows extensions to add additional parameters to // the List request type ListOptsBuilder interface { ToPolicyListQuery() (string, error) } // ListOpts provides options to filter the List results. type ListOpts struct { // Type filters the response by MIME media type // of the serialized policy blob. Type string `q:"type"` // Filters filters the response by custom filters such as // 'type__contains=foo' Filters map[string]string `q:"-"` } // ToPolicyListQuery formats a ListOpts into a query string. func (opts ListOpts) ToPolicyListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return "", err } params := q.Query() for k, v := range opts.Filters { i := strings.Index(k, "__") if i > 0 && i < len(k)-2 { params.Add(k, v) } else { return "", InvalidListFilter{FilterName: k} } } q = &url.URL{RawQuery: params.Encode()} return q.String(), err } // List enumerates the policies to which the current token has access. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToPolicyListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return PolicyPage{pagination.LinkedPageBase{PageResult: r}} }) } // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { ToPolicyCreateMap() (map[string]interface{}, error) } // CreateOpts provides options used to create a policy. type CreateOpts struct { // Type is the MIME media type of the serialized policy blob. Type string `json:"type" required:"true"` // Blob is the policy rule as a serialized blob. Blob []byte `json:"-" required:"true"` // Extra is free-form extra key/value pairs to describe the policy. Extra map[string]interface{} `json:"-"` } // ToPolicyCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { if len(opts.Type) > policyTypeMaxLength { return nil, StringFieldLengthExceedsLimit{ Field: "type", Limit: policyTypeMaxLength, } } b, err := gophercloud.BuildRequestBody(opts, "policy") if err != nil { return nil, err } if v, ok := b["policy"].(map[string]interface{}); ok { v["blob"] = string(opts.Blob) if opts.Extra != nil { for key, value := range opts.Extra { v[key] = value } } } return b, nil } // Create creates a new Policy. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToPolicyCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) return } // Get retrieves details on a single policy, by ID. func Get(client *gophercloud.ServiceClient, policyID string) (r GetResult) { _, r.Err = client.Get(getURL(client, policyID), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { ToPolicyUpdateMap() (map[string]interface{}, error) } // UpdateOpts provides options for updating a policy. type UpdateOpts struct { // Type is the MIME media type of the serialized policy blob. Type string `json:"type,omitempty"` // Blob is the policy rule as a serialized blob. Blob []byte `json:"-"` // Extra is free-form extra key/value pairs to describe the policy. Extra map[string]interface{} `json:"-"` } // ToPolicyUpdateMap formats a UpdateOpts into an update request. func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { if len(opts.Type) > policyTypeMaxLength { return nil, StringFieldLengthExceedsLimit{ Field: "type", Limit: policyTypeMaxLength, } } b, err := gophercloud.BuildRequestBody(opts, "policy") if err != nil { return nil, err } if v, ok := b["policy"].(map[string]interface{}); ok { if len(opts.Blob) != 0 { v["blob"] = string(opts.Blob) } if opts.Extra != nil { for key, value := range opts.Extra { v[key] = value } } } return b, nil } // Update updates an existing Role. func Update(client *gophercloud.ServiceClient, policyID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToPolicyUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Patch(updateURL(client, policyID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete deletes a policy. func Delete(client *gophercloud.ServiceClient, policyID string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, policyID), nil) return } results.go000066400000000000000000000061401335005366400335050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/policiespackage policies import ( "encoding/json" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) // Policy is an arbitrarily serialized policy engine rule // set to be consumed by a remote service. type Policy struct { // ID is the unique ID of the policy. ID string `json:"id"` // Blob is the policy rule as a serialized blob. Blob string `json:"blob"` // Type is the MIME media type of the serialized policy blob. Type string `json:"type"` // Links contains referencing links to the policy. Links map[string]interface{} `json:"links"` // Extra is a collection of miscellaneous key/values. Extra map[string]interface{} `json:"-"` } func (r *Policy) UnmarshalJSON(b []byte) error { type tmp Policy var s struct { tmp Extra map[string]interface{} `json:"extra"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Policy(s.tmp) // Collect other fields and bundle them into Extra // but only if a field titled "extra" wasn't sent. if s.Extra != nil { r.Extra = s.Extra } else { var result interface{} err := json.Unmarshal(b, &result) if err != nil { return err } if resultMap, ok := result.(map[string]interface{}); ok { r.Extra = internal.RemainingKeys(Policy{}, resultMap) } } return err } type policyResult struct { gophercloud.Result } // CreateResult is the response from a Create operation. Call its Extract method // to interpret it as a Policy type CreateResult struct { policyResult } // GetResult is the response from a Get operation. Call its Extract method // to interpret it as a Policy. type GetResult struct { policyResult } // UpdateResult is the response from an Update operation. Call its Extract // method to interpret it as a Policy. type UpdateResult struct { policyResult } // DeleteResult is the response from a Delete operation. Call its ExtractErr to // determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // PolicyPage is a single page of Policy results. type PolicyPage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a page of Policies contains any results. func (r PolicyPage) IsEmpty() (bool, error) { policies, err := ExtractPolicies(r) return len(policies) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (r PolicyPage) NextPageURL() (string, error) { var s struct { Links struct { Next string `json:"next"` Previous string `json:"previous"` } `json:"links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return s.Links.Next, err } // ExtractPolicies returns a slice of Policies // contained in a single page of results. func ExtractPolicies(r pagination.Page) ([]Policy, error) { var s struct { Policies []Policy `json:"policies"` } err := (r.(PolicyPage)).ExtractInto(&s) return s.Policies, err } // Extract interprets any policyResults as a Policy. func (r policyResult) Extract() (*Policy, error) { var s struct { Policy *Policy `json:"policy"` } err := r.ExtractInto(&s) return s.Policy, err } testing/000077500000000000000000000000001335005366400331315ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/policiesdoc.go000066400000000000000000000001001335005366400342140ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/policies/testing// Package testing contains policies unit tests package testing fixtures.go000066400000000000000000000154211335005366400353340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/policies/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/policies" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) // ListOutput provides a single page of Policy results. const ListOutput = ` { "links": { "next": null, "previous": null, "self": "http://example.com/identity/v3/policies" }, "policies": [ { "type": "text/plain", "id": "2844b2a08be147a08ef58317d6471f1f", "links": { "self": "http://example.com/identity/v3/policies/2844b2a08be147a08ef58317d6471f1f" }, "blob": "'foo_user': 'role:compute-user'" }, { "type": "application/json", "id": "b49884da9d31494ea02aff38d4b4e701", "links": { "self": "http://example.com/identity/v3/policies/b49884da9d31494ea02aff38d4b4e701" }, "blob": "{'bar_user': 'role:network-user'}", "description": "policy for bar_user" } ] } ` // ListWithFilterOutput provides a single page of filtered Policy results. const ListWithFilterOutput = ` { "links": { "next": null, "previous": null, "self": "http://example.com/identity/v3/policies" }, "policies": [ { "type": "application/json", "id": "b49884da9d31494ea02aff38d4b4e701", "links": { "self": "http://example.com/identity/v3/policies/b49884da9d31494ea02aff38d4b4e701" }, "blob": "{'bar_user': 'role:network-user'}", "description": "policy for bar_user" } ] } ` // GetOutput provides a Get result. const GetOutput = ` { "policy": { "type": "application/json", "id": "b49884da9d31494ea02aff38d4b4e701", "links": { "self": "http://example.com/identity/v3/policies/b49884da9d31494ea02aff38d4b4e701" }, "blob": "{'bar_user': 'role:network-user'}", "description": "policy for bar_user" } } ` // CreateRequest provides the input to a Create request. const CreateRequest = ` { "policy": { "blob": "{'bar_user': 'role:network-user'}", "description": "policy for bar_user", "type": "application/json" } } ` // UpdateRequest provides the input to as Update request. const UpdateRequest = ` { "policy": { "description": "updated policy for bar_user" } } ` // UpdateOutput provides an update result. const UpdateOutput = ` { "policy": { "type": "application/json", "id": "b49884da9d31494ea02aff38d4b4e701", "links": { "self": "http://example.com/identity/v3/policies/b49884da9d31494ea02aff38d4b4e701" }, "blob": "{'bar_user': 'role:network-user'}", "description": "updated policy for bar_user" } } ` // FirstPolicy is the first policy in the List request. var FirstPolicy = policies.Policy{ ID: "2844b2a08be147a08ef58317d6471f1f", Blob: "'foo_user': 'role:compute-user'", Type: "text/plain", Links: map[string]interface{}{ "self": "http://example.com/identity/v3/policies/2844b2a08be147a08ef58317d6471f1f", }, Extra: map[string]interface{}{}, } // SecondPolicy is the second policy in the List request. var SecondPolicy = policies.Policy{ ID: "b49884da9d31494ea02aff38d4b4e701", Blob: "{'bar_user': 'role:network-user'}", Type: "application/json", Links: map[string]interface{}{ "self": "http://example.com/identity/v3/policies/b49884da9d31494ea02aff38d4b4e701", }, Extra: map[string]interface{}{ "description": "policy for bar_user", }, } // SecondPolicyUpdated is the policy in the Update request. var SecondPolicyUpdated = policies.Policy{ ID: "b49884da9d31494ea02aff38d4b4e701", Blob: "{'bar_user': 'role:network-user'}", Type: "application/json", Links: map[string]interface{}{ "self": "http://example.com/identity/v3/policies/b49884da9d31494ea02aff38d4b4e701", }, Extra: map[string]interface{}{ "description": "updated policy for bar_user", }, } // ExpectedPoliciesSlice is the slice of policies expected to be returned from ListOutput. var ExpectedPoliciesSlice = []policies.Policy{FirstPolicy, SecondPolicy} // HandleListPoliciesSuccessfully creates an HTTP handler at `/policies` on the // test handler mux that responds with a list of two policies. func HandleListPoliciesSuccessfully(t *testing.T) { th.Mux.HandleFunc("/policies", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) switch r.URL.Query().Get("type") { case "": fmt.Fprintf(w, ListOutput) case "application/json": fmt.Fprintf(w, ListWithFilterOutput) default: w.WriteHeader(http.StatusBadRequest) } }) } // HandleCreatePolicySuccessfully creates an HTTP handler at `/policies` on the // test handler mux that tests policy creation. func HandleCreatePolicySuccessfully(t *testing.T) { th.Mux.HandleFunc("/policies", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, GetOutput) }) } // HandleGetPolicySuccessfully creates an HTTP handler at `/policies` on the // test handler mux that responds with a single policy. func HandleGetPolicySuccessfully(t *testing.T) { th.Mux.HandleFunc("/policies/b49884da9d31494ea02aff38d4b4e701", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetOutput) }, ) } // HandleUpdatePolicySuccessfully creates an HTTP handler at `/policies` on the // test handler mux that tests role update. func HandleUpdatePolicySuccessfully(t *testing.T) { th.Mux.HandleFunc("/policies/b49884da9d31494ea02aff38d4b4e701", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, UpdateOutput) }, ) } // HandleDeletePolicySuccessfully creates an HTTP handler at `/policies` on the // test handler mux that tests policy deletion. func HandleDeletePolicySuccessfully(t *testing.T) { th.Mux.HandleFunc("/policies/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000124571335005366400364030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/policies/testingpackage testing import ( "fmt" "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/policies" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListPolicies(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListPoliciesSuccessfully(t) count := 0 err := policies.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := policies.ExtractPolicies(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedPoliciesSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListPoliciesAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListPoliciesSuccessfully(t) allPages, err := policies.List(client.ServiceClient(), nil).AllPages() th.AssertNoErr(t, err) actual, err := policies.ExtractPolicies(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedPoliciesSlice, actual) } func TestListPoliciesWithFilter(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListPoliciesSuccessfully(t) listOpts := policies.ListOpts{ Type: "application/json", } allPages, err := policies.List(client.ServiceClient(), listOpts).AllPages() th.AssertNoErr(t, err) actual, err := policies.ExtractPolicies(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, []policies.Policy{SecondPolicy}, actual) } func TestListPoliciesFiltersCheck(t *testing.T) { type test struct { filterName string wantErr bool } tests := []test{ {"foo__contains", false}, {"foo", true}, {"foo_contains", true}, {"foo__", true}, {"__foo", true}, } var listOpts policies.ListOpts for _, _test := range tests { listOpts.Filters = map[string]string{_test.filterName: "bar"} _, err := listOpts.ToPolicyListQuery() if !_test.wantErr { th.AssertNoErr(t, err) } else { switch _t := err.(type) { case nil: t.Fatal("error expected but got a nil") case policies.InvalidListFilter: default: t.Fatalf("unexpected error type: [%T]", _t) } } } } func TestCreatePolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreatePolicySuccessfully(t) createOpts := policies.CreateOpts{ Type: "application/json", Blob: []byte("{'bar_user': 'role:network-user'}"), Extra: map[string]interface{}{ "description": "policy for bar_user", }, } actual, err := policies.Create(client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondPolicy, *actual) } func TestCreatePolicyTypeLengthCheck(t *testing.T) { // strGenerator generates a string of fixed length filled with '0' strGenerator := func(length int) string { return fmt.Sprintf(fmt.Sprintf("%%0%dd", length), 0) } type test struct { length int wantErr bool } tests := []test{ {100, false}, {255, false}, {256, true}, {300, true}, } createOpts := policies.CreateOpts{ Blob: []byte("{'bar_user': 'role:network-user'}"), } for _, _test := range tests { createOpts.Type = strGenerator(_test.length) if len(createOpts.Type) != _test.length { t.Fatal("function strGenerator does not work properly") } _, err := createOpts.ToPolicyCreateMap() if !_test.wantErr { th.AssertNoErr(t, err) } else { switch _t := err.(type) { case nil: t.Fatal("error expected but got a nil") case policies.StringFieldLengthExceedsLimit: default: t.Fatalf("unexpected error type: [%T]", _t) } } } } func TestGetPolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetPolicySuccessfully(t) id := "b49884da9d31494ea02aff38d4b4e701" actual, err := policies.Get(client.ServiceClient(), id).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondPolicy, *actual) } func TestUpdatePolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdatePolicySuccessfully(t) updateOpts := policies.UpdateOpts{ Extra: map[string]interface{}{ "description": "updated policy for bar_user", }, } id := "b49884da9d31494ea02aff38d4b4e701" actual, err := policies.Update(client.ServiceClient(), id, updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondPolicyUpdated, *actual) } func TestUpdatePolicyTypeLengthCheck(t *testing.T) { // strGenerator generates a string of fixed length filled with '0' strGenerator := func(length int) string { return fmt.Sprintf(fmt.Sprintf("%%0%dd", length), 0) } type test struct { length int wantErr bool } tests := []test{ {100, false}, {255, false}, {256, true}, {300, true}, } var updateOpts policies.UpdateOpts for _, _test := range tests { updateOpts.Type = strGenerator(_test.length) if len(updateOpts.Type) != _test.length { t.Fatal("function strGenerator does not work properly") } _, err := updateOpts.ToPolicyUpdateMap() if !_test.wantErr { th.AssertNoErr(t, err) } else { switch _t := err.(type) { case nil: t.Fatal("error expected but got a nil") case policies.StringFieldLengthExceedsLimit: default: t.Fatalf("unexpected error type: [%T]", _t) } } } } func TestDeletePolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeletePolicySuccessfully(t) res := policies.Delete(client.ServiceClient(), "9fe1d3") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000012351335005366400327710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/policiespackage policies import "github.com/gophercloud/gophercloud" const policyPath = "policies" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(policyPath) } func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(policyPath) } func getURL(client *gophercloud.ServiceClient, policyID string) string { return client.ServiceURL(policyPath, policyID) } func updateURL(client *gophercloud.ServiceClient, policyID string) string { return client.ServiceURL(policyPath, policyID) } func deleteURL(client *gophercloud.ServiceClient, policyID string) string { return client.ServiceURL(policyPath, policyID) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/projects/000077500000000000000000000000001335005366400315555ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/projects/doc.go000066400000000000000000000022011335005366400326440ustar00rootroot00000000000000/* Package projects manages and retrieves Projects in the OpenStack Identity Service. Example to List Projects listOpts := projects.ListOpts{ Enabled: gophercloud.Enabled, } allPages, err := projects.List(identityClient, listOpts).AllPages() if err != nil { panic(err) } allProjects, err := projects.ExtractProjects(allPages) if err != nil { panic(err) } for _, project := range allProjects { fmt.Printf("%+v\n", project) } Example to Create a Project createOpts := projects.CreateOpts{ Name: "project_name", Description: "Project Description" } project, err := projects.Create(identityClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Project projectID := "966b3c7d36a24facaf20b7e458bf2192" updateOpts := projects.UpdateOpts{ Enabled: gophercloud.Disabled, } project, err := projects.Update(identityClient, projectID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Project projectID := "966b3c7d36a24facaf20b7e458bf2192" err := projects.Delete(identityClient, projectID).ExtractErr() if err != nil { panic(err) } */ package projects errors.go000066400000000000000000000005501335005366400333410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/projectspackage projects import "fmt" // InvalidListFilter is returned by the ToUserListQuery method when validation of // a filter does not pass type InvalidListFilter struct { FilterName string } func (e InvalidListFilter) Error() string { s := fmt.Sprintf( "Invalid filter name [%s]: it must be in format of NAME__COMPARATOR", e.FilterName, ) return s } requests.go000066400000000000000000000117401335005366400337030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/projectspackage projects import ( "net/url" "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to // the List request type ListOptsBuilder interface { ToProjectListQuery() (string, error) } // ListOpts enables filtering of a list request. type ListOpts struct { // DomainID filters the response by a domain ID. DomainID string `q:"domain_id"` // Enabled filters the response by enabled projects. Enabled *bool `q:"enabled"` // IsDomain filters the response by projects that are domains. // Setting this to true is effectively listing domains. IsDomain *bool `q:"is_domain"` // Name filters the response by project name. Name string `q:"name"` // ParentID filters the response by projects of a given parent project. ParentID string `q:"parent_id"` // Filters filters the response by custom filters such as // 'name__contains=foo' Filters map[string]string `q:"-"` } // ToProjectListQuery formats a ListOpts into a query string. func (opts ListOpts) ToProjectListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return "", err } params := q.Query() for k, v := range opts.Filters { i := strings.Index(k, "__") if i > 0 && i < len(k)-2 { params.Add(k, v) } else { return "", InvalidListFilter{FilterName: k} } } q = &url.URL{RawQuery: params.Encode()} return q.String(), err } // List enumerates the Projects to which the current token has access. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToProjectListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ProjectPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves details on a single project, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { ToProjectCreateMap() (map[string]interface{}, error) } // CreateOpts represents parameters used to create a project. type CreateOpts struct { // DomainID is the ID this project will belong under. DomainID string `json:"domain_id,omitempty"` // Enabled sets the project status to enabled or disabled. Enabled *bool `json:"enabled,omitempty"` // IsDomain indicates if this project is a domain. IsDomain *bool `json:"is_domain,omitempty"` // Name is the name of the project. Name string `json:"name" required:"true"` // ParentID specifies the parent project of this new project. ParentID string `json:"parent_id,omitempty"` // Description is the description of the project. Description string `json:"description,omitempty"` } // ToProjectCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToProjectCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "project") } // Create creates a new Project. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToProjectCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), &b, &r.Body, nil) return } // Delete deletes a project. func Delete(client *gophercloud.ServiceClient, projectID string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, projectID), nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { ToProjectUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents parameters to update a project. type UpdateOpts struct { // DomainID is the ID this project will belong under. DomainID string `json:"domain_id,omitempty"` // Enabled sets the project status to enabled or disabled. Enabled *bool `json:"enabled,omitempty"` // IsDomain indicates if this project is a domain. IsDomain *bool `json:"is_domain,omitempty"` // Name is the name of the project. Name string `json:"name,omitempty"` // ParentID specifies the parent project of this new project. ParentID string `json:"parent_id,omitempty"` // Description is the description of the project. Description string `json:"description,omitempty"` } // ToUpdateCreateMap formats a UpdateOpts into an update request. func (opts UpdateOpts) ToProjectUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "project") } // Update modifies the attributes of a project. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToProjectUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000050441335005366400335310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/projectspackage projects import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type projectResult struct { gophercloud.Result } // GetResult is the result of a Get request. Call its Extract method to // interpret it as a Project. type GetResult struct { projectResult } // CreateResult is the result of a Create request. Call its Extract method to // interpret it as a Project. type CreateResult struct { projectResult } // DeleteResult is the result of a Delete request. Call its ExtractErr method to // determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // UpdateResult is the result of an Update request. Call its Extract method to // interpret it as a Project. type UpdateResult struct { projectResult } // Project represents an OpenStack Identity Project. type Project struct { // IsDomain indicates whether the project is a domain. IsDomain bool `json:"is_domain"` // Description is the description of the project. Description string `json:"description"` // DomainID is the domain ID the project belongs to. DomainID string `json:"domain_id"` // Enabled is whether or not the project is enabled. Enabled bool `json:"enabled"` // ID is the unique ID of the project. ID string `json:"id"` // Name is the name of the project. Name string `json:"name"` // ParentID is the parent_id of the project. ParentID string `json:"parent_id"` } // ProjectPage is a single page of Project results. type ProjectPage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a page of Projects contains any results. func (r ProjectPage) IsEmpty() (bool, error) { projects, err := ExtractProjects(r) return len(projects) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (r ProjectPage) NextPageURL() (string, error) { var s struct { Links struct { Next string `json:"next"` Previous string `json:"previous"` } `json:"links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return s.Links.Next, err } // ExtractProjects returns a slice of Projects contained in a single page of // results. func ExtractProjects(r pagination.Page) ([]Project, error) { var s struct { Projects []Project `json:"projects"` } err := (r.(ProjectPage)).ExtractInto(&s) return s.Projects, err } // Extract interprets any projectResults as a Project. func (r projectResult) Extract() (*Project, error) { var s struct { Project *Project `json:"project"` } err := r.ExtractInto(&s) return s.Project, err } testing/000077500000000000000000000000001335005366400331535ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/projectsfixtures.go000066400000000000000000000115451335005366400353610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/projects/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListOutput provides a single page of Project results. const ListOutput = ` { "projects": [ { "is_domain": false, "description": "The team that is red", "domain_id": "default", "enabled": true, "id": "1234", "name": "Red Team", "parent_id": null }, { "is_domain": false, "description": "The team that is blue", "domain_id": "default", "enabled": true, "id": "9876", "name": "Blue Team", "parent_id": null } ], "links": { "next": null, "previous": null } } ` // GetOutput provides a Get result. const GetOutput = ` { "project": { "is_domain": false, "description": "The team that is red", "domain_id": "default", "enabled": true, "id": "1234", "name": "Red Team", "parent_id": null } } ` // CreateRequest provides the input to a Create request. const CreateRequest = ` { "project": { "description": "The team that is red", "name": "Red Team" } } ` // UpdateRequest provides the input to an Update request. const UpdateRequest = ` { "project": { "description": "The team that is bright red", "name": "Bright Red Team" } } ` // UpdateOutput provides an Update response. const UpdateOutput = ` { "project": { "is_domain": false, "description": "The team that is bright red", "domain_id": "default", "enabled": true, "id": "1234", "name": "Bright Red Team", "parent_id": null } } ` // RedTeam is a Project fixture. var RedTeam = projects.Project{ IsDomain: false, Description: "The team that is red", DomainID: "default", Enabled: true, ID: "1234", Name: "Red Team", ParentID: "", } // BlueTeam is a Project fixture. var BlueTeam = projects.Project{ IsDomain: false, Description: "The team that is blue", DomainID: "default", Enabled: true, ID: "9876", Name: "Blue Team", ParentID: "", } // UpdatedRedTeam is a Project Fixture. var UpdatedRedTeam = projects.Project{ IsDomain: false, Description: "The team that is bright red", DomainID: "default", Enabled: true, ID: "1234", Name: "Bright Red Team", ParentID: "", } // ExpectedProjectSlice is the slice of projects expected to be returned from ListOutput. var ExpectedProjectSlice = []projects.Project{RedTeam, BlueTeam} // HandleListProjectsSuccessfully creates an HTTP handler at `/projects` on the // test handler mux that responds with a list of two tenants. func HandleListProjectsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/projects", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListOutput) }) } // HandleGetProjectSuccessfully creates an HTTP handler at `/projects` on the // test handler mux that responds with a single project. func HandleGetProjectSuccessfully(t *testing.T) { th.Mux.HandleFunc("/projects/1234", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetOutput) }) } // HandleCreateProjectSuccessfully creates an HTTP handler at `/projects` on the // test handler mux that tests project creation. func HandleCreateProjectSuccessfully(t *testing.T) { th.Mux.HandleFunc("/projects", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, GetOutput) }) } // HandleDeleteProjectSuccessfully creates an HTTP handler at `/projects` on the // test handler mux that tests project deletion. func HandleDeleteProjectSuccessfully(t *testing.T) { th.Mux.HandleFunc("/projects/1234", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleUpdateProjectSuccessfully creates an HTTP handler at `/projects` on the // test handler mux that tests project updates. func HandleUpdateProjectSuccessfully(t *testing.T) { th.Mux.HandleFunc("/projects/1234", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, UpdateOutput) }) } requests_test.go000066400000000000000000000050521335005366400364160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/projects/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListProjects(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListProjectsSuccessfully(t) count := 0 err := projects.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := projects.ExtractProjects(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedProjectSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListGroupsFiltersCheck(t *testing.T) { type test struct { filterName string wantErr bool } tests := []test{ {"foo__contains", false}, {"foo", true}, {"foo_contains", true}, {"foo__", true}, {"__foo", true}, } var listOpts projects.ListOpts for _, _test := range tests { listOpts.Filters = map[string]string{_test.filterName: "bar"} _, err := listOpts.ToProjectListQuery() if !_test.wantErr { th.AssertNoErr(t, err) } else { switch _t := err.(type) { case nil: t.Fatal("error expected but got a nil") case projects.InvalidListFilter: default: t.Fatalf("unexpected error type: [%T]", _t) } } } } func TestGetProject(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetProjectSuccessfully(t) actual, err := projects.Get(client.ServiceClient(), "1234").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, RedTeam, *actual) } func TestCreateProject(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateProjectSuccessfully(t) createOpts := projects.CreateOpts{ Name: "Red Team", Description: "The team that is red", } actual, err := projects.Create(client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, RedTeam, *actual) } func TestDeleteProject(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteProjectSuccessfully(t) res := projects.Delete(client.ServiceClient(), "1234") th.AssertNoErr(t, res.Err) } func TestUpdateProject(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateProjectSuccessfully(t) updateOpts := projects.UpdateOpts{ Name: "Bright Red Team", Description: "The team that is bright red", } actual, err := projects.Update(client.ServiceClient(), "1234", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, UpdatedRedTeam, *actual) } urls.go000066400000000000000000000012041335005366400330070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/projectspackage projects import "github.com/gophercloud/gophercloud" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("projects") } func getURL(client *gophercloud.ServiceClient, projectID string) string { return client.ServiceURL("projects", projectID) } func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("projects") } func deleteURL(client *gophercloud.ServiceClient, projectID string) string { return client.ServiceURL("projects", projectID) } func updateURL(client *gophercloud.ServiceClient, projectID string) string { return client.ServiceURL("projects", projectID) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/regions/000077500000000000000000000000001335005366400313725ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/regions/doc.go000066400000000000000000000025311335005366400324670ustar00rootroot00000000000000/* Package regions manages and retrieves Regions in the OpenStack Identity Service. Example to List Regions listOpts := regions.ListOpts{ ParentRegionID: "RegionOne", } allPages, err := regions.List(identityClient, listOpts).AllPages() if err != nil { panic(err) } allRegions, err := regions.ExtractRegions(allPages) if err != nil { panic(err) } for _, region := range allRegions { fmt.Printf("%+v\n", region) } Example to Create a Region createOpts := regions.CreateOpts{ ID: "TestRegion", Description: "Region for testing" Extra: map[string]interface{}{ "email": "testregionsupport@example.com", } } region, err := regions.Create(identityClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Region regionID := "TestRegion" // There is currently a bug in Keystone where updating the optional Extras // attributes set in regions.Create is not supported, see: // https://bugs.launchpad.net/keystone/+bug/1729933 updateOpts := regions.UpdateOpts{ Description: "Updated Description for region", } region, err := regions.Update(identityClient, regionID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Region regionID := "TestRegion" err := regions.Delete(identityClient, regionID).ExtractErr() if err != nil { panic(err) } */ package regions requests.go000066400000000000000000000112021335005366400335110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/regionspackage regions import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to // the List request type ListOptsBuilder interface { ToRegionListQuery() (string, error) } // ListOpts provides options to filter the List results. type ListOpts struct { // ParentRegionID filters the response by a parent region ID. ParentRegionID string `q:"parent_region_id"` } // ToRegionListQuery formats a ListOpts into a query string. func (opts ListOpts) ToRegionListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List enumerates the Regions to which the current token has access. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToRegionListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return RegionPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves details on a single region, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { ToRegionCreateMap() (map[string]interface{}, error) } // CreateOpts provides options used to create a region. type CreateOpts struct { // ID is the ID of the new region. ID string `json:"id,omitempty"` // Description is a description of the region. Description string `json:"description,omitempty"` // ParentRegionID is the ID of the parent the region to add this region under. ParentRegionID string `json:"parent_region_id,omitempty"` // Extra is free-form extra key/value pairs to describe the region. Extra map[string]interface{} `json:"-"` } // ToRegionCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToRegionCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "region") if err != nil { return nil, err } if opts.Extra != nil { if v, ok := b["region"].(map[string]interface{}); ok { for key, value := range opts.Extra { v[key] = value } } } return b, nil } // Create creates a new Region. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToRegionCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) return } // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { ToRegionUpdateMap() (map[string]interface{}, error) } // UpdateOpts provides options for updating a region. type UpdateOpts struct { // Description is a description of the region. Description string `json:"description,omitempty"` // ParentRegionID is the ID of the parent region. ParentRegionID string `json:"parent_region_id,omitempty"` /* // Due to a bug in Keystone, the Extra column of the Region table // is not updatable, see: https://bugs.launchpad.net/keystone/+bug/1729933 // The following lines should be uncommented once the fix is merged. // Extra is free-form extra key/value pairs to describe the region. Extra map[string]interface{} `json:"-"` */ } // ToRegionUpdateMap formats a UpdateOpts into an update request. func (opts UpdateOpts) ToRegionUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "region") if err != nil { return nil, err } /* // Due to a bug in Keystone, the Extra column of the Region table // is not updatable, see: https://bugs.launchpad.net/keystone/+bug/1729933 // The following lines should be uncommented once the fix is merged. if opts.Extra != nil { if v, ok := b["region"].(map[string]interface{}); ok { for key, value := range opts.Extra { v[key] = value } } } */ return b, nil } // Update updates an existing Region. func Update(client *gophercloud.ServiceClient, regionID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToRegionUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Patch(updateURL(client, regionID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete deletes a region. func Delete(client *gophercloud.ServiceClient, regionID string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, regionID), nil) return } results.go000066400000000000000000000060511335005366400333450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/regionspackage regions import ( "encoding/json" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) // Region helps manage related users. type Region struct { // Description describes the region purpose. Description string `json:"description"` // ID is the unique ID of the region. ID string `json:"id"` // Extra is a collection of miscellaneous key/values. Extra map[string]interface{} `json:"-"` // Links contains referencing links to the region. Links map[string]interface{} `json:"links"` // ParentRegionID is the ID of the parent region. ParentRegionID string `json:"parent_region_id"` } func (r *Region) UnmarshalJSON(b []byte) error { type tmp Region var s struct { tmp Extra map[string]interface{} `json:"extra"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Region(s.tmp) // Collect other fields and bundle them into Extra // but only if a field titled "extra" wasn't sent. if s.Extra != nil { r.Extra = s.Extra } else { var result interface{} err := json.Unmarshal(b, &result) if err != nil { return err } if resultMap, ok := result.(map[string]interface{}); ok { r.Extra = internal.RemainingKeys(Region{}, resultMap) } } return err } type regionResult struct { gophercloud.Result } // GetResult is the response from a Get operation. Call its Extract method // to interpret it as a Region. type GetResult struct { regionResult } // CreateResult is the response from a Create operation. Call its Extract method // to interpret it as a Region. type CreateResult struct { regionResult } // UpdateResult is the response from an Update operation. Call its Extract // method to interpret it as a Region. type UpdateResult struct { regionResult } // DeleteResult is the response from a Delete operation. Call its ExtractErr to // determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // RegionPage is a single page of Region results. type RegionPage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a page of Regions contains any results. func (r RegionPage) IsEmpty() (bool, error) { regions, err := ExtractRegions(r) return len(regions) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (r RegionPage) NextPageURL() (string, error) { var s struct { Links struct { Next string `json:"next"` Previous string `json:"previous"` } `json:"links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return s.Links.Next, err } // ExtractRegions returns a slice of Regions contained in a single page of results. func ExtractRegions(r pagination.Page) ([]Region, error) { var s struct { Regions []Region `json:"regions"` } err := (r.(RegionPage)).ExtractInto(&s) return s.Regions, err } // Extract interprets any region results as a Region. func (r regionResult) Extract() (*Region, error) { var s struct { Region *Region `json:"region"` } err := r.ExtractInto(&s) return s.Region, err } testing/000077500000000000000000000000001335005366400327705ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/regionsfixtures.go000066400000000000000000000153641335005366400352010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/regions/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/regions" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListOutput provides a single page of Region results. const ListOutput = ` { "links": { "next": null, "previous": null, "self": "http://example.com/identity/v3/regions" }, "regions": [ { "id": "RegionOne-East", "description": "East sub-region of RegionOne", "links": { "self": "http://example.com/identity/v3/regions/RegionOne-East" }, "parent_region_id": "RegionOne" }, { "id": "RegionOne-West", "description": "West sub-region of RegionOne", "links": { "self": "https://example.com/identity/v3/regions/RegionOne-West" }, "extra": { "email": "westsupport@example.com" }, "parent_region_id": "RegionOne" } ] } ` // GetOutput provides a Get result. const GetOutput = ` { "region": { "id": "RegionOne-West", "description": "West sub-region of RegionOne", "links": { "self": "https://example.com/identity/v3/regions/RegionOne-West" }, "name": "support", "extra": { "email": "westsupport@example.com" }, "parent_region_id": "RegionOne" } } ` // CreateRequest provides the input to a Create request. const CreateRequest = ` { "region": { "id": "RegionOne-West", "description": "West sub-region of RegionOne", "email": "westsupport@example.com", "parent_region_id": "RegionOne" } } ` /* // Due to a bug in Keystone, the Extra column of the Region table // is not updatable, see: https://bugs.launchpad.net/keystone/+bug/1729933 // The following line should be added to region in UpdateRequest once the // fix is merged. "email": "1stwestsupport@example.com" */ // UpdateRequest provides the input to as Update request. const UpdateRequest = ` { "region": { "description": "First West sub-region of RegionOne" } } ` /* // Due to a bug in Keystone, the Extra column of the Region table // is not updatable, see: https://bugs.launchpad.net/keystone/+bug/1729933 // This following line should replace the email in UpdateOutput.extra once // the fix is merged. "email": "1stwestsupport@example.com" */ // UpdateOutput provides an update result. const UpdateOutput = ` { "region": { "id": "RegionOne-West", "links": { "self": "https://example.com/identity/v3/regions/RegionOne-West" }, "description": "First West sub-region of RegionOne", "extra": { "email": "westsupport@example.com" }, "parent_region_id": "RegionOne" } } ` // FirstRegion is the first region in the List request. var FirstRegion = regions.Region{ ID: "RegionOne-East", Links: map[string]interface{}{ "self": "http://example.com/identity/v3/regions/RegionOne-East", }, Description: "East sub-region of RegionOne", Extra: map[string]interface{}{}, ParentRegionID: "RegionOne", } // SecondRegion is the second region in the List request. var SecondRegion = regions.Region{ ID: "RegionOne-West", Links: map[string]interface{}{ "self": "https://example.com/identity/v3/regions/RegionOne-West", }, Description: "West sub-region of RegionOne", Extra: map[string]interface{}{ "email": "westsupport@example.com", }, ParentRegionID: "RegionOne", } /* // Due to a bug in Keystone, the Extra column of the Region table // is not updatable, see: https://bugs.launchpad.net/keystone/+bug/1729933 // This should replace the email in SecondRegionUpdated.Extra once the fix // is merged. "email": "1stwestsupport@example.com" */ // SecondRegionUpdated is the second region in the List request. var SecondRegionUpdated = regions.Region{ ID: "RegionOne-West", Links: map[string]interface{}{ "self": "https://example.com/identity/v3/regions/RegionOne-West", }, Description: "First West sub-region of RegionOne", Extra: map[string]interface{}{ "email": "westsupport@example.com", }, ParentRegionID: "RegionOne", } // ExpectedRegionsSlice is the slice of regions expected to be returned from ListOutput. var ExpectedRegionsSlice = []regions.Region{FirstRegion, SecondRegion} // HandleListRegionsSuccessfully creates an HTTP handler at `/regions` on the // test handler mux that responds with a list of two regions. func HandleListRegionsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/regions", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListOutput) }) } // HandleGetRegionSuccessfully creates an HTTP handler at `/regions` on the // test handler mux that responds with a single region. func HandleGetRegionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/regions/RegionOne-West", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetOutput) }) } // HandleCreateRegionSuccessfully creates an HTTP handler at `/regions` on the // test handler mux that tests region creation. func HandleCreateRegionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/regions", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, GetOutput) }) } // HandleUpdateRegionSuccessfully creates an HTTP handler at `/regions` on the // test handler mux that tests region update. func HandleUpdateRegionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/regions/RegionOne-West", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, UpdateOutput) }) } // HandleDeleteRegionSuccessfully creates an HTTP handler at `/regions` on the // test handler mux that tests region deletion. func HandleDeleteRegionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/regions/RegionOne-West", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000053621335005366400362370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/regions/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/regions" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListRegions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListRegionsSuccessfully(t) count := 0 err := regions.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := regions.ExtractRegions(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedRegionsSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListRegionsAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListRegionsSuccessfully(t) allPages, err := regions.List(client.ServiceClient(), nil).AllPages() th.AssertNoErr(t, err) actual, err := regions.ExtractRegions(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedRegionsSlice, actual) th.AssertEquals(t, ExpectedRegionsSlice[1].Extra["email"], "westsupport@example.com") } func TestGetRegion(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetRegionSuccessfully(t) actual, err := regions.Get(client.ServiceClient(), "RegionOne-West").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondRegion, *actual) } func TestCreateRegion(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateRegionSuccessfully(t) createOpts := regions.CreateOpts{ ID: "RegionOne-West", Description: "West sub-region of RegionOne", Extra: map[string]interface{}{ "email": "westsupport@example.com", }, ParentRegionID: "RegionOne", } actual, err := regions.Create(client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondRegion, *actual) } func TestUpdateRegion(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateRegionSuccessfully(t) updateOpts := regions.UpdateOpts{ Description: "First West sub-region of RegionOne", /* // Due to a bug in Keystone, the Extra column of the Region table // is not updatable, see: https://bugs.launchpad.net/keystone/+bug/1729933 // The following lines should be uncommented once the fix is merged. Extra: map[string]interface{}{ "email": "1stwestsupport@example.com", }, */ } actual, err := regions.Update(client.ServiceClient(), "RegionOne-West", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondRegionUpdated, *actual) } func TestDeleteRegion(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteRegionSuccessfully(t) res := regions.Delete(client.ServiceClient(), "RegionOne-West") th.AssertNoErr(t, res.Err) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/regions/urls.go000066400000000000000000000011701335005366400327050ustar00rootroot00000000000000package regions import "github.com/gophercloud/gophercloud" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("regions") } func getURL(client *gophercloud.ServiceClient, regionID string) string { return client.ServiceURL("regions", regionID) } func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("regions") } func updateURL(client *gophercloud.ServiceClient, regionID string) string { return client.ServiceURL("regions", regionID) } func deleteURL(client *gophercloud.ServiceClient, regionID string) string { return client.ServiceURL("regions", regionID) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/roles/000077500000000000000000000000001335005366400310505ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/roles/doc.go000066400000000000000000000055641335005366400321560ustar00rootroot00000000000000/* Package roles provides information and interaction with the roles API resource for the OpenStack Identity service. Example to List Roles listOpts := roles.ListOpts{ DomainID: "default", } allPages, err := roles.List(identityClient, listOpts).AllPages() if err != nil { panic(err) } allRoles, err := roles.ExtractRoles(allPages) if err != nil { panic(err) } for _, role := range allRoles { fmt.Printf("%+v\n", role) } Example to Create a Role createOpts := roles.CreateOpts{ Name: "read-only-admin", DomainID: "default", Extra: map[string]interface{}{ "description": "this role grants read-only privilege cross tenant", } } role, err := roles.Create(identityClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Role roleID := "0fe36e73809d46aeae6705c39077b1b3" updateOpts := roles.UpdateOpts{ Name: "read only admin", } role, err := roles.Update(identityClient, roleID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Role roleID := "0fe36e73809d46aeae6705c39077b1b3" err := roles.Delete(identityClient, roleID).ExtractErr() if err != nil { panic(err) } Example to List Role Assignments listOpts := roles.ListAssignmentsOpts{ UserID: "97061de2ed0647b28a393c36ab584f39", ScopeProjectID: "9df1a02f5eb2416a9781e8b0c022d3ae", } allPages, err := roles.ListAssignments(identityClient, listOpts).AllPages() if err != nil { panic(err) } allRoles, err := roles.ExtractRoleAssignments(allPages) if err != nil { panic(err) } for _, role := range allRoles { fmt.Printf("%+v\n", role) } Example to List Role Assignments for a User on a Project projectID := "a99e9b4e620e4db09a2dfb6e42a01e66" userID := "9df1a02f5eb2416a9781e8b0c022d3ae" listAssignmentsOnResourceOpts := roles.ListAssignmentsOnResourceOpts{ UserID: userID, ProjectID: projectID, } allPages, err := roles.ListAssignmentsOnResource(identityClient, listAssignmentsOnResourceOpts).AllPages() if err != nil { panic(err) } allRoles, err := roles.ExtractRoles(allPages) if err != nil { panic(err) } for _, role := range allRoles { fmt.Printf("%+v\n", role) } Example to Assign a Role to a User in a Project projectID := "a99e9b4e620e4db09a2dfb6e42a01e66" userID := "9df1a02f5eb2416a9781e8b0c022d3ae" roleID := "9fe2ff9ee4384b1894a90878d3e92bab" err := roles.Assign(identityClient, roleID, roles.AssignOpts{ UserID: userID, ProjectID: projectID, }).ExtractErr() if err != nil { panic(err) } Example to Unassign a Role From a User in a Project projectID := "a99e9b4e620e4db09a2dfb6e42a01e66" userID := "9df1a02f5eb2416a9781e8b0c022d3ae" roleID := "9fe2ff9ee4384b1894a90878d3e92bab" err := roles.Unassign(identityClient, roleID, roles.UnassignOpts{ UserID: userID, ProjectID: projectID, }).ExtractErr() if err != nil { panic(err) } */ package roles golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/roles/errors.go000066400000000000000000000005451335005366400327170ustar00rootroot00000000000000package roles import "fmt" // InvalidListFilter is returned by the ToUserListQuery method when validation of // a filter does not pass type InvalidListFilter struct { FilterName string } func (e InvalidListFilter) Error() string { s := fmt.Sprintf( "Invalid filter name [%s]: it must be in format of NAME__COMPARATOR", e.FilterName, ) return s } requests.go000066400000000000000000000256431335005366400332050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/rolespackage roles import ( "net/url" "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to // the List request type ListOptsBuilder interface { ToRoleListQuery() (string, error) } // ListOpts provides options to filter the List results. type ListOpts struct { // DomainID filters the response by a domain ID. DomainID string `q:"domain_id"` // Name filters the response by role name. Name string `q:"name"` // Filters filters the response by custom filters such as // 'name__contains=foo' Filters map[string]string `q:"-"` } // ToRoleListQuery formats a ListOpts into a query string. func (opts ListOpts) ToRoleListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return "", err } params := q.Query() for k, v := range opts.Filters { i := strings.Index(k, "__") if i > 0 && i < len(k)-2 { params.Add(k, v) } else { return "", InvalidListFilter{FilterName: k} } } q = &url.URL{RawQuery: params.Encode()} return q.String(), err } // List enumerates the roles to which the current token has access. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToRoleListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return RolePage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves details on a single role, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { ToRoleCreateMap() (map[string]interface{}, error) } // CreateOpts provides options used to create a role. type CreateOpts struct { // Name is the name of the new role. Name string `json:"name" required:"true"` // DomainID is the ID of the domain the role belongs to. DomainID string `json:"domain_id,omitempty"` // Extra is free-form extra key/value pairs to describe the role. Extra map[string]interface{} `json:"-"` } // ToRoleCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToRoleCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "role") if err != nil { return nil, err } if opts.Extra != nil { if v, ok := b["role"].(map[string]interface{}); ok { for key, value := range opts.Extra { v[key] = value } } } return b, nil } // Create creates a new Role. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToRoleCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) return } // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { ToRoleUpdateMap() (map[string]interface{}, error) } // UpdateOpts provides options for updating a role. type UpdateOpts struct { // Name is the name of the new role. Name string `json:"name,omitempty"` // Extra is free-form extra key/value pairs to describe the role. Extra map[string]interface{} `json:"-"` } // ToRoleUpdateMap formats a UpdateOpts into an update request. func (opts UpdateOpts) ToRoleUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "role") if err != nil { return nil, err } if opts.Extra != nil { if v, ok := b["role"].(map[string]interface{}); ok { for key, value := range opts.Extra { v[key] = value } } } return b, nil } // Update updates an existing Role. func Update(client *gophercloud.ServiceClient, roleID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToRoleUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Patch(updateURL(client, roleID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete deletes a role. func Delete(client *gophercloud.ServiceClient, roleID string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, roleID), nil) return } // ListAssignmentsOptsBuilder allows extensions to add additional parameters to // the ListAssignments request. type ListAssignmentsOptsBuilder interface { ToRolesListAssignmentsQuery() (string, error) } // ListAssignmentsOpts allows you to query the ListAssignments method. // Specify one of or a combination of GroupId, RoleId, ScopeDomainId, // ScopeProjectId, and/or UserId to search for roles assigned to corresponding // entities. type ListAssignmentsOpts struct { // GroupID is the group ID to query. GroupID string `q:"group.id"` // RoleID is the specific role to query assignments to. RoleID string `q:"role.id"` // ScopeDomainID filters the results by the given domain ID. ScopeDomainID string `q:"scope.domain.id"` // ScopeProjectID filters the results by the given Project ID. ScopeProjectID string `q:"scope.project.id"` // UserID filterst he results by the given User ID. UserID string `q:"user.id"` // Effective lists effective assignments at the user, project, and domain // level, allowing for the effects of group membership. Effective *bool `q:"effective"` } // ToRolesListAssignmentsQuery formats a ListAssignmentsOpts into a query string. func (opts ListAssignmentsOpts) ToRolesListAssignmentsQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // ListAssignments enumerates the roles assigned to a specified resource. func ListAssignments(client *gophercloud.ServiceClient, opts ListAssignmentsOptsBuilder) pagination.Pager { url := listAssignmentsURL(client) if opts != nil { query, err := opts.ToRolesListAssignmentsQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return RoleAssignmentPage{pagination.LinkedPageBase{PageResult: r}} }) } // ListAssignmentsOnResourceOpts provides options to list role assignments // for a user/group on a project/domain type ListAssignmentsOnResourceOpts struct { // UserID is the ID of a user to assign a role // Note: exactly one of UserID or GroupID must be provided UserID string `xor:"GroupID"` // GroupID is the ID of a group to assign a role // Note: exactly one of UserID or GroupID must be provided GroupID string `xor:"UserID"` // ProjectID is the ID of a project to assign a role on // Note: exactly one of ProjectID or DomainID must be provided ProjectID string `xor:"DomainID"` // DomainID is the ID of a domain to assign a role on // Note: exactly one of ProjectID or DomainID must be provided DomainID string `xor:"ProjectID"` } // AssignOpts provides options to assign a role type AssignOpts struct { // UserID is the ID of a user to assign a role // Note: exactly one of UserID or GroupID must be provided UserID string `xor:"GroupID"` // GroupID is the ID of a group to assign a role // Note: exactly one of UserID or GroupID must be provided GroupID string `xor:"UserID"` // ProjectID is the ID of a project to assign a role on // Note: exactly one of ProjectID or DomainID must be provided ProjectID string `xor:"DomainID"` // DomainID is the ID of a domain to assign a role on // Note: exactly one of ProjectID or DomainID must be provided DomainID string `xor:"ProjectID"` } // UnassignOpts provides options to unassign a role type UnassignOpts struct { // UserID is the ID of a user to unassign a role // Note: exactly one of UserID or GroupID must be provided UserID string `xor:"GroupID"` // GroupID is the ID of a group to unassign a role // Note: exactly one of UserID or GroupID must be provided GroupID string `xor:"UserID"` // ProjectID is the ID of a project to unassign a role on // Note: exactly one of ProjectID or DomainID must be provided ProjectID string `xor:"DomainID"` // DomainID is the ID of a domain to unassign a role on // Note: exactly one of ProjectID or DomainID must be provided DomainID string `xor:"ProjectID"` } // ListAssignmentsOnResource is the operation responsible for listing role // assignments for a user/group on a project/domain. func ListAssignmentsOnResource(client *gophercloud.ServiceClient, opts ListAssignmentsOnResourceOpts) pagination.Pager { // Check xor conditions _, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return pagination.Pager{Err: err} } // Get corresponding URL var targetID string var targetType string if opts.ProjectID != "" { targetID = opts.ProjectID targetType = "projects" } else { targetID = opts.DomainID targetType = "domains" } var actorID string var actorType string if opts.UserID != "" { actorID = opts.UserID actorType = "users" } else { actorID = opts.GroupID actorType = "groups" } url := listAssignmentsOnResourceURL(client, targetType, targetID, actorType, actorID) return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return RolePage{pagination.LinkedPageBase{PageResult: r}} }) } // Assign is the operation responsible for assigning a role // to a user/group on a project/domain. func Assign(client *gophercloud.ServiceClient, roleID string, opts AssignOpts) (r AssignmentResult) { // Check xor conditions _, err := gophercloud.BuildRequestBody(opts, "") if err != nil { r.Err = err return } // Get corresponding URL var targetID string var targetType string if opts.ProjectID != "" { targetID = opts.ProjectID targetType = "projects" } else { targetID = opts.DomainID targetType = "domains" } var actorID string var actorType string if opts.UserID != "" { actorID = opts.UserID actorType = "users" } else { actorID = opts.GroupID actorType = "groups" } _, r.Err = client.Put(assignURL(client, targetType, targetID, actorType, actorID, roleID), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) return } // Unassign is the operation responsible for unassigning a role // from a user/group on a project/domain. func Unassign(client *gophercloud.ServiceClient, roleID string, opts UnassignOpts) (r UnassignmentResult) { // Check xor conditions _, err := gophercloud.BuildRequestBody(opts, "") if err != nil { r.Err = err return } // Get corresponding URL var targetID string var targetType string if opts.ProjectID != "" { targetID = opts.ProjectID targetType = "projects" } else { targetID = opts.DomainID targetType = "domains" } var actorID string var actorType string if opts.UserID != "" { actorID = opts.UserID actorType = "users" } else { actorID = opts.GroupID actorType = "groups" } _, r.Err = client.Delete(assignURL(client, targetType, targetID, actorType, actorID, roleID), &gophercloud.RequestOpts{ OkCodes: []int{204}, }) return } results.go000066400000000000000000000124051335005366400330230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/rolespackage roles import ( "encoding/json" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) // Role grants permissions to a user. type Role struct { // DomainID is the domain ID the role belongs to. DomainID string `json:"domain_id"` // ID is the unique ID of the role. ID string `json:"id"` // Links contains referencing links to the role. Links map[string]interface{} `json:"links"` // Name is the role name Name string `json:"name"` // Extra is a collection of miscellaneous key/values. Extra map[string]interface{} `json:"-"` } func (r *Role) UnmarshalJSON(b []byte) error { type tmp Role var s struct { tmp Extra map[string]interface{} `json:"extra"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Role(s.tmp) // Collect other fields and bundle them into Extra // but only if a field titled "extra" wasn't sent. if s.Extra != nil { r.Extra = s.Extra } else { var result interface{} err := json.Unmarshal(b, &result) if err != nil { return err } if resultMap, ok := result.(map[string]interface{}); ok { r.Extra = internal.RemainingKeys(Role{}, resultMap) } } return err } type roleResult struct { gophercloud.Result } // GetResult is the response from a Get operation. Call its Extract method // to interpret it as a Role. type GetResult struct { roleResult } // CreateResult is the response from a Create operation. Call its Extract method // to interpret it as a Role type CreateResult struct { roleResult } // UpdateResult is the response from an Update operation. Call its Extract // method to interpret it as a Role. type UpdateResult struct { roleResult } // DeleteResult is the response from a Delete operation. Call its ExtractErr to // determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // RolePage is a single page of Role results. type RolePage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a page of Roles contains any results. func (r RolePage) IsEmpty() (bool, error) { roles, err := ExtractRoles(r) return len(roles) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (r RolePage) NextPageURL() (string, error) { var s struct { Links struct { Next string `json:"next"` Previous string `json:"previous"` } `json:"links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return s.Links.Next, err } // ExtractProjects returns a slice of Roles contained in a single page of // results. func ExtractRoles(r pagination.Page) ([]Role, error) { var s struct { Roles []Role `json:"roles"` } err := (r.(RolePage)).ExtractInto(&s) return s.Roles, err } // Extract interprets any roleResults as a Role. func (r roleResult) Extract() (*Role, error) { var s struct { Role *Role `json:"role"` } err := r.ExtractInto(&s) return s.Role, err } // RoleAssignment is the result of a role assignments query. type RoleAssignment struct { Role AssignedRole `json:"role,omitempty"` Scope Scope `json:"scope,omitempty"` User User `json:"user,omitempty"` Group Group `json:"group,omitempty"` } // AssignedRole represents a Role in an assignment. type AssignedRole struct { ID string `json:"id,omitempty"` } // Scope represents a scope in a Role assignment. type Scope struct { Domain Domain `json:"domain,omitempty"` Project Project `json:"project,omitempty"` } // Domain represents a domain in a role assignment scope. type Domain struct { ID string `json:"id,omitempty"` } // Project represents a project in a role assignment scope. type Project struct { ID string `json:"id,omitempty"` } // User represents a user in a role assignment scope. type User struct { ID string `json:"id,omitempty"` } // Group represents a group in a role assignment scope. type Group struct { ID string `json:"id,omitempty"` } // RoleAssignmentPage is a single page of RoleAssignments results. type RoleAssignmentPage struct { pagination.LinkedPageBase } // IsEmpty returns true if the RoleAssignmentPage contains no results. func (r RoleAssignmentPage) IsEmpty() (bool, error) { roleAssignments, err := ExtractRoleAssignments(r) return len(roleAssignments) == 0, err } // NextPageURL uses the response's embedded link reference to navigate to // the next page of results. func (r RoleAssignmentPage) NextPageURL() (string, error) { var s struct { Links struct { Next string `json:"next"` } `json:"links"` } err := r.ExtractInto(&s) return s.Links.Next, err } // ExtractRoleAssignments extracts a slice of RoleAssignments from a Collection // acquired from List. func ExtractRoleAssignments(r pagination.Page) ([]RoleAssignment, error) { var s struct { RoleAssignments []RoleAssignment `json:"role_assignments"` } err := (r.(RoleAssignmentPage)).ExtractInto(&s) return s.RoleAssignments, err } // AssignmentResult represents the result of an assign operation. // Call ExtractErr method to determine if the request succeeded or failed. type AssignmentResult struct { gophercloud.ErrResult } // UnassignmentResult represents the result of an unassign operation. // Call ExtractErr method to determine if the request succeeded or failed. type UnassignmentResult struct { gophercloud.ErrResult } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/roles/testing/000077500000000000000000000000001335005366400325255ustar00rootroot00000000000000doc.go000066400000000000000000000000441335005366400335400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/roles/testing// roles unit tests package testing fixtures.go000066400000000000000000000315611335005366400346540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/roles/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) // ListOutput provides a single page of Role results. const ListOutput = ` { "links": { "next": null, "previous": null, "self": "http://example.com/identity/v3/roles" }, "roles": [ { "domain_id": "default", "id": "2844b2a08be147a08ef58317d6471f1f", "links": { "self": "http://example.com/identity/v3/roles/2844b2a08be147a08ef58317d6471f1f" }, "name": "admin-read-only" }, { "domain_id": "1789d1", "id": "9fe1d3", "links": { "self": "https://example.com/identity/v3/roles/9fe1d3" }, "name": "support", "extra": { "description": "read-only support role" } } ] } ` // GetOutput provides a Get result. const GetOutput = ` { "role": { "domain_id": "1789d1", "id": "9fe1d3", "links": { "self": "https://example.com/identity/v3/roles/9fe1d3" }, "name": "support", "extra": { "description": "read-only support role" } } } ` // CreateRequest provides the input to a Create request. const CreateRequest = ` { "role": { "domain_id": "1789d1", "name": "support", "description": "read-only support role" } } ` // UpdateRequest provides the input to as Update request. const UpdateRequest = ` { "role": { "description": "admin read-only support role" } } ` // UpdateOutput provides an update result. const UpdateOutput = ` { "role": { "domain_id": "1789d1", "id": "9fe1d3", "links": { "self": "https://example.com/identity/v3/roles/9fe1d3" }, "name": "support", "extra": { "description": "admin read-only support role" } } } ` const ListAssignmentOutput = ` { "role_assignments": [ { "links": { "assignment": "http://identity:35357/v3/domains/161718/users/313233/roles/123456" }, "role": { "id": "123456" }, "scope": { "domain": { "id": "161718" } }, "user": { "id": "313233" } }, { "links": { "assignment": "http://identity:35357/v3/projects/456789/groups/101112/roles/123456", "membership": "http://identity:35357/v3/groups/101112/users/313233" }, "role": { "id": "123456" }, "scope": { "project": { "id": "456789" } }, "user": { "id": "313233" } } ], "links": { "self": "http://identity:35357/v3/role_assignments?effective", "previous": null, "next": null } } ` // ListAssignmentsOnResourceOutput provides a result of ListAssignmentsOnResource request. const ListAssignmentsOnResourceOutput = ` { "links": { "next": null, "previous": null, "self": "http://example.com/identity/v3/projects/9e5a15/users/b964a9/roles" }, "roles": [ { "id": "9fe1d3", "links": { "self": "https://example.com/identity/v3/roles/9fe1d3" }, "name": "support", "extra": { "description": "read-only support role" } } ] } ` // FirstRole is the first role in the List request. var FirstRole = roles.Role{ DomainID: "default", ID: "2844b2a08be147a08ef58317d6471f1f", Links: map[string]interface{}{ "self": "http://example.com/identity/v3/roles/2844b2a08be147a08ef58317d6471f1f", }, Name: "admin-read-only", Extra: map[string]interface{}{}, } // SecondRole is the second role in the List request. var SecondRole = roles.Role{ DomainID: "1789d1", ID: "9fe1d3", Links: map[string]interface{}{ "self": "https://example.com/identity/v3/roles/9fe1d3", }, Name: "support", Extra: map[string]interface{}{ "description": "read-only support role", }, } // SecondRoleUpdated is how SecondRole should look after an Update. var SecondRoleUpdated = roles.Role{ DomainID: "1789d1", ID: "9fe1d3", Links: map[string]interface{}{ "self": "https://example.com/identity/v3/roles/9fe1d3", }, Name: "support", Extra: map[string]interface{}{ "description": "admin read-only support role", }, } // ExpectedRolesSlice is the slice of roles expected to be returned from ListOutput. var ExpectedRolesSlice = []roles.Role{FirstRole, SecondRole} // HandleListRolesSuccessfully creates an HTTP handler at `/roles` on the // test handler mux that responds with a list of two roles. func HandleListRolesSuccessfully(t *testing.T) { th.Mux.HandleFunc("/roles", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListOutput) }) } // HandleGetRoleSuccessfully creates an HTTP handler at `/roles` on the // test handler mux that responds with a single role. func HandleGetRoleSuccessfully(t *testing.T) { th.Mux.HandleFunc("/roles/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetOutput) }) } // HandleCreateRoleSuccessfully creates an HTTP handler at `/roles` on the // test handler mux that tests role creation. func HandleCreateRoleSuccessfully(t *testing.T) { th.Mux.HandleFunc("/roles", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, GetOutput) }) } // HandleUpdateRoleSuccessfully creates an HTTP handler at `/roles` on the // test handler mux that tests role update. func HandleUpdateRoleSuccessfully(t *testing.T) { th.Mux.HandleFunc("/roles/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, UpdateOutput) }) } // HandleDeleteRoleSuccessfully creates an HTTP handler at `/roles` on the // test handler mux that tests role deletion. func HandleDeleteRoleSuccessfully(t *testing.T) { th.Mux.HandleFunc("/roles/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) } func HandleAssignSuccessfully(t *testing.T) { th.Mux.HandleFunc("/projects/{project_id}/users/{user_id}/roles/{role_id}", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) th.Mux.HandleFunc("/projects/{project_id}/groups/{group_id}/roles/{role_id}", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) th.Mux.HandleFunc("/domains/{domain_id}/users/{user_id}/roles/{role_id}", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) th.Mux.HandleFunc("/domains/{domain_id}/groups/{group_id}/roles/{role_id}", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) } func HandleUnassignSuccessfully(t *testing.T) { th.Mux.HandleFunc("/projects/{project_id}/users/{user_id}/roles/{role_id}", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) th.Mux.HandleFunc("/projects/{project_id}/groups/{group_id}/roles/{role_id}", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) th.Mux.HandleFunc("/domains/{domain_id}/users/{user_id}/roles/{role_id}", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) th.Mux.HandleFunc("/domains/{domain_id}/groups/{group_id}/roles/{role_id}", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) } // FirstRoleAssignment is the first role assignment in the List request. var FirstRoleAssignment = roles.RoleAssignment{ Role: roles.AssignedRole{ID: "123456"}, Scope: roles.Scope{Domain: roles.Domain{ID: "161718"}}, User: roles.User{ID: "313233"}, Group: roles.Group{}, } // SecondRoleAssignemnt is the second role assignemnt in the List request. var SecondRoleAssignment = roles.RoleAssignment{ Role: roles.AssignedRole{ID: "123456"}, Scope: roles.Scope{Project: roles.Project{ID: "456789"}}, User: roles.User{ID: "313233"}, Group: roles.Group{}, } // ExpectedRoleAssignmentsSlice is the slice of role assignments expected to be // returned from ListAssignmentOutput. var ExpectedRoleAssignmentsSlice = []roles.RoleAssignment{FirstRoleAssignment, SecondRoleAssignment} // HandleListRoleAssignmentsSuccessfully creates an HTTP handler at `/role_assignments` on the // test handler mux that responds with a list of two role assignments. func HandleListRoleAssignmentsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/role_assignments", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListAssignmentOutput) }) } // RoleOnResource is the role in the ListAssignmentsOnResource request. var RoleOnResource = roles.Role{ ID: "9fe1d3", Links: map[string]interface{}{ "self": "https://example.com/identity/v3/roles/9fe1d3", }, Name: "support", Extra: map[string]interface{}{ "description": "read-only support role", }, } // ExpectedRolesOnResourceSlice is the slice of roles expected to be returned // from ListAssignmentsOnResourceOutput. var ExpectedRolesOnResourceSlice = []roles.Role{RoleOnResource} func HandleListAssignmentsOnResourceSuccessfully_ProjectsUsers(t *testing.T) { fn := func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListAssignmentsOnResourceOutput) } th.Mux.HandleFunc("/projects/{project_id}/users/{user_id}/roles", fn) } func HandleListAssignmentsOnResourceSuccessfully_ProjectsGroups(t *testing.T) { fn := func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListAssignmentsOnResourceOutput) } th.Mux.HandleFunc("/projects/{project_id}/groups/{group_id}/roles", fn) } func HandleListAssignmentsOnResourceSuccessfully_DomainsUsers(t *testing.T) { fn := func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListAssignmentsOnResourceOutput) } th.Mux.HandleFunc("/domains/{domain_id}/users/{user_id}/roles", fn) } func HandleListAssignmentsOnResourceSuccessfully_DomainsGroups(t *testing.T) { fn := func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListAssignmentsOnResourceOutput) } th.Mux.HandleFunc("/domains/{domain_id}/groups/{group_id}/roles", fn) } requests_test.go000066400000000000000000000166561335005366400357250ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/roles/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListRoles(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListRolesSuccessfully(t) count := 0 err := roles.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := roles.ExtractRoles(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedRolesSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListRolesAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListRolesSuccessfully(t) allPages, err := roles.List(client.ServiceClient(), nil).AllPages() th.AssertNoErr(t, err) actual, err := roles.ExtractRoles(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedRolesSlice, actual) th.AssertEquals(t, ExpectedRolesSlice[1].Extra["description"], "read-only support role") } func TestListUsersFiltersCheck(t *testing.T) { type test struct { filterName string wantErr bool } tests := []test{ {"foo__contains", false}, {"foo", true}, {"foo_contains", true}, {"foo__", true}, {"__foo", true}, } var listOpts roles.ListOpts for _, _test := range tests { listOpts.Filters = map[string]string{_test.filterName: "bar"} _, err := listOpts.ToRoleListQuery() if !_test.wantErr { th.AssertNoErr(t, err) } else { switch _t := err.(type) { case nil: t.Fatal("error expected but got a nil") case roles.InvalidListFilter: default: t.Fatalf("unexpected error type: [%T]", _t) } } } } func TestGetRole(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetRoleSuccessfully(t) actual, err := roles.Get(client.ServiceClient(), "9fe1d3").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondRole, *actual) } func TestCreateRole(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateRoleSuccessfully(t) createOpts := roles.CreateOpts{ Name: "support", DomainID: "1789d1", Extra: map[string]interface{}{ "description": "read-only support role", }, } actual, err := roles.Create(client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondRole, *actual) } func TestUpdateRole(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateRoleSuccessfully(t) updateOpts := roles.UpdateOpts{ Extra: map[string]interface{}{ "description": "admin read-only support role", }, } actual, err := roles.Update(client.ServiceClient(), "9fe1d3", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondRoleUpdated, *actual) } func TestDeleteRole(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteRoleSuccessfully(t) res := roles.Delete(client.ServiceClient(), "9fe1d3") th.AssertNoErr(t, res.Err) } func TestListAssignmentsSinglePage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListRoleAssignmentsSuccessfully(t) count := 0 err := roles.ListAssignments(client.ServiceClient(), roles.ListAssignmentsOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := roles.ExtractRoleAssignments(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedRoleAssignmentsSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListAssignmentsOnResource_ProjectsUsers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListAssignmentsOnResourceSuccessfully_ProjectsUsers(t) count := 0 err := roles.ListAssignmentsOnResource(client.ServiceClient(), roles.ListAssignmentsOnResourceOpts{ UserID: "{user_id}", ProjectID: "{project_id}", }).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := roles.ExtractRoles(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedRolesOnResourceSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListAssignmentsOnResource_DomainsUsers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListAssignmentsOnResourceSuccessfully_DomainsUsers(t) count := 0 err := roles.ListAssignmentsOnResource(client.ServiceClient(), roles.ListAssignmentsOnResourceOpts{ UserID: "{user_id}", DomainID: "{domain_id}", }).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := roles.ExtractRoles(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedRolesOnResourceSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListAssignmentsOnResource_ProjectsGroups(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListAssignmentsOnResourceSuccessfully_ProjectsGroups(t) count := 0 err := roles.ListAssignmentsOnResource(client.ServiceClient(), roles.ListAssignmentsOnResourceOpts{ GroupID: "{group_id}", ProjectID: "{project_id}", }).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := roles.ExtractRoles(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedRolesOnResourceSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListAssignmentsOnResource_DomainsGroups(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListAssignmentsOnResourceSuccessfully_DomainsGroups(t) count := 0 err := roles.ListAssignmentsOnResource(client.ServiceClient(), roles.ListAssignmentsOnResourceOpts{ GroupID: "{group_id}", DomainID: "{domain_id}", }).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := roles.ExtractRoles(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedRolesOnResourceSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestAssign(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAssignSuccessfully(t) err := roles.Assign(client.ServiceClient(), "{role_id}", roles.AssignOpts{ UserID: "{user_id}", ProjectID: "{project_id}", }).ExtractErr() th.AssertNoErr(t, err) err = roles.Assign(client.ServiceClient(), "{role_id}", roles.AssignOpts{ UserID: "{user_id}", DomainID: "{domain_id}", }).ExtractErr() th.AssertNoErr(t, err) err = roles.Assign(client.ServiceClient(), "{role_id}", roles.AssignOpts{ GroupID: "{group_id}", ProjectID: "{project_id}", }).ExtractErr() th.AssertNoErr(t, err) err = roles.Assign(client.ServiceClient(), "{role_id}", roles.AssignOpts{ GroupID: "{group_id}", DomainID: "{domain_id}", }).ExtractErr() th.AssertNoErr(t, err) } func TestUnassign(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUnassignSuccessfully(t) err := roles.Unassign(client.ServiceClient(), "{role_id}", roles.UnassignOpts{ UserID: "{user_id}", ProjectID: "{project_id}", }).ExtractErr() th.AssertNoErr(t, err) err = roles.Unassign(client.ServiceClient(), "{role_id}", roles.UnassignOpts{ UserID: "{user_id}", DomainID: "{domain_id}", }).ExtractErr() th.AssertNoErr(t, err) err = roles.Unassign(client.ServiceClient(), "{role_id}", roles.UnassignOpts{ GroupID: "{group_id}", ProjectID: "{project_id}", }).ExtractErr() th.AssertNoErr(t, err) err = roles.Unassign(client.ServiceClient(), "{role_id}", roles.UnassignOpts{ GroupID: "{group_id}", DomainID: "{domain_id}", }).ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/roles/urls.go000066400000000000000000000022261335005366400323660ustar00rootroot00000000000000package roles import "github.com/gophercloud/gophercloud" const ( rolePath = "roles" ) func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(rolePath) } func getURL(client *gophercloud.ServiceClient, roleID string) string { return client.ServiceURL(rolePath, roleID) } func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(rolePath) } func updateURL(client *gophercloud.ServiceClient, roleID string) string { return client.ServiceURL(rolePath, roleID) } func deleteURL(client *gophercloud.ServiceClient, roleID string) string { return client.ServiceURL(rolePath, roleID) } func listAssignmentsURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("role_assignments") } func listAssignmentsOnResourceURL(client *gophercloud.ServiceClient, targetType, targetID, actorType, actorID string) string { return client.ServiceURL(targetType, targetID, actorType, actorID, rolePath) } func assignURL(client *gophercloud.ServiceClient, targetType, targetID, actorType, actorID, roleID string) string { return client.ServiceURL(targetType, targetID, actorType, actorID, rolePath, roleID) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/services/000077500000000000000000000000001335005366400315475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/services/doc.go000066400000000000000000000024721335005366400326500ustar00rootroot00000000000000/* Package services provides information and interaction with the services API resource for the OpenStack Identity service. Example to List Services listOpts := services.ListOpts{ ServiceType: "compute", } allPages, err := services.List(identityClient, listOpts).AllPages() if err != nil { panic(err) } allServices, err := services.ExtractServices(allPages) if err != nil { panic(err) } for _, service := range allServices { fmt.Printf("%+v\n", service) } Example to Create a Service createOpts := services.CreateOpts{ Type: "compute", Extra: map[string]interface{}{ "name": "compute-service", "description": "Compute Service", }, } service, err := services.Create(identityClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Service serviceID := "3c7bbe9a6ecb453ca1789586291380ed" var iFalse bool = false updateOpts := services.UpdateOpts{ Enabled: &iFalse, Extra: map[string]interface{}{ "description": "Disabled Compute Service" }, } service, err := services.Update(identityClient, serviceID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Service serviceID := "3c7bbe9a6ecb453ca1789586291380ed" err := services.Delete(identityClient, serviceID).ExtractErr() if err != nil { panic(err) } */ package services requests.go000066400000000000000000000103521335005366400336730ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/servicespackage services import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { ToServiceCreateMap() (map[string]interface{}, error) } // CreateOpts provides options used to create a service. type CreateOpts struct { // Type is the type of the service. Type string `json:"type"` // Enabled is whether or not the service is enabled. Enabled *bool `json:"enabled,omitempty"` // Extra is free-form extra key/value pairs to describe the service. Extra map[string]interface{} `json:"-"` } // ToServiceCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToServiceCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "service") if err != nil { return nil, err } if opts.Extra != nil { if v, ok := b["service"].(map[string]interface{}); ok { for key, value := range opts.Extra { v[key] = value } } } return b, nil } // Create adds a new service of the requested type to the catalog. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToServiceCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) return } // ListOptsBuilder enables extensions to add additional parameters to the List // request. type ListOptsBuilder interface { ToServiceListMap() (string, error) } // ListOpts provides options for filtering the List results. type ListOpts struct { // ServiceType filter the response by a type of service. ServiceType string `q:"type"` // Name filters the response by a service name. Name string `q:"name"` } // ToServiceListMap builds a list query from the list options. func (opts ListOpts) ToServiceListMap() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List enumerates the services available to a specific user. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToServiceListMap() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ServicePage{pagination.LinkedPageBase{PageResult: r}} }) } // Get returns additional information about a service, given its ID. func Get(client *gophercloud.ServiceClient, serviceID string) (r GetResult) { _, r.Err = client.Get(serviceURL(client, serviceID), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { ToServiceUpdateMap() (map[string]interface{}, error) } // UpdateOpts provides options for updating a service. type UpdateOpts struct { // Type is the type of the service. Type string `json:"type"` // Enabled is whether or not the service is enabled. Enabled *bool `json:"enabled,omitempty"` // Extra is free-form extra key/value pairs to describe the service. Extra map[string]interface{} `json:"-"` } // ToServiceUpdateMap formats a UpdateOpts into an update request. func (opts UpdateOpts) ToServiceUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "service") if err != nil { return nil, err } if opts.Extra != nil { if v, ok := b["service"].(map[string]interface{}); ok { for key, value := range opts.Extra { v[key] = value } } } return b, nil } // Update updates an existing Service. func Update(client *gophercloud.ServiceClient, serviceID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToServiceUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Patch(updateURL(client, serviceID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete removes an existing service. // It either deletes all associated endpoints, or fails until all endpoints // are deleted. func Delete(client *gophercloud.ServiceClient, serviceID string) (r DeleteResult) { _, r.Err = client.Delete(serviceURL(client, serviceID), nil) return } results.go000066400000000000000000000061711335005366400335250ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/servicespackage services import ( "encoding/json" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) type serviceResult struct { gophercloud.Result } // Extract interprets a GetResult, CreateResult or UpdateResult as a concrete // Service. An error is returned if the original call or the extraction failed. func (r serviceResult) Extract() (*Service, error) { var s struct { Service *Service `json:"service"` } err := r.ExtractInto(&s) return s.Service, err } // CreateResult is the response from a Create request. Call its Extract method // to interpret it as a Service. type CreateResult struct { serviceResult } // GetResult is the response from a Get request. Call its Extract method // to interpret it as a Service. type GetResult struct { serviceResult } // UpdateResult is the response from an Update request. Call its Extract method // to interpret it as a Service. type UpdateResult struct { serviceResult } // DeleteResult is the response from a Delete request. Call its ExtractErr // method to interpret it as a Service. type DeleteResult struct { gophercloud.ErrResult } // Service represents an OpenStack Service. type Service struct { // ID is the unique ID of the service. ID string `json:"id"` // Type is the type of the service. Type string `json:"type"` // Enabled is whether or not the service is enabled. Enabled bool `json:"enabled"` // Links contains referencing links to the service. Links map[string]interface{} `json:"links"` // Extra is a collection of miscellaneous key/values. Extra map[string]interface{} `json:"-"` } func (r *Service) UnmarshalJSON(b []byte) error { type tmp Service var s struct { tmp Extra map[string]interface{} `json:"extra"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Service(s.tmp) // Collect other fields and bundle them into Extra // but only if a field titled "extra" wasn't sent. if s.Extra != nil { r.Extra = s.Extra } else { var result interface{} err := json.Unmarshal(b, &result) if err != nil { return err } if resultMap, ok := result.(map[string]interface{}); ok { r.Extra = internal.RemainingKeys(Service{}, resultMap) } } return err } // ServicePage is a single page of Service results. type ServicePage struct { pagination.LinkedPageBase } // IsEmpty returns true if the ServicePage contains no results. func (p ServicePage) IsEmpty() (bool, error) { services, err := ExtractServices(p) return len(services) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (r ServicePage) NextPageURL() (string, error) { var s struct { Links struct { Next string `json:"next"` Previous string `json:"previous"` } `json:"links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return s.Links.Next, err } // ExtractServices extracts a slice of Services from a Collection acquired // from List. func ExtractServices(r pagination.Page) ([]Service, error) { var s struct { Services []Service `json:"services"` } err := (r.(ServicePage)).ExtractInto(&s) return s.Services, err } testing/000077500000000000000000000000001335005366400331455ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/servicesdoc.go000066400000000000000000000000471335005366400342420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/services/testing// services unit tests package testing fixtures.go000066400000000000000000000130721335005366400353500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/services/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/services" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListOutput provides a single page of Service results. const ListOutput = ` { "links": { "next": null, "previous": null }, "services": [ { "id": "1234", "links": { "self": "https://example.com/identity/v3/services/1234" }, "type": "identity", "enabled": false, "extra": { "name": "service-one", "description": "Service One" } }, { "id": "9876", "links": { "self": "https://example.com/identity/v3/services/9876" }, "type": "compute", "enabled": false, "extra": { "name": "service-two", "description": "Service Two", "email": "service@example.com" } } ] } ` // GetOutput provides a Get result. const GetOutput = ` { "service": { "id": "9876", "links": { "self": "https://example.com/identity/v3/services/9876" }, "type": "compute", "enabled": false, "extra": { "name": "service-two", "description": "Service Two", "email": "service@example.com" } } } ` // CreateRequest provides the input to a Create request. const CreateRequest = ` { "service": { "description": "Service Two", "email": "service@example.com", "name": "service-two", "type": "compute" } } ` // UpdateRequest provides the input to as Update request. const UpdateRequest = ` { "service": { "type": "compute2", "description": "Service Two Updated" } } ` // UpdateOutput provides an update result. const UpdateOutput = ` { "service": { "id": "9876", "links": { "self": "https://example.com/identity/v3/services/9876" }, "type": "compute2", "enabled": false, "extra": { "name": "service-two", "description": "Service Two Updated", "email": "service@example.com" } } } ` // FirstService is the first service in the List request. var FirstService = services.Service{ ID: "1234", Links: map[string]interface{}{ "self": "https://example.com/identity/v3/services/1234", }, Type: "identity", Enabled: false, Extra: map[string]interface{}{ "name": "service-one", "description": "Service One", }, } // SecondService is the second service in the List request. var SecondService = services.Service{ ID: "9876", Links: map[string]interface{}{ "self": "https://example.com/identity/v3/services/9876", }, Type: "compute", Enabled: false, Extra: map[string]interface{}{ "name": "service-two", "description": "Service Two", "email": "service@example.com", }, } // SecondServiceUpdated is the SecondService should look after an Update. var SecondServiceUpdated = services.Service{ ID: "9876", Links: map[string]interface{}{ "self": "https://example.com/identity/v3/services/9876", }, Type: "compute2", Enabled: false, Extra: map[string]interface{}{ "name": "service-two", "description": "Service Two Updated", "email": "service@example.com", }, } // ExpectedServicesSlice is the slice of services to be returned from ListOutput. var ExpectedServicesSlice = []services.Service{FirstService, SecondService} // HandleListServicesSuccessfully creates an HTTP handler at `/services` on the // test handler mux that responds with a list of two services. func HandleListServicesSuccessfully(t *testing.T) { th.Mux.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListOutput) }) } // HandleGetServiceSuccessfully creates an HTTP handler at `/services` on the // test handler mux that responds with a single service. func HandleGetServiceSuccessfully(t *testing.T) { th.Mux.HandleFunc("/services/9876", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetOutput) }) } // HandleCreateServiceSuccessfully creates an HTTP handler at `/services` on the // test handler mux that tests service creation. func HandleCreateServiceSuccessfully(t *testing.T) { th.Mux.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, GetOutput) }) } // HandleUpdateServiceSuccessfully creates an HTTP handler at `/services` on the // test handler mux that tests service update. func HandleUpdateServiceSuccessfully(t *testing.T) { th.Mux.HandleFunc("/services/9876", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, UpdateOutput) }) } requests_test.go000066400000000000000000000056201335005366400364110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/services/testingpackage testing import ( "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/services" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreateSuccessful(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateServiceSuccessfully(t) createOpts := services.CreateOpts{ Type: "compute", Extra: map[string]interface{}{ "name": "service-two", "description": "Service Two", "email": "service@example.com", }, } actual, err := services.Create(client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondService, *actual) } func TestListServices(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListServicesSuccessfully(t) count := 0 err := services.List(client.ServiceClient(), services.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := services.ExtractServices(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedServicesSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListServicesAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListServicesSuccessfully(t) allPages, err := services.List(client.ServiceClient(), nil).AllPages() th.AssertNoErr(t, err) actual, err := services.ExtractServices(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedServicesSlice, actual) th.AssertEquals(t, ExpectedServicesSlice[0].Extra["name"], "service-one") th.AssertEquals(t, ExpectedServicesSlice[1].Extra["email"], "service@example.com") } func TestGetSuccessful(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetServiceSuccessfully(t) actual, err := services.Get(client.ServiceClient(), "9876").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondService, *actual) th.AssertEquals(t, SecondService.Extra["email"], "service@example.com") } func TestUpdateSuccessful(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateServiceSuccessfully(t) updateOpts := services.UpdateOpts{ Type: "compute2", Extra: map[string]interface{}{ "description": "Service Two Updated", }, } actual, err := services.Update(client.ServiceClient(), "9876", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondServiceUpdated, *actual) th.AssertEquals(t, SecondServiceUpdated.Extra["description"], "Service Two Updated") } func TestDeleteSuccessful(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/services/12345", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) res := services.Delete(client.ServiceClient(), "12345") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000010071335005366400330020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/servicespackage services import "github.com/gophercloud/gophercloud" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("services") } func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("services") } func serviceURL(client *gophercloud.ServiceClient, serviceID string) string { return client.ServiceURL("services", serviceID) } func updateURL(client *gophercloud.ServiceClient, serviceID string) string { return client.ServiceURL("services", serviceID) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/tokens/000077500000000000000000000000001335005366400312275ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/tokens/doc.go000066400000000000000000000043601335005366400323260ustar00rootroot00000000000000/* Package tokens provides information and interaction with the token API resource for the OpenStack Identity service. For more information, see: http://developer.openstack.org/api-ref-identity-v3.html#tokens-v3 Example to Create a Token From a Username and Password authOptions := tokens.AuthOptions{ UserID: "username", Password: "password", } token, err := tokens.Create(identityClient, authOptions).ExtractToken() if err != nil { panic(err) } Example to Create a Token From a Username, Password, and Domain authOptions := tokens.AuthOptions{ UserID: "username", Password: "password", DomainID: "default", } token, err := tokens.Create(identityClient, authOptions).ExtractToken() if err != nil { panic(err) } authOptions = tokens.AuthOptions{ UserID: "username", Password: "password", DomainName: "default", } token, err = tokens.Create(identityClient, authOptions).ExtractToken() if err != nil { panic(err) } Example to Create a Token From a Token authOptions := tokens.AuthOptions{ TokenID: "token_id", } token, err := tokens.Create(identityClient, authOptions).ExtractToken() if err != nil { panic(err) } Example to Create a Token from a Username and Password with Project ID Scope scope := tokens.Scope{ ProjectID: "0fe36e73809d46aeae6705c39077b1b3", } authOptions := tokens.AuthOptions{ Scope: &scope, UserID: "username", Password: "password", } token, err = tokens.Create(identityClient, authOptions).ExtractToken() if err != nil { panic(err) } Example to Create a Token from a Username and Password with Domain ID Scope scope := tokens.Scope{ DomainID: "default", } authOptions := tokens.AuthOptions{ Scope: &scope, UserID: "username", Password: "password", } token, err = tokens.Create(identityClient, authOptions).ExtractToken() if err != nil { panic(err) } Example to Create a Token from a Username and Password with Project Name Scope scope := tokens.Scope{ ProjectName: "project_name", DomainID: "default", } authOptions := tokens.AuthOptions{ Scope: &scope, UserID: "username", Password: "password", } token, err = tokens.Create(identityClient, authOptions).ExtractToken() if err != nil { panic(err) } */ package tokens requests.go000066400000000000000000000125021335005366400333520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/tokenspackage tokens import "github.com/gophercloud/gophercloud" // Scope allows a created token to be limited to a specific domain or project. type Scope struct { ProjectID string ProjectName string DomainID string DomainName string } // AuthOptionsBuilder provides the ability for extensions to add additional // parameters to AuthOptions. Extensions must satisfy all required methods. type AuthOptionsBuilder interface { // ToTokenV3CreateMap assembles the Create request body, returning an error // if parameters are missing or inconsistent. ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) ToTokenV3ScopeMap() (map[string]interface{}, error) CanReauth() bool } // AuthOptions represents options for authenticating a user. type AuthOptions struct { // IdentityEndpoint specifies the HTTP endpoint that is required to work with // the Identity API of the appropriate version. While it's ultimately needed // by all of the identity services, it will often be populated by a // provider-level function. IdentityEndpoint string `json:"-"` // Username is required if using Identity V2 API. Consult with your provider's // control panel to discover your account's username. In Identity V3, either // UserID or a combination of Username and DomainID or DomainName are needed. Username string `json:"username,omitempty"` UserID string `json:"id,omitempty"` Password string `json:"password,omitempty"` // At most one of DomainID and DomainName must be provided if using Username // with Identity V3. Otherwise, either are optional. DomainID string `json:"-"` DomainName string `json:"name,omitempty"` // AllowReauth should be set to true if you grant permission for Gophercloud // to cache your credentials in memory, and to allow Gophercloud to attempt // to re-authenticate automatically if/when your token expires. If you set // it to false, it will not cache these settings, but re-authentication will // not be possible. This setting defaults to false. AllowReauth bool `json:"-"` // TokenID allows users to authenticate (possibly as another user) with an // authentication token ID. TokenID string `json:"-"` // Authentication through Application Credentials requires supplying name, project and secret // For project we can use TenantID ApplicationCredentialID string `json:"-"` ApplicationCredentialName string `json:"-"` ApplicationCredentialSecret string `json:"-"` Scope Scope `json:"-"` } // ToTokenV3CreateMap builds a request body from AuthOptions. func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { gophercloudAuthOpts := gophercloud.AuthOptions{ Username: opts.Username, UserID: opts.UserID, Password: opts.Password, DomainID: opts.DomainID, DomainName: opts.DomainName, AllowReauth: opts.AllowReauth, TokenID: opts.TokenID, ApplicationCredentialID: opts.ApplicationCredentialID, ApplicationCredentialName: opts.ApplicationCredentialName, ApplicationCredentialSecret: opts.ApplicationCredentialSecret, } return gophercloudAuthOpts.ToTokenV3CreateMap(scope) } // ToTokenV3CreateMap builds a scope request body from AuthOptions. func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { scope := gophercloud.AuthScope(opts.Scope) gophercloudAuthOpts := gophercloud.AuthOptions{ Scope: &scope, DomainID: opts.DomainID, DomainName: opts.DomainName, } return gophercloudAuthOpts.ToTokenV3ScopeMap() } func (opts *AuthOptions) CanReauth() bool { return opts.AllowReauth } func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string { return map[string]string{ "X-Subject-Token": subjectToken, } } // Create authenticates and either generates a new token, or changes the Scope // of an existing token. func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) { scope, err := opts.ToTokenV3ScopeMap() if err != nil { r.Err = err return } b, err := opts.ToTokenV3CreateMap(scope) if err != nil { r.Err = err return } resp, err := c.Post(tokenURL(c), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"X-Auth-Token": ""}, }) r.Err = err if resp != nil { r.Header = resp.Header } return } // Get validates and retrieves information about another token. func Get(c *gophercloud.ServiceClient, token string) (r GetResult) { resp, err := c.Get(tokenURL(c), &r.Body, &gophercloud.RequestOpts{ MoreHeaders: subjectTokenHeaders(c, token), OkCodes: []int{200, 203}, }) if resp != nil { r.Err = err r.Header = resp.Header } return } // Validate determines if a specified token is valid or not. func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { resp, err := c.Head(tokenURL(c), &gophercloud.RequestOpts{ MoreHeaders: subjectTokenHeaders(c, token), OkCodes: []int{200, 204, 404}, }) if err != nil { return false, err } return resp.StatusCode == 200 || resp.StatusCode == 204, nil } // Revoke immediately makes specified token invalid. func Revoke(c *gophercloud.ServiceClient, token string) (r RevokeResult) { _, r.Err = c.Delete(tokenURL(c), &gophercloud.RequestOpts{ MoreHeaders: subjectTokenHeaders(c, token), }) return } results.go000066400000000000000000000115111335005366400331770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/tokenspackage tokens import ( "time" "github.com/gophercloud/gophercloud" ) // Endpoint represents a single API endpoint offered by a service. // It matches either a public, internal or admin URL. // If supported, it contains a region specifier, again if provided. // The significance of the Region field will depend upon your provider. type Endpoint struct { ID string `json:"id"` Region string `json:"region"` RegionID string `json:"region_id"` Interface string `json:"interface"` URL string `json:"url"` } // CatalogEntry provides a type-safe interface to an Identity API V3 service // catalog listing. Each class of service, such as cloud DNS or block storage // services, could have multiple CatalogEntry representing it (one by interface // type, e.g public, admin or internal). // // Note: when looking for the desired service, try, whenever possible, to key // off the type field. Otherwise, you'll tie the representation of the service // to a specific provider. type CatalogEntry struct { // Service ID ID string `json:"id"` // Name will contain the provider-specified name for the service. Name string `json:"name"` // Type will contain a type string if OpenStack defines a type for the // service. Otherwise, for provider-specific services, the provider may // assign their own type strings. Type string `json:"type"` // Endpoints will let the caller iterate over all the different endpoints that // may exist for the service. Endpoints []Endpoint `json:"endpoints"` } // ServiceCatalog provides a view into the service catalog from a previous, // successful authentication. type ServiceCatalog struct { Entries []CatalogEntry `json:"catalog"` } // Domain provides information about the domain to which this token grants // access. type Domain struct { ID string `json:"id"` Name string `json:"name"` } // User represents a user resource that exists in the Identity Service. type User struct { Domain Domain `json:"domain"` ID string `json:"id"` Name string `json:"name"` } // Role provides information about roles to which User is authorized. type Role struct { ID string `json:"id"` Name string `json:"name"` } // Project provides information about project to which User is authorized. type Project struct { Domain Domain `json:"domain"` ID string `json:"id"` Name string `json:"name"` } // commonResult is the response from a request. A commonResult has various // methods which can be used to extract different details about the result. type commonResult struct { gophercloud.Result } // Extract is a shortcut for ExtractToken. // This function is deprecated and still present for backward compatibility. func (r commonResult) Extract() (*Token, error) { return r.ExtractToken() } // ExtractToken interprets a commonResult as a Token. func (r commonResult) ExtractToken() (*Token, error) { var s Token err := r.ExtractInto(&s) if err != nil { return nil, err } // Parse the token itself from the stored headers. s.ID = r.Header.Get("X-Subject-Token") return &s, err } // ExtractServiceCatalog returns the ServiceCatalog that was generated along // with the user's Token. func (r commonResult) ExtractServiceCatalog() (*ServiceCatalog, error) { var s ServiceCatalog err := r.ExtractInto(&s) return &s, err } // ExtractUser returns the User that is the owner of the Token. func (r commonResult) ExtractUser() (*User, error) { var s struct { User *User `json:"user"` } err := r.ExtractInto(&s) return s.User, err } // ExtractRoles returns Roles to which User is authorized. func (r commonResult) ExtractRoles() ([]Role, error) { var s struct { Roles []Role `json:"roles"` } err := r.ExtractInto(&s) return s.Roles, err } // ExtractProject returns Project to which User is authorized. func (r commonResult) ExtractProject() (*Project, error) { var s struct { Project *Project `json:"project"` } err := r.ExtractInto(&s) return s.Project, err } // CreateResult is the response from a Create request. Use ExtractToken() // to interpret it as a Token, or ExtractServiceCatalog() to interpret it // as a service catalog. type CreateResult struct { commonResult } // GetResult is the response from a Get request. Use ExtractToken() // to interpret it as a Token, or ExtractServiceCatalog() to interpret it // as a service catalog. type GetResult struct { commonResult } // RevokeResult is response from a Revoke request. type RevokeResult struct { commonResult } // Token is a string that grants a user access to a controlled set of services // in an OpenStack provider. Each Token is valid for a set length of time. type Token struct { // ID is the issued token. ID string `json:"id"` // ExpiresAt is the timestamp at which this token will no longer be accepted. ExpiresAt time.Time `json:"expires_at"` } func (r commonResult) ExtractInto(v interface{}) error { return r.ExtractIntoStructPtr(v, "token") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/tokens/testing/000077500000000000000000000000001335005366400327045ustar00rootroot00000000000000doc.go000066400000000000000000000000451335005366400337200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/tokens/testing// tokens unit tests package testing fixtures.go000066400000000000000000000140011335005366400350210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/tokens/testingpackage testing import ( "encoding/json" "net/http" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/testhelper" ) const testTokenID = "130f6c17-420e-4a0b-97b0-0c9cf2a05f30" // TokenOutput is a sample response to a Token call. const TokenOutput = ` { "token":{ "is_domain":false, "methods":[ "password" ], "roles":[ { "id":"434426788d5a451faf763b0e6db5aefb", "name":"admin" } ], "expires_at":"2017-06-03T02:19:49.000000Z", "project":{ "domain":{ "id":"default", "name":"Default" }, "id":"a99e9b4e620e4db09a2dfb6e42a01e66", "name":"admin" }, "catalog":[ { "endpoints":[ { "url":"http://127.0.0.1:8774/v2.1/a99e9b4e620e4db09a2dfb6e42a01e66", "interface":"admin", "region":"RegionOne", "region_id":"RegionOne", "id":"3eac9e7588eb4eb2a4650cf5e079505f" }, { "url":"http://127.0.0.1:8774/v2.1/a99e9b4e620e4db09a2dfb6e42a01e66", "interface":"internal", "region":"RegionOne", "region_id":"RegionOne", "id":"6b33fabc69c34ea782a3f6282582b59f" }, { "url":"http://127.0.0.1:8774/v2.1/a99e9b4e620e4db09a2dfb6e42a01e66", "interface":"public", "region":"RegionOne", "region_id":"RegionOne", "id":"dae63c71bee24070a71f5425e7a916b5" } ], "type":"compute", "id":"17e0fa04647d4155a7933ee624dd66da", "name":"nova" }, { "endpoints":[ { "url":"http://127.0.0.1:35357/v3", "interface":"admin", "region":"RegionOne", "region_id":"RegionOne", "id":"0539aeff80954a0bb756cec496768d3d" }, { "url":"http://127.0.0.1:5000/v3", "interface":"public", "region":"RegionOne", "region_id":"RegionOne", "id":"15bdf2d0853e4c939993d29548b1b56f" }, { "url":"http://127.0.0.1:5000/v3", "interface":"internal", "region":"RegionOne", "region_id":"RegionOne", "id":"3b4423c54ba343c58226bc424cb11c4b" } ], "type":"identity", "id":"1cde0ea8cb3c49d8928cb172ca825ca5", "name":"keystone" } ], "user":{ "domain":{ "id":"default", "name":"Default" }, "password_expires_at":null, "name":"admin", "id":"0fe36e73809d46aeae6705c39077b1b3" }, "audit_ids":[ "ysSI0bEWR0Gmrp4LHL9LFw" ], "issued_at":"2017-06-03T01:19:49.000000Z" } }` var expectedTokenTime, _ = time.Parse(gophercloud.RFC3339Milli, "2017-06-03T02:19:49.000000Z") var ExpectedToken = tokens.Token{ ID: testTokenID, ExpiresAt: expectedTokenTime, } var catalogEntry1 = tokens.CatalogEntry{ ID: "17e0fa04647d4155a7933ee624dd66da", Name: "nova", Type: "compute", Endpoints: []tokens.Endpoint{ tokens.Endpoint{ ID: "3eac9e7588eb4eb2a4650cf5e079505f", Region: "RegionOne", RegionID: "RegionOne", Interface: "admin", URL: "http://127.0.0.1:8774/v2.1/a99e9b4e620e4db09a2dfb6e42a01e66", }, tokens.Endpoint{ ID: "6b33fabc69c34ea782a3f6282582b59f", Region: "RegionOne", RegionID: "RegionOne", Interface: "internal", URL: "http://127.0.0.1:8774/v2.1/a99e9b4e620e4db09a2dfb6e42a01e66", }, tokens.Endpoint{ ID: "dae63c71bee24070a71f5425e7a916b5", Region: "RegionOne", RegionID: "RegionOne", Interface: "public", URL: "http://127.0.0.1:8774/v2.1/a99e9b4e620e4db09a2dfb6e42a01e66", }, }, } var catalogEntry2 = tokens.CatalogEntry{ ID: "1cde0ea8cb3c49d8928cb172ca825ca5", Name: "keystone", Type: "identity", Endpoints: []tokens.Endpoint{ tokens.Endpoint{ ID: "0539aeff80954a0bb756cec496768d3d", Region: "RegionOne", RegionID: "RegionOne", Interface: "admin", URL: "http://127.0.0.1:35357/v3", }, tokens.Endpoint{ ID: "15bdf2d0853e4c939993d29548b1b56f", Region: "RegionOne", RegionID: "RegionOne", Interface: "public", URL: "http://127.0.0.1:5000/v3", }, tokens.Endpoint{ ID: "3b4423c54ba343c58226bc424cb11c4b", Region: "RegionOne", RegionID: "RegionOne", Interface: "internal", URL: "http://127.0.0.1:5000/v3", }, }, } // ExpectedServiceCatalog contains expected service extracted from token response. var ExpectedServiceCatalog = tokens.ServiceCatalog{ Entries: []tokens.CatalogEntry{catalogEntry1, catalogEntry2}, } var domain = tokens.Domain{ ID: "default", Name: "Default", } // ExpectedUser contains expected user extracted from token response. var ExpectedUser = tokens.User{ Domain: domain, ID: "0fe36e73809d46aeae6705c39077b1b3", Name: "admin", } var role = tokens.Role{ ID: "434426788d5a451faf763b0e6db5aefb", Name: "admin", } // ExpectedRoles contains expected roles extracted from token response. var ExpectedRoles = []tokens.Role{role} // ExpectedProject contains expected project extracted from token response. var ExpectedProject = tokens.Project{ Domain: domain, ID: "a99e9b4e620e4db09a2dfb6e42a01e66", Name: "admin", } func getGetResult(t *testing.T) tokens.GetResult { result := tokens.GetResult{} result.Header = http.Header{ "X-Subject-Token": []string{testTokenID}, } err := json.Unmarshal([]byte(TokenOutput), &result.Body) testhelper.AssertNoErr(t, err) return result } requests_test.go000066400000000000000000000374041335005366400360760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/tokens/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/testhelper" ) // authTokenPost verifies that providing certain AuthOptions and Scope results in an expected JSON structure. func authTokenPost(t *testing.T, options tokens.AuthOptions, scope *tokens.Scope, requestJSON string) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() client := gophercloud.ServiceClient{ ProviderClient: &gophercloud.ProviderClient{}, Endpoint: testhelper.Endpoint(), } testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "POST") testhelper.TestHeader(t, r, "Content-Type", "application/json") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestJSONRequest(t, r, requestJSON) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, `{ "token": { "expires_at": "2014-10-02T13:45:00.000000Z" } }`) }) if scope != nil { options.Scope = *scope } expected := &tokens.Token{ ExpiresAt: time.Date(2014, 10, 2, 13, 45, 0, 0, time.UTC), } actual, err := tokens.Create(&client, &options).Extract() testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, expected, actual) } func authTokenPostErr(t *testing.T, options tokens.AuthOptions, scope *tokens.Scope, includeToken bool, expectedErr error) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() client := gophercloud.ServiceClient{ ProviderClient: &gophercloud.ProviderClient{}, Endpoint: testhelper.Endpoint(), } if includeToken { client.TokenID = "abcdef123456" } if scope != nil { options.Scope = *scope } _, err := tokens.Create(&client, &options).Extract() if err == nil { t.Errorf("Create did NOT return an error") } if err != expectedErr { t.Errorf("Create returned an unexpected error: wanted %v, got %v", expectedErr, err) } } func TestCreateUserIDAndPassword(t *testing.T) { authTokenPost(t, tokens.AuthOptions{UserID: "me", Password: "squirrel!"}, nil, ` { "auth": { "identity": { "methods": ["password"], "password": { "user": { "id": "me", "password": "squirrel!" } } } } } `) } func TestCreateUsernameDomainIDPassword(t *testing.T) { authTokenPost(t, tokens.AuthOptions{Username: "fakey", Password: "notpassword", DomainID: "abc123"}, nil, ` { "auth": { "identity": { "methods": ["password"], "password": { "user": { "domain": { "id": "abc123" }, "name": "fakey", "password": "notpassword" } } } } } `) } func TestCreateUsernameDomainNamePassword(t *testing.T) { authTokenPost(t, tokens.AuthOptions{Username: "frank", Password: "swordfish", DomainName: "spork.net"}, nil, ` { "auth": { "identity": { "methods": ["password"], "password": { "user": { "domain": { "name": "spork.net" }, "name": "frank", "password": "swordfish" } } } } } `) } func TestCreateTokenID(t *testing.T) { authTokenPost(t, tokens.AuthOptions{TokenID: "12345abcdef"}, nil, ` { "auth": { "identity": { "methods": ["token"], "token": { "id": "12345abcdef" } } } } `) } func TestCreateProjectIDScope(t *testing.T) { options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"} scope := &tokens.Scope{ProjectID: "123456"} authTokenPost(t, options, scope, ` { "auth": { "identity": { "methods": ["password"], "password": { "user": { "id": "fenris", "password": "g0t0h311" } } }, "scope": { "project": { "id": "123456" } } } } `) } func TestCreateDomainIDScope(t *testing.T) { options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"} scope := &tokens.Scope{DomainID: "1000"} authTokenPost(t, options, scope, ` { "auth": { "identity": { "methods": ["password"], "password": { "user": { "id": "fenris", "password": "g0t0h311" } } }, "scope": { "domain": { "id": "1000" } } } } `) } func TestCreateDomainNameScope(t *testing.T) { options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"} scope := &tokens.Scope{DomainName: "evil-plans"} authTokenPost(t, options, scope, ` { "auth": { "identity": { "methods": ["password"], "password": { "user": { "id": "fenris", "password": "g0t0h311" } } }, "scope": { "domain": { "name": "evil-plans" } } } } `) } func TestCreateProjectNameAndDomainIDScope(t *testing.T) { options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"} scope := &tokens.Scope{ProjectName: "world-domination", DomainID: "1000"} authTokenPost(t, options, scope, ` { "auth": { "identity": { "methods": ["password"], "password": { "user": { "id": "fenris", "password": "g0t0h311" } } }, "scope": { "project": { "domain": { "id": "1000" }, "name": "world-domination" } } } } `) } func TestCreateProjectNameAndDomainNameScope(t *testing.T) { options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"} scope := &tokens.Scope{ProjectName: "world-domination", DomainName: "evil-plans"} authTokenPost(t, options, scope, ` { "auth": { "identity": { "methods": ["password"], "password": { "user": { "id": "fenris", "password": "g0t0h311" } } }, "scope": { "project": { "domain": { "name": "evil-plans" }, "name": "world-domination" } } } } `) } func TestCreateApplicationCredentialIDAndSecret(t *testing.T) { authTokenPost(t, tokens.AuthOptions{ApplicationCredentialID: "12345abcdef", ApplicationCredentialSecret: "mysecret"}, nil, ` { "auth": { "identity": { "application_credential": { "id": "12345abcdef", "secret": "mysecret" }, "methods": [ "application_credential" ] } } } `) } func TestCreateApplicationCredentialNameAndSecret(t *testing.T) { authTokenPost(t, tokens.AuthOptions{ApplicationCredentialName: "myappcred", ApplicationCredentialSecret: "mysecret", Username: "fenris", DomainName: "evil-plans"}, nil, ` { "auth": { "identity": { "application_credential": { "name": "myappcred", "secret": "mysecret", "user": { "name": "fenris", "domain": { "name": "evil-plans" } } }, "methods": [ "application_credential" ] } } } `) } func TestCreateExtractsTokenFromResponse(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() client := gophercloud.ServiceClient{ ProviderClient: &gophercloud.ProviderClient{}, Endpoint: testhelper.Endpoint(), } testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("X-Subject-Token", "aaa111") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, `{ "token": { "expires_at": "2014-10-02T13:45:00.000000Z" } }`) }) options := tokens.AuthOptions{UserID: "me", Password: "shhh"} token, err := tokens.Create(&client, &options).Extract() if err != nil { t.Fatalf("Create returned an error: %v", err) } if token.ID != "aaa111" { t.Errorf("Expected token to be aaa111, but was %s", token.ID) } } func TestCreateFailureEmptyAuth(t *testing.T) { authTokenPostErr(t, tokens.AuthOptions{}, nil, false, gophercloud.ErrMissingPassword{}) } func TestCreateFailureTokenIDUsername(t *testing.T) { authTokenPostErr(t, tokens.AuthOptions{Username: "something", TokenID: "12345"}, nil, true, gophercloud.ErrUsernameWithToken{}) } func TestCreateFailureTokenIDUserID(t *testing.T) { authTokenPostErr(t, tokens.AuthOptions{UserID: "something", TokenID: "12345"}, nil, true, gophercloud.ErrUserIDWithToken{}) } func TestCreateFailureTokenIDDomainID(t *testing.T) { authTokenPostErr(t, tokens.AuthOptions{DomainID: "something", TokenID: "12345"}, nil, true, gophercloud.ErrDomainIDWithToken{}) } func TestCreateFailureTokenIDDomainName(t *testing.T) { authTokenPostErr(t, tokens.AuthOptions{DomainName: "something", TokenID: "12345"}, nil, true, gophercloud.ErrDomainNameWithToken{}) } func TestCreateFailureMissingUser(t *testing.T) { options := tokens.AuthOptions{Password: "supersecure"} authTokenPostErr(t, options, nil, false, gophercloud.ErrUsernameOrUserID{}) } func TestCreateFailureBothUser(t *testing.T) { options := tokens.AuthOptions{ Password: "supersecure", Username: "oops", UserID: "redundancy", } authTokenPostErr(t, options, nil, false, gophercloud.ErrUsernameOrUserID{}) } func TestCreateFailureMissingDomain(t *testing.T) { options := tokens.AuthOptions{ Password: "supersecure", Username: "notuniqueenough", } authTokenPostErr(t, options, nil, false, gophercloud.ErrDomainIDOrDomainName{}) } func TestCreateFailureBothDomain(t *testing.T) { options := tokens.AuthOptions{ Password: "supersecure", Username: "someone", DomainID: "hurf", DomainName: "durf", } authTokenPostErr(t, options, nil, false, gophercloud.ErrDomainIDOrDomainName{}) } func TestCreateFailureUserIDDomainID(t *testing.T) { options := tokens.AuthOptions{ UserID: "100", Password: "stuff", DomainID: "oops", } authTokenPostErr(t, options, nil, false, gophercloud.ErrDomainIDWithUserID{}) } func TestCreateFailureUserIDDomainName(t *testing.T) { options := tokens.AuthOptions{ UserID: "100", Password: "sssh", DomainName: "oops", } authTokenPostErr(t, options, nil, false, gophercloud.ErrDomainNameWithUserID{}) } func TestCreateFailureScopeProjectNameAlone(t *testing.T) { options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"} scope := &tokens.Scope{ProjectName: "notenough"} authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeDomainIDOrDomainName{}) } func TestCreateFailureScopeProjectNameAndID(t *testing.T) { options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"} scope := &tokens.Scope{ProjectName: "whoops", ProjectID: "toomuch", DomainID: "1234"} authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeProjectIDOrProjectName{}) } func TestCreateFailureScopeProjectIDAndDomainID(t *testing.T) { options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"} scope := &tokens.Scope{ProjectID: "toomuch", DomainID: "notneeded"} authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeProjectIDAlone{}) } func TestCreateFailureScopeProjectIDAndDomainNAme(t *testing.T) { options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"} scope := &tokens.Scope{ProjectID: "toomuch", DomainName: "notneeded"} authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeProjectIDAlone{}) } func TestCreateFailureScopeDomainIDAndDomainName(t *testing.T) { options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"} scope := &tokens.Scope{DomainID: "toomuch", DomainName: "notneeded"} authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeDomainIDOrDomainName{}) } /* func TestCreateFailureEmptyScope(t *testing.T) { options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"} scope := &tokens.Scope{} authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeEmpty{}) } */ func TestGetRequest(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() client := gophercloud.ServiceClient{ ProviderClient: &gophercloud.ProviderClient{ TokenID: "12345abcdef", }, Endpoint: testhelper.Endpoint(), } testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") testhelper.TestHeader(t, r, "Content-Type", "") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestHeader(t, r, "X-Auth-Token", "12345abcdef") testhelper.TestHeader(t, r, "X-Subject-Token", "abcdef12345") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "token": { "expires_at": "2014-08-29T13:10:01.000000Z" } } `) }) token, err := tokens.Get(&client, "abcdef12345").Extract() if err != nil { t.Errorf("Info returned an error: %v", err) } expected, _ := time.Parse(time.UnixDate, "Fri Aug 29 13:10:01 UTC 2014") if token.ExpiresAt != expected { t.Errorf("Expected expiration time %s, but was %s", expected.Format(time.UnixDate), time.Time(token.ExpiresAt).Format(time.UnixDate)) } } func prepareAuthTokenHandler(t *testing.T, expectedMethod string, status int) gophercloud.ServiceClient { client := gophercloud.ServiceClient{ ProviderClient: &gophercloud.ProviderClient{ TokenID: "12345abcdef", }, Endpoint: testhelper.Endpoint(), } testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, expectedMethod) testhelper.TestHeader(t, r, "Content-Type", "") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestHeader(t, r, "X-Auth-Token", "12345abcdef") testhelper.TestHeader(t, r, "X-Subject-Token", "abcdef12345") w.WriteHeader(status) }) return client } func TestValidateRequestSuccessful(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() client := prepareAuthTokenHandler(t, "HEAD", http.StatusNoContent) ok, err := tokens.Validate(&client, "abcdef12345") if err != nil { t.Errorf("Unexpected error from Validate: %v", err) } if !ok { t.Errorf("Validate returned false for a valid token") } } func TestValidateRequestFailure(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() client := prepareAuthTokenHandler(t, "HEAD", http.StatusNotFound) ok, err := tokens.Validate(&client, "abcdef12345") if err != nil { t.Errorf("Unexpected error from Validate: %v", err) } if ok { t.Errorf("Validate returned true for an invalid token") } } func TestValidateRequestError(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() client := prepareAuthTokenHandler(t, "HEAD", http.StatusMethodNotAllowed) _, err := tokens.Validate(&client, "abcdef12345") if err == nil { t.Errorf("Missing expected error from Validate") } } func TestRevokeRequestSuccessful(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() client := prepareAuthTokenHandler(t, "DELETE", http.StatusNoContent) res := tokens.Revoke(&client, "abcdef12345") testhelper.AssertNoErr(t, res.Err) } func TestRevokeRequestError(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() client := prepareAuthTokenHandler(t, "DELETE", http.StatusNotFound) res := tokens.Revoke(&client, "abcdef12345") if res.Err == nil { t.Errorf("Missing expected error from Revoke") } } func TestNoTokenInResponse(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() client := gophercloud.ServiceClient{ ProviderClient: &gophercloud.ProviderClient{}, Endpoint: testhelper.Endpoint(), } testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, `{}`) }) options := tokens.AuthOptions{UserID: "me", Password: "squirrel!"} _, err := tokens.Create(&client, &options).Extract() testhelper.AssertNoErr(t, err) } results_test.go000066400000000000000000000020721335005366400357150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/tokens/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/testhelper" ) func TestExtractToken(t *testing.T) { result := getGetResult(t) token, err := result.ExtractToken() testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, &ExpectedToken, token) } func TestExtractCatalog(t *testing.T) { result := getGetResult(t) catalog, err := result.ExtractServiceCatalog() testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, &ExpectedServiceCatalog, catalog) } func TestExtractUser(t *testing.T) { result := getGetResult(t) user, err := result.ExtractUser() testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, &ExpectedUser, user) } func TestExtractRoles(t *testing.T) { result := getGetResult(t) roles, err := result.ExtractRoles() testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, ExpectedRoles, roles) } func TestExtractProject(t *testing.T) { result := getGetResult(t) project, err := result.ExtractProject() testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, &ExpectedProject, project) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/tokens/urls.go000066400000000000000000000002331335005366400325410ustar00rootroot00000000000000package tokens import "github.com/gophercloud/gophercloud" func tokenURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("auth", "tokens") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/users/000077500000000000000000000000001335005366400310655ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/users/doc.go000066400000000000000000000070231335005366400321630ustar00rootroot00000000000000/* Package users manages and retrieves Users in the OpenStack Identity Service. Example to List Users listOpts := users.ListOpts{ DomainID: "default", } allPages, err := users.List(identityClient, listOpts).AllPages() if err != nil { panic(err) } allUsers, err := users.ExtractUsers(allPages) if err != nil { panic(err) } for _, user := range allUsers { fmt.Printf("%+v\n", user) } Example to Create a User projectID := "a99e9b4e620e4db09a2dfb6e42a01e66" createOpts := users.CreateOpts{ Name: "username", DomainID: "default", DefaultProjectID: projectID, Enabled: gophercloud.Enabled, Password: "supersecret", Extra: map[string]interface{}{ "email": "username@example.com", } } user, err := users.Create(identityClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a User userID := "0fe36e73809d46aeae6705c39077b1b3" updateOpts := users.UpdateOpts{ Enabled: gophercloud.Disabled, } user, err := users.Update(identityClient, userID, updateOpts).Extract() if err != nil { panic(err) } Example to Change Password of a User userID := "0fe36e73809d46aeae6705c39077b1b3" originalPassword := "secretsecret" password := "new_secretsecret" changePasswordOpts := users.ChangePasswordOpts{ OriginalPassword: originalPassword, Password: password, } err := users.ChangePassword(identityClient, userID, changePasswordOpts).ExtractErr() if err != nil { panic(err) } Example to Delete a User userID := "0fe36e73809d46aeae6705c39077b1b3" err := users.Delete(identityClient, userID).ExtractErr() if err != nil { panic(err) } Example to List Groups a User Belongs To userID := "0fe36e73809d46aeae6705c39077b1b3" allPages, err := users.ListGroups(identityClient, userID).AllPages() if err != nil { panic(err) } allGroups, err := groups.ExtractGroups(allPages) if err != nil { panic(err) } for _, group := range allGroups { fmt.Printf("%+v\n", group) } Example to Add a User to a Group groupID := "bede500ee1124ae9b0006ff859758b3a" userID := "0fe36e73809d46aeae6705c39077b1b3" err := users.AddToGroup(identityClient, groupID, userID).ExtractErr() if err != nil { panic(err) } Example to Check Whether a User Belongs to a Group groupID := "bede500ee1124ae9b0006ff859758b3a" userID := "0fe36e73809d46aeae6705c39077b1b3" ok, err := users.IsMemberOfGroup(identityClient, groupID, userID).Extract() if err != nil { panic(err) } if ok { fmt.Printf("user %s is a member of group %s\n", userID, groupID) } Example to Remove a User from a Group groupID := "bede500ee1124ae9b0006ff859758b3a" userID := "0fe36e73809d46aeae6705c39077b1b3" err := users.RemoveFromGroup(identityClient, groupID, userID).ExtractErr() if err != nil { panic(err) } Example to List Projects a User Belongs To userID := "0fe36e73809d46aeae6705c39077b1b3" allPages, err := users.ListProjects(identityClient, userID).AllPages() if err != nil { panic(err) } allProjects, err := projects.ExtractProjects(allPages) if err != nil { panic(err) } for _, project := range allProjects { fmt.Printf("%+v\n", project) } Example to List Users in a Group groupID := "bede500ee1124ae9b0006ff859758b3a" listOpts := users.ListOpts{ DomainID: "default", } allPages, err := users.ListInGroup(identityClient, groupID, listOpts).AllPages() if err != nil { panic(err) } allUsers, err := users.ExtractUsers(allPages) if err != nil { panic(err) } for _, user := range allUsers { fmt.Printf("%+v\n", user) } */ package users golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/users/errors.go000066400000000000000000000005451335005366400327340ustar00rootroot00000000000000package users import "fmt" // InvalidListFilter is returned by the ToUserListQuery method when validation of // a filter does not pass type InvalidListFilter struct { FilterName string } func (e InvalidListFilter) Error() string { s := fmt.Sprintf( "Invalid filter name [%s]: it must be in format of NAME__COMPARATOR", e.FilterName, ) return s } requests.go000066400000000000000000000237271335005366400332230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/userspackage users import ( "net/http" "net/url" "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" "github.com/gophercloud/gophercloud/pagination" ) // Option is a specific option defined at the API to enable features // on a user account. type Option string const ( IgnoreChangePasswordUponFirstUse Option = "ignore_change_password_upon_first_use" IgnorePasswordExpiry Option = "ignore_password_expiry" IgnoreLockoutFailureAttempts Option = "ignore_lockout_failure_attempts" MultiFactorAuthRules Option = "multi_factor_auth_rules" MultiFactorAuthEnabled Option = "multi_factor_auth_enabled" ) // ListOptsBuilder allows extensions to add additional parameters to // the List request type ListOptsBuilder interface { ToUserListQuery() (string, error) } // ListOpts provides options to filter the List results. type ListOpts struct { // DomainID filters the response by a domain ID. DomainID string `q:"domain_id"` // Enabled filters the response by enabled users. Enabled *bool `q:"enabled"` // IdpID filters the response by an Identity Provider ID. IdPID string `q:"idp_id"` // Name filters the response by username. Name string `q:"name"` // PasswordExpiresAt filters the response based on expiring passwords. PasswordExpiresAt string `q:"password_expires_at"` // ProtocolID filters the response by protocol ID. ProtocolID string `q:"protocol_id"` // UniqueID filters the response by unique ID. UniqueID string `q:"unique_id"` // Filters filters the response by custom filters such as // 'name__contains=foo' Filters map[string]string `q:"-"` } // ToUserListQuery formats a ListOpts into a query string. func (opts ListOpts) ToUserListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return "", err } params := q.Query() for k, v := range opts.Filters { i := strings.Index(k, "__") if i > 0 && i < len(k)-2 { params.Add(k, v) } else { return "", InvalidListFilter{FilterName: k} } } q = &url.URL{RawQuery: params.Encode()} return q.String(), err } // List enumerates the Users to which the current token has access. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToUserListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return UserPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves details on a single user, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { ToUserCreateMap() (map[string]interface{}, error) } // CreateOpts provides options used to create a user. type CreateOpts struct { // Name is the name of the new user. Name string `json:"name" required:"true"` // DefaultProjectID is the ID of the default project of the user. DefaultProjectID string `json:"default_project_id,omitempty"` // Description is a description of the user. Description string `json:"description,omitempty"` // DomainID is the ID of the domain the user belongs to. DomainID string `json:"domain_id,omitempty"` // Enabled sets the user status to enabled or disabled. Enabled *bool `json:"enabled,omitempty"` // Extra is free-form extra key/value pairs to describe the user. Extra map[string]interface{} `json:"-"` // Options are defined options in the API to enable certain features. Options map[Option]interface{} `json:"options,omitempty"` // Password is the password of the new user. Password string `json:"password,omitempty"` } // ToUserCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToUserCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "user") if err != nil { return nil, err } if opts.Extra != nil { if v, ok := b["user"].(map[string]interface{}); ok { for key, value := range opts.Extra { v[key] = value } } } return b, nil } // Create creates a new User. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToUserCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) return } // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { ToUserUpdateMap() (map[string]interface{}, error) } // UpdateOpts provides options for updating a user account. type UpdateOpts struct { // Name is the name of the new user. Name string `json:"name,omitempty"` // DefaultProjectID is the ID of the default project of the user. DefaultProjectID string `json:"default_project_id,omitempty"` // Description is a description of the user. Description string `json:"description,omitempty"` // DomainID is the ID of the domain the user belongs to. DomainID string `json:"domain_id,omitempty"` // Enabled sets the user status to enabled or disabled. Enabled *bool `json:"enabled,omitempty"` // Extra is free-form extra key/value pairs to describe the user. Extra map[string]interface{} `json:"-"` // Options are defined options in the API to enable certain features. Options map[Option]interface{} `json:"options,omitempty"` // Password is the password of the new user. Password string `json:"password,omitempty"` } // ToUserUpdateMap formats a UpdateOpts into an update request. func (opts UpdateOpts) ToUserUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "user") if err != nil { return nil, err } if opts.Extra != nil { if v, ok := b["user"].(map[string]interface{}); ok { for key, value := range opts.Extra { v[key] = value } } } return b, nil } // Update updates an existing User. func Update(client *gophercloud.ServiceClient, userID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToUserUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Patch(updateURL(client, userID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // ChangePasswordOptsBuilder allows extensions to add additional parameters to // the ChangePassword request. type ChangePasswordOptsBuilder interface { ToUserChangePasswordMap() (map[string]interface{}, error) } // ChangePasswordOpts provides options for changing password for a user. type ChangePasswordOpts struct { // OriginalPassword is the original password of the user. OriginalPassword string `json:"original_password"` // Password is the new password of the user. Password string `json:"password"` } // ToUserChangePasswordMap formats a ChangePasswordOpts into a ChangePassword request. func (opts ChangePasswordOpts) ToUserChangePasswordMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "user") if err != nil { return nil, err } return b, nil } // ChangePassword changes password for a user. func ChangePassword(client *gophercloud.ServiceClient, userID string, opts ChangePasswordOptsBuilder) (r ChangePasswordResult) { b, err := opts.ToUserChangePasswordMap() if err != nil { r.Err = err return } _, r.Err = client.Post(changePasswordURL(client, userID), &b, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) return } // Delete deletes a user. func Delete(client *gophercloud.ServiceClient, userID string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, userID), nil) return } // ListGroups enumerates groups user belongs to. func ListGroups(client *gophercloud.ServiceClient, userID string) pagination.Pager { url := listGroupsURL(client, userID) return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return groups.GroupPage{LinkedPageBase: pagination.LinkedPageBase{PageResult: r}} }) } // AddToGroup adds a user to a group. func AddToGroup(client *gophercloud.ServiceClient, groupID, userID string) (r AddToGroupResult) { url := addToGroupURL(client, groupID, userID) _, r.Err = client.Put(url, nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) return } // IsMemberOfGroup checks whether a user belongs to a group. func IsMemberOfGroup(client *gophercloud.ServiceClient, groupID, userID string) (r IsMemberOfGroupResult) { url := isMemberOfGroupURL(client, groupID, userID) var response *http.Response response, r.Err = client.Head(url, &gophercloud.RequestOpts{ OkCodes: []int{204, 404}, }) if r.Err == nil && response != nil { if (*response).StatusCode == 204 { r.isMember = true } } return } // RemoveFromGroup removes a user from a group. func RemoveFromGroup(client *gophercloud.ServiceClient, groupID, userID string) (r RemoveFromGroupResult) { url := removeFromGroupURL(client, groupID, userID) _, r.Err = client.Delete(url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) return } // ListProjects enumerates groups user belongs to. func ListProjects(client *gophercloud.ServiceClient, userID string) pagination.Pager { url := listProjectsURL(client, userID) return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return projects.ProjectPage{LinkedPageBase: pagination.LinkedPageBase{PageResult: r}} }) } // ListInGroup enumerates users that belong to a group. func ListInGroup(client *gophercloud.ServiceClient, groupID string, opts ListOptsBuilder) pagination.Pager { url := listInGroupURL(client, groupID) if opts != nil { query, err := opts.ToUserListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return UserPage{pagination.LinkedPageBase{PageResult: r}} }) } results.go000066400000000000000000000112031335005366400330330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/userspackage users import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) // User represents a User in the OpenStack Identity Service. type User struct { // DefaultProjectID is the ID of the default project of the user. DefaultProjectID string `json:"default_project_id"` // Description is the description of the user. Description string `json:"description"` // DomainID is the domain ID the user belongs to. DomainID string `json:"domain_id"` // Enabled is whether or not the user is enabled. Enabled bool `json:"enabled"` // Extra is a collection of miscellaneous key/values. Extra map[string]interface{} `json:"-"` // ID is the unique ID of the user. ID string `json:"id"` // Links contains referencing links to the user. Links map[string]interface{} `json:"links"` // Name is the name of the user. Name string `json:"name"` // Options are a set of defined options of the user. Options map[string]interface{} `json:"options"` // PasswordExpiresAt is the timestamp when the user's password expires. PasswordExpiresAt time.Time `json:"-"` } func (r *User) UnmarshalJSON(b []byte) error { type tmp User var s struct { tmp Extra map[string]interface{} `json:"extra"` PasswordExpiresAt gophercloud.JSONRFC3339MilliNoZ `json:"password_expires_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = User(s.tmp) r.PasswordExpiresAt = time.Time(s.PasswordExpiresAt) // Collect other fields and bundle them into Extra // but only if a field titled "extra" wasn't sent. if s.Extra != nil { r.Extra = s.Extra } else { var result interface{} err := json.Unmarshal(b, &result) if err != nil { return err } if resultMap, ok := result.(map[string]interface{}); ok { delete(resultMap, "password_expires_at") r.Extra = internal.RemainingKeys(User{}, resultMap) } } return err } type userResult struct { gophercloud.Result } // GetResult is the response from a Get operation. Call its Extract method // to interpret it as a User. type GetResult struct { userResult } // CreateResult is the response from a Create operation. Call its Extract method // to interpret it as a User. type CreateResult struct { userResult } // UpdateResult is the response from an Update operation. Call its Extract // method to interpret it as a User. type UpdateResult struct { userResult } // ChangePasswordResult is the response from a ChangePassword operation. Call its // ExtractErr method to determine if the request succeeded or failed. type ChangePasswordResult struct { gophercloud.ErrResult } // DeleteResult is the response from a Delete operation. Call its ExtractErr to // determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // AddToGroupResult is the response from a AddToGroup operation. Call its // ExtractErr method to determine if the request succeeded or failed. type AddToGroupResult struct { gophercloud.ErrResult } // IsMemberOfGroupResult is the response from a IsMemberOfGroup operation. Call its // Extract method to determine if the request succeeded or failed. type IsMemberOfGroupResult struct { isMember bool gophercloud.Result } // RemoveFromGroupResult is the response from a RemoveFromGroup operation. Call its // ExtractErr method to determine if the request succeeded or failed. type RemoveFromGroupResult struct { gophercloud.ErrResult } // UserPage is a single page of User results. type UserPage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a UserPage contains any results. func (r UserPage) IsEmpty() (bool, error) { users, err := ExtractUsers(r) return len(users) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (r UserPage) NextPageURL() (string, error) { var s struct { Links struct { Next string `json:"next"` Previous string `json:"previous"` } `json:"links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return s.Links.Next, err } // ExtractUsers returns a slice of Users contained in a single page of results. func ExtractUsers(r pagination.Page) ([]User, error) { var s struct { Users []User `json:"users"` } err := (r.(UserPage)).ExtractInto(&s) return s.Users, err } // Extract interprets any user results as a User. func (r userResult) Extract() (*User, error) { var s struct { User *User `json:"user"` } err := r.ExtractInto(&s) return s.User, err } // Extract extracts IsMemberOfGroupResult as bool and error values func (r IsMemberOfGroupResult) Extract() (bool, error) { return r.isMember, r.Err } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/users/testing/000077500000000000000000000000001335005366400325425ustar00rootroot00000000000000fixtures.go000066400000000000000000000374731335005366400347010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/users/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" "github.com/gophercloud/gophercloud/openstack/identity/v3/users" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListOutput provides a single page of User results. const ListOutput = ` { "links": { "next": null, "previous": null, "self": "http://example.com/identity/v3/users" }, "users": [ { "domain_id": "default", "enabled": true, "id": "2844b2a08be147a08ef58317d6471f1f", "links": { "self": "http://example.com/identity/v3/users/2844b2a08be147a08ef58317d6471f1f" }, "name": "glance", "password_expires_at": null, "description": "some description", "extra": { "email": "glance@localhost" } }, { "default_project_id": "263fd9", "domain_id": "1789d1", "enabled": true, "id": "9fe1d3", "links": { "self": "https://example.com/identity/v3/users/9fe1d3" }, "name": "jsmith", "password_expires_at": "2016-11-06T15:32:17.000000", "email": "jsmith@example.com", "options": { "ignore_password_expiry": true, "multi_factor_auth_rules": [["password", "totp"], ["password", "custom-auth-method"]] } } ] } ` // GetOutput provides a Get result. const GetOutput = ` { "user": { "default_project_id": "263fd9", "domain_id": "1789d1", "enabled": true, "id": "9fe1d3", "links": { "self": "https://example.com/identity/v3/users/9fe1d3" }, "name": "jsmith", "password_expires_at": "2016-11-06T15:32:17.000000", "email": "jsmith@example.com", "options": { "ignore_password_expiry": true, "multi_factor_auth_rules": [["password", "totp"], ["password", "custom-auth-method"]] } } } ` // GetOutputNoOptions provides a Get result of a user with no options. const GetOutputNoOptions = ` { "user": { "default_project_id": "263fd9", "domain_id": "1789d1", "enabled": true, "id": "9fe1d3", "links": { "self": "https://example.com/identity/v3/users/9fe1d3" }, "name": "jsmith", "password_expires_at": "2016-11-06T15:32:17.000000", "email": "jsmith@example.com" } } ` // CreateRequest provides the input to a Create request. const CreateRequest = ` { "user": { "default_project_id": "263fd9", "domain_id": "1789d1", "enabled": true, "name": "jsmith", "password": "secretsecret", "email": "jsmith@example.com", "options": { "ignore_password_expiry": true, "multi_factor_auth_rules": [["password", "totp"], ["password", "custom-auth-method"]] } } } ` // CreateNoOptionsRequest provides the input to a Create request with no Options. const CreateNoOptionsRequest = ` { "user": { "default_project_id": "263fd9", "domain_id": "1789d1", "enabled": true, "name": "jsmith", "password": "secretsecret", "email": "jsmith@example.com" } } ` // UpdateRequest provides the input to an Update request. const UpdateRequest = ` { "user": { "enabled": false, "disabled_reason": "DDOS", "options": { "multi_factor_auth_rules": null } } } ` // UpdateOutput provides an update result. const UpdateOutput = ` { "user": { "default_project_id": "263fd9", "domain_id": "1789d1", "enabled": false, "id": "9fe1d3", "links": { "self": "https://example.com/identity/v3/users/9fe1d3" }, "name": "jsmith", "password_expires_at": "2016-11-06T15:32:17.000000", "email": "jsmith@example.com", "disabled_reason": "DDOS", "options": { "ignore_password_expiry": true } } } ` // ChangePasswordRequest provides the input to a ChangePassword request. const ChangePasswordRequest = ` { "user": { "password": "new_secretsecret", "original_password": "secretsecret" } } ` // ListGroupsOutput provides a ListGroups result. const ListGroupsOutput = ` { "groups": [ { "description": "Developers cleared for work on all general projects", "domain_id": "1789d1", "id": "ea167b", "links": { "self": "https://example.com/identity/v3/groups/ea167b" }, "building": "Hilltop A", "name": "Developers" }, { "description": "Developers cleared for work on secret projects", "domain_id": "1789d1", "id": "a62db1", "links": { "self": "https://example.com/identity/v3/groups/a62db1" }, "name": "Secure Developers" } ], "links": { "self": "http://example.com/identity/v3/users/9fe1d3/groups", "previous": null, "next": null } } ` // ListProjectsOutput provides a ListProjects result. const ListProjectsOutput = ` { "links": { "next": null, "previous": null, "self": "http://localhost:5000/identity/v3/users/foobar/projects" }, "projects": [ { "description": "my first project", "domain_id": "11111", "enabled": true, "id": "abcde", "links": { "self": "http://localhost:5000/identity/v3/projects/abcde" }, "name": "project 1", "parent_id": "11111" }, { "description": "my second project", "domain_id": "22222", "enabled": true, "id": "bcdef", "links": { "self": "http://localhost:5000/identity/v3/projects/bcdef" }, "name": "project 2", "parent_id": "22222" } ] } ` // FirstUser is the first user in the List request. var nilTime time.Time var FirstUser = users.User{ DomainID: "default", Enabled: true, ID: "2844b2a08be147a08ef58317d6471f1f", Links: map[string]interface{}{ "self": "http://example.com/identity/v3/users/2844b2a08be147a08ef58317d6471f1f", }, Name: "glance", PasswordExpiresAt: nilTime, Description: "some description", Extra: map[string]interface{}{ "email": "glance@localhost", }, } // SecondUser is the second user in the List request. var SecondUserPasswordExpiresAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2016-11-06T15:32:17.000000") var SecondUser = users.User{ DefaultProjectID: "263fd9", DomainID: "1789d1", Enabled: true, ID: "9fe1d3", Links: map[string]interface{}{ "self": "https://example.com/identity/v3/users/9fe1d3", }, Name: "jsmith", PasswordExpiresAt: SecondUserPasswordExpiresAt, Extra: map[string]interface{}{ "email": "jsmith@example.com", }, Options: map[string]interface{}{ "ignore_password_expiry": true, "multi_factor_auth_rules": []interface{}{ []string{"password", "totp"}, []string{"password", "custom-auth-method"}, }, }, } var SecondUserNoOptions = users.User{ DefaultProjectID: "263fd9", DomainID: "1789d1", Enabled: true, ID: "9fe1d3", Links: map[string]interface{}{ "self": "https://example.com/identity/v3/users/9fe1d3", }, Name: "jsmith", PasswordExpiresAt: SecondUserPasswordExpiresAt, Extra: map[string]interface{}{ "email": "jsmith@example.com", }, } // SecondUserUpdated is how SecondUser should look after an Update. var SecondUserUpdated = users.User{ DefaultProjectID: "263fd9", DomainID: "1789d1", Enabled: false, ID: "9fe1d3", Links: map[string]interface{}{ "self": "https://example.com/identity/v3/users/9fe1d3", }, Name: "jsmith", PasswordExpiresAt: SecondUserPasswordExpiresAt, Extra: map[string]interface{}{ "email": "jsmith@example.com", "disabled_reason": "DDOS", }, Options: map[string]interface{}{ "ignore_password_expiry": true, }, } // ExpectedUsersSlice is the slice of users expected to be returned from ListOutput. var ExpectedUsersSlice = []users.User{FirstUser, SecondUser} var FirstGroup = groups.Group{ Description: "Developers cleared for work on all general projects", DomainID: "1789d1", ID: "ea167b", Links: map[string]interface{}{ "self": "https://example.com/identity/v3/groups/ea167b", }, Extra: map[string]interface{}{ "building": "Hilltop A", }, Name: "Developers", } var SecondGroup = groups.Group{ Description: "Developers cleared for work on secret projects", DomainID: "1789d1", ID: "a62db1", Links: map[string]interface{}{ "self": "https://example.com/identity/v3/groups/a62db1", }, Extra: map[string]interface{}{}, Name: "Secure Developers", } var ExpectedGroupsSlice = []groups.Group{FirstGroup, SecondGroup} var FirstProject = projects.Project{ Description: "my first project", DomainID: "11111", Enabled: true, ID: "abcde", Name: "project 1", ParentID: "11111", } var SecondProject = projects.Project{ Description: "my second project", DomainID: "22222", Enabled: true, ID: "bcdef", Name: "project 2", ParentID: "22222", } var ExpectedProjectsSlice = []projects.Project{FirstProject, SecondProject} // HandleListUsersSuccessfully creates an HTTP handler at `/users` on the // test handler mux that responds with a list of two users. func HandleListUsersSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListOutput) }) } // HandleGetUserSuccessfully creates an HTTP handler at `/users` on the // test handler mux that responds with a single user. func HandleGetUserSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetOutput) }) } // HandleCreateUserSuccessfully creates an HTTP handler at `/users` on the // test handler mux that tests user creation. func HandleCreateUserSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, GetOutput) }) } // HandleCreateNoOptionsUserSuccessfully creates an HTTP handler at `/users` on the // test handler mux that tests user creation. func HandleCreateNoOptionsUserSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, CreateNoOptionsRequest) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, GetOutputNoOptions) }) } // HandleUpdateUserSuccessfully creates an HTTP handler at `/users` on the // test handler mux that tests user update. func HandleUpdateUserSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, UpdateOutput) }) } // HandleChangeUserPasswordSuccessfully creates an HTTP handler at `/users` on the // test handler mux that tests change user password. func HandleChangeUserPasswordSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users/9fe1d3/password", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, ChangePasswordRequest) w.WriteHeader(http.StatusNoContent) }) } // HandleDeleteUserSuccessfully creates an HTTP handler at `/users` on the // test handler mux that tests user deletion. func HandleDeleteUserSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleListUserGroupsSuccessfully creates an HTTP handler at /users/{userID}/groups // on the test handler mux that respons with a list of two groups func HandleListUserGroupsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users/9fe1d3/groups", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListGroupsOutput) }) } // HandleAddToGroupSuccessfully creates an HTTP handler at /groups/{groupID}/users/{userID} // on the test handler mux that tests adding user to group. func HandleAddToGroupSuccessfully(t *testing.T) { th.Mux.HandleFunc("/groups/ea167b/users/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleIsMemberOfGroupSuccessfully creates an HTTP handler at /groups/{groupID}/users/{userID} // on the test handler mux that tests checking whether user belongs to group. func HandleIsMemberOfGroupSuccessfully(t *testing.T) { th.Mux.HandleFunc("/groups/ea167b/users/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "HEAD") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleRemoveFromGroupSuccessfully creates an HTTP handler at /groups/{groupID}/users/{userID} // on the test handler mux that tests removing user from group. func HandleRemoveFromGroupSuccessfully(t *testing.T) { th.Mux.HandleFunc("/groups/ea167b/users/9fe1d3", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleListUserProjectsSuccessfully creates an HTTP handler at /users/{userID}/projects // on the test handler mux that respons wit a list of two projects func HandleListUserProjectsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users/9fe1d3/projects", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListProjectsOutput) }) } // HandleListInGroupSuccessfully creates an HTTP handler at /groups/{groupID}/users // on the test handler mux that response with a list of two users func HandleListInGroupSuccessfully(t *testing.T) { th.Mux.HandleFunc("/groups/ea167b/users", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListOutput) }) } requests_test.go000066400000000000000000000145421335005366400357320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/users/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" "github.com/gophercloud/gophercloud/openstack/identity/v3/users" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListUsers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListUsersSuccessfully(t) count := 0 err := users.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := users.ExtractUsers(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedUsersSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListUsersAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListUsersSuccessfully(t) allPages, err := users.List(client.ServiceClient(), nil).AllPages() th.AssertNoErr(t, err) actual, err := users.ExtractUsers(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedUsersSlice, actual) th.AssertEquals(t, ExpectedUsersSlice[0].Extra["email"], "glance@localhost") th.AssertEquals(t, ExpectedUsersSlice[1].Extra["email"], "jsmith@example.com") } func TestListUsersFiltersCheck(t *testing.T) { type test struct { filterName string wantErr bool } tests := []test{ {"foo__contains", false}, {"foo", true}, {"foo_contains", true}, {"foo__", true}, {"__foo", true}, } var listOpts users.ListOpts for _, _test := range tests { listOpts.Filters = map[string]string{_test.filterName: "bar"} _, err := listOpts.ToUserListQuery() if !_test.wantErr { th.AssertNoErr(t, err) } else { switch _t := err.(type) { case nil: t.Fatal("error expected but got a nil") case users.InvalidListFilter: default: t.Fatalf("unexpected error type: [%T]", _t) } } } } func TestGetUser(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetUserSuccessfully(t) actual, err := users.Get(client.ServiceClient(), "9fe1d3").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondUser, *actual) th.AssertEquals(t, SecondUser.Extra["email"], "jsmith@example.com") } func TestCreateUser(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateUserSuccessfully(t) iTrue := true createOpts := users.CreateOpts{ Name: "jsmith", DomainID: "1789d1", Enabled: &iTrue, Password: "secretsecret", DefaultProjectID: "263fd9", Options: map[users.Option]interface{}{ users.IgnorePasswordExpiry: true, users.MultiFactorAuthRules: []interface{}{ []string{"password", "totp"}, []string{"password", "custom-auth-method"}, }, }, Extra: map[string]interface{}{ "email": "jsmith@example.com", }, } actual, err := users.Create(client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondUser, *actual) } func TestCreateNoOptionsUser(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateNoOptionsUserSuccessfully(t) iTrue := true createOpts := users.CreateOpts{ Name: "jsmith", DomainID: "1789d1", Enabled: &iTrue, Password: "secretsecret", DefaultProjectID: "263fd9", Extra: map[string]interface{}{ "email": "jsmith@example.com", }, } actual, err := users.Create(client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondUserNoOptions, *actual) } func TestUpdateUser(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateUserSuccessfully(t) iFalse := false updateOpts := users.UpdateOpts{ Enabled: &iFalse, Options: map[users.Option]interface{}{ users.MultiFactorAuthRules: nil, }, Extra: map[string]interface{}{ "disabled_reason": "DDOS", }, } actual, err := users.Update(client.ServiceClient(), "9fe1d3", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondUserUpdated, *actual) } func TestChangeUserPassword(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleChangeUserPasswordSuccessfully(t) changePasswordOpts := users.ChangePasswordOpts{ OriginalPassword: "secretsecret", Password: "new_secretsecret", } res := users.ChangePassword(client.ServiceClient(), "9fe1d3", changePasswordOpts) th.AssertNoErr(t, res.Err) } func TestDeleteUser(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteUserSuccessfully(t) res := users.Delete(client.ServiceClient(), "9fe1d3") th.AssertNoErr(t, res.Err) } func TestListUserGroups(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListUserGroupsSuccessfully(t) allPages, err := users.ListGroups(client.ServiceClient(), "9fe1d3").AllPages() th.AssertNoErr(t, err) actual, err := groups.ExtractGroups(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedGroupsSlice, actual) } func TestAddToGroup(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAddToGroupSuccessfully(t) res := users.AddToGroup(client.ServiceClient(), "ea167b", "9fe1d3") th.AssertNoErr(t, res.Err) } func TestIsMemberOfGroup(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleIsMemberOfGroupSuccessfully(t) ok, err := users.IsMemberOfGroup(client.ServiceClient(), "ea167b", "9fe1d3").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, true, ok) } func TestRemoveFromGroup(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleRemoveFromGroupSuccessfully(t) res := users.RemoveFromGroup(client.ServiceClient(), "ea167b", "9fe1d3") th.AssertNoErr(t, res.Err) } func TestListUserProjects(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListUserProjectsSuccessfully(t) allPages, err := users.ListProjects(client.ServiceClient(), "9fe1d3").AllPages() th.AssertNoErr(t, err) actual, err := projects.ExtractProjects(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedProjectsSlice, actual) } func TestListInGroup(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListInGroupSuccessfully(t) iTrue := true listOpts := users.ListOpts{ Enabled: &iTrue, } allPages, err := users.ListInGroup(client.ServiceClient(), "ea167b", listOpts).AllPages() th.AssertNoErr(t, err) actual, err := users.ExtractUsers(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedUsersSlice, actual) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/identity/v3/users/urls.go000066400000000000000000000031271335005366400324040ustar00rootroot00000000000000package users import "github.com/gophercloud/gophercloud" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("users") } func getURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID) } func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("users") } func updateURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID) } func changePasswordURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID, "password") } func deleteURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID) } func listGroupsURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID, "groups") } func addToGroupURL(client *gophercloud.ServiceClient, groupID, userID string) string { return client.ServiceURL("groups", groupID, "users", userID) } func isMemberOfGroupURL(client *gophercloud.ServiceClient, groupID, userID string) string { return client.ServiceURL("groups", groupID, "users", userID) } func removeFromGroupURL(client *gophercloud.ServiceClient, groupID, userID string) string { return client.ServiceURL("groups", groupID, "users", userID) } func listProjectsURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID, "projects") } func listInGroupURL(client *gophercloud.ServiceClient, groupID string) string { return client.ServiceURL("groups", groupID, "users") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/000077500000000000000000000000001335005366400302065ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/000077500000000000000000000000001335005366400305355ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/README.md000066400000000000000000000002401335005366400320100ustar00rootroot00000000000000This provides a Go API which wraps any service implementing the [OpenStack Image Service API, version 2](http://developer.openstack.org/api-ref-image-v2.html). golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imagedata/000077500000000000000000000000001335005366400324515ustar00rootroot00000000000000doc.go000066400000000000000000000016471335005366400334760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imagedata/* Package imagedata enables management of image data. Example to Upload Image Data imageID := "da3b75d9-3f4a-40e7-8a2c-bfab23927dea" imageData, err := os.Open("/path/to/image/file") if err != nil { panic(err) } defer imageData.Close() err = imagedata.Upload(imageClient, imageID, imageData).ExtractErr() if err != nil { panic(err) } Example to Stage Image Data imageID := "da3b75d9-3f4a-40e7-8a2c-bfab23927dea" imageData, err := os.Open("/path/to/image/file") if err != nil { panic(err) } defer imageData.Close() err = imagedata.Stage(imageClient, imageID, imageData).ExtractErr() if err != nil { panic(err) } Example to Download Image Data imageID := "da3b75d9-3f4a-40e7-8a2c-bfab23927dea" image, err := imagedata.Download(imageClient, imageID).Extract() if err != nil { panic(err) } imageData, err := ioutil.ReadAll(image) if err != nil { panic(err) } */ package imagedata requests.go000066400000000000000000000021721335005366400345760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imagedatapackage imagedata import ( "io" "net/http" "github.com/gophercloud/gophercloud" ) // Upload uploads an image file. func Upload(client *gophercloud.ServiceClient, id string, data io.Reader) (r UploadResult) { _, r.Err = client.Put(uploadURL(client, id), data, nil, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"Content-Type": "application/octet-stream"}, OkCodes: []int{204}, }) return } // Stage performs PUT call on the existing image object in the Imageservice with // the provided file. // Existing image object must be in the "queued" status. func Stage(client *gophercloud.ServiceClient, id string, data io.Reader) (r StageResult) { _, r.Err = client.Put(stageURL(client, id), data, nil, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"Content-Type": "application/octet-stream"}, OkCodes: []int{204}, }) return } // Download retrieves an image. func Download(client *gophercloud.ServiceClient, id string) (r DownloadResult) { var resp *http.Response resp, r.Err = client.Get(downloadURL(client, id), nil, nil) if resp != nil { r.Body = resp.Body r.Header = resp.Header } return } results.go000066400000000000000000000015561335005366400344310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imagedatapackage imagedata import ( "fmt" "io" "github.com/gophercloud/gophercloud" ) // UploadResult is the result of an upload image operation. Call its ExtractErr // method to determine if the request succeeded or failed. type UploadResult struct { gophercloud.ErrResult } // StageResult is the result of a stage image operation. Call its ExtractErr // method to determine if the request succeeded or failed. type StageResult struct { gophercloud.ErrResult } // DownloadResult is the result of a download image operation. Call its Extract // method to gain access to the image data. type DownloadResult struct { gophercloud.Result } // Extract builds images model from io.Reader func (r DownloadResult) Extract() (io.Reader, error) { if r, ok := r.Body.(io.Reader); ok { return r, nil } return nil, fmt.Errorf("Expected io.Reader but got: %T(%#v)", r.Body, r.Body) } testing/000077500000000000000000000000001335005366400340475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imagedatadoc.go000066400000000000000000000000501335005366400351360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imagedata/testing// imagedata unit tests package testing fixtures.go000066400000000000000000000031461335005366400362530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imagedata/testingpackage testing import ( "io/ioutil" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fakeclient "github.com/gophercloud/gophercloud/testhelper/client" ) // HandlePutImageDataSuccessfully setup func HandlePutImageDataSuccessfully(t *testing.T) { th.Mux.HandleFunc("/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/file", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) b, err := ioutil.ReadAll(r.Body) if err != nil { t.Errorf("Unable to read request body: %v", err) } th.AssertByteArrayEquals(t, []byte{5, 3, 7, 24}, b) w.WriteHeader(http.StatusNoContent) }) } // HandleStageImageDataSuccessfully setup func HandleStageImageDataSuccessfully(t *testing.T) { th.Mux.HandleFunc("/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/stage", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) b, err := ioutil.ReadAll(r.Body) if err != nil { t.Errorf("Unable to read request body: %v", err) } th.AssertByteArrayEquals(t, []byte{5, 3, 7, 24}, b) w.WriteHeader(http.StatusNoContent) }) } // HandleGetImageDataSuccessfully setup func HandleGetImageDataSuccessfully(t *testing.T) { th.Mux.HandleFunc("/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/file", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) w.WriteHeader(http.StatusOK) _, err := w.Write([]byte{34, 87, 0, 23, 23, 23, 56, 255, 254, 0}) th.AssertNoErr(t, err) }) } requests_test.go000066400000000000000000000041161335005366400373120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imagedata/testingpackage testing import ( "fmt" "io" "io/ioutil" "testing" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata" th "github.com/gophercloud/gophercloud/testhelper" fakeclient "github.com/gophercloud/gophercloud/testhelper/client" ) func TestUpload(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePutImageDataSuccessfully(t) err := imagedata.Upload( fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", readSeekerOfBytes([]byte{5, 3, 7, 24})).ExtractErr() th.AssertNoErr(t, err) } func TestStage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleStageImageDataSuccessfully(t) err := imagedata.Stage( fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", readSeekerOfBytes([]byte{5, 3, 7, 24})).ExtractErr() th.AssertNoErr(t, err) } func readSeekerOfBytes(bs []byte) io.ReadSeeker { return &RS{bs: bs} } // implements io.ReadSeeker type RS struct { bs []byte offset int } func (rs *RS) Read(p []byte) (int, error) { leftToRead := len(rs.bs) - rs.offset if 0 < leftToRead { bytesToWrite := min(leftToRead, len(p)) for i := 0; i < bytesToWrite; i++ { p[i] = rs.bs[rs.offset] rs.offset++ } return bytesToWrite, nil } return 0, io.EOF } func min(a int, b int) int { if a < b { return a } return b } func (rs *RS) Seek(offset int64, whence int) (int64, error) { var offsetInt = int(offset) if whence == 0 { rs.offset = offsetInt } else if whence == 1 { rs.offset = rs.offset + offsetInt } else if whence == 2 { rs.offset = len(rs.bs) - offsetInt } else { return 0, fmt.Errorf("For parameter `whence`, expected value in {0,1,2} but got: %#v", whence) } return int64(rs.offset), nil } func TestDownload(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetImageDataSuccessfully(t) rdr, err := imagedata.Download(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea").Extract() th.AssertNoErr(t, err) bs, err := ioutil.ReadAll(rdr) th.AssertNoErr(t, err) th.AssertByteArrayEquals(t, []byte{34, 87, 0, 23, 23, 23, 56, 255, 254, 0}, bs) } urls.go000066400000000000000000000011431335005366400337050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imagedatapackage imagedata import "github.com/gophercloud/gophercloud" const ( rootPath = "images" uploadPath = "file" stagePath = "stage" ) // `imageDataURL(c,i)` is the URL for the binary image data for the // image identified by ID `i` in the service `c`. func uploadURL(c *gophercloud.ServiceClient, imageID string) string { return c.ServiceURL(rootPath, imageID, uploadPath) } func stageURL(c *gophercloud.ServiceClient, imageID string) string { return c.ServiceURL(rootPath, imageID, stagePath) } func downloadURL(c *gophercloud.ServiceClient, imageID string) string { return uploadURL(c, imageID) } imageimport/000077500000000000000000000000001335005366400327735ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2doc.go000066400000000000000000000005001335005366400340620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imageimport/* Package imageimport enables management of images import and retrieval of the Imageservice Import API information. Example to Get an information about the Import API importInfo, err := imageimport.Get(imagesClient).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", importInfo) */ package imageimport requests.go000066400000000000000000000010371335005366400351760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imageimportpackage imageimport import "github.com/gophercloud/gophercloud" // ImportMethod represents valid Import API method. type ImportMethod string const ( // GlanceDirectMethod represents glance-direct Import API method. GlanceDirectMethod ImportMethod = "glance-direct" // WebDownloadMethod represents web-download Import API method. WebDownloadMethod ImportMethod = "web-download" ) // Get retrieves Import API information data. func Get(c *gophercloud.ServiceClient) (r GetResult) { _, r.Err = c.Get(infoURL(c), &r.Body, nil) return } results.go000066400000000000000000000014671335005366400350330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imageimportpackage imageimport import "github.com/gophercloud/gophercloud" type commonResult struct { gophercloud.Result } // GetResult represents the result of a get operation. Call its Extract method // to interpret it as ImportInfo. type GetResult struct { commonResult } // ImportInfo represents information data for the Import API. type ImportInfo struct { ImportMethods ImportMethods `json:"import-methods"` } // ImportMethods contains information about available Import API methods. type ImportMethods struct { Description string `json:"description"` Type string `json:"type"` Value []string `json:"value"` } // Extract is a function that accepts a result and extracts ImportInfo. func (r commonResult) Extract() (*ImportInfo, error) { var s *ImportInfo err := r.ExtractInto(&s) return s, err } testing/000077500000000000000000000000001335005366400344505ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imageimportdoc.go000066400000000000000000000000201335005366400355340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imageimport/testingpackage testing fixtures.go000066400000000000000000000004651335005366400366550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imageimport/testingpackage testing // ImportGetResult represents raw server response on a Get request. const ImportGetResult = ` { "import-methods": { "description": "Import methods available.", "type": "array", "value": [ "glance-direct", "web-download" ] } } ` requests_test.go000066400000000000000000000020041335005366400377050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imageimport/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport" th "github.com/gophercloud/gophercloud/testhelper" fakeclient "github.com/gophercloud/gophercloud/testhelper/client" ) func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/info/import", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ImportGetResult) }) validImportMethods := []string{ string(imageimport.GlanceDirectMethod), string(imageimport.WebDownloadMethod), } s, err := imageimport.Get(fakeclient.ServiceClient()).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.ImportMethods.Description, "Import methods available.") th.AssertEquals(t, s.ImportMethods.Type, "array") th.AssertDeepEquals(t, s.ImportMethods.Value, validImportMethods) } urls.go000066400000000000000000000003401335005366400343040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imageimportpackage imageimport import "github.com/gophercloud/gophercloud" const ( infoPath = "info" resourcePath = "import" ) func infoURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(infoPath, resourcePath) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/images/000077500000000000000000000000001335005366400320025ustar00rootroot00000000000000doc.go000066400000000000000000000021551335005366400330220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/images/* Package images enables management and retrieval of images from the OpenStack Image Service. Example to List Images images.ListOpts{ Owner: "a7509e1ae65945fda83f3e52c6296017", } allPages, err := images.List(imagesClient, listOpts).AllPages() if err != nil { panic(err) } allImages, err := images.ExtractImages(allPages) if err != nil { panic(err) } for _, image := range allImages { fmt.Printf("%+v\n", image) } Example to Create an Image createOpts := images.CreateOpts{ Name: "image_name", Visibility: images.ImageVisibilityPrivate, } image, err := images.Create(imageClient, createOpts) if err != nil { panic(err) } Example to Update an Image imageID := "1bea47ed-f6a9-463b-b423-14b9cca9ad27" updateOpts := images.UpdateOpts{ images.ReplaceImageName{ NewName: "new_name", }, } image, err := images.Update(imageClient, imageID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete an Image imageID := "1bea47ed-f6a9-463b-b423-14b9cca9ad27" err := images.Delete(imageClient, imageID).ExtractErr() if err != nil { panic(err) } */ package images requests.go000066400000000000000000000235521335005366400341340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imagespackage images import ( "fmt" "net/url" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToImageListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the server attributes you want to see returned. Marker and Limit are used // for pagination. // // http://developer.openstack.org/api-ref-image-v2.html type ListOpts struct { // ID is the ID of the image. // Multiple IDs can be specified by constructing a string // such as "in:uuid1,uuid2,uuid3". ID string `q:"id"` // Integer value for the limit of values to return. Limit int `q:"limit"` // UUID of the server at which you want to set a marker. Marker string `q:"marker"` // Name filters on the name of the image. // Multiple names can be specified by constructing a string // such as "in:name1,name2,name3". Name string `q:"name"` // Visibility filters on the visibility of the image. Visibility ImageVisibility `q:"visibility"` // MemberStatus filters on the member status of the image. MemberStatus ImageMemberStatus `q:"member_status"` // Owner filters on the project ID of the image. Owner string `q:"owner"` // Status filters on the status of the image. // Multiple statuses can be specified by constructing a string // such as "in:saving,queued". Status ImageStatus `q:"status"` // SizeMin filters on the size_min image property. SizeMin int64 `q:"size_min"` // SizeMax filters on the size_max image property. SizeMax int64 `q:"size_max"` // Sort sorts the results using the new style of sorting. See the OpenStack // Image API reference for the exact syntax. // // Sort cannot be used with the classic sort options (sort_key and sort_dir). Sort string `q:"sort"` // SortKey will sort the results based on a specified image property. SortKey string `q:"sort_key"` // SortDir will sort the list results either ascending or decending. SortDir string `q:"sort_dir"` // Tags filters on specific image tags. Tags []string `q:"tag"` // CreatedAtQuery filters images based on their creation date. CreatedAtQuery *ImageDateQuery // UpdatedAtQuery filters images based on their updated date. UpdatedAtQuery *ImageDateQuery // ContainerFormat filters images based on the container_format. // Multiple container formats can be specified by constructing a // string such as "in:bare,ami". ContainerFormat string `q:"container_format"` // DiskFormat filters images based on the disk_format. // Multiple disk formats can be specified by constructing a string // such as "in:qcow2,iso". DiskFormat string `q:"disk_format"` } // ToImageListQuery formats a ListOpts into a query string. func (opts ListOpts) ToImageListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) params := q.Query() if opts.CreatedAtQuery != nil { createdAt := opts.CreatedAtQuery.Date.Format(time.RFC3339) if v := opts.CreatedAtQuery.Filter; v != "" { createdAt = fmt.Sprintf("%s:%s", v, createdAt) } params.Add("created_at", createdAt) } if opts.UpdatedAtQuery != nil { updatedAt := opts.UpdatedAtQuery.Date.Format(time.RFC3339) if v := opts.UpdatedAtQuery.Filter; v != "" { updatedAt = fmt.Sprintf("%s:%s", v, updatedAt) } params.Add("updated_at", updatedAt) } q = &url.URL{RawQuery: params.Encode()} return q.String(), err } // List implements image list request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(c) if opts != nil { query, err := opts.ToImageListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { imagePage := ImagePage{ serviceURL: c.ServiceURL(), LinkedPageBase: pagination.LinkedPageBase{PageResult: r}, } return imagePage }) } // CreateOptsBuilder allows extensions to add parameters to the Create request. type CreateOptsBuilder interface { // Returns value that can be passed to json.Marshal ToImageCreateMap() (map[string]interface{}, error) } // CreateOpts represents options used to create an image. type CreateOpts struct { // Name is the name of the new image. Name string `json:"name" required:"true"` // Id is the the image ID. ID string `json:"id,omitempty"` // Visibility defines who can see/use the image. Visibility *ImageVisibility `json:"visibility,omitempty"` // Tags is a set of image tags. Tags []string `json:"tags,omitempty"` // ContainerFormat is the format of the // container. Valid values are ami, ari, aki, bare, and ovf. ContainerFormat string `json:"container_format,omitempty"` // DiskFormat is the format of the disk. If set, // valid values are ami, ari, aki, vhd, vmdk, raw, qcow2, vdi, // and iso. DiskFormat string `json:"disk_format,omitempty"` // MinDisk is the amount of disk space in // GB that is required to boot the image. MinDisk int `json:"min_disk,omitempty"` // MinRAM is the amount of RAM in MB that // is required to boot the image. MinRAM int `json:"min_ram,omitempty"` // protected is whether the image is not deletable. Protected *bool `json:"protected,omitempty"` // properties is a set of properties, if any, that // are associated with the image. Properties map[string]string `json:"-"` } // ToImageCreateMap assembles a request body based on the contents of // a CreateOpts. func (opts CreateOpts) ToImageCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } if opts.Properties != nil { for k, v := range opts.Properties { b[k] = v } } return b, nil } // Create implements create image request. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToImageCreateMap() if err != nil { r.Err = err return r } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{201}}) return } // Delete implements image delete request. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // Get implements image get request. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // Update implements image updated request. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToImageUpdateMap() if err != nil { r.Err = err return r } _, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, MoreHeaders: map[string]string{"Content-Type": "application/openstack-images-v2.1-json-patch"}, }) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { // returns value implementing json.Marshaler which when marshaled matches // the patch schema: // http://specs.openstack.org/openstack/glance-specs/specs/api/v2/http-patch-image-api-v2.html ToImageUpdateMap() ([]interface{}, error) } // UpdateOpts implements UpdateOpts type UpdateOpts []Patch // ToImageUpdateMap assembles a request body based on the contents of // UpdateOpts. func (opts UpdateOpts) ToImageUpdateMap() ([]interface{}, error) { m := make([]interface{}, len(opts)) for i, patch := range opts { patchJSON := patch.ToImagePatchMap() m[i] = patchJSON } return m, nil } // Patch represents a single update to an existing image. Multiple updates // to an image can be submitted at the same time. type Patch interface { ToImagePatchMap() map[string]interface{} } // UpdateVisibility represents an updated visibility property request. type UpdateVisibility struct { Visibility ImageVisibility } // ToImagePatchMap assembles a request body based on UpdateVisibility. func (r UpdateVisibility) ToImagePatchMap() map[string]interface{} { return map[string]interface{}{ "op": "replace", "path": "/visibility", "value": r.Visibility, } } // ReplaceImageName represents an updated image_name property request. type ReplaceImageName struct { NewName string } // ToImagePatchMap assembles a request body based on ReplaceImageName. func (r ReplaceImageName) ToImagePatchMap() map[string]interface{} { return map[string]interface{}{ "op": "replace", "path": "/name", "value": r.NewName, } } // ReplaceImageChecksum represents an updated checksum property request. type ReplaceImageChecksum struct { Checksum string } // ReplaceImageChecksum assembles a request body based on ReplaceImageChecksum. func (r ReplaceImageChecksum) ToImagePatchMap() map[string]interface{} { return map[string]interface{}{ "op": "replace", "path": "/checksum", "value": r.Checksum, } } // ReplaceImageTags represents an updated tags property request. type ReplaceImageTags struct { NewTags []string } // ToImagePatchMap assembles a request body based on ReplaceImageTags. func (r ReplaceImageTags) ToImagePatchMap() map[string]interface{} { return map[string]interface{}{ "op": "replace", "path": "/tags", "value": r.NewTags, } } // UpdateOp represents a valid update operation. type UpdateOp string const ( AddOp UpdateOp = "add" ReplaceOp UpdateOp = "replace" RemoveOp UpdateOp = "remove" ) // UpdateImageProperty represents an update property request. type UpdateImageProperty struct { Op UpdateOp Name string Value string } // ToImagePatchMap assembles a request body based on UpdateImageProperty. func (r UpdateImageProperty) ToImagePatchMap() map[string]interface{} { updateMap := map[string]interface{}{ "op": r.Op, "path": fmt.Sprintf("/%s", r.Name), } if r.Value != "" { updateMap["value"] = r.Value } return updateMap } results.go000066400000000000000000000123701335005366400337560ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imagespackage images import ( "encoding/json" "fmt" "reflect" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) // Image represents an image found in the OpenStack Image service. type Image struct { // ID is the image UUID. ID string `json:"id"` // Name is the human-readable display name for the image. Name string `json:"name"` // Status is the image status. It can be "queued" or "active" // See imageservice/v2/images/type.go Status ImageStatus `json:"status"` // Tags is a list of image tags. Tags are arbitrarily defined strings // attached to an image. Tags []string `json:"tags"` // ContainerFormat is the format of the container. // Valid values are ami, ari, aki, bare, and ovf. ContainerFormat string `json:"container_format"` // DiskFormat is the format of the disk. // If set, valid values are ami, ari, aki, vhd, vmdk, raw, qcow2, vdi, // and iso. DiskFormat string `json:"disk_format"` // MinDiskGigabytes is the amount of disk space in GB that is required to // boot the image. MinDiskGigabytes int `json:"min_disk"` // MinRAMMegabytes [optional] is the amount of RAM in MB that is required to // boot the image. MinRAMMegabytes int `json:"min_ram"` // Owner is the tenant ID the image belongs to. Owner string `json:"owner"` // Protected is whether the image is deletable or not. Protected bool `json:"protected"` // Visibility defines who can see/use the image. Visibility ImageVisibility `json:"visibility"` // Checksum is the checksum of the data that's associated with the image. Checksum string `json:"checksum"` // SizeBytes is the size of the data that's associated with the image. SizeBytes int64 `json:"-"` // Metadata is a set of metadata associated with the image. // Image metadata allow for meaningfully define the image properties // and tags. // See http://docs.openstack.org/developer/glance/metadefs-concepts.html. Metadata map[string]string `json:"metadata"` // Properties is a set of key-value pairs, if any, that are associated with // the image. Properties map[string]interface{} // CreatedAt is the date when the image has been created. CreatedAt time.Time `json:"created_at"` // UpdatedAt is the date when the last change has been made to the image or // it's properties. UpdatedAt time.Time `json:"updated_at"` // File is the trailing path after the glance endpoint that represent the // location of the image or the path to retrieve it. File string `json:"file"` // Schema is the path to the JSON-schema that represent the image or image // entity. Schema string `json:"schema"` // VirtualSize is the virtual size of the image VirtualSize int64 `json:"virtual_size"` } func (r *Image) UnmarshalJSON(b []byte) error { type tmp Image var s struct { tmp SizeBytes interface{} `json:"size"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Image(s.tmp) switch t := s.SizeBytes.(type) { case nil: r.SizeBytes = 0 case float32: r.SizeBytes = int64(t) case float64: r.SizeBytes = int64(t) default: return fmt.Errorf("Unknown type for SizeBytes: %v (value: %v)", reflect.TypeOf(t), t) } // Bundle all other fields into Properties var result interface{} err = json.Unmarshal(b, &result) if err != nil { return err } if resultMap, ok := result.(map[string]interface{}); ok { delete(resultMap, "self") delete(resultMap, "size") r.Properties = internal.RemainingKeys(Image{}, resultMap) } return err } type commonResult struct { gophercloud.Result } // Extract interprets any commonResult as an Image. func (r commonResult) Extract() (*Image, error) { var s *Image err := r.ExtractInto(&s) return s, err } // CreateResult represents the result of a Create operation. Call its Extract // method to interpret it as an Image. type CreateResult struct { commonResult } // UpdateResult represents the result of an Update operation. Call its Extract // method to interpret it as an Image. type UpdateResult struct { commonResult } // GetResult represents the result of a Get operation. Call its Extract // method to interpret it as an Image. type GetResult struct { commonResult } // DeleteResult represents the result of a Delete operation. Call its // ExtractErr method to interpret it as an Image. type DeleteResult struct { gophercloud.ErrResult } // ImagePage represents the results of a List request. type ImagePage struct { serviceURL string pagination.LinkedPageBase } // IsEmpty returns true if an ImagePage contains no Images results. func (r ImagePage) IsEmpty() (bool, error) { images, err := ExtractImages(r) return len(images) == 0, err } // NextPageURL uses the response's embedded link reference to navigate to // the next page of results. func (r ImagePage) NextPageURL() (string, error) { var s struct { Next string `json:"next"` } err := r.ExtractInto(&s) if err != nil { return "", err } if s.Next == "" { return "", nil } return nextPageURL(r.serviceURL, s.Next) } // ExtractImages interprets the results of a single page from a List() call, // producing a slice of Image entities. func ExtractImages(r pagination.Page) ([]Image, error) { var s struct { Images []Image `json:"images"` } err := (r.(ImagePage)).ExtractInto(&s) return s.Images, err } testing/000077500000000000000000000000001335005366400334005ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imagesdoc.go000066400000000000000000000000451335005366400344730ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/images/testing// images unit tests package testing fixtures.go000066400000000000000000000322311335005366400356010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/images/testingpackage testing import ( "fmt" "net/http" "strconv" "strings" "testing" th "github.com/gophercloud/gophercloud/testhelper" fakeclient "github.com/gophercloud/gophercloud/testhelper/client" ) type imageEntry struct { ID string JSON string } // HandleImageListSuccessfully test setup func HandleImageListSuccessfully(t *testing.T) { images := make([]imageEntry, 3) images[0] = imageEntry{"cirros-0.3.4-x86_64-uec", `{ "status": "active", "name": "cirros-0.3.4-x86_64-uec", "tags": [], "kernel_id": "e1b6edd4-bd9b-40ac-b010-8a6c16de4ba4", "container_format": "ami", "created_at": "2015-07-15T11:43:35Z", "ramdisk_id": "8c64f48a-45a3-4eaa-adff-a8106b6c005b", "disk_format": "ami", "updated_at": "2015-07-15T11:43:35Z", "visibility": "public", "self": "/v2/images/07aa21a9-fa1a-430e-9a33-185be5982431", "min_disk": 0, "protected": false, "id": "07aa21a9-fa1a-430e-9a33-185be5982431", "size": 25165824, "file": "/v2/images/07aa21a9-fa1a-430e-9a33-185be5982431/file", "checksum": "eb9139e4942121f22bbc2afc0400b2a4", "owner": "cba624273b8344e59dd1fd18685183b0", "virtual_size": null, "min_ram": 0, "schema": "/v2/schemas/image", "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi" }`} images[1] = imageEntry{"cirros-0.3.4-x86_64-uec-ramdisk", `{ "status": "active", "name": "cirros-0.3.4-x86_64-uec-ramdisk", "tags": [], "container_format": "ari", "created_at": "2015-07-15T11:43:32Z", "size": 3740163, "disk_format": "ari", "updated_at": "2015-07-15T11:43:32Z", "visibility": "public", "self": "/v2/images/8c64f48a-45a3-4eaa-adff-a8106b6c005b", "min_disk": 0, "protected": false, "id": "8c64f48a-45a3-4eaa-adff-a8106b6c005b", "file": "/v2/images/8c64f48a-45a3-4eaa-adff-a8106b6c005b/file", "checksum": "be575a2b939972276ef675752936977f", "owner": "cba624273b8344e59dd1fd18685183b0", "virtual_size": null, "min_ram": 0, "schema": "/v2/schemas/image", "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi" }`} images[2] = imageEntry{"cirros-0.3.4-x86_64-uec-kernel", `{ "status": "active", "name": "cirros-0.3.4-x86_64-uec-kernel", "tags": [], "container_format": "aki", "created_at": "2015-07-15T11:43:29Z", "size": 4979632, "disk_format": "aki", "updated_at": "2015-07-15T11:43:30Z", "visibility": "public", "self": "/v2/images/e1b6edd4-bd9b-40ac-b010-8a6c16de4ba4", "min_disk": 0, "protected": false, "id": "e1b6edd4-bd9b-40ac-b010-8a6c16de4ba4", "file": "/v2/images/e1b6edd4-bd9b-40ac-b010-8a6c16de4ba4/file", "checksum": "8a40c862b5735975d82605c1dd395796", "owner": "cba624273b8344e59dd1fd18685183b0", "virtual_size": null, "min_ram": 0, "schema": "/v2/schemas/image", "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi" }`} th.Mux.HandleFunc("/images", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) limit := 10 var err error if r.FormValue("limit") != "" { limit, err = strconv.Atoi(r.FormValue("limit")) if err != nil { t.Errorf("Error value for 'limit' parameter %v (error: %v)", r.FormValue("limit"), err) } } marker := "" newMarker := "" if r.Form["marker"] != nil { marker = r.Form["marker"][0] } t.Logf("limit = %v marker = %v", limit, marker) selected := 0 addNext := false var imageJSON []string fmt.Fprintf(w, `{"images": [`) for _, i := range images { if marker == "" || addNext { t.Logf("Adding image %v to page", i.ID) imageJSON = append(imageJSON, i.JSON) newMarker = i.ID selected++ } else { if strings.Contains(i.JSON, marker) { addNext = true } } if selected == limit { break } } t.Logf("Writing out %v image(s)", len(imageJSON)) fmt.Fprintf(w, strings.Join(imageJSON, ",")) fmt.Fprintf(w, `], "next": "/images?marker=%s&limit=%v", "schema": "/schemas/images", "first": "/images?limit=%v"}`, newMarker, limit, limit) }) } // HandleImageCreationSuccessfully test setup func HandleImageCreationSuccessfully(t *testing.T) { th.Mux.HandleFunc("/images", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) th.TestJSONRequest(t, r, `{ "id": "e7db3b45-8db7-47ad-8109-3fb55c2c24fd", "name": "Ubuntu 12.10", "architecture": "x86_64", "tags": [ "ubuntu", "quantal" ] }`) w.WriteHeader(http.StatusCreated) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "status": "queued", "name": "Ubuntu 12.10", "protected": false, "tags": ["ubuntu","quantal"], "container_format": "bare", "created_at": "2014-11-11T20:47:55Z", "disk_format": "qcow2", "updated_at": "2014-11-11T20:47:55Z", "visibility": "private", "self": "/v2/images/e7db3b45-8db7-47ad-8109-3fb55c2c24fd", "min_disk": 0, "protected": false, "id": "e7db3b45-8db7-47ad-8109-3fb55c2c24fd", "file": "/v2/images/e7db3b45-8db7-47ad-8109-3fb55c2c24fd/file", "owner": "b4eedccc6fb74fa8a7ad6b08382b852b", "min_ram": 0, "schema": "/v2/schemas/image", "size": 0, "checksum": "", "virtual_size": 0, "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi" }`) }) } // HandleImageCreationSuccessfullyNulls test setup // JSON null values could be also returned according to behaviour https://bugs.launchpad.net/glance/+bug/1481512 func HandleImageCreationSuccessfullyNulls(t *testing.T) { th.Mux.HandleFunc("/images", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) th.TestJSONRequest(t, r, `{ "id": "e7db3b45-8db7-47ad-8109-3fb55c2c24fd", "architecture": "x86_64", "name": "Ubuntu 12.10", "tags": [ "ubuntu", "quantal" ] }`) w.WriteHeader(http.StatusCreated) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "architecture": "x86_64", "status": "queued", "name": "Ubuntu 12.10", "protected": false, "tags": ["ubuntu","quantal"], "container_format": "bare", "created_at": "2014-11-11T20:47:55Z", "disk_format": "qcow2", "updated_at": "2014-11-11T20:47:55Z", "visibility": "private", "self": "/v2/images/e7db3b45-8db7-47ad-8109-3fb55c2c24fd", "min_disk": 0, "protected": false, "id": "e7db3b45-8db7-47ad-8109-3fb55c2c24fd", "file": "/v2/images/e7db3b45-8db7-47ad-8109-3fb55c2c24fd/file", "owner": "b4eedccc6fb74fa8a7ad6b08382b852b", "min_ram": 0, "schema": "/v2/schemas/image", "size": null, "checksum": null, "virtual_size": null }`) }) } // HandleImageGetSuccessfully test setup func HandleImageGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/images/1bea47ed-f6a9-463b-b423-14b9cca9ad27", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "status": "active", "name": "cirros-0.3.2-x86_64-disk", "tags": [], "container_format": "bare", "created_at": "2014-05-05T17:15:10Z", "disk_format": "qcow2", "updated_at": "2014-05-05T17:15:11Z", "visibility": "public", "self": "/v2/images/1bea47ed-f6a9-463b-b423-14b9cca9ad27", "min_disk": 0, "protected": false, "id": "1bea47ed-f6a9-463b-b423-14b9cca9ad27", "file": "/v2/images/1bea47ed-f6a9-463b-b423-14b9cca9ad27/file", "checksum": "64d7c1cd2b6f60c92c14662941cb7913", "owner": "5ef70662f8b34079a6eddb8da9d75fe8", "size": 13167616, "min_ram": 0, "schema": "/v2/schemas/image", "virtual_size": null, "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi" }`) }) } // HandleImageDeleteSuccessfully test setup func HandleImageDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc("/images/1bea47ed-f6a9-463b-b423-14b9cca9ad27", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleImageUpdateSuccessfully setup func HandleImageUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) th.TestJSONRequest(t, r, `[ { "op": "replace", "path": "/name", "value": "Fedora 17" }, { "op": "replace", "path": "/tags", "value": [ "fedora", "beefy" ] } ]`) th.AssertEquals(t, "application/openstack-images-v2.1-json-patch", r.Header.Get("Content-Type")) w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "id": "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "name": "Fedora 17", "status": "active", "visibility": "public", "size": 2254249, "checksum": "2cec138d7dae2aa59038ef8c9aec2390", "tags": [ "fedora", "beefy" ], "created_at": "2012-08-10T19:23:50Z", "updated_at": "2012-08-12T11:11:33Z", "self": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "file": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/file", "schema": "/v2/schemas/image", "owner": "", "min_ram": 0, "min_disk": 0, "disk_format": "", "virtual_size": 0, "container_format": "", "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi" }`) }) } // HandleImageListByTagsSuccessfully tests a list operation with tags. func HandleImageListByTagsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/images", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, `{ "images": [ { "status": "active", "name": "cirros-0.3.2-x86_64-disk", "tags": ["foo", "bar"], "container_format": "bare", "created_at": "2014-05-05T17:15:10Z", "disk_format": "qcow2", "updated_at": "2014-05-05T17:15:11Z", "visibility": "public", "self": "/v2/images/1bea47ed-f6a9-463b-b423-14b9cca9ad27", "min_disk": 0, "protected": false, "id": "1bea47ed-f6a9-463b-b423-14b9cca9ad27", "file": "/v2/images/1bea47ed-f6a9-463b-b423-14b9cca9ad27/file", "checksum": "64d7c1cd2b6f60c92c14662941cb7913", "owner": "5ef70662f8b34079a6eddb8da9d75fe8", "size": 13167616, "min_ram": 0, "schema": "/v2/schemas/image", "virtual_size": null, "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi" } ] }`) }) } // HandleImageUpdatePropertiesSuccessfully setup func HandleImageUpdatePropertiesSuccessfully(t *testing.T) { th.Mux.HandleFunc("/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) th.TestJSONRequest(t, r, `[ { "op": "add", "path": "/hw_disk_bus", "value": "scsi" }, { "op": "add", "path": "/hw_disk_bus_model", "value": "virtio-scsi" }, { "op": "add", "path": "/hw_scsi_model", "value": "virtio-scsi" } ]`) th.AssertEquals(t, "application/openstack-images-v2.1-json-patch", r.Header.Get("Content-Type")) w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "id": "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "name": "Fedora 17", "status": "active", "visibility": "public", "size": 2254249, "checksum": "2cec138d7dae2aa59038ef8c9aec2390", "tags": [ "fedora", "beefy" ], "created_at": "2012-08-10T19:23:50Z", "updated_at": "2012-08-12T11:11:33Z", "self": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "file": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/file", "schema": "/v2/schemas/image", "owner": "", "min_ram": 0, "min_disk": 0, "disk_format": "", "virtual_size": 0, "container_format": "", "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi" }`) }) } requests_test.go000066400000000000000000000255141335005366400366500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/images/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fakeclient "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListImage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleImageListSuccessfully(t) t.Logf("Test setup %+v\n", th.Server) t.Logf("Id\tName\tOwner\tChecksum\tSizeBytes") pager := images.List(fakeclient.ServiceClient(), images.ListOpts{Limit: 1}) t.Logf("Pager state %v", pager) count, pages := 0, 0 err := pager.EachPage(func(page pagination.Page) (bool, error) { pages++ t.Logf("Page %v", page) images, err := images.ExtractImages(page) if err != nil { return false, err } for _, i := range images { t.Logf("%s\t%s\t%s\t%s\t%v\t\n", i.ID, i.Name, i.Owner, i.Checksum, i.SizeBytes) count++ } return true, nil }) th.AssertNoErr(t, err) t.Logf("--------\n%d images listed on %d pages.\n", count, pages) th.AssertEquals(t, 3, pages) th.AssertEquals(t, 3, count) } func TestAllPagesImage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleImageListSuccessfully(t) pages, err := images.List(fakeclient.ServiceClient(), nil).AllPages() th.AssertNoErr(t, err) images, err := images.ExtractImages(pages) th.AssertNoErr(t, err) th.AssertEquals(t, 3, len(images)) } func TestCreateImage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleImageCreationSuccessfully(t) id := "e7db3b45-8db7-47ad-8109-3fb55c2c24fd" name := "Ubuntu 12.10" actualImage, err := images.Create(fakeclient.ServiceClient(), images.CreateOpts{ ID: id, Name: name, Properties: map[string]string{ "architecture": "x86_64", }, Tags: []string{"ubuntu", "quantal"}, }).Extract() th.AssertNoErr(t, err) containerFormat := "bare" diskFormat := "qcow2" owner := "b4eedccc6fb74fa8a7ad6b08382b852b" minDiskGigabytes := 0 minRAMMegabytes := 0 file := actualImage.File createdDate := actualImage.CreatedAt lastUpdate := actualImage.UpdatedAt schema := "/v2/schemas/image" expectedImage := images.Image{ ID: "e7db3b45-8db7-47ad-8109-3fb55c2c24fd", Name: "Ubuntu 12.10", Tags: []string{"ubuntu", "quantal"}, Status: images.ImageStatusQueued, ContainerFormat: containerFormat, DiskFormat: diskFormat, MinDiskGigabytes: minDiskGigabytes, MinRAMMegabytes: minRAMMegabytes, Owner: owner, Visibility: images.ImageVisibilityPrivate, File: file, CreatedAt: createdDate, UpdatedAt: lastUpdate, Schema: schema, VirtualSize: 0, Properties: map[string]interface{}{ "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi", }, } th.AssertDeepEquals(t, &expectedImage, actualImage) } func TestCreateImageNulls(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleImageCreationSuccessfullyNulls(t) id := "e7db3b45-8db7-47ad-8109-3fb55c2c24fd" name := "Ubuntu 12.10" actualImage, err := images.Create(fakeclient.ServiceClient(), images.CreateOpts{ ID: id, Name: name, Tags: []string{"ubuntu", "quantal"}, Properties: map[string]string{ "architecture": "x86_64", }, }).Extract() th.AssertNoErr(t, err) containerFormat := "bare" diskFormat := "qcow2" owner := "b4eedccc6fb74fa8a7ad6b08382b852b" minDiskGigabytes := 0 minRAMMegabytes := 0 file := actualImage.File createdDate := actualImage.CreatedAt lastUpdate := actualImage.UpdatedAt schema := "/v2/schemas/image" properties := map[string]interface{}{ "architecture": "x86_64", } sizeBytes := int64(0) expectedImage := images.Image{ ID: "e7db3b45-8db7-47ad-8109-3fb55c2c24fd", Name: "Ubuntu 12.10", Tags: []string{"ubuntu", "quantal"}, Status: images.ImageStatusQueued, ContainerFormat: containerFormat, DiskFormat: diskFormat, MinDiskGigabytes: minDiskGigabytes, MinRAMMegabytes: minRAMMegabytes, Owner: owner, Visibility: images.ImageVisibilityPrivate, File: file, CreatedAt: createdDate, UpdatedAt: lastUpdate, Schema: schema, Properties: properties, SizeBytes: sizeBytes, } th.AssertDeepEquals(t, &expectedImage, actualImage) } func TestGetImage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleImageGetSuccessfully(t) actualImage, err := images.Get(fakeclient.ServiceClient(), "1bea47ed-f6a9-463b-b423-14b9cca9ad27").Extract() th.AssertNoErr(t, err) checksum := "64d7c1cd2b6f60c92c14662941cb7913" sizeBytes := int64(13167616) containerFormat := "bare" diskFormat := "qcow2" minDiskGigabytes := 0 minRAMMegabytes := 0 owner := "5ef70662f8b34079a6eddb8da9d75fe8" file := actualImage.File createdDate := actualImage.CreatedAt lastUpdate := actualImage.UpdatedAt schema := "/v2/schemas/image" expectedImage := images.Image{ ID: "1bea47ed-f6a9-463b-b423-14b9cca9ad27", Name: "cirros-0.3.2-x86_64-disk", Tags: []string{}, Status: images.ImageStatusActive, ContainerFormat: containerFormat, DiskFormat: diskFormat, MinDiskGigabytes: minDiskGigabytes, MinRAMMegabytes: minRAMMegabytes, Owner: owner, Protected: false, Visibility: images.ImageVisibilityPublic, Checksum: checksum, SizeBytes: sizeBytes, File: file, CreatedAt: createdDate, UpdatedAt: lastUpdate, Schema: schema, VirtualSize: 0, Properties: map[string]interface{}{ "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi", }, } th.AssertDeepEquals(t, &expectedImage, actualImage) } func TestDeleteImage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleImageDeleteSuccessfully(t) result := images.Delete(fakeclient.ServiceClient(), "1bea47ed-f6a9-463b-b423-14b9cca9ad27") th.AssertNoErr(t, result.Err) } func TestUpdateImage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleImageUpdateSuccessfully(t) actualImage, err := images.Update(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", images.UpdateOpts{ images.ReplaceImageName{NewName: "Fedora 17"}, images.ReplaceImageTags{NewTags: []string{"fedora", "beefy"}}, }).Extract() th.AssertNoErr(t, err) sizebytes := int64(2254249) checksum := "2cec138d7dae2aa59038ef8c9aec2390" file := actualImage.File createdDate := actualImage.CreatedAt lastUpdate := actualImage.UpdatedAt schema := "/v2/schemas/image" expectedImage := images.Image{ ID: "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", Name: "Fedora 17", Status: images.ImageStatusActive, Visibility: images.ImageVisibilityPublic, SizeBytes: sizebytes, Checksum: checksum, Tags: []string{ "fedora", "beefy", }, Owner: "", MinRAMMegabytes: 0, MinDiskGigabytes: 0, DiskFormat: "", ContainerFormat: "", File: file, CreatedAt: createdDate, UpdatedAt: lastUpdate, Schema: schema, VirtualSize: 0, Properties: map[string]interface{}{ "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi", }, } th.AssertDeepEquals(t, &expectedImage, actualImage) } func TestImageDateQuery(t *testing.T) { date := time.Date(2014, 1, 1, 1, 1, 1, 0, time.UTC) listOpts := images.ListOpts{ CreatedAtQuery: &images.ImageDateQuery{ Date: date, Filter: images.FilterGTE, }, UpdatedAtQuery: &images.ImageDateQuery{ Date: date, }, } expectedQueryString := "?created_at=gte%3A2014-01-01T01%3A01%3A01Z&updated_at=2014-01-01T01%3A01%3A01Z" actualQueryString, err := listOpts.ToImageListQuery() th.AssertNoErr(t, err) th.AssertEquals(t, expectedQueryString, actualQueryString) } func TestImageListByTags(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleImageListByTagsSuccessfully(t) listOpts := images.ListOpts{ Tags: []string{"foo", "bar"}, } expectedQueryString := "?tag=foo&tag=bar" actualQueryString, err := listOpts.ToImageListQuery() th.AssertNoErr(t, err) th.AssertEquals(t, expectedQueryString, actualQueryString) pages, err := images.List(fakeclient.ServiceClient(), listOpts).AllPages() th.AssertNoErr(t, err) allImages, err := images.ExtractImages(pages) th.AssertNoErr(t, err) checksum := "64d7c1cd2b6f60c92c14662941cb7913" sizeBytes := int64(13167616) containerFormat := "bare" diskFormat := "qcow2" minDiskGigabytes := 0 minRAMMegabytes := 0 owner := "5ef70662f8b34079a6eddb8da9d75fe8" file := allImages[0].File createdDate := allImages[0].CreatedAt lastUpdate := allImages[0].UpdatedAt schema := "/v2/schemas/image" tags := []string{"foo", "bar"} expectedImage := images.Image{ ID: "1bea47ed-f6a9-463b-b423-14b9cca9ad27", Name: "cirros-0.3.2-x86_64-disk", Tags: tags, Status: images.ImageStatusActive, ContainerFormat: containerFormat, DiskFormat: diskFormat, MinDiskGigabytes: minDiskGigabytes, MinRAMMegabytes: minRAMMegabytes, Owner: owner, Protected: false, Visibility: images.ImageVisibilityPublic, Checksum: checksum, SizeBytes: sizeBytes, File: file, CreatedAt: createdDate, UpdatedAt: lastUpdate, Schema: schema, VirtualSize: 0, Properties: map[string]interface{}{ "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi", }, } th.AssertDeepEquals(t, expectedImage, allImages[0]) } func TestUpdateImageProperties(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleImageUpdatePropertiesSuccessfully(t) actualImage, err := images.Update(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", images.UpdateOpts{ images.UpdateImageProperty{ Op: images.AddOp, Name: "hw_disk_bus", Value: "scsi", }, images.UpdateImageProperty{ Op: images.AddOp, Name: "hw_disk_bus_model", Value: "virtio-scsi", }, images.UpdateImageProperty{ Op: images.AddOp, Name: "hw_scsi_model", Value: "virtio-scsi", }, }).Extract() th.AssertNoErr(t, err) sizebytes := int64(2254249) checksum := "2cec138d7dae2aa59038ef8c9aec2390" file := actualImage.File createdDate := actualImage.CreatedAt lastUpdate := actualImage.UpdatedAt schema := "/v2/schemas/image" expectedImage := images.Image{ ID: "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", Name: "Fedora 17", Status: images.ImageStatusActive, Visibility: images.ImageVisibilityPublic, SizeBytes: sizebytes, Checksum: checksum, Tags: []string{ "fedora", "beefy", }, Owner: "", MinRAMMegabytes: 0, MinDiskGigabytes: 0, DiskFormat: "", ContainerFormat: "", File: file, CreatedAt: createdDate, UpdatedAt: lastUpdate, Schema: schema, VirtualSize: 0, Properties: map[string]interface{}{ "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi", }, } th.AssertDeepEquals(t, &expectedImage, actualImage) } types.go000066400000000000000000000066611335005366400334270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imagespackage images import ( "time" ) // ImageStatus image statuses // http://docs.openstack.org/developer/glance/statuses.html type ImageStatus string const ( // ImageStatusQueued is a status for an image which identifier has // been reserved for an image in the image registry. ImageStatusQueued ImageStatus = "queued" // ImageStatusSaving denotes that an image’s raw data is currently being // uploaded to Glance ImageStatusSaving ImageStatus = "saving" // ImageStatusActive denotes an image that is fully available in Glance. ImageStatusActive ImageStatus = "active" // ImageStatusKilled denotes that an error occurred during the uploading // of an image’s data, and that the image is not readable. ImageStatusKilled ImageStatus = "killed" // ImageStatusDeleted is used for an image that is no longer available to use. // The image information is retained in the image registry. ImageStatusDeleted ImageStatus = "deleted" // ImageStatusPendingDelete is similar to Delete, but the image is not yet // deleted. ImageStatusPendingDelete ImageStatus = "pending_delete" // ImageStatusDeactivated denotes that access to image data is not allowed to // any non-admin user. ImageStatusDeactivated ImageStatus = "deactivated" ) // ImageVisibility denotes an image that is fully available in Glance. // This occurs when the image data is uploaded, or the image size is explicitly // set to zero on creation. // According to design // https://wiki.openstack.org/wiki/Glance-v2-community-image-visibility-design type ImageVisibility string const ( // ImageVisibilityPublic all users ImageVisibilityPublic ImageVisibility = "public" // ImageVisibilityPrivate users with tenantId == tenantId(owner) ImageVisibilityPrivate ImageVisibility = "private" // ImageVisibilityShared images are visible to: // - users with tenantId == tenantId(owner) // - users with tenantId in the member-list of the image // - users with tenantId in the member-list with member_status == 'accepted' ImageVisibilityShared ImageVisibility = "shared" // ImageVisibilityCommunity images: // - all users can see and boot it // - users with tenantId in the member-list of the image with // member_status == 'accepted' have this image in their default image-list. ImageVisibilityCommunity ImageVisibility = "community" ) // MemberStatus is a status for adding a new member (tenant) to an image // member list. type ImageMemberStatus string const ( // ImageMemberStatusAccepted is the status for an accepted image member. ImageMemberStatusAccepted ImageMemberStatus = "accepted" // ImageMemberStatusPending shows that the member addition is pending ImageMemberStatusPending ImageMemberStatus = "pending" // ImageMemberStatusAccepted is the status for a rejected image member ImageMemberStatusRejected ImageMemberStatus = "rejected" // ImageMemberStatusAll ImageMemberStatusAll ImageMemberStatus = "all" ) // ImageDateFilter represents a valid filter to use for filtering // images by their date during a List. type ImageDateFilter string const ( FilterGT ImageDateFilter = "gt" FilterGTE ImageDateFilter = "gte" FilterLT ImageDateFilter = "lt" FilterLTE ImageDateFilter = "lte" FilterNEQ ImageDateFilter = "neq" FilterEQ ImageDateFilter = "eq" ) // ImageDateQuery represents a date field to be used for listing images. // If no filter is specified, the query will act as though FilterEQ was // set. type ImageDateQuery struct { Date time.Time Filter ImageDateFilter } urls.go000066400000000000000000000031641335005366400332430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/imagespackage images import ( "net/url" "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/utils" ) // `listURL` is a pure function. `listURL(c)` is a URL for which a GET // request will respond with a list of images in the service `c`. func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("images") } func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("images") } // `imageURL(c,i)` is the URL for the image identified by ID `i` in // the service `c`. func imageURL(c *gophercloud.ServiceClient, imageID string) string { return c.ServiceURL("images", imageID) } // `getURL(c,i)` is a URL for which a GET request will respond with // information about the image identified by ID `i` in the service // `c`. func getURL(c *gophercloud.ServiceClient, imageID string) string { return imageURL(c, imageID) } func updateURL(c *gophercloud.ServiceClient, imageID string) string { return imageURL(c, imageID) } func deleteURL(c *gophercloud.ServiceClient, imageID string) string { return imageURL(c, imageID) } // builds next page full url based on current url func nextPageURL(serviceURL, requestedNext string) (string, error) { base, err := utils.BaseEndpoint(serviceURL) if err != nil { return "", err } requestedNextURL, err := url.Parse(requestedNext) if err != nil { return "", err } base = gophercloud.NormalizeURL(base) nextPath := base + strings.TrimPrefix(requestedNextURL.Path, "/") nextURL, err := url.Parse(nextPath) if err != nil { return "", err } nextURL.RawQuery = requestedNextURL.RawQuery return nextURL.String(), nil } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/members/000077500000000000000000000000001335005366400321675ustar00rootroot00000000000000doc.go000066400000000000000000000024441335005366400332100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/members/* Package members enables management and retrieval of image members. Members are projects other than the image owner who have access to the image. Example to List Members of an Image imageID := "2b6cacd4-cfd6-4b95-8302-4c04ccf0be3f" allPages, err := members.List(imageID).AllPages() if err != nil { panic(err) } allMembers, err := members.ExtractMembers(allPages) if err != nil { panic(err) } for _, member := range allMembers { fmt.Printf("%+v\n", member) } Example to Add a Member to an Image imageID := "2b6cacd4-cfd6-4b95-8302-4c04ccf0be3f" projectID := "fc404778935a4cebaddcb4788fb3ff2c" member, err := members.Create(imageClient, imageID, projectID).Extract() if err != nil { panic(err) } Example to Update the Status of a Member imageID := "2b6cacd4-cfd6-4b95-8302-4c04ccf0be3f" projectID := "fc404778935a4cebaddcb4788fb3ff2c" updateOpts := members.UpdateOpts{ Status: "accepted", } member, err := members.Update(imageClient, imageID, projectID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Member from an Image imageID := "2b6cacd4-cfd6-4b95-8302-4c04ccf0be3f" projectID := "fc404778935a4cebaddcb4788fb3ff2c" err := members.Delete(imageClient, imageID, projectID).ExtractErr() if err != nil { panic(err) } */ package members requests.go000066400000000000000000000050401335005366400343110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/memberspackage members import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) /* Create member for specific image Preconditions * The specified images must exist. * You can only add a new member to an image which 'visibility' attribute is private. * You must be the owner of the specified image. Synchronous Postconditions With correct permissions, you can see the member status of the image as pending through API calls. More details here: http://developer.openstack.org/api-ref-image-v2.html#createImageMember-v2 */ func Create(client *gophercloud.ServiceClient, id string, member string) (r CreateResult) { b := map[string]interface{}{"member": member} _, r.Err = client.Post(createMemberURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // List members returns list of members for specifed image id. func List(client *gophercloud.ServiceClient, id string) pagination.Pager { return pagination.NewPager(client, listMembersURL(client, id), func(r pagination.PageResult) pagination.Page { return MemberPage{pagination.SinglePageBase(r)} }) } // Get image member details. func Get(client *gophercloud.ServiceClient, imageID string, memberID string) (r DetailsResult) { _, r.Err = client.Get(getMemberURL(client, imageID, memberID), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) return } // Delete membership for given image. Callee should be image owner. func Delete(client *gophercloud.ServiceClient, imageID string, memberID string) (r DeleteResult) { _, r.Err = client.Delete(deleteMemberURL(client, imageID, memberID), &gophercloud.RequestOpts{OkCodes: []int{204}}) return } // UpdateOptsBuilder allows extensions to add additional attributes to the // Update request. type UpdateOptsBuilder interface { ToImageMemberUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents options to an Update request. type UpdateOpts struct { Status string } // ToMemberUpdateMap formats an UpdateOpts structure into a request body. func (opts UpdateOpts) ToImageMemberUpdateMap() (map[string]interface{}, error) { return map[string]interface{}{ "status": opts.Status, }, nil } // Update function updates member. func Update(client *gophercloud.ServiceClient, imageID string, memberID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToImageMemberUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Put(updateMemberURL(client, imageID, memberID), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) return } results.go000066400000000000000000000035051335005366400341430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/memberspackage members import ( "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Member represents a member of an Image. type Member struct { CreatedAt time.Time `json:"created_at"` ImageID string `json:"image_id"` MemberID string `json:"member_id"` Schema string `json:"schema"` Status string `json:"status"` UpdatedAt time.Time `json:"updated_at"` } // Extract Member model from a request. func (r commonResult) Extract() (*Member, error) { var s *Member err := r.ExtractInto(&s) return s, err } // MemberPage is a single page of Members results. type MemberPage struct { pagination.SinglePageBase } // ExtractMembers returns a slice of Members contained in a single page // of results. func ExtractMembers(r pagination.Page) ([]Member, error) { var s struct { Members []Member `json:"members"` } err := r.(MemberPage).ExtractInto(&s) return s.Members, err } // IsEmpty determines whether or not a MemberPage contains any results. func (r MemberPage) IsEmpty() (bool, error) { members, err := ExtractMembers(r) return len(members) == 0, err } type commonResult struct { gophercloud.Result } // CreateResult represents the result of a Create operation. Call its Extract // method to interpret it as a Member. type CreateResult struct { commonResult } // DetailsResult represents the result of a Get operation. Call its Extract // method to interpret it as a Member. type DetailsResult struct { commonResult } // UpdateResult represents the result of an Update operation. Call its Extract // method to interpret it as a Member. type UpdateResult struct { commonResult } // DeleteResult represents the result of a Delete operation. Call its // ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400335655ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/membersdoc.go000066400000000000000000000000461335005366400346610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/members/testing// members unit tests package testing fixtures.go000066400000000000000000000106031335005366400357650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/members/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fakeclient "github.com/gophercloud/gophercloud/testhelper/client" ) // HandleCreateImageMemberSuccessfully setup func HandleCreateImageMemberSuccessfully(t *testing.T) { th.Mux.HandleFunc("/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/members", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) th.TestJSONRequest(t, r, `{"member": "8989447062e04a818baf9e073fd04fa7"}`) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, `{ "created_at": "2013-09-20T19:22:19Z", "image_id": "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "member_id": "8989447062e04a818baf9e073fd04fa7", "schema": "/v2/schemas/member", "status": "pending", "updated_at": "2013-09-20T19:25:31Z" }`) }) } // HandleImageMemberList happy path setup func HandleImageMemberList(t *testing.T) { th.Mux.HandleFunc("/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/members", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "members": [ { "created_at": "2013-10-07T17:58:03Z", "image_id": "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "member_id": "123456789", "schema": "/v2/schemas/member", "status": "pending", "updated_at": "2013-10-07T17:58:03Z" }, { "created_at": "2013-10-07T17:58:55Z", "image_id": "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "member_id": "987654321", "schema": "/v2/schemas/member", "status": "accepted", "updated_at": "2013-10-08T12:08:55Z" } ], "schema": "/v2/schemas/members" }`) }) } // HandleImageMemberEmptyList happy path setup func HandleImageMemberEmptyList(t *testing.T) { th.Mux.HandleFunc("/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/members", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "members": [], "schema": "/v2/schemas/members" }`) }) } // HandleImageMemberDetails setup func HandleImageMemberDetails(t *testing.T) { th.Mux.HandleFunc("/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/members/8989447062e04a818baf9e073fd04fa7", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, `{ "status": "pending", "created_at": "2013-11-26T07:21:21Z", "updated_at": "2013-11-26T07:21:21Z", "image_id": "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "member_id": "8989447062e04a818baf9e073fd04fa7", "schema": "/v2/schemas/member" }`) }) } // HandleImageMemberDeleteSuccessfully setup func HandleImageMemberDeleteSuccessfully(t *testing.T) *CallsCounter { var counter CallsCounter th.Mux.HandleFunc("/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/members/8989447062e04a818baf9e073fd04fa7", func(w http.ResponseWriter, r *http.Request) { counter.Counter = counter.Counter + 1 th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) w.WriteHeader(http.StatusNoContent) }) return &counter } // HandleImageMemberUpdate setup func HandleImageMemberUpdate(t *testing.T) *CallsCounter { var counter CallsCounter th.Mux.HandleFunc("/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/members/8989447062e04a818baf9e073fd04fa7", func(w http.ResponseWriter, r *http.Request) { counter.Counter = counter.Counter + 1 th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) th.TestJSONRequest(t, r, `{"status": "accepted"}`) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, `{ "status": "accepted", "created_at": "2013-11-26T07:21:21Z", "updated_at": "2013-11-26T07:21:21Z", "image_id": "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "member_id": "8989447062e04a818baf9e073fd04fa7", "schema": "/v2/schemas/member" }`) }) return &counter } // CallsCounter for checking if request handler was called at all type CallsCounter struct { Counter int } requests_test.go000066400000000000000000000105531335005366400370320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/members/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/members" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fakeclient "github.com/gophercloud/gophercloud/testhelper/client" ) const createdAtString = "2013-09-20T19:22:19Z" const updatedAtString = "2013-09-20T19:25:31Z" func TestCreateMemberSuccessfully(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateImageMemberSuccessfully(t) im, err := members.Create(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "8989447062e04a818baf9e073fd04fa7").Extract() th.AssertNoErr(t, err) createdAt, err := time.Parse(time.RFC3339, createdAtString) th.AssertNoErr(t, err) updatedAt, err := time.Parse(time.RFC3339, updatedAtString) th.AssertNoErr(t, err) th.AssertDeepEquals(t, members.Member{ CreatedAt: createdAt, ImageID: "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", MemberID: "8989447062e04a818baf9e073fd04fa7", Schema: "/v2/schemas/member", Status: "pending", UpdatedAt: updatedAt, }, *im) } func TestMemberListSuccessfully(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleImageMemberList(t) pager := members.List(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea") t.Logf("Pager state %v", pager) count, pages := 0, 0 err := pager.EachPage(func(page pagination.Page) (bool, error) { pages++ t.Logf("Page %v", page) members, err := members.ExtractMembers(page) if err != nil { return false, err } for _, i := range members { t.Logf("%s\t%s\t%s\t%s\t\n", i.ImageID, i.MemberID, i.Status, i.Schema) count++ } return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, pages) th.AssertEquals(t, 2, count) } func TestMemberListEmpty(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleImageMemberEmptyList(t) pager := members.List(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea") t.Logf("Pager state %v", pager) count, pages := 0, 0 err := pager.EachPage(func(page pagination.Page) (bool, error) { pages++ t.Logf("Page %v", page) members, err := members.ExtractMembers(page) if err != nil { return false, err } for _, i := range members { t.Logf("%s\t%s\t%s\t%s\t\n", i.ImageID, i.MemberID, i.Status, i.Schema) count++ } return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, 0, pages) th.AssertEquals(t, 0, count) } func TestShowMemberDetails(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleImageMemberDetails(t) md, err := members.Get(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "8989447062e04a818baf9e073fd04fa7").Extract() th.AssertNoErr(t, err) if md == nil { t.Errorf("Expected non-nil value for md") } createdAt, err := time.Parse(time.RFC3339, "2013-11-26T07:21:21Z") th.AssertNoErr(t, err) updatedAt, err := time.Parse(time.RFC3339, "2013-11-26T07:21:21Z") th.AssertNoErr(t, err) th.AssertDeepEquals(t, members.Member{ CreatedAt: createdAt, ImageID: "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", MemberID: "8989447062e04a818baf9e073fd04fa7", Schema: "/v2/schemas/member", Status: "pending", UpdatedAt: updatedAt, }, *md) } func TestDeleteMember(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() counter := HandleImageMemberDeleteSuccessfully(t) result := members.Delete(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "8989447062e04a818baf9e073fd04fa7") th.AssertEquals(t, 1, counter.Counter) th.AssertNoErr(t, result.Err) } func TestMemberUpdateSuccessfully(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() counter := HandleImageMemberUpdate(t) im, err := members.Update(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "8989447062e04a818baf9e073fd04fa7", members.UpdateOpts{ Status: "accepted", }).Extract() th.AssertEquals(t, 1, counter.Counter) th.AssertNoErr(t, err) createdAt, err := time.Parse(time.RFC3339, "2013-11-26T07:21:21Z") th.AssertNoErr(t, err) updatedAt, err := time.Parse(time.RFC3339, "2013-11-26T07:21:21Z") th.AssertNoErr(t, err) th.AssertDeepEquals(t, members.Member{ CreatedAt: createdAt, ImageID: "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", MemberID: "8989447062e04a818baf9e073fd04fa7", Schema: "/v2/schemas/member", Status: "accepted", UpdatedAt: updatedAt, }, *im) } urls.go000066400000000000000000000017441335005366400334320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/memberspackage members import "github.com/gophercloud/gophercloud" func imageMembersURL(c *gophercloud.ServiceClient, imageID string) string { return c.ServiceURL("images", imageID, "members") } func listMembersURL(c *gophercloud.ServiceClient, imageID string) string { return imageMembersURL(c, imageID) } func createMemberURL(c *gophercloud.ServiceClient, imageID string) string { return imageMembersURL(c, imageID) } func imageMemberURL(c *gophercloud.ServiceClient, imageID string, memberID string) string { return c.ServiceURL("images", imageID, "members", memberID) } func getMemberURL(c *gophercloud.ServiceClient, imageID string, memberID string) string { return imageMemberURL(c, imageID, memberID) } func updateMemberURL(c *gophercloud.ServiceClient, imageID string, memberID string) string { return imageMemberURL(c, imageID, memberID) } func deleteMemberURL(c *gophercloud.ServiceClient, imageID string, memberID string) string { return imageMemberURL(c, imageID, memberID) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/tasks/000077500000000000000000000000001335005366400316625ustar00rootroot00000000000000doc.go000066400000000000000000000022411335005366400326760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/tasks/* Package tasks enables management and retrieval of tasks from the OpenStack Imageservice. Example to List Tasks listOpts := tasks.ListOpts{ Owner: "424e7cf0243c468ca61732ba45973b3e", } allPages, err := tasks.List(imagesClient, listOpts).AllPages() if err != nil { panic(err) } allTasks, err := tasks.ExtractTasks(allPages) if err != nil { panic(err) } for _, task := range allTasks { fmt.Printf("%+v\n", task) } Example to Get a Task task, err := tasks.Get(imagesClient, "1252f636-1246-4319-bfba-c47cde0efbe0").Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", task) Example to Create a Task createOpts := tasks.CreateOpts{ Type: "import", Input: map[string]interface{}{ "image_properties": map[string]interface{}{ "container_format": "bare", "disk_format": "raw", }, "import_from_format": "raw", "import_from": "https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img", }, } task, err := tasks.Create(imagesClient, createOpts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", task) */ package tasks requests.go000066400000000000000000000070661335005366400340160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/taskspackage tasks import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // TaskStatus represents valid task status. // You can use this type to compare the actual status of a task to a one of the // pre-defined statuses. type TaskStatus string const ( // TaskStatusPending represents status of the pending task. TaskStatusPending TaskStatus = "pending" // TaskStatusProcessing represents status of the processing task. TaskStatusProcessing TaskStatus = "processing" // TaskStatusSuccess represents status of the success task. TaskStatusSuccess TaskStatus = "success" // TaskStatusFailure represents status of the failure task. TaskStatusFailure TaskStatus = "failure" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToTaskListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the OpenStack Imageservice tasks API. type ListOpts struct { // Integer value for the limit of values to return. Limit int `q:"limit"` // ID of the task at which you want to set a marker. Marker string `q:"marker"` // SortDir allows to select sort direction. // It can be "asc" or "desc" (default). SortDir string `q:"sort_dir"` // SortKey allows to sort by one of the following tTask attributes: // - created_at // - expires_at // - status // - type // - updated_at // Default is created_at. SortKey string `q:"sort_key"` // ID filters on the identifier of the task. ID string `json:"id"` // Type filters on the type of the task. Type string `json:"type"` // Status filters on the status of the task. Status TaskStatus `q:"status"` } // ToTaskListQuery formats a ListOpts into a query string. func (opts ListOpts) ToTaskListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of the tasks. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(c) if opts != nil { query, err := opts.ToTaskListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { taskPage := TaskPage{ serviceURL: c.ServiceURL(), LinkedPageBase: pagination.LinkedPageBase{PageResult: r}, } return taskPage }) } // Get retrieves a specific Imageservice task based on its ID. func Get(c *gophercloud.ServiceClient, taskID string) (r GetResult) { _, r.Err = c.Get(getURL(c, taskID), &r.Body, nil) return } // CreateOptsBuilder allows to add additional parameters to the Create request. type CreateOptsBuilder interface { ToTaskCreateMap() (map[string]interface{}, error) } // CreateOpts specifies parameters of a new Imageservice task. type CreateOpts struct { Type string `json:"type" required:"true"` Input map[string]interface{} `json:"input"` } // ToTaskCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToTaskCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } return b, nil } // Create requests the creation of a new Imageservice task on the server. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToTaskCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) return } results.go000066400000000000000000000055171335005366400336430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/taskspackage tasks import ( "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { gophercloud.Result } // GetResult represents the result of a Get operation. Call its Extract // method to interpret it as a Task. type GetResult struct { commonResult } // CreateResult represents the result of a Create operation. Call its Extract // method to interpret it as a Task. type CreateResult struct { commonResult } // Task represents a single task of the OpenStack Image service. type Task struct { // ID is a unique identifier of the task. ID string `json:"id"` // Type represents the type of the task. Type string `json:"type"` // Status represents current status of the task. // You can use the TaskStatus custom type to unmarshal raw JSON response into // the pre-defined valid task status. Status string `json:"status"` // Input represents different parameters for the task. Input map[string]interface{} `json:"input"` // Result represents task result details. Result map[string]interface{} `json:"result"` // Owner is a unique identifier of the task owner. Owner string `json:"owner"` // Message represents human-readable message that is usually populated // on task failure. Message string `json:"message"` // ExpiresAt contains the timestamp of when the task will become a subject of // removal. ExpiresAt time.Time `json:"expires_at"` // CreatedAt contains the task creation timestamp. CreatedAt time.Time `json:"created_at"` // UpdatedAt contains the latest timestamp of when the task was updated. UpdatedAt time.Time `json:"updated_at"` // Self contains URI for the task. Self string `json:"self"` // Schema the path to the JSON-schema that represent the task. Schema string `json:"schema"` } // Extract interprets any commonResult as a Task. func (r commonResult) Extract() (*Task, error) { var s *Task err := r.ExtractInto(&s) return s, err } // TaskPage represents the results of a List request. type TaskPage struct { serviceURL string pagination.LinkedPageBase } // IsEmpty returns true if a TaskPage contains no Tasks results. func (r TaskPage) IsEmpty() (bool, error) { tasks, err := ExtractTasks(r) return len(tasks) == 0, err } // NextPageURL uses the response's embedded link reference to navigate to // the next page of results. func (r TaskPage) NextPageURL() (string, error) { var s struct { Next string `json:"next"` } err := r.ExtractInto(&s) if err != nil { return "", err } if s.Next == "" { return "", nil } return nextPageURL(r.serviceURL, s.Next) } // ExtractTasks interprets the results of a single page from a List() call, // producing a slice of Task entities. func ExtractTasks(r pagination.Page) ([]Task, error) { var s struct { Tasks []Task `json:"tasks"` } err := (r.(TaskPage)).ExtractInto(&s) return s.Tasks, err } testing/000077500000000000000000000000001335005366400332605ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/tasksdoc.go000066400000000000000000000000441335005366400343520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/tasks/testing// tasks unit tests package testing fixtures.go000066400000000000000000000077011335005366400354650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/tasks/testingpackage testing import ( "time" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/tasks" ) // TasksListResult represents raw server response from a server to a list call. const TasksListResult = ` { "schema": "/v2/schemas/tasks", "tasks": [ { "status": "pending", "self": "/v2/tasks/1252f636-1246-4319-bfba-c47cde0efbe0", "updated_at": "2018-07-25T08:59:14Z", "id": "1252f636-1246-4319-bfba-c47cde0efbe0", "owner": "424e7cf0243c468ca61732ba45973b3e", "type": "import", "created_at": "2018-07-25T08:59:13Z", "schema": "/v2/schemas/task" }, { "status": "processing", "self": "/v2/tasks/349a51f4-d51d-47b6-82da-4fa516f0ca32", "updated_at": "2018-07-25T08:56:19Z", "id": "349a51f4-d51d-47b6-82da-4fa516f0ca32", "owner": "fb57277ef2f84a0e85b9018ec2dedbf7", "type": "import", "created_at": "2018-07-25T08:56:17Z", "schema": "/v2/schemas/task" } ], "first": "/v2/tasks?sort_key=status&sort_dir=desc&limit=20" } ` // Task1 is an expected representation of a first task from the TasksListResult. var Task1 = tasks.Task{ ID: "1252f636-1246-4319-bfba-c47cde0efbe0", Status: string(tasks.TaskStatusPending), Type: "import", Owner: "424e7cf0243c468ca61732ba45973b3e", CreatedAt: time.Date(2018, 7, 25, 8, 59, 13, 0, time.UTC), UpdatedAt: time.Date(2018, 7, 25, 8, 59, 14, 0, time.UTC), Self: "/v2/tasks/1252f636-1246-4319-bfba-c47cde0efbe0", Schema: "/v2/schemas/task", } // Task2 is an expected representation of a first task from the TasksListResult. var Task2 = tasks.Task{ ID: "349a51f4-d51d-47b6-82da-4fa516f0ca32", Status: string(tasks.TaskStatusProcessing), Type: "import", Owner: "fb57277ef2f84a0e85b9018ec2dedbf7", CreatedAt: time.Date(2018, 7, 25, 8, 56, 17, 0, time.UTC), UpdatedAt: time.Date(2018, 7, 25, 8, 56, 19, 0, time.UTC), Self: "/v2/tasks/349a51f4-d51d-47b6-82da-4fa516f0ca32", Schema: "/v2/schemas/task", } // TasksGetResult represents raw server response from a server to a get call. const TasksGetResult = ` { "status": "pending", "created_at": "2018-07-25T08:59:13Z", "updated_at": "2018-07-25T08:59:14Z", "self": "/v2/tasks/1252f636-1246-4319-bfba-c47cde0efbe0", "result": null, "owner": "424e7cf0243c468ca61732ba45973b3e", "input": { "image_properties": { "container_format": "bare", "disk_format": "raw" }, "import_from_format": "raw", "import_from": "http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img" }, "message": "", "type": "import", "id": "1252f636-1246-4319-bfba-c47cde0efbe0", "schema": "/v2/schemas/task" } ` // TaskCreateRequest represents a request to create a task. const TaskCreateRequest = ` { "input": { "image_properties": { "container_format": "bare", "disk_format": "raw" }, "import_from_format": "raw", "import_from": "https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img" }, "type": "import" } ` // TaskCreateResult represents a raw server response to the TaskCreateRequest. const TaskCreateResult = ` { "status": "pending", "created_at": "2018-07-25T11:07:54Z", "updated_at": "2018-07-25T11:07:54Z", "self": "/v2/tasks/d550c87d-86ed-430a-9895-c7a1f5ce87e9", "result": null, "owner": "fb57277ef2f84a0e85b9018ec2dedbf7", "input": { "image_properties": { "container_format": "bare", "disk_format": "raw" }, "import_from_format": "raw", "import_from": "https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img" }, "message": "", "type": "import", "id": "d550c87d-86ed-430a-9895-c7a1f5ce87e9", "schema": "/v2/schemas/task" } ` requests_test.go000066400000000000000000000103711335005366400365230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/tasks/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/tasks" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fakeclient "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/tasks", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, TasksListResult) }) count := 0 tasks.List(fakeclient.ServiceClient(), tasks.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := tasks.ExtractTasks(page) if err != nil { t.Errorf("Failed to extract tasks: %v", err) return false, nil } expected := []tasks.Task{ Task1, Task2, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/tasks/1252f636-1246-4319-bfba-c47cde0efbe0", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, TasksGetResult) }) s, err := tasks.Get(fakeclient.ServiceClient(), "1252f636-1246-4319-bfba-c47cde0efbe0").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Status, string(tasks.TaskStatusPending)) th.AssertEquals(t, s.CreatedAt, time.Date(2018, 7, 25, 8, 59, 13, 0, time.UTC)) th.AssertEquals(t, s.UpdatedAt, time.Date(2018, 7, 25, 8, 59, 14, 0, time.UTC)) th.AssertEquals(t, s.Self, "/v2/tasks/1252f636-1246-4319-bfba-c47cde0efbe0") th.AssertEquals(t, s.Owner, "424e7cf0243c468ca61732ba45973b3e") th.AssertEquals(t, s.Message, "") th.AssertEquals(t, s.Type, "import") th.AssertEquals(t, s.ID, "1252f636-1246-4319-bfba-c47cde0efbe0") th.AssertEquals(t, s.Schema, "/v2/schemas/task") th.AssertDeepEquals(t, s.Result, map[string]interface{}(nil)) th.AssertDeepEquals(t, s.Input, map[string]interface{}{ "import_from": "http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img", "import_from_format": "raw", "image_properties": map[string]interface{}{ "container_format": "bare", "disk_format": "raw", }, }) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/tasks", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) th.TestJSONRequest(t, r, TaskCreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, TaskCreateResult) }) opts := tasks.CreateOpts{ Type: "import", Input: map[string]interface{}{ "image_properties": map[string]interface{}{ "container_format": "bare", "disk_format": "raw", }, "import_from_format": "raw", "import_from": "https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img", }, } s, err := tasks.Create(fakeclient.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Status, string(tasks.TaskStatusPending)) th.AssertEquals(t, s.CreatedAt, time.Date(2018, 7, 25, 11, 7, 54, 0, time.UTC)) th.AssertEquals(t, s.UpdatedAt, time.Date(2018, 7, 25, 11, 7, 54, 0, time.UTC)) th.AssertEquals(t, s.Self, "/v2/tasks/d550c87d-86ed-430a-9895-c7a1f5ce87e9") th.AssertEquals(t, s.Owner, "fb57277ef2f84a0e85b9018ec2dedbf7") th.AssertEquals(t, s.Message, "") th.AssertEquals(t, s.Type, "import") th.AssertEquals(t, s.ID, "d550c87d-86ed-430a-9895-c7a1f5ce87e9") th.AssertEquals(t, s.Schema, "/v2/schemas/task") th.AssertDeepEquals(t, s.Result, map[string]interface{}(nil)) th.AssertDeepEquals(t, s.Input, map[string]interface{}{ "import_from": "https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img", "import_from_format": "raw", "image_properties": map[string]interface{}{ "container_format": "bare", "disk_format": "raw", }, }) } urls.go000066400000000000000000000021531335005366400331200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/imageservice/v2/taskspackage tasks import ( "net/url" "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/utils" ) const resourcePath = "tasks" func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } func resourceURL(c *gophercloud.ServiceClient, taskID string) string { return c.ServiceURL(resourcePath, taskID) } func listURL(c *gophercloud.ServiceClient) string { return rootURL(c) } func getURL(c *gophercloud.ServiceClient, taskID string) string { return resourceURL(c, taskID) } func createURL(c *gophercloud.ServiceClient) string { return rootURL(c) } func nextPageURL(serviceURL, requestedNext string) (string, error) { base, err := utils.BaseEndpoint(serviceURL) if err != nil { return "", err } requestedNextURL, err := url.Parse(requestedNext) if err != nil { return "", err } base = gophercloud.NormalizeURL(base) nextPath := base + strings.TrimPrefix(requestedNextURL.Path, "/") nextURL, err := url.Parse(nextPath) if err != nil { return "", err } nextURL.RawQuery = requestedNextURL.RawQuery return nextURL.String(), nil } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/000077500000000000000000000000001335005366400276665ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/000077500000000000000000000000001335005366400302145ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/acls/000077500000000000000000000000001335005366400311365ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/acls/doc.go000066400000000000000000000017431335005366400322370ustar00rootroot00000000000000/* Package acls manages acls in the OpenStack Key Manager Service. All functions have a Secret and Container equivalent. Example to Get a Secret's ACL acl, err := acls.GetSecretACL(client, secretID).Extract() if err != nil { panic(err) } fmt.Printf("%v\n", acl) Example to Set a Secret's ACL users := []string{"uuid", "uuid"} iFalse := false setOpts := acls.SetOpts{ Type: "read", users: &users, ProjectAccess: &iFalse, } aclRef, err := acls.SetSecretACL(client, secretID, setOpts).Extract() if err != nil { panic(err) } fmt.Printf("%v\n", aclRef) Example to Update a Secret's ACL users := []string{} setOpts := acls.SetOpts{ Type: "read", users: &users, } aclRef, err := acls.UpdateSecretACL(client, secretID, setOpts).Extract() if err != nil { panic(err) } fmt.Printf("%v\n", aclRef) Example to Delete a Secret's ACL err := acls.DeleteSecretACL(client, secretID).ExtractErr() if err != nil { panci(err) } */ package acls requests.go000066400000000000000000000063161335005366400332670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/aclspackage acls import ( "github.com/gophercloud/gophercloud" ) // GetContainerACL retrieves the ACL of a container. func GetContainerACL(client *gophercloud.ServiceClient, containerID string) (r ACLResult) { _, r.Err = client.Get(containerURL(client, containerID), &r.Body, nil) return } // GetSecretACL retrieves the ACL of a secret. func GetSecretACL(client *gophercloud.ServiceClient, secretID string) (r ACLResult) { _, r.Err = client.Get(secretURL(client, secretID), &r.Body, nil) return } // SetOptsBuilder allows extensions to add additional parameters to the // Set request. type SetOptsBuilder interface { ToACLSetMap() (map[string]interface{}, error) } // SetOpts represents options to set an ACL on a resource. type SetOpts struct { // Type is the type of ACL to set. ie: read. Type string `json:"-" required:"true"` // Users are the list of Keystone user UUIDs. Users *[]string `json:"users,omitempty"` // ProjectAccess toggles if all users in a project can access the resource. ProjectAccess *bool `json:"project-access,omitempty"` } // ToACLSetMap formats a SetOpts into a set request. func (opts SetOpts) ToACLSetMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, opts.Type) } // SetContainerACL will set an ACL on a container. func SetContainerACL(client *gophercloud.ServiceClient, containerID string, opts SetOptsBuilder) (r ACLRefResult) { b, err := opts.ToACLSetMap() if err != nil { r.Err = err return } _, r.Err = client.Put(containerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // SetSecretACL will set an ACL on a secret. func SetSecretACL(client *gophercloud.ServiceClient, secretID string, opts SetOptsBuilder) (r ACLRefResult) { b, err := opts.ToACLSetMap() if err != nil { r.Err = err return } _, r.Err = client.Put(secretURL(client, secretID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // UpdateContainerACL will update an ACL on a container. func UpdateContainerACL(client *gophercloud.ServiceClient, containerID string, opts SetOptsBuilder) (r ACLRefResult) { b, err := opts.ToACLSetMap() if err != nil { r.Err = err return } _, r.Err = client.Patch(containerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // UpdateSecretACL will update an ACL on a secret. func UpdateSecretACL(client *gophercloud.ServiceClient, secretID string, opts SetOptsBuilder) (r ACLRefResult) { b, err := opts.ToACLSetMap() if err != nil { r.Err = err return } _, r.Err = client.Patch(secretURL(client, secretID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // DeleteContainerACL will delete an ACL from a conatiner. func DeleteContainerACL(client *gophercloud.ServiceClient, containerID string) (r DeleteResult) { _, r.Err = client.Delete(containerURL(client, containerID), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // DeleteSecretACL will delete an ACL from a secret. func DeleteSecretACL(client *gophercloud.ServiceClient, secretID string) (r DeleteResult) { _, r.Err = client.Delete(secretURL(client, secretID), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000035231335005366400331120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/aclspackage acls import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" ) // ACL represents an ACL on a resource. type ACL map[string]ACLDetails // ACLDetails represents the details of an ACL. type ACLDetails struct { // Created is when the ACL was created. Created time.Time `json:"-"` // ProjectAccess denotes project-level access of the resource. ProjectAccess bool `json:"project-access"` // Updated is when the ACL was updated Updated time.Time `json:"-"` // Users are the UserIDs who have access to the resource. Users []string `json:"users"` } func (r *ACLDetails) UnmarshalJSON(b []byte) error { type tmp ACLDetails var s struct { tmp Created gophercloud.JSONRFC3339NoZ `json:"created"` Updated gophercloud.JSONRFC3339NoZ `json:"updated"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = ACLDetails(s.tmp) r.Created = time.Time(s.Created) r.Updated = time.Time(s.Updated) return nil } // ACLRef represents an ACL reference. type ACLRef string type commonResult struct { gophercloud.Result } // Extract interprets any commonResult as an ACL. func (r commonResult) Extract() (*ACL, error) { var s *ACL err := r.ExtractInto(&s) return s, err } // ACLResult is the response from a Get operation. Call its Extract method // to interpret it as an ACL. type ACLResult struct { commonResult } // ACLRefResult is the response from a Set or Update operation. Call its // Extract method to interpret it as an ACLRef. type ACLRefResult struct { gophercloud.Result } func (r ACLRefResult) Extract() (*ACLRef, error) { var s struct { ACLRef ACLRef `json:"acl_ref"` } err := r.ExtractInto(&s) return &s.ACLRef, err } // DeleteResult is the response from a Delete operation. Call its ExtractErr to // determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/acls/testing/000077500000000000000000000000001335005366400326135ustar00rootroot00000000000000fixtures.go000066400000000000000000000125051335005366400347370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/acls/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/acls" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const GetResponse = ` { "read": { "created": "2018-06-22T17:54:24", "project-access": false, "updated": "2018-06-22T17:54:24", "users": [ "GG27dVwR9gBMnsOaRoJ1DFJmZfdVjIdW" ] } }` const SetRequest = ` { "read": { "project-access": false, "users": [ "GG27dVwR9gBMnsOaRoJ1DFJmZfdVjIdW" ] } }` const SecretSetResponse = ` { "acl_ref": "http://barbican:9311/v1/secrets/4befede0-fbde-4480-982c-b160c1014a47/acl" }` const ContainerSetResponse = ` { "acl_ref": "http://barbican:9311/v1/containers/4befede0-fbde-4480-982c-b160c1014a47/acl" }` var ExpectedACL = acls.ACL{ "read": acls.ACLDetails{ Created: time.Date(2018, 6, 22, 17, 54, 24, 0, time.UTC), ProjectAccess: false, Updated: time.Date(2018, 6, 22, 17, 54, 24, 0, time.UTC), Users: []string{ "GG27dVwR9gBMnsOaRoJ1DFJmZfdVjIdW", }, }, } var ExpectedSecretACLRef = acls.ACLRef("http://barbican:9311/v1/secrets/4befede0-fbde-4480-982c-b160c1014a47/acl") var ExpectedContainerACLRef = acls.ACLRef("http://barbican:9311/v1/containers/4befede0-fbde-4480-982c-b160c1014a47/acl") const UpdateRequest = ` { "read": { "users": [] } }` // HandleGetSecretACLSuccessfully creates an HTTP handler at `/secrets/uuid/acl` // on the test handler mux that responds with an acl. func HandleGetSecretACLSuccessfully(t *testing.T) { th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/acl", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetResponse) }) } // HandleGetContainerACLSuccessfully creates an HTTP handler at `/secrets/uuid/acl` // on the test handler mux that responds with an acl. func HandleGetContainerACLSuccessfully(t *testing.T) { th.Mux.HandleFunc("/containers/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/acl", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetResponse) }) } // HandleSetSecretACLSuccessfully creates an HTTP handler at `/secrets` on the // test handler mux that tests secret creation. func HandleSetSecretACLSuccessfully(t *testing.T) { th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/acl", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, SetRequest) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, SecretSetResponse) }) } // HandleSetContainerACLSuccessfully creates an HTTP handler at `/secrets` on the // test handler mux that tests secret creation. func HandleSetContainerACLSuccessfully(t *testing.T) { th.Mux.HandleFunc("/containers/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/acl", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, SetRequest) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ContainerSetResponse) }) } // HandleUpdateSecretACLSuccessfully creates an HTTP handler at `/secrets` on the // test handler mux that tests secret creation. func HandleUpdateSecretACLSuccessfully(t *testing.T) { th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/acl", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, SecretSetResponse) }) } // HandleUpdateContainerACLSuccessfully creates an HTTP handler at `/secrets` on the // test handler mux that tests secret creation. func HandleUpdateContainerACLSuccessfully(t *testing.T) { th.Mux.HandleFunc("/containers/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/acl", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ContainerSetResponse) }) } // HandleDeleteSecretACLSuccessfully creates an HTTP handler at `/secrets` on the // test handler mux that tests secret deletion. func HandleDeleteSecretACLSuccessfully(t *testing.T) { th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/acl", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusOK) }) } // HandleDeleteContainerACLSuccessfully creates an HTTP handler at `/secrets` on the // test handler mux that tests secret deletion. func HandleDeleteContainerACLSuccessfully(t *testing.T) { th.Mux.HandleFunc("/containers/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/acl", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusOK) }) } requests_test.go000066400000000000000000000061331335005366400360000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/acls/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/acls" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestGetSecretACL(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSecretACLSuccessfully(t) actual, err := acls.GetSecretACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedACL, *actual) } func TestGetContainerACL(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetContainerACLSuccessfully(t) actual, err := acls.GetContainerACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedACL, *actual) } func TestSetSecretACL(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleSetSecretACLSuccessfully(t) users := []string{"GG27dVwR9gBMnsOaRoJ1DFJmZfdVjIdW"} iFalse := false setOpts := acls.SetOpts{ Type: "read", Users: &users, ProjectAccess: &iFalse, } actual, err := acls.SetSecretACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", setOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedSecretACLRef, *actual) } func TestSetContainerACL(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleSetContainerACLSuccessfully(t) users := []string{"GG27dVwR9gBMnsOaRoJ1DFJmZfdVjIdW"} iFalse := false setOpts := acls.SetOpts{ Type: "read", Users: &users, ProjectAccess: &iFalse, } actual, err := acls.SetContainerACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", setOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedContainerACLRef, *actual) } func TestDeleteSecretACL(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSecretACLSuccessfully(t) res := acls.DeleteSecretACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c") th.AssertNoErr(t, res.Err) } func TestDeleteContainerACL(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteContainerACLSuccessfully(t) res := acls.DeleteContainerACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c") th.AssertNoErr(t, res.Err) } func TestUpdateSecretACL(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateSecretACLSuccessfully(t) newUsers := []string{} updateOpts := acls.SetOpts{ Type: "read", Users: &newUsers, } actual, err := acls.UpdateSecretACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedSecretACLRef, *actual) } func TestUpdateContainerACL(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateContainerACLSuccessfully(t) newUsers := []string{} updateOpts := acls.SetOpts{ Type: "read", Users: &newUsers, } actual, err := acls.UpdateContainerACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedContainerACLRef, *actual) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/acls/urls.go000066400000000000000000000005201335005366400324470ustar00rootroot00000000000000package acls import "github.com/gophercloud/gophercloud" func containerURL(client *gophercloud.ServiceClient, containerID string) string { return client.ServiceURL("containers", containerID, "acl") } func secretURL(client *gophercloud.ServiceClient, secretID string) string { return client.ServiceURL("secrets", secretID, "acl") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/containers/000077500000000000000000000000001335005366400323615ustar00rootroot00000000000000doc.go000066400000000000000000000032521335005366400334000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/containers/* Package containers manages and retrieves containers in the OpenStack Key Manager Service. Example to List Containers allPages, err := containers.List(client, nil).AllPages() if err != nil { panic(err) } allContainers, err := containers.ExtractContainers(allPages) if err != nil { panic(err) } for _, v := range allContainers { fmt.Printf("%v\n", v) } Example to Create a Container createOpts := containers.CreateOpts{ Type: containers.GenericContainer, Name: "mycontainer", SecretRefs: []containers.SecretRef{ { Name: secret.Name, SecretRef: secret.SecretRef, }, }, } container, err := containers.Create(client, createOpts).Extract() if err != nil { panic(err) } fmt.Printf("%v\n", container) Example to Delete a Container err := containers.Delete(client, containerID).ExtractErr() if err != nil { panic(err) } Example to List Consumers of a Container allPages, err := containers.ListConsumers(client, containerID, nil).AllPages() if err != nil { panic(err) } allConsumers, err := containers.ExtractConsumers(allPages) if err != nil { panic(err) } fmt.Printf("%v\n", allConsumers) Example to Create a Consumer of a Container createOpts := containers.CreateConsumerOpts{ Name: "jdoe", URL: "http://example.com", } container, err := containers.CreateConsumer(client, containerID, createOpts).Extract() if err != nil { panic(err) } Example to Delete a Consumer of a Container deleteOpts := containers.DeleteConsumerOpts{ Name: "jdoe", URL: "http://example.com", } container, err := containers.DeleteConsumer(client, containerID, deleteOpts).Extract() if err != nil { panic(err) } */ package containers requests.go000066400000000000000000000143311335005366400345060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/containerspackage containers import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ContainerType represents the valid types of containers. type ContainerType string const ( GenericContainer ContainerType = "generic" RSAContainer ContainerType = "rsa" CertificateContainer ContainerType = "certificate" ) // ListOptsBuilder allows extensions to add additional parameters to // the List request type ListOptsBuilder interface { ToContainerListQuery() (string, error) } // ListOpts provides options to filter the List results. type ListOpts struct { // Limit is the amount of containers to retrieve. Limit int `q:"limit"` // Name is the name of the container Name string `q:"name"` // Offset is the index within the list to retrieve. Offset int `q:"offset"` } // ToContainerListQuery formats a ListOpts into a query string. func (opts ListOpts) ToContainerListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List retrieves a list of containers. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToContainerListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ContainerPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves details of a container. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { ToContainerCreateMap() (map[string]interface{}, error) } // CreateOpts provides options used to create a container. type CreateOpts struct { // Type represents the type of container. Type ContainerType `json:"type" required:"true"` // Name is the name of the container. Name string `json:"name"` // SecretRefs is a list of secret refs for the container. SecretRefs []SecretRef `json:"secret_refs"` } // ToContainerCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToContainerCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // Create creates a new container. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToContainerCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) return } // Delete deletes a container. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // ListConsumersOptsBuilder allows extensions to add additional parameters to // the ListConsumers request type ListConsumersOptsBuilder interface { ToContainerListConsumersQuery() (string, error) } // ListConsumersOpts provides options to filter the List results. type ListConsumersOpts struct { // Limit is the amount of consumers to retrieve. Limit int `q:"limit"` // Offset is the index within the list to retrieve. Offset int `q:"offset"` } // ToContainerListConsumersQuery formats a ListConsumersOpts into a query // string. func (opts ListOpts) ToContainerListConsumersQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // ListConsumers retrieves a list of consumers from a container. func ListConsumers(client *gophercloud.ServiceClient, containerID string, opts ListConsumersOptsBuilder) pagination.Pager { url := listConsumersURL(client, containerID) if opts != nil { query, err := opts.ToContainerListConsumersQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ConsumerPage{pagination.LinkedPageBase{PageResult: r}} }) } // CreateConsumerOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateConsumerOptsBuilder interface { ToContainerConsumerCreateMap() (map[string]interface{}, error) } // CreateConsumerOpts provides options used to create a container. type CreateConsumerOpts struct { // Name is the name of the consumer. Name string `json:"name"` // URL is the URL to the consumer resource. URL string `json:"URL"` } // ToContainerConsumerCreateMap formats a CreateConsumerOpts into a create // request. func (opts CreateConsumerOpts) ToContainerConsumerCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // CreateConsumer creates a new consumer. func CreateConsumer(client *gophercloud.ServiceClient, containerID string, opts CreateConsumerOptsBuilder) (r CreateConsumerResult) { b, err := opts.ToContainerConsumerCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createConsumerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // DeleteConsumerOptsBuilder allows extensions to add additional parameters to // the Delete request. type DeleteConsumerOptsBuilder interface { ToContainerConsumerDeleteMap() (map[string]interface{}, error) } // DeleteConsumerOpts represents options used for deleting a consumer. type DeleteConsumerOpts struct { // Name is the name of the consumer. Name string `json:"name"` // URL is the URL to the consumer resource. URL string `json:"URL"` } // ToContainerConsumerDeleteMap formats a DeleteConsumerOpts into a create // request. func (opts DeleteConsumerOpts) ToContainerConsumerDeleteMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // DeleteConsumer deletes a consumer. func DeleteConsumer(client *gophercloud.ServiceClient, containerID string, opts DeleteConsumerOptsBuilder) (r DeleteConsumerResult) { url := deleteConsumerURL(client, containerID) b, err := opts.ToContainerConsumerDeleteMap() if err != nil { r.Err = err return } _, r.Err = client.Request("DELETE", url, &gophercloud.RequestOpts{ JSONBody: b, JSONResponse: &r.Body, OkCodes: []int{200}, }) return } results.go000066400000000000000000000131151335005366400343330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/containerspackage containers import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Container represents a container in the key manager service. type Container struct { // Consumers are the consumers of the container. Consumers []ConsumerRef `json:"consumers"` // ContainerRef is the URL to the container ContainerRef string `json:"container_ref"` // Created is the date the container was created. Created time.Time `json:"-"` // CreatorID is the creator of the container. CreatorID string `json:"creator_id"` // Name is the name of the container. Name string `json:"name"` // SecretRefs are the secret references of the container. SecretRefs []SecretRef `json:"secret_refs"` // Status is the status of the container. Status string `json:"status"` // Type is the type of container. Type string `json:"type"` // Updated is the date the container was updated. Updated time.Time `json:"-"` } func (r *Container) UnmarshalJSON(b []byte) error { type tmp Container var s struct { tmp Created gophercloud.JSONRFC3339NoZ `json:"created"` Updated gophercloud.JSONRFC3339NoZ `json:"updated"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Container(s.tmp) r.Created = time.Time(s.Created) r.Updated = time.Time(s.Updated) return nil } // ConsumerRef represents a consumer reference in a container. type ConsumerRef struct { // Name is the name of the consumer. Name string `json:"name"` // URL is the URL to the consumer resource. URL string `json:"url"` } // SecretRef is a reference to a secret. type SecretRef struct { SecretRef string `json:"secret_ref"` Name string `json:"name"` } type commonResult struct { gophercloud.Result } // Extract interprets any commonResult as a Container. func (r commonResult) Extract() (*Container, error) { var s *Container err := r.ExtractInto(&s) return s, err } // GetResult is the response from a Get operation. Call its Extract method // to interpret it as a container. type GetResult struct { commonResult } // CreateResult is the response from a Create operation. Call its Extract method // to interpret it as a container. type CreateResult struct { commonResult } // DeleteResult is the response from a Delete operation. Call its ExtractErr to // determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // ContainerPage is a single page of container results. type ContainerPage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a page of Container contains any results. func (r ContainerPage) IsEmpty() (bool, error) { containers, err := ExtractContainers(r) return len(containers) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (r ContainerPage) NextPageURL() (string, error) { var s struct { Next string `json:"next"` Previous string `json:"previous"` } err := r.ExtractInto(&s) if err != nil { return "", err } return s.Next, err } // ExtractContainers returns a slice of Containers contained in a single page of // results. func ExtractContainers(r pagination.Page) ([]Container, error) { var s struct { Containers []Container `json:"containers"` } err := (r.(ContainerPage)).ExtractInto(&s) return s.Containers, err } // Consumer represents a consumer in a container. type Consumer struct { // Created is the date the container was created. Created time.Time `json:"-"` // Name is the name of the container. Name string `json:"name"` // Status is the status of the container. Status string `json:"status"` // Updated is the date the container was updated. Updated time.Time `json:"-"` // URL is the url to the consumer. URL string `json:"url"` } func (r *Consumer) UnmarshalJSON(b []byte) error { type tmp Consumer var s struct { tmp Created gophercloud.JSONRFC3339NoZ `json:"created"` Updated gophercloud.JSONRFC3339NoZ `json:"updated"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Consumer(s.tmp) r.Created = time.Time(s.Created) r.Updated = time.Time(s.Updated) return nil } type consumerResult struct { gophercloud.Result } // Extract interprets any consumerResult as a Consumer. func (r consumerResult) Extract() (*Consumer, error) { var s *Consumer err := r.ExtractInto(&s) return s, err } // CreateConsumerResult is the response from a CreateConsumer operation. // Call its Extract method to interpret it as a container. type CreateConsumerResult struct { // This is not a typo. commonResult } // DeleteConsumerResult is the response from a DeleteConsumer operation. // Call its Extract to interpret it as a container. type DeleteConsumerResult struct { // This is not a typo. commonResult } // ConsumerPage is a single page of consumer results. type ConsumerPage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a page of consumers contains any results. func (r ConsumerPage) IsEmpty() (bool, error) { consumers, err := ExtractConsumers(r) return len(consumers) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (r ConsumerPage) NextPageURL() (string, error) { var s struct { Next string `json:"next"` Previous string `json:"previous"` } err := r.ExtractInto(&s) if err != nil { return "", err } return s.Next, err } // ExtractConsumers returns a slice of Consumers contained in a single page of // results. func ExtractConsumers(r pagination.Page) ([]Consumer, error) { var s struct { Consumers []Consumer `json:"consumers"` } err := (r.(ConsumerPage)).ExtractInto(&s) return s.Consumers, err } testing/000077500000000000000000000000001335005366400337575ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/containersfixtures.go000066400000000000000000000237371335005366400361730ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/containers/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/containers" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListResponse provides a single page of container results. const ListResponse = ` { "containers": [ { "consumers": [], "container_ref": "http://barbican:9311/v1/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0", "created": "2018-06-21T21:28:37", "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", "name": "mycontainer", "secret_refs": [ { "name": "mysecret", "secret_ref": "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c" } ], "status": "ACTIVE", "type": "generic", "updated": "2018-06-21T21:28:37" }, { "consumers": [], "container_ref": "http://barbican:9311/v1/containers/47b20e73-335b-4867-82dc-3796524d5e20", "created": "2018-06-21T21:30:09", "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", "name": "anothercontainer", "secret_refs": [ { "name": "another", "secret_ref": "http://barbican:9311/v1/secrets/1b12b69a-8822-442e-a303-da24ade648ac" } ], "status": "ACTIVE", "type": "generic", "updated": "2018-06-21T21:30:09" } ], "total": 2 }` // GetResponse provides a Get result. const GetResponse = ` { "consumers": [], "container_ref": "http://barbican:9311/v1/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0", "created": "2018-06-21T21:28:37", "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", "name": "mycontainer", "secret_refs": [ { "name": "mysecret", "secret_ref": "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c" } ], "status": "ACTIVE", "type": "generic", "updated": "2018-06-21T21:28:37" }` // CreateRequest provides the input to a Create request. const CreateRequest = ` { "name": "mycontainer", "secret_refs": [ { "name": "mysecret", "secret_ref": "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c" } ], "type": "generic" }` // CreateResponse is the response of a Create request. const CreateResponse = ` { "consumers": [], "container_ref": "http://barbican:9311/v1/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0", "created": "2018-06-21T21:28:37", "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", "name": "mycontainer", "secret_refs": [ { "name": "mysecret", "secret_ref": "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c" } ], "status": "ACTIVE", "type": "generic", "updated": "2018-06-21T21:28:37" }` // FirstContainer is the first resource in the List request. var FirstContainer = containers.Container{ Consumers: []containers.ConsumerRef{}, ContainerRef: "http://barbican:9311/v1/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0", Created: time.Date(2018, 6, 21, 21, 28, 37, 0, time.UTC), CreatorID: "5c70d99f4a8641c38f8084b32b5e5c0e", Name: "mycontainer", SecretRefs: []containers.SecretRef{ { Name: "mysecret", SecretRef: "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", }, }, Status: "ACTIVE", Type: "generic", Updated: time.Date(2018, 6, 21, 21, 28, 37, 0, time.UTC), } // SecondContainer is the second resource in the List request. var SecondContainer = containers.Container{ Consumers: []containers.ConsumerRef{}, ContainerRef: "http://barbican:9311/v1/containers/47b20e73-335b-4867-82dc-3796524d5e20", Created: time.Date(2018, 6, 21, 21, 30, 9, 0, time.UTC), CreatorID: "5c70d99f4a8641c38f8084b32b5e5c0e", Name: "anothercontainer", SecretRefs: []containers.SecretRef{ { Name: "another", SecretRef: "http://barbican:9311/v1/secrets/1b12b69a-8822-442e-a303-da24ade648ac", }, }, Status: "ACTIVE", Type: "generic", Updated: time.Date(2018, 6, 21, 21, 30, 9, 0, time.UTC), } // ExpectedContainersSlice is the slice of containers expected to be returned from ListResponse. var ExpectedContainersSlice = []containers.Container{FirstContainer, SecondContainer} const ListConsumersResponse = ` { "consumers": [ { "URL": "http://example.com", "created": "2018-06-22T16:26:25", "name": "CONSUMER-LZILN1zq", "status": "ACTIVE", "updated": "2018-06-22T16:26:25" } ], "total": 1 }` // ExpectedConsumer is the expected result of a consumer retrieval. var ExpectedConsumer = containers.Consumer{ URL: "http://example.com", Created: time.Date(2018, 6, 22, 16, 26, 25, 0, time.UTC), Name: "CONSUMER-LZILN1zq", Status: "ACTIVE", Updated: time.Date(2018, 6, 22, 16, 26, 25, 0, time.UTC), } // ExpectedConsumersSlice is an expected slice of consumers. var ExpectedConsumersSlice = []containers.Consumer{ExpectedConsumer} const CreateConsumerRequest = ` { "URL": "http://example.com", "name": "CONSUMER-LZILN1zq" }` const CreateConsumerResponse = ` { "consumers": [ { "URL": "http://example.com", "name": "CONSUMER-LZILN1zq" } ], "container_ref": "http://barbican:9311/v1/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0", "created": "2018-06-21T21:28:37", "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", "name": "mycontainer", "secret_refs": [ { "name": "mysecret", "secret_ref": "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c" } ], "status": "ACTIVE", "type": "generic", "updated": "2018-06-21T21:28:37" }` // ExpectedCreatedConsumer is the expected result of adding a consumer. var ExpectedCreatedConsumer = containers.Container{ Consumers: []containers.ConsumerRef{ { Name: "CONSUMER-LZILN1zq", URL: "http://example.com", }, }, ContainerRef: "http://barbican:9311/v1/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0", Created: time.Date(2018, 6, 21, 21, 28, 37, 0, time.UTC), CreatorID: "5c70d99f4a8641c38f8084b32b5e5c0e", Name: "mycontainer", SecretRefs: []containers.SecretRef{ { Name: "mysecret", SecretRef: "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", }, }, Status: "ACTIVE", Type: "generic", Updated: time.Date(2018, 6, 21, 21, 28, 37, 0, time.UTC), } const DeleteConsumerRequest = ` { "URL": "http://example.com", "name": "CONSUMER-LZILN1zq" }` // HandleListContainersSuccessfully creates an HTTP handler at `/containers` on the // test handler mux that responds with a list of two containers. func HandleListContainersSuccessfully(t *testing.T) { th.Mux.HandleFunc("/containers", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListResponse) }) } // HandleGetContainerSuccessfully creates an HTTP handler at `/containers` on the // test handler mux that responds with a single resource. func HandleGetContainerSuccessfully(t *testing.T) { th.Mux.HandleFunc("/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetResponse) }) } // HandleCreateContainerSuccessfully creates an HTTP handler at `/containers` on the // test handler mux that tests resource creation. func HandleCreateContainerSuccessfully(t *testing.T) { th.Mux.HandleFunc("/containers", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, GetResponse) }) } // HandleDeleteContainerSuccessfully creates an HTTP handler at `/containers` on the // test handler mux that tests resource deletion. func HandleDeleteContainerSuccessfully(t *testing.T) { th.Mux.HandleFunc("/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleListConsumersSuccessfully creates an HTTP handler at // `/containers/uuid/consumers` on the test handler mux that responds with // a list of consumers. func HandleListConsumersSuccessfully(t *testing.T) { th.Mux.HandleFunc("/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0/consumers", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListConsumersResponse) }) } // HandleCreateConsumerSuccessfully creates an HTTP handler at // `/containers/uuid/consumers` on the test handler mux that tests resource // creation. func HandleCreateConsumerSuccessfully(t *testing.T) { th.Mux.HandleFunc("/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0/consumers", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, CreateConsumerRequest) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, CreateConsumerResponse) }) } // HandleDeleteConsumerSuccessfully creates an HTTP handler at // `/containers/uuid/consumers` on the test handler mux that tests resource // deletion. func HandleDeleteConsumerSuccessfully(t *testing.T) { th.Mux.HandleFunc("/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0/consumers", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, CreateConsumerRequest) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetResponse) }) } requests_test.go000066400000000000000000000076061335005366400372310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/containers/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/containers" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListContainers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListContainersSuccessfully(t) count := 0 err := containers.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := containers.ExtractContainers(page) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedContainersSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, count, 1) } func TestListContainersAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListContainersSuccessfully(t) allPages, err := containers.List(client.ServiceClient(), nil).AllPages() th.AssertNoErr(t, err) actual, err := containers.ExtractContainers(allPages) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedContainersSlice, actual) } func TestGetContainer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetContainerSuccessfully(t) actual, err := containers.Get(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, FirstContainer, *actual) } func TestCreateContainer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateContainerSuccessfully(t) createOpts := containers.CreateOpts{ Type: containers.GenericContainer, Name: "mycontainer", SecretRefs: []containers.SecretRef{ { Name: "mysecret", SecretRef: "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", }, }, } actual, err := containers.Create(client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, FirstContainer, *actual) } func TestDeleteContainer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteContainerSuccessfully(t) res := containers.Delete(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0") th.AssertNoErr(t, res.Err) } func TestListConsumers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListConsumersSuccessfully(t) count := 0 err := containers.ListConsumers(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0", nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := containers.ExtractConsumers(page) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedConsumersSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, count, 1) } func TestListConsumersAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListConsumersSuccessfully(t) allPages, err := containers.ListConsumers(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0", nil).AllPages() th.AssertNoErr(t, err) actual, err := containers.ExtractConsumers(allPages) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedConsumersSlice, actual) } func TestCreateConsumer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateConsumerSuccessfully(t) createOpts := containers.CreateConsumerOpts{ Name: "CONSUMER-LZILN1zq", URL: "http://example.com", } actual, err := containers.CreateConsumer(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0", createOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCreatedConsumer, *actual) } func TestDeleteConsumer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteConsumerSuccessfully(t) deleteOpts := containers.DeleteConsumerOpts{ Name: "CONSUMER-LZILN1zq", URL: "http://example.com", } actual, err := containers.DeleteConsumer(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0", deleteOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, FirstContainer, *actual) } urls.go000066400000000000000000000016161335005366400336220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/containerspackage containers import "github.com/gophercloud/gophercloud" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("containers") } func getURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("containers", id) } func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("containers") } func deleteURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("containers", id) } func listConsumersURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("containers", id, "consumers") } func createConsumerURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("containers", id, "consumers") } func deleteConsumerURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("containers", id, "consumers") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/orders/000077500000000000000000000000001335005366400315125ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/orders/doc.go000066400000000000000000000014061335005366400326070ustar00rootroot00000000000000/* Package orders manages and retrieves orders in the OpenStack Key Manager Service. Example to List Orders allPages, err := orders.List(client, nil).AllPages() if err != nil { panic(err) } allOrders, err := orders.ExtractOrders(allPages) if err != nil { panic(err) } fmt.Printf("%v\n", allOrders) Example to Create a Order createOpts := orders.CreateOpts{ Type: orders.KeyOrder, Meta: orders.MetaOpts{ Name: "order-name", Algorithm: "aes", BitLength: 256, Mode: "cbc", }, } order, err := orders.Create(client, createOpts).Extract() if err != nil { panic(err) } fmt.Printf("%v\n", order) Example to Delete a Order err := orders.Delete(client, orderID).ExtractErr() if err != nil { panic(err) } */ package orders requests.go000066400000000000000000000065501335005366400336430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/orderspackage orders import ( "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // OrderType represents the valid types of orders. type OrderType string const ( KeyOrder OrderType = "key" AsymmetricOrder OrderType = "asymmetric" ) // ListOptsBuilder allows extensions to add additional parameters to // the List request type ListOptsBuilder interface { ToOrderListQuery() (string, error) } // ListOpts provides options to filter the List results. type ListOpts struct { // Limit is the amount of containers to retrieve. Limit int `q:"limit"` // Offset is the index within the list to retrieve. Offset int `q:"offset"` } // ToOrderListQuery formats a ListOpts into a query string. func (opts ListOpts) ToOrderListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List retrieves a list of orders. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToOrderListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return OrderPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves details of a orders. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { ToOrderCreateMap() (map[string]interface{}, error) } // MetaOpts represents options used for creating an order. type MetaOpts struct { // Algorithm is the algorithm of the secret. Algorithm string `json:"algorithm"` // BitLength is the bit length of the secret. BitLength int `json:"bit_length"` // Expiration is the expiration date of the order. Expiration *time.Time `json:"-"` // Mode is the mode of the secret. Mode string `json:"mode"` // Name is the name of the secret. Name string `json:"name,omitempty"` // PayloadContentType is the content type of the secret payload. PayloadContentType string `json:"payload_content_type,omitempty"` } // CreateOpts provides options used to create a orders. type CreateOpts struct { // Type is the type of order to create. Type OrderType `json:"type"` // Meta contains secrets data to create a secret. Meta MetaOpts `json:"meta"` } // ToOrderCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToOrderCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } if opts.Meta.Expiration != nil { meta := b["meta"].(map[string]interface{}) meta["expiration"] = opts.Meta.Expiration.Format(gophercloud.RFC3339NoZ) b["meta"] = meta } return b, nil } // Create creates a new orders. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToOrderCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return } // Delete deletes a orders. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } results.go000066400000000000000000000075751335005366400335010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/orderspackage orders import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Order represents an order in the key manager service. type Order struct { // ContainerRef is the container URL. ContainerRef string `json:"container_ref"` // Created is when the order was created. Created time.Time `json:"-"` // CreatorID is the creator of the order. CreatorID string `json:"creator_id"` // ErrorReason is the reason of the error. ErrorReason string `json:"error_reason"` // ErrorStatusCode is the error status code. ErrorStatusCode string `json:"error_status_code"` // OrderRef is the order URL. OrderRef string `json:"order_ref"` // Meta is secret data about the order. Meta Meta `json:"meta"` // SecretRef is the secret URL. SecretRef string `json:"secret_ref"` // Status is the status of the order. Status string `json:"status"` // SubStatus is the status of the order. SubStatus string `json:"sub_status"` // SubStatusMessage is the message of the sub status. SubStatusMessage string `json:"sub_status_message"` // Type is the order type. Type string `json:"type"` // Updated is when the order was updated. Updated time.Time `json:"-"` } func (r *Order) UnmarshalJSON(b []byte) error { type tmp Order var s struct { tmp Created gophercloud.JSONRFC3339NoZ `json:"created"` Updated gophercloud.JSONRFC3339NoZ `json:"updated"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Order(s.tmp) r.Created = time.Time(s.Created) r.Updated = time.Time(s.Updated) return nil } type Meta struct { // Algorithm is the algorithm of the secret. Algorithm string `json:"algorithm"` // BitLength is the bit length of the secret. BitLength int `json:"bit_length"` // Expiration is the expiration date of the order. Expiration time.Time `json:"-"` // Mode is the mode of the secret. Mode string `json:"mode"` // Name is the name of the secret. Name string `json:"name"` // PayloadContentType is the content type of the secret payload. PayloadContentType string `json:"payload_content_type"` } func (r *Meta) UnmarshalJSON(b []byte) error { type tmp Meta var s struct { tmp Expiration gophercloud.JSONRFC3339NoZ `json:"expiration"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Meta(s.tmp) r.Expiration = time.Time(s.Expiration) return nil } type commonResult struct { gophercloud.Result } // GetResult is the response from a Get operation. Call its Extract method // to interpret it as a orders. type GetResult struct { commonResult } // CreateResult is the response from a Create operation. Call its Extract method // to interpret it as a orders. type CreateResult struct { commonResult } // DeleteResult is the response from a Delete operation. Call its ExtractErr to // determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // OrderPage is a single page of orders results. type OrderPage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a page of ordersS contains any results. func (r OrderPage) IsEmpty() (bool, error) { orders, err := ExtractOrders(r) return len(orders) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (r OrderPage) NextPageURL() (string, error) { var s struct { Next string `json:"next"` Previous string `json:"previous"` } err := r.ExtractInto(&s) if err != nil { return "", err } return s.Next, err } // ExtractOrders returns a slice of Orders contained in a single page of // results. func ExtractOrders(r pagination.Page) ([]Order, error) { var s struct { Orders []Order `json:"orders"` } err := (r.(OrderPage)).ExtractInto(&s) return s.Orders, err } // Extract interprets any commonResult as a Order. func (r commonResult) Extract() (*Order, error) { var s *Order err := r.ExtractInto(&s) return s, err } testing/000077500000000000000000000000001335005366400331105ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/ordersfixtures.go000066400000000000000000000137401335005366400353150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/orders/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/orders" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListResponse provides a single page of RESOURCE results. const ListResponse = ` { "orders": [ { "created": "2018-06-22T05:05:43", "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", "meta": { "algorithm": "aes", "bit_length": 256, "expiration": null, "mode": "cbc", "name": null, "payload_content_type": "application/octet-stream" }, "order_ref": "http://barbican:9311/v1/orders/46f73695-82bb-447a-bf96-6635f0fb0ce7", "secret_ref": "http://barbican:9311/v1/secrets/22dfef44-1046-4549-a86d-95af462e8fa0", "status": "ACTIVE", "sub_status": "Unknown", "sub_status_message": "Unknown", "type": "key", "updated": "2018-06-22T05:05:43" }, { "created": "2018-06-22T05:08:15", "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", "meta": { "algorithm": "aes", "bit_length": 256, "expiration": null, "mode": "cbc", "name": null, "payload_content_type": "application/octet-stream" }, "order_ref": "http://barbican:9311/v1/orders/07fba88b-3dcf-44e3-a4a3-0bad7f56f01c", "secret_ref": "http://barbican:9311/v1/secrets/a31ad551-1aa5-4ba0-810e-0865163e0fa9", "status": "ACTIVE", "sub_status": "Unknown", "sub_status_message": "Unknown", "type": "key", "updated": "2018-06-22T05:08:15" } ], "total": 2 }` // GetResponse provides a Get result. const GetResponse = ` { "created": "2018-06-22T05:08:15", "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", "meta": { "algorithm": "aes", "bit_length": 256, "expiration": null, "mode": "cbc", "name": null, "payload_content_type": "application/octet-stream" }, "order_ref": "http://barbican:9311/v1/orders/07fba88b-3dcf-44e3-a4a3-0bad7f56f01c", "secret_ref": "http://barbican:9311/v1/secrets/a31ad551-1aa5-4ba0-810e-0865163e0fa9", "status": "ACTIVE", "sub_status": "Unknown", "sub_status_message": "Unknown", "type": "key", "updated": "2018-06-22T05:08:15" } ` // CreateRequest provides the input to a Create request. const CreateRequest = ` { "meta": { "algorithm": "aes", "bit_length": 256, "mode": "cbc", "payload_content_type": "application/octet-stream" }, "type": "key" }` // FirstOrder is the first resource in the List request. var FirstOrder = orders.Order{ Created: time.Date(2018, 6, 22, 5, 5, 43, 0, time.UTC), CreatorID: "5c70d99f4a8641c38f8084b32b5e5c0e", Meta: orders.Meta{ Algorithm: "aes", BitLength: 256, Mode: "cbc", PayloadContentType: "application/octet-stream", }, OrderRef: "http://barbican:9311/v1/orders/46f73695-82bb-447a-bf96-6635f0fb0ce7", SecretRef: "http://barbican:9311/v1/secrets/22dfef44-1046-4549-a86d-95af462e8fa0", Status: "ACTIVE", SubStatus: "Unknown", SubStatusMessage: "Unknown", Type: "key", Updated: time.Date(2018, 6, 22, 5, 5, 43, 0, time.UTC), } // SecondOrder is the second resource in the List request. var SecondOrder = orders.Order{ Created: time.Date(2018, 6, 22, 5, 8, 15, 0, time.UTC), CreatorID: "5c70d99f4a8641c38f8084b32b5e5c0e", Meta: orders.Meta{ Algorithm: "aes", BitLength: 256, Mode: "cbc", PayloadContentType: "application/octet-stream", }, OrderRef: "http://barbican:9311/v1/orders/07fba88b-3dcf-44e3-a4a3-0bad7f56f01c", SecretRef: "http://barbican:9311/v1/secrets/a31ad551-1aa5-4ba0-810e-0865163e0fa9", Status: "ACTIVE", SubStatus: "Unknown", SubStatusMessage: "Unknown", Type: "key", Updated: time.Date(2018, 6, 22, 5, 8, 15, 0, time.UTC), } // ExpectedOrdersSlice is the slice of orders expected to be returned from ListResponse. var ExpectedOrdersSlice = []orders.Order{FirstOrder, SecondOrder} // HandleListOrdersSuccessfully creates an HTTP handler at `/orders` on the // test handler mux that responds with a list of two orders. func HandleListOrdersSuccessfully(t *testing.T) { th.Mux.HandleFunc("/orders", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListResponse) }) } // HandleGetOrderSuccessfully creates an HTTP handler at `/orders` on the // test handler mux that responds with a single resource. func HandleGetOrderSuccessfully(t *testing.T) { th.Mux.HandleFunc("/orders/46f73695-82bb-447a-bf96-6635f0fb0ce7", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetResponse) }) } // HandleCreateOrderSuccessfully creates an HTTP handler at `/orders` on the // test handler mux that tests resource creation. func HandleCreateOrderSuccessfully(t *testing.T) { th.Mux.HandleFunc("/orders", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, GetResponse) }) } // HandleDeleteOrderSuccessfully creates an HTTP handler at `/orders` on the // test handler mux that tests resource deletion. func HandleDeleteOrderSuccessfully(t *testing.T) { th.Mux.HandleFunc("/orders/46f73695-82bb-447a-bf96-6635f0fb0ce7", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000040031335005366400363460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/orders/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/orders" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListOrders(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListOrdersSuccessfully(t) count := 0 err := orders.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := orders.ExtractOrders(page) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedOrdersSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, count, 1) } func TestListOrdersAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListOrdersSuccessfully(t) allPages, err := orders.List(client.ServiceClient(), nil).AllPages() th.AssertNoErr(t, err) actual, err := orders.ExtractOrders(allPages) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedOrdersSlice, actual) } func TestGetOrder(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetOrderSuccessfully(t) actual, err := orders.Get(client.ServiceClient(), "46f73695-82bb-447a-bf96-6635f0fb0ce7").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, SecondOrder, *actual) } func TestCreateOrder(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateOrderSuccessfully(t) createOpts := orders.CreateOpts{ Type: orders.KeyOrder, Meta: orders.MetaOpts{ Algorithm: "aes", BitLength: 256, Mode: "cbc", PayloadContentType: "application/octet-stream", }, } actual, err := orders.Create(client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, SecondOrder, *actual) } func TestDeleteOrder(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteOrderSuccessfully(t) res := orders.Delete(client.ServiceClient(), "46f73695-82bb-447a-bf96-6635f0fb0ce7") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000007351335005366400327540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/orderspackage orders import "github.com/gophercloud/gophercloud" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("orders") } func getURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("orders", id) } func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("orders") } func deleteURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("orders", id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/secrets/000077500000000000000000000000001335005366400316645ustar00rootroot00000000000000doc.go000066400000000000000000000052361335005366400327070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/secrets/* Package secrets manages and retrieves secrets in the OpenStack Key Manager Service. Example to List Secrets createdQuery := &secrets.DateQuery{ Date: time.Date(2049, 6, 7, 1, 2, 3, 0, time.UTC), Filter: secrets.DateFilterLT, } listOpts := secrets.ListOpts{ CreatedQuery: createdQuery, } allPages, err := secrets.List(client, listOpts).AllPages() if err != nil { panic(err) } allSecrets, err := secrets.ExtractSecrets(allPages) if err != nil { panic(err) } for _, v := range allSecrets { fmt.Printf("%v\n", v) } Example to Get a Secret secret, err := secrets.Get(client, secretID).Extract() if err != nil { panic(err) } fmt.Printf("%v\n", secret) Example to Get a Payload payload, err := secrets.GetPayload(client, secretID).Extract() if err != nil { panic(err) } fmt.Println(string(payload)) Example to Create a Secrets createOpts := secrets.CreateOpts{ Algorithm: "aes", BitLength: 256, Mode: "cbc", Name: "mysecret", Payload: "super-secret", PayloadContentType: "text/plain", SecretType: secrets.OpaqueSecret, } secret, err := secrets.Create(client, createOpts).Extract() if err != nil { panic(err) } fmt.Println(secret.SecretRef) Example to Add a Payload updateOpts := secrets.UpdateOpts{ ContentType: "text/plain", Payload: "super-secret", } err := secrets.Update(client, secretID, updateOpts).ExtractErr() if err != nil { panic(err) } Example to Delete a Secrets err := secrets.Delete(client, secretID).ExtractErr() if err != nil { panic(err) } Example to Create Metadata for a Secret createOpts := secrets.MetadataOpts{ "foo": "bar", "something": "something else", } ref, err := secrets.CreateMetadata(client, secretID, createOpts).Extract() if err != nil { panic(err) } fmt.Printf("%v\n", ref) Example to Get Metadata for a Secret metadata, err := secrets.GetMetadata(client, secretID).Extract() if err != nil { panic(err) } fmt.Printf("%v\n", metadata) Example to Add Metadata to a Secret metadatumOpts := secrets.MetadatumOpts{ Key: "foo", Value: "bar", } err := secrets.CreateMetadatum(client, secretID, metadatumOpts).ExtractErr() if err != nil { panic(err) } Example to Update Metadata of a Secret metadatumOpts := secrets.MetadatumOpts{ Key: "foo", Value: "bar", } metadatum, err := secrets.UpdateMetadatum(client, secretID, metadatumOpts).Extract() if err != nil { panic(err) } fmt.Printf("%v\n", metadatum) Example to Delete Metadata of a Secret err := secrets.DeleteMetadatum(client, secretID, "foo").ExtractErr() if err != nil { panic(err) } */ package secrets requests.go000066400000000000000000000272001335005366400340100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/secretspackage secrets import ( "fmt" "net/url" "strings" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // DateFilter represents a valid filter to use for filtering // secrets by their date during a list. type DateFilter string const ( DateFilterGT DateFilter = "gt" DateFilterGTE DateFilter = "gte" DateFilterLT DateFilter = "lt" DateFilterLTE DateFilter = "lte" ) // DateQuery represents a date field to be used for listing secrets. // If no filter is specified, the query will act as if "equal" is used. type DateQuery struct { Date time.Time Filter DateFilter } // SecretType represents a valid secret type. type SecretType string const ( SymmetricSecret SecretType = "symmetric" PublicSecret SecretType = "public" PrivateSecret SecretType = "private" PassphraseSecret SecretType = "passphrase" CertificateSecret SecretType = "certificate" OpaqueSecret SecretType = "opaque" ) // ListOptsBuilder allows extensions to add additional parameters to // the List request type ListOptsBuilder interface { ToSecretListQuery() (string, error) } // ListOpts provides options to filter the List results. type ListOpts struct { // Offset is the starting index within the total list of the secrets that // you would like to retrieve. Offset int `q:"offset"` // Limit is the maximum number of records to return. Limit int `q:"limit"` // Name will select all secrets with a matching name. Name string `q:"name"` // Alg will select all secrets with a matching algorithm. Alg string `q:"alg"` // Mode will select all secrets with a matching mode. Mode string `q:"mode"` // Bits will select all secrets with a matching bit length. Bits int `q:"bits"` // SecretType will select all secrets with a matching secret type. SecretType SecretType `q:"secret_type"` // ACLOnly will select all secrets with an ACL that contains the user. ACLOnly *bool `q:"acl_only"` // CreatedQuery will select all secrets with a created date matching // the query. CreatedQuery *DateQuery // UpdatedQuery will select all secrets with an updated date matching // the query. UpdatedQuery *DateQuery // ExpirationQuery will select all secrets with an expiration date // matching the query. ExpirationQuery *DateQuery // Sort will sort the results in the requested order. Sort string `q:"sort"` } // ToSecretListQuery formats a ListOpts into a query string. func (opts ListOpts) ToSecretListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) params := q.Query() if opts.CreatedQuery != nil { created := opts.CreatedQuery.Date.Format(time.RFC3339) if v := opts.CreatedQuery.Filter; v != "" { created = fmt.Sprintf("%s:%s", v, created) } params.Add("created", created) } if opts.UpdatedQuery != nil { updated := opts.UpdatedQuery.Date.Format(time.RFC3339) if v := opts.UpdatedQuery.Filter; v != "" { updated = fmt.Sprintf("%s:%s", v, updated) } params.Add("updated", updated) } if opts.ExpirationQuery != nil { expiration := opts.ExpirationQuery.Date.Format(time.RFC3339) if v := opts.ExpirationQuery.Filter; v != "" { expiration = fmt.Sprintf("%s:%s", v, expiration) } params.Add("expiration", expiration) } q = &url.URL{RawQuery: params.Encode()} return q.String(), err } // List retrieves a list of Secrets. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToSecretListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return SecretPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves details of a secrets. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // GetPayloadOpts represents options used for obtaining a payload. type GetPayloadOpts struct { PayloadContentType string `h:"Accept"` } // GetPayloadOptsBuilder allows extensions to add additional parameters to // the GetPayload request. type GetPayloadOptsBuilder interface { ToSecretPayloadGetParams() (map[string]string, error) } // ToSecretPayloadGetParams formats a GetPayloadOpts into a query string. func (opts GetPayloadOpts) ToSecretPayloadGetParams() (map[string]string, error) { return gophercloud.BuildHeaders(opts) } // GetPayload retrieves the payload of a secret. func GetPayload(client *gophercloud.ServiceClient, id string, opts GetPayloadOptsBuilder) (r PayloadResult) { h := map[string]string{"Accept": "text/plain"} if opts != nil { headers, err := opts.ToSecretPayloadGetParams() if err != nil { r.Err = err return } for k, v := range headers { h[k] = v } } url := payloadURL(client, id) resp, err := client.Get(url, nil, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{200}, }) if resp != nil { r.Header = resp.Header r.Body = resp.Body } r.Err = err return } // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { ToSecretCreateMap() (map[string]interface{}, error) } // CreateOpts provides options used to create a secrets. type CreateOpts struct { // Algorithm is the algorithm of the secret. Algorithm string `json:"algorithm,omitempty"` // BitLength is the bit length of the secret. BitLength int `json:"bit_length,omitempty"` // Mode is the mode of encryption for the secret. Mode string `json:"mode,omitempty"` // Name is the name of the secret Name string `json:"name,omitempty"` // Payload is the secret. Payload string `json:"payload,omitempty"` // PayloadContentType is the content type of the payload. PayloadContentType string `json:"payload_content_type,omitempty"` // PayloadContentEncoding is the content encoding of the payload. PayloadContentEncoding string `json:"payload_content_encoding,omitempty"` // SecretType is the type of secret. SecretType SecretType `json:"secret_type,omitempty"` } // ToSecretCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToSecretCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // Create creates a new secrets. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSecretCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) return } // Delete deletes a secrets. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { ToSecretUpdateRequest() (string, map[string]string, error) } // UpdateOpts represents parameters to add a payload to an existing // secret which does not already contain a payload. type UpdateOpts struct { // ContentType represents the content type of the payload. ContentType string `h:"Content-Type"` // ContentEncoding represents the content encoding of the payload. ContentEncoding string `h:"Content-Encoding"` // Payload is the payload of the secret. Payload string } // ToUpdateCreateRequest formats a UpdateOpts into an update request. func (opts UpdateOpts) ToSecretUpdateRequest() (string, map[string]string, error) { h, err := gophercloud.BuildHeaders(opts) if err != nil { return "", nil, err } return opts.Payload, h, nil } // Update modifies the attributes of a secrets. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { url := updateURL(client, id) h := make(map[string]string) var b string if opts != nil { payload, headers, err := opts.ToSecretUpdateRequest() if err != nil { r.Err = err return } for k, v := range headers { h[k] = v } b = payload } resp, err := client.Put(url, nil, nil, &gophercloud.RequestOpts{ RawBody: strings.NewReader(b), MoreHeaders: h, OkCodes: []int{204}, }) r.Err = err if resp != nil { r.Header = resp.Header } return } // GetMetadata will list metadata for a given secret. func GetMetadata(client *gophercloud.ServiceClient, secretID string) (r MetadataResult) { _, r.Err = client.Get(metadataURL(client, secretID), &r.Body, nil) return } // MetadataOpts is a map that contains key-value pairs for secret metadata. type MetadataOpts map[string]string // CreateMetadataOptsBuilder allows extensions to add additional parameters to // the CreateMetadata request. type CreateMetadataOptsBuilder interface { ToMetadataCreateMap() (map[string]interface{}, error) } // ToMetadataCreateMap converts a MetadataOpts into a request body. func (opts MetadataOpts) ToMetadataCreateMap() (map[string]interface{}, error) { return map[string]interface{}{"metadata": opts}, nil } // CreateMetadata will set metadata for a given secret. func CreateMetadata(client *gophercloud.ServiceClient, secretID string, opts CreateMetadataOptsBuilder) (r MetadataCreateResult) { b, err := opts.ToMetadataCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Put(metadataURL(client, secretID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) return } // GetMetadatum will get a single key/value metadata from a secret. func GetMetadatum(client *gophercloud.ServiceClient, secretID string, key string) (r MetadatumResult) { _, r.Err = client.Get(metadatumURL(client, secretID, key), &r.Body, nil) return } // MetadatumOpts represents a single metadata. type MetadatumOpts struct { Key string `json:"key" required:"true"` Value string `json:"value" required:"true"` } // CreateMetadatumOptsBuilder allows extensions to add additional parameters to // the CreateMetadatum request. type CreateMetadatumOptsBuilder interface { ToMetadatumCreateMap() (map[string]interface{}, error) } // ToMetadatumCreateMap converts a MetadatumOpts into a request body. func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // CreateMetadatum will add a single key/value metadata to a secret. func CreateMetadatum(client *gophercloud.ServiceClient, secretID string, opts CreateMetadatumOptsBuilder) (r MetadatumCreateResult) { b, err := opts.ToMetadatumCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(metadataURL(client, secretID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) return } // UpdateMetadatumOptsBuilder allows extensions to add additional parameters to // the UpdateMetadatum request. type UpdateMetadatumOptsBuilder interface { ToMetadatumUpdateMap() (map[string]interface{}, string, error) } // ToMetadatumUpdateMap converts a MetadataOpts into a request body. func (opts MetadatumOpts) ToMetadatumUpdateMap() (map[string]interface{}, string, error) { b, err := gophercloud.BuildRequestBody(opts, "") return b, opts.Key, err } // UpdateMetadatum will update a single key/value metadata to a secret. func UpdateMetadatum(client *gophercloud.ServiceClient, secretID string, opts UpdateMetadatumOptsBuilder) (r MetadatumResult) { b, key, err := opts.ToMetadatumUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Put(metadatumURL(client, secretID, key), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // DeleteMetadatum will delete an individual metadatum from a secret. func DeleteMetadatum(client *gophercloud.ServiceClient, secretID string, key string) (r MetadatumDeleteResult) { _, r.Err = client.Delete(metadatumURL(client, secretID, key), nil) return } results.go000066400000000000000000000137441335005366400336460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/secretspackage secrets import ( "encoding/json" "io" "io/ioutil" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Secret represents a secret stored in the key manager service. type Secret struct { // BitLength is the bit length of the secret. BitLength int `json:"bit_length"` // Algorithm is the algorithm type of the secret. Algorithm string `json:"algorithm"` // Expiration is the expiration date of the secret. Expiration time.Time `json:"-"` // ContentTypes are the content types of the secret. ContentTypes map[string]string `json:"content_types"` // Created is the created date of the secret. Created time.Time `json:"-"` // CreatorID is the creator of the secret. CreatorID string `json:"creator_id"` // Mode is the mode of the secret. Mode string `json:"mode"` // Name is the name of the secret. Name string `json:"name"` // SecretRef is the URL to the secret. SecretRef string `json:"secret_ref"` // SecretType represents the type of secret. SecretType string `json:"secret_type"` // Status represents the status of the secret. Status string `json:"status"` // Updated is the updated date of the secret. Updated time.Time `json:"-"` } func (r *Secret) UnmarshalJSON(b []byte) error { type tmp Secret var s struct { tmp Created gophercloud.JSONRFC3339NoZ `json:"created"` Updated gophercloud.JSONRFC3339NoZ `json:"updated"` Expiration gophercloud.JSONRFC3339NoZ `json:"expiration"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Secret(s.tmp) r.Created = time.Time(s.Created) r.Updated = time.Time(s.Updated) r.Expiration = time.Time(s.Expiration) return nil } type commonResult struct { gophercloud.Result } // Extract interprets any commonResult as a Secret. func (r commonResult) Extract() (*Secret, error) { var s *Secret err := r.ExtractInto(&s) return s, err } // GetResult is the response from a Get operation. Call its Extract method // to interpret it as a secrets. type GetResult struct { commonResult } // CreateResult is the response from a Create operation. Call its Extract method // to interpret it as a secrets. type CreateResult struct { commonResult } // UpdateResult is the response from an Update operation. Call its ExtractErr to // determine if the request succeeded or failed. type UpdateResult struct { gophercloud.ErrResult } // DeleteResult is the response from a Delete operation. Call its ExtractErr to // determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // PayloadResult is the response from a GetPayload operation. Call its Extract // method to extract the payload as a string. type PayloadResult struct { gophercloud.Result Body io.ReadCloser } // Extract is a function that takes a PayloadResult's io.Reader body // and reads all available data into a slice of bytes. Please be aware that due // to the nature of io.Reader is forward-only - meaning that it can only be read // once and not rewound. You can recreate a reader from the output of this // function by using bytes.NewReader(downloadBytes) func (r PayloadResult) Extract() ([]byte, error) { if r.Err != nil { return nil, r.Err } defer r.Body.Close() body, err := ioutil.ReadAll(r.Body) if err != nil { return nil, err } r.Body.Close() return body, nil } // SecretPage is a single page of secrets results. type SecretPage struct { pagination.LinkedPageBase } // IsEmpty determines whether or not a page of secrets contains any results. func (r SecretPage) IsEmpty() (bool, error) { secrets, err := ExtractSecrets(r) return len(secrets) == 0, err } // NextPageURL extracts the "next" link from the links section of the result. func (r SecretPage) NextPageURL() (string, error) { var s struct { Next string `json:"next"` Previous string `json:"previous"` } err := r.ExtractInto(&s) if err != nil { return "", err } return s.Next, err } // ExtractSecrets returns a slice of Secrets contained in a single page of // results. func ExtractSecrets(r pagination.Page) ([]Secret, error) { var s struct { Secrets []Secret `json:"secrets"` } err := (r.(SecretPage)).ExtractInto(&s) return s.Secrets, err } // MetadataResult is the result of a metadata request. Call its Extract method // to interpret it as a map[string]string. type MetadataResult struct { gophercloud.Result } // Extract interprets any MetadataResult as map[string]string. func (r MetadataResult) Extract() (map[string]string, error) { var s struct { Metadata map[string]string `json:"metadata"` } err := r.ExtractInto(&s) return s.Metadata, err } // MetadataCreateResult is the result of a metadata create request. Call its // Extract method to interpret it as a map[string]string. type MetadataCreateResult struct { gophercloud.Result } // Extract interprets any MetadataCreateResult as a map[string]string. func (r MetadataCreateResult) Extract() (map[string]string, error) { var s map[string]string err := r.ExtractInto(&s) return s, err } // Metadatum represents an individual metadata. type Metadatum struct { Key string `json:"key"` Value string `json:"value"` } // MetadatumResult is the result of a metadatum request. Call its // Extract method to interpret it as a map[string]string. type MetadatumResult struct { gophercloud.Result } // Extract interprets any MetadatumResult as a map[string]string. func (r MetadatumResult) Extract() (*Metadatum, error) { var s *Metadatum err := r.ExtractInto(&s) return s, err } // MetadatumCreateResult is the response from a metadata Create operation. Call // it's ExtractErr to determine if the request succeeded or failed. // // NOTE: This could be a MetadatumResponse but, at the time of testing, it looks // like Barbican was returning errneous JSON in the response. type MetadatumCreateResult struct { gophercloud.ErrResult } // MetadatumDeleteResult is the response from a metadatum Delete operation. Call // its ExtractErr to determine if the request succeeded or failed. type MetadatumDeleteResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400332625ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/secretsfixtures.go000066400000000000000000000267341335005366400354760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/secrets/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/secrets" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListResponse provides a single page of RESOURCE results. const ListResponse = ` { "secrets": [ { "algorithm": "aes", "bit_length": 256, "content_types": { "default": "text/plain" }, "created": "2018-06-21T02:49:48", "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", "expiration": null, "mode": "cbc", "name": "mysecret", "secret_ref": "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", "secret_type": "opaque", "status": "ACTIVE", "updated": "2018-06-21T02:49:48" }, { "algorithm": "aes", "bit_length": 256, "content_types": { "default": "text/plain" }, "created": "2018-06-21T05:18:45", "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", "expiration": null, "mode": "cbc", "name": "anothersecret", "secret_ref": "http://barbican:9311/v1/secrets/1b12b69a-8822-442e-a303-da24ade648ac", "secret_type": "opaque", "status": "ACTIVE", "updated": "2018-06-21T05:18:45" } ], "total": 2 }` // GetResponse provides a Get result. const GetResponse = ` { "algorithm": "aes", "bit_length": 256, "content_types": { "default": "text/plain" }, "created": "2018-06-21T02:49:48", "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", "expiration": null, "mode": "cbc", "name": "mysecret", "secret_ref": "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", "secret_type": "opaque", "status": "ACTIVE", "updated": "2018-06-21T02:49:48" }` // GetPayloadResponse provides a payload result. const GetPayloadResponse = `foobar` // CreateRequest provides the input to a Create request. const CreateRequest = ` { "algorithm": "aes", "bit_length": 256, "mode": "cbc", "name": "mysecret", "payload": "foobar", "payload_content_type": "text/plain", "secret_type": "opaque" }` // CreateResponse provides a Create result. const CreateResponse = ` { "secret_ref": "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c" }` // UpdateRequest provides the input to as Update request. const UpdateRequest = `foobar` // FirstSecret is the first secret in the List request. var FirstSecret = secrets.Secret{ Algorithm: "aes", BitLength: 256, ContentTypes: map[string]string{ "default": "text/plain", }, Created: time.Date(2018, 6, 21, 2, 49, 48, 0, time.UTC), CreatorID: "5c70d99f4a8641c38f8084b32b5e5c0e", Mode: "cbc", Name: "mysecret", SecretRef: "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", SecretType: "opaque", Status: "ACTIVE", Updated: time.Date(2018, 6, 21, 2, 49, 48, 0, time.UTC), } // SecondSecret is the second secret in the List request. var SecondSecret = secrets.Secret{ Algorithm: "aes", BitLength: 256, ContentTypes: map[string]string{ "default": "text/plain", }, Created: time.Date(2018, 6, 21, 5, 18, 45, 0, time.UTC), CreatorID: "5c70d99f4a8641c38f8084b32b5e5c0e", Mode: "cbc", Name: "anothersecret", SecretRef: "http://barbican:9311/v1/secrets/1b12b69a-8822-442e-a303-da24ade648ac", SecretType: "opaque", Status: "ACTIVE", Updated: time.Date(2018, 6, 21, 5, 18, 45, 0, time.UTC), } // ExpectedSecretsSlice is the slice of secrets expected to be returned from ListResponse. var ExpectedSecretsSlice = []secrets.Secret{FirstSecret, SecondSecret} // ExpectedCreateResult is the result of a create request var ExpectedCreateResult = secrets.Secret{ SecretRef: "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", } const GetMetadataResponse = ` { "metadata": { "foo": "bar", "something": "something else" } }` // ExpectedMetadata is the result of a Get or Create request. var ExpectedMetadata = map[string]string{ "foo": "bar", "something": "something else", } const CreateMetadataRequest = ` { "metadata": { "foo": "bar", "something": "something else" } }` const CreateMetadataResponse = ` { "metadata_ref": "http://barbican:9311/v1/secrets/1b12b69a-8822-442e-a303-da24ade648ac/metadata" }` // ExpectedCreateMetadataResult is the result of a Metadata create request. var ExpectedCreateMetadataResult = map[string]string{ "metadata_ref": "http://barbican:9311/v1/secrets/1b12b69a-8822-442e-a303-da24ade648ac/metadata", } const MetadatumRequest = ` { "key": "foo", "value": "bar" }` const MetadatumResponse = ` { "key": "foo", "value": "bar" }` // ExpectedMetadatum is the result of a Metadatum Get, Create, or Update // request var ExpectedMetadatum = secrets.Metadatum{ Key: "foo", Value: "bar", } // HandleListSecretsSuccessfully creates an HTTP handler at `/secrets` on the // test handler mux that responds with a list of two secrets. func HandleListSecretsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/secrets", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListResponse) }) } // HandleGetSecretSuccessfully creates an HTTP handler at `/secrets` on the // test handler mux that responds with a single secret. func HandleGetSecretSuccessfully(t *testing.T) { th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetResponse) }) } // HandleGetPayloadSuccessfully creates an HTTP handler at `/secrets` on the // test handler mux that responds with a single secret. func HandleGetPayloadSuccessfully(t *testing.T) { th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/payload", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetPayloadResponse) }) } // HandleCreateSecretSuccessfully creates an HTTP handler at `/secrets` on the // test handler mux that tests secret creation. func HandleCreateSecretSuccessfully(t *testing.T) { th.Mux.HandleFunc("/secrets", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, CreateResponse) }) } // HandleDeleteSecretSuccessfully creates an HTTP handler at `/secrets` on the // test handler mux that tests secret deletion. func HandleDeleteSecretSuccessfully(t *testing.T) { th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleUpdateSecretSuccessfully creates an HTTP handler at `/secrets` on the // test handler mux that tests secret updates. func HandleUpdateSecretSuccessfully(t *testing.T) { th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusNoContent) }) } // HandleGetMetadataSuccessfully creates an HTTP handler at // `/secrets/uuid/metadata` on the test handler mux that responds with // retrieved metadata. func HandleGetMetadataSuccessfully(t *testing.T) { th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/metadata", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetMetadataResponse) }) } // HandleCreateMetadataSuccessfully creates an HTTP handler at // `/secrets/uuid/metadata` on the test handler mux that responds with // a metadata reference URL. func HandleCreateMetadataSuccessfully(t *testing.T) { th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/metadata", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, CreateMetadataRequest) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, CreateMetadataResponse) }) } // HandleGetMetadatumSuccessfully creates an HTTP handler at // `/secrets/uuid/metadata/foo` on the test handler mux that responds with a // single metadatum. func HandleGetMetadatumSuccessfully(t *testing.T) { th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/metadata/foo", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, MetadatumResponse) }) } // HandleCreateMetadatumSuccessfully creates an HTTP handler at // `/secrets/uuid/metadata` on the test handler mux that responds with // a single created metadata. func HandleCreateMetadatumSuccessfully(t *testing.T) { th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/metadata", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, MetadatumRequest) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, MetadatumResponse) }) } // HandleUpdateMetadatumSuccessfully creates an HTTP handler at // `/secrets/uuid/metadata/foo` on the test handler mux that responds with a // single updated metadatum. func HandleUpdateMetadatumSuccessfully(t *testing.T) { th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/metadata/foo", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, MetadatumRequest) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, MetadatumResponse) }) } // HandleDeleteMetadatumSuccessfully creates an HTTP handler at // `/secrets/uuid/metadata/key` on the test handler mux that tests metadata // deletion. func HandleDeleteMetadatumSuccessfully(t *testing.T) { th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/metadata/foo", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000114621335005366400365270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/secrets/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/secrets" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListSecrets(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSecretsSuccessfully(t) count := 0 err := secrets.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := secrets.ExtractSecrets(page) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedSecretsSlice, actual) return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, count, 1) } func TestListSecretsAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSecretsSuccessfully(t) allPages, err := secrets.List(client.ServiceClient(), nil).AllPages() th.AssertNoErr(t, err) actual, err := secrets.ExtractSecrets(allPages) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedSecretsSlice, actual) } func TestGetSecret(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSecretSuccessfully(t) actual, err := secrets.Get(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, FirstSecret, *actual) } func TestCreateSecret(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateSecretSuccessfully(t) createOpts := secrets.CreateOpts{ Algorithm: "aes", BitLength: 256, Mode: "cbc", Name: "mysecret", Payload: "foobar", PayloadContentType: "text/plain", SecretType: secrets.OpaqueSecret, } actual, err := secrets.Create(client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCreateResult, *actual) } func TestDeleteSecret(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSecretSuccessfully(t) res := secrets.Delete(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c") th.AssertNoErr(t, res.Err) } func TestUpdateSecret(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateSecretSuccessfully(t) updateOpts := secrets.UpdateOpts{ Payload: "foobar", } err := secrets.Update(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", updateOpts).ExtractErr() th.AssertNoErr(t, err) } func TestGetPayloadSecret(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetPayloadSuccessfully(t) res := secrets.GetPayload(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", nil) th.AssertNoErr(t, res.Err) payload, err := res.Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, GetPayloadResponse, string(payload)) } func TestGetMetadataSuccessfully(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetMetadataSuccessfully(t) actual, err := secrets.GetMetadata(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedMetadata, actual) } func TestCreateMetadataSuccessfully(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateMetadataSuccessfully(t) createOpts := secrets.MetadataOpts{ "foo": "bar", "something": "something else", } actual, err := secrets.CreateMetadata(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", createOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCreateMetadataResult, actual) } func TestGetMetadatumSuccessfully(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetMetadatumSuccessfully(t) actual, err := secrets.GetMetadatum(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", "foo").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedMetadatum, *actual) } func TestCreateMetadatumSuccessfully(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateMetadatumSuccessfully(t) createOpts := secrets.MetadatumOpts{ Key: "foo", Value: "bar", } err := secrets.CreateMetadatum(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", createOpts).ExtractErr() th.AssertNoErr(t, err) } func TestUpdateMetadatumSuccessfully(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateMetadatumSuccessfully(t) updateOpts := secrets.MetadatumOpts{ Key: "foo", Value: "bar", } actual, err := secrets.UpdateMetadatum(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedMetadatum, *actual) } func TestDeleteMetadatumSuccessfully(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteMetadatumSuccessfully(t) err := secrets.DeleteMetadatum(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", "foo").ExtractErr() th.AssertNoErr(t, err) } urls.go000066400000000000000000000017351335005366400331270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/keymanager/v1/secretspackage secrets import "github.com/gophercloud/gophercloud" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("secrets") } func getURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("secrets", id) } func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("secrets") } func deleteURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("secrets", id) } func updateURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("secrets", id) } func payloadURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("secrets", id, "payload") } func metadataURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("secrets", id, "metadata") } func metadatumURL(client *gophercloud.ServiceClient, id, key string) string { return client.ServiceURL("secrets", id, "metadata", key) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/000077500000000000000000000000001335005366400301525ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/000077500000000000000000000000001335005366400305015ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/amphorae/000077500000000000000000000000001335005366400322755ustar00rootroot00000000000000doc.go000066400000000000000000000010021335005366400333030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/amphorae/* Package amphorae provides information and interaction with Amphorae of OpenStack Load-balancing service. Example to List Amphorae listOpts := amphorae.ListOpts{ LoadbalancerID: "6bd55cd3-802e-447e-a518-1e74e23bb106", } allPages, err := amphorae.List(octaviaClient, listOpts).AllPages() if err != nil { panic(err) } allAmphorae, err := amphorae.ExtractAmphorae(allPages) if err != nil { panic(err) } for _, amphora := range allAmphorae { fmt.Printf("%+v\n", amphora) } */ package amphorae requests.go000066400000000000000000000037701335005366400344270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/amphoraepackage amphorae import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToAmphoraListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the Amphorae attributes you want to see returned. SortKey allows you to // sort by a particular attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { LoadbalancerID string `q:"loadbalancer_id"` ImageID string `q:"image_id"` Role string `q:"role"` Status string `q:"status"` HAPortID string `q:"ha_port_id"` VRRPPortID string `q:"vrrp_port_id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToAmphoraListQuery formats a ListOpts into a query string. func (opts ListOpts) ToAmphoraListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // amphorae. It accepts a ListOpts struct, which allows you to filter // and sort the returned collection for greater efficiency. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { query, err := opts.ToAmphoraListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return AmphoraPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves a particular amphora based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } results.go000066400000000000000000000103071335005366400342470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/amphoraepackage amphorae import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Amphora is virtual machine, container, dedicated hardware, appliance or device that actually performs the task of // load balancing in the Octavia system. type Amphora struct { // The unique ID for the Amphora. ID string `json:"id"` // The ID of the load balancer. LoadbalancerID string `json:"loadbalancer_id"` // The management IP of the amphora. LBNetworkIP string `json:"lb_network_ip"` // The ID of the amphora resource in the compute system. ComputeID string `json:"compute_id"` // The IP address of the Virtual IP (VIP). HAIP string `json:"ha_ip"` // The ID of the Virtual IP (VIP) port. HAPortID string `json:"ha_port_id"` // The date the certificate for the amphora expires. CertExpiration time.Time `json:"-"` // Whether the certificate is in the process of being replaced. CertBusy bool `json:"cert_busy"` // The role of the amphora. One of STANDALONE, MASTER, BACKUP. Role string `json:"role"` // The status of the amphora. One of: BOOTING, ALLOCATED, READY, PENDING_CREATE, PENDING_DELETE, DELETED, ERROR. Status string `json:"status"` // The vrrp port’s ID in the networking system. VRRPPortID string `json:"vrrp_port_id"` // The address of the vrrp port on the amphora. VRRPIP string `json:"vrrp_ip"` // The bound interface name of the vrrp port on the amphora. VRRPInterface string `json:"vrrp_interface"` // The vrrp group’s ID for the amphora. VRRPID int `json:"vrrp_id"` // The priority of the amphora in the vrrp group. VRRPPriority int `json:"vrrp_priority"` // The availability zone of a compute instance, cached at create time. This is not guaranteed to be current. May be // an empty-string if the compute service does not use zones. CachedZone string `json:"cached_zone"` // The ID of the glance image used for the amphora. ImageID string `json:"image_id"` // The UTC date and timestamp when the resource was created. CreatedAt time.Time `json:"-"` // The UTC date and timestamp when the resource was last updated. UpdatedAt time.Time `json:"-"` } func (a *Amphora) UnmarshalJSON(b []byte) error { type tmp Amphora var s struct { tmp CertExpiration gophercloud.JSONRFC3339NoZ `json:"cert_expiration"` CreatedAt gophercloud.JSONRFC3339NoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339NoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *a = Amphora(s.tmp) a.CreatedAt = time.Time(s.CreatedAt) a.UpdatedAt = time.Time(s.UpdatedAt) a.CertExpiration = time.Time(s.CertExpiration) return nil } // AmphoraPage is the page returned by a pager when traversing over a // collection of amphorae. type AmphoraPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of amphoraes has // reached the end of a page and the pager seeks to traverse over a new one. // In order to do this, it needs to construct the next page's URL. func (r AmphoraPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"amphorae_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a AmphoraPage struct is empty. func (r AmphoraPage) IsEmpty() (bool, error) { is, err := ExtractAmphorae(r) return len(is) == 0, err } // ExtractAmphorae accepts a Page struct, specifically a AmphoraPage // struct, and extracts the elements into a slice of Amphora structs. In // other words, a generic collection is mapped into a relevant slice. func ExtractAmphorae(r pagination.Page) ([]Amphora, error) { var s struct { Amphorae []Amphora `json:"amphorae"` } err := (r.(AmphoraPage)).ExtractInto(&s) return s.Amphorae, err } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts an amphora. func (r commonResult) Extract() (*Amphora, error) { var s struct { Amphora *Amphora `json:"amphora"` } err := r.ExtractInto(&s) return s.Amphora, err } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as an amphora. type GetResult struct { commonResult } testing/000077500000000000000000000000001335005366400336735ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/amphoraedoc.go000066400000000000000000000000201335005366400347570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/amphorae/testingpackage testing fixtures.go000066400000000000000000000136431335005366400361020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/amphorae/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/amphorae" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // AmphoraeListBody contains the canned body of a amphora list response. const AmphoraeListBody = ` { "amphorae": [ { "cached_zone": "nova", "cert_busy": false, "cert_expiration": "2020-08-08T23:44:31", "compute_id": "667bb225-69aa-44b1-8908-694dc624c267", "created_at": "2018-08-09T23:44:31", "ha_ip": "10.0.0.6", "ha_port_id": "35254b63-9361-4561-9b8f-2bb4e3be60e3", "id": "45f40289-0551-483a-b089-47214bc2a8a4", "image_id": "5d1aed06-2624-43f5-a413-9212263c3d53", "lb_network_ip": "192.168.0.6", "loadbalancer_id": "882f2a9d-9d53-4bd0-b0e9-08e9d0de11f9", "role": "MASTER", "status": "READY", "updated_at": "2018-08-09T23:51:06", "vrrp_id": 1, "vrrp_interface": "eth1", "vrrp_ip": "10.0.0.4", "vrrp_port_id": "dcf0c8b5-6a08-4658-997d-eac97f2b9bbd", "vrrp_priority": 100 }, { "cached_zone": "nova", "cert_busy": false, "cert_expiration": "2020-08-08T23:44:30", "compute_id": "9cd0f9a2-fe12-42fc-a7e3-5b6fbbe20395", "created_at": "2018-08-09T23:44:31", "ha_ip": "10.0.0.6", "ha_port_id": "35254b63-9361-4561-9b8f-2bb4e3be60e3", "id": "7f890893-ced0-46ed-8697-33415d070e5a", "image_id": "5d1aed06-2624-43f5-a413-9212263c3d53", "lb_network_ip": "192.168.0.17", "loadbalancer_id": "882f2a9d-9d53-4bd0-b0e9-08e9d0de11f9", "role": "BACKUP", "status": "READY", "updated_at": "2018-08-09T23:51:06", "vrrp_id": 1, "vrrp_interface": "eth1", "vrrp_ip": "10.0.0.21", "vrrp_port_id": "13c88c77-207d-4f85-8f7a-84344592e367", "vrrp_priority": 90 } ], "amphorae_links": [] } ` const SingleAmphoraBody = ` { "amphora": { "cached_zone": "nova", "cert_busy": false, "cert_expiration": "2020-08-08T23:44:31", "compute_id": "667bb225-69aa-44b1-8908-694dc624c267", "created_at": "2018-08-09T23:44:31", "ha_ip": "10.0.0.6", "ha_port_id": "35254b63-9361-4561-9b8f-2bb4e3be60e3", "id": "45f40289-0551-483a-b089-47214bc2a8a4", "image_id": "5d1aed06-2624-43f5-a413-9212263c3d53", "lb_network_ip": "192.168.0.6", "loadbalancer_id": "882f2a9d-9d53-4bd0-b0e9-08e9d0de11f9", "role": "MASTER", "status": "READY", "updated_at": "2018-08-09T23:51:06", "vrrp_id": 1, "vrrp_interface": "eth1", "vrrp_ip": "10.0.0.4", "vrrp_port_id": "dcf0c8b5-6a08-4658-997d-eac97f2b9bbd", "vrrp_priority": 100 } } ` // FirstAmphora is the first resource in the List request. var FirstAmphora = amphorae.Amphora{ CachedZone: "nova", CertBusy: false, CertExpiration: time.Date(2020, 8, 8, 23, 44, 31, 0, time.UTC), ComputeID: "667bb225-69aa-44b1-8908-694dc624c267", CreatedAt: time.Date(2018, 8, 9, 23, 44, 31, 0, time.UTC), HAIP: "10.0.0.6", HAPortID: "35254b63-9361-4561-9b8f-2bb4e3be60e3", ID: "45f40289-0551-483a-b089-47214bc2a8a4", ImageID: "5d1aed06-2624-43f5-a413-9212263c3d53", LBNetworkIP: "192.168.0.6", LoadbalancerID: "882f2a9d-9d53-4bd0-b0e9-08e9d0de11f9", Role: "MASTER", Status: "READY", UpdatedAt: time.Date(2018, 8, 9, 23, 51, 6, 0, time.UTC), VRRPID: 1, VRRPInterface: "eth1", VRRPIP: "10.0.0.4", VRRPPortID: "dcf0c8b5-6a08-4658-997d-eac97f2b9bbd", VRRPPriority: 100, } // SecondAmphora is the second resource in the List request. var SecondAmphora = amphorae.Amphora{ CachedZone: "nova", CertBusy: false, CertExpiration: time.Date(2020, 8, 8, 23, 44, 30, 0, time.UTC), ComputeID: "9cd0f9a2-fe12-42fc-a7e3-5b6fbbe20395", CreatedAt: time.Date(2018, 8, 9, 23, 44, 31, 0, time.UTC), HAIP: "10.0.0.6", HAPortID: "35254b63-9361-4561-9b8f-2bb4e3be60e3", ID: "7f890893-ced0-46ed-8697-33415d070e5a", ImageID: "5d1aed06-2624-43f5-a413-9212263c3d53", LBNetworkIP: "192.168.0.17", LoadbalancerID: "882f2a9d-9d53-4bd0-b0e9-08e9d0de11f9", Role: "BACKUP", Status: "READY", UpdatedAt: time.Date(2018, 8, 9, 23, 51, 6, 0, time.UTC), VRRPID: 1, VRRPInterface: "eth1", VRRPIP: "10.0.0.21", VRRPPortID: "13c88c77-207d-4f85-8f7a-84344592e367", VRRPPriority: 90, } // ExpectedAmphoraeSlice is the slice of amphorae expected to be returned from ListResponse. var ExpectedAmphoraeSlice = []amphorae.Amphora{FirstAmphora, SecondAmphora} // HandleAmphoraListSuccessfully sets up the test server to respond to a amphorae List request. func HandleAmphoraListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/octavia/amphorae", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, AmphoraeListBody) case "7f890893-ced0-46ed-8697-33415d070e5a": fmt.Fprintf(w, `{ "amphorae": [] }`) default: t.Fatalf("/v2.0/octavia/amphorae invoked with unexpected marker=[%s]", marker) } }) } // HandleAmphoraGetSuccessfully sets up the test server to respond to am amphora Get request. func HandleAmphoraGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/octavia/amphorae/45f40289-0551-483a-b089-47214bc2a8a4", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, SingleAmphoraBody) }) } requests_test.go000066400000000000000000000030561335005366400371400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/amphorae/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/amphorae" fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestListAmphorae(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAmphoraListSuccessfully(t) pages := 0 err := amphorae.List(fake.ServiceClient(), amphorae.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := amphorae.ExtractAmphorae(page) if err != nil { return false, err } if len(actual) != 2 { t.Fatalf("Expected 2 amphorae, got %d", len(actual)) } return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListAllAmphorae(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAmphoraListSuccessfully(t) allPages, err := amphorae.List(fake.ServiceClient(), amphorae.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := amphorae.ExtractAmphorae(allPages) th.AssertNoErr(t, err) th.AssertEquals(t, 2, len(actual)) th.AssertDeepEquals(t, ExpectedAmphoraeSlice, actual) } func TestGetAmphora(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAmphoraGetSuccessfully(t) client := fake.ServiceClient() actual, err := amphorae.Get(client, "45f40289-0551-483a-b089-47214bc2a8a4").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, FirstAmphora, *actual) } urls.go000066400000000000000000000005311335005366400335310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/amphoraepackage amphorae import "github.com/gophercloud/gophercloud" const ( rootPath = "octavia" resourcePath = "amphorae" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/doc.go000066400000000000000000000002441335005366400315750ustar00rootroot00000000000000// Package lbaas_v2 provides information and interaction with the Load Balancer // as a Service v2 extension for the OpenStack Networking service. package lbaas_v2 golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/l7policies/000077500000000000000000000000001335005366400325535ustar00rootroot00000000000000doc.go000066400000000000000000000060721335005366400335750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/l7policies/* Package l7policies provides information and interaction with L7Policies and Rules of the LBaaS v2 extension for the OpenStack Networking service. Example to Create a L7Policy createOpts := l7policies.CreateOpts{ Name: "redirect-example.com", ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", Action: l7policies.ActionRedirectToURL, RedirectURL: "http://www.example.com", } l7policy, err := l7policies.Create(lbClient, createOpts).Extract() if err != nil { panic(err) } Example to List L7Policies listOpts := l7policies.ListOpts{ ListenerID: "c79a4468-d788-410c-bf79-9a8ef6354852", } allPages, err := l7policies.List(lbClient, listOpts).AllPages() if err != nil { panic(err) } allL7Policies, err := l7policies.ExtractL7Policies(allPages) if err != nil { panic(err) } for _, l7policy := range allL7Policies { fmt.Printf("%+v\n", l7policy) } Example to Get a L7Policy l7policy, err := l7policies.Get(lbClient, "023f2e34-7806-443b-bfae-16c324569a3d").Extract() if err != nil { panic(err) } Example to Delete a L7Policy l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" err := l7policies.Delete(lbClient, l7policyID).ExtractErr() if err != nil { panic(err) } Example to Update a L7Policy l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" name := "new-name" updateOpts := l7policies.UpdateOpts{ Name: &name, } l7policy, err := l7policies.Update(lbClient, l7policyID, updateOpts).Extract() if err != nil { panic(err) } Example to Create a Rule l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" createOpts := l7policies.CreateRuleOpts{ RuleType: l7policies.TypePath, CompareType: l7policies.CompareTypeRegex, Value: "/images*", } rule, err := l7policies.CreateRule(lbClient, l7policyID, createOpts).Extract() if err != nil { panic(err) } Example to List L7 Rules l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" listOpts := l7policies.ListRulesOpts{ RuleType: l7policies.TypePath, } allPages, err := l7policies.ListRules(lbClient, l7policyID, listOpts).AllPages() if err != nil { panic(err) } allRules, err := l7policies.ExtractRules(allPages) if err != nil { panic(err) } for _, rule := allRules { fmt.Printf("%+v\n", rule) } Example to Get a l7 rule l7rule, err := l7policies.GetRule(lbClient, "023f2e34-7806-443b-bfae-16c324569a3d", "53ad8ab8-40fa-11e8-a508-00224d6b7bc1").Extract() if err != nil { panic(err) } Example to Delete a l7 rule l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" ruleID := "64dba99f-8af8-4200-8882-e32a0660f23e" err := l7policies.DeleteRule(lbClient, l7policyID, ruleID).ExtractErr() if err != nil { panic(err) } Example to Update a Rule l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" ruleID := "64dba99f-8af8-4200-8882-e32a0660f23e" updateOpts := l7policies.UpdateRuleOpts{ RuleType: l7policies.TypePath, CompareType: l7policies.CompareTypeRegex, Value: "/images/special*", } rule, err := l7policies.UpdateRule(lbClient, l7policyID, ruleID, updateOpts).Extract() if err != nil { panic(err) } */ package l7policies requests.go000066400000000000000000000262241335005366400347040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/l7policiespackage l7policies import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToL7PolicyCreateMap() (map[string]interface{}, error) } type Action string type RuleType string type CompareType string const ( ActionRedirectToPool Action = "REDIRECT_TO_POOL" ActionRedirectToURL Action = "REDIRECT_TO_URL" ActionReject Action = "REJECT" TypeCookie RuleType = "COOKIE" TypeFileType RuleType = "FILE_TYPE" TypeHeader RuleType = "HEADER" TypeHostName RuleType = "HOST_NAME" TypePath RuleType = "PATH" CompareTypeContains CompareType = "CONTAINS" CompareTypeEndWith CompareType = "ENDS_WITH" CompareTypeEqual CompareType = "EQUAL_TO" CompareTypeRegex CompareType = "REGEX" CompareTypeStartWith CompareType = "STARTS_WITH" ) // CreateOpts is the common options struct used in this package's Create // operation. type CreateOpts struct { // Name of the L7 policy. Name string `json:"name,omitempty"` // The ID of the listener. ListenerID string `json:"listener_id" required:"true"` // The L7 policy action. One of REDIRECT_TO_POOL, REDIRECT_TO_URL, or REJECT. Action Action `json:"action" required:"true"` // The position of this policy on the listener. Position int32 `json:"position,omitempty"` // A human-readable description for the resource. Description string `json:"description,omitempty"` // ProjectID is the UUID of the project who owns the L7 policy in octavia. // Only administrative users can specify a project UUID other than their own. ProjectID string `json:"project_id,omitempty"` // Requests matching this policy will be redirected to the pool with this ID. // Only valid if action is REDIRECT_TO_POOL. RedirectPoolID string `json:"redirect_pool_id,omitempty"` // Requests matching this policy will be redirected to this URL. // Only valid if action is REDIRECT_TO_URL. RedirectURL string `json:"redirect_url,omitempty"` } // ToL7PolicyCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToL7PolicyCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "l7policy") } // Create accepts a CreateOpts struct and uses the values to create a new l7policy. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToL7PolicyCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToL7PolicyListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. type ListOpts struct { Name string `q:"name"` ListenerID string `q:"listener_id"` Action string `q:"action"` ProjectID string `q:"project_id"` RedirectPoolID string `q:"redirect_pool_id"` RedirectURL string `q:"redirect_url"` ID string `q:"id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToL7PolicyListQuery formats a ListOpts into a query string. func (opts ListOpts) ToL7PolicyListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // l7policies. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. // // Default policy settings return only those l7policies that are owned by the // project who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { query, err := opts.ToL7PolicyListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return L7PolicyPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves a particular l7policy based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // Delete will permanently delete a particular l7policy based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToL7PolicyUpdateMap() (map[string]interface{}, error) } // UpdateOpts is the common options struct used in this package's Update // operation. type UpdateOpts struct { // Name of the L7 policy, empty string is allowed. Name *string `json:"name,omitempty"` // The L7 policy action. One of REDIRECT_TO_POOL, REDIRECT_TO_URL, or REJECT. Action Action `json:"action,omitempty"` // The position of this policy on the listener. Position int32 `json:"position,omitempty"` // A human-readable description for the resource, empty string is allowed. Description *string `json:"description,omitempty"` // Requests matching this policy will be redirected to the pool with this ID. // Only valid if action is REDIRECT_TO_POOL. RedirectPoolID string `json:"redirect_pool_id,omitempty"` // Requests matching this policy will be redirected to this URL. // Only valid if action is REDIRECT_TO_URL. RedirectURL string `json:"redirect_url,omitempty"` } // ToL7PolicyUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToL7PolicyUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "l7policy") } // Update allows l7policy to be updated. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToL7PolicyUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // CreateRuleOpts is the common options struct used in this package's CreateRule // operation. type CreateRuleOpts struct { // The L7 rule type. One of COOKIE, FILE_TYPE, HEADER, HOST_NAME, or PATH. RuleType RuleType `json:"type" required:"true"` // The comparison type for the L7 rule. One of CONTAINS, ENDS_WITH, EQUAL_TO, REGEX, or STARTS_WITH. CompareType CompareType `json:"compare_type" required:"true"` // The value to use for the comparison. For example, the file type to compare. Value string `json:"value" required:"true"` // ProjectID is the UUID of the project who owns the rule in octavia. // Only administrative users can specify a project UUID other than their own. ProjectID string `json:"project_id,omitempty"` // The key to use for the comparison. For example, the name of the cookie to evaluate. Key string `json:"key,omitempty"` // When true the logic of the rule is inverted. For example, with invert true, // equal to would become not equal to. Default is false. Invert bool `json:"invert,omitempty"` } // ToRuleCreateMap builds a request body from CreateRuleOpts. func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "rule") } // CreateRule will create and associate a Rule with a particular L7Policy. func CreateRule(c *gophercloud.ServiceClient, policyID string, opts CreateRuleOpts) (r CreateRuleResult) { b, err := opts.ToRuleCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(ruleRootURL(c, policyID), b, &r.Body, nil) return } // ListRulesOptsBuilder allows extensions to add additional parameters to the // ListRules request. type ListRulesOptsBuilder interface { ToRulesListQuery() (string, error) } // ListRulesOpts allows the filtering and sorting of paginated collections // through the API. type ListRulesOpts struct { RuleType RuleType `q:"type"` ProjectID string `q:"project_id"` ID string `q:"id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToRulesListQuery formats a ListOpts into a query string. func (opts ListRulesOpts) ToRulesListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // ListRules returns a Pager which allows you to iterate over a collection of // rules. It accepts a ListRulesOptsBuilder, which allows you to filter and // sort the returned collection for greater efficiency. // // Default policy settings return only those rules that are owned by the // project who submits the request, unless an admin user submits the request. func ListRules(c *gophercloud.ServiceClient, policyID string, opts ListRulesOptsBuilder) pagination.Pager { url := ruleRootURL(c, policyID) if opts != nil { query, err := opts.ToRulesListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return RulePage{pagination.LinkedPageBase{PageResult: r}} }) } // GetRule retrieves a particular L7Policy Rule based on its unique ID. func GetRule(c *gophercloud.ServiceClient, policyID string, ruleID string) (r GetRuleResult) { _, r.Err = c.Get(ruleResourceURL(c, policyID, ruleID), &r.Body, nil) return } // DeleteRule will remove a Rule from a particular L7Policy. func DeleteRule(c *gophercloud.ServiceClient, policyID string, ruleID string) (r DeleteRuleResult) { _, r.Err = c.Delete(ruleResourceURL(c, policyID, ruleID), nil) return } // UpdateRuleOptsBuilder allows to add additional parameters to the PUT request. type UpdateRuleOptsBuilder interface { ToRuleUpdateMap() (map[string]interface{}, error) } // UpdateRuleOpts is the common options struct used in this package's Update // operation. type UpdateRuleOpts struct { // The L7 rule type. One of COOKIE, FILE_TYPE, HEADER, HOST_NAME, or PATH. RuleType RuleType `json:"type,omitempty"` // The comparison type for the L7 rule. One of CONTAINS, ENDS_WITH, EQUAL_TO, REGEX, or STARTS_WITH. CompareType CompareType `json:"compare_type,omitempty"` // The value to use for the comparison. For example, the file type to compare. Value string `json:"value,omitempty"` // The key to use for the comparison. For example, the name of the cookie to evaluate. Key string `json:"key,omitempty"` // When true the logic of the rule is inverted. For example, with invert true, // equal to would become not equal to. Default is false. Invert bool `json:"invert,omitempty"` } // ToRuleUpdateMap builds a request body from UpdateRuleOpts. func (opts UpdateRuleOpts) ToRuleUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "rule") } // UpdateRule allows Rule to be updated. func UpdateRule(c *gophercloud.ServiceClient, policyID string, ruleID string, opts UpdateRuleOptsBuilder) (r UpdateRuleResult) { b, err := opts.ToRuleUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(ruleResourceURL(c, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) return } results.go000066400000000000000000000153011335005366400345240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/l7policiespackage l7policies import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // L7Policy is a collection of L7 rules associated with a Listener, and which // may also have an association to a back-end pool. type L7Policy struct { // The unique ID for the L7 policy. ID string `json:"id"` // Name of the L7 policy. Name string `json:"name"` // The ID of the listener. ListenerID string `json:"listener_id"` // The L7 policy action. One of REDIRECT_TO_POOL, REDIRECT_TO_URL, or REJECT. Action string `json:"action"` // The position of this policy on the listener. Position int32 `json:"position"` // A human-readable description for the resource. Description string `json:"description"` // ProjectID is the UUID of the project who owns the L7 policy in octavia. // Only administrative users can specify a project UUID other than their own. ProjectID string `json:"project_id"` // Requests matching this policy will be redirected to the pool with this ID. // Only valid if action is REDIRECT_TO_POOL. RedirectPoolID string `json:"redirect_pool_id"` // Requests matching this policy will be redirected to this URL. // Only valid if action is REDIRECT_TO_URL. RedirectURL string `json:"redirect_url"` // The administrative state of the L7 policy, which is up (true) or down (false). AdminStateUp bool `json:"admin_state_up"` // Rules are List of associated L7 rule IDs. Rules []Rule `json:"rules"` } // Rule represents layer 7 load balancing rule. type Rule struct { // The unique ID for the L7 rule. ID string `json:"id"` // The L7 rule type. One of COOKIE, FILE_TYPE, HEADER, HOST_NAME, or PATH. RuleType string `json:"type"` // The comparison type for the L7 rule. One of CONTAINS, ENDS_WITH, EQUAL_TO, REGEX, or STARTS_WITH. CompareType string `json:"compare_type"` // The value to use for the comparison. For example, the file type to compare. Value string `json:"value"` // ProjectID is the UUID of the project who owns the rule in octavia. // Only administrative users can specify a project UUID other than their own. ProjectID string `json:"project_id"` // The key to use for the comparison. For example, the name of the cookie to evaluate. Key string `json:"key"` // When true the logic of the rule is inverted. For example, with invert true, // equal to would become not equal to. Default is false. Invert bool `json:"invert"` // The administrative state of the L7 rule, which is up (true) or down (false). AdminStateUp bool `json:"admin_state_up"` } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a l7policy. func (r commonResult) Extract() (*L7Policy, error) { var s struct { L7Policy *L7Policy `json:"l7policy"` } err := r.ExtractInto(&s) return s.L7Policy, err } // CreateResult represents the result of a Create operation. Call its Extract // method to interpret the result as a L7Policy. type CreateResult struct { commonResult } // L7PolicyPage is the page returned by a pager when traversing over a // collection of l7policies. type L7PolicyPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of l7policies has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r L7PolicyPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"l7policies_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a L7PolicyPage struct is empty. func (r L7PolicyPage) IsEmpty() (bool, error) { is, err := ExtractL7Policies(r) return len(is) == 0, err } // ExtractL7Policies accepts a Page struct, specifically a L7PolicyPage struct, // and extracts the elements into a slice of L7Policy structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractL7Policies(r pagination.Page) ([]L7Policy, error) { var s struct { L7Policies []L7Policy `json:"l7policies"` } err := (r.(L7PolicyPage)).ExtractInto(&s) return s.L7Policies, err } // GetResult represents the result of a Get operation. Call its Extract // method to interpret the result as a L7Policy. type GetResult struct { commonResult } // DeleteResult represents the result of a Delete operation. Call its // ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // UpdateResult represents the result of an Update operation. Call its Extract // method to interpret the result as a L7Policy. type UpdateResult struct { commonResult } type commonRuleResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a rule. func (r commonRuleResult) Extract() (*Rule, error) { var s struct { Rule *Rule `json:"rule"` } err := r.ExtractInto(&s) return s.Rule, err } // CreateRuleResult represents the result of a CreateRule operation. // Call its Extract method to interpret it as a Rule. type CreateRuleResult struct { commonRuleResult } // RulePage is the page returned by a pager when traversing over a // collection of Rules in a L7Policy. type RulePage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of rules has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r RulePage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"rules_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a RulePage struct is empty. func (r RulePage) IsEmpty() (bool, error) { is, err := ExtractRules(r) return len(is) == 0, err } // ExtractRules accepts a Page struct, specifically a RulePage struct, // and extracts the elements into a slice of Rules structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractRules(r pagination.Page) ([]Rule, error) { var s struct { Rules []Rule `json:"rules"` } err := (r.(RulePage)).ExtractInto(&s) return s.Rules, err } // GetRuleResult represents the result of a GetRule operation. // Call its Extract method to interpret it as a Rule. type GetRuleResult struct { commonRuleResult } // DeleteRuleResult represents the result of a DeleteRule operation. // Call its ExtractErr method to determine if the request succeeded or failed. type DeleteRuleResult struct { gophercloud.ErrResult } // UpdateRuleResult represents the result of an UpdateRule operation. // Call its Extract method to interpret it as a Rule. type UpdateRuleResult struct { commonRuleResult } testing/000077500000000000000000000000001335005366400341515ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/l7policiesdoc.go000066400000000000000000000000511335005366400352410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/l7policies/testing// l7policies unit tests package testing fixtures.go000066400000000000000000000277371335005366400363710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/l7policies/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // SingleL7PolicyBody is the canned body of a Get request on an existing l7policy. const SingleL7PolicyBody = ` { "l7policy": { "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", "description": "", "admin_state_up": true, "redirect_pool_id": null, "redirect_url": "http://www.example.com", "action": "REDIRECT_TO_URL", "position": 1, "project_id": "e3cd678b11784734bc366148aa37580e", "id": "8a1412f0-4c32-4257-8b07-af4770b604fd", "name": "redirect-example.com", "rules": [] } } ` var ( L7PolicyToURL = l7policies.L7Policy{ ID: "8a1412f0-4c32-4257-8b07-af4770b604fd", Name: "redirect-example.com", ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", Action: "REDIRECT_TO_URL", Position: 1, Description: "", ProjectID: "e3cd678b11784734bc366148aa37580e", RedirectPoolID: "", RedirectURL: "http://www.example.com", AdminStateUp: true, Rules: []l7policies.Rule{}, } L7PolicyToPool = l7policies.L7Policy{ ID: "964f4ba4-f6cd-405c-bebd-639460af7231", Name: "redirect-pool", ListenerID: "be3138a3-5cf7-4513-a4c2-bb137e668bab", Action: "REDIRECT_TO_POOL", Position: 1, Description: "", ProjectID: "c1f7910086964990847dc6c8b128f63c", RedirectPoolID: "bac433c6-5bea-4311-80da-bd1cd90fbd25", RedirectURL: "", AdminStateUp: true, Rules: []l7policies.Rule{}, } L7PolicyUpdated = l7policies.L7Policy{ ID: "8a1412f0-4c32-4257-8b07-af4770b604fd", Name: "NewL7PolicyName", ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", Action: "REDIRECT_TO_URL", Position: 1, Description: "Redirect requests to example.com", ProjectID: "e3cd678b11784734bc366148aa37580e", RedirectPoolID: "", RedirectURL: "http://www.new-example.com", AdminStateUp: true, Rules: []l7policies.Rule{}, } RulePath = l7policies.Rule{ ID: "16621dbb-a736-4888-a57a-3ecd53df784c", RuleType: "PATH", CompareType: "REGEX", Value: "/images*", ProjectID: "e3cd678b11784734bc366148aa37580e", Key: "", Invert: false, AdminStateUp: true, } RuleHostName = l7policies.Rule{ ID: "d24521a0-df84-4468-861a-a531af116d1e", RuleType: "HOST_NAME", CompareType: "EQUAL_TO", Value: "www.example.com", ProjectID: "e3cd678b11784734bc366148aa37580e", Key: "", Invert: false, AdminStateUp: true, } RuleUpdated = l7policies.Rule{ ID: "16621dbb-a736-4888-a57a-3ecd53df784c", RuleType: "PATH", CompareType: "REGEX", Value: "/images/special*", ProjectID: "e3cd678b11784734bc366148aa37580e", Key: "", Invert: false, AdminStateUp: true, } ) // HandleL7PolicyCreationSuccessfully sets up the test server to respond to a l7policy creation request // with a given response. func HandleL7PolicyCreationSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/v2.0/lbaas/l7policies", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "l7policy": { "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", "redirect_url": "http://www.example.com", "name": "redirect-example.com", "action": "REDIRECT_TO_URL" } }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // L7PoliciesListBody contains the canned body of a l7policy list response. const L7PoliciesListBody = ` { "l7policies": [ { "redirect_pool_id": null, "description": "", "admin_state_up": true, "rules": [], "project_id": "e3cd678b11784734bc366148aa37580e", "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", "redirect_url": "http://www.example.com", "action": "REDIRECT_TO_URL", "position": 1, "id": "8a1412f0-4c32-4257-8b07-af4770b604fd", "name": "redirect-example.com" }, { "redirect_pool_id": "bac433c6-5bea-4311-80da-bd1cd90fbd25", "description": "", "admin_state_up": true, "rules": [], "project_id": "c1f7910086964990847dc6c8b128f63c", "listener_id": "be3138a3-5cf7-4513-a4c2-bb137e668bab", "action": "REDIRECT_TO_POOL", "position": 1, "id": "964f4ba4-f6cd-405c-bebd-639460af7231", "name": "redirect-pool" } ] } ` // PostUpdateL7PolicyBody is the canned response body of a Update request on an existing l7policy. const PostUpdateL7PolicyBody = ` { "l7policy": { "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", "description": "Redirect requests to example.com", "admin_state_up": true, "redirect_pool_id": null, "redirect_url": "http://www.new-example.com", "action": "REDIRECT_TO_URL", "position": 1, "project_id": "e3cd678b11784734bc366148aa37580e", "id": "8a1412f0-4c32-4257-8b07-af4770b604fd", "name": "NewL7PolicyName", "rules": [] } } ` // HandleL7PolicyListSuccessfully sets up the test server to respond to a l7policy List request. func HandleL7PolicyListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/l7policies", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, L7PoliciesListBody) case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": fmt.Fprintf(w, `{ "l7policies": [] }`) default: t.Fatalf("/v2.0/lbaas/l7policies invoked with unexpected marker=[%s]", marker) } }) } // HandleL7PolicyGetSuccessfully sets up the test server to respond to a l7policy Get request. func HandleL7PolicyGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, SingleL7PolicyBody) }) } // HandleL7PolicyDeletionSuccessfully sets up the test server to respond to a l7policy deletion request. func HandleL7PolicyDeletionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleL7PolicyUpdateSuccessfully sets up the test server to respond to a l7policy Update request. func HandleL7PolicyUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, `{ "l7policy": { "name": "NewL7PolicyName", "action": "REDIRECT_TO_URL", "redirect_url": "http://www.new-example.com" } }`) fmt.Fprintf(w, PostUpdateL7PolicyBody) }) } // SingleRuleBody is the canned body of a Get request on an existing rule. const SingleRuleBody = ` { "rule": { "compare_type": "REGEX", "invert": false, "admin_state_up": true, "value": "/images*", "key": null, "project_id": "e3cd678b11784734bc366148aa37580e", "type": "PATH", "id": "16621dbb-a736-4888-a57a-3ecd53df784c" } } ` // HandleRuleCreationSuccessfully sets up the test server to respond to a rule creation request // with a given response. func HandleRuleCreationSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "rule": { "compare_type": "REGEX", "type": "PATH", "value": "/images*" } }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // RulesListBody contains the canned body of a rule list response. const RulesListBody = ` { "rules":[ { "compare_type": "REGEX", "invert": false, "admin_state_up": true, "value": "/images*", "key": null, "project_id": "e3cd678b11784734bc366148aa37580e", "type": "PATH", "id": "16621dbb-a736-4888-a57a-3ecd53df784c" }, { "compare_type": "EQUAL_TO", "invert": false, "admin_state_up": true, "value": "www.example.com", "key": null, "project_id": "e3cd678b11784734bc366148aa37580e", "type": "HOST_NAME", "id": "d24521a0-df84-4468-861a-a531af116d1e" } ] } ` // HandleRuleListSuccessfully sets up the test server to respond to a rule List request. func HandleRuleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, RulesListBody) case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": fmt.Fprintf(w, `{ "rules": [] }`) default: t.Fatalf("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules invoked with unexpected marker=[%s]", marker) } }) } // HandleRuleGetSuccessfully sets up the test server to respond to a rule Get request. func HandleRuleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules/16621dbb-a736-4888-a57a-3ecd53df784c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, SingleRuleBody) }) } // HandleRuleDeletionSuccessfully sets up the test server to respond to a rule deletion request. func HandleRuleDeletionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules/16621dbb-a736-4888-a57a-3ecd53df784c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // PostUpdateRuleBody is the canned response body of a Update request on an existing rule. const PostUpdateRuleBody = ` { "rule": { "compare_type": "REGEX", "invert": false, "admin_state_up": true, "value": "/images/special*", "key": null, "project_id": "e3cd678b11784734bc366148aa37580e", "type": "PATH", "id": "16621dbb-a736-4888-a57a-3ecd53df784c" } } ` // HandleRuleUpdateSuccessfully sets up the test server to respond to a rule Update request. func HandleRuleUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules/16621dbb-a736-4888-a57a-3ecd53df784c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, `{ "rule": { "compare_type": "REGEX", "type": "PATH", "value": "/images/special*" } }`) fmt.Fprintf(w, PostUpdateRuleBody) }) } requests_test.go000066400000000000000000000177031335005366400374220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/l7policies/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestCreateL7Policy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleL7PolicyCreationSuccessfully(t, SingleL7PolicyBody) actual, err := l7policies.Create(fake.ServiceClient(), l7policies.CreateOpts{ Name: "redirect-example.com", ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", Action: l7policies.ActionRedirectToURL, RedirectURL: "http://www.example.com", }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, L7PolicyToURL, *actual) } func TestRequiredL7PolicyCreateOpts(t *testing.T) { // no param specified. res := l7policies.Create(fake.ServiceClient(), l7policies.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } // Action is invalid. res = l7policies.Create(fake.ServiceClient(), l7policies.CreateOpts{ ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", Action: l7policies.Action("invalid"), }) if res.Err == nil { t.Fatalf("Expected error, but got none") } } func TestListL7Policies(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleL7PolicyListSuccessfully(t) pages := 0 err := l7policies.List(fake.ServiceClient(), l7policies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := l7policies.ExtractL7Policies(page) if err != nil { return false, err } if len(actual) != 2 { t.Fatalf("Expected 2 l7policies, got %d", len(actual)) } th.CheckDeepEquals(t, L7PolicyToURL, actual[0]) th.CheckDeepEquals(t, L7PolicyToPool, actual[1]) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListAllL7Policies(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleL7PolicyListSuccessfully(t) allPages, err := l7policies.List(fake.ServiceClient(), l7policies.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := l7policies.ExtractL7Policies(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, L7PolicyToURL, actual[0]) th.CheckDeepEquals(t, L7PolicyToPool, actual[1]) } func TestGetL7Policy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleL7PolicyGetSuccessfully(t) client := fake.ServiceClient() actual, err := l7policies.Get(client, "8a1412f0-4c32-4257-8b07-af4770b604fd").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, L7PolicyToURL, *actual) } func TestDeleteL7Policy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleL7PolicyDeletionSuccessfully(t) res := l7policies.Delete(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd") th.AssertNoErr(t, res.Err) } func TestUpdateL7Policy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleL7PolicyUpdateSuccessfully(t) client := fake.ServiceClient() newName := "NewL7PolicyName" actual, err := l7policies.Update(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.UpdateOpts{ Name: &newName, Action: l7policies.ActionRedirectToURL, RedirectURL: "http://www.new-example.com", }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, L7PolicyUpdated, *actual) } func TestUpdateL7PolicyWithInvalidOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() res := l7policies.Update(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.UpdateOpts{ Action: l7policies.Action("invalid"), }) if res.Err == nil { t.Fatalf("Expected error, got none") } } func TestCreateRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleRuleCreationSuccessfully(t, SingleRuleBody) actual, err := l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ RuleType: l7policies.TypePath, CompareType: l7policies.CompareTypeRegex, Value: "/images*", }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, RulePath, *actual) } func TestRequiredRuleCreateOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() res := l7policies.CreateRule(fake.ServiceClient(), "", l7policies.CreateRuleOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ RuleType: l7policies.TypePath, }) if res.Err == nil { t.Fatalf("Expected error, but got none") } res = l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ RuleType: l7policies.RuleType("invalid"), CompareType: l7policies.CompareTypeRegex, Value: "/images*", }) if res.Err == nil { t.Fatalf("Expected error, but got none") } res = l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ RuleType: l7policies.TypePath, CompareType: l7policies.CompareType("invalid"), Value: "/images*", }) if res.Err == nil { t.Fatalf("Expected error, but got none") } } func TestListRules(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleRuleListSuccessfully(t) pages := 0 err := l7policies.ListRules(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.ListRulesOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := l7policies.ExtractRules(page) if err != nil { return false, err } if len(actual) != 2 { t.Fatalf("Expected 2 rules, got %d", len(actual)) } th.CheckDeepEquals(t, RulePath, actual[0]) th.CheckDeepEquals(t, RuleHostName, actual[1]) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListAllRules(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleRuleListSuccessfully(t) allPages, err := l7policies.ListRules(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.ListRulesOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := l7policies.ExtractRules(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, RulePath, actual[0]) th.CheckDeepEquals(t, RuleHostName, actual[1]) } func TestGetRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleRuleGetSuccessfully(t) client := fake.ServiceClient() actual, err := l7policies.GetRule(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, RulePath, *actual) } func TestDeleteRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleRuleDeletionSuccessfully(t) res := l7policies.DeleteRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c") th.AssertNoErr(t, res.Err) } func TestUpdateRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleRuleUpdateSuccessfully(t) client := fake.ServiceClient() actual, err := l7policies.UpdateRule(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c", l7policies.UpdateRuleOpts{ RuleType: l7policies.TypePath, CompareType: l7policies.CompareTypeRegex, Value: "/images/special*", }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, RuleUpdated, *actual) } func TestUpdateRuleWithInvalidOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() res := l7policies.UpdateRule(fake.ServiceClient(), "", "", l7policies.UpdateRuleOpts{ RuleType: l7policies.RuleType("invalid"), }) if res.Err == nil { t.Fatalf("Expected error, got none") } res = l7policies.UpdateRule(fake.ServiceClient(), "", "", l7policies.UpdateRuleOpts{ CompareType: l7policies.CompareType("invalid"), }) if res.Err == nil { t.Fatalf("Expected error, got none") } } urls.go000066400000000000000000000012501335005366400340060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/l7policiespackage l7policies import "github.com/gophercloud/gophercloud" const ( rootPath = "lbaas" resourcePath = "l7policies" rulePath = "rules" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } func ruleRootURL(c *gophercloud.ServiceClient, policyID string) string { return c.ServiceURL(rootPath, resourcePath, policyID, rulePath) } func ruleResourceURL(c *gophercloud.ServiceClient, policyID string, ruleID string) string { return c.ServiceURL(rootPath, resourcePath, policyID, rulePath, ruleID) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/listeners/000077500000000000000000000000001335005366400325115ustar00rootroot00000000000000doc.go000066400000000000000000000032121335005366400335240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/listeners/* Package listeners provides information and interaction with Listeners of the LBaaS v2 extension for the OpenStack Networking service. Example to List Listeners listOpts := listeners.ListOpts{ LoadbalancerID : "ca430f80-1737-4712-8dc6-3f640d55594b", } allPages, err := listeners.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allListeners, err := listeners.ExtractListeners(allPages) if err != nil { panic(err) } for _, listener := range allListeners { fmt.Printf("%+v\n", listener) } Example to Create a Listener createOpts := listeners.CreateOpts{ Protocol: "TCP", Name: "db", LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", AdminStateUp: gophercloud.Enabled, DefaultPoolID: "41efe233-7591-43c5-9cf7-923964759f9e", ProtocolPort: 3306, } listener, err := listeners.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Listener listenerID := "d67d56a6-4a86-4688-a282-f46444705c64" i1001 := 1001 updateOpts := listeners.UpdateOpts{ ConnLimit: &i1001, } listener, err := listeners.Update(networkClient, listenerID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Listener listenerID := "d67d56a6-4a86-4688-a282-f46444705c64" err := listeners.Delete(networkClient, listenerID).ExtractErr() if err != nil { panic(err) } Example to Get the Statistics of a Listener listenerID := "d67d56a6-4a86-4688-a282-f46444705c64" stats, err := listeners.GetStats(networkClient, listenerID).Extract() if err != nil { panic(err) } */ package listeners requests.go000066400000000000000000000155671335005366400346520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/listenerspackage listeners import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Type Protocol represents a listener protocol. type Protocol string // Supported attributes for create/update operations. const ( ProtocolTCP Protocol = "TCP" ProtocolHTTP Protocol = "HTTP" ProtocolHTTPS Protocol = "HTTPS" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToListenerListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the floating IP attributes you want to see returned. SortKey allows you to // sort by a particular listener attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { ID string `q:"id"` Name string `q:"name"` AdminStateUp *bool `q:"admin_state_up"` ProjectID string `q:"project_id"` LoadbalancerID string `q:"loadbalancer_id"` DefaultPoolID string `q:"default_pool_id"` Protocol string `q:"protocol"` ProtocolPort int `q:"protocol_port"` ConnectionLimit int `q:"connection_limit"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToListenerListQuery formats a ListOpts into a query string. func (opts ListOpts) ToListenerListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // listeners. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. // // Default policy settings return only those listeners that are owned by the // project who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { query, err := opts.ToListenerListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return ListenerPage{pagination.LinkedPageBase{PageResult: r}} }) } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToListenerCreateMap() (map[string]interface{}, error) } // CreateOpts represents options for creating a listener. type CreateOpts struct { // The load balancer on which to provision this listener. LoadbalancerID string `json:"loadbalancer_id" required:"true"` // The protocol - can either be TCP, HTTP or HTTPS. Protocol Protocol `json:"protocol" required:"true"` // The port on which to listen for client traffic. ProtocolPort int `json:"protocol_port" required:"true"` // ProjectID is only required if the caller has an admin role and wants // to create a pool for another project. ProjectID string `json:"project_id,omitempty"` // Human-readable name for the Listener. Does not have to be unique. Name string `json:"name,omitempty"` // The ID of the default pool with which the Listener is associated. DefaultPoolID string `json:"default_pool_id,omitempty"` // Human-readable description for the Listener. Description string `json:"description,omitempty"` // The maximum number of connections allowed for the Listener. ConnLimit *int `json:"connection_limit,omitempty"` // A reference to a Barbican container of TLS secrets. DefaultTlsContainerRef string `json:"default_tls_container_ref,omitempty"` // A list of references to TLS secrets. SniContainerRefs []string `json:"sni_container_refs,omitempty"` // The administrative state of the Listener. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToListenerCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToListenerCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "listener") } // Create is an operation which provisions a new Listeners based on the // configuration defined in the CreateOpts struct. Once the request is // validated and progress has started on the provisioning process, a // CreateResult will be returned. // // Users with an admin role can create Listeners on behalf of other projects by // specifying a ProjectID attribute different than their own. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToListenerCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular Listeners based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToListenerUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents options for updating a Listener. type UpdateOpts struct { // Human-readable name for the Listener. Does not have to be unique. Name string `json:"name,omitempty"` // Human-readable description for the Listener. Description string `json:"description,omitempty"` // The maximum number of connections allowed for the Listener. ConnLimit *int `json:"connection_limit,omitempty"` // A reference to a Barbican container of TLS secrets. DefaultTlsContainerRef string `json:"default_tls_container_ref,omitempty"` // A list of references to TLS secrets. SniContainerRefs []string `json:"sni_container_refs,omitempty"` // The administrative state of the Listener. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToListenerUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToListenerUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "listener") } // Update is an operation which modifies the attributes of the specified // Listener. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { b, err := opts.ToListenerUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) return } // Delete will permanently delete a particular Listeners based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } // GetStats will return the shows the current statistics of a particular Listeners. func GetStats(c *gophercloud.ServiceClient, id string) (r StatsResult) { _, r.Err = c.Get(statisticsRootURL(c, id), &r.Body, nil) return } results.go000066400000000000000000000116271335005366400344710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/listenerspackage listeners import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" "github.com/gophercloud/gophercloud/pagination" ) type LoadBalancerID struct { ID string `json:"id"` } // Listener is the primary load balancing configuration object that specifies // the loadbalancer and port on which client traffic is received, as well // as other details such as the load balancing method to be use, protocol, etc. type Listener struct { // The unique ID for the Listener. ID string `json:"id"` // Owner of the Listener. ProjectID string `json:"project_id"` // Human-readable name for the Listener. Does not have to be unique. Name string `json:"name"` // Human-readable description for the Listener. Description string `json:"description"` // The protocol to loadbalance. A valid value is TCP, HTTP, or HTTPS. Protocol string `json:"protocol"` // The port on which to listen to client traffic that is associated with the // Loadbalancer. A valid value is from 0 to 65535. ProtocolPort int `json:"protocol_port"` // The UUID of default pool. Must have compatible protocol with listener. DefaultPoolID string `json:"default_pool_id"` // A list of load balancer IDs. Loadbalancers []LoadBalancerID `json:"loadbalancers"` // The maximum number of connections allowed for the Loadbalancer. // Default is -1, meaning no limit. ConnLimit int `json:"connection_limit"` // The list of references to TLS secrets. SniContainerRefs []string `json:"sni_container_refs"` // A reference to a Barbican container of TLS secrets. DefaultTlsContainerRef string `json:"default_tls_container_ref"` // The administrative state of the Listener. A valid value is true (UP) or false (DOWN). AdminStateUp bool `json:"admin_state_up"` // Pools are the pools which are part of this listener. Pools []pools.Pool `json:"pools"` // The provisioning status of the Listener. // This value is ACTIVE, PENDING_* or ERROR. ProvisioningStatus string `json:"provisioning_status"` } type Stats struct { // The currently active connections. ActiveConnections int `json:"active_connections"` // The total bytes received. BytesIn int `json:"bytes_in"` // The total bytes sent. BytesOut int `json:"bytes_out"` // The total requests that were unable to be fulfilled. RequestErrors int `json:"request_errors"` // The total connections handled. TotalConnections int `json:"total_connections"` } // ListenerPage is the page returned by a pager when traversing over a // collection of listeners. type ListenerPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of listeners has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r ListenerPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"listeners_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a ListenerPage struct is empty. func (r ListenerPage) IsEmpty() (bool, error) { is, err := ExtractListeners(r) return len(is) == 0, err } // ExtractListeners accepts a Page struct, specifically a ListenerPage struct, // and extracts the elements into a slice of Listener structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractListeners(r pagination.Page) ([]Listener, error) { var s struct { Listeners []Listener `json:"listeners"` } err := (r.(ListenerPage)).ExtractInto(&s) return s.Listeners, err } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a listener. func (r commonResult) Extract() (*Listener, error) { var s struct { Listener *Listener `json:"listener"` } err := r.ExtractInto(&s) return s.Listener, err } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a Listener. type CreateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a Listener. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a Listener. type UpdateResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its // ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // StatsResult represents the result of a GetStats operation. // Call its Extract method to interpret it as a Stats. type StatsResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts the status of // a Listener. func (r StatsResult) Extract() (*Stats, error) { var s struct { Stats *Stats `json:"stats"` } err := r.ExtractInto(&s) return s.Stats, err } testing/000077500000000000000000000000001335005366400341075ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/listenersdoc.go000066400000000000000000000000501335005366400351760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/listeners/testing// listeners unit tests package testing fixtures.go000066400000000000000000000220641335005366400363130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/listeners/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListenersListBody contains the canned body of a listeners list response. const ListenersListBody = ` { "listeners":[ { "id": "db902c0c-d5ff-4753-b465-668ad9656918", "project_id": "310df60f-2a10-4ee5-9554-98393092194c", "name": "web", "description": "listener config for the web tier", "loadbalancers": [{"id": "53306cda-815d-4354-9444-59e09da9c3c5"}], "protocol": "HTTP", "protocol_port": 80, "default_pool_id": "fad389a3-9a4a-4762-a365-8c7038508b5d", "admin_state_up": true, "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"] }, { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", "project_id": "310df60f-2a10-4ee5-9554-98393092194c", "name": "db", "description": "listener config for the db tier", "loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], "protocol": "TCP", "protocol_port": 3306, "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e", "connection_limit": 2000, "admin_state_up": true, "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"] } ] } ` // SingleServerBody is the canned body of a Get request on an existing listener. const SingleListenerBody = ` { "listener": { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", "project_id": "310df60f-2a10-4ee5-9554-98393092194c", "name": "db", "description": "listener config for the db tier", "loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], "protocol": "TCP", "protocol_port": 3306, "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e", "connection_limit": 2000, "admin_state_up": true, "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"] } } ` // PostUpdateListenerBody is the canned response body of a Update request on an existing listener. const PostUpdateListenerBody = ` { "listener": { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", "project_id": "310df60f-2a10-4ee5-9554-98393092194c", "name": "NewListenerName", "description": "listener config for the db tier", "loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], "protocol": "TCP", "protocol_port": 3306, "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e", "connection_limit": 1000, "admin_state_up": true, "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"] } } ` // GetListenerStatsBody is the canned request body of a Get request on listener's statistics. const GetListenerStatsBody = ` { "stats": { "active_connections": 0, "bytes_in": 9532, "bytes_out": 22033, "request_errors": 46, "total_connections": 112 } } ` var ( ListenerWeb = listeners.Listener{ ID: "db902c0c-d5ff-4753-b465-668ad9656918", ProjectID: "310df60f-2a10-4ee5-9554-98393092194c", Name: "web", Description: "listener config for the web tier", Loadbalancers: []listeners.LoadBalancerID{{ID: "53306cda-815d-4354-9444-59e09da9c3c5"}}, Protocol: "HTTP", ProtocolPort: 80, DefaultPoolID: "fad389a3-9a4a-4762-a365-8c7038508b5d", AdminStateUp: true, DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", SniContainerRefs: []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"}, } ListenerDb = listeners.Listener{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", ProjectID: "310df60f-2a10-4ee5-9554-98393092194c", Name: "db", Description: "listener config for the db tier", Loadbalancers: []listeners.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, Protocol: "TCP", ProtocolPort: 3306, DefaultPoolID: "41efe233-7591-43c5-9cf7-923964759f9e", ConnLimit: 2000, AdminStateUp: true, DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", SniContainerRefs: []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"}, } ListenerUpdated = listeners.Listener{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", ProjectID: "310df60f-2a10-4ee5-9554-98393092194c", Name: "NewListenerName", Description: "listener config for the db tier", Loadbalancers: []listeners.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, Protocol: "TCP", ProtocolPort: 3306, DefaultPoolID: "41efe233-7591-43c5-9cf7-923964759f9e", ConnLimit: 1000, AdminStateUp: true, DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", SniContainerRefs: []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"}, } ListenerStatsTree = listeners.Stats{ ActiveConnections: 0, BytesIn: 9532, BytesOut: 22033, RequestErrors: 46, TotalConnections: 112, } ) // HandleListenerListSuccessfully sets up the test server to respond to a listener List request. func HandleListenerListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/listeners", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, ListenersListBody) case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": fmt.Fprintf(w, `{ "listeners": [] }`) default: t.Fatalf("/v2.0/lbaas/listeners invoked with unexpected marker=[%s]", marker) } }) } // HandleListenerCreationSuccessfully sets up the test server to respond to a listener creation request // with a given response. func HandleListenerCreationSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/v2.0/lbaas/listeners", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "listener": { "loadbalancer_id": "79e05663-7f03-45d2-a092-8b94062f22ab", "protocol": "TCP", "name": "db", "admin_state_up": true, "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e", "protocol_port": 3306 } }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // HandleListenerGetSuccessfully sets up the test server to respond to a listener Get request. func HandleListenerGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, SingleListenerBody) }) } // HandleListenerDeletionSuccessfully sets up the test server to respond to a listener deletion request. func HandleListenerDeletionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleListenerUpdateSuccessfully sets up the test server to respond to a listener Update request. func HandleListenerUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, `{ "listener": { "name": "NewListenerName", "connection_limit": 1001 } }`) fmt.Fprintf(w, PostUpdateListenerBody) }) } // HandleListenerGetStatsTree sets up the test server to respond to a listener Get stats tree request. func HandleListenerGetStatsTree(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304/stats", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, GetListenerStatsBody) }) } requests_test.go000066400000000000000000000103131335005366400373460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/listeners/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestListListeners(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListenerListSuccessfully(t) pages := 0 err := listeners.List(fake.ServiceClient(), listeners.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := listeners.ExtractListeners(page) if err != nil { return false, err } if len(actual) != 2 { t.Fatalf("Expected 2 listeners, got %d", len(actual)) } th.CheckDeepEquals(t, ListenerWeb, actual[0]) th.CheckDeepEquals(t, ListenerDb, actual[1]) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListAllListeners(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListenerListSuccessfully(t) allPages, err := listeners.List(fake.ServiceClient(), listeners.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := listeners.ExtractListeners(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ListenerWeb, actual[0]) th.CheckDeepEquals(t, ListenerDb, actual[1]) } func TestCreateListener(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListenerCreationSuccessfully(t, SingleListenerBody) actual, err := listeners.Create(fake.ServiceClient(), listeners.CreateOpts{ Protocol: "TCP", Name: "db", LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", AdminStateUp: gophercloud.Enabled, DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", DefaultPoolID: "41efe233-7591-43c5-9cf7-923964759f9e", ProtocolPort: 3306, }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ListenerDb, *actual) } func TestRequiredCreateOpts(t *testing.T) { res := listeners.Create(fake.ServiceClient(), listeners.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo"}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", ProjectID: "bar"}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", ProjectID: "bar", Protocol: "bar"}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", ProjectID: "bar", Protocol: "bar", ProtocolPort: 80}) if res.Err == nil { t.Fatalf("Expected error, got none") } } func TestGetListener(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListenerGetSuccessfully(t) client := fake.ServiceClient() actual, err := listeners.Get(client, "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, ListenerDb, *actual) } func TestDeleteListener(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListenerDeletionSuccessfully(t) res := listeners.Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") th.AssertNoErr(t, res.Err) } func TestUpdateListener(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListenerUpdateSuccessfully(t) client := fake.ServiceClient() i1001 := 1001 actual, err := listeners.Update(client, "4ec89087-d057-4e2c-911f-60a3b47ee304", listeners.UpdateOpts{ Name: "NewListenerName", ConnLimit: &i1001, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, ListenerUpdated, *actual) } func TestGetListenerStatsTree(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListenerGetStatsTree(t) client := fake.ServiceClient() actual, err := listeners.GetStats(client, "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, ListenerStatsTree, *actual) } urls.go000066400000000000000000000010041335005366400337410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/listenerspackage listeners import "github.com/gophercloud/gophercloud" const ( rootPath = "lbaas" resourcePath = "listeners" statisticsPath = "stats" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } func statisticsRootURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id, statisticsPath) } loadbalancers/000077500000000000000000000000001335005366400332145ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2doc.go000066400000000000000000000040051335005366400343070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/loadbalancers/* Package loadbalancers provides information and interaction with Load Balancers of the LBaaS v2 extension for the OpenStack Networking service. Example to List Load Balancers listOpts := loadbalancers.ListOpts{ Provider: "haproxy", } allPages, err := loadbalancers.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allLoadbalancers, err := loadbalancers.ExtractLoadBalancers(allPages) if err != nil { panic(err) } for _, lb := range allLoadbalancers { fmt.Printf("%+v\n", lb) } Example to Create a Load Balancer createOpts := loadbalancers.CreateOpts{ Name: "db_lb", AdminStateUp: gophercloud.Enabled, VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", VipAddress: "10.30.176.48", Flavor: "medium", Provider: "haproxy", } lb, err := loadbalancers.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Load Balancer lbID := "d67d56a6-4a86-4688-a282-f46444705c64" i1001 := 1001 updateOpts := loadbalancers.UpdateOpts{ Name: "new-name", } lb, err := loadbalancers.Update(networkClient, lbID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Load Balancers deleteOpts := loadbalancers.DeleteOpts{ Cascade: true, } lbID := "d67d56a6-4a86-4688-a282-f46444705c64" err := loadbalancers.Delete(networkClient, lbID, deleteOpts).ExtractErr() if err != nil { panic(err) } Example to Get the Status of a Load Balancer lbID := "d67d56a6-4a86-4688-a282-f46444705c64" status, err := loadbalancers.GetStatuses(networkClient, LBID).Extract() if err != nil { panic(err) } Example to Get the Statistics of a Load Balancer lbID := "d67d56a6-4a86-4688-a282-f46444705c64" stats, err := loadbalancers.GetStats(networkClient, LBID).Extract() if err != nil { panic(err) } Example to Failover a Load Balancers lbID := "d67d56a6-4a86-4688-a282-f46444705c64" err := loadbalancers.Failover(networkClient, lbID).ExtractErr() if err != nil { panic(err) } */ package loadbalancers requests.go000066400000000000000000000166431335005366400354300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/loadbalancerspackage loadbalancers import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToLoadBalancerListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the Loadbalancer attributes you want to see returned. SortKey allows you to // sort by a particular attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { Description string `q:"description"` AdminStateUp *bool `q:"admin_state_up"` ProjectID string `q:"project_id"` ProvisioningStatus string `q:"provisioning_status"` VipAddress string `q:"vip_address"` VipPortID string `q:"vip_port_id"` VipSubnetID string `q:"vip_subnet_id"` ID string `q:"id"` OperatingStatus string `q:"operating_status"` Name string `q:"name"` Flavor string `q:"flavor"` Provider string `q:"provider"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToLoadBalancerListQuery formats a ListOpts into a query string. func (opts ListOpts) ToLoadBalancerListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // load balancers. It accepts a ListOpts struct, which allows you to filter // and sort the returned collection for greater efficiency. // // Default policy settings return only those load balancers that are owned by // the project who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { query, err := opts.ToLoadBalancerListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return LoadBalancerPage{pagination.LinkedPageBase{PageResult: r}} }) } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToLoadBalancerCreateMap() (map[string]interface{}, error) } // CreateOpts is the common options struct used in this package's Create // operation. type CreateOpts struct { // Human-readable name for the Loadbalancer. Does not have to be unique. Name string `json:"name,omitempty"` // Human-readable description for the Loadbalancer. Description string `json:"description,omitempty"` // The network on which to allocate the Loadbalancer's address. A project can // only create Loadbalancers on networks authorized by policy (e.g. networks // that belong to them or networks that are shared). VipSubnetID string `json:"vip_subnet_id" required:"true"` // ProjectID is the UUID of the project who owns the Loadbalancer. // Only administrative users can specify a project UUID other than their own. ProjectID string `json:"project_id,omitempty"` // The IP address of the Loadbalancer. VipAddress string `json:"vip_address,omitempty"` // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` // The UUID of a flavor. Flavor string `json:"flavor,omitempty"` // The name of the provider. Provider string `json:"provider,omitempty"` } // ToLoadBalancerCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToLoadBalancerCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "loadbalancer") } // Create is an operation which provisions a new loadbalancer based on the // configuration defined in the CreateOpts struct. Once the request is // validated and progress has started on the provisioning process, a // CreateResult will be returned. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToLoadBalancerCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular Loadbalancer based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToLoadBalancerUpdateMap() (map[string]interface{}, error) } // UpdateOpts is the common options struct used in this package's Update // operation. type UpdateOpts struct { // Human-readable name for the Loadbalancer. Does not have to be unique. Name string `json:"name,omitempty"` // Human-readable description for the Loadbalancer. Description string `json:"description,omitempty"` // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToLoadBalancerUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToLoadBalancerUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "loadbalancer") } // Update is an operation which modifies the attributes of the specified // LoadBalancer. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { b, err := opts.ToLoadBalancerUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) return } // DeleteOptsBuilder allows extensions to add additional parameters to the // Delete request. type DeleteOptsBuilder interface { ToLoadBalancerDeleteQuery() (string, error) } // DeleteOpts is the common options struct used in this package's Delete // operation. type DeleteOpts struct { // Cascade will delete all children of the load balancer (listners, monitors, etc). Cascade bool `q:"cascade"` } // ToLoadBalancerDeleteQuery formats a DeleteOpts into a query string. func (opts DeleteOpts) ToLoadBalancerDeleteQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // Delete will permanently delete a particular LoadBalancer based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { url := resourceURL(c, id) if opts != nil { query, err := opts.ToLoadBalancerDeleteQuery() if err != nil { r.Err = err return } url += query } _, r.Err = c.Delete(url, nil) return } // GetStatuses will return the status of a particular LoadBalancer. func GetStatuses(c *gophercloud.ServiceClient, id string) (r GetStatusesResult) { _, r.Err = c.Get(statusRootURL(c, id), &r.Body, nil) return } // GetStats will return the shows the current statistics of a particular LoadBalancer. func GetStats(c *gophercloud.ServiceClient, id string) (r StatsResult) { _, r.Err = c.Get(statisticsRootURL(c, id), &r.Body, nil) return } // Failover performs a failover of a load balancer. func Failover(c *gophercloud.ServiceClient, id string) (r FailoverResult) { _, r.Err = c.Put(failoverRootURL(c, id), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return } results.go000066400000000000000000000130211335005366400352410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/loadbalancerspackage loadbalancers import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" "github.com/gophercloud/gophercloud/pagination" ) // LoadBalancer is the primary load balancing configuration object that // specifies the virtual IP address on which client traffic is received, as well // as other details such as the load balancing method to be use, protocol, etc. type LoadBalancer struct { // Human-readable description for the Loadbalancer. Description string `json:"description"` // The administrative state of the Loadbalancer. // A valid value is true (UP) or false (DOWN). AdminStateUp bool `json:"admin_state_up"` // Owner of the LoadBalancer. ProjectID string `json:"project_id"` // The provisioning status of the LoadBalancer. // This value is ACTIVE, PENDING_CREATE or ERROR. ProvisioningStatus string `json:"provisioning_status"` // The IP address of the Loadbalancer. VipAddress string `json:"vip_address"` // The UUID of the port associated with the IP address. VipPortID string `json:"vip_port_id"` // The UUID of the subnet on which to allocate the virtual IP for the // Loadbalancer address. VipSubnetID string `json:"vip_subnet_id"` // The unique ID for the LoadBalancer. ID string `json:"id"` // The operating status of the LoadBalancer. This value is ONLINE or OFFLINE. OperatingStatus string `json:"operating_status"` // Human-readable name for the LoadBalancer. Does not have to be unique. Name string `json:"name"` // The UUID of a flavor if set. Flavor string `json:"flavor"` // The name of the provider. Provider string `json:"provider"` // Listeners are the listeners related to this Loadbalancer. Listeners []listeners.Listener `json:"listeners"` } // StatusTree represents the status of a loadbalancer. type StatusTree struct { Loadbalancer *LoadBalancer `json:"loadbalancer"` } type Stats struct { // The currently active connections. ActiveConnections int `json:"active_connections"` // The total bytes received. BytesIn int `json:"bytes_in"` // The total bytes sent. BytesOut int `json:"bytes_out"` // The total requests that were unable to be fulfilled. RequestErrors int `json:"request_errors"` // The total connections handled. TotalConnections int `json:"total_connections"` } // LoadBalancerPage is the page returned by a pager when traversing over a // collection of load balancers. type LoadBalancerPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of load balancers has // reached the end of a page and the pager seeks to traverse over a new one. // In order to do this, it needs to construct the next page's URL. func (r LoadBalancerPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"loadbalancers_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a LoadBalancerPage struct is empty. func (r LoadBalancerPage) IsEmpty() (bool, error) { is, err := ExtractLoadBalancers(r) return len(is) == 0, err } // ExtractLoadBalancers accepts a Page struct, specifically a LoadbalancerPage // struct, and extracts the elements into a slice of LoadBalancer structs. In // other words, a generic collection is mapped into a relevant slice. func ExtractLoadBalancers(r pagination.Page) ([]LoadBalancer, error) { var s struct { LoadBalancers []LoadBalancer `json:"loadbalancers"` } err := (r.(LoadBalancerPage)).ExtractInto(&s) return s.LoadBalancers, err } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a loadbalancer. func (r commonResult) Extract() (*LoadBalancer, error) { var s struct { LoadBalancer *LoadBalancer `json:"loadbalancer"` } err := r.ExtractInto(&s) return s.LoadBalancer, err } // GetStatusesResult represents the result of a GetStatuses operation. // Call its Extract method to interpret it as a StatusTree. type GetStatusesResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts the status of // a Loadbalancer. func (r GetStatusesResult) Extract() (*StatusTree, error) { var s struct { Statuses *StatusTree `json:"statuses"` } err := r.ExtractInto(&s) return s.Statuses, err } // StatsResult represents the result of a GetStats operation. // Call its Extract method to interpret it as a Stats. type StatsResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts the status of // a Loadbalancer. func (r StatsResult) Extract() (*Stats, error) { var s struct { Stats *Stats `json:"stats"` } err := r.ExtractInto(&s) return s.Stats, err } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a LoadBalancer. type CreateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a LoadBalancer. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a LoadBalancer. type UpdateResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its // ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // FailoverResult represents the result of a failover operation. Call its // ExtractErr method to determine if the request succeeded or failed. type FailoverResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400346715ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/loadbalancersdoc.go000066400000000000000000000000541335005366400357640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/loadbalancers/testing// loadbalancers unit tests package testing fixtures.go000066400000000000000000000264311335005366400370770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/loadbalancers/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" ) // LoadbalancersListBody contains the canned body of a loadbalancer list response. const LoadbalancersListBody = ` { "loadbalancers":[ { "id": "c331058c-6a40-4144-948e-b9fb1df9db4b", "project_id": "54030507-44f7-473c-9342-b4d14a95f692", "name": "web_lb", "description": "lb config for the web tier", "vip_subnet_id": "8a49c438-848f-467b-9655-ea1548708154", "vip_address": "10.30.176.47", "vip_port_id": "2a22e552-a347-44fd-b530-1f2b1b2a6735", "flavor": "small", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "ACTIVE", "operating_status": "ONLINE" }, { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", "project_id": "54030507-44f7-473c-9342-b4d14a95f692", "name": "db_lb", "description": "lb config for the db tier", "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", "vip_address": "10.30.176.48", "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", "flavor": "medium", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "PENDING_CREATE", "operating_status": "OFFLINE" } ] } ` // SingleLoadbalancerBody is the canned body of a Get request on an existing loadbalancer. const SingleLoadbalancerBody = ` { "loadbalancer": { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", "project_id": "54030507-44f7-473c-9342-b4d14a95f692", "name": "db_lb", "description": "lb config for the db tier", "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", "vip_address": "10.30.176.48", "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", "flavor": "medium", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "PENDING_CREATE", "operating_status": "OFFLINE" } } ` // PostUpdateLoadbalancerBody is the canned response body of a Update request on an existing loadbalancer. const PostUpdateLoadbalancerBody = ` { "loadbalancer": { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", "project_id": "54030507-44f7-473c-9342-b4d14a95f692", "name": "NewLoadbalancerName", "description": "lb config for the db tier", "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", "vip_address": "10.30.176.48", "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", "flavor": "medium", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "PENDING_CREATE", "operating_status": "OFFLINE" } } ` // GetLoadbalancerStatusesBody is the canned request body of a Get request on loadbalancer's status. const GetLoadbalancerStatusesBody = ` { "statuses" : { "loadbalancer": { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", "name": "db_lb", "provisioning_status": "PENDING_UPDATE", "operating_status": "ACTIVE", "listeners": [{ "id": "db902c0c-d5ff-4753-b465-668ad9656918", "name": "db", "provisioning_status": "ACTIVE", "pools": [{ "id": "fad389a3-9a4a-4762-a365-8c7038508b5d", "name": "db", "provisioning_status": "ACTIVE", "healthmonitor": { "id": "67306cda-815d-4354-9fe4-59e09da9c3c5", "type":"PING", "provisioning_status": "ACTIVE" }, "members":[{ "id": "2a280670-c202-4b0b-a562-34077415aabf", "name": "db", "address": "10.0.2.11", "protocol_port": 80, "provisioning_status": "ACTIVE" }] }] }] } } } ` // LoadbalancerStatsTree is the canned request body of a Get request on loadbalancer's statistics. const GetLoadbalancerStatsBody = ` { "stats": { "active_connections": 0, "bytes_in": 9532, "bytes_out": 22033, "request_errors": 46, "total_connections": 112 } } ` var ( LoadbalancerWeb = loadbalancers.LoadBalancer{ ID: "c331058c-6a40-4144-948e-b9fb1df9db4b", ProjectID: "54030507-44f7-473c-9342-b4d14a95f692", Name: "web_lb", Description: "lb config for the web tier", VipSubnetID: "8a49c438-848f-467b-9655-ea1548708154", VipAddress: "10.30.176.47", VipPortID: "2a22e552-a347-44fd-b530-1f2b1b2a6735", Flavor: "small", Provider: "haproxy", AdminStateUp: true, ProvisioningStatus: "ACTIVE", OperatingStatus: "ONLINE", } LoadbalancerDb = loadbalancers.LoadBalancer{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", ProjectID: "54030507-44f7-473c-9342-b4d14a95f692", Name: "db_lb", Description: "lb config for the db tier", VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", VipAddress: "10.30.176.48", VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55", Flavor: "medium", Provider: "haproxy", AdminStateUp: true, ProvisioningStatus: "PENDING_CREATE", OperatingStatus: "OFFLINE", } LoadbalancerUpdated = loadbalancers.LoadBalancer{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", ProjectID: "54030507-44f7-473c-9342-b4d14a95f692", Name: "NewLoadbalancerName", Description: "lb config for the db tier", VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", VipAddress: "10.30.176.48", VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55", Flavor: "medium", Provider: "haproxy", AdminStateUp: true, ProvisioningStatus: "PENDING_CREATE", OperatingStatus: "OFFLINE", } LoadbalancerStatusesTree = loadbalancers.StatusTree{ Loadbalancer: &loadbalancers.LoadBalancer{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", Name: "db_lb", ProvisioningStatus: "PENDING_UPDATE", OperatingStatus: "ACTIVE", Listeners: []listeners.Listener{{ ID: "db902c0c-d5ff-4753-b465-668ad9656918", Name: "db", ProvisioningStatus: "ACTIVE", Pools: []pools.Pool{{ ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", Name: "db", ProvisioningStatus: "ACTIVE", Monitor: monitors.Monitor{ ID: "67306cda-815d-4354-9fe4-59e09da9c3c5", Type: "PING", ProvisioningStatus: "ACTIVE", }, Members: []pools.Member{{ ID: "2a280670-c202-4b0b-a562-34077415aabf", Name: "db", Address: "10.0.2.11", ProtocolPort: 80, ProvisioningStatus: "ACTIVE", }}, }}, }}, }, } LoadbalancerStatsTree = loadbalancers.Stats{ ActiveConnections: 0, BytesIn: 9532, BytesOut: 22033, RequestErrors: 46, TotalConnections: 112, } ) // HandleLoadbalancerListSuccessfully sets up the test server to respond to a loadbalancer List request. func HandleLoadbalancerListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, LoadbalancersListBody) case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": fmt.Fprintf(w, `{ "loadbalancers": [] }`) default: t.Fatalf("/v2.0/lbaas/loadbalancers invoked with unexpected marker=[%s]", marker) } }) } // HandleLoadbalancerCreationSuccessfully sets up the test server to respond to a loadbalancer creation request // with a given response. func HandleLoadbalancerCreationSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "loadbalancer": { "name": "db_lb", "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", "vip_address": "10.30.176.48", "flavor": "medium", "provider": "haproxy", "admin_state_up": true } }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // HandleLoadbalancerGetSuccessfully sets up the test server to respond to a loadbalancer Get request. func HandleLoadbalancerGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, SingleLoadbalancerBody) }) } // HandleLoadbalancerGetStatusesTree sets up the test server to respond to a loadbalancer Get statuses tree request. func HandleLoadbalancerGetStatusesTree(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab/status", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, GetLoadbalancerStatusesBody) }) } // HandleLoadbalancerDeletionSuccessfully sets up the test server to respond to a loadbalancer deletion request. func HandleLoadbalancerDeletionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleLoadbalancerUpdateSuccessfully sets up the test server to respond to a loadbalancer Update request. func HandleLoadbalancerUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, `{ "loadbalancer": { "name": "NewLoadbalancerName" } }`) fmt.Fprintf(w, PostUpdateLoadbalancerBody) }) } // HandleLoadbalancerGetStatsTree sets up the test server to respond to a loadbalancer Get stats tree request. func HandleLoadbalancerGetStatsTree(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab/stats", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, GetLoadbalancerStatsBody) }) } // HandleLoadbalancerFailoverSuccessfully sets up the test server to respond to a loadbalancer failover request. func HandleLoadbalancerFailoverSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab/failover", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000122021335005366400401270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/loadbalancers/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers" fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestListLoadbalancers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleLoadbalancerListSuccessfully(t) pages := 0 err := loadbalancers.List(fake.ServiceClient(), loadbalancers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := loadbalancers.ExtractLoadBalancers(page) if err != nil { return false, err } if len(actual) != 2 { t.Fatalf("Expected 2 loadbalancers, got %d", len(actual)) } th.CheckDeepEquals(t, LoadbalancerWeb, actual[0]) th.CheckDeepEquals(t, LoadbalancerDb, actual[1]) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListAllLoadbalancers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleLoadbalancerListSuccessfully(t) allPages, err := loadbalancers.List(fake.ServiceClient(), loadbalancers.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := loadbalancers.ExtractLoadBalancers(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, LoadbalancerWeb, actual[0]) th.CheckDeepEquals(t, LoadbalancerDb, actual[1]) } func TestCreateLoadbalancer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleLoadbalancerCreationSuccessfully(t, SingleLoadbalancerBody) actual, err := loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{ Name: "db_lb", AdminStateUp: gophercloud.Enabled, VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", VipAddress: "10.30.176.48", Flavor: "medium", Provider: "haproxy", }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, LoadbalancerDb, *actual) } func TestRequiredCreateOpts(t *testing.T) { res := loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo"}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo", Description: "bar"}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo", Description: "bar", VipAddress: "bar"}) if res.Err == nil { t.Fatalf("Expected error, got none") } } func TestGetLoadbalancer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleLoadbalancerGetSuccessfully(t) client := fake.ServiceClient() actual, err := loadbalancers.Get(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, LoadbalancerDb, *actual) } func TestGetLoadbalancerStatusesTree(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleLoadbalancerGetStatusesTree(t) client := fake.ServiceClient() actual, err := loadbalancers.GetStatuses(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, LoadbalancerStatusesTree, *actual) } func TestDeleteLoadbalancer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleLoadbalancerDeletionSuccessfully(t) res := loadbalancers.Delete(fake.ServiceClient(), "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", nil) th.AssertNoErr(t, res.Err) } func TestUpdateLoadbalancer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleLoadbalancerUpdateSuccessfully(t) client := fake.ServiceClient() actual, err := loadbalancers.Update(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", loadbalancers.UpdateOpts{ Name: "NewLoadbalancerName", }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, LoadbalancerUpdated, *actual) } func TestCascadingDeleteLoadbalancer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleLoadbalancerDeletionSuccessfully(t) sc := fake.ServiceClient() deleteOpts := loadbalancers.DeleteOpts{ Cascade: true, } query, err := deleteOpts.ToLoadBalancerDeleteQuery() th.AssertNoErr(t, err) th.AssertEquals(t, query, "?cascade=true") err = loadbalancers.Delete(sc, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", deleteOpts).ExtractErr() th.AssertNoErr(t, err) } func TestGetLoadbalancerStatsTree(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleLoadbalancerGetStatsTree(t) client := fake.ServiceClient() actual, err := loadbalancers.GetStats(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, LoadbalancerStatsTree, *actual) } func TestFailoverLoadbalancer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleLoadbalancerFailoverSuccessfully(t) res := loadbalancers.Failover(fake.ServiceClient(), "36e08a3e-a78f-4b40-a229-1e7e23eee1ab") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000015221335005366400345300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/loadbalancerspackage loadbalancers import "github.com/gophercloud/gophercloud" const ( rootPath = "lbaas" resourcePath = "loadbalancers" statusPath = "status" statisticsPath = "stats" failoverPath = "failover" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } func statusRootURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id, statusPath) } func statisticsRootURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id, statisticsPath) } func failoverRootURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id, failoverPath) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/monitors/000077500000000000000000000000001335005366400323535ustar00rootroot00000000000000doc.go000066400000000000000000000027431335005366400333760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/monitors/* Package monitors provides information and interaction with Monitors of the LBaaS v2 extension for the OpenStack Networking service. Example to List Monitors listOpts := monitors.ListOpts{ PoolID: "c79a4468-d788-410c-bf79-9a8ef6354852", } allPages, err := monitors.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allMonitors, err := monitors.ExtractMonitors(allPages) if err != nil { panic(err) } for _, monitor := range allMonitors { fmt.Printf("%+v\n", monitor) } Example to Create a Monitor createOpts := monitors.CreateOpts{ Type: "HTTP", Name: "db", PoolID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", Delay: 20, Timeout: 10, MaxRetries: 5, URLPath: "/check", ExpectedCodes: "200-299", } monitor, err := monitors.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Monitor monitorID := "d67d56a6-4a86-4688-a282-f46444705c64" updateOpts := monitors.UpdateOpts{ Name: "NewHealthmonitorName", Delay: 3, Timeout: 20, MaxRetries: 10, URLPath: "/another_check", ExpectedCodes: "301", } monitor, err := monitors.Update(networkClient, monitorID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Monitor monitorID := "d67d56a6-4a86-4688-a282-f46444705c64" err := monitors.Delete(networkClient, monitorID).ExtractErr() if err != nil { panic(err) } */ package monitors requests.go000066400000000000000000000207761335005366400345120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/monitorspackage monitors import ( "fmt" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToMonitorListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the Monitor attributes you want to see returned. SortKey allows you to // sort by a particular Monitor attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { ID string `q:"id"` Name string `q:"name"` TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` PoolID string `q:"pool_id"` Type string `q:"type"` Delay int `q:"delay"` Timeout int `q:"timeout"` MaxRetries int `q:"max_retries"` HTTPMethod string `q:"http_method"` URLPath string `q:"url_path"` ExpectedCodes string `q:"expected_codes"` AdminStateUp *bool `q:"admin_state_up"` Status string `q:"status"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToMonitorListQuery formats a ListOpts into a query string. func (opts ListOpts) ToMonitorListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return "", err } return q.String(), nil } // List returns a Pager which allows you to iterate over a collection of // health monitors. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. // // Default policy settings return only those health monitors that are owned by the // tenant who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { query, err := opts.ToMonitorListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return MonitorPage{pagination.LinkedPageBase{PageResult: r}} }) } // Constants that represent approved monitoring types. const ( TypePING = "PING" TypeTCP = "TCP" TypeHTTP = "HTTP" TypeHTTPS = "HTTPS" ) var ( errDelayMustGETimeout = fmt.Errorf("Delay must be greater than or equal to timeout") ) // CreateOptsBuilder allows extensions to add additional parameters to the // List request. type CreateOptsBuilder interface { ToMonitorCreateMap() (map[string]interface{}, error) } // CreateOpts is the common options struct used in this package's Create // operation. type CreateOpts struct { // The Pool to Monitor. PoolID string `json:"pool_id" required:"true"` // The type of probe, which is PING, TCP, HTTP, or HTTPS, that is // sent by the load balancer to verify the member state. Type string `json:"type" required:"true"` // The time, in seconds, between sending probes to members. Delay int `json:"delay" required:"true"` // Maximum number of seconds for a Monitor to wait for a ping reply // before it times out. The value must be less than the delay value. Timeout int `json:"timeout" required:"true"` // Number of permissible ping failures before changing the member's // status to INACTIVE. Must be a number between 1 and 10. MaxRetries int `json:"max_retries" required:"true"` // URI path that will be accessed if Monitor type is HTTP or HTTPS. // Required for HTTP(S) types. URLPath string `json:"url_path,omitempty"` // The HTTP method used for requests by the Monitor. If this attribute // is not specified, it defaults to "GET". Required for HTTP(S) types. HTTPMethod string `json:"http_method,omitempty"` // Expected HTTP codes for a passing HTTP(S) Monitor. You can either specify // a single status like "200", or a range like "200-202". Required for HTTP(S) // types. ExpectedCodes string `json:"expected_codes,omitempty"` // TenantID is the UUID of the project who owns the Monitor. // Only administrative users can specify a project UUID other than their own. TenantID string `json:"tenant_id,omitempty"` // ProjectID is the UUID of the project who owns the Monitor. // Only administrative users can specify a project UUID other than their own. ProjectID string `json:"project_id,omitempty"` // The Name of the Monitor. Name string `json:"name,omitempty"` // The administrative state of the Monitor. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToMonitorCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToMonitorCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "healthmonitor") if err != nil { return nil, err } switch opts.Type { case TypeHTTP, TypeHTTPS: switch opts.URLPath { case "": return nil, fmt.Errorf("URLPath must be provided for HTTP and HTTPS") } switch opts.ExpectedCodes { case "": return nil, fmt.Errorf("ExpectedCodes must be provided for HTTP and HTTPS") } } return b, nil } /* Create is an operation which provisions a new Health Monitor. There are different types of Monitor you can provision: PING, TCP or HTTP(S). Below are examples of how to create each one. Here is an example config struct to use when creating a PING or TCP Monitor: CreateOpts{Type: TypePING, Delay: 20, Timeout: 10, MaxRetries: 3} CreateOpts{Type: TypeTCP, Delay: 20, Timeout: 10, MaxRetries: 3} Here is an example config struct to use when creating a HTTP(S) Monitor: CreateOpts{Type: TypeHTTP, Delay: 20, Timeout: 10, MaxRetries: 3, HttpMethod: "HEAD", ExpectedCodes: "200", PoolID: "2c946bfc-1804-43ab-a2ff-58f6a762b505"} */ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToMonitorCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular Health Monitor based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToMonitorUpdateMap() (map[string]interface{}, error) } // UpdateOpts is the common options struct used in this package's Update // operation. type UpdateOpts struct { // The time, in seconds, between sending probes to members. Delay int `json:"delay,omitempty"` // Maximum number of seconds for a Monitor to wait for a ping reply // before it times out. The value must be less than the delay value. Timeout int `json:"timeout,omitempty"` // Number of permissible ping failures before changing the member's // status to INACTIVE. Must be a number between 1 and 10. MaxRetries int `json:"max_retries,omitempty"` // URI path that will be accessed if Monitor type is HTTP or HTTPS. // Required for HTTP(S) types. URLPath string `json:"url_path,omitempty"` // The HTTP method used for requests by the Monitor. If this attribute // is not specified, it defaults to "GET". Required for HTTP(S) types. HTTPMethod string `json:"http_method,omitempty"` // Expected HTTP codes for a passing HTTP(S) Monitor. You can either specify // a single status like "200", or a range like "200-202". Required for HTTP(S) // types. ExpectedCodes string `json:"expected_codes,omitempty"` // The Name of the Monitor. Name string `json:"name,omitempty"` // The administrative state of the Monitor. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToMonitorUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToMonitorUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "healthmonitor") } // Update is an operation which modifies the attributes of the specified // Monitor. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToMonitorUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) return } // Delete will permanently delete a particular Monitor based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } results.go000066400000000000000000000114301335005366400343230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/monitorspackage monitors import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type PoolID struct { ID string `json:"id"` } // Monitor represents a load balancer health monitor. A health monitor is used // to determine whether or not back-end members of the VIP's pool are usable // for processing a request. A pool can have several health monitors associated // with it. There are different types of health monitors supported: // // PING: used to ping the members using ICMP. // TCP: used to connect to the members using TCP. // HTTP: used to send an HTTP request to the member. // HTTPS: used to send a secure HTTP request to the member. // // When a pool has several monitors associated with it, each member of the pool // is monitored by all these monitors. If any monitor declares the member as // unhealthy, then the member status is changed to INACTIVE and the member // won't participate in its pool's load balancing. In other words, ALL monitors // must declare the member to be healthy for it to stay ACTIVE. type Monitor struct { // The unique ID for the Monitor. ID string `json:"id"` // The Name of the Monitor. Name string `json:"name"` // The owner of the Monitor. ProjectID string `json:"project_id"` // The type of probe sent by the load balancer to verify the member state, // which is PING, TCP, HTTP, or HTTPS. Type string `json:"type"` // The time, in seconds, between sending probes to members. Delay int `json:"delay"` // The maximum number of seconds for a monitor to wait for a connection to be // established before it times out. This value must be less than the delay // value. Timeout int `json:"timeout"` // Number of allowed connection failures before changing the status of the // member to INACTIVE. A valid value is from 1 to 10. MaxRetries int `json:"max_retries"` // The HTTP method that the monitor uses for requests. HTTPMethod string `json:"http_method"` // The HTTP path of the request sent by the monitor to test the health of a // member. Must be a string beginning with a forward slash (/). URLPath string `json:"url_path" ` // Expected HTTP codes for a passing HTTP(S) monitor. ExpectedCodes string `json:"expected_codes"` // The administrative state of the health monitor, which is up (true) or // down (false). AdminStateUp bool `json:"admin_state_up"` // The status of the health monitor. Indicates whether the health monitor is // operational. Status string `json:"status"` // List of pools that are associated with the health monitor. Pools []PoolID `json:"pools"` // The provisioning status of the Monitor. // This value is ACTIVE, PENDING_* or ERROR. ProvisioningStatus string `json:"provisioning_status"` } // MonitorPage is the page returned by a pager when traversing over a // collection of health monitors. type MonitorPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of monitors has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r MonitorPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"healthmonitors_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a MonitorPage struct is empty. func (r MonitorPage) IsEmpty() (bool, error) { is, err := ExtractMonitors(r) return len(is) == 0, err } // ExtractMonitors accepts a Page struct, specifically a MonitorPage struct, // and extracts the elements into a slice of Monitor structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractMonitors(r pagination.Page) ([]Monitor, error) { var s struct { Monitors []Monitor `json:"healthmonitors"` } err := (r.(MonitorPage)).ExtractInto(&s) return s.Monitors, err } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a monitor. func (r commonResult) Extract() (*Monitor, error) { var s struct { Monitor *Monitor `json:"healthmonitor"` } err := r.ExtractInto(&s) return s.Monitor, err } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a Monitor. type CreateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a Monitor. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a Monitor. type UpdateResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its // ExtractErr method to determine if the result succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400337515ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/monitorsdoc.go000066400000000000000000000000471335005366400350460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/monitors/testing// monitors unit tests package testing fixtures.go000066400000000000000000000146451335005366400361630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/monitors/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // HealthmonitorsListBody contains the canned body of a healthmonitor list response. const HealthmonitorsListBody = ` { "healthmonitors":[ { "admin_state_up":true, "project_id":"83657cfcdfe44cd5920adaf26c48ceea", "delay":10, "name":"web", "max_retries":1, "timeout":1, "type":"PING", "pools": [{"id": "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}], "id":"466c8345-28d8-4f84-a246-e04380b0461d" }, { "admin_state_up":true, "project_id":"83657cfcdfe44cd5920adaf26c48ceea", "delay":5, "name":"db", "expected_codes":"200", "max_retries":2, "http_method":"GET", "timeout":2, "url_path":"/", "type":"HTTP", "pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}], "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7" } ] } ` // SingleHealthmonitorBody is the canned body of a Get request on an existing healthmonitor. const SingleHealthmonitorBody = ` { "healthmonitor": { "admin_state_up":true, "project_id":"83657cfcdfe44cd5920adaf26c48ceea", "delay":5, "name":"db", "expected_codes":"200", "max_retries":2, "http_method":"GET", "timeout":2, "url_path":"/", "type":"HTTP", "pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}], "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7" } } ` // PostUpdateHealthmonitorBody is the canned response body of a Update request on an existing healthmonitor. const PostUpdateHealthmonitorBody = ` { "healthmonitor": { "admin_state_up":true, "project_id":"83657cfcdfe44cd5920adaf26c48ceea", "delay":3, "name":"NewHealthmonitorName", "expected_codes":"301", "max_retries":10, "http_method":"GET", "timeout":20, "url_path":"/another_check", "type":"HTTP", "pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}], "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7" } } ` var ( HealthmonitorWeb = monitors.Monitor{ AdminStateUp: true, Name: "web", ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", Delay: 10, MaxRetries: 1, Timeout: 1, Type: "PING", ID: "466c8345-28d8-4f84-a246-e04380b0461d", Pools: []monitors.PoolID{{ID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}}, } HealthmonitorDb = monitors.Monitor{ AdminStateUp: true, Name: "db", ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", Delay: 5, ExpectedCodes: "200", MaxRetries: 2, Timeout: 2, URLPath: "/", Type: "HTTP", HTTPMethod: "GET", ID: "5d4b5228-33b0-4e60-b225-9b727c1a20e7", Pools: []monitors.PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}}, } HealthmonitorUpdated = monitors.Monitor{ AdminStateUp: true, Name: "NewHealthmonitorName", ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", Delay: 3, ExpectedCodes: "301", MaxRetries: 10, Timeout: 20, URLPath: "/another_check", Type: "HTTP", HTTPMethod: "GET", ID: "5d4b5228-33b0-4e60-b225-9b727c1a20e7", Pools: []monitors.PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}}, } ) // HandleHealthmonitorListSuccessfully sets up the test server to respond to a healthmonitor List request. func HandleHealthmonitorListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, HealthmonitorsListBody) case "556c8345-28d8-4f84-a246-e04380b0461d": fmt.Fprintf(w, `{ "healthmonitors": [] }`) default: t.Fatalf("/v2.0/lbaas/healthmonitors invoked with unexpected marker=[%s]", marker) } }) } // HandleHealthmonitorCreationSuccessfully sets up the test server to respond to a healthmonitor creation request // with a given response. func HandleHealthmonitorCreationSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "healthmonitor": { "type":"HTTP", "pool_id":"84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", "project_id":"453105b9-1754-413f-aab1-55f1af620750", "delay":20, "name":"db", "timeout":10, "max_retries":5, "url_path":"/check", "expected_codes":"200-299" } }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // HandleHealthmonitorGetSuccessfully sets up the test server to respond to a healthmonitor Get request. func HandleHealthmonitorGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors/5d4b5228-33b0-4e60-b225-9b727c1a20e7", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, SingleHealthmonitorBody) }) } // HandleHealthmonitorDeletionSuccessfully sets up the test server to respond to a healthmonitor deletion request. func HandleHealthmonitorDeletionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors/5d4b5228-33b0-4e60-b225-9b727c1a20e7", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleHealthmonitorUpdateSuccessfully sets up the test server to respond to a healthmonitor Update request. func HandleHealthmonitorUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors/5d4b5228-33b0-4e60-b225-9b727c1a20e7", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, `{ "healthmonitor": { "name": "NewHealthmonitorName", "delay": 3, "timeout": 20, "max_retries": 10, "url_path": "/another_check", "expected_codes": "301" } }`) fmt.Fprintf(w, PostUpdateHealthmonitorBody) }) } requests_test.go000066400000000000000000000100151335005366400372070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/monitors/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestListHealthmonitors(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleHealthmonitorListSuccessfully(t) pages := 0 err := monitors.List(fake.ServiceClient(), monitors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := monitors.ExtractMonitors(page) if err != nil { return false, err } if len(actual) != 2 { t.Fatalf("Expected 2 healthmonitors, got %d", len(actual)) } th.CheckDeepEquals(t, HealthmonitorWeb, actual[0]) th.CheckDeepEquals(t, HealthmonitorDb, actual[1]) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListAllHealthmonitors(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleHealthmonitorListSuccessfully(t) allPages, err := monitors.List(fake.ServiceClient(), monitors.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := monitors.ExtractMonitors(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, HealthmonitorWeb, actual[0]) th.CheckDeepEquals(t, HealthmonitorDb, actual[1]) } func TestCreateHealthmonitor(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleHealthmonitorCreationSuccessfully(t, SingleHealthmonitorBody) actual, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{ Type: "HTTP", Name: "db", PoolID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", ProjectID: "453105b9-1754-413f-aab1-55f1af620750", Delay: 20, Timeout: 10, MaxRetries: 5, URLPath: "/check", ExpectedCodes: "200-299", }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, HealthmonitorDb, *actual) } func TestRequiredCreateOpts(t *testing.T) { res := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = monitors.Create(fake.ServiceClient(), monitors.CreateOpts{Type: monitors.TypeHTTP}) if res.Err == nil { t.Fatalf("Expected error, got none") } } func TestGetHealthmonitor(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleHealthmonitorGetSuccessfully(t) client := fake.ServiceClient() actual, err := monitors.Get(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, HealthmonitorDb, *actual) } func TestDeleteHealthmonitor(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleHealthmonitorDeletionSuccessfully(t) res := monitors.Delete(fake.ServiceClient(), "5d4b5228-33b0-4e60-b225-9b727c1a20e7") th.AssertNoErr(t, res.Err) } func TestUpdateHealthmonitor(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleHealthmonitorUpdateSuccessfully(t) client := fake.ServiceClient() actual, err := monitors.Update(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7", monitors.UpdateOpts{ Name: "NewHealthmonitorName", Delay: 3, Timeout: 20, MaxRetries: 10, URLPath: "/another_check", ExpectedCodes: "301", }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, HealthmonitorUpdated, *actual) } func TestDelayMustBeGreaterOrEqualThanTimeout(t *testing.T) { _, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{ Type: "HTTP", PoolID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d", Delay: 1, Timeout: 10, MaxRetries: 5, URLPath: "/check", ExpectedCodes: "200-299", }).Extract() if err == nil { t.Fatalf("Expected error, got none") } _, err = monitors.Update(fake.ServiceClient(), "453105b9-1754-413f-aab1-55f1af620750", monitors.UpdateOpts{ Delay: 1, Timeout: 10, }).Extract() if err == nil { t.Fatalf("Expected error, got none") } } urls.go000066400000000000000000000005351335005366400336130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/monitorspackage monitors import "github.com/gophercloud/gophercloud" const ( rootPath = "lbaas" resourcePath = "healthmonitors" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/pools/000077500000000000000000000000001335005366400316355ustar00rootroot00000000000000doc.go000066400000000000000000000063011335005366400326520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/pools/* Package pools provides information and interaction with Pools and Members of the LBaaS v2 extension for the OpenStack Networking service. Example to List Pools listOpts := pools.ListOpts{ LoadbalancerID: "c79a4468-d788-410c-bf79-9a8ef6354852", } allPages, err := pools.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allPools, err := pools.ExtractMonitors(allPages) if err != nil { panic(err) } for _, pools := range allPools { fmt.Printf("%+v\n", pool) } Example to Create a Pool createOpts := pools.CreateOpts{ LBMethod: pools.LBMethodRoundRobin, Protocol: "HTTP", Name: "Example pool", LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", } pool, err := pools.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Pool poolID := "d67d56a6-4a86-4688-a282-f46444705c64" updateOpts := pools.UpdateOpts{ Name: "new-name", } pool, err := pools.Update(networkClient, poolID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Pool poolID := "d67d56a6-4a86-4688-a282-f46444705c64" err := pools.Delete(networkClient, poolID).ExtractErr() if err != nil { panic(err) } Example to List Pool Members poolID := "d67d56a6-4a86-4688-a282-f46444705c64" listOpts := pools.ListMemberOpts{ ProtocolPort: 80, } allPages, err := pools.ListMembers(networkClient, poolID, listOpts).AllPages() if err != nil { panic(err) } allMembers, err := pools.ExtractMembers(allPages) if err != nil { panic(err) } for _, member := allMembers { fmt.Printf("%+v\n", member) } Example to Create a Member poolID := "d67d56a6-4a86-4688-a282-f46444705c64" createOpts := pools.CreateMemberOpts{ Name: "db", SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", Address: "10.0.2.11", ProtocolPort: 80, Weight: 10, } member, err := pools.CreateMember(networkClient, poolID, createOpts).Extract() if err != nil { panic(err) } Example to Update a Member poolID := "d67d56a6-4a86-4688-a282-f46444705c64" memberID := "64dba99f-8af8-4200-8882-e32a0660f23e" updateOpts := pools.UpdateMemberOpts{ Name: "new-name", Weight: 4, } member, err := pools.UpdateMember(networkClient, poolID, memberID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Member poolID := "d67d56a6-4a86-4688-a282-f46444705c64" memberID := "64dba99f-8af8-4200-8882-e32a0660f23e" err := pools.DeleteMember(networkClient, poolID, memberID).ExtractErr() if err != nil { panic(err) } Example to Update Members: poolID := "d67d56a6-4a86-4688-a282-f46444705c64" member1 := pools.BatchUpdateMemberOpts{ Address: "192.0.2.16", ProtocolPort: 80, Name: "web-server-1", SubnetID: "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", Weight: 20, } member2 := pools.BatchUpdateMemberOpts{ Address: "192.0.2.17", ProtocolPort: 80, Name: "web-server-2", Weight: 10, SubnetID: "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", } members := []pools.BatchUpdateMemberOpts{member1, member2} err := pools.BatchUpdateMembers(networkClient, poolID, members).ExtractErr() if err != nil { panic(err) } */ package pools requests.go000066400000000000000000000321341335005366400337630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/poolspackage pools import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToPoolListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the Pool attributes you want to see returned. SortKey allows you to // sort by a particular Pool attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { LBMethod string `q:"lb_algorithm"` Protocol string `q:"protocol"` ProjectID string `q:"project_id"` AdminStateUp *bool `q:"admin_state_up"` Name string `q:"name"` ID string `q:"id"` LoadbalancerID string `q:"loadbalancer_id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToPoolListQuery formats a ListOpts into a query string. func (opts ListOpts) ToPoolListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // pools. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. // // Default policy settings return only those pools that are owned by the // project who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { query, err := opts.ToPoolListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return PoolPage{pagination.LinkedPageBase{PageResult: r}} }) } type LBMethod string type Protocol string // Supported attributes for create/update operations. const ( LBMethodRoundRobin LBMethod = "ROUND_ROBIN" LBMethodLeastConnections LBMethod = "LEAST_CONNECTIONS" LBMethodSourceIp LBMethod = "SOURCE_IP" ProtocolTCP Protocol = "TCP" ProtocolHTTP Protocol = "HTTP" ProtocolHTTPS Protocol = "HTTPS" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToPoolCreateMap() (map[string]interface{}, error) } // CreateOpts is the common options struct used in this package's Create // operation. type CreateOpts struct { // The algorithm used to distribute load between the members of the pool. The // current specification supports LBMethodRoundRobin, LBMethodLeastConnections // and LBMethodSourceIp as valid values for this attribute. LBMethod LBMethod `json:"lb_algorithm" required:"true"` // The protocol used by the pool members, you can use either // ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS. Protocol Protocol `json:"protocol" required:"true"` // The Loadbalancer on which the members of the pool will be associated with. // Note: one of LoadbalancerID or ListenerID must be provided. LoadbalancerID string `json:"loadbalancer_id,omitempty" xor:"ListenerID"` // The Listener on which the members of the pool will be associated with. // Note: one of LoadbalancerID or ListenerID must be provided. ListenerID string `json:"listener_id,omitempty" xor:"LoadbalancerID"` // ProjectID is the UUID of the project who owns the Pool. // Only administrative users can specify a project UUID other than their own. ProjectID string `json:"project_id,omitempty"` // Name of the pool. Name string `json:"name,omitempty"` // Human-readable description for the pool. Description string `json:"description,omitempty"` // Persistence is the session persistence of the pool. // Omit this field to prevent session persistence. Persistence *SessionPersistence `json:"session_persistence,omitempty"` // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToPoolCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToPoolCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "pool") } // Create accepts a CreateOpts struct and uses the values to create a new // load balancer pool. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToPoolCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular pool based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToPoolUpdateMap() (map[string]interface{}, error) } // UpdateOpts is the common options struct used in this package's Update // operation. type UpdateOpts struct { // Name of the pool. Name string `json:"name,omitempty"` // Human-readable description for the pool. Description string `json:"description,omitempty"` // The algorithm used to distribute load between the members of the pool. The // current specification supports LBMethodRoundRobin, LBMethodLeastConnections // and LBMethodSourceIp as valid values for this attribute. LBMethod LBMethod `json:"lb_algorithm,omitempty"` // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToPoolUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToPoolUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "pool") } // Update allows pools to be updated. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToPoolUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete will permanently delete a particular pool based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } // ListMemberOptsBuilder allows extensions to add additional parameters to the // ListMembers request. type ListMembersOptsBuilder interface { ToMembersListQuery() (string, error) } // ListMembersOpts allows the filtering and sorting of paginated collections // through the API. Filtering is achieved by passing in struct field values // that map to the Member attributes you want to see returned. SortKey allows // you to sort by a particular Member attribute. SortDir sets the direction, // and is either `asc' or `desc'. Marker and Limit are used for pagination. type ListMembersOpts struct { Name string `q:"name"` Weight int `q:"weight"` AdminStateUp *bool `q:"admin_state_up"` ProjectID string `q:"project_id"` Address string `q:"address"` ProtocolPort int `q:"protocol_port"` ID string `q:"id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToMemberListQuery formats a ListOpts into a query string. func (opts ListMembersOpts) ToMembersListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // ListMembers returns a Pager which allows you to iterate over a collection of // members. It accepts a ListMembersOptsBuilder, which allows you to filter and // sort the returned collection for greater efficiency. // // Default policy settings return only those members that are owned by the // project who submits the request, unless an admin user submits the request. func ListMembers(c *gophercloud.ServiceClient, poolID string, opts ListMembersOptsBuilder) pagination.Pager { url := memberRootURL(c, poolID) if opts != nil { query, err := opts.ToMembersListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return MemberPage{pagination.LinkedPageBase{PageResult: r}} }) } // CreateMemberOptsBuilder allows extensions to add additional parameters to the // CreateMember request. type CreateMemberOptsBuilder interface { ToMemberCreateMap() (map[string]interface{}, error) } // CreateMemberOpts is the common options struct used in this package's CreateMember // operation. type CreateMemberOpts struct { // The IP address of the member to receive traffic from the load balancer. Address string `json:"address" required:"true"` // The port on which to listen for client traffic. ProtocolPort int `json:"protocol_port" required:"true"` // Name of the Member. Name string `json:"name,omitempty"` // ProjectID is the UUID of the project who owns the Member. // Only administrative users can specify a project UUID other than their own. ProjectID string `json:"project_id,omitempty"` // A positive integer value that indicates the relative portion of traffic // that this member should receive from the pool. For example, a member with // a weight of 10 receives five times as much traffic as a member with a // weight of 2. Weight int `json:"weight,omitempty"` // If you omit this parameter, LBaaS uses the vip_subnet_id parameter value // for the subnet UUID. SubnetID string `json:"subnet_id,omitempty"` // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToMemberCreateMap builds a request body from CreateMemberOpts. func (opts CreateMemberOpts) ToMemberCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "member") } // CreateMember will create and associate a Member with a particular Pool. func CreateMember(c *gophercloud.ServiceClient, poolID string, opts CreateMemberOpts) (r CreateMemberResult) { b, err := opts.ToMemberCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(memberRootURL(c, poolID), b, &r.Body, nil) return } // GetMember retrieves a particular Pool Member based on its unique ID. func GetMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r GetMemberResult) { _, r.Err = c.Get(memberResourceURL(c, poolID, memberID), &r.Body, nil) return } // UpdateMemberOptsBuilder allows extensions to add additional parameters to the // List request. type UpdateMemberOptsBuilder interface { ToMemberUpdateMap() (map[string]interface{}, error) } // UpdateMemberOpts is the common options struct used in this package's Update // operation. type UpdateMemberOpts struct { // Name of the Member. Name string `json:"name,omitempty"` // A positive integer value that indicates the relative portion of traffic // that this member should receive from the pool. For example, a member with // a weight of 10 receives five times as much traffic as a member with a // weight of 2. Weight int `json:"weight,omitempty"` // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToMemberUpdateMap builds a request body from UpdateMemberOpts. func (opts UpdateMemberOpts) ToMemberUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "member") } // Update allows Member to be updated. func UpdateMember(c *gophercloud.ServiceClient, poolID string, memberID string, opts UpdateMemberOptsBuilder) (r UpdateMemberResult) { b, err := opts.ToMemberUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(memberResourceURL(c, poolID, memberID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) return } // BatchUpdateMemberOptsBuilder allows extensions to add additional parameters to the BatchUpdateMembers request. type BatchUpdateMemberOptsBuilder interface { ToBatchMemberUpdateMap() (map[string]interface{}, error) } type BatchUpdateMemberOpts CreateMemberOpts // ToBatchMemberUpdateMap builds a request body from BatchUpdateMemberOpts. func (opts BatchUpdateMemberOpts) ToBatchMemberUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // BatchUpdateMembers updates the pool members in batch func BatchUpdateMembers(c *gophercloud.ServiceClient, poolID string, opts []BatchUpdateMemberOpts) (r UpdateMembersResult) { var members []map[string]interface{} for _, opt := range opts { b, err := opt.ToBatchMemberUpdateMap() if err != nil { r.Err = err return } members = append(members, b) } b := map[string]interface{}{"members": members} _, r.Err = c.Put(memberRootURL(c, poolID), b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) return } // DisassociateMember will remove and disassociate a Member from a particular // Pool. func DeleteMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r DeleteMemberResult) { _, r.Err = c.Delete(memberResourceURL(c, poolID, memberID), nil) return } results.go000066400000000000000000000214301335005366400336060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/poolspackage pools import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" "github.com/gophercloud/gophercloud/pagination" ) // SessionPersistence represents the session persistence feature of the load // balancing service. It attempts to force connections or requests in the same // session to be processed by the same member as long as it is ative. Three // types of persistence are supported: // // SOURCE_IP: With this mode, all connections originating from the same source // IP address, will be handled by the same Member of the Pool. // HTTP_COOKIE: With this persistence mode, the load balancing function will // create a cookie on the first request from a client. Subsequent // requests containing the same cookie value will be handled by // the same Member of the Pool. // APP_COOKIE: With this persistence mode, the load balancing function will // rely on a cookie established by the backend application. All // requests carrying the same cookie value will be handled by the // same Member of the Pool. type SessionPersistence struct { // The type of persistence mode. Type string `json:"type"` // Name of cookie if persistence mode is set appropriately. CookieName string `json:"cookie_name,omitempty"` } // LoadBalancerID represents a load balancer. type LoadBalancerID struct { ID string `json:"id"` } // ListenerID represents a listener. type ListenerID struct { ID string `json:"id"` } // Pool represents a logical set of devices, such as web servers, that you // group together to receive and process traffic. The load balancing function // chooses a Member of the Pool according to the configured load balancing // method to handle the new requests or connections received on the VIP address. type Pool struct { // The load-balancer algorithm, which is round-robin, least-connections, and // so on. This value, which must be supported, is dependent on the provider. // Round-robin must be supported. LBMethod string `json:"lb_algorithm"` // The protocol of the Pool, which is TCP, HTTP, or HTTPS. Protocol string `json:"protocol"` // Description for the Pool. Description string `json:"description"` // A list of listeners objects IDs. Listeners []ListenerID `json:"listeners"` //[]map[string]interface{} // A list of member objects IDs. Members []Member `json:"members"` // The ID of associated health monitor. MonitorID string `json:"healthmonitor_id"` // The network on which the members of the Pool will be located. Only members // that are on this network can be added to the Pool. SubnetID string `json:"subnet_id"` // Owner of the Pool. ProjectID string `json:"project_id"` // The administrative state of the Pool, which is up (true) or down (false). AdminStateUp bool `json:"admin_state_up"` // Pool name. Does not have to be unique. Name string `json:"name"` // The unique ID for the Pool. ID string `json:"id"` // A list of load balancer objects IDs. Loadbalancers []LoadBalancerID `json:"loadbalancers"` // Indicates whether connections in the same session will be processed by the // same Pool member or not. Persistence SessionPersistence `json:"session_persistence"` // The load balancer provider. Provider string `json:"provider"` // The Monitor associated with this Pool. Monitor monitors.Monitor `json:"healthmonitor"` // The provisioning status of the pool. // This value is ACTIVE, PENDING_* or ERROR. ProvisioningStatus string `json:"provisioning_status"` } // PoolPage is the page returned by a pager when traversing over a // collection of pools. type PoolPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of pools has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r PoolPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"pools_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a PoolPage struct is empty. func (r PoolPage) IsEmpty() (bool, error) { is, err := ExtractPools(r) return len(is) == 0, err } // ExtractPools accepts a Page struct, specifically a PoolPage struct, // and extracts the elements into a slice of Pool structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractPools(r pagination.Page) ([]Pool, error) { var s struct { Pools []Pool `json:"pools"` } err := (r.(PoolPage)).ExtractInto(&s) return s.Pools, err } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a pool. func (r commonResult) Extract() (*Pool, error) { var s struct { Pool *Pool `json:"pool"` } err := r.ExtractInto(&s) return s.Pool, err } // CreateResult represents the result of a Create operation. Call its Extract // method to interpret the result as a Pool. type CreateResult struct { commonResult } // GetResult represents the result of a Get operation. Call its Extract // method to interpret the result as a Pool. type GetResult struct { commonResult } // UpdateResult represents the result of an Update operation. Call its Extract // method to interpret the result as a Pool. type UpdateResult struct { commonResult } // DeleteResult represents the result of a Delete operation. Call its // ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // Member represents the application running on a backend server. type Member struct { // Name of the Member. Name string `json:"name"` // Weight of Member. Weight int `json:"weight"` // The administrative state of the member, which is up (true) or down (false). AdminStateUp bool `json:"admin_state_up"` // Owner of the Member. ProjectID string `json:"project_id"` // Parameter value for the subnet UUID. SubnetID string `json:"subnet_id"` // The Pool to which the Member belongs. PoolID string `json:"pool_id"` // The IP address of the Member. Address string `json:"address"` // The port on which the application is hosted. ProtocolPort int `json:"protocol_port"` // The unique ID for the Member. ID string `json:"id"` // The provisioning status of the pool. // This value is ACTIVE, PENDING_* or ERROR. ProvisioningStatus string `json:"provisioning_status"` } // MemberPage is the page returned by a pager when traversing over a // collection of Members in a Pool. type MemberPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of members has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r MemberPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"members_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a MemberPage struct is empty. func (r MemberPage) IsEmpty() (bool, error) { is, err := ExtractMembers(r) return len(is) == 0, err } // ExtractMembers accepts a Page struct, specifically a MemberPage struct, // and extracts the elements into a slice of Members structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractMembers(r pagination.Page) ([]Member, error) { var s struct { Members []Member `json:"members"` } err := (r.(MemberPage)).ExtractInto(&s) return s.Members, err } type commonMemberResult struct { gophercloud.Result } // ExtractMember is a function that accepts a result and extracts a member. func (r commonMemberResult) Extract() (*Member, error) { var s struct { Member *Member `json:"member"` } err := r.ExtractInto(&s) return s.Member, err } // CreateMemberResult represents the result of a CreateMember operation. // Call its Extract method to interpret it as a Member. type CreateMemberResult struct { commonMemberResult } // GetMemberResult represents the result of a GetMember operation. // Call its Extract method to interpret it as a Member. type GetMemberResult struct { commonMemberResult } // UpdateMemberResult represents the result of an UpdateMember operation. // Call its Extract method to interpret it as a Member. type UpdateMemberResult struct { commonMemberResult } // UpdateMembersResult represents the result of an UpdateMembers operation. // Call its ExtractErr method to determine if the request succeeded or failed. type UpdateMembersResult struct { gophercloud.ErrResult } // DeleteMemberResult represents the result of a DeleteMember operation. // Call its ExtractErr method to determine if the request succeeded or failed. type DeleteMemberResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400332335ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/poolsdoc.go000066400000000000000000000000441335005366400343250ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/pools/testing// pools unit tests package testing fixtures.go000066400000000000000000000331541335005366400354410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/pools/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // PoolsListBody contains the canned body of a pool list response. const PoolsListBody = ` { "pools":[ { "lb_algorithm":"ROUND_ROBIN", "protocol":"HTTP", "description":"", "healthmonitor_id": "466c8345-28d8-4f84-a246-e04380b0461d", "members":[{"id": "53306cda-815d-4354-9fe4-59e09da9c3c5"}], "listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}], "loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], "id":"72741b06-df4d-4715-b142-276b6bce75ab", "name":"web", "admin_state_up":true, "project_id":"83657cfcdfe44cd5920adaf26c48ceea", "provider": "haproxy" }, { "lb_algorithm":"LEAST_CONNECTION", "protocol":"HTTP", "description":"", "healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d", "members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}], "listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}], "loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], "id":"c3741b06-df4d-4715-b142-276b6bce75ab", "name":"db", "admin_state_up":true, "project_id":"83657cfcdfe44cd5920adaf26c48ceea", "provider": "haproxy" } ] } ` // SinglePoolBody is the canned body of a Get request on an existing pool. const SinglePoolBody = ` { "pool": { "lb_algorithm":"LEAST_CONNECTION", "protocol":"HTTP", "description":"", "healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d", "members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}], "listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}], "loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], "id":"c3741b06-df4d-4715-b142-276b6bce75ab", "name":"db", "admin_state_up":true, "project_id":"83657cfcdfe44cd5920adaf26c48ceea", "provider": "haproxy" } } ` // PostUpdatePoolBody is the canned response body of a Update request on an existing pool. const PostUpdatePoolBody = ` { "pool": { "lb_algorithm":"LEAST_CONNECTION", "protocol":"HTTP", "description":"", "healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d", "members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}], "listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}], "loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], "id":"c3741b06-df4d-4715-b142-276b6bce75ab", "name":"db", "admin_state_up":true, "project_id":"83657cfcdfe44cd5920adaf26c48ceea", "provider": "haproxy" } } ` var ( PoolWeb = pools.Pool{ LBMethod: "ROUND_ROBIN", Protocol: "HTTP", Description: "", MonitorID: "466c8345-28d8-4f84-a246-e04380b0461d", ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", AdminStateUp: true, Name: "web", Members: []pools.Member{{ID: "53306cda-815d-4354-9fe4-59e09da9c3c5"}}, ID: "72741b06-df4d-4715-b142-276b6bce75ab", Loadbalancers: []pools.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, Listeners: []pools.ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}}, Provider: "haproxy", } PoolDb = pools.Pool{ LBMethod: "LEAST_CONNECTION", Protocol: "HTTP", Description: "", MonitorID: "5f6c8345-28d8-4f84-a246-e04380b0461d", ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", AdminStateUp: true, Name: "db", Members: []pools.Member{{ID: "67306cda-815d-4354-9fe4-59e09da9c3c5"}}, ID: "c3741b06-df4d-4715-b142-276b6bce75ab", Loadbalancers: []pools.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, Listeners: []pools.ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}}, Provider: "haproxy", } PoolUpdated = pools.Pool{ LBMethod: "LEAST_CONNECTION", Protocol: "HTTP", Description: "", MonitorID: "5f6c8345-28d8-4f84-a246-e04380b0461d", ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", AdminStateUp: true, Name: "db", Members: []pools.Member{{ID: "67306cda-815d-4354-9fe4-59e09da9c3c5"}}, ID: "c3741b06-df4d-4715-b142-276b6bce75ab", Loadbalancers: []pools.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, Listeners: []pools.ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}}, Provider: "haproxy", } ) // HandlePoolListSuccessfully sets up the test server to respond to a pool List request. func HandlePoolListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/pools", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, PoolsListBody) case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": fmt.Fprintf(w, `{ "pools": [] }`) default: t.Fatalf("/v2.0/lbaas/pools invoked with unexpected marker=[%s]", marker) } }) } // HandlePoolCreationSuccessfully sets up the test server to respond to a pool creation request // with a given response. func HandlePoolCreationSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/v2.0/lbaas/pools", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "pool": { "lb_algorithm": "ROUND_ROBIN", "protocol": "HTTP", "name": "Example pool", "project_id": "2ffc6e22aae24e4795f87155d24c896f", "loadbalancer_id": "79e05663-7f03-45d2-a092-8b94062f22ab" } }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // HandlePoolGetSuccessfully sets up the test server to respond to a pool Get request. func HandlePoolGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, SinglePoolBody) }) } // HandlePoolDeletionSuccessfully sets up the test server to respond to a pool deletion request. func HandlePoolDeletionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandlePoolUpdateSuccessfully sets up the test server to respond to a pool Update request. func HandlePoolUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, `{ "pool": { "name": "NewPoolName", "lb_algorithm": "LEAST_CONNECTIONS" } }`) fmt.Fprintf(w, PostUpdatePoolBody) }) } // MembersListBody contains the canned body of a member list response. const MembersListBody = ` { "members":[ { "id": "2a280670-c202-4b0b-a562-34077415aabf", "address": "10.0.2.10", "weight": 5, "name": "web", "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", "project_id": "2ffc6e22aae24e4795f87155d24c896f", "admin_state_up":true, "protocol_port": 80 }, { "id": "fad389a3-9a4a-4762-a365-8c7038508b5d", "address": "10.0.2.11", "weight": 10, "name": "db", "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", "project_id": "2ffc6e22aae24e4795f87155d24c896f", "admin_state_up":false, "protocol_port": 80 } ] } ` // SingleMemberBody is the canned body of a Get request on an existing member. const SingleMemberBody = ` { "member": { "id": "fad389a3-9a4a-4762-a365-8c7038508b5d", "address": "10.0.2.11", "weight": 10, "name": "db", "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", "project_id": "2ffc6e22aae24e4795f87155d24c896f", "admin_state_up":false, "protocol_port": 80 } } ` // PostUpdateMemberBody is the canned response body of a Update request on an existing member. const PostUpdateMemberBody = ` { "member": { "id": "fad389a3-9a4a-4762-a365-8c7038508b5d", "address": "10.0.2.11", "weight": 10, "name": "db", "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", "project_id": "2ffc6e22aae24e4795f87155d24c896f", "admin_state_up":false, "protocol_port": 80 } } ` var ( MemberWeb = pools.Member{ SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", ProjectID: "2ffc6e22aae24e4795f87155d24c896f", AdminStateUp: true, Name: "web", ID: "2a280670-c202-4b0b-a562-34077415aabf", Address: "10.0.2.10", Weight: 5, ProtocolPort: 80, } MemberDb = pools.Member{ SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", ProjectID: "2ffc6e22aae24e4795f87155d24c896f", AdminStateUp: false, Name: "db", ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", Address: "10.0.2.11", Weight: 10, ProtocolPort: 80, } MemberUpdated = pools.Member{ SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", ProjectID: "2ffc6e22aae24e4795f87155d24c896f", AdminStateUp: false, Name: "db", ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", Address: "10.0.2.11", Weight: 10, ProtocolPort: 80, } ) // HandleMemberListSuccessfully sets up the test server to respond to a member List request. func HandleMemberListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, MembersListBody) case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": fmt.Fprintf(w, `{ "members": [] }`) default: t.Fatalf("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members invoked with unexpected marker=[%s]", marker) } }) } // HandleMemberCreationSuccessfully sets up the test server to respond to a member creation request // with a given response. func HandleMemberCreationSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "member": { "address": "10.0.2.11", "weight": 10, "name": "db", "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", "project_id": "2ffc6e22aae24e4795f87155d24c896f", "protocol_port": 80 } }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // HandleMemberGetSuccessfully sets up the test server to respond to a member Get request. func HandleMemberGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, SingleMemberBody) }) } // HandleMemberDeletionSuccessfully sets up the test server to respond to a member deletion request. func HandleMemberDeletionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleMemberUpdateSuccessfully sets up the test server to respond to a member Update request. func HandleMemberUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, `{ "member": { "name": "newMemberName", "weight": 4 } }`) fmt.Fprintf(w, PostUpdateMemberBody) }) } // HandleMembersUpdateSuccessfully sets up the test server to respond to a batch member Update request. func HandleMembersUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, `{ "members": [ { "name": "web-server-1", "weight": 20, "subnet_id": "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", "address": "192.0.2.16", "protocol_port": 80 }, { "name": "web-server-2", "weight": 10, "subnet_id": "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", "address": "192.0.2.17", "protocol_port": 80 } ] }`) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000211701335005366400364750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/pools/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestListPools(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePoolListSuccessfully(t) pages := 0 err := pools.List(fake.ServiceClient(), pools.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := pools.ExtractPools(page) if err != nil { return false, err } if len(actual) != 2 { t.Fatalf("Expected 2 pools, got %d", len(actual)) } th.CheckDeepEquals(t, PoolWeb, actual[0]) th.CheckDeepEquals(t, PoolDb, actual[1]) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListAllPools(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePoolListSuccessfully(t) allPages, err := pools.List(fake.ServiceClient(), pools.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := pools.ExtractPools(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, PoolWeb, actual[0]) th.CheckDeepEquals(t, PoolDb, actual[1]) } func TestCreatePool(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePoolCreationSuccessfully(t, SinglePoolBody) actual, err := pools.Create(fake.ServiceClient(), pools.CreateOpts{ LBMethod: pools.LBMethodRoundRobin, Protocol: "HTTP", Name: "Example pool", ProjectID: "2ffc6e22aae24e4795f87155d24c896f", LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, PoolDb, *actual) } func TestGetPool(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePoolGetSuccessfully(t) client := fake.ServiceClient() actual, err := pools.Get(client, "c3741b06-df4d-4715-b142-276b6bce75ab").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, PoolDb, *actual) } func TestDeletePool(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePoolDeletionSuccessfully(t) res := pools.Delete(fake.ServiceClient(), "c3741b06-df4d-4715-b142-276b6bce75ab") th.AssertNoErr(t, res.Err) } func TestUpdatePool(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePoolUpdateSuccessfully(t) client := fake.ServiceClient() actual, err := pools.Update(client, "c3741b06-df4d-4715-b142-276b6bce75ab", pools.UpdateOpts{ Name: "NewPoolName", LBMethod: pools.LBMethodLeastConnections, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, PoolUpdated, *actual) } func TestRequiredPoolCreateOpts(t *testing.T) { res := pools.Create(fake.ServiceClient(), pools.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = pools.Create(fake.ServiceClient(), pools.CreateOpts{ LBMethod: pools.LBMethod("invalid"), Protocol: pools.ProtocolHTTPS, LoadbalancerID: "69055154-f603-4a28-8951-7cc2d9e54a9a", }) if res.Err == nil { t.Fatalf("Expected error, but got none") } res = pools.Create(fake.ServiceClient(), pools.CreateOpts{ LBMethod: pools.LBMethodRoundRobin, Protocol: pools.Protocol("invalid"), LoadbalancerID: "69055154-f603-4a28-8951-7cc2d9e54a9a", }) if res.Err == nil { t.Fatalf("Expected error, but got none") } res = pools.Create(fake.ServiceClient(), pools.CreateOpts{ LBMethod: pools.LBMethodRoundRobin, Protocol: pools.ProtocolHTTPS, }) if res.Err == nil { t.Fatalf("Expected error, but got none") } } func TestListMembers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMemberListSuccessfully(t) pages := 0 err := pools.ListMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.ListMembersOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := pools.ExtractMembers(page) if err != nil { return false, err } if len(actual) != 2 { t.Fatalf("Expected 2 members, got %d", len(actual)) } th.CheckDeepEquals(t, MemberWeb, actual[0]) th.CheckDeepEquals(t, MemberDb, actual[1]) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListAllMembers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMemberListSuccessfully(t) allPages, err := pools.ListMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.ListMembersOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := pools.ExtractMembers(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, MemberWeb, actual[0]) th.CheckDeepEquals(t, MemberDb, actual[1]) } func TestCreateMember(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMemberCreationSuccessfully(t, SingleMemberBody) actual, err := pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{ Name: "db", SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", ProjectID: "2ffc6e22aae24e4795f87155d24c896f", Address: "10.0.2.11", ProtocolPort: 80, Weight: 10, }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, MemberDb, *actual) } func TestRequiredMemberCreateOpts(t *testing.T) { res := pools.CreateMember(fake.ServiceClient(), "", pools.CreateMemberOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = pools.CreateMember(fake.ServiceClient(), "", pools.CreateMemberOpts{Address: "1.2.3.4", ProtocolPort: 80}) if res.Err == nil { t.Fatalf("Expected error, but got none") } res = pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{ProtocolPort: 80}) if res.Err == nil { t.Fatalf("Expected error, but got none") } res = pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{Address: "1.2.3.4"}) if res.Err == nil { t.Fatalf("Expected error, but got none") } } func TestGetMember(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMemberGetSuccessfully(t) client := fake.ServiceClient() actual, err := pools.GetMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, MemberDb, *actual) } func TestDeleteMember(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMemberDeletionSuccessfully(t) res := pools.DeleteMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf") th.AssertNoErr(t, res.Err) } func TestUpdateMember(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMemberUpdateSuccessfully(t) client := fake.ServiceClient() actual, err := pools.UpdateMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf", pools.UpdateMemberOpts{ Name: "newMemberName", Weight: 4, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, MemberUpdated, *actual) } func TestBatchUpdateMembers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMembersUpdateSuccessfully(t) member1 := pools.BatchUpdateMemberOpts{ Address: "192.0.2.16", ProtocolPort: 80, Name: "web-server-1", SubnetID: "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", Weight: 20, } member2 := pools.BatchUpdateMemberOpts{ Address: "192.0.2.17", ProtocolPort: 80, Name: "web-server-2", Weight: 10, SubnetID: "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", } members := []pools.BatchUpdateMemberOpts{member1, member2} res := pools.BatchUpdateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", members) th.AssertNoErr(t, res.Err) } func TestRequiredBatchUpdateMemberOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() res := pools.BatchUpdateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", []pools.BatchUpdateMemberOpts{ { Name: "web-server-1", }, }) if res.Err == nil { t.Fatalf("Expected error, but got none") } res = pools.BatchUpdateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", []pools.BatchUpdateMemberOpts{ { Address: "192.0.2.17", Name: "web-server-1", }, }) if res.Err == nil { t.Fatalf("Expected error, but got none") } res = pools.BatchUpdateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", []pools.BatchUpdateMemberOpts{ { ProtocolPort: 80, Name: "web-server-1", }, }) if res.Err == nil { t.Fatalf("Expected error, but got none") } } urls.go000066400000000000000000000012461335005366400330750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/poolspackage pools import "github.com/gophercloud/gophercloud" const ( rootPath = "lbaas" resourcePath = "pools" memberPath = "members" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } func memberRootURL(c *gophercloud.ServiceClient, poolId string) string { return c.ServiceURL(rootPath, resourcePath, poolId, memberPath) } func memberResourceURL(c *gophercloud.ServiceClient, poolID string, memeberID string) string { return c.ServiceURL(rootPath, resourcePath, poolID, memberPath, memeberID) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/testhelper/000077500000000000000000000000001335005366400326605ustar00rootroot00000000000000client.go000066400000000000000000000004401335005366400344040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/loadbalancer/v2/testhelperpackage common import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/testhelper/client" ) const TokenID = client.TokenID func ServiceClient() *gophercloud.ServiceClient { sc := client.ServiceClient() sc.ResourceBase = sc.Endpoint + "v2.0/" return sc } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/000077500000000000000000000000001335005366400275205ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/000077500000000000000000000000001335005366400300475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/claims/000077500000000000000000000000001335005366400313175ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/claims/doc.go000066400000000000000000000021001335005366400324040ustar00rootroot00000000000000/* Package claims provides information and interaction with the Zaqar API claims resource for the OpenStack Messaging service. Example to Create a Claim on a specified Zaqar queue createOpts := claims.CreateOpts{ TTL: 60, Grace: 120, Limit: 20, } queueName := "my_queue" messages, err := claims.Create(messagingClient, queueName, createOpts).Extract() if err != nil { panic(err) } Example to get a claim for a specified Zaqar queue queueName := "my_queue" claimID := "123456789012345678" claim, err := claims.Get(messagingClient, queueName, claimID).Extract() if err != nil { panic(err) } Example to update a claim for a specified Zaqar queue updateOpts := claims.UpdateOpts{ TTL: 600 Grace: 1200 } queueName := "my_queue" err := claims.Update(messagingClient, queueName, claimID, updateOpts).ExtractErr() if err != nil { panic(err) } Example to delete a claim for a specified Zaqar queue queueName := "my_queue" err := claims.Delete(messagingClient, queueName, claimID).ExtractErr() if err != nil { panic(err) } */ package claims requests.go000066400000000000000000000065511335005366400334510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/claimspackage claims import ( "net/http" "github.com/gophercloud/gophercloud" ) // CreateOptsBuilder Builder. type CreateOptsBuilder interface { ToClaimCreateRequest() (map[string]interface{}, string, error) } // CreateOpts params to be used with Create. type CreateOpts struct { // Sets the TTL for the claim. When the claim expires un-deleted messages will be able to be claimed again. TTL int `json:"ttl,omitempty"` // Sets the Grace period for the claimed messages. The server extends the lifetime of claimed messages // to be at least as long as the lifetime of the claim itself, plus the specified grace period. Grace int `json:"grace,omitempty"` // Set the limit of messages returned by create. Limit int `q:"limit" json:"-"` } // ToClaimCreateRequest assembles a body and URL for a Create request based on // the contents of a CreateOpts. func (opts CreateOpts) ToClaimCreateRequest() (map[string]interface{}, string, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return nil, q.String(), err } b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return b, "", err } return b, q.String(), err } // Create creates a Claim that claims messages on a specified queue. func Create(client *gophercloud.ServiceClient, queueName string, opts CreateOptsBuilder) (r CreateResult) { b, q, err := opts.ToClaimCreateRequest() if err != nil { r.Err = err return } url := createURL(client, queueName) if q != "" { url += q } var resp *http.Response resp, r.Err = client.Post(url, b, nil, &gophercloud.RequestOpts{ OkCodes: []int{201, 204}, }) // If the Claim has no content return an empty CreateResult if resp.StatusCode == 204 { r.Body = CreateResult{} } else { r.Body = resp.Body } return } // Get queries the specified claim for the specified queue. func Get(client *gophercloud.ServiceClient, queueName string, claimID string) (r GetResult) { _, r.Err = client.Get(getURL(client, queueName, claimID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToClaimUpdateMap() (map[string]interface{}, error) } // UpdateOpts implements UpdateOpts. type UpdateOpts struct { // Update the TTL for the specified Claim. TTL int `json:"ttl,omitempty"` // Update the grace period for Messages in a specified Claim. Grace int `json:"grace,omitempty"` } // ToClaimUpdateMap assembles a request body based on the contents of // UpdateOpts. func (opts UpdateOpts) ToClaimUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } return b, nil } // Update will update the options for a specified claim. func Update(client *gophercloud.ServiceClient, queueName string, claimID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToClaimUpdateMap() if err != nil { r.Err = err return r } _, r.Err = client.Patch(updateURL(client, queueName, claimID), &b, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) return } // Delete will delete a Claim for a specified Queue. func Delete(client *gophercloud.ServiceClient, queueName string, claimID string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, queueName, claimID), &gophercloud.RequestOpts{ OkCodes: []int{204}, }) return } results.go000066400000000000000000000022441335005366400332720ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/claimspackage claims import "github.com/gophercloud/gophercloud" func (r CreateResult) Extract() ([]Messages, error) { var s struct { Messages []Messages `json:"messages"` } err := r.ExtractInto(&s) return s.Messages, err } func (r GetResult) Extract() (*Claim, error) { var s *Claim err := r.ExtractInto(&s) return s, err } // CreateResult is the response of a Create operations. type CreateResult struct { gophercloud.Result } // GetResult is the response of a Get operations. type GetResult struct { gophercloud.Result } // UpdateResult is the response of a Update operations. type UpdateResult struct { gophercloud.ErrResult } // DeleteResult is the result from a Delete operation. Call its ExtractErr // method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } type Messages struct { Age float32 `json:"age"` Href string `json:"href"` TTL int `json:"ttl"` Body map[string]interface{} `json:"body"` } type Claim struct { Age float32 `json:"age"` Href string `json:"href"` Messages []Messages `json:"messages"` TTL int `json:"ttl"` } testing/000077500000000000000000000000001335005366400327155ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/claimsdoc.go000066400000000000000000000000451335005366400340100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/claims/testing// Claims unit tests package testing fixtures.go000066400000000000000000000072511335005366400351220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/claims/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/messaging/v2/claims" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) // QueueName is the name of the queue var QueueName = "FakeTestQueue" var ClaimID = "51db7067821e727dc24df754" // CreateClaimResponse is a sample response to a create claim const CreateClaimResponse = ` { "messages": [ { "body": {"event": "BackupStarted"}, "href": "/v2/queues/FakeTestQueue/messages/51db6f78c508f17ddc924357?claim_id=51db7067821e727dc24df754", "age": 57, "ttl": 300 } ] }` // GetClaimResponse is a sample response to a get claim const GetClaimResponse = ` { "age": 50, "href": "/v2/queues/demoqueue/claims/51db7067821e727dc24df754", "messages": [ { "body": {"event": "BackupStarted"}, "href": "/v2/queues/FakeTestQueue/messages/51db6f78c508f17ddc924357?claim_id=51db7067821e727dc24df754", "age": 57, "ttl": 300 } ], "ttl": 50 }` // CreateClaimRequest is a sample request to create a claim. const CreateClaimRequest = ` { "ttl": 3600, "grace": 3600 }` // UpdateClaimRequest is a sample request to update a claim. const UpdateClaimRequest = ` { "ttl": 1200, "grace": 1600 }` // CreatedClaim is the result of a create request. var CreatedClaim = []claims.Messages{ { Age: 57, Href: fmt.Sprintf("/v2/queues/%s/messages/51db6f78c508f17ddc924357?claim_id=%s", QueueName, ClaimID), TTL: 300, Body: map[string]interface{}{"event": "BackupStarted"}, }, } // FirstClaim is the result of a get claim. var FirstClaim = claims.Claim{ Age: 50, Href: "/v2/queues/demoqueue/claims/51db7067821e727dc24df754", Messages: []claims.Messages{ { Age: 57, Href: fmt.Sprintf("/v2/queues/%s/messages/51db6f78c508f17ddc924357?claim_id=%s", QueueName, ClaimID), TTL: 300, Body: map[string]interface{}{"event": "BackupStarted"}, }, }, TTL: 50, } // HandleCreateSuccessfully configures the test server to respond to a Create request. func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/claims", QueueName), func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, CreateClaimRequest) w.WriteHeader(http.StatusCreated) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, CreateClaimResponse) }) } // HandleGetSuccessfully configures the test server to respond to a Get request. func HandleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/claims/%s", QueueName, ClaimID), func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, GetClaimResponse) }) } // HandleUpdateSuccessfully configures the test server to respond to a Update request. func HandleUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/claims/%s", QueueName, ClaimID), func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, UpdateClaimRequest) w.WriteHeader(http.StatusNoContent) }) } // HandleDeleteSuccessfully configures the test server to respond to an Delete request. func HandleDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/claims/%s", QueueName, ClaimID), func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000024271335005366400361630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/claims/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/messaging/v2/claims" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateSuccessfully(t) createOpts := claims.CreateOpts{ TTL: 3600, Grace: 3600, Limit: 10, } actual, err := claims.Create(fake.ServiceClient(), QueueName, createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, CreatedClaim, actual) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t) actual, err := claims.Get(fake.ServiceClient(), QueueName, ClaimID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstClaim, actual) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateSuccessfully(t) updateOpts := claims.UpdateOpts{ Grace: 1600, TTL: 1200, } err := claims.Update(fake.ServiceClient(), QueueName, ClaimID, updateOpts).ExtractErr() th.AssertNoErr(t, err) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSuccessfully(t) err := claims.Delete(fake.ServiceClient(), QueueName, ClaimID).ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/claims/urls.go000066400000000000000000000014111335005366400326300ustar00rootroot00000000000000package claims import "github.com/gophercloud/gophercloud" const ( apiVersion = "v2" apiName = "queues" ) func createURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(apiVersion, apiName, queueName, "claims") } func getURL(client *gophercloud.ServiceClient, queueName string, claimID string) string { return client.ServiceURL(apiVersion, apiName, queueName, "claims", claimID) } func updateURL(client *gophercloud.ServiceClient, queueName string, claimID string) string { return client.ServiceURL(apiVersion, apiName, queueName, "claims", claimID) } func deleteURL(client *gophercloud.ServiceClient, queueName string, claimID string) string { return client.ServiceURL(apiVersion, apiName, queueName, "claims", claimID) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/messages/000077500000000000000000000000001335005366400316565ustar00rootroot00000000000000doc.go000066400000000000000000000045061335005366400327000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/messages/* Package messages provides information and interaction with the messages through the OpenStack Messaging(Zaqar) service. Example to List Messages listOpts := messages.ListOpts{ Limit: 10, } queueName := "my_queue" pager := messages.List(client, queueName, listOpts) err = pager.EachPage(func(page pagination.Page) (bool, error) { allMessages, err := queues.ExtractQueues(page) if err != nil { panic(err) } for _, message := range allMessages { fmt.Printf("%+v\n", message) } return true, nil }) Example to Create Messages queueName = "my_queue" createOpts := messages.CreateOpts{ Messages: []messages.Messages{ { TTL: 300, Delay: 20, Body: map[string]interface{}{ "event": "BackupStarted", "backup_id": "c378813c-3f0b-11e2-ad92-7823d2b0f3ce", }, }, { Body: map[string]interface{}{ "event": "BackupProgress", "current_bytes": "0", "total_bytes": "99614720", }, }, }, } resources, err := messages.Create(client, queueName, createOpts).Extract() if err != nil { panic(err) } Example to Get a set of Messages queueName := "my_queue" getMessageOpts := messages.GetMessagesOpts{ IDs: "123456", } messagesList, err := messages.GetMessages(client, createdQueueName, getMessageOpts).Extract() if err != nil { panic(err) } Example to get a singular Message queueName := "my_queue" messageID := "123456" message, err := messages.Get(client, queueName, messageID).Extract() if err != nil { panic(err) } Example to Delete a set of Messages queueName := "my_queue" deleteMessagesOpts := messages.DeleteMessagesOpts{ IDs: []string{"9988776655"}, } err := messages.DeleteMessages(client, queueName, deleteMessagesOpts).ExtractErr() if err != nil { panic(err) } Example to Pop a set of Messages queueName := "my_queue" popMessagesOpts := messages.PopMessagesOpts{ Pop: 5, } resources, err := messages.PopMessages(client, queueName, popMessagesOpts).Extract() if err != nil { panic(err) } Example to Delete a singular Message clientID := "3381af92-2b9e-11e3-b191-71861300734d" queueName := "my_queue" messageID := "123456" deleteOpts := messages.DeleteOpts{ ClaimID: "12345", } err := messages.Delete(client), queueName, messageID, deleteOpts).ExtractErr() if err != nil { panic(err) } */ package messages requests.go000066400000000000000000000167131335005366400340110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/messagespackage messages import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToMessageListQuery() (string, error) } // ListOpts params to be used with List. type ListOpts struct { // Limit instructs List to refrain from sending excessively large lists of queues Limit int `q:"limit,omitempty"` // Marker and Limit control paging. Marker instructs List where to start listing from. Marker string `q:"marker,omitempty"` // Indicate if the messages can be echoed back to the client that posted them. Echo bool `q:"echo,omitempty"` // Indicate if the messages list should include the claimed messages. IncludeClaimed bool `q:"include_claimed,omitempty"` //Indicate if the messages list should include the delayed messages. IncludeDelayed bool `q:"include_delayed,omitempty"` } func (opts ListOpts) ToMessageListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // ListMessages lists messages on a specific queue based off queue name. func List(client *gophercloud.ServiceClient, queueName string, opts ListOptsBuilder) pagination.Pager { url := listURL(client, queueName) if opts != nil { query, err := opts.ToMessageListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } pager := pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return MessagePage{pagination.LinkedPageBase{PageResult: r}} }) return pager } // CreateOptsBuilder Builder. type CreateOptsBuilder interface { ToMessageCreateMap() (map[string]interface{}, error) } // BatchCreateOpts is an array of CreateOpts. type BatchCreateOpts []CreateOpts // CreateOpts params to be used with Create. type CreateOpts struct { // TTL specifies how long the server waits before marking the message // as expired and removing it from the queue. TTL int `json:"ttl,omitempty"` // Delay specifies how long the message can be claimed. Delay int `json:"delay,omitempty"` // Body specifies an arbitrary document that constitutes the body of the message being sent. Body map[string]interface{} `json:"body" required:"true"` } // ToMessageCreateMap constructs a request body from BatchCreateOpts. func (opts BatchCreateOpts) ToMessageCreateMap() (map[string]interface{}, error) { messages := make([]map[string]interface{}, len(opts)) for i, message := range opts { messageMap, err := message.ToMap() if err != nil { return nil, err } messages[i] = messageMap } return map[string]interface{}{"messages": messages}, nil } // ToMap constructs a request body from UpdateOpts. func (opts CreateOpts) ToMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // Create creates a message on a specific queue based of off queue name. func Create(client *gophercloud.ServiceClient, queueName string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToMessageCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client, queueName), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) return } // DeleteMessagesOptsBuilder allows extensions to add additional parameters to the // DeleteMessages request. type DeleteMessagesOptsBuilder interface { ToMessagesDeleteQuery() (string, error) } // DeleteMessagesOpts params to be used with DeleteMessages. type DeleteMessagesOpts struct { IDs []string `q:"ids,omitempty"` } // ToMessagesDeleteQuery formats a DeleteMessagesOpts structure into a query string. func (opts DeleteMessagesOpts) ToMessagesDeleteQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // DeleteMessages deletes multiple messages based off of ID. func DeleteMessages(client *gophercloud.ServiceClient, queueName string, opts DeleteMessagesOptsBuilder) (r DeleteResult) { url := deleteURL(client, queueName) if opts != nil { query, err := opts.ToMessagesDeleteQuery() if err != nil { r.Err = err return } url += query } _, r.Err = client.Delete(url, &gophercloud.RequestOpts{ OkCodes: []int{200, 204}, }) return } // PopMessagesOptsBuilder allows extensions to add additional parameters to the // DeleteMessages request. type PopMessagesOptsBuilder interface { ToMessagesPopQuery() (string, error) } // PopMessagesOpts params to be used with PopMessages. type PopMessagesOpts struct { Pop int `q:"pop,omitempty"` } // ToMessagesPopQuery formats a PopMessagesOpts structure into a query string. func (opts PopMessagesOpts) ToMessagesPopQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // PopMessages deletes and returns multiple messages based off of number of messages. func PopMessages(client *gophercloud.ServiceClient, queueName string, opts PopMessagesOptsBuilder) (r PopResult) { url := deleteURL(client, queueName) if opts != nil { query, err := opts.ToMessagesPopQuery() if err != nil { r.Err = err return } url += query } result, err := client.Delete(url, &gophercloud.RequestOpts{ OkCodes: []int{200, 204}, }) r.Body = result.Body r.Header = result.Header r.Err = err return } // GetMessagesOptsBuilder allows extensions to add additional parameters to the // GetMessages request. type GetMessagesOptsBuilder interface { ToGetMessagesListQuery() (string, error) } // GetMessagesOpts params to be used with GetMessages. type GetMessagesOpts struct { IDs []string `q:"ids,omitempty"` } // ToGetMessagesListQuery formats a GetMessagesOpts structure into a query string. func (opts GetMessagesOpts) ToGetMessagesListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // GetMessages requests details on a multiple messages, by IDs. func GetMessages(client *gophercloud.ServiceClient, queueName string, opts GetMessagesOptsBuilder) (r GetMessagesResult) { url := getURL(client, queueName) if opts != nil { query, err := opts.ToGetMessagesListQuery() if err != nil { r.Err = err return } url += query } _, r.Err = client.Get(url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Get requests details on a single message, by ID. func Get(client *gophercloud.ServiceClient, queueName string, messageID string) (r GetResult) { _, r.Err = client.Get(messageURL(client, queueName, messageID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // DeleteOptsBuilder allows extensions to add additional parameters to the // delete request. type DeleteOptsBuilder interface { ToMessageDeleteQuery() (string, error) } // DeleteOpts params to be used with Delete. type DeleteOpts struct { // ClaimID instructs Delete to delete a message that is associated with a claim ID ClaimID string `q:"claim_id,omitempty"` } // ToMessageDeleteQuery formats a DeleteOpts structure into a query string. func (opts DeleteOpts) ToMessageDeleteQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // Delete deletes a specific message from the queue. func Delete(client *gophercloud.ServiceClient, queueName string, messageID string, opts DeleteOptsBuilder) (r DeleteResult) { url := DeleteMessageURL(client, queueName, messageID) if opts != nil { query, err := opts.ToMessageDeleteQuery() if err != nil { r.Err = err return } url += query } _, r.Err = client.Delete(url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) return } results.go000066400000000000000000000066111335005366400336330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/messagespackage messages import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // commonResult is the response of a base result. type commonResult struct { gophercloud.Result } // CreateResult is the response of a Create operations. type CreateResult struct { gophercloud.Result } // MessagePage contains a single page of all clusters from a ListDetails call. type MessagePage struct { pagination.LinkedPageBase } // DeleteResult is the result from a Delete operation. Call its ExtractErr // method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // CreateResult is the response of a Create operations. type PopResult struct { gophercloud.Result } // GetMessagesResult is the response of a GetMessages operations. type GetMessagesResult struct { gophercloud.Result } // GetResult is the response of a Get operations. type GetResult struct { gophercloud.Result } // Message represents a message on a queue. type Message struct { Body map[string]interface{} `json:"body"` Age int `json:"age"` Href string `json:"href"` ID string `json:"id"` TTL int `json:"ttl"` Checksum string `json:"checksum"` } // PopMessage represents a message returned from PopMessages. type PopMessage struct { Body map[string]interface{} `json:"body"` Age int `json:"age"` ID string `json:"id"` TTL int `json:"ttl"` ClaimCount int `json:"claim_count"` ClaimID string `json:"claim_id"` } // ResourceList represents the result of creating a message. type ResourceList struct { Resources []string `json:"resources"` } // Extract interprets any CreateResult as a ResourceList. func (r CreateResult) Extract() (ResourceList, error) { var s ResourceList err := r.ExtractInto(&s) return s, err } // Extract interprets any PopResult as a list of PopMessage. func (r PopResult) Extract() ([]PopMessage, error) { var s struct { PopMessages []PopMessage `json:"messages"` } err := r.ExtractInto(&s) return s.PopMessages, err } // Extract interprets any GetMessagesResult as a list of Message. func (r GetMessagesResult) Extract() ([]Message, error) { var s struct { Messages []Message `json:"messages"` } err := r.ExtractInto(&s) return s.Messages, err } // Extract interprets any GetResult as a Message. func (r GetResult) Extract() (Message, error) { var s Message err := r.ExtractInto(&s) return s, err } // ExtractMessage extracts message into a list of Message. func ExtractMessages(r pagination.Page) ([]Message, error) { var s struct { Messages []Message `json:"messages"` } err := (r.(MessagePage)).ExtractInto(&s) return s.Messages, err } // IsEmpty determines if a MessagePage contains any results. func (r MessagePage) IsEmpty() (bool, error) { s, err := ExtractMessages(r) return len(s) == 0, err } // NextPageURL uses the response's embedded link reference to navigate to the // next page of results. func (r MessagePage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"links"` } err := r.ExtractInto(&s) if err != nil { return "", err } next, err := gophercloud.ExtractNextURL(s.Links) if err != nil { return "", err } return nextPageURL(r.URL.String(), next) } testing/000077500000000000000000000000001335005366400332545ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/messagesdoc.go000066400000000000000000000000471335005366400343510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/messages/testing// messages unit tests package testing fixtures.go000066400000000000000000000217011335005366400354550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/messages/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/messaging/v2/messages" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) // QueueName is the name of the queue var QueueName = "FakeTestQueue" // MessageID is the id of the message var MessageID = "9988776655" // CreateMessageResponse is a sample response to a Create message. const CreateMessageResponse = ` { "resources": [ "/v2/queues/demoqueue/messages/51db6f78c508f17ddc924357", "/v2/queues/demoqueue/messages/51db6f78c508f17ddc924358" ] }` // CreateMessageRequest is a sample request to create a message. const CreateMessageRequest = ` { "messages": [ { "body": { "backup_id": "c378813c-3f0b-11e2-ad92-7823d2b0f3ce", "event": "BackupStarted" }, "delay": 20, "ttl": 300 }, { "body": { "current_bytes": "0", "event": "BackupProgress", "total_bytes": "99614720" } } ] }` // ListMessagesResponse is a sample response to list messages. const ListMessagesResponse1 = ` { "messages": [ { "body": { "current_bytes": "0", "event": "BackupProgress", "total_bytes": "99614720" }, "age": 482, "href": "/v2/queues/FakeTestQueue/messages/578edfe6508f153f256f717b", "id": "578edfe6508f153f256f717b", "ttl": 3600, "checksum": "MD5:abf7213555626e29c3cb3e5dc58b3515" } ], "links": [ { "href": "/v2/queues/FakeTestQueue/messages?marker=1", "rel": "next" } ] }` // ListMessagesResponse is a sample response to list messages. const ListMessagesResponse2 = ` { "messages": [ { "body": { "current_bytes": "0", "event": "BackupProgress", "total_bytes": "99614720" }, "age": 456, "href": "/v2/queues/FakeTestQueue/messages/578ee000508f153f256f717d", "id": "578ee000508f153f256f717d", "ttl": 3600, "checksum": "MD5:abf7213555626e29c3cb3e5dc58b3515" } ], "links": [ { "href": "/v2/queues/FakeTestQueue/messages?marker=2", "rel": "next" } ] }` // GetMessagesResponse is a sample response to GetMessages. const GetMessagesResponse = ` { "messages": [ { "body": { "current_bytes": "0", "event": "BackupProgress", "total_bytes": "99614720" }, "age": 443, "href": "/v2/queues/beijing/messages/578f0055508f153f256f717f", "id": "578f0055508f153f256f717f", "ttl": 3600 } ] }` // GetMessageResponse is a sample response to Get. const GetMessageResponse = ` { "body": { "current_bytes": "0", "event": "BackupProgress", "total_bytes": "99614720" }, "age": 482, "href": "/v2/queues/FakeTestQueue/messages/578edfe6508f153f256f717b", "id": "578edfe6508f153f256f717b", "ttl": 3600, "checksum": "MD5:abf7213555626e29c3cb3e5dc58b3515" }` // PopMessageResponse is a sample reponse to pop messages const PopMessageResponse = ` { "messages": [ { "body": { "current_bytes": "0", "event": "BackupProgress", "total_bytes": "99614720" }, "age": 20, "ttl": 120, "claim_count": 55, "claim_id": "123456", "id": "5ae7972599352b436763aee7" } ] }` // ExpectedResources is the expected result in Create var ExpectedResources = messages.ResourceList{ Resources: []string{ "/v2/queues/demoqueue/messages/51db6f78c508f17ddc924357", "/v2/queues/demoqueue/messages/51db6f78c508f17ddc924358", }, } // FirstMessage is the first result in a List. var FirstMessage = messages.Message{ Body: map[string]interface{}{ "current_bytes": "0", "event": "BackupProgress", "total_bytes": "99614720", }, Age: 482, Href: fmt.Sprintf("/v2/queues/%s/messages/578edfe6508f153f256f717b", QueueName), ID: "578edfe6508f153f256f717b", TTL: 3600, Checksum: "MD5:abf7213555626e29c3cb3e5dc58b3515", } // SecondMessage is the second result in a List. var SecondMessage = messages.Message{ Body: map[string]interface{}{ "current_bytes": "0", "event": "BackupProgress", "total_bytes": "99614720", }, Age: 456, Href: fmt.Sprintf("/v2/queues/%s/messages/578ee000508f153f256f717d", QueueName), ID: "578ee000508f153f256f717d", TTL: 3600, Checksum: "MD5:abf7213555626e29c3cb3e5dc58b3515", } // ExpectedMessagesSlice is the expected result in a List. var ExpectedMessagesSlice = [][]messages.Message{{FirstMessage}, {SecondMessage}} // ExpectedMessagesSet is the expected result in GetMessages var ExpectedMessagesSet = []messages.Message{ { Body: map[string]interface{}{ "total_bytes": "99614720", "current_bytes": "0", "event": "BackupProgress", }, Age: 443, Href: "/v2/queues/beijing/messages/578f0055508f153f256f717f", ID: "578f0055508f153f256f717f", TTL: 3600, Checksum: "", }, } // ExpectedPopMessage is the expected result of a Pop. var ExpectedPopMessage = []messages.PopMessage{{ Body: map[string]interface{}{ "current_bytes": "0", "event": "BackupProgress", "total_bytes": "99614720", }, Age: 20, TTL: 120, ClaimID: "123456", ClaimCount: 55, ID: "5ae7972599352b436763aee7", }} // HandleCreateSuccessfully configures the test server to respond to a Create request. func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/messages", QueueName), func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, CreateMessageRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, CreateMessageResponse) }) } // HandleListSuccessfully configures the test server to respond to a List request. func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/messages", QueueName), func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") next := r.RequestURI switch next { case fmt.Sprintf("/v2/queues/%s/messages?limit=1", QueueName): fmt.Fprintf(w, ListMessagesResponse1) case fmt.Sprintf("/v2/queues/%s/messages?marker=1", QueueName): fmt.Fprint(w, ListMessagesResponse2) case fmt.Sprintf("/v2/queues/%s/messages?marker=2", QueueName): fmt.Fprint(w, `{ "messages": [] }`) } }) } // HandleGetMessagesSuccessfully configures the test server to respond to a GetMessages request. func HandleGetMessagesSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/messages", QueueName), func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, GetMessagesResponse) }) } // HandleGetSuccessfully configures the test server to respond to a Get request. func HandleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/messages/%s", QueueName, MessageID), func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, GetMessageResponse) }) } // HandleDeleteMessagesSuccessfully configures the test server to respond to a Delete request. func HandleDeleteMessagesSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/messages", QueueName), func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNoContent) }) } // HandlePopSuccessfully configures the test server to respond to a Pop request. func HandlePopSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/messages", QueueName), func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, PopMessageResponse) }) } // HandleGetSuccessfully configures the test server to respond to a Get request. func HandleDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/messages/%s", QueueName, MessageID), func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000057251335005366400365260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/messages/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/messaging/v2/messages" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t) listOpts := messages.ListOpts{ Limit: 1, } count := 0 err := messages.List(fake.ServiceClient(), QueueName, listOpts).EachPage(func(page pagination.Page) (bool, error) { actual, err := messages.ExtractMessages(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedMessagesSlice[count], actual) count++ return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, 2, count) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateSuccessfully(t) createOpts := messages.BatchCreateOpts{ messages.CreateOpts{ TTL: 300, Delay: 20, Body: map[string]interface{}{ "event": "BackupStarted", "backup_id": "c378813c-3f0b-11e2-ad92-7823d2b0f3ce", }, }, messages.CreateOpts{ Body: map[string]interface{}{ "event": "BackupProgress", "current_bytes": "0", "total_bytes": "99614720", }, }, } actual, err := messages.Create(fake.ServiceClient(), QueueName, createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedResources, actual) } func TestGetMessages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetMessagesSuccessfully(t) getMessagesOpts := messages.GetMessagesOpts{ IDs: []string{"9988776655"}, } actual, err := messages.GetMessages(fake.ServiceClient(), QueueName, getMessagesOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedMessagesSet, actual) } func TestGetMessage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t) actual, err := messages.Get(fake.ServiceClient(), QueueName, MessageID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, FirstMessage, actual) } func TestDeleteMessages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteMessagesSuccessfully(t) deleteMessagesOpts := messages.DeleteMessagesOpts{ IDs: []string{"9988776655"}, } err := messages.DeleteMessages(fake.ServiceClient(), QueueName, deleteMessagesOpts).ExtractErr() th.AssertNoErr(t, err) } func TestPopMessages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePopSuccessfully(t) popMessagesOpts := messages.PopMessagesOpts{ Pop: 1, } actual, err := messages.PopMessages(fake.ServiceClient(), QueueName, popMessagesOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedPopMessage, actual) } func TestDeleteMessage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSuccessfully(t) deleteOpts := messages.DeleteOpts{ ClaimID: "12345", } err := messages.Delete(fake.ServiceClient(), QueueName, MessageID, deleteOpts).ExtractErr() th.AssertNoErr(t, err) } urls.go000066400000000000000000000025721335005366400331210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/messagespackage messages import ( "net/url" "github.com/gophercloud/gophercloud" ) const ( apiVersion = "v2" apiName = "queues" ) func createURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(apiVersion, apiName, queueName, "messages") } func listURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(apiVersion, apiName, queueName, "messages") } func getURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(apiVersion, apiName, queueName, "messages") } func deleteURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(apiVersion, apiName, queueName, "messages") } func DeleteMessageURL(client *gophercloud.ServiceClient, queueName string, messageID string) string { return client.ServiceURL(apiVersion, apiName, queueName, "messages", messageID) } func messageURL(client *gophercloud.ServiceClient, queueName string, messageID string) string { return client.ServiceURL(apiVersion, apiName, queueName, "messages", messageID) } // Builds next page full url based on current url. func nextPageURL(currentURL string, next string) (string, error) { base, err := url.Parse(currentURL) if err != nil { return "", err } rel, err := url.Parse(next) if err != nil { return "", err } return base.ResolveReference(rel).String(), nil } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/queues/000077500000000000000000000000001335005366400313565ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/queues/doc.go000066400000000000000000000044241335005366400324560ustar00rootroot00000000000000/* Package queues provides information and interaction with the queues through the OpenStack Messaging (Zaqar) service. Lists all queues and creates, shows information for updates, deletes, and actions on a queue. Example to List Queues listOpts := queues.ListOpts{ Limit: 10, } pager := queues.List(client, listOpts) err = pager.EachPage(func(page pagination.Page) (bool, error) { queues, err := queues.ExtractQueues(page) if err != nil { panic(err) } for _, queue := range queues { fmt.Printf("%+v\n", queue) } return true, nil }) Example to Create a Queue createOpts := queues.CreateOpts{ QueueName: "My_Queue", MaxMessagesPostSize: 262143, DefaultMessageTTL: 3700, DefaultMessageDelay: 25, DeadLetterQueueMessageTTL: 3500, MaxClaimCount: 10, Extra: map[string]interface{}{"description": "Test queue."}, } err := queues.Create(client, createOpts).ExtractErr() if err != nil { panic(err) } Example to Update a Queue updateOpts := queues.BatchUpdateOpts{ queues.UpdateOpts{ Op: "replace", Path: "/metadata/_max_claim_count", Value: 15, }, queues.UpdateOpts{ Op: "replace", Path: "/metadata/description", Value: "Updated description test queue.", }, } updateResult, err := queues.Update(client, queueName, updateOpts).Extract() if err != nil { panic(err) } Example to Get a Queue queue, err := queues.Get(client, queueName).Extract() if err != nil { panic(err) } Example to Delete a Queue err := queues.Delete(client, queueName).ExtractErr() if err != nil { panic(err) } Example to Get Message Stats of a Queue queueStats, err := queues.GetStats(client, queueName).Extract() if err != nil { panic(err) } Example to Share a queue shareOpts := queues.ShareOpts{ Paths: []queues.SharePath{queues.ShareMessages}, Methods: []queues.ShareMethod{queues.MethodGet}, } queueShare, err := queues.Share(client, queueName, shareOpts).Extract() if err != nil { panic(err) } Example to Purge a queue purgeOpts := queues.PurgeOpts{ ResourceTypes: []queues.PurgeResource{ queues.ResourceMessages, }, } err := queues.Purge(client, queueName, purgeOpts).ExtractErr() if err != nil { panic(err) } */ package queues requests.go000066400000000000000000000205661335005366400335120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/queuespackage queues import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToQueueListQuery() (string, error) } // ListOpts params to be used with List type ListOpts struct { // Limit instructs List to refrain from sending excessively large lists of queues Limit int `q:"limit,omitempty"` // Marker and Limit control paging. Marker instructs List where to start listing from. Marker string `q:"marker,omitempty"` // Specifies if showing the detailed information when querying queues Detailed bool `q:"detailed,omitempty"` } // ToQueueListQuery formats a ListOpts into a query string. func (opts ListOpts) ToQueueListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List instructs OpenStack to provide a list of queues. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToQueueListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } pager := pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return QueuePage{pagination.LinkedPageBase{PageResult: r}} }) return pager } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToQueueCreateMap() (map[string]interface{}, error) } // CreateOpts specifies the queue creation parameters. type CreateOpts struct { // The name of the queue to create. QueueName string `json:"queue_name" required:"true"` // The target incoming messages will be moved to when a message can’t // processed successfully after meet the max claim count is met. DeadLetterQueue string `json:"_dead_letter_queue,omitempty"` // The new TTL setting for messages when moved to dead letter queue. DeadLetterQueueMessagesTTL int `json:"_dead_letter_queue_messages_ttl,omitempty"` // The delay of messages defined for a queue. When the messages send to // the queue, it will be delayed for some times and means it can not be // claimed until the delay expired. DefaultMessageDelay int `json:"_default_message_delay,omitempty"` // The default TTL of messages defined for a queue, which will effect for // any messages posted to the queue. DefaultMessageTTL int `json:"_default_message_ttl" required:"true"` // The flavor name which can tell Zaqar which storage pool will be used // to create the queue. Flavor string `json:"_flavor,omitempty"` // The max number the message can be claimed. MaxClaimCount int `json:"_max_claim_count,omitempty"` // The max post size of messages defined for a queue, which will effect // for any messages posted to the queue. MaxMessagesPostSize int `json:"_max_messages_post_size,omitempty"` // Extra is free-form extra key/value pairs to describe the queue. Extra map[string]interface{} `json:"-"` } // ToQueueCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToQueueCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } if opts.Extra != nil { for key, value := range opts.Extra { b[key] = value } } return b, nil } // Create requests the creation of a new queue. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToQueueCreateMap() if err != nil { r.Err = err return } queueName := b["queue_name"].(string) delete(b, "queue_name") _, r.Err = client.Put(createURL(client, queueName), b, r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201, 204}, }) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // update request. type UpdateOptsBuilder interface { ToQueueUpdateMap() ([]map[string]interface{}, error) } // BatchUpdateOpts is an array of UpdateOpts. type BatchUpdateOpts []UpdateOpts // UpdateOpts is the struct responsible for updating a property of a queue. type UpdateOpts struct { Op UpdateOp `json:"op" required:"true"` Path string `json:"path" required:"true"` Value interface{} `json:"value" required:"true"` } type UpdateOp string const ( ReplaceOp UpdateOp = "replace" AddOp UpdateOp = "add" RemoveOp UpdateOp = "remove" ) // ToQueueUpdateMap constructs a request body from UpdateOpts. func (opts BatchUpdateOpts) ToQueueUpdateMap() ([]map[string]interface{}, error) { queuesUpdates := make([]map[string]interface{}, len(opts)) for i, queue := range opts { queueMap, err := queue.ToMap() if err != nil { return nil, err } queuesUpdates[i] = queueMap } return queuesUpdates, nil } // ToMap constructs a request body from UpdateOpts. func (opts UpdateOpts) ToMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // Update Updates the specified queue. func Update(client *gophercloud.ServiceClient, queueName string, opts UpdateOptsBuilder) (r UpdateResult) { _, r.Err = client.Patch(updateURL(client, queueName), opts, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 204}, MoreHeaders: map[string]string{ "Content-Type": "application/openstack-messaging-v2.0-json-patch"}, }) return } // Get requests details on a single queue, by name. func Get(client *gophercloud.ServiceClient, queueName string) (r GetResult) { _, r.Err = client.Get(getURL(client, queueName), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete deletes the specified queue. func Delete(client *gophercloud.ServiceClient, queueName string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, queueName), &gophercloud.RequestOpts{ OkCodes: []int{204}, }) return } // GetStats returns statistics for the specified queue. func GetStats(client *gophercloud.ServiceClient, queueName string) (r StatResult) { _, r.Err = client.Get(statURL(client, queueName), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}}) return } type SharePath string const ( PathMessages SharePath = "messages" PathClaims SharePath = "claims" PathSubscriptions SharePath = "subscriptions" ) type ShareMethod string const ( MethodGet ShareMethod = "GET" MethodPatch ShareMethod = "PATCH" MethodPost ShareMethod = "POST" MethodPut ShareMethod = "PUT" ) // ShareOpts specifies share creation parameters. type ShareOpts struct { Paths []SharePath `json:"paths,omitempty"` Methods []ShareMethod `json:"methods,omitempty"` Expires string `json:"expires,omitempty"` } // ShareOptsBuilder allows extensions to add additional attributes to the // Share request. type ShareOptsBuilder interface { ToQueueShareMap() (map[string]interface{}, error) } // ToShareQueueMap formats a ShareOpts structure into a request body. func (opts ShareOpts) ToQueueShareMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } return b, nil } // Share creates a pre-signed URL for a given queue. func Share(client *gophercloud.ServiceClient, queueName string, opts ShareOptsBuilder) (r ShareResult) { b, err := opts.ToQueueShareMap() if err != nil { r.Err = err return r } _, r.Err = client.Post(shareURL(client, queueName), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } type PurgeResource string const ( ResourceMessages PurgeResource = "messages" ResourceSubscriptions PurgeResource = "subscriptions" ) // PurgeOpts specifies the purge parameters. type PurgeOpts struct { ResourceTypes []PurgeResource `json:"resource_types" required:"true"` } // PurgeOptsBuilder allows extensions to add additional attributes to the // Purge request. type PurgeOptsBuilder interface { ToQueuePurgeMap() (map[string]interface{}, error) } // ToPurgeQueueMap formats a PurgeOpts structure into a request body func (opts PurgeOpts) ToQueuePurgeMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } return b, nil } // Purge purges particular resource of the queue. func Purge(client *gophercloud.ServiceClient, queueName string, opts PurgeOptsBuilder) (r PurgeResult) { b, err := opts.ToQueuePurgeMap() if err != nil { r.Err = err return r } _, r.Err = client.Post(purgeURL(client, queueName), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) return } results.go000066400000000000000000000117231335005366400333330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/queuespackage queues import ( "encoding/json" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) // commonResult is the response of a base result. type commonResult struct { gophercloud.Result } // QueuePage contains a single page of all queues from a List operation. type QueuePage struct { pagination.LinkedPageBase } // CreateResult is the response of a Create operation. type CreateResult struct { gophercloud.ErrResult } // UpdateResult is the response of a Update operation. type UpdateResult struct { commonResult } // GetResult is the response of a Get operation. type GetResult struct { commonResult } // StatResult contains the result of a Share operation. type StatResult struct { gophercloud.Result } // DeleteResult is the result from a Delete operation. Call its ExtractErr // method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // ShareResult contains the result of a Share operation. type ShareResult struct { gophercloud.Result } // PurgeResult is the response of a Purge operation. type PurgeResult struct { gophercloud.ErrResult } // Queue represents a messaging queue. type Queue struct { Href string `json:"href"` Methods []string `json:"methods"` Name string `json:"name"` Paths []string `json:"paths"` ResourceTypes []string `json:"resource_types"` Metadata QueueDetails `json:"metadata"` } // QueueDetails represents the metadata of a queue. type QueueDetails struct { // The queue the message will be moved to when the message can’t // be processed successfully after the max claim count is met. DeadLetterQueue string `json:"_dead_letter_queue"` // The TTL setting for messages when moved to dead letter queue. DeadLetterQueueMessageTTL int `json:"_dead_letter_queue_messages_ttl"` // The delay of messages defined for the queue. DefaultMessageDelay int `json:"_default_message_delay"` // The default TTL of messages defined for the queue. DefaultMessageTTL int `json:"_default_message_ttl"` // Extra is a collection of miscellaneous key/values. Extra map[string]interface{} `json:"-"` // The max number the message can be claimed from the queue. MaxClaimCount int `json:"_max_claim_count"` // The max post size of messages defined for the queue. MaxMessagesPostSize int `json:"_max_messages_post_size"` // The flavor defined for the queue. Flavor string `json:"flavor"` } // Stats represents a stats response. type Stats struct { // Number of Claimed messages for a queue Claimed int `json:"claimed"` // Total Messages for a queue Total int `json:"total"` // Number of free messages Free int `json:"free"` } // QueueShare represents a share response. type QueueShare struct { Project string `json:"project"` Paths []string `json:"paths"` Expires string `json:"expires"` Methods []string `json:"methods"` Signature string `json:"signature"` } // Extract interprets any commonResult as a Queue. func (r commonResult) Extract() (QueueDetails, error) { var s QueueDetails err := r.ExtractInto(&s) return s, err } // Extract interprets any StatResult as a Stats. func (r StatResult) Extract() (Stats, error) { var s struct { Stats Stats `json:"messages"` } err := r.ExtractInto(&s) return s.Stats, err } // Extract interprets any ShareResult as a QueueShare. func (r ShareResult) Extract() (QueueShare, error) { var s QueueShare err := r.ExtractInto(&s) return s, err } // ExtractQueues interprets the results of a single page from a // List() call, producing a map of queues. func ExtractQueues(r pagination.Page) ([]Queue, error) { var s struct { Queues []Queue `json:"queues"` } err := (r.(QueuePage)).ExtractInto(&s) return s.Queues, err } // IsEmpty determines if a QueuesPage contains any results. func (r QueuePage) IsEmpty() (bool, error) { s, err := ExtractQueues(r) return len(s) == 0, err } // NextPageURL uses the response's embedded link reference to navigate to the // next page of results. func (r QueuePage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"links"` } err := r.ExtractInto(&s) if err != nil { return "", err } next, err := gophercloud.ExtractNextURL(s.Links) if err != nil { return "", err } return nextPageURL(r.URL.String(), next) } func (r *QueueDetails) UnmarshalJSON(b []byte) error { type tmp QueueDetails var s struct { tmp Extra map[string]interface{} `json:"extra"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = QueueDetails(s.tmp) // Collect other fields and bundle them into Extra // but only if a field titled "extra" wasn't sent. if s.Extra != nil { r.Extra = s.Extra } else { var result interface{} err := json.Unmarshal(b, &result) if err != nil { return err } if resultMap, ok := result.(map[string]interface{}); ok { r.Extra = internal.RemainingKeys(QueueDetails{}, resultMap) } } return err } testing/000077500000000000000000000000001335005366400327545ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/queuesdoc.go000066400000000000000000000000451335005366400340470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/queues/testing// queues unit tests package testing fixtures.go000066400000000000000000000216751335005366400351670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/queues/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/messaging/v2/queues" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) // QueueName is the name of the queue var QueueName = "FakeTestQueue" // CreateQueueRequest is a sample request to create a queue. const CreateQueueRequest = ` { "_max_messages_post_size": 262144, "_default_message_ttl": 3600, "_default_message_delay": 30, "_dead_letter_queue": "dead_letter", "_dead_letter_queue_messages_ttl": 3600, "_max_claim_count": 10, "description": "Queue for unit testing." }` // CreateShareRequest is a sample request to a share. const CreateShareRequest = ` { "paths": ["messages", "claims", "subscriptions"], "methods": ["GET", "POST", "PUT", "PATCH"], "expires": "2016-09-01T00:00:00" }` // CreatePurgeRequest is a sample request to a purge. const CreatePurgeRequest = ` { "resource_types": ["messages", "subscriptions"] }` // ListQueuesResponse1 is a sample response to a List queues. const ListQueuesResponse1 = ` { "queues":[ { "href":"/v2/queues/london", "name":"london", "metadata":{ "_dead_letter_queue":"fake_queue", "_dead_letter_queue_messages_ttl":3500, "_default_message_delay":25, "_default_message_ttl":3700, "_max_claim_count":10, "_max_messages_post_size":262143, "description":"Test queue." } } ], "links":[ { "href":"/v2/queues?marker=london", "rel":"next" } ] }` // ListQueuesResponse2 is a sample response to a List queues. const ListQueuesResponse2 = ` { "queues":[ { "href":"/v2/queues/beijing", "name":"beijing", "metadata":{ "_dead_letter_queue":"fake_queue", "_dead_letter_queue_messages_ttl":3500, "_default_message_delay":25, "_default_message_ttl":3700, "_max_claim_count":10, "_max_messages_post_size":262143, "description":"Test queue." } } ], "links":[ { "href":"/v2/queues?marker=beijing", "rel":"next" } ] }` // UpdateQueueRequest is a sample request to update a queue. const UpdateQueueRequest = ` [ { "op": "replace", "path": "/metadata/description", "value": "Update queue description" } ]` // UpdateQueueResponse is a sample response to a update queue. const UpdateQueueResponse = ` { "description": "Update queue description" }` // GetQueueResponse is a sample response to a get queue. const GetQueueResponse = ` { "_max_messages_post_size": 262144, "_default_message_ttl": 3600, "description": "Queue used for unit testing." }` // GetStatsResponse is a sample response to a stats request. const GetStatsResponse = ` { "messages":{ "claimed": 10, "total": 20, "free": 10 } }` // CreateShareResponse is a sample response to a share request. const CreateShareResponse = ` { "project": "2887aabf368046a3bb0070f1c0413470", "paths": [ "/v2/queues/test/messages", "/v2/queues/test/claims", "/v2/queues/test/subscriptions" ], "expires": "2016-09-01T00:00:00", "methods": [ "GET", "PATCH", "POST", "PUT" ], "signature": "6a63d63242ebd18c3518871dda6fdcb6273db2672c599bf985469241e9a1c799" }` // FirstQueue is the first result in a List. var FirstQueue = queues.Queue{ Href: "/v2/queues/london", Name: "london", Metadata: queues.QueueDetails{ DeadLetterQueue: "fake_queue", DeadLetterQueueMessageTTL: 3500, DefaultMessageDelay: 25, DefaultMessageTTL: 3700, MaxClaimCount: 10, MaxMessagesPostSize: 262143, Extra: map[string]interface{}{"description": "Test queue."}, }, } // SecondQueue is the second result in a List. var SecondQueue = queues.Queue{ Href: "/v2/queues/beijing", Name: "beijing", Metadata: queues.QueueDetails{ DeadLetterQueue: "fake_queue", DeadLetterQueueMessageTTL: 3500, DefaultMessageDelay: 25, DefaultMessageTTL: 3700, MaxClaimCount: 10, MaxMessagesPostSize: 262143, Extra: map[string]interface{}{"description": "Test queue."}, }, } // ExpectedQueueSlice is the expected result in a List. var ExpectedQueueSlice = [][]queues.Queue{{FirstQueue}, {SecondQueue}} // QueueDetails is the expected result in a Get. var QueueDetails = queues.QueueDetails{ DefaultMessageTTL: 3600, MaxMessagesPostSize: 262144, Extra: map[string]interface{}{"description": "Queue used for unit testing."}, } // ExpectedStats is the expected result in a GetStats. var ExpectedStats = queues.Stats{ Claimed: 10, Total: 20, Free: 10, } // ExpectedShare is the expected result in Share. var ExpectedShare = queues.QueueShare{ Project: "2887aabf368046a3bb0070f1c0413470", Paths: []string{ "/v2/queues/test/messages", "/v2/queues/test/claims", "/v2/queues/test/subscriptions", }, Expires: "2016-09-01T00:00:00", Methods: []string{ "GET", "PATCH", "POST", "PUT", }, Signature: "6a63d63242ebd18c3518871dda6fdcb6273db2672c599bf985469241e9a1c799", } // HandleListSuccessfully configures the test server to respond to a List request. func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2/queues", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") next := r.RequestURI switch next { case "/v2/queues?limit=1": fmt.Fprintf(w, ListQueuesResponse1) case "/v2/queues?marker=london": fmt.Fprint(w, ListQueuesResponse2) case "/v2/queues?marker=beijing": fmt.Fprint(w, `{ "queues": [] }`) } }) } // HandleCreateSuccessfully configures the test server to respond to a Create request. func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s", QueueName), func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, CreateQueueRequest) w.WriteHeader(http.StatusNoContent) }) } // HandleUpdateSuccessfully configures the test server to respond to an Update request. func HandleUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s", QueueName), func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, UpdateQueueRequest) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, UpdateQueueResponse) }) } // HandleGetSuccessfully configures the test server to respond to a Get request. func HandleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s", QueueName), func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, GetQueueResponse) }) } // HandleDeleteSuccessfully configures the test server to respond to a Delete request. func HandleDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s", QueueName), func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleGetSuccessfully configures the test server to respond to a Get request. func HandleGetStatsSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/stats", QueueName), func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, GetStatsResponse) }) } // HandleShareSuccessfully configures the test server to respond to a Share request. func HandleShareSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/share", QueueName), func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, CreateShareRequest) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, CreateShareResponse) }) } // HandlePurgeSuccessfully configures the test server to respond to a Purge request. func HandlePurgeSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/purge", QueueName), func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, CreatePurgeRequest) w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000066141335005366400362240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/queues/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/messaging/v2/queues" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t) listOpts := queues.ListOpts{ Limit: 1, } count := 0 err := queues.List(fake.ServiceClient(), listOpts).EachPage(func(page pagination.Page) (bool, error) { actual, err := queues.ExtractQueues(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedQueueSlice[count], actual) count++ return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, 2, count) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateSuccessfully(t) createOpts := queues.CreateOpts{ QueueName: QueueName, MaxMessagesPostSize: 262144, DefaultMessageTTL: 3600, DefaultMessageDelay: 30, DeadLetterQueue: "dead_letter", DeadLetterQueueMessagesTTL: 3600, MaxClaimCount: 10, Extra: map[string]interface{}{"description": "Queue for unit testing."}, } err := queues.Create(fake.ServiceClient(), createOpts).ExtractErr() th.AssertNoErr(t, err) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateSuccessfully(t) updateOpts := queues.BatchUpdateOpts{ queues.UpdateOpts{ Op: queues.ReplaceOp, Path: "/metadata/description", Value: "Update queue description", }, } updatedQueueResult := queues.QueueDetails{ Extra: map[string]interface{}{"description": "Update queue description"}, } actual, err := queues.Update(fake.ServiceClient(), QueueName, updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, updatedQueueResult, actual) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t) actual, err := queues.Get(fake.ServiceClient(), QueueName).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, QueueDetails, actual) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSuccessfully(t) err := queues.Delete(fake.ServiceClient(), QueueName).ExtractErr() th.AssertNoErr(t, err) } func TestGetStat(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetStatsSuccessfully(t) actual, err := queues.GetStats(fake.ServiceClient(), QueueName).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedStats, actual) } func TestShare(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleShareSuccessfully(t) shareOpts := queues.ShareOpts{ Paths: []queues.SharePath{queues.PathMessages, queues.PathClaims, queues.PathSubscriptions}, Methods: []queues.ShareMethod{queues.MethodGet, queues.MethodPost, queues.MethodPut, queues.MethodPatch}, Expires: "2016-09-01T00:00:00", } actual, err := queues.Share(fake.ServiceClient(), QueueName, shareOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedShare, actual) } func TestPurge(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePurgeSuccessfully(t) purgeOpts := queues.PurgeOpts{ ResourceTypes: []queues.PurgeResource{queues.ResourceMessages, queues.ResourceSubscriptions}, } err := queues.Purge(fake.ServiceClient(), QueueName, purgeOpts).ExtractErr() th.AssertNoErr(t, err) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/messaging/v2/queues/urls.go000066400000000000000000000031231335005366400326710ustar00rootroot00000000000000package queues import ( "net/url" "github.com/gophercloud/gophercloud" ) const ( apiVersion = "v2" apiName = "queues" ) func commonURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName) } func createURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(apiVersion, apiName, queueName) } func listURL(client *gophercloud.ServiceClient) string { return commonURL(client) } func updateURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(apiVersion, apiName, queueName) } func getURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(apiVersion, apiName, queueName) } func deleteURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(apiVersion, apiName, queueName) } func statURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(apiVersion, apiName, queueName, "stats") } func shareURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(apiVersion, apiName, queueName, "share") } func purgeURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(apiVersion, apiName, queueName, "purge") } // builds next page full url based on current url func nextPageURL(currentURL string, next string) (string, error) { base, err := url.Parse(currentURL) if err != nil { return "", err } rel, err := url.Parse(next) if err != nil { return "", err } return base.ResolveReference(rel).String(), nil } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/000077500000000000000000000000001335005366400277325ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/000077500000000000000000000000001335005366400302615ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/apiversions/000077500000000000000000000000001335005366400326235ustar00rootroot00000000000000doc.go000066400000000000000000000010111335005366400336310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/apiversions/* Package apiversions provides information and interaction with the different API versions for the OpenStack Neutron service. This functionality is not restricted to this particular version. Example to List API Versions allPages, err := apiversions.ListVersions(networkingClient).AllPages() if err != nil { panic(err) } allVersions, err := apiversions.ExtractAPIVersions(allPages) if err != nil { panic(err) } for _, version := range allVersions { fmt.Printf("%+v\n", version) } */ package apiversions requests.go000066400000000000000000000015051335005366400347470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/apiversionspackage apiversions import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListVersions lists all the Neutron API versions available to end-users. func ListVersions(c *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(c, apiVersionsURL(c), func(r pagination.PageResult) pagination.Page { return APIVersionPage{pagination.SinglePageBase(r)} }) } // ListVersionResources lists all of the different API resources for a // particular API versions. Typical resources for Neutron might be: networks, // subnets, etc. func ListVersionResources(c *gophercloud.ServiceClient, v string) pagination.Pager { return pagination.NewPager(c, apiInfoURL(c, v), func(r pagination.PageResult) pagination.Page { return APIVersionResourcePage{pagination.SinglePageBase(r)} }) } results.go000066400000000000000000000041351335005366400345770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/apiversionspackage apiversions import ( "github.com/gophercloud/gophercloud/pagination" ) // APIVersion represents an API version for Neutron. It contains the status of // the API, and its unique ID. type APIVersion struct { Status string `son:"status"` ID string `json:"id"` } // APIVersionPage is the page returned by a pager when traversing over a // collection of API versions. type APIVersionPage struct { pagination.SinglePageBase } // IsEmpty checks whether an APIVersionPage struct is empty. func (r APIVersionPage) IsEmpty() (bool, error) { is, err := ExtractAPIVersions(r) return len(is) == 0, err } // ExtractAPIVersions takes a collection page, extracts all of the elements, // and returns them a slice of APIVersion structs. It is effectively a cast. func ExtractAPIVersions(r pagination.Page) ([]APIVersion, error) { var s struct { Versions []APIVersion `json:"versions"` } err := (r.(APIVersionPage)).ExtractInto(&s) return s.Versions, err } // APIVersionResource represents a generic API resource. It contains the name // of the resource and its plural collection name. type APIVersionResource struct { Name string `json:"name"` Collection string `json:"collection"` } // APIVersionResourcePage is a concrete type which embeds the common // SinglePageBase struct, and is used when traversing API versions collections. type APIVersionResourcePage struct { pagination.SinglePageBase } // IsEmpty is a concrete function which indicates whether an // APIVersionResourcePage is empty or not. func (r APIVersionResourcePage) IsEmpty() (bool, error) { is, err := ExtractVersionResources(r) return len(is) == 0, err } // ExtractVersionResources accepts a Page struct, specifically a // APIVersionResourcePage struct, and extracts the elements into a slice of // APIVersionResource structs. In other words, the collection is mapped into // a relevant slice. func ExtractVersionResources(r pagination.Page) ([]APIVersionResource, error) { var s struct { APIVersionResources []APIVersionResource `json:"resources"` } err := (r.(APIVersionResourcePage)).ExtractInto(&s) return s.APIVersionResources, err } testing/000077500000000000000000000000001335005366400342215ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/apiversionsdoc.go000066400000000000000000000000521335005366400353120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/apiversions/testing// apiversions unit tests package testing requests_test.go000066400000000000000000000101561335005366400374650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/apiversions/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/networking/v2/apiversions" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListVersions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "versions": [ { "status": "CURRENT", "id": "v2.0", "links": [ { "href": "http://23.253.228.211:9696/v2.0", "rel": "self" } ] } ] }`) }) count := 0 apiversions.ListVersions(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := apiversions.ExtractAPIVersions(page) if err != nil { t.Errorf("Failed to extract API versions: %v", err) return false, err } expected := []apiversions.APIVersion{ { Status: "CURRENT", ID: "v2.0", }, } th.AssertDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestNonJSONCannotBeExtractedIntoAPIVersions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }) apiversions.ListVersions(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { if _, err := apiversions.ExtractAPIVersions(page); err == nil { t.Fatalf("Expected error, got nil") } return true, nil }) } func TestAPIInfo(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "resources": [ { "links": [ { "href": "http://23.253.228.211:9696/v2.0/subnets", "rel": "self" } ], "name": "subnet", "collection": "subnets" }, { "links": [ { "href": "http://23.253.228.211:9696/v2.0/networks", "rel": "self" } ], "name": "network", "collection": "networks" }, { "links": [ { "href": "http://23.253.228.211:9696/v2.0/ports", "rel": "self" } ], "name": "port", "collection": "ports" } ] } `) }) count := 0 apiversions.ListVersionResources(fake.ServiceClient(), "v2.0").EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := apiversions.ExtractVersionResources(page) if err != nil { t.Errorf("Failed to extract version resources: %v", err) return false, err } expected := []apiversions.APIVersionResource{ { Name: "subnet", Collection: "subnets", }, { Name: "network", Collection: "networks", }, { Name: "port", Collection: "ports", }, } th.AssertDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestNonJSONCannotBeExtractedIntoAPIVersionResources(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }) apiversions.ListVersionResources(fake.ServiceClient(), "v2.0").EachPage(func(page pagination.Page) (bool, error) { if _, err := apiversions.ExtractVersionResources(page); err == nil { t.Fatalf("Expected error, got nil") } return true, nil }) } urls.go000066400000000000000000000004501335005366400340570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/apiversionspackage apiversions import ( "strings" "github.com/gophercloud/gophercloud" ) func apiVersionsURL(c *gophercloud.ServiceClient) string { return c.Endpoint } func apiInfoURL(c *gophercloud.ServiceClient, version string) string { return c.Endpoint + strings.TrimRight(version, "/") + "/" } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/common/000077500000000000000000000000001335005366400315515ustar00rootroot00000000000000common_tests.go000066400000000000000000000004401335005366400345310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/commonpackage common import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/testhelper/client" ) const TokenID = client.TokenID func ServiceClient() *gophercloud.ServiceClient { sc := client.ServiceClient() sc.ResourceBase = sc.Endpoint + "v2.0/" return sc } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/000077500000000000000000000000001335005366400324605ustar00rootroot00000000000000delegate.go000066400000000000000000000021321335005366400345000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensionspackage extensions import ( "github.com/gophercloud/gophercloud" common "github.com/gophercloud/gophercloud/openstack/common/extensions" "github.com/gophercloud/gophercloud/pagination" ) // Extension is a single OpenStack extension. type Extension struct { common.Extension } // GetResult wraps a GetResult from common. type GetResult struct { common.GetResult } // ExtractExtensions interprets a Page as a slice of Extensions. func ExtractExtensions(page pagination.Page) ([]Extension, error) { inner, err := common.ExtractExtensions(page) if err != nil { return nil, err } outer := make([]Extension, len(inner)) for index, ext := range inner { outer[index] = Extension{ext} } return outer, nil } // Get retrieves information for a specific extension using its alias. func Get(c *gophercloud.ServiceClient, alias string) GetResult { return GetResult{common.Get(c, alias)} } // List returns a Pager which allows you to iterate over the full collection of extensions. // It does not accept query parameters. func List(c *gophercloud.ServiceClient) pagination.Pager { return common.List(c) } external/000077500000000000000000000000001335005366400342235ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensionsdoc.go000066400000000000000000000021101335005366400353110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/external/* Package external provides information and interaction with the external extension for the OpenStack Networking service. Example to List Networks with External Information iTrue := true networkListOpts := networks.ListOpts{} listOpts := external.ListOptsExt{ ListOptsBuilder: networkListOpts, External: &iTrue, } type NetworkWithExternalExt struct { networks.Network external.NetworkExternalExt } var allNetworks []NetworkWithExternalExt allPages, err := networks.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } err = networks.ExtractNetworksInto(allPages, &allNetworks) if err != nil { panic(err) } for _, network := range allNetworks { fmt.Println("%+v\n", network) } Example to Create a Network with External Information iTrue := true networkCreateOpts := networks.CreateOpts{ Name: "private", AdminStateUp: &iTrue, } createOpts := external.CreateOptsExt{ networkCreateOpts, &iTrue, } network, err := networks.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } */ package external requests.go000066400000000000000000000043741335005366400364350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/externalpackage external import ( "net/url" "strconv" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" ) // ListOptsExt adds the external network options to the base ListOpts. type ListOptsExt struct { networks.ListOptsBuilder External *bool `q:"router:external"` } // ToNetworkListQuery adds the router:external option to the base network // list options. func (opts ListOptsExt) ToNetworkListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts.ListOptsBuilder) if err != nil { return "", err } params := q.Query() if opts.External != nil { v := strconv.FormatBool(*opts.External) params.Add("router:external", v) } q = &url.URL{RawQuery: params.Encode()} return q.String(), err } // CreateOptsExt is the structure used when creating new external network // resources. It embeds networks.CreateOpts and so inherits all of its required // and optional fields, with the addition of the External field. type CreateOptsExt struct { networks.CreateOptsBuilder External *bool `json:"router:external,omitempty"` } // ToNetworkCreateMap adds the router:external options to the base network // creation options. func (opts CreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { base, err := opts.CreateOptsBuilder.ToNetworkCreateMap() if err != nil { return nil, err } if opts.External == nil { return base, nil } networkMap := base["network"].(map[string]interface{}) networkMap["router:external"] = opts.External return base, nil } // UpdateOptsExt is the structure used when updating existing external network // resources. It embeds networks.UpdateOpts and so inherits all of its required // and optional fields, with the addition of the External field. type UpdateOptsExt struct { networks.UpdateOptsBuilder External *bool `json:"router:external,omitempty"` } // ToNetworkUpdateMap casts an UpdateOpts struct to a map. func (opts UpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { base, err := opts.UpdateOptsBuilder.ToNetworkUpdateMap() if err != nil { return nil, err } if opts.External == nil { return base, nil } networkMap := base["network"].(map[string]interface{}) networkMap["router:external"] = opts.External return base, nil } results.go000066400000000000000000000004141335005366400362520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/externalpackage external // NetworkExternalExt represents a decorated form of a Network with based on the // "external-net" extension. type NetworkExternalExt struct { // Specifies whether the network is an external network or not. External bool `json:"router:external"` } testing/000077500000000000000000000000001335005366400357005ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/externaldoc.go000066400000000000000000000000471335005366400367750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/external/testing// external unit tests package testing fixtures.go000066400000000000000000000031411335005366400400770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/external/testingpackage testing // These fixtures are here instead of in the underlying networks package // because all network tests (including extensions) would have to // implement the NetworkExternalExt extention for create/update tests // to pass. const CreateRequest = ` { "network": { "name": "private", "admin_state_up": true, "router:external": false } }` const CreateResponse = ` { "network": { "status": "ACTIVE", "subnets": ["08eae331-0402-425a-923c-34f7cfe39c1b"], "name": "private", "admin_state_up": true, "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", "shared": false, "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", "provider:segmentation_id": 9876543210, "provider:physical_network": null, "provider:network_type": "local", "router:external": false } }` const UpdateRequest = ` { "network": { "name": "new_network_name", "admin_state_up": false, "shared": true, "router:external": false } }` const UpdateResponse = ` { "network": { "status": "ACTIVE", "subnets": [], "name": "new_network_name", "admin_state_up": false, "tenant_id": "4fd44f30292945e481c7b8a0c8908869", "shared": true, "id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c", "provider:segmentation_id": 1234567890, "provider:physical_network": null, "provider:network_type": "local", "router:external": false } }` const ExpectedListOpts = "?id=d32019d3-bc6e-4319-9c1d-6722fc136a22&router%3Aexternal=true" requests_test.go000066400000000000000000000011511335005366400411370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/external/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" th "github.com/gophercloud/gophercloud/testhelper" ) func TestListExternal(t *testing.T) { var iTrue bool = true networkListOpts := networks.ListOpts{ ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", } listOpts := external.ListOptsExt{ ListOptsBuilder: networkListOpts, External: &iTrue, } actual, err := listOpts.ToNetworkListQuery() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedListOpts, actual) } results_test.go000066400000000000000000000073601335005366400407750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/external/testingpackage testing import ( "fmt" "net/http" "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" nettest "github.com/gophercloud/gophercloud/openstack/networking/v2/networks/testing" th "github.com/gophercloud/gophercloud/testhelper" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, nettest.ListResponse) }) type NetworkWithExternalExt struct { networks.Network external.NetworkExternalExt } var actual []NetworkWithExternalExt allPages, err := networks.List(fake.ServiceClient(), networks.ListOpts{}).AllPages() th.AssertNoErr(t, err) err = networks.ExtractNetworksInto(allPages, &actual) th.AssertNoErr(t, err) th.AssertEquals(t, "d32019d3-bc6e-4319-9c1d-6722fc136a22", actual[0].ID) th.AssertEquals(t, true, actual[0].External) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, nettest.GetResponse) }) var s struct { networks.Network external.NetworkExternalExt } err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "d32019d3-bc6e-4319-9c1d-6722fc136a22", s.ID) th.AssertEquals(t, true, s.External) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, CreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, CreateResponse) }) iTrue := true iFalse := false networkCreateOpts := networks.CreateOpts{ Name: "private", AdminStateUp: &iTrue, } externalCreateOpts := external.CreateOptsExt{ CreateOptsBuilder: &networkCreateOpts, External: &iFalse, } _, err := networks.Create(fake.ServiceClient(), externalCreateOpts).Extract() th.AssertNoErr(t, err) th.AssertNoErr(t, err) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, UpdateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, UpdateResponse) }) iTrue := true iFalse := false networkUpdateOpts := networks.UpdateOpts{ Name: "new_network_name", AdminStateUp: &iFalse, Shared: &iTrue, } externalUpdateOpts := external.UpdateOptsExt{ UpdateOptsBuilder: &networkUpdateOpts, External: &iFalse, } _, err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", externalUpdateOpts).Extract() th.AssertNoErr(t, err) } extradhcpopts/000077500000000000000000000000001335005366400352715ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensionsdoc.go000066400000000000000000000033241335005366400363670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/extradhcpopts/* Package extradhcpopts allow to work with extra DHCP functionality of Neutron ports. Example to Get a Port with Extra DHCP Options portID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" var s struct { ports.Port extradhcpopts.ExtraDHCPOptsExt } err := ports.Get(networkClient, portID).ExtractInto(&s) if err != nil { panic(err) } Example to Create a Port with Extra DHCP Options var s struct { ports.Port extradhcpopts.ExtraDHCPOptsExt } adminStateUp := true portCreateOpts := ports.CreateOpts{ Name: "dhcp-conf-port", AdminStateUp: &adminStateUp, NetworkID: "a87cc70a-3e15-4acf-8205-9b711a3531b7", FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, }, } createOpts := extradhcpopts.CreateOptsExt{ CreateOptsBuilder: portCreateOpts, ExtraDHCPOpts: []extradhcpopts.CreateExtraDHCPOpt{ { OptName: "optionA", OptValue: "valueA", }, }, } err := ports.Create(networkClient, createOpts).ExtractInto(&s) if err != nil { panic(err) } Example to Update a Port with Extra DHCP Options var s struct { ports.Port extradhcpopts.ExtraDHCPOptsExt } portUpdateOpts := ports.UpdateOpts{ Name: "updated-dhcp-conf-port", FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, } value := "valueB" updateOpts := extradhcpopts.UpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, ExtraDHCPOpts: []extradhcpopts.UpdateExtraDHCPOpt{ { OptName: "optionB", OptValue: &value, }, }, } portID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" err := ports.Update(networkClient, portID, updateOpts).ExtractInto(&s) if err != nil { panic(err) } */ package extradhcpopts requests.go000066400000000000000000000061471335005366400375030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/extradhcpoptspackage extradhcpopts import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" ) // CreateOptsExt adds extra DHCP options to the base ports.CreateOpts. type CreateOptsExt struct { // CreateOptsBuilder is the interface options structs have to satisfy in order // to be used in the main Create operation in this package. ports.CreateOptsBuilder // ExtraDHCPOpts field is a set of DHCP options for a single port. ExtraDHCPOpts []CreateExtraDHCPOpt `json:"extra_dhcp_opts,omitempty"` } // CreateExtraDHCPOpt represents the options required to create an extra DHCP // option on a port. type CreateExtraDHCPOpt struct { // OptName is the name of a DHCP option. OptName string `json:"opt_name" required:"true"` // OptValue is the value of the DHCP option. OptValue string `json:"opt_value" required:"true"` // IPVersion is the IP protocol version of a DHCP option. IPVersion gophercloud.IPVersion `json:"ip_version,omitempty"` } // ToPortCreateMap casts a CreateOptsExt struct to a map. func (opts CreateOptsExt) ToPortCreateMap() (map[string]interface{}, error) { base, err := opts.CreateOptsBuilder.ToPortCreateMap() if err != nil { return nil, err } port := base["port"].(map[string]interface{}) // Convert opts.ExtraDHCPOpts to a slice of maps. if opts.ExtraDHCPOpts != nil { extraDHCPOpts := make([]map[string]interface{}, len(opts.ExtraDHCPOpts)) for i, opt := range opts.ExtraDHCPOpts { b, err := gophercloud.BuildRequestBody(opt, "") if err != nil { return nil, err } extraDHCPOpts[i] = b } port["extra_dhcp_opts"] = extraDHCPOpts } return base, nil } // UpdateOptsExt adds extra DHCP options to the base ports.UpdateOpts. type UpdateOptsExt struct { // UpdateOptsBuilder is the interface options structs have to satisfy in order // to be used in the main Update operation in this package. ports.UpdateOptsBuilder // ExtraDHCPOpts field is a set of DHCP options for a single port. ExtraDHCPOpts []UpdateExtraDHCPOpt `json:"extra_dhcp_opts,omitempty"` } // UpdateExtraDHCPOpt represents the options required to update an extra DHCP // option on a port. type UpdateExtraDHCPOpt struct { // OptName is the name of a DHCP option. OptName string `json:"opt_name" required:"true"` // OptValue is the value of the DHCP option. OptValue *string `json:"opt_value"` // IPVersion is the IP protocol version of a DHCP option. IPVersion gophercloud.IPVersion `json:"ip_version,omitempty"` } // ToPortUpdateMap casts an UpdateOpts struct to a map. func (opts UpdateOptsExt) ToPortUpdateMap() (map[string]interface{}, error) { base, err := opts.UpdateOptsBuilder.ToPortUpdateMap() if err != nil { return nil, err } port := base["port"].(map[string]interface{}) // Convert opts.ExtraDHCPOpts to a slice of maps. if opts.ExtraDHCPOpts != nil { extraDHCPOpts := make([]map[string]interface{}, len(opts.ExtraDHCPOpts)) for i, opt := range opts.ExtraDHCPOpts { b, err := gophercloud.BuildRequestBody(opt, "") if err != nil { return nil, err } extraDHCPOpts[i] = b } port["extra_dhcp_opts"] = extraDHCPOpts } return base, nil } results.go000066400000000000000000000011631335005366400373220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/extradhcpoptspackage extradhcpopts // ExtraDHCPOptsExt is a struct that contains different DHCP options for a // single port. type ExtraDHCPOptsExt struct { ExtraDHCPOpts []ExtraDHCPOpt `json:"extra_dhcp_opts"` } // ExtraDHCPOpt represents a single set of extra DHCP options for a single port. type ExtraDHCPOpt struct { // OptName is the name of a single DHCP option. OptName string `json:"opt_name"` // OptValue is the value of a single DHCP option. OptValue string `json:"opt_value"` // IPVersion is the IP protocol version of a single DHCP option. // Valid value is 4 or 6. Default is 4. IPVersion int `json:"ip_version"` } fwaas/000077500000000000000000000000001335005366400335025ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensionsdoc.go000066400000000000000000000002261335005366400345760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas// Package fwaas provides information and interaction with the Firewall // as a Service extension for the OpenStack Networking service. package fwaas firewalls/000077500000000000000000000000001335005366400354725ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaasdoc.go000066400000000000000000000023641335005366400365730ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/firewalls/* Package firewalls allows management and retrieval of firewalls from the OpenStack Networking Service. Example to List Firewalls listOpts := firewalls.ListOpts{ TenantID: "tenant-id", } allPages, err := firewalls.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allFirewalls, err := firewalls.ExtractFirewalls(allPages) if err != nil { panic(err) } for _, fw := range allFirewalls { fmt.Printf("%+v\n", fw) } Example to Create a Firewall createOpts := firewalls.CreateOpts{ Name: "firewall_1", Description: "A firewall", PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", AdminStateUp: gophercloud.Enabled, } firewall, err := firewalls.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Firewall firewallID := "a6917946-38ab-4ffd-a55a-26c0980ce5ee" updateOpts := firewalls.UpdateOpts{ AdminStateUp: gophercloud.Disabled, } firewall, err := firewalls.Update(networkClient, firewallID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Firewall firewallID := "a6917946-38ab-4ffd-a55a-26c0980ce5ee" err := firewalls.Delete(networkClient, firewallID).ExtractErr() if err != nil { panic(err) } */ package firewalls errors.go000066400000000000000000000002331335005366400373330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/firewallspackage firewalls import "fmt" func err(str string) error { return fmt.Errorf("%s", str) } var ( errPolicyRequired = err("A policy ID is required") ) requests.go000066400000000000000000000114331335005366400376760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/firewallspackage firewalls import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToFirewallListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the firewall attributes you want to see returned. SortKey allows you to sort // by a particular firewall attribute. SortDir sets the direction, and is either // `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` Name string `q:"name"` Description string `q:"description"` AdminStateUp bool `q:"admin_state_up"` Shared bool `q:"shared"` PolicyID string `q:"firewall_policy_id"` ID string `q:"id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToFirewallListQuery formats a ListOpts into a query string. func (opts ListOpts) ToFirewallListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // firewalls. It accepts a ListOpts struct, which allows you to filter // and sort the returned collection for greater efficiency. // // Default policy settings return only those firewalls that are owned by the // tenant who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { query, err := opts.ToFirewallListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return FirewallPage{pagination.LinkedPageBase{PageResult: r}} }) } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToFirewallCreateMap() (map[string]interface{}, error) } // CreateOpts contains all the values needed to create a new firewall. type CreateOpts struct { PolicyID string `json:"firewall_policy_id" required:"true"` // TenantID specifies a tenant to own the firewall. The caller must have // an admin role in order to set this. Otherwise, this field is left unset // and the caller will be the owner. TenantID string `json:"tenant_id,omitempty"` ProjectID string `json:"project_id,omitempty"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` AdminStateUp *bool `json:"admin_state_up,omitempty"` Shared *bool `json:"shared,omitempty"` } // ToFirewallCreateMap casts a CreateOpts struct to a map. func (opts CreateOpts) ToFirewallCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "firewall") } // Create accepts a CreateOpts struct and uses the values to create a new firewall. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToFirewallCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular firewall based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToFirewallUpdateMap() (map[string]interface{}, error) } // UpdateOpts contains the values used when updating a firewall. type UpdateOpts struct { PolicyID string `json:"firewall_policy_id" required:"true"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` AdminStateUp *bool `json:"admin_state_up,omitempty"` Shared *bool `json:"shared,omitempty"` } // ToFirewallUpdateMap casts a CreateOpts struct to a map. func (opts UpdateOpts) ToFirewallUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "firewall") } // Update allows firewalls to be updated. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToFirewallUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete will permanently delete a particular firewall based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } results.go000066400000000000000000000053461335005366400375320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/firewallspackage firewalls import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Firewall is an OpenStack firewall. type Firewall struct { ID string `json:"id"` Name string `json:"name"` Description string `json:"description"` AdminStateUp bool `json:"admin_state_up"` Status string `json:"status"` PolicyID string `json:"firewall_policy_id"` TenantID string `json:"tenant_id"` ProjectID string `json:"project_id"` } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a firewall. func (r commonResult) Extract() (*Firewall, error) { var s Firewall err := r.ExtractInto(&s) return &s, err } func (r commonResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "firewall") } func ExtractFirewallsInto(r pagination.Page, v interface{}) error { return r.(FirewallPage).Result.ExtractIntoSlicePtr(v, "firewalls") } // FirewallPage is the page returned by a pager when traversing over a // collection of firewalls. type FirewallPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of firewalls has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r FirewallPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"firewalls_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a FirewallPage struct is empty. func (r FirewallPage) IsEmpty() (bool, error) { is, err := ExtractFirewalls(r) return len(is) == 0, err } // ExtractFirewalls accepts a Page struct, specifically a FirewallPage struct, // and extracts the elements into a slice of Firewall structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractFirewalls(r pagination.Page) ([]Firewall, error) { var s []Firewall err := ExtractFirewallsInto(r, &s) return s, err } // GetResult represents the result of a Get operation. Call its Extract // method to interpret it as a Firewall. type GetResult struct { commonResult } // UpdateResult represents the result of an Update operation. Call its Extract // method to interpret it as a Firewall. type UpdateResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its // ExtractErr method to determine if the operation succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // CreateResult represents the result of a Create operation. Call its Extract // method to interpret it as a Firewall. type CreateResult struct { commonResult } testing/000077500000000000000000000000001335005366400371475ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/firewallsdoc.go000066400000000000000000000000501335005366400402360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/firewalls/testing// firewalls unit tests package testing requests_test.go000066400000000000000000000236111335005366400424130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/firewalls/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/routerinsertion" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewalls", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "firewalls":[ { "status": "ACTIVE", "name": "fw1", "admin_state_up": false, "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e28a", "id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f61", "description": "OpenStack firewall 1" }, { "status": "PENDING_UPDATE", "name": "fw2", "admin_state_up": true, "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e299", "id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f99", "description": "OpenStack firewall 2" } ] } `) }) count := 0 firewalls.List(fake.ServiceClient(), firewalls.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := firewalls.ExtractFirewalls(page) if err != nil { t.Errorf("Failed to extract members: %v", err) return false, err } expected := []firewalls.Firewall{ { Status: "ACTIVE", Name: "fw1", AdminStateUp: false, TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", PolicyID: "34be8c83-4d42-4dca-a74e-b77fffb8e28a", ID: "fb5b5315-64f6-4ea3-8e58-981cc37c6f61", Description: "OpenStack firewall 1", }, { Status: "PENDING_UPDATE", Name: "fw2", AdminStateUp: true, TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", PolicyID: "34be8c83-4d42-4dca-a74e-b77fffb8e299", ID: "fb5b5315-64f6-4ea3-8e58-981cc37c6f99", Description: "OpenStack firewall 2", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestListWithExtensions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewalls", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "firewalls":[ { "status": "ACTIVE", "name": "fw1", "admin_state_up": false, "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e28a", "id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f61", "description": "OpenStack firewall 1", "router_ids": ["abcd1234"] }, { "status": "PENDING_UPDATE", "name": "fw2", "admin_state_up": true, "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e299", "id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f99", "description": "OpenStack firewall 2" } ] } `) }) type FirewallsWithExt struct { firewalls.Firewall routerinsertion.FirewallExt } allPages, err := firewalls.List(fake.ServiceClient(), nil).AllPages() th.AssertNoErr(t, err) var actual []FirewallsWithExt err = firewalls.ExtractFirewallsInto(allPages, &actual) th.AssertNoErr(t, err) th.AssertEquals(t, 2, len(actual)) th.AssertEquals(t, "fb5b5315-64f6-4ea3-8e58-981cc37c6f61", actual[0].ID) th.AssertEquals(t, "abcd1234", actual[0].RouterIDs[0]) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewalls", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "firewall":{ "name": "fw", "description": "OpenStack firewall", "admin_state_up": true, "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "firewall":{ "status": "PENDING_CREATE", "name": "fw", "description": "OpenStack firewall", "admin_state_up": true, "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c" } } `) }) options := firewalls.CreateOpts{ TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", Name: "fw", Description: "OpenStack firewall", AdminStateUp: gophercloud.Enabled, PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", } _, err := firewalls.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewalls/fb5b5315-64f6-4ea3-8e58-981cc37c6f61", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "firewall": { "status": "ACTIVE", "name": "fw", "admin_state_up": true, "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e28a", "id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f61", "description": "OpenStack firewall" } } `) }) fw, err := firewalls.Get(fake.ServiceClient(), "fb5b5315-64f6-4ea3-8e58-981cc37c6f61").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "ACTIVE", fw.Status) th.AssertEquals(t, "fw", fw.Name) th.AssertEquals(t, "OpenStack firewall", fw.Description) th.AssertEquals(t, true, fw.AdminStateUp) th.AssertEquals(t, "34be8c83-4d42-4dca-a74e-b77fffb8e28a", fw.PolicyID) th.AssertEquals(t, "fb5b5315-64f6-4ea3-8e58-981cc37c6f61", fw.ID) th.AssertEquals(t, "b4eedccc6fb74fa8a7ad6b08382b852b", fw.TenantID) } func TestGetWithExtensions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewalls/fb5b5315-64f6-4ea3-8e58-981cc37c6f61", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "firewall": { "status": "ACTIVE", "name": "fw", "admin_state_up": true, "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e28a", "id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f61", "description": "OpenStack firewall", "router_ids": ["abcd1234"] } } `) }) var fw struct { firewalls.Firewall routerinsertion.FirewallExt } err := firewalls.Get(fake.ServiceClient(), "fb5b5315-64f6-4ea3-8e58-981cc37c6f61").ExtractInto(&fw) th.AssertNoErr(t, err) th.AssertEquals(t, "ACTIVE", fw.Status) th.AssertEquals(t, "fw", fw.Name) th.AssertEquals(t, "OpenStack firewall", fw.Description) th.AssertEquals(t, true, fw.AdminStateUp) th.AssertEquals(t, "34be8c83-4d42-4dca-a74e-b77fffb8e28a", fw.PolicyID) th.AssertEquals(t, "fb5b5315-64f6-4ea3-8e58-981cc37c6f61", fw.ID) th.AssertEquals(t, "b4eedccc6fb74fa8a7ad6b08382b852b", fw.TenantID) th.AssertEquals(t, "abcd1234", fw.RouterIDs[0]) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewalls/ea5b5315-64f6-4ea3-8e58-981cc37c6576", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "firewall":{ "name": "fw", "description": "updated fw", "admin_state_up":false, "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "firewall": { "status": "ACTIVE", "name": "fw", "admin_state_up": false, "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", "id": "ea5b5315-64f6-4ea3-8e58-981cc37c6576", "description": "OpenStack firewall" } } `) }) options := firewalls.UpdateOpts{ Name: "fw", Description: "updated fw", AdminStateUp: gophercloud.Disabled, PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", } _, err := firewalls.Update(fake.ServiceClient(), "ea5b5315-64f6-4ea3-8e58-981cc37c6576", options).Extract() th.AssertNoErr(t, err) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewalls/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := firewalls.Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000005261335005366400370110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/firewallspackage firewalls import "github.com/gophercloud/gophercloud" const ( rootPath = "fw" resourcePath = "firewalls" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } policies/000077500000000000000000000000001335005366400353115ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaasdoc.go000066400000000000000000000034421335005366400364100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/policies/* Package policies allows management and retrieval of Firewall Policies in the OpenStack Networking Service. Example to List Policies listOpts := policies.ListOpts{ TenantID: "966b3c7d36a24facaf20b7e458bf2192", } allPages, err := policies.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allPolicies, err := policies.ExtractPolicies(allPages) if err != nil { panic(err) } for _, policy := range allPolicies { fmt.Printf("%+v\n", policy) } Example to Create a Policy createOpts := policies.CreateOpts{ Name: "policy_1", Description: "A policy", Rules: []string{ "98a58c87-76be-ae7c-a74e-b77fffb88d95", "7c4f087a-ed46-4ea8-8040-11ca460a61c0", } } policy, err := policies.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Policy policyID := "38aee955-6283-4279-b091-8b9c828000ec" updateOpts := policies.UpdateOpts{ Description: "New Description", } policy, err := policies.Update(networkClient, policyID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Policy policyID := "38aee955-6283-4279-b091-8b9c828000ec" err := policies.Delete(networkClient, policyID).ExtractErr() if err != nil { panic(err) } Example to Add a Rule to a Policy policyID := "38aee955-6283-4279-b091-8b9c828000ec" ruleOpts := policies.InsertRuleOpts{ ID: "98a58c87-76be-ae7c-a74e-b77fffb88d95", } policy, err := policies.AddRule(networkClient, policyID, ruleOpts).Extract() if err != nil { panic(err) } Example to Delete a Rule from a Policy policyID := "38aee955-6283-4279-b091-8b9c828000ec" ruleID := "98a58c87-76be-ae7c-a74e-b77fffb88d95", policy, err := policies.RemoveRule(networkClient, policyID, ruleID).Extract() if err != nil { panic(err) } */ package policies requests.go000066400000000000000000000141101335005366400375100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/policiespackage policies import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToPolicyListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the firewall policy attributes you want to see returned. SortKey allows you // to sort by a particular firewall policy attribute. SortDir sets the direction, // and is either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` Name string `q:"name"` Description string `q:"description"` Shared *bool `q:"shared"` Audited *bool `q:"audited"` ID string `q:"id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToPolicyListQuery formats a ListOpts into a query string. func (opts ListOpts) ToPolicyListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // firewall policies. It accepts a ListOpts struct, which allows you to filter // and sort the returned collection for greater efficiency. // // Default policy settings return only those firewall policies that are owned by // the tenant who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { query, err := opts.ToPolicyListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return PolicyPage{pagination.LinkedPageBase{PageResult: r}} }) } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToFirewallPolicyCreateMap() (map[string]interface{}, error) } // CreateOpts contains all the values needed to create a new firewall policy. type CreateOpts struct { // TenantID specifies a tenant to own the firewall. The caller must have // an admin role in order to set this. Otherwise, this field is left unset // and the caller will be the owner. TenantID string `json:"tenant_id,omitempty"` ProjectID string `json:"project_id,omitempty"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` Shared *bool `json:"shared,omitempty"` Audited *bool `json:"audited,omitempty"` Rules []string `json:"firewall_rules,omitempty"` } // ToFirewallPolicyCreateMap casts a CreateOpts struct to a map. func (opts CreateOpts) ToFirewallPolicyCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "firewall_policy") } // Create accepts a CreateOpts struct and uses the values to create a new // firewall policy. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToFirewallPolicyCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular firewall policy based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToFirewallPolicyUpdateMap() (map[string]interface{}, error) } // UpdateOpts contains the values used when updating a firewall policy. type UpdateOpts struct { Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` Shared *bool `json:"shared,omitempty"` Audited *bool `json:"audited,omitempty"` Rules []string `json:"firewall_rules,omitempty"` } // ToFirewallPolicyUpdateMap casts a CreateOpts struct to a map. func (opts UpdateOpts) ToFirewallPolicyUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "firewall_policy") } // Update allows firewall policies to be updated. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToFirewallPolicyUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete will permanently delete a particular firewall policy based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } // InsertRuleOptsBuilder allows extensions to add additional parameters to the // InsertRule request. type InsertRuleOptsBuilder interface { ToFirewallPolicyInsertRuleMap() (map[string]interface{}, error) } // InsertRuleOpts contains the values used when updating a policy's rules. type InsertRuleOpts struct { ID string `json:"firewall_rule_id" required:"true"` BeforeRuleID string `json:"insert_before,omitempty"` AfterRuleID string `json:"insert_after,omitempty"` } func (opts InsertRuleOpts) ToFirewallPolicyInsertRuleMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // AddRule will add a rule to a policy. func AddRule(c *gophercloud.ServiceClient, id string, opts InsertRuleOptsBuilder) (r InsertRuleResult) { b, err := opts.ToFirewallPolicyInsertRuleMap() if err != nil { r.Err = err return } _, r.Err = c.Put(insertURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // RemoveRule will add a rule to a policy. func RemoveRule(c *gophercloud.ServiceClient, id, ruleID string) (r RemoveRuleResult) { b := map[string]interface{}{"firewall_rule_id": ruleID} _, r.Err = c.Put(removeURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000056641335005366400373540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/policiespackage policies import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Policy is a firewall policy. type Policy struct { ID string `json:"id"` Name string `json:"name"` Description string `json:"description"` TenantID string `json:"tenant_id"` ProjectID string `json:"project_id"` Audited bool `json:"audited"` Shared bool `json:"shared"` Rules []string `json:"firewall_rules,omitempty"` } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a firewall policy. func (r commonResult) Extract() (*Policy, error) { var s struct { Policy *Policy `json:"firewall_policy"` } err := r.ExtractInto(&s) return s.Policy, err } // PolicyPage is the page returned by a pager when traversing over a // collection of firewall policies. type PolicyPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of firewall policies has // reached the end of a page and the pager seeks to traverse over a new one. // In order to do this, it needs to construct the next page's URL. func (r PolicyPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"firewall_policies_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a PolicyPage struct is empty. func (r PolicyPage) IsEmpty() (bool, error) { is, err := ExtractPolicies(r) return len(is) == 0, err } // ExtractPolicies accepts a Page struct, specifically a Policy struct, // and extracts the elements into a slice of Policy structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractPolicies(r pagination.Page) ([]Policy, error) { var s struct { Policies []Policy `json:"firewall_policies"` } err := (r.(PolicyPage)).ExtractInto(&s) return s.Policies, err } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a Policy. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its // Extract method to interpret it as a Policy. type UpdateResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its // ExtractErr method to determine if the operation succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a Policy. type CreateResult struct { commonResult } // InsertRuleResult represents the result of an InsertRule operation. Call its // Extract method to interpret it as a Policy. type InsertRuleResult struct { commonResult } // RemoveRuleResult represents the result of a RemoveRule operation. Call its // Extract method to interpret it as a Policy. type RemoveRuleResult struct { commonResult } testing/000077500000000000000000000000001335005366400367665ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/policiesdoc.go000066400000000000000000000000471335005366400400630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/policies/testing// policies unit tests package testing requests_test.go000066400000000000000000000172201335005366400422310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/policies/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewall_policies", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "firewall_policies": [ { "name": "policy1", "firewall_rules": [ "75452b36-268e-4e75-aaf4-f0e7ed50bc97", "c9e77ca0-1bc8-497d-904d-948107873dc6" ], "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", "audited": true, "shared": false, "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", "description": "Firewall policy 1" }, { "name": "policy2", "firewall_rules": [ "03d2a6ad-633f-431a-8463-4370d06a22c8" ], "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", "audited": false, "shared": true, "id": "c854fab5-bdaf-4a86-9359-78de93e5df01", "description": "Firewall policy 2" } ] } `) }) count := 0 policies.List(fake.ServiceClient(), policies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := policies.ExtractPolicies(page) if err != nil { t.Errorf("Failed to extract members: %v", err) return false, err } expected := []policies.Policy{ { Name: "policy1", Rules: []string{ "75452b36-268e-4e75-aaf4-f0e7ed50bc97", "c9e77ca0-1bc8-497d-904d-948107873dc6", }, TenantID: "9145d91459d248b1b02fdaca97c6a75d", Audited: true, Shared: false, ID: "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", Description: "Firewall policy 1", }, { Name: "policy2", Rules: []string{ "03d2a6ad-633f-431a-8463-4370d06a22c8", }, TenantID: "9145d91459d248b1b02fdaca97c6a75d", Audited: false, Shared: true, ID: "c854fab5-bdaf-4a86-9359-78de93e5df01", Description: "Firewall policy 2", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewall_policies", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "firewall_policy":{ "name": "policy", "firewall_rules": [ "98a58c87-76be-ae7c-a74e-b77fffb88d95", "11a58c87-76be-ae7c-a74e-b77fffb88a32" ], "description": "Firewall policy", "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", "audited": true, "shared": false } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "firewall_policy":{ "name": "policy", "firewall_rules": [ "98a58c87-76be-ae7c-a74e-b77fffb88d95", "11a58c87-76be-ae7c-a74e-b77fffb88a32" ], "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", "audited": false, "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", "description": "Firewall policy" } } `) }) options := policies.CreateOpts{ TenantID: "9145d91459d248b1b02fdaca97c6a75d", Name: "policy", Description: "Firewall policy", Shared: gophercloud.Disabled, Audited: gophercloud.Enabled, Rules: []string{ "98a58c87-76be-ae7c-a74e-b77fffb88d95", "11a58c87-76be-ae7c-a74e-b77fffb88a32", }, } _, err := policies.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewall_policies/bcab5315-64f6-4ea3-8e58-981cc37c6f61", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "firewall_policy":{ "name": "www", "firewall_rules": [ "75452b36-268e-4e75-aaf4-f0e7ed50bc97", "c9e77ca0-1bc8-497d-904d-948107873dc6", "03d2a6ad-633f-431a-8463-4370d06a22c8" ], "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", "audited": false, "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", "description": "Firewall policy web" } } `) }) policy, err := policies.Get(fake.ServiceClient(), "bcab5315-64f6-4ea3-8e58-981cc37c6f61").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "www", policy.Name) th.AssertEquals(t, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", policy.ID) th.AssertEquals(t, "Firewall policy web", policy.Description) th.AssertEquals(t, 3, len(policy.Rules)) th.AssertEquals(t, "75452b36-268e-4e75-aaf4-f0e7ed50bc97", policy.Rules[0]) th.AssertEquals(t, "c9e77ca0-1bc8-497d-904d-948107873dc6", policy.Rules[1]) th.AssertEquals(t, "03d2a6ad-633f-431a-8463-4370d06a22c8", policy.Rules[2]) th.AssertEquals(t, "9145d91459d248b1b02fdaca97c6a75d", policy.TenantID) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewall_policies/f2b08c1e-aa81-4668-8ae1-1401bcb0576c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "firewall_policy":{ "name": "policy", "firewall_rules": [ "98a58c87-76be-ae7c-a74e-b77fffb88d95", "11a58c87-76be-ae7c-a74e-b77fffb88a32" ], "description": "Firewall policy" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "firewall_policy":{ "name": "policy", "firewall_rules": [ "75452b36-268e-4e75-aaf4-f0e7ed50bc97", "c9e77ca0-1bc8-497d-904d-948107873dc6", "03d2a6ad-633f-431a-8463-4370d06a22c8" ], "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", "audited": false, "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", "description": "Firewall policy" } } `) }) options := policies.UpdateOpts{ Name: "policy", Description: "Firewall policy", Rules: []string{ "98a58c87-76be-ae7c-a74e-b77fffb88d95", "11a58c87-76be-ae7c-a74e-b77fffb88a32", }, } _, err := policies.Update(fake.ServiceClient(), "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", options).Extract() th.AssertNoErr(t, err) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewall_policies/4ec89077-d057-4a2b-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := policies.Delete(fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000012331335005366400366240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/policiespackage policies import "github.com/gophercloud/gophercloud" const ( rootPath = "fw" resourcePath = "firewall_policies" insertPath = "insert_rule" removePath = "remove_rule" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } func insertURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id, insertPath) } func removeURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id, removePath) } routerinsertion/000077500000000000000000000000001335005366400367555ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaasdoc.go000066400000000000000000000030421335005366400400500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/routerinsertion/* Package routerinsertion implements the fwaasrouterinsertion Firewall extension. It is used to manage the router information of a firewall. Example to List Firewalls with Router Information type FirewallsWithRouters struct { firewalls.Firewall routerinsertion.FirewallExt } var allFirewalls []FirewallsWithRouters allPages, err := firewalls.List(networkClient, nil).AllPages() if err != nil { panic(err) } err = firewalls.ExtractFirewallsInto(allPages, &allFirewalls) if err != nil { panic(err) } for _, fw := range allFirewalls { fmt.Printf("%+v\n", fw) } Example to Create a Firewall with a Router firewallCreateOpts := firewalls.CreateOpts{ Name: "firewall_1", PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", } createOpts := routerinsertion.CreateOptsExt{ CreateOptsBuilder: firewallCreateOpts, RouterIDs: []string{ "8a3a0d6a-34b5-4a92-b65d-6375a4c1e9e8", }, } firewall, err := firewalls.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Firewall with a Router firewallID := "a6917946-38ab-4ffd-a55a-26c0980ce5ee" firewallUpdateOpts := firewalls.UpdateOpts{ Description: "updated firewall", PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", } updateOpts := routerinsertion.UpdateOptsExt{ UpdateOptsBuilder: firewallUpdateOpts, RouterIDs: []string{ "8a3a0d6a-34b5-4a92-b65d-6375a4c1e9e8", }, } firewall, err := firewalls.Update(networkClient, firewallID, updateOpts).Extract() if err != nil { panic(err) } */ package routerinsertion requests.go000066400000000000000000000023051335005366400411570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/routerinsertionpackage routerinsertion import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls" ) // CreateOptsExt adds the RouterIDs option to the base CreateOpts. type CreateOptsExt struct { firewalls.CreateOptsBuilder RouterIDs []string `json:"router_ids"` } // ToFirewallCreateMap adds router_ids to the base firewall creation options. func (opts CreateOptsExt) ToFirewallCreateMap() (map[string]interface{}, error) { base, err := opts.CreateOptsBuilder.ToFirewallCreateMap() if err != nil { return nil, err } firewallMap := base["firewall"].(map[string]interface{}) firewallMap["router_ids"] = opts.RouterIDs return base, nil } // UpdateOptsExt adds the RouterIDs option to the base UpdateOpts. type UpdateOptsExt struct { firewalls.UpdateOptsBuilder RouterIDs []string `json:"router_ids"` } // ToFirewallUpdateMap adds router_ids to the base firewall update options. func (opts UpdateOptsExt) ToFirewallUpdateMap() (map[string]interface{}, error) { base, err := opts.UpdateOptsBuilder.ToFirewallUpdateMap() if err != nil { return nil, err } firewallMap := base["firewall"].(map[string]interface{}) firewallMap["router_ids"] = opts.RouterIDs return base, nil } results.go000066400000000000000000000003301335005366400410010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/routerinsertionpackage routerinsertion // FirewallExt is an extension to the base Firewall object type FirewallExt struct { // RouterIDs are the routers that the firewall is attached to. RouterIDs []string `json:"router_ids"` } testing/000077500000000000000000000000001335005366400404325ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/routerinsertiondoc.go000066400000000000000000000000561335005366400415270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/routerinsertion/testing// routerinsertion unit tests package testing requests_test.go000066400000000000000000000152671335005366400437060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/routerinsertion/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/routerinsertion" th "github.com/gophercloud/gophercloud/testhelper" ) func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewalls", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "firewall":{ "name": "fw", "description": "OpenStack firewall", "admin_state_up": true, "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "router_ids": [ "8a3a0d6a-34b5-4a92-b65d-6375a4c1e9e8" ] } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "firewall":{ "status": "PENDING_CREATE", "name": "fw", "description": "OpenStack firewall", "admin_state_up": true, "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c" } } `) }) firewallCreateOpts := firewalls.CreateOpts{ TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", Name: "fw", Description: "OpenStack firewall", AdminStateUp: gophercloud.Enabled, PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", } createOpts := routerinsertion.CreateOptsExt{ CreateOptsBuilder: firewallCreateOpts, RouterIDs: []string{"8a3a0d6a-34b5-4a92-b65d-6375a4c1e9e8"}, } _, err := firewalls.Create(fake.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) } func TestCreateWithNoRouters(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewalls", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "firewall":{ "name": "fw", "description": "OpenStack firewall", "admin_state_up": true, "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "router_ids": [] } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "firewall":{ "status": "PENDING_CREATE", "name": "fw", "description": "OpenStack firewall", "admin_state_up": true, "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c" } } `) }) firewallCreateOpts := firewalls.CreateOpts{ TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", Name: "fw", Description: "OpenStack firewall", AdminStateUp: gophercloud.Enabled, PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", } createOpts := routerinsertion.CreateOptsExt{ CreateOptsBuilder: firewallCreateOpts, RouterIDs: []string{}, } _, err := firewalls.Create(fake.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewalls/ea5b5315-64f6-4ea3-8e58-981cc37c6576", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "firewall":{ "name": "fw", "description": "updated fw", "admin_state_up":false, "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", "router_ids": [ "8a3a0d6a-34b5-4a92-b65d-6375a4c1e9e8" ] } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "firewall": { "status": "ACTIVE", "name": "fw", "admin_state_up": false, "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", "id": "ea5b5315-64f6-4ea3-8e58-981cc37c6576", "description": "OpenStack firewall" } } `) }) firewallUpdateOpts := firewalls.UpdateOpts{ Name: "fw", Description: "updated fw", AdminStateUp: gophercloud.Disabled, PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", } updateOpts := routerinsertion.UpdateOptsExt{ UpdateOptsBuilder: firewallUpdateOpts, RouterIDs: []string{"8a3a0d6a-34b5-4a92-b65d-6375a4c1e9e8"}, } _, err := firewalls.Update(fake.ServiceClient(), "ea5b5315-64f6-4ea3-8e58-981cc37c6576", updateOpts).Extract() th.AssertNoErr(t, err) } func TestUpdateWithNoRouters(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewalls/ea5b5315-64f6-4ea3-8e58-981cc37c6576", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "firewall":{ "name": "fw", "description": "updated fw", "admin_state_up":false, "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", "router_ids": [] } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "firewall": { "status": "ACTIVE", "name": "fw", "admin_state_up": false, "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", "id": "ea5b5315-64f6-4ea3-8e58-981cc37c6576", "description": "OpenStack firewall" } } `) }) firewallUpdateOpts := firewalls.UpdateOpts{ Name: "fw", Description: "updated fw", AdminStateUp: gophercloud.Disabled, PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", } updateOpts := routerinsertion.UpdateOptsExt{ UpdateOptsBuilder: firewallUpdateOpts, RouterIDs: []string{}, } _, err := firewalls.Update(fake.ServiceClient(), "ea5b5315-64f6-4ea3-8e58-981cc37c6576", updateOpts).Extract() th.AssertNoErr(t, err) } rules/000077500000000000000000000000001335005366400346345ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaasdoc.go000066400000000000000000000023751335005366400357370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/rules/* Package rules enables management and retrieval of Firewall Rules in the OpenStack Networking Service. Example to List Rules listOpts := rules.ListOpts{ Protocol: rules.ProtocolAny, } allPages, err := rules.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allRules, err := rules.ExtractRules(allPages) if err != nil { panic(err) } for _, rule := range allRules { fmt.Printf("%+v\n", rule) } Example to Create a Rule createOpts := rules.CreateOpts{ Action: "allow", Protocol: rules.ProtocolTCP, Description: "ssh", DestinationPort: 22, DestinationIPAddress: "192.168.1.0/24", } rule, err := rules.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Rule ruleID := "f03bd950-6c56-4f5e-a307-45967078f507" newPort := 80 newDescription := "http" updateOpts := rules.UpdateOpts{ Description: &newDescription, port: &newPort, } rule, err := rules.Update(networkClient, ruleID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Rule ruleID := "f03bd950-6c56-4f5e-a307-45967078f507" err := rules.Delete(networkClient, ruleID).ExtractErr() if err != nil { panic(err) } */ package rules errors.go000066400000000000000000000003641335005366400365020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/rulespackage rules import "fmt" func err(str string) error { return fmt.Errorf("%s", str) } var ( errProtocolRequired = err("A protocol is required (tcp, udp, icmp or any)") errActionRequired = err("An action is required (allow or deny)") ) requests.go000066400000000000000000000154441335005366400370460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/rulespackage rules import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type ( // Protocol represents a valid rule protocol. Protocol string ) const ( // ProtocolAny is to allow any protocol. ProtocolAny Protocol = "any" // ProtocolICMP is to allow the ICMP protocol. ProtocolICMP Protocol = "icmp" // ProtocolTCP is to allow the TCP protocol. ProtocolTCP Protocol = "tcp" // ProtocolUDP is to allow the UDP protocol. ProtocolUDP Protocol = "udp" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToRuleListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the Firewall rule attributes you want to see returned. SortKey allows you to // sort by a particular firewall rule attribute. SortDir sets the direction, and // is either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` Name string `q:"name"` Description string `q:"description"` Protocol string `q:"protocol"` Action string `q:"action"` IPVersion int `q:"ip_version"` SourceIPAddress string `q:"source_ip_address"` DestinationIPAddress string `q:"destination_ip_address"` SourcePort string `q:"source_port"` DestinationPort string `q:"destination_port"` Enabled bool `q:"enabled"` ID string `q:"id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToRuleListQuery formats a ListOpts into a query string. func (opts ListOpts) ToRuleListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return "", err } return q.String(), nil } // List returns a Pager which allows you to iterate over a collection of // firewall rules. It accepts a ListOpts struct, which allows you to filter // and sort the returned collection for greater efficiency. // // Default policy settings return only those firewall rules that are owned by // the tenant who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { query, err := opts.ToRuleListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return RulePage{pagination.LinkedPageBase{PageResult: r}} }) } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToRuleCreateMap() (map[string]interface{}, error) } // CreateOpts contains all the values needed to create a new firewall rule. type CreateOpts struct { Protocol Protocol `json:"protocol" required:"true"` Action string `json:"action" required:"true"` TenantID string `json:"tenant_id,omitempty"` ProjectID string `json:"project_id,omitempty"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` IPVersion gophercloud.IPVersion `json:"ip_version,omitempty"` SourceIPAddress string `json:"source_ip_address,omitempty"` DestinationIPAddress string `json:"destination_ip_address,omitempty"` SourcePort string `json:"source_port,omitempty"` DestinationPort string `json:"destination_port,omitempty"` Shared *bool `json:"shared,omitempty"` Enabled *bool `json:"enabled,omitempty"` } // ToRuleCreateMap casts a CreateOpts struct to a map. func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "firewall_rule") if err != nil { return nil, err } if m := b["firewall_rule"].(map[string]interface{}); m["protocol"] == "any" { m["protocol"] = nil } return b, nil } // Create accepts a CreateOpts struct and uses the values to create a new // firewall rule. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToRuleCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular firewall rule based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToRuleUpdateMap() (map[string]interface{}, error) } // UpdateOpts contains the values used when updating a firewall rule. // These fields are all pointers so that unset fields will not cause the // existing Rule attribute to be removed. type UpdateOpts struct { Protocol *string `json:"protocol,omitempty"` Action *string `json:"action,omitempty"` Name *string `json:"name,omitempty"` Description *string `json:"description,omitempty"` IPVersion *gophercloud.IPVersion `json:"ip_version,omitempty"` SourceIPAddress *string `json:"source_ip_address,omitempty"` DestinationIPAddress *string `json:"destination_ip_address,omitempty"` SourcePort *string `json:"source_port,omitempty"` DestinationPort *string `json:"destination_port,omitempty"` Shared *bool `json:"shared,omitempty"` Enabled *bool `json:"enabled,omitempty"` } // ToRuleUpdateMap casts a UpdateOpts struct to a map. func (opts UpdateOpts) ToRuleUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "firewall_rule") } // Update allows firewall policies to be updated. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToRuleUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete will permanently delete a particular firewall rule based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } results.go000066400000000000000000000060771335005366400366760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/rulespackage rules import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Rule represents a firewall rule. type Rule struct { ID string `json:"id"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` Protocol string `json:"protocol"` Action string `json:"action"` IPVersion int `json:"ip_version,omitempty"` SourceIPAddress string `json:"source_ip_address,omitempty"` DestinationIPAddress string `json:"destination_ip_address,omitempty"` SourcePort string `json:"source_port,omitempty"` DestinationPort string `json:"destination_port,omitempty"` Shared bool `json:"shared,omitempty"` Enabled bool `json:"enabled,omitempty"` PolicyID string `json:"firewall_policy_id"` Position int `json:"position"` TenantID string `json:"tenant_id"` ProjectID string `json:"project_id"` } // RulePage is the page returned by a pager when traversing over a // collection of firewall rules. type RulePage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of firewall rules has // reached the end of a page and the pager seeks to traverse over a new one. // In order to do this, it needs to construct the next page's URL. func (r RulePage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"firewall_rules_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a RulePage struct is empty. func (r RulePage) IsEmpty() (bool, error) { is, err := ExtractRules(r) return len(is) == 0, err } // ExtractRules accepts a Page struct, specifically a RulePage struct, // and extracts the elements into a slice of Rule structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractRules(r pagination.Page) ([]Rule, error) { var s struct { Rules []Rule `json:"firewall_rules"` } err := (r.(RulePage)).ExtractInto(&s) return s.Rules, err } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a firewall rule. func (r commonResult) Extract() (*Rule, error) { var s struct { Rule *Rule `json:"firewall_rule"` } err := r.ExtractInto(&s) return s.Rule, err } // GetResult represents the result of a get operation. Call its Extract method // to interpret it as a Rule. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a Rule. type UpdateResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its ExtractErr // method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a Rule. type CreateResult struct { commonResult } testing/000077500000000000000000000000001335005366400363115ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/rulesdoc.go000066400000000000000000000000441335005366400374030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/rules/testing// rules unit tests package testing requests_test.go000066400000000000000000000252521335005366400415600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/rules/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewall_rules", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "firewall_rules": [ { "protocol": "tcp", "description": "ssh rule", "source_port": null, "source_ip_address": null, "destination_ip_address": "192.168.1.0/24", "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919", "position": 2, "destination_port": "22", "id": "f03bd950-6c56-4f5e-a307-45967078f507", "name": "ssh_form_any", "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", "enabled": true, "action": "allow", "ip_version": 4, "shared": false }, { "protocol": "udp", "description": "udp rule", "source_port": null, "source_ip_address": null, "destination_ip_address": null, "firewall_policy_id": "98d7fb51-698c-4123-87e8-f1eee6b5ab7e", "position": 1, "destination_port": null, "id": "ab7bd950-6c56-4f5e-a307-45967078f890", "name": "deny_all_udp", "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", "enabled": true, "action": "deny", "ip_version": 4, "shared": false } ] } `) }) count := 0 rules.List(fake.ServiceClient(), rules.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := rules.ExtractRules(page) if err != nil { t.Errorf("Failed to extract members: %v", err) return false, err } expected := []rules.Rule{ { Protocol: "tcp", Description: "ssh rule", SourcePort: "", SourceIPAddress: "", DestinationIPAddress: "192.168.1.0/24", PolicyID: "e2a5fb51-698c-4898-87e8-f1eee6b50919", Position: 2, DestinationPort: "22", ID: "f03bd950-6c56-4f5e-a307-45967078f507", Name: "ssh_form_any", TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61", Enabled: true, Action: "allow", IPVersion: 4, Shared: false, }, { Protocol: "udp", Description: "udp rule", SourcePort: "", SourceIPAddress: "", DestinationIPAddress: "", PolicyID: "98d7fb51-698c-4123-87e8-f1eee6b5ab7e", Position: 1, DestinationPort: "", ID: "ab7bd950-6c56-4f5e-a307-45967078f890", Name: "deny_all_udp", TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61", Enabled: true, Action: "deny", IPVersion: 4, Shared: false, }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewall_rules", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "firewall_rule": { "protocol": "tcp", "description": "ssh rule", "destination_ip_address": "192.168.1.0/24", "destination_port": "22", "name": "ssh_form_any", "action": "allow", "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "firewall_rule":{ "protocol": "tcp", "description": "ssh rule", "source_port": null, "source_ip_address": null, "destination_ip_address": "192.168.1.0/24", "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919", "position": 2, "destination_port": "22", "id": "f03bd950-6c56-4f5e-a307-45967078f507", "name": "ssh_form_any", "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", "enabled": true, "action": "allow", "ip_version": 4, "shared": false } } `) }) options := rules.CreateOpts{ TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61", Protocol: rules.ProtocolTCP, Description: "ssh rule", DestinationIPAddress: "192.168.1.0/24", DestinationPort: "22", Name: "ssh_form_any", Action: "allow", } _, err := rules.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) } func TestCreateAnyProtocol(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewall_rules", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "firewall_rule": { "protocol": null, "description": "any to 192.168.1.0/24", "destination_ip_address": "192.168.1.0/24", "name": "any_to_192.168.1.0/24", "action": "allow", "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "firewall_rule":{ "protocol": null, "description": "any to 192.168.1.0/24", "source_port": null, "source_ip_address": null, "destination_ip_address": "192.168.1.0/24", "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919", "position": 2, "destination_port": null, "id": "f03bd950-6c56-4f5e-a307-45967078f507", "name": "any_to_192.168.1.0/24", "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", "enabled": true, "action": "allow", "ip_version": 4, "shared": false } } `) }) options := rules.CreateOpts{ TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61", Protocol: rules.ProtocolAny, Description: "any to 192.168.1.0/24", DestinationIPAddress: "192.168.1.0/24", Name: "any_to_192.168.1.0/24", Action: "allow", } _, err := rules.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewall_rules/f03bd950-6c56-4f5e-a307-45967078f507", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "firewall_rule":{ "protocol": "tcp", "description": "ssh rule", "source_port": null, "source_ip_address": null, "destination_ip_address": "192.168.1.0/24", "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919", "position": 2, "destination_port": "22", "id": "f03bd950-6c56-4f5e-a307-45967078f507", "name": "ssh_form_any", "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", "enabled": true, "action": "allow", "ip_version": 4, "shared": false } } `) }) rule, err := rules.Get(fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "tcp", rule.Protocol) th.AssertEquals(t, "ssh rule", rule.Description) th.AssertEquals(t, "192.168.1.0/24", rule.DestinationIPAddress) th.AssertEquals(t, "e2a5fb51-698c-4898-87e8-f1eee6b50919", rule.PolicyID) th.AssertEquals(t, 2, rule.Position) th.AssertEquals(t, "22", rule.DestinationPort) th.AssertEquals(t, "f03bd950-6c56-4f5e-a307-45967078f507", rule.ID) th.AssertEquals(t, "ssh_form_any", rule.Name) th.AssertEquals(t, "80cf934d6ffb4ef5b244f1c512ad1e61", rule.TenantID) th.AssertEquals(t, true, rule.Enabled) th.AssertEquals(t, "allow", rule.Action) th.AssertEquals(t, 4, rule.IPVersion) th.AssertEquals(t, false, rule.Shared) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewall_rules/f03bd950-6c56-4f5e-a307-45967078f507", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "firewall_rule":{ "protocol": "tcp", "description": "ssh rule", "destination_ip_address": "192.168.1.0/24", "destination_port": "22", "name": "ssh_form_any", "action": "allow", "enabled": false } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "firewall_rule":{ "protocol": "tcp", "description": "ssh rule", "destination_ip_address": "192.168.1.0/24", "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919", "position": 2, "destination_port": "22", "id": "f03bd950-6c56-4f5e-a307-45967078f507", "name": "ssh_form_any", "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", "enabled": false, "action": "allow", "ip_version": 4, "shared": false } } `) }) newProtocol := "tcp" newDescription := "ssh rule" newDestinationIP := "192.168.1.0/24" newDestintionPort := "22" newName := "ssh_form_any" newAction := "allow" options := rules.UpdateOpts{ Protocol: &newProtocol, Description: &newDescription, DestinationIPAddress: &newDestinationIP, DestinationPort: &newDestintionPort, Name: &newName, Action: &newAction, Enabled: gophercloud.Disabled, } _, err := rules.Update(fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507", options).Extract() th.AssertNoErr(t, err) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/fw/firewall_rules/4ec89077-d057-4a2b-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := rules.Delete(fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000005271335005366400361540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/fwaas/rulespackage rules import "github.com/gophercloud/gophercloud" const ( rootPath = "fw" resourcePath = "firewall_rules" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } layer3/000077500000000000000000000000001335005366400336005ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensionsdoc.go000066400000000000000000000004761335005366400347030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/layer3// Package layer3 provides access to the Layer-3 networking extension for the // OpenStack Neutron service. This extension allows API users to route packets // between subnets, forward packets from internal networks to external ones, // and access instances from external networks through floating IPs. package layer3 floatingips/000077500000000000000000000000001335005366400361175ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/layer3doc.go000066400000000000000000000027641335005366400372240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/layer3/floatingips/* package floatingips enables management and retrieval of Floating IPs from the OpenStack Networking service. Example to List Floating IPs listOpts := floatingips.ListOpts{ FloatingNetworkID: "a6917946-38ab-4ffd-a55a-26c0980ce5ee", } allPages, err := floatingips.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allFIPs, err := floatingips.ExtractFloatingIPs(allPages) if err != nil { panic(err) } for _, fip := range allFIPs { fmt.Printf("%+v\n", fip) } Example to Create a Floating IP createOpts := floatingips.CreateOpts{ FloatingNetworkID: "a6917946-38ab-4ffd-a55a-26c0980ce5ee", } fip, err := floatingips.Create(networkingClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Floating IP fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" portID := "76d0a61b-b8e5-490c-9892-4cf674f2bec8" updateOpts := floatingips.UpdateOpts{ PortID: &portID, } fip, err := floatingips.Update(networkingClient, fipID, updateOpts).Extract() if err != nil { panic(err) } Example to Disassociate a Floating IP with a Port fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" updateOpts := floatingips.UpdateOpts{ PortID: nil, } fip, err := floatingips.Update(networkingClient, fipID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Floating IP fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" err := floatingips.Delete(networkClient, fipID).ExtractErr() if err != nil { panic(err) } */ package floatingips requests.go000066400000000000000000000142511335005366400403240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/layer3/floatingipspackage floatingips import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the floating IP attributes you want to see returned. SortKey allows you to // sort by a particular network attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { ID string `q:"id"` FloatingNetworkID string `q:"floating_network_id"` PortID string `q:"port_id"` FixedIP string `q:"fixed_ip_address"` FloatingIP string `q:"floating_ip_address"` TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` RouterID string `q:"router_id"` Status string `q:"status"` } // List returns a Pager which allows you to iterate over a collection of // floating IP resources. It accepts a ListOpts struct, which allows you to // filter and sort the returned collection for greater efficiency. func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { q, err := gophercloud.BuildQueryString(&opts) if err != nil { return pagination.Pager{Err: err} } u := rootURL(c) + q.String() return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page { return FloatingIPPage{pagination.LinkedPageBase{PageResult: r}} }) } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToFloatingIPCreateMap() (map[string]interface{}, error) } // CreateOpts contains all the values needed to create a new floating IP // resource. The only required fields are FloatingNetworkID and PortID which // refer to the external network and internal port respectively. type CreateOpts struct { FloatingNetworkID string `json:"floating_network_id" required:"true"` FloatingIP string `json:"floating_ip_address,omitempty"` PortID string `json:"port_id,omitempty"` FixedIP string `json:"fixed_ip_address,omitempty"` SubnetID string `json:"subnet_id,omitempty"` TenantID string `json:"tenant_id,omitempty"` ProjectID string `json:"project_id,omitempty"` } // ToFloatingIPCreateMap allows CreateOpts to satisfy the CreateOptsBuilder // interface func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "floatingip") } // Create accepts a CreateOpts struct and uses the values provided to create a // new floating IP resource. You can create floating IPs on external networks // only. If you provide a FloatingNetworkID which refers to a network that is // not external (i.e. its `router:external' attribute is False), the operation // will fail and return a 400 error. // // If you do not specify a FloatingIP address value, the operation will // automatically allocate an available address for the new resource. If you do // choose to specify one, it must fall within the subnet range for the external // network - otherwise the operation returns a 400 error. If the FloatingIP // address is already in use, the operation returns a 409 error code. // // You can associate the new resource with an internal port by using the PortID // field. If you specify a PortID that is not valid, the operation will fail and // return 404 error code. // // You must also configure an IP address for the port associated with the PortID // you have provided - this is what the FixedIP refers to: an IP fixed to a // port. Because a port might be associated with multiple IP addresses, you can // use the FixedIP field to associate a particular IP address rather than have // the API assume for you. If you specify an IP address that is not valid, the // operation will fail and return a 400 error code. If the PortID and FixedIP // are already associated with another resource, the operation will fail and // returns a 409 error code. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToFloatingIPCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular floating IP resource based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToFloatingIPUpdateMap() (map[string]interface{}, error) } // UpdateOpts contains the values used when updating a floating IP resource. The // only value that can be updated is which internal port the floating IP is // linked to. To associate the floating IP with a new internal port, provide its // ID. To disassociate the floating IP from all ports, provide an empty string. type UpdateOpts struct { PortID *string `json:"port_id"` } // ToFloatingIPUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder // interface func (opts UpdateOpts) ToFloatingIPUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "floatingip") } // Update allows floating IP resources to be updated. Currently, the only way to // "update" a floating IP is to associate it with a new internal port, or // disassociated it from all ports. See UpdateOpts for instructions of how to // do this. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToFloatingIPUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete will permanently delete a particular floating IP resource. Please // ensure this is what you want - you can also disassociate the IP from existing // internal ports. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } results.go000066400000000000000000000074641335005366400401620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/layer3/floatingipspackage floatingips import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // FloatingIP represents a floating IP resource. A floating IP is an external // IP address that is mapped to an internal port and, optionally, a specific // IP address on a private network. In other words, it enables access to an // instance on a private network from an external network. For this reason, // floating IPs can only be defined on networks where the `router:external' // attribute (provided by the external network extension) is set to True. type FloatingIP struct { // ID is the unique identifier for the floating IP instance. ID string `json:"id"` // FloatingNetworkID is the UUID of the external network where the floating // IP is to be created. FloatingNetworkID string `json:"floating_network_id"` // FloatingIP is the address of the floating IP on the external network. FloatingIP string `json:"floating_ip_address"` // PortID is the UUID of the port on an internal network that is associated // with the floating IP. PortID string `json:"port_id"` // FixedIP is the specific IP address of the internal port which should be // associated with the floating IP. FixedIP string `json:"fixed_ip_address"` // TenantID is the project owner of the floating IP. Only admin users can // specify a project identifier other than its own. TenantID string `json:"tenant_id"` // ProjectID is the project owner of the floating IP. ProjectID string `json:"project_id"` // Status is the condition of the API resource. Status string `json:"status"` // RouterID is the ID of the router used for this floating IP. RouterID string `json:"router_id"` } type commonResult struct { gophercloud.Result } // Extract will extract a FloatingIP resource from a result. func (r commonResult) Extract() (*FloatingIP, error) { var s struct { FloatingIP *FloatingIP `json:"floatingip"` } err := r.ExtractInto(&s) return s.FloatingIP, err } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a FloatingIP. type CreateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a FloatingIP. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a FloatingIP. type UpdateResult struct { commonResult } // DeleteResult represents the result of an update operation. Call its // ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // FloatingIPPage is the page returned by a pager when traversing over a // collection of floating IPs. type FloatingIPPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of floating IPs has // reached the end of a page and the pager seeks to traverse over a new one. // In order to do this, it needs to construct the next page's URL. func (r FloatingIPPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"floatingips_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a FloatingIPPage struct is empty. func (r FloatingIPPage) IsEmpty() (bool, error) { is, err := ExtractFloatingIPs(r) return len(is) == 0, err } // ExtractFloatingIPs accepts a Page struct, specifically a FloatingIPPage // struct, and extracts the elements into a slice of FloatingIP structs. In // other words, a generic collection is mapped into a relevant slice. func ExtractFloatingIPs(r pagination.Page) ([]FloatingIP, error) { var s struct { FloatingIPs []FloatingIP `json:"floatingips"` } err := (r.(FloatingIPPage)).ExtractInto(&s) return s.FloatingIPs, err } testing/000077500000000000000000000000001335005366400375745ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/layer3/floatingipsdoc.go000066400000000000000000000000521335005366400406650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/layer3/floatingips/testing// floatingips unit tests package testing requests_test.go000066400000000000000000000314401335005366400430370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/layer3/floatingips/testingpackage testing import ( "fmt" "net/http" "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "floatingips": [ { "floating_network_id": "6d67c30a-ddb4-49a1-bec3-a65b286b4170", "router_id": null, "fixed_ip_address": null, "floating_ip_address": "192.0.0.4", "tenant_id": "017d8de156df4177889f31a9bd6edc00", "status": "DOWN", "port_id": null, "id": "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", "router_id": "1117c30a-ddb4-49a1-bec3-a65b286b4170" }, { "floating_network_id": "90f742b1-6d17-487b-ba95-71881dbc0b64", "router_id": "0a24cb83-faf5-4d7f-b723-3144ed8a2167", "fixed_ip_address": "192.0.0.2", "floating_ip_address": "10.0.0.3", "tenant_id": "017d8de156df4177889f31a9bd6edc00", "status": "DOWN", "port_id": "74a342ce-8e07-4e91-880c-9f834b68fa25", "id": "ada25a95-f321-4f59-b0e0-f3a970dd3d63", "router_id": "2227c30a-ddb4-49a1-bec3-a65b286b4170" } ] } `) }) count := 0 floatingips.List(fake.ServiceClient(), floatingips.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := floatingips.ExtractFloatingIPs(page) if err != nil { t.Errorf("Failed to extract floating IPs: %v", err) return false, err } expected := []floatingips.FloatingIP{ { FloatingNetworkID: "6d67c30a-ddb4-49a1-bec3-a65b286b4170", FixedIP: "", FloatingIP: "192.0.0.4", TenantID: "017d8de156df4177889f31a9bd6edc00", Status: "DOWN", PortID: "", ID: "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", RouterID: "1117c30a-ddb4-49a1-bec3-a65b286b4170", }, { FloatingNetworkID: "90f742b1-6d17-487b-ba95-71881dbc0b64", FixedIP: "192.0.0.2", FloatingIP: "10.0.0.3", TenantID: "017d8de156df4177889f31a9bd6edc00", Status: "DOWN", PortID: "74a342ce-8e07-4e91-880c-9f834b68fa25", ID: "ada25a95-f321-4f59-b0e0-f3a970dd3d63", RouterID: "2227c30a-ddb4-49a1-bec3-a65b286b4170", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestInvalidNextPageURLs(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, `{"floatingips": [{}], "floatingips_links": {}}`) }) floatingips.List(fake.ServiceClient(), floatingips.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { floatingips.ExtractFloatingIPs(page) return true, nil }) } func TestRequiredFieldsForCreate(t *testing.T) { res1 := floatingips.Create(fake.ServiceClient(), floatingips.CreateOpts{FloatingNetworkID: ""}) if res1.Err == nil { t.Fatalf("Expected error, got none") } res2 := floatingips.Create(fake.ServiceClient(), floatingips.CreateOpts{FloatingNetworkID: "foo", PortID: ""}) if res2.Err == nil { t.Fatalf("Expected error, got none") } } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "floatingip": { "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57", "port_id": "ce705c24-c1ef-408a-bda3-7bbd946164ab" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "floatingip": { "router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f", "tenant_id": "4969c491a3c74ee4af974e6d800c62de", "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57", "fixed_ip_address": "10.0.0.3", "floating_ip_address": "", "port_id": "ce705c24-c1ef-408a-bda3-7bbd946164ab", "id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7" } } `) }) options := floatingips.CreateOpts{ FloatingNetworkID: "376da547-b977-4cfe-9cba-275c80debf57", PortID: "ce705c24-c1ef-408a-bda3-7bbd946164ab", } ip, err := floatingips.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID) th.AssertEquals(t, "4969c491a3c74ee4af974e6d800c62de", ip.TenantID) th.AssertEquals(t, "376da547-b977-4cfe-9cba-275c80debf57", ip.FloatingNetworkID) th.AssertEquals(t, "", ip.FloatingIP) th.AssertEquals(t, "ce705c24-c1ef-408a-bda3-7bbd946164ab", ip.PortID) th.AssertEquals(t, "10.0.0.3", ip.FixedIP) } func TestCreateEmptyPort(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "floatingip": { "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "floatingip": { "router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f", "tenant_id": "4969c491a3c74ee4af974e6d800c62de", "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57", "fixed_ip_address": "10.0.0.3", "floating_ip_address": "", "id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7" } } `) }) options := floatingips.CreateOpts{ FloatingNetworkID: "376da547-b977-4cfe-9cba-275c80debf57", } ip, err := floatingips.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID) th.AssertEquals(t, "4969c491a3c74ee4af974e6d800c62de", ip.TenantID) th.AssertEquals(t, "376da547-b977-4cfe-9cba-275c80debf57", ip.FloatingNetworkID) th.AssertEquals(t, "", ip.FloatingIP) th.AssertEquals(t, "", ip.PortID) th.AssertEquals(t, "10.0.0.3", ip.FixedIP) } func TestCreateWithSubnetID(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "floatingip": { "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57", "subnet_id": "37adf01c-24db-467a-b845-7ab1e8216c01" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "floatingip": { "router_id": null, "tenant_id": "4969c491a3c74ee4af974e6d800c62de", "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57", "fixed_ip_address": null, "floating_ip_address": "172.24.4.3", "port_id": null, "id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7" } } `) }) options := floatingips.CreateOpts{ FloatingNetworkID: "376da547-b977-4cfe-9cba-275c80debf57", SubnetID: "37adf01c-24db-467a-b845-7ab1e8216c01", } ip, err := floatingips.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID) th.AssertEquals(t, "4969c491a3c74ee4af974e6d800c62de", ip.TenantID) th.AssertEquals(t, "376da547-b977-4cfe-9cba-275c80debf57", ip.FloatingNetworkID) th.AssertEquals(t, "172.24.4.3", ip.FloatingIP) th.AssertEquals(t, "", ip.PortID) th.AssertEquals(t, "", ip.FixedIP) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "floatingip": { "floating_network_id": "90f742b1-6d17-487b-ba95-71881dbc0b64", "fixed_ip_address": "192.0.0.2", "floating_ip_address": "10.0.0.3", "tenant_id": "017d8de156df4177889f31a9bd6edc00", "status": "DOWN", "port_id": "74a342ce-8e07-4e91-880c-9f834b68fa25", "id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7", "router_id": "1117c30a-ddb4-49a1-bec3-a65b286b4170" } } `) }) ip, err := floatingips.Get(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "90f742b1-6d17-487b-ba95-71881dbc0b64", ip.FloatingNetworkID) th.AssertEquals(t, "10.0.0.3", ip.FloatingIP) th.AssertEquals(t, "74a342ce-8e07-4e91-880c-9f834b68fa25", ip.PortID) th.AssertEquals(t, "192.0.0.2", ip.FixedIP) th.AssertEquals(t, "017d8de156df4177889f31a9bd6edc00", ip.TenantID) th.AssertEquals(t, "DOWN", ip.Status) th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID) th.AssertEquals(t, "1117c30a-ddb4-49a1-bec3-a65b286b4170", ip.RouterID) } func TestAssociate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "floatingip": { "port_id": "423abc8d-2991-4a55-ba98-2aaea84cc72e" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "floatingip": { "router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f", "tenant_id": "4969c491a3c74ee4af974e6d800c62de", "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57", "fixed_ip_address": null, "floating_ip_address": "172.24.4.228", "port_id": "423abc8d-2991-4a55-ba98-2aaea84cc72e", "id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7" } } `) }) portID := "423abc8d-2991-4a55-ba98-2aaea84cc72e" ip, err := floatingips.Update(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", floatingips.UpdateOpts{PortID: &portID}).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, portID, ip.PortID) } func TestDisassociate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "floatingip": { "port_id": null } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "floatingip": { "router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f", "tenant_id": "4969c491a3c74ee4af974e6d800c62de", "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57", "fixed_ip_address": null, "floating_ip_address": "172.24.4.228", "port_id": null, "id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7" } } `) }) ip, err := floatingips.Update(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", floatingips.UpdateOpts{PortID: nil}).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, "", ip.FixedIP) th.AssertDeepEquals(t, "", ip.PortID) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := floatingips.Delete(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000004541335005366400374360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/layer3/floatingipspackage floatingips import "github.com/gophercloud/gophercloud" const resourcePath = "floatingips" func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id) } routers/000077500000000000000000000000001335005366400353035ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/layer3doc.go000066400000000000000000000043061335005366400364020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/layer3/routers/* Package routers enables management and retrieval of Routers from the OpenStack Networking service. Example to List Routers listOpts := routers.ListOpts{} allPages, err := routers.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allRouters, err := routers.ExtractRouters(allPages) if err != nil { panic(err) } for _, router := range allRoutes { fmt.Printf("%+v\n", router) } Example to Create a Router iTrue := true gwi := routers.GatewayInfo{ NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b", } createOpts := routers.CreateOpts{ Name: "router_1", AdminStateUp: &iTrue, GatewayInfo: &gwi, } router, err := routers.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Router routerID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" routes := []routers.Route{{ DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10", }} updateOpts := routers.UpdateOpts{ Name: "new_name", Routes: routes, } router, err := routers.Update(networkClient, routerID, updateOpts).Extract() if err != nil { panic(err) } Example to Remove all Routes from a Router routerID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" routes := []routers.Route{} updateOpts := routers.UpdateOpts{ Routes: routes, } router, err := routers.Update(networkClient, routerID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Router routerID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" err := routers.Delete(networkClient, routerID).ExtractErr() if err != nil { panic(err) } Example to Add an Interface to a Router routerID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" intOpts := routers.AddInterfaceOpts{ SubnetID: "a2f1f29d-571b-4533-907f-5803ab96ead1", } interface, err := routers.AddInterface(networkClient, routerID, intOpts).Extract() if err != nil { panic(err) } Example to Remove an Interface from a Router routerID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" intOpts := routers.RemoveInterfaceOpts{ SubnetID: "a2f1f29d-571b-4533-907f-5803ab96ead1", } interface, err := routers.RemoveInterface(networkClient, routerID, intOpts).Extract() if err != nil { panic(err) } */ package routers requests.go000066400000000000000000000215061335005366400375110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/layer3/routerspackage routers import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the floating IP attributes you want to see returned. SortKey allows you to // sort by a particular network attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { ID string `q:"id"` Name string `q:"name"` AdminStateUp *bool `q:"admin_state_up"` Distributed *bool `q:"distributed"` Status string `q:"status"` TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // List returns a Pager which allows you to iterate over a collection of // routers. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. // // Default policy settings return only those routers that are owned by the // tenant who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { q, err := gophercloud.BuildQueryString(&opts) if err != nil { return pagination.Pager{Err: err} } u := rootURL(c) + q.String() return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page { return RouterPage{pagination.LinkedPageBase{PageResult: r}} }) } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToRouterCreateMap() (map[string]interface{}, error) } // CreateOpts contains all the values needed to create a new router. There are // no required values. type CreateOpts struct { Name string `json:"name,omitempty"` AdminStateUp *bool `json:"admin_state_up,omitempty"` Distributed *bool `json:"distributed,omitempty"` TenantID string `json:"tenant_id,omitempty"` ProjectID string `json:"project_id,omitempty"` GatewayInfo *GatewayInfo `json:"external_gateway_info,omitempty"` AvailabilityZoneHints []string `json:"availability_zone_hints,omitempty"` } // ToRouterCreateMap builds a create request body from CreateOpts. func (opts CreateOpts) ToRouterCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "router") } // Create accepts a CreateOpts struct and uses the values to create a new // logical router. When it is created, the router does not have an internal // interface - it is not associated to any subnet. // // You can optionally specify an external gateway for a router using the // GatewayInfo struct. The external gateway for the router must be plugged into // an external network (it is external if its `router:external' field is set to // true). func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToRouterCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular router based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToRouterUpdateMap() (map[string]interface{}, error) } // UpdateOpts contains the values used when updating a router. type UpdateOpts struct { Name string `json:"name,omitempty"` AdminStateUp *bool `json:"admin_state_up,omitempty"` Distributed *bool `json:"distributed,omitempty"` GatewayInfo *GatewayInfo `json:"external_gateway_info,omitempty"` Routes []Route `json:"routes"` } // ToRouterUpdateMap builds an update body based on UpdateOpts. func (opts UpdateOpts) ToRouterUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "router") } // Update allows routers to be updated. You can update the name, administrative // state, and the external gateway. For more information about how to set the // external gateway for a router, see Create. This operation does not enable // the update of router interfaces. To do this, use the AddInterface and // RemoveInterface functions. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToRouterUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete will permanently delete a particular router based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } // AddInterfaceOptsBuilder allows extensions to add additional parameters to // the AddInterface request. type AddInterfaceOptsBuilder interface { ToRouterAddInterfaceMap() (map[string]interface{}, error) } // AddInterfaceOpts represents the options for adding an interface to a router. type AddInterfaceOpts struct { SubnetID string `json:"subnet_id,omitempty" xor:"PortID"` PortID string `json:"port_id,omitempty" xor:"SubnetID"` } // ToRouterAddInterfaceMap builds a request body from AddInterfaceOpts. func (opts AddInterfaceOpts) ToRouterAddInterfaceMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // AddInterface attaches a subnet to an internal router interface. You must // specify either a SubnetID or PortID in the request body. If you specify both, // the operation will fail and an error will be returned. // // If you specify a SubnetID, the gateway IP address for that particular subnet // is used to create the router interface. Alternatively, if you specify a // PortID, the IP address associated with the port is used to create the router // interface. // // If you reference a port that is associated with multiple IP addresses, or // if the port is associated with zero IP addresses, the operation will fail and // a 400 Bad Request error will be returned. // // If you reference a port already in use, the operation will fail and a 409 // Conflict error will be returned. // // The PortID that is returned after using Extract() on the result of this // operation can either be the same PortID passed in or, on the other hand, the // identifier of a new port created by this operation. After the operation // completes, the device ID of the port is set to the router ID, and the // device owner attribute is set to `network:router_interface'. func AddInterface(c *gophercloud.ServiceClient, id string, opts AddInterfaceOptsBuilder) (r InterfaceResult) { b, err := opts.ToRouterAddInterfaceMap() if err != nil { r.Err = err return } _, r.Err = c.Put(addInterfaceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // RemoveInterfaceOptsBuilder allows extensions to add additional parameters to // the RemoveInterface request. type RemoveInterfaceOptsBuilder interface { ToRouterRemoveInterfaceMap() (map[string]interface{}, error) } // RemoveInterfaceOpts represents options for removing an interface from // a router. type RemoveInterfaceOpts struct { SubnetID string `json:"subnet_id,omitempty" or:"PortID"` PortID string `json:"port_id,omitempty" or:"SubnetID"` } // ToRouterRemoveInterfaceMap builds a request body based on // RemoveInterfaceOpts. func (opts RemoveInterfaceOpts) ToRouterRemoveInterfaceMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // RemoveInterface removes an internal router interface, which detaches a // subnet from the router. You must specify either a SubnetID or PortID, since // these values are used to identify the router interface to remove. // // Unlike AddInterface, you can also specify both a SubnetID and PortID. If you // choose to specify both, the subnet ID must correspond to the subnet ID of // the first IP address on the port specified by the port ID. Otherwise, the // operation will fail and return a 409 Conflict error. // // If the router, subnet or port which are referenced do not exist or are not // visible to you, the operation will fail and a 404 Not Found error will be // returned. After this operation completes, the port connecting the router // with the subnet is removed from the subnet for the network. func RemoveInterface(c *gophercloud.ServiceClient, id string, opts RemoveInterfaceOptsBuilder) (r InterfaceResult) { b, err := opts.ToRouterRemoveInterfaceMap() if err != nil { r.Err = err return } _, r.Err = c.Put(removeInterfaceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000127331335005366400373410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/layer3/routerspackage routers import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // GatewayInfo represents the information of an external gateway for any // particular network router. type GatewayInfo struct { NetworkID string `json:"network_id"` EnableSNAT *bool `json:"enable_snat,omitempty"` ExternalFixedIPs []ExternalFixedIP `json:"external_fixed_ips,omitempty"` } // ExternalFixedIP is the IP address and subnet ID of the external gateway of a // router. type ExternalFixedIP struct { IPAddress string `json:"ip_address"` SubnetID string `json:"subnet_id"` } // Route is a possible route in a router. type Route struct { NextHop string `json:"nexthop"` DestinationCIDR string `json:"destination"` } // Router represents a Neutron router. A router is a logical entity that // forwards packets across internal subnets and NATs (network address // translation) them on external networks through an appropriate gateway. // // A router has an interface for each subnet with which it is associated. By // default, the IP address of such interface is the subnet's gateway IP. Also, // whenever a router is associated with a subnet, a port for that router // interface is added to the subnet's network. type Router struct { // Status indicates whether or not a router is currently operational. Status string `json:"status"` // GateayInfo provides information on external gateway for the router. GatewayInfo GatewayInfo `json:"external_gateway_info"` // AdminStateUp is the administrative state of the router. AdminStateUp bool `json:"admin_state_up"` // Distributed is whether router is disitrubted or not. Distributed bool `json:"distributed"` // Name is the human readable name for the router. It does not have to be // unique. Name string `json:"name"` // ID is the unique identifier for the router. ID string `json:"id"` // TenantID is the project owner of the router. Only admin users can // specify a project identifier other than its own. TenantID string `json:"tenant_id"` // ProjectID is the project owner of the router. ProjectID string `json:"project_id"` // Routes are a collection of static routes that the router will host. Routes []Route `json:"routes"` // Availability zone hints groups network nodes that run services like DHCP, L3, FW, and others. // Used to make network resources highly available. AvailabilityZoneHints []string `json:"availability_zone_hints"` } // RouterPage is the page returned by a pager when traversing over a // collection of routers. type RouterPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of routers has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r RouterPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"routers_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a RouterPage struct is empty. func (r RouterPage) IsEmpty() (bool, error) { is, err := ExtractRouters(r) return len(is) == 0, err } // ExtractRouters accepts a Page struct, specifically a RouterPage struct, // and extracts the elements into a slice of Router structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractRouters(r pagination.Page) ([]Router, error) { var s struct { Routers []Router `json:"routers"` } err := (r.(RouterPage)).ExtractInto(&s) return s.Routers, err } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a router. func (r commonResult) Extract() (*Router, error) { var s struct { Router *Router `json:"router"` } err := r.ExtractInto(&s) return s.Router, err } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a Router. type CreateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a Router. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a Router. type UpdateResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its ExtractErr // method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // InterfaceInfo represents information about a particular router interface. As // mentioned above, in order for a router to forward to a subnet, it needs an // interface. type InterfaceInfo struct { // SubnetID is the ID of the subnet which this interface is associated with. SubnetID string `json:"subnet_id"` // PortID is the ID of the port that is a part of the subnet. PortID string `json:"port_id"` // ID is the UUID of the interface. ID string `json:"id"` // TenantID is the owner of the interface. TenantID string `json:"tenant_id"` } // InterfaceResult represents the result of interface operations, such as // AddInterface() and RemoveInterface(). Call its Extract method to interpret // the result as a InterfaceInfo. type InterfaceResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts an information struct. func (r InterfaceResult) Extract() (*InterfaceInfo, error) { var s InterfaceInfo err := r.ExtractInto(&s) return &s, err } testing/000077500000000000000000000000001335005366400367605ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/layer3/routersdoc.go000066400000000000000000000000461335005366400400540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/layer3/routers/testing// routers unit tests package testing requests_test.go000066400000000000000000000345071335005366400422320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/layer3/routers/testingpackage testing import ( "fmt" "net/http" "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/routers", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "routers": [ { "status": "ACTIVE", "external_gateway_info": null, "name": "second_routers", "admin_state_up": true, "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3", "distributed": false, "id": "7177abc4-5ae9-4bb7-b0d4-89e94a4abf3b" }, { "status": "ACTIVE", "external_gateway_info": { "network_id": "3c5bcddd-6af9-4e6b-9c3e-c153e521cab8" }, "name": "router1", "admin_state_up": true, "tenant_id": "33a40233088643acb66ff6eb0ebea679", "distributed": false, "id": "a9254bdb-2613-4a13-ac4c-adc581fba50d" }, { "status": "ACTIVE", "external_gateway_info": { "network_id": "2b37576e-b050-4891-8b20-e1e37a93942a", "external_fixed_ips": [ {"ip_address": "192.0.2.17", "subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"}, {"ip_address": "198.51.100.33", "subnet_id": "1d699529-bdfd-43f8-bcaa-bff00c547af2"} ] }, "name": "gateway", "admin_state_up": true, "tenant_id": "a3e881e0a6534880c5473d95b9442099", "distributed": false, "id": "308a035c-005d-4452-a9fe-6f8f2f0c28d8" } ] } `) }) count := 0 routers.List(fake.ServiceClient(), routers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := routers.ExtractRouters(page) if err != nil { t.Errorf("Failed to extract routers: %v", err) return false, err } expected := []routers.Router{ { Status: "ACTIVE", GatewayInfo: routers.GatewayInfo{NetworkID: ""}, AdminStateUp: true, Distributed: false, Name: "second_routers", ID: "7177abc4-5ae9-4bb7-b0d4-89e94a4abf3b", TenantID: "6b96ff0cb17a4b859e1e575d221683d3", }, { Status: "ACTIVE", GatewayInfo: routers.GatewayInfo{NetworkID: "3c5bcddd-6af9-4e6b-9c3e-c153e521cab8"}, AdminStateUp: true, Distributed: false, Name: "router1", ID: "a9254bdb-2613-4a13-ac4c-adc581fba50d", TenantID: "33a40233088643acb66ff6eb0ebea679", }, { Status: "ACTIVE", GatewayInfo: routers.GatewayInfo{ NetworkID: "2b37576e-b050-4891-8b20-e1e37a93942a", ExternalFixedIPs: []routers.ExternalFixedIP{ {IPAddress: "192.0.2.17", SubnetID: "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"}, {IPAddress: "198.51.100.33", SubnetID: "1d699529-bdfd-43f8-bcaa-bff00c547af2"}, }, }, AdminStateUp: true, Distributed: false, Name: "gateway", ID: "308a035c-005d-4452-a9fe-6f8f2f0c28d8", TenantID: "a3e881e0a6534880c5473d95b9442099", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/routers", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "router":{ "name": "foo_router", "admin_state_up": false, "external_gateway_info":{ "enable_snat": false, "network_id":"8ca37218-28ff-41cb-9b10-039601ea7e6b" }, "availability_zone_hints": ["zone1", "zone2"] } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "router": { "status": "ACTIVE", "external_gateway_info": { "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b", "enable_snat": false, "external_fixed_ips": [ {"ip_address": "192.0.2.17", "subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"} ] }, "name": "foo_router", "admin_state_up": false, "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3", "distributed": false, "availability_zone_hints": ["zone1", "zone2"], "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e" } } `) }) asu := false enableSNAT := false gwi := routers.GatewayInfo{ NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b", EnableSNAT: &enableSNAT, } options := routers.CreateOpts{ Name: "foo_router", AdminStateUp: &asu, GatewayInfo: &gwi, AvailabilityZoneHints: []string{"zone1", "zone2"}, } r, err := routers.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) gwi.ExternalFixedIPs = []routers.ExternalFixedIP{{ IPAddress: "192.0.2.17", SubnetID: "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def", }} th.AssertEquals(t, "foo_router", r.Name) th.AssertEquals(t, false, r.AdminStateUp) th.AssertDeepEquals(t, gwi, r.GatewayInfo) th.AssertDeepEquals(t, []string{"zone1", "zone2"}, r.AvailabilityZoneHints) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/routers/a07eea83-7710-4860-931b-5fe220fae533", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "router": { "status": "ACTIVE", "external_gateway_info": { "network_id": "85d76829-6415-48ff-9c63-5c5ca8c61ac6", "external_fixed_ips": [ {"ip_address": "198.51.100.33", "subnet_id": "1d699529-bdfd-43f8-bcaa-bff00c547af2"} ] }, "routes": [ { "nexthop": "10.1.0.10", "destination": "40.0.1.0/24" } ], "name": "router1", "admin_state_up": true, "tenant_id": "d6554fe62e2f41efbb6e026fad5c1542", "distributed": false, "availability_zone_hints": ["zone1", "zone2"], "id": "a07eea83-7710-4860-931b-5fe220fae533" } } `) }) n, err := routers.Get(fake.ServiceClient(), "a07eea83-7710-4860-931b-5fe220fae533").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "ACTIVE") th.AssertDeepEquals(t, n.GatewayInfo, routers.GatewayInfo{ NetworkID: "85d76829-6415-48ff-9c63-5c5ca8c61ac6", ExternalFixedIPs: []routers.ExternalFixedIP{ {IPAddress: "198.51.100.33", SubnetID: "1d699529-bdfd-43f8-bcaa-bff00c547af2"}, }, }) th.AssertEquals(t, n.Name, "router1") th.AssertEquals(t, n.AdminStateUp, true) th.AssertEquals(t, n.TenantID, "d6554fe62e2f41efbb6e026fad5c1542") th.AssertEquals(t, n.ID, "a07eea83-7710-4860-931b-5fe220fae533") th.AssertDeepEquals(t, n.Routes, []routers.Route{{DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10"}}) th.AssertDeepEquals(t, n.AvailabilityZoneHints, []string{"zone1", "zone2"}) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "router": { "name": "new_name", "external_gateway_info": { "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b" }, "routes": [ { "nexthop": "10.1.0.10", "destination": "40.0.1.0/24" } ] } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "router": { "status": "ACTIVE", "external_gateway_info": { "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b", "external_fixed_ips": [ {"ip_address": "192.0.2.17", "subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"} ] }, "name": "new_name", "admin_state_up": true, "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3", "distributed": false, "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e", "routes": [ { "nexthop": "10.1.0.10", "destination": "40.0.1.0/24" } ] } } `) }) gwi := routers.GatewayInfo{NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b"} r := []routers.Route{{DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10"}} options := routers.UpdateOpts{Name: "new_name", GatewayInfo: &gwi, Routes: r} n, err := routers.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() th.AssertNoErr(t, err) gwi.ExternalFixedIPs = []routers.ExternalFixedIP{ {IPAddress: "192.0.2.17", SubnetID: "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"}, } th.AssertEquals(t, n.Name, "new_name") th.AssertDeepEquals(t, n.GatewayInfo, gwi) th.AssertDeepEquals(t, n.Routes, []routers.Route{{DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10"}}) } func TestAllRoutesRemoved(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "router": { "routes": [] } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "router": { "status": "ACTIVE", "external_gateway_info": { "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b" }, "name": "name", "admin_state_up": true, "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3", "distributed": false, "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e", "routes": [] } } `) }) r := []routers.Route{} options := routers.UpdateOpts{Routes: r} n, err := routers.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, n.Routes, []routers.Route{}) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := routers.Delete(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c") th.AssertNoErr(t, res.Err) } func TestAddInterface(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c/add_router_interface", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "subnet_id": "a2f1f29d-571b-4533-907f-5803ab96ead1" } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "subnet_id": "0d32a837-8069-4ec3-84c4-3eef3e10b188", "tenant_id": "017d8de156df4177889f31a9bd6edc00", "port_id": "3f990102-4485-4df1-97a0-2c35bdb85b31", "id": "9a83fa11-8da5-436e-9afe-3d3ac5ce7770" } `) }) opts := routers.AddInterfaceOpts{SubnetID: "a2f1f29d-571b-4533-907f-5803ab96ead1"} res, err := routers.AddInterface(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "0d32a837-8069-4ec3-84c4-3eef3e10b188", res.SubnetID) th.AssertEquals(t, "017d8de156df4177889f31a9bd6edc00", res.TenantID) th.AssertEquals(t, "3f990102-4485-4df1-97a0-2c35bdb85b31", res.PortID) th.AssertEquals(t, "9a83fa11-8da5-436e-9afe-3d3ac5ce7770", res.ID) } func TestAddInterfaceRequiredOpts(t *testing.T) { _, err := routers.AddInterface(fake.ServiceClient(), "foo", routers.AddInterfaceOpts{}).Extract() if err == nil { t.Fatalf("Expected error, got none") } _, err = routers.AddInterface(fake.ServiceClient(), "foo", routers.AddInterfaceOpts{SubnetID: "bar", PortID: "baz"}).Extract() if err == nil { t.Fatalf("Expected error, got none") } } func TestRemoveInterface(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c/remove_router_interface", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "subnet_id": "a2f1f29d-571b-4533-907f-5803ab96ead1" } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "subnet_id": "0d32a837-8069-4ec3-84c4-3eef3e10b188", "tenant_id": "017d8de156df4177889f31a9bd6edc00", "port_id": "3f990102-4485-4df1-97a0-2c35bdb85b31", "id": "9a83fa11-8da5-436e-9afe-3d3ac5ce7770" } `) }) opts := routers.RemoveInterfaceOpts{SubnetID: "a2f1f29d-571b-4533-907f-5803ab96ead1"} res, err := routers.RemoveInterface(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "0d32a837-8069-4ec3-84c4-3eef3e10b188", res.SubnetID) th.AssertEquals(t, "017d8de156df4177889f31a9bd6edc00", res.TenantID) th.AssertEquals(t, "3f990102-4485-4df1-97a0-2c35bdb85b31", res.PortID) th.AssertEquals(t, "9a83fa11-8da5-436e-9afe-3d3ac5ce7770", res.ID) } urls.go000066400000000000000000000010741335005366400366210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/layer3/routerspackage routers import "github.com/gophercloud/gophercloud" const resourcePath = "routers" func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id) } func addInterfaceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id, "add_router_interface") } func removeInterfaceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id, "remove_router_interface") } lbaas/000077500000000000000000000000001335005366400334635ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensionsdoc.go000066400000000000000000000002331335005366400345550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas// Package lbaas provides information and interaction with the Load Balancer // as a Service extension for the OpenStack Networking service. package lbaas members/000077500000000000000000000000001335005366400351155ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaasdoc.go000066400000000000000000000023161335005366400362130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/members/* Package members provides information and interaction with Members of the Load Balancer as a Service extension for the OpenStack Networking service. Example to List Members listOpts := members.ListOpts{ ProtocolPort: 80, } allPages, err := members.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allMembers, err := members.ExtractMembers(allPages) if err != nil { panic(err) } for _, member := range allMembers { fmt.Printf("%+v\n", member) } Example to Create a Member createOpts := members.CreateOpts{ Address: "192.168.2.14", ProtocolPort: 80, PoolID: "0b266a12-0fdf-4434-bd11-649d84e54bd5" } member, err := members.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Member memberID := "46592c54-03f7-40ef-9cdf-b1fcf2775ddf" updateOpts := members.UpdateOpts{ AdminStateUp: gophercloud.Disabled, } member, err := members.Update(networkClient, memberID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Member memberID := "46592c54-03f7-40ef-9cdf-b1fcf2775ddf" err := members.Delete(networkClient, memberID).ExtractErr() if err != nil { panic(err) } */ package members requests.go000066400000000000000000000103411335005366400373160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/memberspackage members import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the floating IP attributes you want to see returned. SortKey allows you to // sort by a particular network attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { Status string `q:"status"` Weight int `q:"weight"` AdminStateUp *bool `q:"admin_state_up"` TenantID string `q:"tenant_id"` PoolID string `q:"pool_id"` Address string `q:"address"` ProtocolPort int `q:"protocol_port"` ID string `q:"id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // List returns a Pager which allows you to iterate over a collection of // members. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. // // Default policy settings return only those members that are owned by the // tenant who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { q, err := gophercloud.BuildQueryString(&opts) if err != nil { return pagination.Pager{Err: err} } u := rootURL(c) + q.String() return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page { return MemberPage{pagination.LinkedPageBase{PageResult: r}} }) } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToLBMemberCreateMap() (map[string]interface{}, error) } // CreateOpts contains all the values needed to create a new pool member. type CreateOpts struct { // Address is the IP address of the member. Address string `json:"address" required:"true"` // ProtocolPort is the port on which the application is hosted. ProtocolPort int `json:"protocol_port" required:"true"` // PoolID is the pool to which this member will belong. PoolID string `json:"pool_id" required:"true"` // TenantID is only required if the caller has an admin role and wants // to create a pool for another tenant. TenantID string `json:"tenant_id,omitempty"` } // ToLBMemberCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToLBMemberCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "member") } // Create accepts a CreateOpts struct and uses the values to create a new // load balancer pool member. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToLBMemberCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular pool member based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToLBMemberUpdateMap() (map[string]interface{}, error) } // UpdateOpts contains the values used when updating a pool member. type UpdateOpts struct { // The administrative state of the member, which is up (true) or down (false). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToLBMemberUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToLBMemberUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "member") } // Update allows members to be updated. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToLBMemberUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) return } // Delete will permanently delete a particular member based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } results.go000066400000000000000000000056411335005366400371530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/memberspackage members import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Member represents the application running on a backend server. type Member struct { // Status is the status of the member. Indicates whether the member // is operational. Status string // Weight is the weight of member. Weight int // AdminStateUp is the administrative state of the member, which is up // (true) or down (false). AdminStateUp bool `json:"admin_state_up"` // TenantID is the owner of the member. TenantID string `json:"tenant_id"` // PoolID is the pool to which the member belongs. PoolID string `json:"pool_id"` // Address is the IP address of the member. Address string // ProtocolPort is the port on which the application is hosted. ProtocolPort int `json:"protocol_port"` // ID is the unique ID for the member. ID string } // MemberPage is the page returned by a pager when traversing over a // collection of pool members. type MemberPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of members has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r MemberPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"members_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a MemberPage struct is empty. func (r MemberPage) IsEmpty() (bool, error) { is, err := ExtractMembers(r) return len(is) == 0, err } // ExtractMembers accepts a Page struct, specifically a MemberPage struct, // and extracts the elements into a slice of Member structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractMembers(r pagination.Page) ([]Member, error) { var s struct { Members []Member `json:"members"` } err := (r.(MemberPage)).ExtractInto(&s) return s.Members, err } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a member. func (r commonResult) Extract() (*Member, error) { var s struct { Member *Member `json:"member"` } err := r.ExtractInto(&s) return s.Member, err } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a Member. type CreateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a Member. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a Member. type UpdateResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its // ExtractErr method to determine if the result succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400365725ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/membersdoc.go000066400000000000000000000000461335005366400376660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/members/testing// members unit tests package testing requests_test.go000066400000000000000000000142701335005366400420370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/members/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/members" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/members", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "members":[ { "status":"ACTIVE", "weight":1, "admin_state_up":true, "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", "pool_id":"72741b06-df4d-4715-b142-276b6bce75ab", "address":"10.0.0.4", "protocol_port":80, "id":"701b531b-111a-4f21-ad85-4795b7b12af6" }, { "status":"ACTIVE", "weight":1, "admin_state_up":true, "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", "pool_id":"72741b06-df4d-4715-b142-276b6bce75ab", "address":"10.0.0.3", "protocol_port":80, "id":"beb53b4d-230b-4abd-8118-575b8fa006ef" } ] } `) }) count := 0 members.List(fake.ServiceClient(), members.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := members.ExtractMembers(page) if err != nil { t.Errorf("Failed to extract members: %v", err) return false, err } expected := []members.Member{ { Status: "ACTIVE", Weight: 1, AdminStateUp: true, TenantID: "83657cfcdfe44cd5920adaf26c48ceea", PoolID: "72741b06-df4d-4715-b142-276b6bce75ab", Address: "10.0.0.4", ProtocolPort: 80, ID: "701b531b-111a-4f21-ad85-4795b7b12af6", }, { Status: "ACTIVE", Weight: 1, AdminStateUp: true, TenantID: "83657cfcdfe44cd5920adaf26c48ceea", PoolID: "72741b06-df4d-4715-b142-276b6bce75ab", Address: "10.0.0.3", ProtocolPort: 80, ID: "beb53b4d-230b-4abd-8118-575b8fa006ef", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/members", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "member": { "tenant_id": "453105b9-1754-413f-aab1-55f1af620750", "pool_id": "foo", "address": "192.0.2.14", "protocol_port":8080 } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "member": { "id": "975592ca-e308-48ad-8298-731935ee9f45", "address": "192.0.2.14", "protocol_port": 8080, "tenant_id": "453105b9-1754-413f-aab1-55f1af620750", "admin_state_up":true, "weight": 1, "status": "DOWN" } } `) }) options := members.CreateOpts{ TenantID: "453105b9-1754-413f-aab1-55f1af620750", Address: "192.0.2.14", ProtocolPort: 8080, PoolID: "foo", } _, err := members.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/members/975592ca-e308-48ad-8298-731935ee9f45", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "member":{ "id":"975592ca-e308-48ad-8298-731935ee9f45", "address":"192.0.2.14", "protocol_port":8080, "tenant_id":"453105b9-1754-413f-aab1-55f1af620750", "admin_state_up":true, "weight":1, "status":"DOWN" } } `) }) m, err := members.Get(fake.ServiceClient(), "975592ca-e308-48ad-8298-731935ee9f45").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "975592ca-e308-48ad-8298-731935ee9f45", m.ID) th.AssertEquals(t, "192.0.2.14", m.Address) th.AssertEquals(t, 8080, m.ProtocolPort) th.AssertEquals(t, "453105b9-1754-413f-aab1-55f1af620750", m.TenantID) th.AssertEquals(t, true, m.AdminStateUp) th.AssertEquals(t, 1, m.Weight) th.AssertEquals(t, "DOWN", m.Status) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/members/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "member":{ "admin_state_up":false } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "member":{ "status":"PENDING_UPDATE", "protocol_port":8080, "weight":1, "admin_state_up":false, "tenant_id":"4fd44f30292945e481c7b8a0c8908869", "pool_id":"7803631d-f181-4500-b3a2-1b68ba2a75fd", "address":"10.0.0.5", "status_description":null, "id":"48a471ea-64f1-4eb6-9be7-dae6bbe40a0f" } } `) }) options := members.UpdateOpts{AdminStateUp: gophercloud.Disabled} _, err := members.Update(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", options).Extract() th.AssertNoErr(t, err) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/members/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := members.Delete(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000005221335005366400364300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/memberspackage members import "github.com/gophercloud/gophercloud" const ( rootPath = "lb" resourcePath = "members" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } monitors/000077500000000000000000000000001335005366400353355ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaasdoc.go000066400000000000000000000023611335005366400364330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/monitors/* Package monitors provides information and interaction with the Monitors of the Load Balancer as a Service extension for the OpenStack Networking Service. Example to List Monitors listOpts: monitors.ListOpts{ Type: "HTTP", } allPages, err := monitors.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allMonitors, err := monitors.ExtractMonitors(allPages) if err != nil { panic(err) } for _, monitor := range allMonitors { fmt.Printf("%+v\n", monitor) } Example to Create a Monitor createOpts := monitors.CreateOpts{ Type: "HTTP", Delay: 20, Timeout: 20, MaxRetries: 5, URLPath: "/check", ExpectedCodes: "200-299", } monitor, err := monitors.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Monitor monitorID := "681aed03-aadb-43ae-aead-b9016375650a" updateOpts := monitors.UpdateOpts{ Timeout: 30, } monitor, err := monitors.Update(networkClient, monitorID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Member monitorID := "681aed03-aadb-43ae-aead-b9016375650a" err := monitors.Delete(networkClient, monitorID).ExtractErr() if err != nil { panic(err) } */ package monitors requests.go000066400000000000000000000204371335005366400375450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/monitorspackage monitors import ( "fmt" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the floating IP attributes you want to see returned. SortKey allows you to // sort by a particular network attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { ID string `q:"id"` TenantID string `q:"tenant_id"` Type string `q:"type"` Delay int `q:"delay"` Timeout int `q:"timeout"` MaxRetries int `q:"max_retries"` HTTPMethod string `q:"http_method"` URLPath string `q:"url_path"` ExpectedCodes string `q:"expected_codes"` AdminStateUp *bool `q:"admin_state_up"` Status string `q:"status"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // List returns a Pager which allows you to iterate over a collection of // monitors. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. // // Default policy settings return only those monitors that are owned by the // tenant who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { q, err := gophercloud.BuildQueryString(&opts) if err != nil { return pagination.Pager{Err: err} } u := rootURL(c) + q.String() return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page { return MonitorPage{pagination.LinkedPageBase{PageResult: r}} }) } // MonitorType is the type for all the types of LB monitors. type MonitorType string // Constants that represent approved monitoring types. const ( TypePING MonitorType = "PING" TypeTCP MonitorType = "TCP" TypeHTTP MonitorType = "HTTP" TypeHTTPS MonitorType = "HTTPS" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToLBMonitorCreateMap() (map[string]interface{}, error) } // CreateOpts contains all the values needed to create a new health monitor. type CreateOpts struct { // MonitorType is the type of probe, which is PING, TCP, HTTP, or HTTPS, // that is sent by the load balancer to verify the member state. Type MonitorType `json:"type" required:"true"` // Delay is the time, in seconds, between sending probes to members. Delay int `json:"delay" required:"true"` // Timeout is the maximum number of seconds for a monitor to wait for a ping // reply before it times out. The value must be less than the delay value. Timeout int `json:"timeout" required:"true"` // MaxRetries is the number of permissible ping failures before changing the // member's status to INACTIVE. Must be a number between 1 and 10. MaxRetries int `json:"max_retries" required:"true"` // URLPath is the URI path that will be accessed if monitor type // is HTTP or HTTPS. Required for HTTP(S) types. URLPath string `json:"url_path,omitempty"` // HTTPMethod is the HTTP method used for requests by the monitor. If this // attribute is not specified, it defaults to "GET". Required for HTTP(S) // types. HTTPMethod string `json:"http_method,omitempty"` // ExpectedCodes is the expected HTTP codes for a passing HTTP(S) monitor // You can either specify a single status like "200", or a range like // "200-202". Required for HTTP(S) types. ExpectedCodes string `json:"expected_codes,omitempty"` // TenantID is only required if the caller has an admin role and wants // to create a pool for another tenant. TenantID string `json:"tenant_id,omitempty"` // AdminStateUp denotes whether the monitor is administratively up or down. AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToLBMonitorCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToLBMonitorCreateMap() (map[string]interface{}, error) { if opts.Type == TypeHTTP || opts.Type == TypeHTTPS { if opts.URLPath == "" { err := gophercloud.ErrMissingInput{} err.Argument = "monitors.CreateOpts.URLPath" return nil, err } if opts.ExpectedCodes == "" { err := gophercloud.ErrMissingInput{} err.Argument = "monitors.CreateOpts.ExpectedCodes" return nil, err } } if opts.Delay < opts.Timeout { err := gophercloud.ErrInvalidInput{} err.Argument = "monitors.CreateOpts.Delay/monitors.CreateOpts.Timeout" err.Info = "Delay must be greater than or equal to timeout" return nil, err } return gophercloud.BuildRequestBody(opts, "health_monitor") } // Create is an operation which provisions a new health monitor. There are // different types of monitor you can provision: PING, TCP or HTTP(S). Below // are examples of how to create each one. // // Here is an example config struct to use when creating a PING or TCP monitor: // // CreateOpts{Type: TypePING, Delay: 20, Timeout: 10, MaxRetries: 3} // CreateOpts{Type: TypeTCP, Delay: 20, Timeout: 10, MaxRetries: 3} // // Here is an example config struct to use when creating a HTTP(S) monitor: // // CreateOpts{Type: TypeHTTP, Delay: 20, Timeout: 10, MaxRetries: 3, // HttpMethod: "HEAD", ExpectedCodes: "200"} // func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToLBMonitorCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular health monitor based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToLBMonitorUpdateMap() (map[string]interface{}, error) } // UpdateOpts contains all the values needed to update an existing monitor. // Attributes not listed here but appear in CreateOpts are immutable and cannot // be updated. type UpdateOpts struct { // Delay is the time, in seconds, between sending probes to members. Delay int `json:"delay,omitempty"` // Timeout is the maximum number of seconds for a monitor to wait for a ping // reply before it times out. The value must be less than the delay value. Timeout int `json:"timeout,omitempty"` // MaxRetries is the number of permissible ping failures before changing the // member's status to INACTIVE. Must be a number between 1 and 10. MaxRetries int `json:"max_retries,omitempty"` // URLPath is the URI path that will be accessed if monitor type // is HTTP or HTTPS. URLPath string `json:"url_path,omitempty"` // HTTPMethod is the HTTP method used for requests by the monitor. If this // attribute is not specified, it defaults to "GET". HTTPMethod string `json:"http_method,omitempty"` // ExpectedCodes is the expected HTTP codes for a passing HTTP(S) monitor // You can either specify a single status like "200", or a range like // "200-202". ExpectedCodes string `json:"expected_codes,omitempty"` // AdminStateUp denotes whether the monitor is administratively up or down. AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToLBMonitorUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToLBMonitorUpdateMap() (map[string]interface{}, error) { if opts.Delay > 0 && opts.Timeout > 0 && opts.Delay < opts.Timeout { err := gophercloud.ErrInvalidInput{} err.Argument = "monitors.CreateOpts.Delay/monitors.CreateOpts.Timeout" err.Value = fmt.Sprintf("%d/%d", opts.Delay, opts.Timeout) err.Info = "Delay must be greater than or equal to timeout" return nil, err } return gophercloud.BuildRequestBody(opts, "health_monitor") } // Update is an operation which modifies the attributes of the specified // monitor. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToLBMonitorUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) return } // Delete will permanently delete a particular monitor based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } results.go000066400000000000000000000110761335005366400373720ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/monitorspackage monitors import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Monitor represents a load balancer health monitor. A health monitor is used // to determine whether or not back-end members of the VIP's pool are usable // for processing a request. A pool can have several health monitors associated // with it. There are different types of health monitors supported: // // PING: used to ping the members using ICMP. // TCP: used to connect to the members using TCP. // HTTP: used to send an HTTP request to the member. // HTTPS: used to send a secure HTTP request to the member. // // When a pool has several monitors associated with it, each member of the pool // is monitored by all these monitors. If any monitor declares the member as // unhealthy, then the member status is changed to INACTIVE and the member // won't participate in its pool's load balancing. In other words, ALL monitors // must declare the member to be healthy for it to stay ACTIVE. type Monitor struct { // ID is the unique ID for the Monitor. ID string // Name is the monitor name. Does not have to be unique. Name string // TenantID is the owner of the Monitor. TenantID string `json:"tenant_id"` // Type is the type of probe sent by the load balancer to verify the member // state, which is PING, TCP, HTTP, or HTTPS. Type string // Delay is the time, in seconds, between sending probes to members. Delay int // Timeout is the maximum number of seconds for a monitor to wait for a // connection to be established before it times out. This value must be less // than the delay value. Timeout int // MaxRetries is the number of allowed connection failures before changing the // status of the member to INACTIVE. A valid value is from 1 to 10. MaxRetries int `json:"max_retries"` // HTTPMethod is the HTTP method that the monitor uses for requests. HTTPMethod string `json:"http_method"` // URLPath is the HTTP path of the request sent by the monitor to test the // health of a member. Must be a string beginning with a forward slash (/). URLPath string `json:"url_path"` // ExpectedCodes is the expected HTTP codes for a passing HTTP(S) monitor. ExpectedCodes string `json:"expected_codes"` // AdminStateUp is the administrative state of the health monitor, which is up // (true) or down (false). AdminStateUp bool `json:"admin_state_up"` // Status is the status of the health monitor. Indicates whether the health // monitor is operational. Status string } // MonitorPage is the page returned by a pager when traversing over a // collection of health monitors. type MonitorPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of monitors has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r MonitorPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"health_monitors_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a PoolPage struct is empty. func (r MonitorPage) IsEmpty() (bool, error) { is, err := ExtractMonitors(r) return len(is) == 0, err } // ExtractMonitors accepts a Page struct, specifically a MonitorPage struct, // and extracts the elements into a slice of Monitor structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractMonitors(r pagination.Page) ([]Monitor, error) { var s struct { Monitors []Monitor `json:"health_monitors"` } err := (r.(MonitorPage)).ExtractInto(&s) return s.Monitors, err } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a monitor. func (r commonResult) Extract() (*Monitor, error) { var s struct { Monitor *Monitor `json:"health_monitor"` } err := r.ExtractInto(&s) return s.Monitor, err } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a Monitor. type CreateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a Monitor. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a Monitor. type UpdateResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its Extract // method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400370125ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/monitorsdoc.go000066400000000000000000000000471335005366400401070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/monitors/testing// monitors unit tests package testing requests_test.go000066400000000000000000000176071335005366400422660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/monitors/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/health_monitors", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "health_monitors":[ { "admin_state_up":true, "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", "delay":10, "max_retries":1, "timeout":1, "type":"PING", "id":"466c8345-28d8-4f84-a246-e04380b0461d" }, { "admin_state_up":true, "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", "delay":5, "expected_codes":"200", "max_retries":2, "http_method":"GET", "timeout":2, "url_path":"/", "type":"HTTP", "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7" } ] } `) }) count := 0 monitors.List(fake.ServiceClient(), monitors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := monitors.ExtractMonitors(page) if err != nil { t.Errorf("Failed to extract monitors: %v", err) return false, err } expected := []monitors.Monitor{ { AdminStateUp: true, TenantID: "83657cfcdfe44cd5920adaf26c48ceea", Delay: 10, MaxRetries: 1, Timeout: 1, Type: "PING", ID: "466c8345-28d8-4f84-a246-e04380b0461d", }, { AdminStateUp: true, TenantID: "83657cfcdfe44cd5920adaf26c48ceea", Delay: 5, ExpectedCodes: "200", MaxRetries: 2, Timeout: 2, URLPath: "/", Type: "HTTP", HTTPMethod: "GET", ID: "5d4b5228-33b0-4e60-b225-9b727c1a20e7", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestDelayMustBeGreaterOrEqualThanTimeout(t *testing.T) { _, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{ Type: "HTTP", Delay: 1, Timeout: 10, MaxRetries: 5, URLPath: "/check", ExpectedCodes: "200-299", }).Extract() if err == nil { t.Fatalf("Expected error, got none") } _, err = monitors.Update(fake.ServiceClient(), "453105b9-1754-413f-aab1-55f1af620750", monitors.UpdateOpts{ Delay: 1, Timeout: 10, }).Extract() if err == nil { t.Fatalf("Expected error, got none") } } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/health_monitors", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "health_monitor":{ "type":"HTTP", "tenant_id":"453105b9-1754-413f-aab1-55f1af620750", "delay":20, "timeout":10, "max_retries":5, "url_path":"/check", "expected_codes":"200-299" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "health_monitor":{ "id":"f3eeab00-8367-4524-b662-55e64d4cacb5", "tenant_id":"453105b9-1754-413f-aab1-55f1af620750", "type":"HTTP", "delay":20, "timeout":10, "max_retries":5, "http_method":"GET", "url_path":"/check", "expected_codes":"200-299", "admin_state_up":true, "status":"ACTIVE" } } `) }) _, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{ Type: "HTTP", TenantID: "453105b9-1754-413f-aab1-55f1af620750", Delay: 20, Timeout: 10, MaxRetries: 5, URLPath: "/check", ExpectedCodes: "200-299", }).Extract() th.AssertNoErr(t, err) } func TestRequiredCreateOpts(t *testing.T) { res := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = monitors.Create(fake.ServiceClient(), monitors.CreateOpts{Type: monitors.TypeHTTP}) if res.Err == nil { t.Fatalf("Expected error, got none") } } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/health_monitors/f3eeab00-8367-4524-b662-55e64d4cacb5", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "health_monitor":{ "id":"f3eeab00-8367-4524-b662-55e64d4cacb5", "tenant_id":"453105b9-1754-413f-aab1-55f1af620750", "type":"HTTP", "delay":20, "timeout":10, "max_retries":5, "http_method":"GET", "url_path":"/check", "expected_codes":"200-299", "admin_state_up":true, "status":"ACTIVE" } } `) }) hm, err := monitors.Get(fake.ServiceClient(), "f3eeab00-8367-4524-b662-55e64d4cacb5").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "f3eeab00-8367-4524-b662-55e64d4cacb5", hm.ID) th.AssertEquals(t, "453105b9-1754-413f-aab1-55f1af620750", hm.TenantID) th.AssertEquals(t, "HTTP", hm.Type) th.AssertEquals(t, 20, hm.Delay) th.AssertEquals(t, 10, hm.Timeout) th.AssertEquals(t, 5, hm.MaxRetries) th.AssertEquals(t, "GET", hm.HTTPMethod) th.AssertEquals(t, "/check", hm.URLPath) th.AssertEquals(t, "200-299", hm.ExpectedCodes) th.AssertEquals(t, true, hm.AdminStateUp) th.AssertEquals(t, "ACTIVE", hm.Status) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/health_monitors/b05e44b5-81f9-4551-b474-711a722698f7", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "health_monitor":{ "delay": 30, "timeout": 20, "max_retries": 10, "url_path": "/another_check", "expected_codes": "301", "admin_state_up": true } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, ` { "health_monitor": { "admin_state_up": true, "tenant_id": "4fd44f30292945e481c7b8a0c8908869", "delay": 30, "max_retries": 10, "http_method": "GET", "timeout": 20, "pools": [ { "status": "PENDING_CREATE", "status_description": null, "pool_id": "6e55751f-6ad4-4e53-b8d4-02e442cd21df" } ], "type": "PING", "id": "b05e44b5-81f9-4551-b474-711a722698f7" } } `) }) _, err := monitors.Update(fake.ServiceClient(), "b05e44b5-81f9-4551-b474-711a722698f7", monitors.UpdateOpts{ Delay: 30, Timeout: 20, MaxRetries: 10, URLPath: "/another_check", ExpectedCodes: "301", AdminStateUp: gophercloud.Enabled, }).Extract() th.AssertNoErr(t, err) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/health_monitors/b05e44b5-81f9-4551-b474-711a722698f7", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := monitors.Delete(fake.ServiceClient(), "b05e44b5-81f9-4551-b474-711a722698f7") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000005331335005366400366520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/monitorspackage monitors import "github.com/gophercloud/gophercloud" const ( rootPath = "lb" resourcePath = "health_monitors" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } pools/000077500000000000000000000000001335005366400346175ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaasdoc.go000066400000000000000000000034151335005366400357160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/pools/* Package pools provides information and interaction with the Pools of the Load Balancing as a Service extension for the OpenStack Networking service. Example to List Pools listOpts := pools.ListOpts{ SubnetID: "d9bd223b-f1a9-4f98-953b-df977b0f902d", } allPages, err := pools.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allPools, err := pools.ExtractPools(allPages) if err != nil { panic(err) } for _, pool := range allPools { fmt.Printf("%+v\n", pool) } Example to Create a Pool createOpts := pools.CreateOpts{ LBMethod: pools.LBMethodRoundRobin, Protocol: "HTTP", Name: "Example pool", SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", Provider: "haproxy", } pool, err := pools.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Pool poolID := "166db5e6-c72a-4d77-8776-3573e27ae271" updateOpts := pools.UpdateOpts{ LBMethod: pools.LBMethodLeastConnections, } pool, err := pools.Update(networkClient, poolID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Pool poolID := "166db5e6-c72a-4d77-8776-3573e27ae271" err := pools.Delete(networkClient, poolID).ExtractErr() if err != nil { panic(err) } Example to Associate a Monitor to a Pool poolID := "166db5e6-c72a-4d77-8776-3573e27ae271" monitorID := "8bbfbe1c-6faa-4d97-abdb-0df6c90df70b" pool, err := pools.AssociateMonitor(networkClient, poolID, monitorID).Extract() if err != nil { panic(err) } Example to Disassociate a Monitor from a Pool poolID := "166db5e6-c72a-4d77-8776-3573e27ae271" monitorID := "8bbfbe1c-6faa-4d97-abdb-0df6c90df70b" pool, err := pools.DisassociateMonitor(networkClient, poolID, monitorID).Extract() if err != nil { panic(err) } */ package pools requests.go000066400000000000000000000143111335005366400370210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/poolspackage pools import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the floating IP attributes you want to see returned. SortKey allows you to // sort by a particular network attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { Status string `q:"status"` LBMethod string `q:"lb_method"` Protocol string `q:"protocol"` SubnetID string `q:"subnet_id"` TenantID string `q:"tenant_id"` AdminStateUp *bool `q:"admin_state_up"` Name string `q:"name"` ID string `q:"id"` VIPID string `q:"vip_id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // List returns a Pager which allows you to iterate over a collection of // pools. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. // // Default policy settings return only those pools that are owned by the // tenant who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { q, err := gophercloud.BuildQueryString(&opts) if err != nil { return pagination.Pager{Err: err} } u := rootURL(c) + q.String() return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page { return PoolPage{pagination.LinkedPageBase{PageResult: r}} }) } // LBMethod is a type used for possible load balancing methods. type LBMethod string // LBProtocol is a type used for possible load balancing protocols. type LBProtocol string // Supported attributes for create/update operations. const ( LBMethodRoundRobin LBMethod = "ROUND_ROBIN" LBMethodLeastConnections LBMethod = "LEAST_CONNECTIONS" ProtocolTCP LBProtocol = "TCP" ProtocolHTTP LBProtocol = "HTTP" ProtocolHTTPS LBProtocol = "HTTPS" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToLBPoolCreateMap() (map[string]interface{}, error) } // CreateOpts contains all the values needed to create a new pool. type CreateOpts struct { // Name of the pool. Name string `json:"name" required:"true"` // Protocol used by the pool members, you can use either // ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS. Protocol LBProtocol `json:"protocol" required:"true"` // TenantID is only required if the caller has an admin role and wants // to create a pool for another tenant. TenantID string `json:"tenant_id,omitempty"` // SubnetID is the network on which the members of the pool will be located. // Only members that are on this network can be added to the pool. SubnetID string `json:"subnet_id,omitempty"` // LBMethod is the algorithm used to distribute load between the members of // the pool. The current specification supports LBMethodRoundRobin and // LBMethodLeastConnections as valid values for this attribute. LBMethod LBMethod `json:"lb_method" required:"true"` // Provider of the pool. Provider string `json:"provider,omitempty"` } // ToLBPoolCreateMap builds a request body based on CreateOpts. func (opts CreateOpts) ToLBPoolCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "pool") } // Create accepts a CreateOptsBuilder and uses the values to create a new // load balancer pool. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToLBPoolCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular pool based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters ot the // Update request. type UpdateOptsBuilder interface { ToLBPoolUpdateMap() (map[string]interface{}, error) } // UpdateOpts contains the values used when updating a pool. type UpdateOpts struct { // Name of the pool. Name string `json:"name,omitempty"` // LBMethod is the algorithm used to distribute load between the members of // the pool. The current specification supports LBMethodRoundRobin and // LBMethodLeastConnections as valid values for this attribute. LBMethod LBMethod `json:"lb_method,omitempty"` } // ToLBPoolUpdateMap builds a request body based on UpdateOpts. func (opts UpdateOpts) ToLBPoolUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "pool") } // Update allows pools to be updated. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToLBPoolUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete will permanently delete a particular pool based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } // AssociateMonitor will associate a health monitor with a particular pool. // Once associated, the health monitor will start monitoring the members of the // pool and will deactivate these members if they are deemed unhealthy. A // member can be deactivated (status set to INACTIVE) if any of health monitors // finds it unhealthy. func AssociateMonitor(c *gophercloud.ServiceClient, poolID, monitorID string) (r AssociateResult) { b := map[string]interface{}{"health_monitor": map[string]string{"id": monitorID}} _, r.Err = c.Post(associateURL(c, poolID), b, &r.Body, nil) return } // DisassociateMonitor will disassociate a health monitor with a particular // pool. When dissociation is successful, the health monitor will no longer // check for the health of the members of the pool. func DisassociateMonitor(c *gophercloud.ServiceClient, poolID, monitorID string) (r AssociateResult) { _, r.Err = c.Delete(disassociateURL(c, poolID, monitorID), nil) return } results.go000066400000000000000000000075611335005366400366600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/poolspackage pools import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Pool represents a logical set of devices, such as web servers, that you // group together to receive and process traffic. The load balancing function // chooses a member of the pool according to the configured load balancing // method to handle the new requests or connections received on the VIP address. // There is only one pool per virtual IP. type Pool struct { // Status of the pool. Indicates whether the pool is operational. Status string // LBMethod is the load-balancer algorithm, which is round-robin, // least-connections, and so on. This value, which must be supported, is // dependent on the provider. LBMethod string `json:"lb_method"` // Protocol of the pool, which is TCP, HTTP, or HTTPS. Protocol string // Description for the pool. Description string // MonitorIDs are the IDs of associated monitors which check the health of // the pool members. MonitorIDs []string `json:"health_monitors"` // SubnetID is the network on which the members of the pool will be located. // Only members that are on this network can be added to the pool. SubnetID string `json:"subnet_id"` // TenantID is the owner of the pool. TenantID string `json:"tenant_id"` // AdminStateUp is the administrative state of the pool, which is up // (true) or down (false). AdminStateUp bool `json:"admin_state_up"` // Name of the pool. Name string // MemberIDs is the list of member IDs that belong to the pool. MemberIDs []string `json:"members"` // ID is the unique ID for the pool. ID string // VIPID is the ID of the virtual IP associated with this pool. VIPID string `json:"vip_id"` // The provider. Provider string } // PoolPage is the page returned by a pager when traversing over a // collection of pools. type PoolPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of pools has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r PoolPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"pools_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a PoolPage struct is empty. func (r PoolPage) IsEmpty() (bool, error) { is, err := ExtractPools(r) return len(is) == 0, err } // ExtractPools accepts a Page struct, specifically a PoolPage struct, // and extracts the elements into a slice of Router structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractPools(r pagination.Page) ([]Pool, error) { var s struct { Pools []Pool `json:"pools"` } err := (r.(PoolPage)).ExtractInto(&s) return s.Pools, err } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a router. func (r commonResult) Extract() (*Pool, error) { var s struct { Pool *Pool `json:"pool"` } err := r.ExtractInto(&s) return s.Pool, err } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a Pool. type CreateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a Pool. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a Pool. type UpdateResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its // ExtractErr method to interpret it as a Pool. type DeleteResult struct { gophercloud.ErrResult } // AssociateResult represents the result of an association operation. Call its Extract // method to interpret it as a Pool. type AssociateResult struct { commonResult } testing/000077500000000000000000000000001335005366400362745ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/poolsdoc.go000066400000000000000000000000441335005366400373660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/pools/testing// pools unit tests package testing requests_test.go000066400000000000000000000214711335005366400415420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/pools/testingpackage testing import ( "fmt" "net/http" "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/pools", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "pools":[ { "status":"ACTIVE", "lb_method":"ROUND_ROBIN", "protocol":"HTTP", "description":"", "health_monitors":[ "466c8345-28d8-4f84-a246-e04380b0461d", "5d4b5228-33b0-4e60-b225-9b727c1a20e7" ], "members":[ "701b531b-111a-4f21-ad85-4795b7b12af6", "beb53b4d-230b-4abd-8118-575b8fa006ef" ], "status_description": null, "id":"72741b06-df4d-4715-b142-276b6bce75ab", "vip_id":"4ec89087-d057-4e2c-911f-60a3b47ee304", "name":"app_pool", "admin_state_up":true, "subnet_id":"8032909d-47a1-4715-90af-5153ffe39861", "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", "health_monitors_status": [], "provider": "haproxy" } ] } `) }) count := 0 pools.List(fake.ServiceClient(), pools.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := pools.ExtractPools(page) if err != nil { t.Errorf("Failed to extract pools: %v", err) return false, err } expected := []pools.Pool{ { Status: "ACTIVE", LBMethod: "ROUND_ROBIN", Protocol: "HTTP", Description: "", MonitorIDs: []string{ "466c8345-28d8-4f84-a246-e04380b0461d", "5d4b5228-33b0-4e60-b225-9b727c1a20e7", }, SubnetID: "8032909d-47a1-4715-90af-5153ffe39861", TenantID: "83657cfcdfe44cd5920adaf26c48ceea", AdminStateUp: true, Name: "app_pool", MemberIDs: []string{ "701b531b-111a-4f21-ad85-4795b7b12af6", "beb53b4d-230b-4abd-8118-575b8fa006ef", }, ID: "72741b06-df4d-4715-b142-276b6bce75ab", VIPID: "4ec89087-d057-4e2c-911f-60a3b47ee304", Provider: "haproxy", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/pools", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "pool": { "lb_method": "ROUND_ROBIN", "protocol": "HTTP", "name": "Example pool", "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", "provider": "haproxy" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "pool": { "status": "PENDING_CREATE", "lb_method": "ROUND_ROBIN", "protocol": "HTTP", "description": "", "health_monitors": [], "members": [], "status_description": null, "id": "69055154-f603-4a28-8951-7cc2d9e54a9a", "vip_id": null, "name": "Example pool", "admin_state_up": true, "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", "health_monitors_status": [], "provider": "haproxy" } } `) }) options := pools.CreateOpts{ LBMethod: pools.LBMethodRoundRobin, Protocol: "HTTP", Name: "Example pool", SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", TenantID: "2ffc6e22aae24e4795f87155d24c896f", Provider: "haproxy", } p, err := pools.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "PENDING_CREATE", p.Status) th.AssertEquals(t, "ROUND_ROBIN", p.LBMethod) th.AssertEquals(t, "HTTP", p.Protocol) th.AssertEquals(t, "", p.Description) th.AssertDeepEquals(t, []string{}, p.MonitorIDs) th.AssertDeepEquals(t, []string{}, p.MemberIDs) th.AssertEquals(t, "69055154-f603-4a28-8951-7cc2d9e54a9a", p.ID) th.AssertEquals(t, "Example pool", p.Name) th.AssertEquals(t, "1981f108-3c48-48d2-b908-30f7d28532c9", p.SubnetID) th.AssertEquals(t, "2ffc6e22aae24e4795f87155d24c896f", p.TenantID) th.AssertEquals(t, "haproxy", p.Provider) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "pool":{ "id":"332abe93-f488-41ba-870b-2ac66be7f853", "tenant_id":"19eaa775-cf5d-49bc-902e-2f85f668d995", "name":"Example pool", "description":"", "protocol":"tcp", "lb_algorithm":"ROUND_ROBIN", "session_persistence":{ }, "healthmonitor_id":null, "members":[ ], "admin_state_up":true, "status":"ACTIVE" } } `) }) n, err := pools.Get(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.ID, "332abe93-f488-41ba-870b-2ac66be7f853") } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "pool":{ "name":"SuperPool", "lb_method": "LEAST_CONNECTIONS" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "pool":{ "status":"PENDING_UPDATE", "lb_method":"LEAST_CONNECTIONS", "protocol":"TCP", "description":"", "health_monitors":[ ], "subnet_id":"8032909d-47a1-4715-90af-5153ffe39861", "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", "admin_state_up":true, "name":"SuperPool", "members":[ ], "id":"61b1f87a-7a21-4ad3-9dda-7f81d249944f", "vip_id":null } } `) }) options := pools.UpdateOpts{Name: "SuperPool", LBMethod: pools.LBMethodLeastConnections} n, err := pools.Update(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "SuperPool", n.Name) th.AssertDeepEquals(t, "LEAST_CONNECTIONS", n.LBMethod) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := pools.Delete(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853") th.AssertNoErr(t, res.Err) } func TestAssociateHealthMonitor(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853/health_monitors", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "health_monitor":{ "id":"b624decf-d5d3-4c66-9a3d-f047e7786181" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, `{}`) }) _, err := pools.AssociateMonitor(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "b624decf-d5d3-4c66-9a3d-f047e7786181").Extract() th.AssertNoErr(t, err) } func TestDisassociateHealthMonitor(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853/health_monitors/b624decf-d5d3-4c66-9a3d-f047e7786181", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := pools.DisassociateMonitor(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "b624decf-d5d3-4c66-9a3d-f047e7786181") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000012331335005366400361320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/poolspackage pools import "github.com/gophercloud/gophercloud" const ( rootPath = "lb" resourcePath = "pools" monitorPath = "health_monitors" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } func associateURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id, monitorPath) } func disassociateURL(c *gophercloud.ServiceClient, poolID, monitorID string) string { return c.ServiceURL(rootPath, resourcePath, poolID, monitorPath, monitorID) } vips/000077500000000000000000000000001335005366400344445ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaasdoc.go000066400000000000000000000026611335005366400355450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/vips/* Package vips provides information and interaction with the Virtual IPs of the Load Balancing as a Service extension for the OpenStack Networking service. Example to List Virtual IPs listOpts := vips.ListOpts{ SubnetID: "d9bd223b-f1a9-4f98-953b-df977b0f902d", } allPages, err := vips.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allVIPs, err := vips.ExtractVIPs(allPages) if err != nil { panic(err) } for _, vip := range allVIPs { fmt.Printf("%+v\n", vip) } Example to Create a Virtual IP createOpts := vips.CreateOpts{ Protocol: "HTTP", Name: "NewVip", AdminStateUp: gophercloud.Enabled, SubnetID: "8032909d-47a1-4715-90af-5153ffe39861", PoolID: "61b1f87a-7a21-4ad3-9dda-7f81d249944f", ProtocolPort: 80, Persistence: &vips.SessionPersistence{Type: "SOURCE_IP"}, } vip, err := vips.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Virtual IP vipID := "93f1bad4-0423-40a8-afac-3fc541839912" i1000 := 1000 updateOpts := vips.UpdateOpts{ ConnLimit: &i1000, Persistence: &vips.SessionPersistence{Type: "SOURCE_IP"}, } vip, err := vips.Update(networkClient, vipID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Virtual IP vipID := "93f1bad4-0423-40a8-afac-3fc541839912" err := vips.Delete(networkClient, vipID).ExtractErr() if err != nil { panic(err) } */ package vips requests.go000066400000000000000000000152531335005366400366540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/vipspackage vips import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the floating IP attributes you want to see returned. SortKey allows you to // sort by a particular network attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { ID string `q:"id"` Name string `q:"name"` AdminStateUp *bool `q:"admin_state_up"` Status string `q:"status"` TenantID string `q:"tenant_id"` SubnetID string `q:"subnet_id"` Address string `q:"address"` PortID string `q:"port_id"` Protocol string `q:"protocol"` ProtocolPort int `q:"protocol_port"` ConnectionLimit int `q:"connection_limit"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // List returns a Pager which allows you to iterate over a collection of // Virtual IPs. It accepts a ListOpts struct, which allows you to filter and // sort the returned collection for greater efficiency. // // Default policy settings return only those virtual IPs that are owned by the // tenant who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { q, err := gophercloud.BuildQueryString(&opts) if err != nil { return pagination.Pager{Err: err} } u := rootURL(c) + q.String() return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page { return VIPPage{pagination.LinkedPageBase{PageResult: r}} }) } // CreateOptsBuilder allows extensions to add additional parameters to the // Create Request. type CreateOptsBuilder interface { ToVIPCreateMap() (map[string]interface{}, error) } // CreateOpts contains all the values needed to create a new virtual IP. type CreateOpts struct { // Name is the human-readable name for the VIP. Does not have to be unique. Name string `json:"name" required:"true"` // SubnetID is the network on which to allocate the VIP's address. A tenant // can only create VIPs on networks authorized by policy (e.g. networks that // belong to them or networks that are shared). SubnetID string `json:"subnet_id" required:"true"` // Protocol - can either be TCP, HTTP or HTTPS. Protocol string `json:"protocol" required:"true"` // ProtocolPort is the port on which to listen for client traffic. ProtocolPort int `json:"protocol_port" required:"true"` // PoolID is the ID of the pool with which the VIP is associated. PoolID string `json:"pool_id" required:"true"` // TenantID is only required if the caller has an admin role and wants // to create a pool for another tenant. TenantID string `json:"tenant_id,omitempty"` // Address is the IP address of the VIP. Address string `json:"address,omitempty"` // Description is the human-readable description for the VIP. Description string `json:"description,omitempty"` // Persistence is the the of session persistence to use. // Omit this field to prevent session persistence. Persistence *SessionPersistence `json:"session_persistence,omitempty"` // ConnLimit is the maximum number of connections allowed for the VIP. ConnLimit *int `json:"connection_limit,omitempty"` // AdminStateUp is the administrative state of the VIP. A valid value is // true (UP) or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToVIPCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToVIPCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "vip") } // Create is an operation which provisions a new virtual IP based on the // configuration defined in the CreateOpts struct. Once the request is // validated and progress has started on the provisioning process, a // CreateResult will be returned. // // Please note that the PoolID should refer to a pool that is not already // associated with another vip. If the pool is already used by another vip, // then the operation will fail with a 409 Conflict error will be returned. // // Users with an admin role can create VIPs on behalf of other tenants by // specifying a TenantID attribute different than their own. func Create(c *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { b, err := opts.ToVIPCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular virtual IP based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToVIPUpdateMap() (map[string]interface{}, error) } // UpdateOpts contains all the values needed to update an existing virtual IP. // Attributes not listed here but appear in CreateOpts are immutable and cannot // be updated. type UpdateOpts struct { // Name is the human-readable name for the VIP. Does not have to be unique. Name *string `json:"name,omitempty"` // PoolID is the ID of the pool with which the VIP is associated. PoolID *string `json:"pool_id,omitempty"` // Description is the human-readable description for the VIP. Description *string `json:"description,omitempty"` // Persistence is the the of session persistence to use. // Omit this field to prevent session persistence. Persistence *SessionPersistence `json:"session_persistence,omitempty"` // ConnLimit is the maximum number of connections allowed for the VIP. ConnLimit *int `json:"connection_limit,omitempty"` // AdminStateUp is the administrative state of the VIP. A valid value is // true (UP) or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToVIPUpdateMap builds a request body based on UpdateOpts. func (opts UpdateOpts) ToVIPUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "vip") } // Update is an operation which modifies the attributes of the specified VIP. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToVIPUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) return } // Delete will permanently delete a particular virtual IP based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } results.go000066400000000000000000000124601335005366400364770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/vipspackage vips import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // SessionPersistence represents the session persistence feature of the load // balancing service. It attempts to force connections or requests in the same // session to be processed by the same member as long as it is ative. Three // types of persistence are supported: // // SOURCE_IP: With this mode, all connections originating from the same source // IP address, will be handled by the same member of the pool. // HTTP_COOKIE: With this persistence mode, the load balancing function will // create a cookie on the first request from a client. Subsequent // requests containing the same cookie value will be handled by // the same member of the pool. // APP_COOKIE: With this persistence mode, the load balancing function will // rely on a cookie established by the backend application. All // requests carrying the same cookie value will be handled by the // same member of the pool. type SessionPersistence struct { // Type is the type of persistence mode. Type string `json:"type"` // CookieName is the name of cookie if persistence mode is set appropriately. CookieName string `json:"cookie_name,omitempty"` } // VirtualIP is the primary load balancing configuration object that specifies // the virtual IP address and port on which client traffic is received, as well // as other details such as the load balancing method to be use, protocol, etc. // This entity is sometimes known in LB products under the name of a "virtual // server", a "vserver" or a "listener". type VirtualIP struct { // ID is the unique ID for the VIP. ID string `json:"id"` // TenantID is the owner of the VIP. TenantID string `json:"tenant_id"` // Name is the human-readable name for the VIP. Does not have to be unique. Name string `json:"name"` // Description is the human-readable description for the VIP. Description string `json:"description"` // SubnetID is the ID of the subnet on which to allocate the VIP address. SubnetID string `json:"subnet_id"` // Address is the IP address of the VIP. Address string `json:"address"` // Protocol of the VIP address. A valid value is TCP, HTTP, or HTTPS. Protocol string `json:"protocol"` // ProtocolPort is the port on which to listen to client traffic that is // associated with the VIP address. A valid value is from 0 to 65535. ProtocolPort int `json:"protocol_port"` // PoolID is the ID of the pool with which the VIP is associated. PoolID string `json:"pool_id"` // PortID is the ID of the port which belongs to the load balancer. PortID string `json:"port_id"` // Persistence indicates whether connections in the same session will be // processed by the same pool member or not. Persistence SessionPersistence `json:"session_persistence"` // ConnLimit is the maximum number of connections allowed for the VIP. // Default is -1, meaning no limit. ConnLimit int `json:"connection_limit"` // AdminStateUp is the administrative state of the VIP. A valid value is // true (UP) or false (DOWN). AdminStateUp bool `json:"admin_state_up"` // Status is the status of the VIP. Indicates whether the VIP is operational. Status string `json:"status"` } // VIPPage is the page returned by a pager when traversing over a // collection of virtual IPs. type VIPPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of routers has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r VIPPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"vips_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a VIPPage struct is empty. func (r VIPPage) IsEmpty() (bool, error) { is, err := ExtractVIPs(r) return len(is) == 0, err } // ExtractVIPs accepts a Page struct, specifically a VIPPage struct, // and extracts the elements into a slice of VirtualIP structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractVIPs(r pagination.Page) ([]VirtualIP, error) { var s struct { VIPs []VirtualIP `json:"vips"` } err := (r.(VIPPage)).ExtractInto(&s) return s.VIPs, err } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a VirtualIP. func (r commonResult) Extract() (*VirtualIP, error) { var s struct { VirtualIP *VirtualIP `json:"vip" json:"vip"` } err := r.ExtractInto(&s) return s.VirtualIP, err } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a VirtualIP type CreateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a VirtualIP type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a VirtualIP type UpdateResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its // ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400361215ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/vipsdoc.go000066400000000000000000000000431335005366400372120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/vips/testing// vips unit tests package testing requests_test.go000066400000000000000000000241411335005366400413640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/vips/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/vips" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/vips", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "vips":[ { "id": "db902c0c-d5ff-4753-b465-668ad9656918", "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c", "name": "web_vip", "description": "lb config for the web tier", "subnet_id": "96a4386a-f8c3-42ed-afce-d7954eee77b3", "address" : "10.30.176.47", "port_id" : "cd1f7a47-4fa6-449c-9ee7-632838aedfea", "protocol": "HTTP", "protocol_port": 80, "pool_id" : "cfc6589d-f949-4c66-99d2-c2da56ef3764", "admin_state_up": true, "status": "ACTIVE" }, { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c", "name": "db_vip", "description": "lb config for the db tier", "subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", "address" : "10.30.176.48", "port_id" : "cd1f7a47-4fa6-449c-9ee7-632838aedfea", "protocol": "TCP", "protocol_port": 3306, "pool_id" : "41efe233-7591-43c5-9cf7-923964759f9e", "session_persistence" : {"type" : "SOURCE_IP"}, "connection_limit" : 2000, "admin_state_up": true, "status": "INACTIVE" } ] } `) }) count := 0 vips.List(fake.ServiceClient(), vips.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := vips.ExtractVIPs(page) if err != nil { t.Errorf("Failed to extract LBs: %v", err) return false, err } expected := []vips.VirtualIP{ { ID: "db902c0c-d5ff-4753-b465-668ad9656918", TenantID: "310df60f-2a10-4ee5-9554-98393092194c", Name: "web_vip", Description: "lb config for the web tier", SubnetID: "96a4386a-f8c3-42ed-afce-d7954eee77b3", Address: "10.30.176.47", PortID: "cd1f7a47-4fa6-449c-9ee7-632838aedfea", Protocol: "HTTP", ProtocolPort: 80, PoolID: "cfc6589d-f949-4c66-99d2-c2da56ef3764", Persistence: vips.SessionPersistence{}, ConnLimit: 0, AdminStateUp: true, Status: "ACTIVE", }, { ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", TenantID: "310df60f-2a10-4ee5-9554-98393092194c", Name: "db_vip", Description: "lb config for the db tier", SubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", Address: "10.30.176.48", PortID: "cd1f7a47-4fa6-449c-9ee7-632838aedfea", Protocol: "TCP", ProtocolPort: 3306, PoolID: "41efe233-7591-43c5-9cf7-923964759f9e", Persistence: vips.SessionPersistence{Type: "SOURCE_IP"}, ConnLimit: 2000, AdminStateUp: true, Status: "INACTIVE", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/vips", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "vip": { "protocol": "HTTP", "name": "NewVip", "admin_state_up": true, "subnet_id": "8032909d-47a1-4715-90af-5153ffe39861", "pool_id": "61b1f87a-7a21-4ad3-9dda-7f81d249944f", "protocol_port": 80, "session_persistence": {"type": "SOURCE_IP"} } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "vip": { "status": "PENDING_CREATE", "protocol": "HTTP", "description": "", "admin_state_up": true, "subnet_id": "8032909d-47a1-4715-90af-5153ffe39861", "tenant_id": "83657cfcdfe44cd5920adaf26c48ceea", "connection_limit": -1, "pool_id": "61b1f87a-7a21-4ad3-9dda-7f81d249944f", "address": "10.0.0.11", "protocol_port": 80, "port_id": "f7e6fe6a-b8b5-43a8-8215-73456b32e0f5", "id": "c987d2be-9a3c-4ac9-a046-e8716b1350e2", "name": "NewVip" } } `) }) opts := vips.CreateOpts{ Protocol: "HTTP", Name: "NewVip", AdminStateUp: gophercloud.Enabled, SubnetID: "8032909d-47a1-4715-90af-5153ffe39861", PoolID: "61b1f87a-7a21-4ad3-9dda-7f81d249944f", ProtocolPort: 80, Persistence: &vips.SessionPersistence{Type: "SOURCE_IP"}, } r, err := vips.Create(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "PENDING_CREATE", r.Status) th.AssertEquals(t, "HTTP", r.Protocol) th.AssertEquals(t, "", r.Description) th.AssertEquals(t, true, r.AdminStateUp) th.AssertEquals(t, "8032909d-47a1-4715-90af-5153ffe39861", r.SubnetID) th.AssertEquals(t, "83657cfcdfe44cd5920adaf26c48ceea", r.TenantID) th.AssertEquals(t, -1, r.ConnLimit) th.AssertEquals(t, "61b1f87a-7a21-4ad3-9dda-7f81d249944f", r.PoolID) th.AssertEquals(t, "10.0.0.11", r.Address) th.AssertEquals(t, 80, r.ProtocolPort) th.AssertEquals(t, "f7e6fe6a-b8b5-43a8-8215-73456b32e0f5", r.PortID) th.AssertEquals(t, "c987d2be-9a3c-4ac9-a046-e8716b1350e2", r.ID) th.AssertEquals(t, "NewVip", r.Name) } func TestRequiredCreateOpts(t *testing.T) { res := vips.Create(fake.ServiceClient(), vips.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = vips.Create(fake.ServiceClient(), vips.CreateOpts{Name: "foo"}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = vips.Create(fake.ServiceClient(), vips.CreateOpts{Name: "foo", SubnetID: "bar"}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = vips.Create(fake.ServiceClient(), vips.CreateOpts{Name: "foo", SubnetID: "bar", Protocol: "bar"}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = vips.Create(fake.ServiceClient(), vips.CreateOpts{Name: "foo", SubnetID: "bar", Protocol: "bar", ProtocolPort: 80}) if res.Err == nil { t.Fatalf("Expected error, got none") } } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/vips/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "vip": { "status": "ACTIVE", "protocol": "HTTP", "description": "", "admin_state_up": true, "subnet_id": "8032909d-47a1-4715-90af-5153ffe39861", "tenant_id": "83657cfcdfe44cd5920adaf26c48ceea", "connection_limit": 1000, "pool_id": "72741b06-df4d-4715-b142-276b6bce75ab", "session_persistence": { "cookie_name": "MyAppCookie", "type": "APP_COOKIE" }, "address": "10.0.0.10", "protocol_port": 80, "port_id": "b5a743d6-056b-468b-862d-fb13a9aa694e", "id": "4ec89087-d057-4e2c-911f-60a3b47ee304", "name": "my-vip" } } `) }) vip, err := vips.Get(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "ACTIVE", vip.Status) th.AssertEquals(t, "HTTP", vip.Protocol) th.AssertEquals(t, "", vip.Description) th.AssertEquals(t, true, vip.AdminStateUp) th.AssertEquals(t, 1000, vip.ConnLimit) th.AssertEquals(t, vips.SessionPersistence{Type: "APP_COOKIE", CookieName: "MyAppCookie"}, vip.Persistence) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/vips/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "vip": { "connection_limit": 1000, "session_persistence": {"type": "SOURCE_IP"} } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, ` { "vip": { "status": "PENDING_UPDATE", "protocol": "HTTP", "description": "", "admin_state_up": true, "subnet_id": "8032909d-47a1-4715-90af-5153ffe39861", "tenant_id": "83657cfcdfe44cd5920adaf26c48ceea", "connection_limit": 1000, "pool_id": "61b1f87a-7a21-4ad3-9dda-7f81d249944f", "address": "10.0.0.11", "protocol_port": 80, "port_id": "f7e6fe6a-b8b5-43a8-8215-73456b32e0f5", "id": "c987d2be-9a3c-4ac9-a046-e8716b1350e2", "name": "NewVip" } } `) }) i1000 := 1000 options := vips.UpdateOpts{ ConnLimit: &i1000, Persistence: &vips.SessionPersistence{Type: "SOURCE_IP"}, } vip, err := vips.Update(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "PENDING_UPDATE", vip.Status) th.AssertEquals(t, 1000, vip.ConnLimit) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/lb/vips/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := vips.Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000005141335005366400357600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas/vipspackage vips import "github.com/gophercloud/gophercloud" const ( rootPath = "lb" resourcePath = "vips" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } lbaas_v2/000077500000000000000000000000001335005366400340725ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensionsdoc.go000066400000000000000000000002441335005366400351660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2// Package lbaas_v2 provides information and interaction with the Load Balancer // as a Service v2 extension for the OpenStack Networking service. package lbaas_v2 listeners/000077500000000000000000000000001335005366400361025ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2doc.go000066400000000000000000000026761335005366400372110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/listeners/* Package listeners provides information and interaction with Listeners of the LBaaS v2 extension for the OpenStack Networking service. Example to List Listeners listOpts := listeners.ListOpts{ LoadbalancerID : "ca430f80-1737-4712-8dc6-3f640d55594b", } allPages, err := listeners.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allListeners, err := listeners.ExtractListeners(allPages) if err != nil { panic(err) } for _, listener := range allListeners { fmt.Printf("%+v\n", listener) } Example to Create a Listener createOpts := listeners.CreateOpts{ Protocol: "TCP", Name: "db", LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", AdminStateUp: gophercloud.Enabled, DefaultPoolID: "41efe233-7591-43c5-9cf7-923964759f9e", ProtocolPort: 3306, } listener, err := listeners.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Listener listenerID := "d67d56a6-4a86-4688-a282-f46444705c64" i1001 := 1001 updateOpts := listeners.UpdateOpts{ ConnLimit: &i1001, } listener, err := listeners.Update(networkClient, listenerID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Listener listenerID := "d67d56a6-4a86-4688-a282-f46444705c64" err := listeners.Delete(networkClient, listenerID).ExtractErr() if err != nil { panic(err) } */ package listeners requests.go000066400000000000000000000155331335005366400403130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/listenerspackage listeners import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Type Protocol represents a listener protocol. type Protocol string // Supported attributes for create/update operations. const ( ProtocolTCP Protocol = "TCP" ProtocolHTTP Protocol = "HTTP" ProtocolHTTPS Protocol = "HTTPS" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToListenerListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the floating IP attributes you want to see returned. SortKey allows you to // sort by a particular listener attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { ID string `q:"id"` Name string `q:"name"` AdminStateUp *bool `q:"admin_state_up"` TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` LoadbalancerID string `q:"loadbalancer_id"` DefaultPoolID string `q:"default_pool_id"` Protocol string `q:"protocol"` ProtocolPort int `q:"protocol_port"` ConnectionLimit int `q:"connection_limit"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToListenerListQuery formats a ListOpts into a query string. func (opts ListOpts) ToListenerListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // listeners. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. // // Default policy settings return only those listeners that are owned by the // tenant who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { query, err := opts.ToListenerListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return ListenerPage{pagination.LinkedPageBase{PageResult: r}} }) } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToListenerCreateMap() (map[string]interface{}, error) } // CreateOpts represents options for creating a listener. type CreateOpts struct { // The load balancer on which to provision this listener. LoadbalancerID string `json:"loadbalancer_id" required:"true"` // The protocol - can either be TCP, HTTP or HTTPS. Protocol Protocol `json:"protocol" required:"true"` // The port on which to listen for client traffic. ProtocolPort int `json:"protocol_port" required:"true"` // TenantID is only required if the caller has an admin role and wants // to create a pool for another project. TenantID string `json:"tenant_id,omitempty"` // ProjectID is only required if the caller has an admin role and wants // to create a pool for another project. ProjectID string `json:"project_id,omitempty"` // Human-readable name for the Listener. Does not have to be unique. Name string `json:"name,omitempty"` // The ID of the default pool with which the Listener is associated. DefaultPoolID string `json:"default_pool_id,omitempty"` // Human-readable description for the Listener. Description string `json:"description,omitempty"` // The maximum number of connections allowed for the Listener. ConnLimit *int `json:"connection_limit,omitempty"` // A reference to a Barbican container of TLS secrets. DefaultTlsContainerRef string `json:"default_tls_container_ref,omitempty"` // A list of references to TLS secrets. SniContainerRefs []string `json:"sni_container_refs,omitempty"` // The administrative state of the Listener. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToListenerCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToListenerCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "listener") } // Create is an operation which provisions a new Listeners based on the // configuration defined in the CreateOpts struct. Once the request is // validated and progress has started on the provisioning process, a // CreateResult will be returned. // // Users with an admin role can create Listeners on behalf of other tenants by // specifying a TenantID attribute different than their own. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToListenerCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular Listeners based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToListenerUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents options for updating a Listener. type UpdateOpts struct { // Human-readable name for the Listener. Does not have to be unique. Name string `json:"name,omitempty"` // Human-readable description for the Listener. Description string `json:"description,omitempty"` // The maximum number of connections allowed for the Listener. ConnLimit *int `json:"connection_limit,omitempty"` // A reference to a Barbican container of TLS secrets. DefaultTlsContainerRef string `json:"default_tls_container_ref,omitempty"` // A list of references to TLS secrets. SniContainerRefs []string `json:"sni_container_refs,omitempty"` // The administrative state of the Listener. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToListenerUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToListenerUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "listener") } // Update is an operation which modifies the attributes of the specified // Listener. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { b, err := opts.ToListenerUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) return } // Delete will permanently delete a particular Listeners based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } results.go000066400000000000000000000101601335005366400401300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/listenerspackage listeners import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" "github.com/gophercloud/gophercloud/pagination" ) type LoadBalancerID struct { ID string `json:"id"` } // Listener is the primary load balancing configuration object that specifies // the loadbalancer and port on which client traffic is received, as well // as other details such as the load balancing method to be use, protocol, etc. type Listener struct { // The unique ID for the Listener. ID string `json:"id"` // Owner of the Listener. TenantID string `json:"tenant_id"` // Human-readable name for the Listener. Does not have to be unique. Name string `json:"name"` // Human-readable description for the Listener. Description string `json:"description"` // The protocol to loadbalance. A valid value is TCP, HTTP, or HTTPS. Protocol string `json:"protocol"` // The port on which to listen to client traffic that is associated with the // Loadbalancer. A valid value is from 0 to 65535. ProtocolPort int `json:"protocol_port"` // The UUID of default pool. Must have compatible protocol with listener. DefaultPoolID string `json:"default_pool_id"` // A list of load balancer IDs. Loadbalancers []LoadBalancerID `json:"loadbalancers"` // The maximum number of connections allowed for the Loadbalancer. // Default is -1, meaning no limit. ConnLimit int `json:"connection_limit"` // The list of references to TLS secrets. SniContainerRefs []string `json:"sni_container_refs"` // A reference to a Barbican container of TLS secrets. DefaultTlsContainerRef string `json:"default_tls_container_ref"` // The administrative state of the Listener. A valid value is true (UP) or false (DOWN). AdminStateUp bool `json:"admin_state_up"` // Pools are the pools which are part of this listener. Pools []pools.Pool `json:"pools"` // The provisioning status of the listener. // This value is ACTIVE, PENDING_* or ERROR. ProvisioningStatus string `json:"provisioning_status"` } // ListenerPage is the page returned by a pager when traversing over a // collection of listeners. type ListenerPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of listeners has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r ListenerPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"listeners_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a ListenerPage struct is empty. func (r ListenerPage) IsEmpty() (bool, error) { is, err := ExtractListeners(r) return len(is) == 0, err } // ExtractListeners accepts a Page struct, specifically a ListenerPage struct, // and extracts the elements into a slice of Listener structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractListeners(r pagination.Page) ([]Listener, error) { var s struct { Listeners []Listener `json:"listeners"` } err := (r.(ListenerPage)).ExtractInto(&s) return s.Listeners, err } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a listener. func (r commonResult) Extract() (*Listener, error) { var s struct { Listener *Listener `json:"listener"` } err := r.ExtractInto(&s) return s.Listener, err } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a Listener. type CreateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a Listener. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a Listener. type UpdateResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its // ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400375575ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/listenersdoc.go000066400000000000000000000000501335005366400406460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/listeners/testing// listeners unit tests package testing fixtures.go000066400000000000000000000202251335005366400417600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/listeners/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // ListenersListBody contains the canned body of a listeners list response. const ListenersListBody = ` { "listeners":[ { "id": "db902c0c-d5ff-4753-b465-668ad9656918", "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c", "name": "web", "description": "listener config for the web tier", "loadbalancers": [{"id": "53306cda-815d-4354-9444-59e09da9c3c5"}], "protocol": "HTTP", "protocol_port": 80, "default_pool_id": "fad389a3-9a4a-4762-a365-8c7038508b5d", "admin_state_up": true, "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"] }, { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c", "name": "db", "description": "listener config for the db tier", "loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], "protocol": "TCP", "protocol_port": 3306, "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e", "connection_limit": 2000, "admin_state_up": true, "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"] } ] } ` // SingleServerBody is the canned body of a Get request on an existing listener. const SingleListenerBody = ` { "listener": { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c", "name": "db", "description": "listener config for the db tier", "loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], "protocol": "TCP", "protocol_port": 3306, "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e", "connection_limit": 2000, "admin_state_up": true, "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"] } } ` // PostUpdateListenerBody is the canned response body of a Update request on an existing listener. const PostUpdateListenerBody = ` { "listener": { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c", "name": "NewListenerName", "description": "listener config for the db tier", "loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], "protocol": "TCP", "protocol_port": 3306, "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e", "connection_limit": 1000, "admin_state_up": true, "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"] } } ` var ( ListenerWeb = listeners.Listener{ ID: "db902c0c-d5ff-4753-b465-668ad9656918", TenantID: "310df60f-2a10-4ee5-9554-98393092194c", Name: "web", Description: "listener config for the web tier", Loadbalancers: []listeners.LoadBalancerID{{ID: "53306cda-815d-4354-9444-59e09da9c3c5"}}, Protocol: "HTTP", ProtocolPort: 80, DefaultPoolID: "fad389a3-9a4a-4762-a365-8c7038508b5d", AdminStateUp: true, DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", SniContainerRefs: []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"}, } ListenerDb = listeners.Listener{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", TenantID: "310df60f-2a10-4ee5-9554-98393092194c", Name: "db", Description: "listener config for the db tier", Loadbalancers: []listeners.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, Protocol: "TCP", ProtocolPort: 3306, DefaultPoolID: "41efe233-7591-43c5-9cf7-923964759f9e", ConnLimit: 2000, AdminStateUp: true, DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", SniContainerRefs: []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"}, } ListenerUpdated = listeners.Listener{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", TenantID: "310df60f-2a10-4ee5-9554-98393092194c", Name: "NewListenerName", Description: "listener config for the db tier", Loadbalancers: []listeners.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, Protocol: "TCP", ProtocolPort: 3306, DefaultPoolID: "41efe233-7591-43c5-9cf7-923964759f9e", ConnLimit: 1000, AdminStateUp: true, DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", SniContainerRefs: []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"}, } ) // HandleListenerListSuccessfully sets up the test server to respond to a listener List request. func HandleListenerListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/listeners", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, ListenersListBody) case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": fmt.Fprintf(w, `{ "listeners": [] }`) default: t.Fatalf("/v2.0/lbaas/listeners invoked with unexpected marker=[%s]", marker) } }) } // HandleListenerCreationSuccessfully sets up the test server to respond to a listener creation request // with a given response. func HandleListenerCreationSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/v2.0/lbaas/listeners", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "listener": { "loadbalancer_id": "79e05663-7f03-45d2-a092-8b94062f22ab", "protocol": "TCP", "name": "db", "admin_state_up": true, "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e", "protocol_port": 3306 } }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // HandleListenerGetSuccessfully sets up the test server to respond to a listener Get request. func HandleListenerGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, SingleListenerBody) }) } // HandleListenerDeletionSuccessfully sets up the test server to respond to a listener deletion request. func HandleListenerDeletionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleListenerUpdateSuccessfully sets up the test server to respond to a listener Update request. func HandleListenerUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, `{ "listener": { "name": "NewListenerName", "connection_limit": 1001 } }`) fmt.Fprintf(w, PostUpdateListenerBody) }) } requests_test.go000066400000000000000000000075511335005366400430300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/listeners/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestListListeners(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListenerListSuccessfully(t) pages := 0 err := listeners.List(fake.ServiceClient(), listeners.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := listeners.ExtractListeners(page) if err != nil { return false, err } if len(actual) != 2 { t.Fatalf("Expected 2 listeners, got %d", len(actual)) } th.CheckDeepEquals(t, ListenerWeb, actual[0]) th.CheckDeepEquals(t, ListenerDb, actual[1]) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListAllListeners(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListenerListSuccessfully(t) allPages, err := listeners.List(fake.ServiceClient(), listeners.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := listeners.ExtractListeners(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ListenerWeb, actual[0]) th.CheckDeepEquals(t, ListenerDb, actual[1]) } func TestCreateListener(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListenerCreationSuccessfully(t, SingleListenerBody) actual, err := listeners.Create(fake.ServiceClient(), listeners.CreateOpts{ Protocol: "TCP", Name: "db", LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", AdminStateUp: gophercloud.Enabled, DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", DefaultPoolID: "41efe233-7591-43c5-9cf7-923964759f9e", ProtocolPort: 3306, }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ListenerDb, *actual) } func TestRequiredCreateOpts(t *testing.T) { res := listeners.Create(fake.ServiceClient(), listeners.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo"}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar"}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar", Protocol: "bar"}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar", Protocol: "bar", ProtocolPort: 80}) if res.Err == nil { t.Fatalf("Expected error, got none") } } func TestGetListener(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListenerGetSuccessfully(t) client := fake.ServiceClient() actual, err := listeners.Get(client, "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, ListenerDb, *actual) } func TestDeleteListener(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListenerDeletionSuccessfully(t) res := listeners.Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") th.AssertNoErr(t, res.Err) } func TestUpdateListener(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListenerUpdateSuccessfully(t) client := fake.ServiceClient() i1001 := 1001 actual, err := listeners.Update(client, "4ec89087-d057-4e2c-911f-60a3b47ee304", listeners.UpdateOpts{ Name: "NewListenerName", ConnLimit: &i1001, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, ListenerUpdated, *actual) } urls.go000066400000000000000000000005311335005366400374150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/listenerspackage listeners import "github.com/gophercloud/gophercloud" const ( rootPath = "lbaas" resourcePath = "listeners" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } loadbalancers/000077500000000000000000000000001335005366400366645ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2doc.go000066400000000000000000000030671335005366400377660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/* Package loadbalancers provides information and interaction with Load Balancers of the LBaaS v2 extension for the OpenStack Networking service. Example to List Load Balancers listOpts := loadbalancers.ListOpts{ Provider: "haproxy", } allPages, err := loadbalancers.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allLoadbalancers, err := loadbalancers.ExtractLoadBalancers(allPages) if err != nil { panic(err) } for _, lb := range allLoadbalancers { fmt.Printf("%+v\n", lb) } Example to Create a Load Balancer createOpts := loadbalancers.CreateOpts{ Name: "db_lb", AdminStateUp: gophercloud.Enabled, VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", VipAddress: "10.30.176.48", Flavor: "medium", Provider: "haproxy", } lb, err := loadbalancers.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Load Balancer lbID := "d67d56a6-4a86-4688-a282-f46444705c64" i1001 := 1001 updateOpts := loadbalancers.UpdateOpts{ Name: "new-name", } lb, err := loadbalancers.Update(networkClient, lbID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Load Balancers lbID := "d67d56a6-4a86-4688-a282-f46444705c64" err := loadbalancers.Delete(networkClient, lbID).ExtractErr() if err != nil { panic(err) } Example to Get the Status of a Load Balancer lbID := "d67d56a6-4a86-4688-a282-f46444705c64" status, err := loadbalancers.GetStatuses(networkClient, LBID).Extract() if err != nil { panic(err) } */ package loadbalancers requests.go000066400000000000000000000157021335005366400410730ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/loadbalancerspackage loadbalancers import ( "fmt" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToLoadBalancerListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the Loadbalancer attributes you want to see returned. SortKey allows you to // sort by a particular attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { Description string `q:"description"` AdminStateUp *bool `q:"admin_state_up"` TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` ProvisioningStatus string `q:"provisioning_status"` VipAddress string `q:"vip_address"` VipPortID string `q:"vip_port_id"` VipSubnetID string `q:"vip_subnet_id"` ID string `q:"id"` OperatingStatus string `q:"operating_status"` Name string `q:"name"` Flavor string `q:"flavor"` Provider string `q:"provider"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToLoadBalancerListQuery formats a ListOpts into a query string. func (opts ListOpts) ToLoadBalancerListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // load balancers. It accepts a ListOpts struct, which allows you to filter // and sort the returned collection for greater efficiency. // // Default policy settings return only those load balancers that are owned by // the tenant who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { query, err := opts.ToLoadBalancerListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return LoadBalancerPage{pagination.LinkedPageBase{PageResult: r}} }) } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToLoadBalancerCreateMap() (map[string]interface{}, error) } // CreateOpts is the common options struct used in this package's Create // operation. type CreateOpts struct { // Human-readable name for the Loadbalancer. Does not have to be unique. Name string `json:"name,omitempty"` // Human-readable description for the Loadbalancer. Description string `json:"description,omitempty"` // The network on which to allocate the Loadbalancer's address. A tenant can // only create Loadbalancers on networks authorized by policy (e.g. networks // that belong to them or networks that are shared). VipSubnetID string `json:"vip_subnet_id" required:"true"` // TenantID is the UUID of the project who owns the Loadbalancer. // Only administrative users can specify a project UUID other than their own. TenantID string `json:"tenant_id,omitempty"` // ProjectID is the UUID of the project who owns the Loadbalancer. // Only administrative users can specify a project UUID other than their own. ProjectID string `json:"project_id,omitempty"` // The IP address of the Loadbalancer. VipAddress string `json:"vip_address,omitempty"` // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` // The UUID of a flavor. Flavor string `json:"flavor,omitempty"` // The name of the provider. Provider string `json:"provider,omitempty"` } // ToLoadBalancerCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToLoadBalancerCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "loadbalancer") } // Create is an operation which provisions a new loadbalancer based on the // configuration defined in the CreateOpts struct. Once the request is // validated and progress has started on the provisioning process, a // CreateResult will be returned. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToLoadBalancerCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular Loadbalancer based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToLoadBalancerUpdateMap() (map[string]interface{}, error) } // UpdateOpts is the common options struct used in this package's Update // operation. type UpdateOpts struct { // Human-readable name for the Loadbalancer. Does not have to be unique. Name string `json:"name,omitempty"` // Human-readable description for the Loadbalancer. Description string `json:"description,omitempty"` // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToLoadBalancerUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToLoadBalancerUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "loadbalancer") } // Update is an operation which modifies the attributes of the specified // LoadBalancer. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { b, err := opts.ToLoadBalancerUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) return } // Delete will permanently delete a particular LoadBalancer based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } // CascadingDelete is like `Delete`, but will also delete any of the load balancer's // children (listener, monitor, etc). // NOTE: This function will only work with Octavia load balancers; Neutron does not // support this. func CascadingDelete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { if c.Type != "load-balancer" { r.Err = fmt.Errorf("error prior to running cascade delete: only Octavia LBs supported") return } u := fmt.Sprintf("%s?cascade=true", resourceURL(c, id)) _, r.Err = c.Delete(u, nil) return } // GetStatuses will return the status of a particular LoadBalancer. func GetStatuses(c *gophercloud.ServiceClient, id string) (r GetStatusesResult) { _, r.Err = c.Get(statusRootURL(c, id), &r.Body, nil) return } results.go000066400000000000000000000110371335005366400407160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/loadbalancerspackage loadbalancers import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners" "github.com/gophercloud/gophercloud/pagination" ) // LoadBalancer is the primary load balancing configuration object that // specifies the virtual IP address on which client traffic is received, as well // as other details such as the load balancing method to be use, protocol, etc. type LoadBalancer struct { // Human-readable description for the Loadbalancer. Description string `json:"description"` // The administrative state of the Loadbalancer. // A valid value is true (UP) or false (DOWN). AdminStateUp bool `json:"admin_state_up"` // Owner of the LoadBalancer. TenantID string `json:"tenant_id"` // The provisioning status of the LoadBalancer. // This value is ACTIVE, PENDING_CREATE or ERROR. ProvisioningStatus string `json:"provisioning_status"` // The IP address of the Loadbalancer. VipAddress string `json:"vip_address"` // The UUID of the port associated with the IP address. VipPortID string `json:"vip_port_id"` // The UUID of the subnet on which to allocate the virtual IP for the // Loadbalancer address. VipSubnetID string `json:"vip_subnet_id"` // The unique ID for the LoadBalancer. ID string `json:"id"` // The operating status of the LoadBalancer. This value is ONLINE or OFFLINE. OperatingStatus string `json:"operating_status"` // Human-readable name for the LoadBalancer. Does not have to be unique. Name string `json:"name"` // The UUID of a flavor if set. Flavor string `json:"flavor"` // The name of the provider. Provider string `json:"provider"` // Listeners are the listeners related to this Loadbalancer. Listeners []listeners.Listener `json:"listeners"` } // StatusTree represents the status of a loadbalancer. type StatusTree struct { Loadbalancer *LoadBalancer `json:"loadbalancer"` } // LoadBalancerPage is the page returned by a pager when traversing over a // collection of load balancers. type LoadBalancerPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of load balancers has // reached the end of a page and the pager seeks to traverse over a new one. // In order to do this, it needs to construct the next page's URL. func (r LoadBalancerPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"loadbalancers_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a LoadBalancerPage struct is empty. func (r LoadBalancerPage) IsEmpty() (bool, error) { is, err := ExtractLoadBalancers(r) return len(is) == 0, err } // ExtractLoadBalancers accepts a Page struct, specifically a LoadbalancerPage // struct, and extracts the elements into a slice of LoadBalancer structs. In // other words, a generic collection is mapped into a relevant slice. func ExtractLoadBalancers(r pagination.Page) ([]LoadBalancer, error) { var s struct { LoadBalancers []LoadBalancer `json:"loadbalancers"` } err := (r.(LoadBalancerPage)).ExtractInto(&s) return s.LoadBalancers, err } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a loadbalancer. func (r commonResult) Extract() (*LoadBalancer, error) { var s struct { LoadBalancer *LoadBalancer `json:"loadbalancer"` } err := r.ExtractInto(&s) return s.LoadBalancer, err } // GetStatusesResult represents the result of a GetStatuses operation. // Call its Extract method to interpret it as a StatusTree. type GetStatusesResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts the status of // a Loadbalancer. func (r GetStatusesResult) Extract() (*StatusTree, error) { var s struct { Statuses *StatusTree `json:"statuses"` } err := r.ExtractInto(&s) return s.Statuses, err } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a LoadBalancer. type CreateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a LoadBalancer. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a LoadBalancer. type UpdateResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its // ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400403415ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/loadbalancersdoc.go000066400000000000000000000000541335005366400414340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing// loadbalancers unit tests package testing fixtures.go000066400000000000000000000236731335005366400425540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" ) // LoadbalancersListBody contains the canned body of a loadbalancer list response. const LoadbalancersListBody = ` { "loadbalancers":[ { "id": "c331058c-6a40-4144-948e-b9fb1df9db4b", "tenant_id": "54030507-44f7-473c-9342-b4d14a95f692", "name": "web_lb", "description": "lb config for the web tier", "vip_subnet_id": "8a49c438-848f-467b-9655-ea1548708154", "vip_address": "10.30.176.47", "vip_port_id": "2a22e552-a347-44fd-b530-1f2b1b2a6735", "flavor": "small", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "ACTIVE", "operating_status": "ONLINE" }, { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", "tenant_id": "54030507-44f7-473c-9342-b4d14a95f692", "name": "db_lb", "description": "lb config for the db tier", "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", "vip_address": "10.30.176.48", "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", "flavor": "medium", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "PENDING_CREATE", "operating_status": "OFFLINE" } ] } ` // SingleLoadbalancerBody is the canned body of a Get request on an existing loadbalancer. const SingleLoadbalancerBody = ` { "loadbalancer": { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", "tenant_id": "54030507-44f7-473c-9342-b4d14a95f692", "name": "db_lb", "description": "lb config for the db tier", "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", "vip_address": "10.30.176.48", "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", "flavor": "medium", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "PENDING_CREATE", "operating_status": "OFFLINE" } } ` // PostUpdateLoadbalancerBody is the canned response body of a Update request on an existing loadbalancer. const PostUpdateLoadbalancerBody = ` { "loadbalancer": { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", "tenant_id": "54030507-44f7-473c-9342-b4d14a95f692", "name": "NewLoadbalancerName", "description": "lb config for the db tier", "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", "vip_address": "10.30.176.48", "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", "flavor": "medium", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "PENDING_CREATE", "operating_status": "OFFLINE" } } ` // SingleLoadbalancerBody is the canned body of a Get request on an existing loadbalancer. const LoadbalancerStatuesesTree = ` { "statuses" : { "loadbalancer": { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", "name": "db_lb", "provisioning_status": "PENDING_UPDATE", "operating_status": "ACTIVE", "listeners": [{ "id": "db902c0c-d5ff-4753-b465-668ad9656918", "name": "db", "provisioning_status": "PENDING_UPDATE", "pools": [{ "id": "fad389a3-9a4a-4762-a365-8c7038508b5d", "name": "db", "provisioning_status": "PENDING_UPDATE", "healthmonitor": { "id": "67306cda-815d-4354-9fe4-59e09da9c3c5", "type":"PING", "provisioning_status": "PENDING_UPDATE" }, "members":[{ "id": "2a280670-c202-4b0b-a562-34077415aabf", "name": "db", "address": "10.0.2.11", "protocol_port": 80, "provisioning_status": "PENDING_UPDATE" }] }] }] } } } ` var ( LoadbalancerWeb = loadbalancers.LoadBalancer{ ID: "c331058c-6a40-4144-948e-b9fb1df9db4b", TenantID: "54030507-44f7-473c-9342-b4d14a95f692", Name: "web_lb", Description: "lb config for the web tier", VipSubnetID: "8a49c438-848f-467b-9655-ea1548708154", VipAddress: "10.30.176.47", VipPortID: "2a22e552-a347-44fd-b530-1f2b1b2a6735", Flavor: "small", Provider: "haproxy", AdminStateUp: true, ProvisioningStatus: "ACTIVE", OperatingStatus: "ONLINE", } LoadbalancerDb = loadbalancers.LoadBalancer{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", TenantID: "54030507-44f7-473c-9342-b4d14a95f692", Name: "db_lb", Description: "lb config for the db tier", VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", VipAddress: "10.30.176.48", VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55", Flavor: "medium", Provider: "haproxy", AdminStateUp: true, ProvisioningStatus: "PENDING_CREATE", OperatingStatus: "OFFLINE", } LoadbalancerUpdated = loadbalancers.LoadBalancer{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", TenantID: "54030507-44f7-473c-9342-b4d14a95f692", Name: "NewLoadbalancerName", Description: "lb config for the db tier", VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", VipAddress: "10.30.176.48", VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55", Flavor: "medium", Provider: "haproxy", AdminStateUp: true, ProvisioningStatus: "PENDING_CREATE", OperatingStatus: "OFFLINE", } LoadbalancerStatusesTree = loadbalancers.LoadBalancer{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", Name: "db_lb", ProvisioningStatus: "PENDING_UPDATE", OperatingStatus: "ACTIVE", Listeners: []listeners.Listener{{ ID: "db902c0c-d5ff-4753-b465-668ad9656918", Name: "db", ProvisioningStatus: "PENDING_UPDATE", Pools: []pools.Pool{{ ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", Name: "db", ProvisioningStatus: "PENDING_UPDATE", Monitor: monitors.Monitor{ ID: "67306cda-815d-4354-9fe4-59e09da9c3c5", Type: "PING", ProvisioningStatus: "PENDING_UPDATE", }, Members: []pools.Member{{ ID: "2a280670-c202-4b0b-a562-34077415aabf", Name: "db", Address: "10.0.2.11", ProtocolPort: 80, ProvisioningStatus: "PENDING_UPDATE", }}, }}, }}, } ) // HandleLoadbalancerListSuccessfully sets up the test server to respond to a loadbalancer List request. func HandleLoadbalancerListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, LoadbalancersListBody) case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": fmt.Fprintf(w, `{ "loadbalancers": [] }`) default: t.Fatalf("/v2.0/lbaas/loadbalancers invoked with unexpected marker=[%s]", marker) } }) } // HandleLoadbalancerCreationSuccessfully sets up the test server to respond to a loadbalancer creation request // with a given response. func HandleLoadbalancerCreationSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "loadbalancer": { "name": "db_lb", "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", "vip_address": "10.30.176.48", "flavor": "medium", "provider": "haproxy", "admin_state_up": true } }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // HandleLoadbalancerGetSuccessfully sets up the test server to respond to a loadbalancer Get request. func HandleLoadbalancerGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, SingleLoadbalancerBody) }) } // HandleLoadbalancerGetStatusesTree sets up the test server to respond to a loadbalancer Get statuses tree request. func HandleLoadbalancerGetStatusesTree(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab/statuses", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, LoadbalancerStatuesesTree) }) } // HandleLoadbalancerDeletionSuccessfully sets up the test server to respond to a loadbalancer deletion request. func HandleLoadbalancerDeletionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleLoadbalancerUpdateSuccessfully sets up the test server to respond to a loadbalancer Update request. func HandleLoadbalancerUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, `{ "loadbalancer": { "name": "NewLoadbalancerName" } }`) fmt.Fprintf(w, PostUpdateLoadbalancerBody) }) } requests_test.go000066400000000000000000000111521335005366400436020ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestListLoadbalancers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleLoadbalancerListSuccessfully(t) pages := 0 err := loadbalancers.List(fake.ServiceClient(), loadbalancers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := loadbalancers.ExtractLoadBalancers(page) if err != nil { return false, err } if len(actual) != 2 { t.Fatalf("Expected 2 loadbalancers, got %d", len(actual)) } th.CheckDeepEquals(t, LoadbalancerWeb, actual[0]) th.CheckDeepEquals(t, LoadbalancerDb, actual[1]) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListAllLoadbalancers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleLoadbalancerListSuccessfully(t) allPages, err := loadbalancers.List(fake.ServiceClient(), loadbalancers.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := loadbalancers.ExtractLoadBalancers(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, LoadbalancerWeb, actual[0]) th.CheckDeepEquals(t, LoadbalancerDb, actual[1]) } func TestCreateLoadbalancer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleLoadbalancerCreationSuccessfully(t, SingleLoadbalancerBody) actual, err := loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{ Name: "db_lb", AdminStateUp: gophercloud.Enabled, VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", VipAddress: "10.30.176.48", Flavor: "medium", Provider: "haproxy", }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, LoadbalancerDb, *actual) } func TestRequiredCreateOpts(t *testing.T) { res := loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo"}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo", Description: "bar"}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo", Description: "bar", VipAddress: "bar"}) if res.Err == nil { t.Fatalf("Expected error, got none") } } func TestGetLoadbalancer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleLoadbalancerGetSuccessfully(t) client := fake.ServiceClient() actual, err := loadbalancers.Get(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, LoadbalancerDb, *actual) } func TestGetLoadbalancerStatusesTree(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleLoadbalancerGetStatusesTree(t) client := fake.ServiceClient() actual, err := loadbalancers.GetStatuses(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, LoadbalancerStatusesTree, *(actual.Loadbalancer)) } func TestDeleteLoadbalancer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleLoadbalancerDeletionSuccessfully(t) res := loadbalancers.Delete(fake.ServiceClient(), "36e08a3e-a78f-4b40-a229-1e7e23eee1ab") th.AssertNoErr(t, res.Err) } func TestUpdateLoadbalancer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleLoadbalancerUpdateSuccessfully(t) client := fake.ServiceClient() actual, err := loadbalancers.Update(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", loadbalancers.UpdateOpts{ Name: "NewLoadbalancerName", }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, LoadbalancerUpdated, *actual) } func TestCascadingDeleteLoadbalancer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleLoadbalancerDeletionSuccessfully(t) sc := fake.ServiceClient() sc.Type = "network" err := loadbalancers.CascadingDelete(sc, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").ExtractErr() if err == nil { t.Fatalf("expected error running CascadingDelete with Neutron service client but didn't get one") } sc.Type = "load-balancer" err = loadbalancers.CascadingDelete(sc, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").ExtractErr() th.AssertNoErr(t, err) } urls.go000066400000000000000000000010011335005366400401700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/loadbalancerspackage loadbalancers import "github.com/gophercloud/gophercloud" const ( rootPath = "lbaas" resourcePath = "loadbalancers" statusPath = "statuses" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } func statusRootURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id, statusPath) } monitors/000077500000000000000000000000001335005366400357445ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2doc.go000066400000000000000000000027431335005366400370460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/monitors/* Package monitors provides information and interaction with Monitors of the LBaaS v2 extension for the OpenStack Networking service. Example to List Monitors listOpts := monitors.ListOpts{ PoolID: "c79a4468-d788-410c-bf79-9a8ef6354852", } allPages, err := monitors.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allMonitors, err := monitors.ExtractMonitors(allPages) if err != nil { panic(err) } for _, monitor := range allMonitors { fmt.Printf("%+v\n", monitor) } Example to Create a Monitor createOpts := monitors.CreateOpts{ Type: "HTTP", Name: "db", PoolID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", Delay: 20, Timeout: 10, MaxRetries: 5, URLPath: "/check", ExpectedCodes: "200-299", } monitor, err := monitors.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Monitor monitorID := "d67d56a6-4a86-4688-a282-f46444705c64" updateOpts := monitors.UpdateOpts{ Name: "NewHealthmonitorName", Delay: 3, Timeout: 20, MaxRetries: 10, URLPath: "/another_check", ExpectedCodes: "301", } monitor, err := monitors.Update(networkClient, monitorID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Monitor monitorID := "d67d56a6-4a86-4688-a282-f46444705c64" err := monitors.Delete(networkClient, monitorID).ExtractErr() if err != nil { panic(err) } */ package monitors requests.go000066400000000000000000000207761335005366400401620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/monitorspackage monitors import ( "fmt" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToMonitorListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the Monitor attributes you want to see returned. SortKey allows you to // sort by a particular Monitor attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { ID string `q:"id"` Name string `q:"name"` TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` PoolID string `q:"pool_id"` Type string `q:"type"` Delay int `q:"delay"` Timeout int `q:"timeout"` MaxRetries int `q:"max_retries"` HTTPMethod string `q:"http_method"` URLPath string `q:"url_path"` ExpectedCodes string `q:"expected_codes"` AdminStateUp *bool `q:"admin_state_up"` Status string `q:"status"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToMonitorListQuery formats a ListOpts into a query string. func (opts ListOpts) ToMonitorListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return "", err } return q.String(), nil } // List returns a Pager which allows you to iterate over a collection of // health monitors. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. // // Default policy settings return only those health monitors that are owned by the // tenant who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { query, err := opts.ToMonitorListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return MonitorPage{pagination.LinkedPageBase{PageResult: r}} }) } // Constants that represent approved monitoring types. const ( TypePING = "PING" TypeTCP = "TCP" TypeHTTP = "HTTP" TypeHTTPS = "HTTPS" ) var ( errDelayMustGETimeout = fmt.Errorf("Delay must be greater than or equal to timeout") ) // CreateOptsBuilder allows extensions to add additional parameters to the // List request. type CreateOptsBuilder interface { ToMonitorCreateMap() (map[string]interface{}, error) } // CreateOpts is the common options struct used in this package's Create // operation. type CreateOpts struct { // The Pool to Monitor. PoolID string `json:"pool_id" required:"true"` // The type of probe, which is PING, TCP, HTTP, or HTTPS, that is // sent by the load balancer to verify the member state. Type string `json:"type" required:"true"` // The time, in seconds, between sending probes to members. Delay int `json:"delay" required:"true"` // Maximum number of seconds for a Monitor to wait for a ping reply // before it times out. The value must be less than the delay value. Timeout int `json:"timeout" required:"true"` // Number of permissible ping failures before changing the member's // status to INACTIVE. Must be a number between 1 and 10. MaxRetries int `json:"max_retries" required:"true"` // URI path that will be accessed if Monitor type is HTTP or HTTPS. // Required for HTTP(S) types. URLPath string `json:"url_path,omitempty"` // The HTTP method used for requests by the Monitor. If this attribute // is not specified, it defaults to "GET". Required for HTTP(S) types. HTTPMethod string `json:"http_method,omitempty"` // Expected HTTP codes for a passing HTTP(S) Monitor. You can either specify // a single status like "200", or a range like "200-202". Required for HTTP(S) // types. ExpectedCodes string `json:"expected_codes,omitempty"` // TenantID is the UUID of the project who owns the Monitor. // Only administrative users can specify a project UUID other than their own. TenantID string `json:"tenant_id,omitempty"` // ProjectID is the UUID of the project who owns the Monitor. // Only administrative users can specify a project UUID other than their own. ProjectID string `json:"project_id,omitempty"` // The Name of the Monitor. Name string `json:"name,omitempty"` // The administrative state of the Monitor. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToMonitorCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToMonitorCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "healthmonitor") if err != nil { return nil, err } switch opts.Type { case TypeHTTP, TypeHTTPS: switch opts.URLPath { case "": return nil, fmt.Errorf("URLPath must be provided for HTTP and HTTPS") } switch opts.ExpectedCodes { case "": return nil, fmt.Errorf("ExpectedCodes must be provided for HTTP and HTTPS") } } return b, nil } /* Create is an operation which provisions a new Health Monitor. There are different types of Monitor you can provision: PING, TCP or HTTP(S). Below are examples of how to create each one. Here is an example config struct to use when creating a PING or TCP Monitor: CreateOpts{Type: TypePING, Delay: 20, Timeout: 10, MaxRetries: 3} CreateOpts{Type: TypeTCP, Delay: 20, Timeout: 10, MaxRetries: 3} Here is an example config struct to use when creating a HTTP(S) Monitor: CreateOpts{Type: TypeHTTP, Delay: 20, Timeout: 10, MaxRetries: 3, HttpMethod: "HEAD", ExpectedCodes: "200", PoolID: "2c946bfc-1804-43ab-a2ff-58f6a762b505"} */ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToMonitorCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular Health Monitor based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToMonitorUpdateMap() (map[string]interface{}, error) } // UpdateOpts is the common options struct used in this package's Update // operation. type UpdateOpts struct { // The time, in seconds, between sending probes to members. Delay int `json:"delay,omitempty"` // Maximum number of seconds for a Monitor to wait for a ping reply // before it times out. The value must be less than the delay value. Timeout int `json:"timeout,omitempty"` // Number of permissible ping failures before changing the member's // status to INACTIVE. Must be a number between 1 and 10. MaxRetries int `json:"max_retries,omitempty"` // URI path that will be accessed if Monitor type is HTTP or HTTPS. // Required for HTTP(S) types. URLPath string `json:"url_path,omitempty"` // The HTTP method used for requests by the Monitor. If this attribute // is not specified, it defaults to "GET". Required for HTTP(S) types. HTTPMethod string `json:"http_method,omitempty"` // Expected HTTP codes for a passing HTTP(S) Monitor. You can either specify // a single status like "200", or a range like "200-202". Required for HTTP(S) // types. ExpectedCodes string `json:"expected_codes,omitempty"` // The Name of the Monitor. Name string `json:"name,omitempty"` // The administrative state of the Monitor. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToMonitorUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToMonitorUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "healthmonitor") } // Update is an operation which modifies the attributes of the specified // Monitor. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToMonitorUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) return } // Delete will permanently delete a particular Monitor based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } results.go000066400000000000000000000114421335005366400377760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/monitorspackage monitors import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type PoolID struct { ID string `json:"id"` } // Monitor represents a load balancer health monitor. A health monitor is used // to determine whether or not back-end members of the VIP's pool are usable // for processing a request. A pool can have several health monitors associated // with it. There are different types of health monitors supported: // // PING: used to ping the members using ICMP. // TCP: used to connect to the members using TCP. // HTTP: used to send an HTTP request to the member. // HTTPS: used to send a secure HTTP request to the member. // // When a pool has several monitors associated with it, each member of the pool // is monitored by all these monitors. If any monitor declares the member as // unhealthy, then the member status is changed to INACTIVE and the member // won't participate in its pool's load balancing. In other words, ALL monitors // must declare the member to be healthy for it to stay ACTIVE. type Monitor struct { // The unique ID for the Monitor. ID string `json:"id"` // The Name of the Monitor. Name string `json:"name"` // TenantID is the owner of the Monitor. TenantID string `json:"tenant_id"` // The type of probe sent by the load balancer to verify the member state, // which is PING, TCP, HTTP, or HTTPS. Type string `json:"type"` // The time, in seconds, between sending probes to members. Delay int `json:"delay"` // The maximum number of seconds for a monitor to wait for a connection to be // established before it times out. This value must be less than the delay // value. Timeout int `json:"timeout"` // Number of allowed connection failures before changing the status of the // member to INACTIVE. A valid value is from 1 to 10. MaxRetries int `json:"max_retries"` // The HTTP method that the monitor uses for requests. HTTPMethod string `json:"http_method"` // The HTTP path of the request sent by the monitor to test the health of a // member. Must be a string beginning with a forward slash (/). URLPath string `json:"url_path" ` // Expected HTTP codes for a passing HTTP(S) monitor. ExpectedCodes string `json:"expected_codes"` // The administrative state of the health monitor, which is up (true) or // down (false). AdminStateUp bool `json:"admin_state_up"` // The status of the health monitor. Indicates whether the health monitor is // operational. Status string `json:"status"` // List of pools that are associated with the health monitor. Pools []PoolID `json:"pools"` // The provisioning status of the monitor. // This value is ACTIVE, PENDING_* or ERROR. ProvisioningStatus string `json:"provisioning_status"` } // MonitorPage is the page returned by a pager when traversing over a // collection of health monitors. type MonitorPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of monitors has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r MonitorPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"healthmonitors_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a MonitorPage struct is empty. func (r MonitorPage) IsEmpty() (bool, error) { is, err := ExtractMonitors(r) return len(is) == 0, err } // ExtractMonitors accepts a Page struct, specifically a MonitorPage struct, // and extracts the elements into a slice of Monitor structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractMonitors(r pagination.Page) ([]Monitor, error) { var s struct { Monitors []Monitor `json:"healthmonitors"` } err := (r.(MonitorPage)).ExtractInto(&s) return s.Monitors, err } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a monitor. func (r commonResult) Extract() (*Monitor, error) { var s struct { Monitor *Monitor `json:"healthmonitor"` } err := r.ExtractInto(&s) return s.Monitor, err } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a Monitor. type CreateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a Monitor. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a Monitor. type UpdateResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its // ExtractErr method to determine if the result succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400374215ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/monitorsdoc.go000066400000000000000000000000471335005366400405160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/monitors/testing// monitors unit tests package testing fixtures.go000066400000000000000000000146621335005366400416320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/monitors/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // HealthmonitorsListBody contains the canned body of a healthmonitor list response. const HealthmonitorsListBody = ` { "healthmonitors":[ { "admin_state_up":true, "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", "delay":10, "name":"web", "max_retries":1, "timeout":1, "type":"PING", "pools": [{"id": "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}], "id":"466c8345-28d8-4f84-a246-e04380b0461d" }, { "admin_state_up":true, "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", "delay":5, "name":"db", "expected_codes":"200", "max_retries":2, "http_method":"GET", "timeout":2, "url_path":"/", "type":"HTTP", "pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}], "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7" } ] } ` // SingleHealthmonitorBody is the canned body of a Get request on an existing healthmonitor. const SingleHealthmonitorBody = ` { "healthmonitor": { "admin_state_up":true, "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", "delay":5, "name":"db", "expected_codes":"200", "max_retries":2, "http_method":"GET", "timeout":2, "url_path":"/", "type":"HTTP", "pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}], "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7" } } ` // PostUpdateHealthmonitorBody is the canned response body of a Update request on an existing healthmonitor. const PostUpdateHealthmonitorBody = ` { "healthmonitor": { "admin_state_up":true, "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", "delay":3, "name":"NewHealthmonitorName", "expected_codes":"301", "max_retries":10, "http_method":"GET", "timeout":20, "url_path":"/another_check", "type":"HTTP", "pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}], "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7" } } ` var ( HealthmonitorWeb = monitors.Monitor{ AdminStateUp: true, Name: "web", TenantID: "83657cfcdfe44cd5920adaf26c48ceea", Delay: 10, MaxRetries: 1, Timeout: 1, Type: "PING", ID: "466c8345-28d8-4f84-a246-e04380b0461d", Pools: []monitors.PoolID{{ID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}}, } HealthmonitorDb = monitors.Monitor{ AdminStateUp: true, Name: "db", TenantID: "83657cfcdfe44cd5920adaf26c48ceea", Delay: 5, ExpectedCodes: "200", MaxRetries: 2, Timeout: 2, URLPath: "/", Type: "HTTP", HTTPMethod: "GET", ID: "5d4b5228-33b0-4e60-b225-9b727c1a20e7", Pools: []monitors.PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}}, } HealthmonitorUpdated = monitors.Monitor{ AdminStateUp: true, Name: "NewHealthmonitorName", TenantID: "83657cfcdfe44cd5920adaf26c48ceea", Delay: 3, ExpectedCodes: "301", MaxRetries: 10, Timeout: 20, URLPath: "/another_check", Type: "HTTP", HTTPMethod: "GET", ID: "5d4b5228-33b0-4e60-b225-9b727c1a20e7", Pools: []monitors.PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}}, } ) // HandleHealthmonitorListSuccessfully sets up the test server to respond to a healthmonitor List request. func HandleHealthmonitorListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, HealthmonitorsListBody) case "556c8345-28d8-4f84-a246-e04380b0461d": fmt.Fprintf(w, `{ "healthmonitors": [] }`) default: t.Fatalf("/v2.0/lbaas/healthmonitors invoked with unexpected marker=[%s]", marker) } }) } // HandleHealthmonitorCreationSuccessfully sets up the test server to respond to a healthmonitor creation request // with a given response. func HandleHealthmonitorCreationSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "healthmonitor": { "type":"HTTP", "pool_id":"84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", "tenant_id":"453105b9-1754-413f-aab1-55f1af620750", "delay":20, "name":"db", "timeout":10, "max_retries":5, "url_path":"/check", "expected_codes":"200-299" } }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // HandleHealthmonitorGetSuccessfully sets up the test server to respond to a healthmonitor Get request. func HandleHealthmonitorGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors/5d4b5228-33b0-4e60-b225-9b727c1a20e7", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, SingleHealthmonitorBody) }) } // HandleHealthmonitorDeletionSuccessfully sets up the test server to respond to a healthmonitor deletion request. func HandleHealthmonitorDeletionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors/5d4b5228-33b0-4e60-b225-9b727c1a20e7", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleHealthmonitorUpdateSuccessfully sets up the test server to respond to a healthmonitor Update request. func HandleHealthmonitorUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors/5d4b5228-33b0-4e60-b225-9b727c1a20e7", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, `{ "healthmonitor": { "name": "NewHealthmonitorName", "delay": 3, "timeout": 20, "max_retries": 10, "url_path": "/another_check", "expected_codes": "301" } }`) fmt.Fprintf(w, PostUpdateHealthmonitorBody) }) } requests_test.go000066400000000000000000000100311335005366400426550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/monitors/testingpackage testing import ( "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestListHealthmonitors(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleHealthmonitorListSuccessfully(t) pages := 0 err := monitors.List(fake.ServiceClient(), monitors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := monitors.ExtractMonitors(page) if err != nil { return false, err } if len(actual) != 2 { t.Fatalf("Expected 2 healthmonitors, got %d", len(actual)) } th.CheckDeepEquals(t, HealthmonitorWeb, actual[0]) th.CheckDeepEquals(t, HealthmonitorDb, actual[1]) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListAllHealthmonitors(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleHealthmonitorListSuccessfully(t) allPages, err := monitors.List(fake.ServiceClient(), monitors.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := monitors.ExtractMonitors(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, HealthmonitorWeb, actual[0]) th.CheckDeepEquals(t, HealthmonitorDb, actual[1]) } func TestCreateHealthmonitor(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleHealthmonitorCreationSuccessfully(t, SingleHealthmonitorBody) actual, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{ Type: "HTTP", Name: "db", PoolID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", TenantID: "453105b9-1754-413f-aab1-55f1af620750", Delay: 20, Timeout: 10, MaxRetries: 5, URLPath: "/check", ExpectedCodes: "200-299", }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, HealthmonitorDb, *actual) } func TestRequiredCreateOpts(t *testing.T) { res := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = monitors.Create(fake.ServiceClient(), monitors.CreateOpts{Type: monitors.TypeHTTP}) if res.Err == nil { t.Fatalf("Expected error, got none") } } func TestGetHealthmonitor(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleHealthmonitorGetSuccessfully(t) client := fake.ServiceClient() actual, err := monitors.Get(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, HealthmonitorDb, *actual) } func TestDeleteHealthmonitor(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleHealthmonitorDeletionSuccessfully(t) res := monitors.Delete(fake.ServiceClient(), "5d4b5228-33b0-4e60-b225-9b727c1a20e7") th.AssertNoErr(t, res.Err) } func TestUpdateHealthmonitor(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleHealthmonitorUpdateSuccessfully(t) client := fake.ServiceClient() actual, err := monitors.Update(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7", monitors.UpdateOpts{ Name: "NewHealthmonitorName", Delay: 3, Timeout: 20, MaxRetries: 10, URLPath: "/another_check", ExpectedCodes: "301", }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, HealthmonitorUpdated, *actual) } func TestDelayMustBeGreaterOrEqualThanTimeout(t *testing.T) { _, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{ Type: "HTTP", PoolID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d", Delay: 1, Timeout: 10, MaxRetries: 5, URLPath: "/check", ExpectedCodes: "200-299", }).Extract() if err == nil { t.Fatalf("Expected error, got none") } _, err = monitors.Update(fake.ServiceClient(), "453105b9-1754-413f-aab1-55f1af620750", monitors.UpdateOpts{ Delay: 1, Timeout: 10, }).Extract() if err == nil { t.Fatalf("Expected error, got none") } } urls.go000066400000000000000000000005351335005366400372630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/monitorspackage monitors import "github.com/gophercloud/gophercloud" const ( rootPath = "lbaas" resourcePath = "healthmonitors" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } pools/000077500000000000000000000000001335005366400352265ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2doc.go000066400000000000000000000050611335005366400363240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/pools/* Package pools provides information and interaction with Pools and Members of the LBaaS v2 extension for the OpenStack Networking service. Example to List Pools listOpts := pools.ListOpts{ LoadbalancerID: "c79a4468-d788-410c-bf79-9a8ef6354852", } allPages, err := pools.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allPools, err := pools.ExtractMonitors(allPages) if err != nil { panic(err) } for _, pools := range allPools { fmt.Printf("%+v\n", pool) } Example to Create a Pool createOpts := pools.CreateOpts{ LBMethod: pools.LBMethodRoundRobin, Protocol: "HTTP", Name: "Example pool", LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", } pool, err := pools.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Pool poolID := "d67d56a6-4a86-4688-a282-f46444705c64" updateOpts := pools.UpdateOpts{ Name: "new-name", } pool, err := pools.Update(networkClient, poolID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Pool poolID := "d67d56a6-4a86-4688-a282-f46444705c64" err := pools.Delete(networkClient, poolID).ExtractErr() if err != nil { panic(err) } Example to List Pool Members poolID := "d67d56a6-4a86-4688-a282-f46444705c64" listOpts := pools.ListMemberOpts{ ProtocolPort: 80, } allPages, err := pools.ListMembers(networkClient, poolID, listOpts).AllPages() if err != nil { panic(err) } allMembers, err := pools.ExtractMembers(allPages) if err != nil { panic(err) } for _, member := allMembers { fmt.Printf("%+v\n", member) } Example to Create a Member poolID := "d67d56a6-4a86-4688-a282-f46444705c64" createOpts := pools.CreateMemberOpts{ Name: "db", SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", Address: "10.0.2.11", ProtocolPort: 80, Weight: 10, } member, err := pools.CreateMember(networkClient, poolID, createOpts).Extract() if err != nil { panic(err) } Example to Update a Member poolID := "d67d56a6-4a86-4688-a282-f46444705c64" memberID := "64dba99f-8af8-4200-8882-e32a0660f23e" updateOpts := pools.UpdateMemberOpts{ Name: "new-name", Weight: 4, } member, err := pools.UpdateMember(networkClient, poolID, memberID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Member poolID := "d67d56a6-4a86-4688-a282-f46444705c64" memberID := "64dba99f-8af8-4200-8882-e32a0660f23e" err := pools.DeleteMember(networkClient, poolID, memberID).ExtractErr() if err != nil { panic(err) } */ package pools requests.go000066400000000000000000000310501335005366400374270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/poolspackage pools import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToPoolListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the Pool attributes you want to see returned. SortKey allows you to // sort by a particular Pool attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { LBMethod string `q:"lb_algorithm"` Protocol string `q:"protocol"` TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` AdminStateUp *bool `q:"admin_state_up"` Name string `q:"name"` ID string `q:"id"` LoadbalancerID string `q:"loadbalancer_id"` ListenerID string `q:"listener_id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToPoolListQuery formats a ListOpts into a query string. func (opts ListOpts) ToPoolListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // pools. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. // // Default policy settings return only those pools that are owned by the // tenant who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { query, err := opts.ToPoolListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return PoolPage{pagination.LinkedPageBase{PageResult: r}} }) } type LBMethod string type Protocol string // Supported attributes for create/update operations. const ( LBMethodRoundRobin LBMethod = "ROUND_ROBIN" LBMethodLeastConnections LBMethod = "LEAST_CONNECTIONS" LBMethodSourceIp LBMethod = "SOURCE_IP" ProtocolTCP Protocol = "TCP" ProtocolHTTP Protocol = "HTTP" ProtocolHTTPS Protocol = "HTTPS" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToPoolCreateMap() (map[string]interface{}, error) } // CreateOpts is the common options struct used in this package's Create // operation. type CreateOpts struct { // The algorithm used to distribute load between the members of the pool. The // current specification supports LBMethodRoundRobin, LBMethodLeastConnections // and LBMethodSourceIp as valid values for this attribute. LBMethod LBMethod `json:"lb_algorithm" required:"true"` // The protocol used by the pool members, you can use either // ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS. Protocol Protocol `json:"protocol" required:"true"` // The Loadbalancer on which the members of the pool will be associated with. // Note: one of LoadbalancerID or ListenerID must be provided. LoadbalancerID string `json:"loadbalancer_id,omitempty" xor:"ListenerID"` // The Listener on which the members of the pool will be associated with. // Note: one of LoadbalancerID or ListenerID must be provided. ListenerID string `json:"listener_id,omitempty" xor:"LoadbalancerID"` // TenantID is the UUID of the project who owns the Pool. // Only administrative users can specify a project UUID other than their own. TenantID string `json:"tenant_id,omitempty"` // ProjectID is the UUID of the project who owns the Pool. // Only administrative users can specify a project UUID other than their own. ProjectID string `json:"project_id,omitempty"` // Name of the pool. Name string `json:"name,omitempty"` // Human-readable description for the pool. Description string `json:"description,omitempty"` // Persistence is the session persistence of the pool. // Omit this field to prevent session persistence. Persistence *SessionPersistence `json:"session_persistence,omitempty"` // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToPoolCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToPoolCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "pool") } // Create accepts a CreateOpts struct and uses the values to create a new // load balancer pool. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToPoolCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular pool based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToPoolUpdateMap() (map[string]interface{}, error) } // UpdateOpts is the common options struct used in this package's Update // operation. type UpdateOpts struct { // Name of the pool. Name string `json:"name,omitempty"` // Human-readable description for the pool. Description string `json:"description,omitempty"` // The algorithm used to distribute load between the members of the pool. The // current specification supports LBMethodRoundRobin, LBMethodLeastConnections // and LBMethodSourceIp as valid values for this attribute. LBMethod LBMethod `json:"lb_algorithm,omitempty"` // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToPoolUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToPoolUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "pool") } // Update allows pools to be updated. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToPoolUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete will permanently delete a particular pool based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } // ListMemberOptsBuilder allows extensions to add additional parameters to the // ListMembers request. type ListMembersOptsBuilder interface { ToMembersListQuery() (string, error) } // ListMembersOpts allows the filtering and sorting of paginated collections // through the API. Filtering is achieved by passing in struct field values // that map to the Member attributes you want to see returned. SortKey allows // you to sort by a particular Member attribute. SortDir sets the direction, // and is either `asc' or `desc'. Marker and Limit are used for pagination. type ListMembersOpts struct { Name string `q:"name"` Weight int `q:"weight"` AdminStateUp *bool `q:"admin_state_up"` TenantID string `q:"tenant_id"` Address string `q:"address"` ProtocolPort int `q:"protocol_port"` ID string `q:"id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToMemberListQuery formats a ListOpts into a query string. func (opts ListMembersOpts) ToMembersListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // ListMembers returns a Pager which allows you to iterate over a collection of // members. It accepts a ListMembersOptsBuilder, which allows you to filter and // sort the returned collection for greater efficiency. // // Default policy settings return only those members that are owned by the // tenant who submits the request, unless an admin user submits the request. func ListMembers(c *gophercloud.ServiceClient, poolID string, opts ListMembersOptsBuilder) pagination.Pager { url := memberRootURL(c, poolID) if opts != nil { query, err := opts.ToMembersListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return MemberPage{pagination.LinkedPageBase{PageResult: r}} }) } // CreateMemberOptsBuilder allows extensions to add additional parameters to the // CreateMember request. type CreateMemberOptsBuilder interface { ToMemberCreateMap() (map[string]interface{}, error) } // CreateMemberOpts is the common options struct used in this package's CreateMember // operation. type CreateMemberOpts struct { // The IP address of the member to receive traffic from the load balancer. Address string `json:"address" required:"true"` // The port on which to listen for client traffic. ProtocolPort int `json:"protocol_port" required:"true"` // Name of the Member. Name string `json:"name,omitempty"` // TenantID is the UUID of the project who owns the Member. // Only administrative users can specify a project UUID other than their own. TenantID string `json:"tenant_id,omitempty"` // ProjectID is the UUID of the project who owns the Member. // Only administrative users can specify a project UUID other than their own. ProjectID string `json:"project_id,omitempty"` // A positive integer value that indicates the relative portion of traffic // that this member should receive from the pool. For example, a member with // a weight of 10 receives five times as much traffic as a member with a // weight of 2. Weight int `json:"weight,omitempty"` // If you omit this parameter, LBaaS uses the vip_subnet_id parameter value // for the subnet UUID. SubnetID string `json:"subnet_id,omitempty"` // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToMemberCreateMap builds a request body from CreateMemberOpts. func (opts CreateMemberOpts) ToMemberCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "member") } // CreateMember will create and associate a Member with a particular Pool. func CreateMember(c *gophercloud.ServiceClient, poolID string, opts CreateMemberOpts) (r CreateMemberResult) { b, err := opts.ToMemberCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(memberRootURL(c, poolID), b, &r.Body, nil) return } // GetMember retrieves a particular Pool Member based on its unique ID. func GetMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r GetMemberResult) { _, r.Err = c.Get(memberResourceURL(c, poolID, memberID), &r.Body, nil) return } // UpdateMemberOptsBuilder allows extensions to add additional parameters to the // List request. type UpdateMemberOptsBuilder interface { ToMemberUpdateMap() (map[string]interface{}, error) } // UpdateMemberOpts is the common options struct used in this package's Update // operation. type UpdateMemberOpts struct { // Name of the Member. Name string `json:"name,omitempty"` // A positive integer value that indicates the relative portion of traffic // that this member should receive from the pool. For example, a member with // a weight of 10 receives five times as much traffic as a member with a // weight of 2. Weight int `json:"weight,omitempty"` // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToMemberUpdateMap builds a request body from UpdateMemberOpts. func (opts UpdateMemberOpts) ToMemberUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "member") } // Update allows Member to be updated. func UpdateMember(c *gophercloud.ServiceClient, poolID string, memberID string, opts UpdateMemberOptsBuilder) (r UpdateMemberResult) { b, err := opts.ToMemberUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(memberResourceURL(c, poolID, memberID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) return } // DisassociateMember will remove and disassociate a Member from a particular // Pool. func DeleteMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r DeleteMemberResult) { _, r.Err = c.Delete(memberResourceURL(c, poolID, memberID), nil) return } results.go000066400000000000000000000211211335005366400372530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/poolspackage pools import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" "github.com/gophercloud/gophercloud/pagination" ) // SessionPersistence represents the session persistence feature of the load // balancing service. It attempts to force connections or requests in the same // session to be processed by the same member as long as it is ative. Three // types of persistence are supported: // // SOURCE_IP: With this mode, all connections originating from the same source // IP address, will be handled by the same Member of the Pool. // HTTP_COOKIE: With this persistence mode, the load balancing function will // create a cookie on the first request from a client. Subsequent // requests containing the same cookie value will be handled by // the same Member of the Pool. // APP_COOKIE: With this persistence mode, the load balancing function will // rely on a cookie established by the backend application. All // requests carrying the same cookie value will be handled by the // same Member of the Pool. type SessionPersistence struct { // The type of persistence mode. Type string `json:"type"` // Name of cookie if persistence mode is set appropriately. CookieName string `json:"cookie_name,omitempty"` } // LoadBalancerID represents a load balancer. type LoadBalancerID struct { ID string `json:"id"` } // ListenerID represents a listener. type ListenerID struct { ID string `json:"id"` } // Pool represents a logical set of devices, such as web servers, that you // group together to receive and process traffic. The load balancing function // chooses a Member of the Pool according to the configured load balancing // method to handle the new requests or connections received on the VIP address. type Pool struct { // The load-balancer algorithm, which is round-robin, least-connections, and // so on. This value, which must be supported, is dependent on the provider. // Round-robin must be supported. LBMethod string `json:"lb_algorithm"` // The protocol of the Pool, which is TCP, HTTP, or HTTPS. Protocol string `json:"protocol"` // Description for the Pool. Description string `json:"description"` // A list of listeners objects IDs. Listeners []ListenerID `json:"listeners"` //[]map[string]interface{} // A list of member objects IDs. Members []Member `json:"members"` // The ID of associated health monitor. MonitorID string `json:"healthmonitor_id"` // The network on which the members of the Pool will be located. Only members // that are on this network can be added to the Pool. SubnetID string `json:"subnet_id"` // Owner of the Pool. TenantID string `json:"tenant_id"` // The administrative state of the Pool, which is up (true) or down (false). AdminStateUp bool `json:"admin_state_up"` // Pool name. Does not have to be unique. Name string `json:"name"` // The unique ID for the Pool. ID string `json:"id"` // A list of load balancer objects IDs. Loadbalancers []LoadBalancerID `json:"loadbalancers"` // Indicates whether connections in the same session will be processed by the // same Pool member or not. Persistence SessionPersistence `json:"session_persistence"` // The load balancer provider. Provider string `json:"provider"` // The Monitor associated with this Pool. Monitor monitors.Monitor `json:"healthmonitor"` // The provisioning status of the pool. // This value is ACTIVE, PENDING_* or ERROR. ProvisioningStatus string `json:"provisioning_status"` } // PoolPage is the page returned by a pager when traversing over a // collection of pools. type PoolPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of pools has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r PoolPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"pools_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a PoolPage struct is empty. func (r PoolPage) IsEmpty() (bool, error) { is, err := ExtractPools(r) return len(is) == 0, err } // ExtractPools accepts a Page struct, specifically a PoolPage struct, // and extracts the elements into a slice of Pool structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractPools(r pagination.Page) ([]Pool, error) { var s struct { Pools []Pool `json:"pools"` } err := (r.(PoolPage)).ExtractInto(&s) return s.Pools, err } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a pool. func (r commonResult) Extract() (*Pool, error) { var s struct { Pool *Pool `json:"pool"` } err := r.ExtractInto(&s) return s.Pool, err } // CreateResult represents the result of a Create operation. Call its Extract // method to interpret the result as a Pool. type CreateResult struct { commonResult } // GetResult represents the result of a Get operation. Call its Extract // method to interpret the result as a Pool. type GetResult struct { commonResult } // UpdateResult represents the result of an Update operation. Call its Extract // method to interpret the result as a Pool. type UpdateResult struct { commonResult } // DeleteResult represents the result of a Delete operation. Call its // ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // Member represents the application running on a backend server. type Member struct { // Name of the Member. Name string `json:"name"` // Weight of Member. Weight int `json:"weight"` // The administrative state of the member, which is up (true) or down (false). AdminStateUp bool `json:"admin_state_up"` // Owner of the Member. TenantID string `json:"tenant_id"` // Parameter value for the subnet UUID. SubnetID string `json:"subnet_id"` // The Pool to which the Member belongs. PoolID string `json:"pool_id"` // The IP address of the Member. Address string `json:"address"` // The port on which the application is hosted. ProtocolPort int `json:"protocol_port"` // The unique ID for the Member. ID string `json:"id"` // The provisioning status of the member. // This value is ACTIVE, PENDING_* or ERROR. ProvisioningStatus string `json:"provisioning_status"` } // MemberPage is the page returned by a pager when traversing over a // collection of Members in a Pool. type MemberPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of members has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r MemberPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"members_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a MemberPage struct is empty. func (r MemberPage) IsEmpty() (bool, error) { is, err := ExtractMembers(r) return len(is) == 0, err } // ExtractMembers accepts a Page struct, specifically a MemberPage struct, // and extracts the elements into a slice of Members structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractMembers(r pagination.Page) ([]Member, error) { var s struct { Members []Member `json:"members"` } err := (r.(MemberPage)).ExtractInto(&s) return s.Members, err } type commonMemberResult struct { gophercloud.Result } // ExtractMember is a function that accepts a result and extracts a member. func (r commonMemberResult) Extract() (*Member, error) { var s struct { Member *Member `json:"member"` } err := r.ExtractInto(&s) return s.Member, err } // CreateMemberResult represents the result of a CreateMember operation. // Call its Extract method to interpret it as a Member. type CreateMemberResult struct { commonMemberResult } // GetMemberResult represents the result of a GetMember operation. // Call its Extract method to interpret it as a Member. type GetMemberResult struct { commonMemberResult } // UpdateMemberResult represents the result of an UpdateMember operation. // Call its Extract method to interpret it as a Member. type UpdateMemberResult struct { commonMemberResult } // DeleteMemberResult represents the result of a DeleteMember operation. // Call its ExtractErr method to determine if the request succeeded or failed. type DeleteMemberResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400367035ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/poolsdoc.go000066400000000000000000000000441335005366400377750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/pools/testing// pools unit tests package testing fixtures.go000066400000000000000000000313211335005366400411030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/pools/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // PoolsListBody contains the canned body of a pool list response. const PoolsListBody = ` { "pools":[ { "lb_algorithm":"ROUND_ROBIN", "protocol":"HTTP", "description":"", "healthmonitor_id": "466c8345-28d8-4f84-a246-e04380b0461d", "members":[{"id": "53306cda-815d-4354-9fe4-59e09da9c3c5"}], "listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}], "loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], "id":"72741b06-df4d-4715-b142-276b6bce75ab", "name":"web", "admin_state_up":true, "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", "provider": "haproxy" }, { "lb_algorithm":"LEAST_CONNECTION", "protocol":"HTTP", "description":"", "healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d", "members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}], "listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}], "loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], "id":"c3741b06-df4d-4715-b142-276b6bce75ab", "name":"db", "admin_state_up":true, "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", "provider": "haproxy" } ] } ` // SinglePoolBody is the canned body of a Get request on an existing pool. const SinglePoolBody = ` { "pool": { "lb_algorithm":"LEAST_CONNECTION", "protocol":"HTTP", "description":"", "healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d", "members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}], "listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}], "loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], "id":"c3741b06-df4d-4715-b142-276b6bce75ab", "name":"db", "admin_state_up":true, "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", "provider": "haproxy" } } ` // PostUpdatePoolBody is the canned response body of a Update request on an existing pool. const PostUpdatePoolBody = ` { "pool": { "lb_algorithm":"LEAST_CONNECTION", "protocol":"HTTP", "description":"", "healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d", "members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}], "listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}], "loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], "id":"c3741b06-df4d-4715-b142-276b6bce75ab", "name":"db", "admin_state_up":true, "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", "provider": "haproxy" } } ` var ( PoolWeb = pools.Pool{ LBMethod: "ROUND_ROBIN", Protocol: "HTTP", Description: "", MonitorID: "466c8345-28d8-4f84-a246-e04380b0461d", TenantID: "83657cfcdfe44cd5920adaf26c48ceea", AdminStateUp: true, Name: "web", Members: []pools.Member{{ID: "53306cda-815d-4354-9fe4-59e09da9c3c5"}}, ID: "72741b06-df4d-4715-b142-276b6bce75ab", Loadbalancers: []pools.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, Listeners: []pools.ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}}, Provider: "haproxy", } PoolDb = pools.Pool{ LBMethod: "LEAST_CONNECTION", Protocol: "HTTP", Description: "", MonitorID: "5f6c8345-28d8-4f84-a246-e04380b0461d", TenantID: "83657cfcdfe44cd5920adaf26c48ceea", AdminStateUp: true, Name: "db", Members: []pools.Member{{ID: "67306cda-815d-4354-9fe4-59e09da9c3c5"}}, ID: "c3741b06-df4d-4715-b142-276b6bce75ab", Loadbalancers: []pools.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, Listeners: []pools.ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}}, Provider: "haproxy", } PoolUpdated = pools.Pool{ LBMethod: "LEAST_CONNECTION", Protocol: "HTTP", Description: "", MonitorID: "5f6c8345-28d8-4f84-a246-e04380b0461d", TenantID: "83657cfcdfe44cd5920adaf26c48ceea", AdminStateUp: true, Name: "db", Members: []pools.Member{{ID: "67306cda-815d-4354-9fe4-59e09da9c3c5"}}, ID: "c3741b06-df4d-4715-b142-276b6bce75ab", Loadbalancers: []pools.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, Listeners: []pools.ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}}, Provider: "haproxy", } ) // HandlePoolListSuccessfully sets up the test server to respond to a pool List request. func HandlePoolListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/pools", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, PoolsListBody) case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": fmt.Fprintf(w, `{ "pools": [] }`) default: t.Fatalf("/v2.0/lbaas/pools invoked with unexpected marker=[%s]", marker) } }) } // HandlePoolCreationSuccessfully sets up the test server to respond to a pool creation request // with a given response. func HandlePoolCreationSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/v2.0/lbaas/pools", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "pool": { "lb_algorithm": "ROUND_ROBIN", "protocol": "HTTP", "name": "Example pool", "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", "loadbalancer_id": "79e05663-7f03-45d2-a092-8b94062f22ab" } }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // HandlePoolGetSuccessfully sets up the test server to respond to a pool Get request. func HandlePoolGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, SinglePoolBody) }) } // HandlePoolDeletionSuccessfully sets up the test server to respond to a pool deletion request. func HandlePoolDeletionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandlePoolUpdateSuccessfully sets up the test server to respond to a pool Update request. func HandlePoolUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, `{ "pool": { "name": "NewPoolName", "lb_algorithm": "LEAST_CONNECTIONS" } }`) fmt.Fprintf(w, PostUpdatePoolBody) }) } // MembersListBody contains the canned body of a member list response. const MembersListBody = ` { "members":[ { "id": "2a280670-c202-4b0b-a562-34077415aabf", "address": "10.0.2.10", "weight": 5, "name": "web", "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", "admin_state_up":true, "protocol_port": 80 }, { "id": "fad389a3-9a4a-4762-a365-8c7038508b5d", "address": "10.0.2.11", "weight": 10, "name": "db", "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", "admin_state_up":false, "protocol_port": 80 } ] } ` // SingleMemberBody is the canned body of a Get request on an existing member. const SingleMemberBody = ` { "member": { "id": "fad389a3-9a4a-4762-a365-8c7038508b5d", "address": "10.0.2.11", "weight": 10, "name": "db", "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", "admin_state_up":false, "protocol_port": 80 } } ` // PostUpdateMemberBody is the canned response body of a Update request on an existing member. const PostUpdateMemberBody = ` { "member": { "id": "fad389a3-9a4a-4762-a365-8c7038508b5d", "address": "10.0.2.11", "weight": 10, "name": "db", "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", "admin_state_up":false, "protocol_port": 80 } } ` var ( MemberWeb = pools.Member{ SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", TenantID: "2ffc6e22aae24e4795f87155d24c896f", AdminStateUp: true, Name: "web", ID: "2a280670-c202-4b0b-a562-34077415aabf", Address: "10.0.2.10", Weight: 5, ProtocolPort: 80, } MemberDb = pools.Member{ SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", TenantID: "2ffc6e22aae24e4795f87155d24c896f", AdminStateUp: false, Name: "db", ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", Address: "10.0.2.11", Weight: 10, ProtocolPort: 80, } MemberUpdated = pools.Member{ SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", TenantID: "2ffc6e22aae24e4795f87155d24c896f", AdminStateUp: false, Name: "db", ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", Address: "10.0.2.11", Weight: 10, ProtocolPort: 80, } ) // HandleMemberListSuccessfully sets up the test server to respond to a member List request. func HandleMemberListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, MembersListBody) case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": fmt.Fprintf(w, `{ "members": [] }`) default: t.Fatalf("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members invoked with unexpected marker=[%s]", marker) } }) } // HandleMemberCreationSuccessfully sets up the test server to respond to a member creation request // with a given response. func HandleMemberCreationSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ "member": { "address": "10.0.2.11", "weight": 10, "name": "db", "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", "protocol_port": 80 } }`) w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, response) }) } // HandleMemberGetSuccessfully sets up the test server to respond to a member Get request. func HandleMemberGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") fmt.Fprintf(w, SingleMemberBody) }) } // HandleMemberDeletionSuccessfully sets up the test server to respond to a member deletion request. func HandleMemberDeletionSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusNoContent) }) } // HandleMemberUpdateSuccessfully sets up the test server to respond to a member Update request. func HandleMemberUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, `{ "member": { "name": "newMemberName", "weight": 4 } }`) fmt.Fprintf(w, PostUpdateMemberBody) }) } requests_test.go000066400000000000000000000161371335005366400421540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/pools/testingpackage testing import ( "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestListPools(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePoolListSuccessfully(t) pages := 0 err := pools.List(fake.ServiceClient(), pools.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := pools.ExtractPools(page) if err != nil { return false, err } if len(actual) != 2 { t.Fatalf("Expected 2 pools, got %d", len(actual)) } th.CheckDeepEquals(t, PoolWeb, actual[0]) th.CheckDeepEquals(t, PoolDb, actual[1]) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListAllPools(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePoolListSuccessfully(t) allPages, err := pools.List(fake.ServiceClient(), pools.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := pools.ExtractPools(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, PoolWeb, actual[0]) th.CheckDeepEquals(t, PoolDb, actual[1]) } func TestCreatePool(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePoolCreationSuccessfully(t, SinglePoolBody) actual, err := pools.Create(fake.ServiceClient(), pools.CreateOpts{ LBMethod: pools.LBMethodRoundRobin, Protocol: "HTTP", Name: "Example pool", TenantID: "2ffc6e22aae24e4795f87155d24c896f", LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, PoolDb, *actual) } func TestGetPool(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePoolGetSuccessfully(t) client := fake.ServiceClient() actual, err := pools.Get(client, "c3741b06-df4d-4715-b142-276b6bce75ab").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, PoolDb, *actual) } func TestDeletePool(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePoolDeletionSuccessfully(t) res := pools.Delete(fake.ServiceClient(), "c3741b06-df4d-4715-b142-276b6bce75ab") th.AssertNoErr(t, res.Err) } func TestUpdatePool(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePoolUpdateSuccessfully(t) client := fake.ServiceClient() actual, err := pools.Update(client, "c3741b06-df4d-4715-b142-276b6bce75ab", pools.UpdateOpts{ Name: "NewPoolName", LBMethod: pools.LBMethodLeastConnections, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, PoolUpdated, *actual) } func TestRequiredPoolCreateOpts(t *testing.T) { res := pools.Create(fake.ServiceClient(), pools.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = pools.Create(fake.ServiceClient(), pools.CreateOpts{ LBMethod: pools.LBMethod("invalid"), Protocol: pools.ProtocolHTTPS, LoadbalancerID: "69055154-f603-4a28-8951-7cc2d9e54a9a", }) if res.Err == nil { t.Fatalf("Expected error, but got none") } res = pools.Create(fake.ServiceClient(), pools.CreateOpts{ LBMethod: pools.LBMethodRoundRobin, Protocol: pools.Protocol("invalid"), LoadbalancerID: "69055154-f603-4a28-8951-7cc2d9e54a9a", }) if res.Err == nil { t.Fatalf("Expected error, but got none") } res = pools.Create(fake.ServiceClient(), pools.CreateOpts{ LBMethod: pools.LBMethodRoundRobin, Protocol: pools.ProtocolHTTPS, }) if res.Err == nil { t.Fatalf("Expected error, but got none") } } func TestListMembers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMemberListSuccessfully(t) pages := 0 err := pools.ListMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.ListMembersOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := pools.ExtractMembers(page) if err != nil { return false, err } if len(actual) != 2 { t.Fatalf("Expected 2 members, got %d", len(actual)) } th.CheckDeepEquals(t, MemberWeb, actual[0]) th.CheckDeepEquals(t, MemberDb, actual[1]) return true, nil }) th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } func TestListAllMembers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMemberListSuccessfully(t) allPages, err := pools.ListMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.ListMembersOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := pools.ExtractMembers(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, MemberWeb, actual[0]) th.CheckDeepEquals(t, MemberDb, actual[1]) } func TestCreateMember(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMemberCreationSuccessfully(t, SingleMemberBody) actual, err := pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{ Name: "db", SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", TenantID: "2ffc6e22aae24e4795f87155d24c896f", Address: "10.0.2.11", ProtocolPort: 80, Weight: 10, }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, MemberDb, *actual) } func TestRequiredMemberCreateOpts(t *testing.T) { res := pools.CreateMember(fake.ServiceClient(), "", pools.CreateMemberOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = pools.CreateMember(fake.ServiceClient(), "", pools.CreateMemberOpts{Address: "1.2.3.4", ProtocolPort: 80}) if res.Err == nil { t.Fatalf("Expected error, but got none") } res = pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{ProtocolPort: 80}) if res.Err == nil { t.Fatalf("Expected error, but got none") } res = pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{Address: "1.2.3.4"}) if res.Err == nil { t.Fatalf("Expected error, but got none") } } func TestGetMember(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMemberGetSuccessfully(t) client := fake.ServiceClient() actual, err := pools.GetMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } th.CheckDeepEquals(t, MemberDb, *actual) } func TestDeleteMember(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMemberDeletionSuccessfully(t) res := pools.DeleteMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf") th.AssertNoErr(t, res.Err) } func TestUpdateMember(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMemberUpdateSuccessfully(t) client := fake.ServiceClient() actual, err := pools.UpdateMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf", pools.UpdateMemberOpts{ Name: "newMemberName", Weight: 4, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } th.CheckDeepEquals(t, MemberUpdated, *actual) } urls.go000066400000000000000000000012461335005366400365450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/lbaas_v2/poolspackage pools import "github.com/gophercloud/gophercloud" const ( rootPath = "lbaas" resourcePath = "pools" memberPath = "members" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } func memberRootURL(c *gophercloud.ServiceClient, poolId string) string { return c.ServiceURL(rootPath, resourcePath, poolId, memberPath) } func memberResourceURL(c *gophercloud.ServiceClient, poolID string, memeberID string) string { return c.ServiceURL(rootPath, resourcePath, poolID, memberPath, memeberID) } networkipavailabilities/000077500000000000000000000000001335005366400373265ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensionsdoc.go000066400000000000000000000014361335005366400404260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/networkipavailabilities/* Package networkipavailabilities provides the ability to retrieve and manage networkipavailabilities through the Neutron API. Example of Listing NetworkIPAvailabilities allPages, err := networkipavailabilities.List(networkClient, networkipavailabilities.ListOpts{}).AllPages() if err != nil { panic(err) } allAvailabilities, err := subnetpools.ExtractSubnetPools(allPages) if err != nil { panic(err) } for _, availability := range allAvailabilities { fmt.Printf("%+v\n", availability) } Example of Getting a single NetworkIPAvailability availability, err := networkipavailabilities.Get(networkClient, "cf11ab78-2302-49fa-870f-851a08c7afb8").Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", availability) */ package networkipavailabilities requests.go000066400000000000000000000037651335005366400415430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/networkipavailabilitiespackage networkipavailabilities import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToNetworkIPAvailabilityListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the Neutron API. type ListOpts struct { // NetworkName allows to filter on the identifier of a network. NetworkID string `q:"network_id"` // NetworkName allows to filter on the name of a network. NetworkName string `q:"network_name"` // IPVersion allows to filter on the version of the IP protocol. // You can use the well-known IP versions with the gophercloud.IPVersion type. IPVersion string `q:"ip_version"` // ProjectID allows to filter on the Identity project field. ProjectID string `q:"project_id"` // TenantID allows to filter on the Identity project field. TenantID string `q:"tenant_id"` } // ToNetworkIPAvailabilityListQuery formats a ListOpts into a query string. func (opts ListOpts) ToNetworkIPAvailabilityListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // networkipavailabilities. It accepts a ListOpts struct, which allows you to // filter the returned collection for greater efficiency. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(c) if opts != nil { query, err := opts.ToNetworkIPAvailabilityListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return NetworkIPAvailabilityPage{pagination.SinglePageBase(r)} }) } // Get retrieves a specific NetworkIPAvailability based on its ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(getURL(c, id), &r.Body, nil) return } results.go000066400000000000000000000060611335005366400413610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/networkipavailabilitiespackage networkipavailabilities import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { gophercloud.Result } // GetResult represents the result of a Get operation. Call its Extract // method to interpret it as a NetworkIPAvailability. type GetResult struct { commonResult } // Extract is a function that accepts a result and extracts a NetworkIPAvailability. func (r commonResult) Extract() (*NetworkIPAvailability, error) { var s struct { NetworkIPAvailability *NetworkIPAvailability `json:"network_ip_availability"` } err := r.ExtractInto(&s) return s.NetworkIPAvailability, err } // NetworkIPAvailability represents availability details for a single network. type NetworkIPAvailability struct { // NetworkID contains an unique identifier of the network. NetworkID string `json:"network_id"` // NetworkName represents human-readable name of the network. NetworkName string `json:"network_name"` // ProjectID is the ID of the Identity project. ProjectID string `json:"project_id"` // TenantID is the ID of the Identity project. TenantID string `json:"tenant_id"` // SubnetIPAvailabilities contains availability details for every subnet // that is associated to the network. SubnetIPAvailabilities []SubnetIPAvailability `json:"subnet_ip_availability"` // TotalIPs represents a number of IP addresses in the network. TotalIPs int `json:"total_ips"` // UsedIPs represents a number of used IP addresses in the network. UsedIPs int `json:"used_ips"` } // SubnetIPAvailability represents availability details for a single subnet. type SubnetIPAvailability struct { // SubnetID contains an unique identifier of the subnet. SubnetID string `json:"subnet_id"` // SubnetName represents human-readable name of the subnet. SubnetName string `json:"subnet_name"` // CIDR represents prefix in the CIDR format. CIDR string `json:"cidr"` // IPVersion is the IP protocol version. IPVersion int `json:"ip_version"` // TotalIPs represents a number of IP addresses in the subnet. TotalIPs int `json:"total_ips"` // UsedIPs represents a number of used IP addresses in the subnet. UsedIPs int `json:"used_ips"` } // NetworkIPAvailabilityPage stores a single page of NetworkIPAvailabilities // from the List call. type NetworkIPAvailabilityPage struct { pagination.SinglePageBase } // IsEmpty determines whether or not a NetworkIPAvailability is empty. func (r NetworkIPAvailabilityPage) IsEmpty() (bool, error) { networkipavailabilities, err := ExtractNetworkIPAvailabilities(r) return len(networkipavailabilities) == 0, err } // ExtractNetworkIPAvailabilities interprets the results of a single page from // a List() API call, producing a slice of NetworkIPAvailabilities structures. func ExtractNetworkIPAvailabilities(r pagination.Page) ([]NetworkIPAvailability, error) { var s struct { NetworkIPAvailabilities []NetworkIPAvailability `json:"network_ip_availabilities"` } err := (r.(NetworkIPAvailabilityPage)).ExtractInto(&s) if err != nil { return nil, err } return s.NetworkIPAvailabilities, nil } testing/000077500000000000000000000000001335005366400410035ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/networkipavailabilitiesdoc.go000066400000000000000000000000661335005366400421010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/networkipavailabilities/testing// networkipavailabilities unit tests package testing fixtures.go000066400000000000000000000104051335005366400432030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/networkipavailabilities/testingpackage testing import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/networkipavailabilities" ) // NetworkIPAvailabilityListResult represents raw server response from a server to a list call. const NetworkIPAvailabilityListResult = ` { "network_ip_availabilities": [ { "network_id": "080ee064-036d-405a-a307-3bde4a213a1b", "network_name": "private", "project_id": "fb57277ef2f84a0e85b9018ec2dedbf7", "subnet_ip_availability": [ { "cidr": "10.0.0.64/26", "ip_version": 4, "subnet_id": "497ac4d3-0b92-42cf-82de-71302ab2b656", "subnet_name": "second-private-subnet", "total_ips": 61, "used_ips": 12 }, { "cidr": "10.0.0.0/26", "ip_version": 4, "subnet_id": "521f47e7-c4fb-452c-b71a-851da38cc571", "subnet_name": "private-subnet", "total_ips": 61, "used_ips": 2 } ], "tenant_id": "fb57277ef2f84a0e85b9018ec2dedbf7", "total_ips": 122, "used_ips": 14 }, { "network_id": "cf11ab78-2302-49fa-870f-851a08c7afb8", "network_name": "public", "project_id": "424e7cf0243c468ca61732ba45973b3e", "subnet_ip_availability": [ { "cidr": "203.0.113.0/24", "ip_version": 4, "subnet_id": "4afe6e5f-9649-40db-b18f-64c7ead942bd", "subnet_name": "public-subnet", "total_ips": 253, "used_ips": 3 } ], "tenant_id": "424e7cf0243c468ca61732ba45973b3e", "total_ips": 253, "used_ips": 3 } ] } ` // NetworkIPAvailability1 is an expected representation of a first object from the ResourceListResult. var NetworkIPAvailability1 = networkipavailabilities.NetworkIPAvailability{ NetworkID: "080ee064-036d-405a-a307-3bde4a213a1b", NetworkName: "private", ProjectID: "fb57277ef2f84a0e85b9018ec2dedbf7", TenantID: "fb57277ef2f84a0e85b9018ec2dedbf7", TotalIPs: 122, UsedIPs: 14, SubnetIPAvailabilities: []networkipavailabilities.SubnetIPAvailability{ { SubnetID: "497ac4d3-0b92-42cf-82de-71302ab2b656", SubnetName: "second-private-subnet", CIDR: "10.0.0.64/26", IPVersion: int(gophercloud.IPv4), TotalIPs: 61, UsedIPs: 12, }, { SubnetID: "521f47e7-c4fb-452c-b71a-851da38cc571", SubnetName: "private-subnet", CIDR: "10.0.0.0/26", IPVersion: int(gophercloud.IPv4), TotalIPs: 61, UsedIPs: 2, }, }, } // NetworkIPAvailability2 is an expected representation of a first object from the ResourceListResult. var NetworkIPAvailability2 = networkipavailabilities.NetworkIPAvailability{ NetworkID: "cf11ab78-2302-49fa-870f-851a08c7afb8", NetworkName: "public", ProjectID: "424e7cf0243c468ca61732ba45973b3e", TenantID: "424e7cf0243c468ca61732ba45973b3e", TotalIPs: 253, UsedIPs: 3, SubnetIPAvailabilities: []networkipavailabilities.SubnetIPAvailability{ { SubnetID: "4afe6e5f-9649-40db-b18f-64c7ead942bd", SubnetName: "public-subnet", CIDR: "203.0.113.0/24", IPVersion: int(gophercloud.IPv4), TotalIPs: 253, UsedIPs: 3, }, }, } // NetworkIPAvailabilityGetResult represents raw server response from a server to a get call. const NetworkIPAvailabilityGetResult = ` { "network_ip_availability": { "network_id": "cf11ab78-2302-49fa-870f-851a08c7afb8", "network_name": "public", "project_id": "424e7cf0243c468ca61732ba45973b3e", "subnet_ip_availability": [ { "cidr": "203.0.113.0/24", "ip_version": 4, "subnet_id": "4afe6e5f-9649-40db-b18f-64c7ead942bd", "subnet_name": "public-subnet", "total_ips": 253, "used_ips": 3 } ], "tenant_id": "424e7cf0243c468ca61732ba45973b3e", "total_ips": 253, "used_ips": 3 } } ` requests_test.go000066400000000000000000000050201335005366400442410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/networkipavailabilities/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/networkipavailabilities" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/network-ip-availabilities", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, NetworkIPAvailabilityListResult) }) count := 0 networkipavailabilities.List(fake.ServiceClient(), networkipavailabilities.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := networkipavailabilities.ExtractNetworkIPAvailabilities(page) if err != nil { t.Errorf("Failed to extract network IP availabilities: %v", err) return false, nil } expected := []networkipavailabilities.NetworkIPAvailability{ NetworkIPAvailability1, NetworkIPAvailability2, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/network-ip-availabilities/cf11ab78-2302-49fa-870f-851a08c7afb8", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, NetworkIPAvailabilityGetResult) }) s, err := networkipavailabilities.Get(fake.ServiceClient(), "cf11ab78-2302-49fa-870f-851a08c7afb8").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.NetworkID, "cf11ab78-2302-49fa-870f-851a08c7afb8") th.AssertEquals(t, s.NetworkName, "public") th.AssertEquals(t, s.ProjectID, "424e7cf0243c468ca61732ba45973b3e") th.AssertEquals(t, s.TenantID, "424e7cf0243c468ca61732ba45973b3e") th.AssertEquals(t, s.TotalIPs, 253) th.AssertEquals(t, s.UsedIPs, 3) th.AssertDeepEquals(t, s.SubnetIPAvailabilities, []networkipavailabilities.SubnetIPAvailability{ { SubnetID: "4afe6e5f-9649-40db-b18f-64c7ead942bd", SubnetName: "public-subnet", CIDR: "203.0.113.0/24", IPVersion: int(gophercloud.IPv4), TotalIPs: 253, UsedIPs: 3, }, }) } urls.go000066400000000000000000000011001335005366400406320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/networkipavailabilitiespackage networkipavailabilities import "github.com/gophercloud/gophercloud" const resourcePath = "network-ip-availabilities" func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } func resourceURL(c *gophercloud.ServiceClient, networkIPAvailabilityID string) string { return c.ServiceURL(resourcePath, networkIPAvailabilityID) } func listURL(c *gophercloud.ServiceClient) string { return rootURL(c) } func getURL(c *gophercloud.ServiceClient, networkIPAvailabilityID string) string { return resourceURL(c, networkIPAvailabilityID) } portsbinding/000077500000000000000000000000001335005366400351035ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensionsdoc.go000066400000000000000000000002331335005366400361750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/portsbinding// Package portsbinding provides information and interaction with the port // binding extension for the OpenStack Networking service. package portsbinding requests.go000066400000000000000000000051141335005366400373060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/portsbindingpackage portsbinding import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" ) // CreateOptsExt adds port binding options to the base ports.CreateOpts. type CreateOptsExt struct { // CreateOptsBuilder is the interface options structs have to satisfy in order // to be used in the main Create operation in this package. ports.CreateOptsBuilder // The ID of the host where the port is allocated HostID string `json:"binding:host_id,omitempty"` // The virtual network interface card (vNIC) type that is bound to the // neutron port. VNICType string `json:"binding:vnic_type,omitempty"` // A dictionary that enables the application running on the specified // host to pass and receive virtual network interface (VIF) port-specific // information to the plug-in. Profile map[string]string `json:"binding:profile,omitempty"` } // ToPortCreateMap casts a CreateOpts struct to a map. func (opts CreateOptsExt) ToPortCreateMap() (map[string]interface{}, error) { base, err := opts.CreateOptsBuilder.ToPortCreateMap() if err != nil { return nil, err } port := base["port"].(map[string]interface{}) if opts.HostID != "" { port["binding:host_id"] = opts.HostID } if opts.VNICType != "" { port["binding:vnic_type"] = opts.VNICType } if opts.Profile != nil { port["binding:profile"] = opts.Profile } return base, nil } // UpdateOptsExt adds port binding options to the base ports.UpdateOpts type UpdateOptsExt struct { // UpdateOptsBuilder is the interface options structs have to satisfy in order // to be used in the main Update operation in this package. ports.UpdateOptsBuilder // The ID of the host where the port is allocated. HostID string `json:"binding:host_id,omitempty"` // The virtual network interface card (vNIC) type that is bound to the // neutron port. VNICType string `json:"binding:vnic_type,omitempty"` // A dictionary that enables the application running on the specified // host to pass and receive virtual network interface (VIF) port-specific // information to the plug-in. Profile map[string]string `json:"binding:profile,omitempty"` } // ToPortUpdateMap casts an UpdateOpts struct to a map. func (opts UpdateOptsExt) ToPortUpdateMap() (map[string]interface{}, error) { base, err := opts.UpdateOptsBuilder.ToPortUpdateMap() if err != nil { return nil, err } port := base["port"].(map[string]interface{}) if opts.HostID != "" { port["binding:host_id"] = opts.HostID } if opts.VNICType != "" { port["binding:vnic_type"] = opts.VNICType } if opts.Profile != nil { port["binding:profile"] = opts.Profile } return base, nil } results.go000066400000000000000000000020041335005366400371270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/portsbindingpackage portsbinding // IP is a sub-struct that represents an individual IP. type IP struct { SubnetID string `json:"subnet_id"` IPAddress string `json:"ip_address"` } // PortsBindingExt represents a decorated form of a Port with the additional // port binding information. type PortsBindingExt struct { // The ID of the host where the port is allocated. HostID string `json:"binding:host_id"` // A dictionary that enables the application to pass information about // functions that the Networking API provides. VIFDetails map[string]interface{} `json:"binding:vif_details"` // The VIF type for the port. VIFType string `json:"binding:vif_type"` // The virtual network interface card (vNIC) type that is bound to the // neutron port. VNICType string `json:"binding:vnic_type"` // A dictionary that enables the application running on the specified // host to pass and receive virtual network interface (VIF) port-specific // information to the plug-in. Profile map[string]string `json:"binding:profile"` } testing/000077500000000000000000000000001335005366400365605ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/portsbindingdoc.go000066400000000000000000000000541335005366400376530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/portsbinding/testing// portsbindings unit tests package testing fixtures.go000066400000000000000000000100641335005366400407610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/portsbinding/testingpackage testing import ( "fmt" "net/http" "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" porttest "github.com/gophercloud/gophercloud/openstack/networking/v2/ports/testing" th "github.com/gophercloud/gophercloud/testhelper" ) func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, porttest.ListResponse) }) } func HandleGet(t *testing.T) { th.Mux.HandleFunc("/v2.0/ports/46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, porttest.GetResponse) }) } func HandleCreate(t *testing.T) { th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "port": { "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "name": "private-port", "admin_state_up": true, "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.2" } ], "security_groups": ["foo"], "binding:host_id": "HOST1", "binding:vnic_type": "normal" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "port": { "status": "DOWN", "name": "private-port", "allowed_address_pairs": [], "admin_state_up": true, "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", "device_owner": "", "mac_address": "fa:16:3e:c9:cb:f0", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.2" } ], "binding:host_id": "HOST1", "binding:vnic_type": "normal", "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "security_groups": [ "f0ac4394-7e4a-4409-9701-ba8be283dbc3" ], "device_id": "" } } `) }) } func HandleUpdate(t *testing.T) { th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "port": { "name": "new_port_name", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.3" } ], "security_groups": [ "f0ac4394-7e4a-4409-9701-ba8be283dbc3" ], "binding:host_id": "HOST1", "binding:vnic_type": "normal" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "port": { "status": "DOWN", "name": "new_port_name", "admin_state_up": true, "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", "device_owner": "", "mac_address": "fa:16:3e:c9:cb:f0", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.3" } ], "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "security_groups": [ "f0ac4394-7e4a-4409-9701-ba8be283dbc3" ], "device_id": "", "binding:host_id": "HOST1", "binding:vnic_type": "normal" } } `) }) } requests_test.go000066400000000000000000000123151335005366400420230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/portsbinding/testingpackage testing import ( "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" th "github.com/gophercloud/gophercloud/testhelper" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t) type PortWithExt struct { ports.Port portsbinding.PortsBindingExt } var actual []PortWithExt expected := []PortWithExt{ { Port: ports.Port{ Status: "ACTIVE", Name: "", AdminStateUp: true, NetworkID: "70c1db1f-b701-45bd-96e0-a313ee3430b3", TenantID: "", DeviceOwner: "network:router_gateway", MACAddress: "fa:16:3e:58:42:ed", FixedIPs: []ports.IP{ { SubnetID: "008ba151-0b8c-4a67-98b5-0d2b87666062", IPAddress: "172.24.4.2", }, }, ID: "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b", SecurityGroups: []string{}, DeviceID: "9ae135f4-b6e0-4dad-9e91-3c223e385824", }, PortsBindingExt: portsbinding.PortsBindingExt{ VNICType: "normal", HostID: "devstack", }, }, } allPages, err := ports.List(fake.ServiceClient(), ports.ListOpts{}).AllPages() th.AssertNoErr(t, err) err = ports.ExtractPortsInto(allPages, &actual) th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGet(t) var s struct { ports.Port portsbinding.PortsBindingExt } err := ports.Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, s.Status, "ACTIVE") th.AssertEquals(t, s.Name, "") th.AssertEquals(t, s.AdminStateUp, true) th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") th.AssertEquals(t, s.TenantID, "7e02058126cc4950b75f9970368ba177") th.AssertEquals(t, s.DeviceOwner, "network:router_interface") th.AssertEquals(t, s.MACAddress, "fa:16:3e:23:fd:d7") th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.1"}, }) th.AssertEquals(t, s.ID, "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2") th.AssertDeepEquals(t, s.SecurityGroups, []string{}) th.AssertEquals(t, s.DeviceID, "5e3898d7-11be-483e-9732-b2f5eccd2b2e") th.AssertEquals(t, s.HostID, "devstack") th.AssertEquals(t, s.VNICType, "normal") th.AssertEquals(t, s.VIFType, "ovs") th.AssertDeepEquals(t, s.VIFDetails, map[string]interface{}{"port_filter": true, "ovs_hybrid_plug": true}) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreate(t) var s struct { ports.Port portsbinding.PortsBindingExt } asu := true portCreateOpts := ports.CreateOpts{ Name: "private-port", AdminStateUp: &asu, NetworkID: "a87cc70a-3e15-4acf-8205-9b711a3531b7", FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, }, SecurityGroups: &[]string{"foo"}, } createOpts := portsbinding.CreateOptsExt{ CreateOptsBuilder: portCreateOpts, HostID: "HOST1", VNICType: "normal", } err := ports.Create(fake.ServiceClient(), createOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, s.Status, "DOWN") th.AssertEquals(t, s.Name, "private-port") th.AssertEquals(t, s.AdminStateUp, true) th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") th.AssertEquals(t, s.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") th.AssertEquals(t, s.DeviceOwner, "") th.AssertEquals(t, s.MACAddress, "fa:16:3e:c9:cb:f0") th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, }) th.AssertEquals(t, s.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}) th.AssertEquals(t, s.HostID, "HOST1") th.AssertEquals(t, s.VNICType, "normal") } func TestRequiredCreateOpts(t *testing.T) { res := ports.Create(fake.ServiceClient(), portsbinding.CreateOptsExt{CreateOptsBuilder: ports.CreateOpts{}}) if res.Err == nil { t.Fatalf("Expected error, got none") } } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdate(t) var s struct { ports.Port portsbinding.PortsBindingExt } portUpdateOpts := ports.UpdateOpts{ Name: "new_port_name", FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, SecurityGroups: &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}, } updateOpts := portsbinding.UpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, HostID: "HOST1", VNICType: "normal", } err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "new_port_name") th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }) th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}) th.AssertEquals(t, s.HostID, "HOST1") th.AssertEquals(t, s.VNICType, "normal") } portsecurity/000077500000000000000000000000001335005366400351555ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensionsdoc.go000066400000000000000000000065531335005366400362620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/portsecurity/* Package portsecurity provides information and interaction with the port security extension for the OpenStack Networking service. Example to List Networks with Port Security Information type NetworkWithPortSecurityExt struct { networks.Network portsecurity.PortSecurityExt } var allNetworks []NetworkWithPortSecurityExt listOpts := networks.ListOpts{ Name: "network_1", } allPages, err := networks.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } err = networks.ExtractNetworksInto(allPages, &allNetworks) if err != nil { panic(err) } for _, network := range allNetworks { fmt.Println("%+v\n", network) } Example to Create a Network without Port Security var networkWithPortSecurityExt struct { networks.Network portsecurity.PortSecurityExt } networkCreateOpts := networks.CreateOpts{ Name: "private", } iFalse := false createOpts := portsecurity.NetworkCreateOptsExt{ CreateOptsBuilder: networkCreateOpts, PortSecurityEnabled: &iFalse, } err := networks.Create(networkClient, createOpts).ExtractInto(&networkWithPortSecurityExt) if err != nil { panic(err) } fmt.Println("%+v\n", networkWithPortSecurityExt) Example to Disable Port Security on an Existing Network var networkWithPortSecurityExt struct { networks.Network portsecurity.PortSecurityExt } iFalse := false networkID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" networkUpdateOpts := networks.UpdateOpts{} updateOpts := portsecurity.NetworkUpdateOptsExt{ UpdateOptsBuilder: networkUpdateOpts, PortSecurityEnabled: &iFalse, } err := networks.Update(networkClient, networkID, updateOpts).ExtractInto(&networkWithPortSecurityExt) if err != nil { panic(err) } fmt.Println("%+v\n", networkWithPortSecurityExt) Example to Get a Port with Port Security Information var portWithPortSecurityExtensions struct { ports.Port portsecurity.PortSecurityExt } portID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" err := ports.Get(networkingClient, portID).ExtractInto(&portWithPortSecurityExtensions) if err != nil { panic(err) } fmt.Println("%+v\n", portWithPortSecurityExtensions) Example to Create a Port Without Port Security var portWithPortSecurityExtensions struct { ports.Port portsecurity.PortSecurityExt } iFalse := false networkID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" subnetID := "a87cc70a-3e15-4acf-8205-9b711a3531b7" portCreateOpts := ports.CreateOpts{ NetworkID: networkID, FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, } createOpts := portsecurity.PortCreateOptsExt{ CreateOptsBuilder: portCreateOpts, PortSecurityEnabled: &iFalse, } err := ports.Create(networkingClient, createOpts).ExtractInto(&portWithPortSecurityExtensions) if err != nil { panic(err) } fmt.Println("%+v\n", portWithPortSecurityExtensions) Example to Disable Port Security on an Existing Port var portWithPortSecurityExtensions struct { ports.Port portsecurity.PortSecurityExt } iFalse := false portID := "65c0ee9f-d634-4522-8954-51021b570b0d" portUpdateOpts := ports.UpdateOpts{} updateOpts := portsecurity.PortUpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, PortSecurityEnabled: &iFalse, } err := ports.Update(networkingClient, portID, updateOpts).ExtractInto(&portWithPortSecurityExtensions) if err != nil { panic(err) } fmt.Println("%+v\n", portWithPortSecurityExtensions) */ package portsecurity requests.go000066400000000000000000000055341335005366400373660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/portsecuritypackage portsecurity import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" ) // PortCreateOptsExt adds port security options to the base ports.CreateOpts. type PortCreateOptsExt struct { ports.CreateOptsBuilder // PortSecurityEnabled toggles port security on a port. PortSecurityEnabled *bool `json:"port_security_enabled,omitempty"` } // ToPortCreateMap casts a CreateOpts struct to a map. func (opts PortCreateOptsExt) ToPortCreateMap() (map[string]interface{}, error) { base, err := opts.CreateOptsBuilder.ToPortCreateMap() if err != nil { return nil, err } port := base["port"].(map[string]interface{}) if opts.PortSecurityEnabled != nil { port["port_security_enabled"] = &opts.PortSecurityEnabled } return base, nil } // PortUpdateOptsExt adds port security options to the base ports.UpdateOpts. type PortUpdateOptsExt struct { ports.UpdateOptsBuilder // PortSecurityEnabled toggles port security on a port. PortSecurityEnabled *bool `json:"port_security_enabled,omitempty"` } // ToPortUpdateMap casts a UpdateOpts struct to a map. func (opts PortUpdateOptsExt) ToPortUpdateMap() (map[string]interface{}, error) { base, err := opts.UpdateOptsBuilder.ToPortUpdateMap() if err != nil { return nil, err } port := base["port"].(map[string]interface{}) if opts.PortSecurityEnabled != nil { port["port_security_enabled"] = &opts.PortSecurityEnabled } return base, nil } // NetworkCreateOptsExt adds port security options to the base // networks.CreateOpts. type NetworkCreateOptsExt struct { networks.CreateOptsBuilder // PortSecurityEnabled toggles port security on a port. PortSecurityEnabled *bool `json:"port_security_enabled,omitempty"` } // ToNetworkCreateMap casts a CreateOpts struct to a map. func (opts NetworkCreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { base, err := opts.CreateOptsBuilder.ToNetworkCreateMap() if err != nil { return nil, err } network := base["network"].(map[string]interface{}) if opts.PortSecurityEnabled != nil { network["port_security_enabled"] = &opts.PortSecurityEnabled } return base, nil } // NetworkUpdateOptsExt adds port security options to the base // networks.UpdateOpts. type NetworkUpdateOptsExt struct { networks.UpdateOptsBuilder // PortSecurityEnabled toggles port security on a port. PortSecurityEnabled *bool `json:"port_security_enabled,omitempty"` } // ToNetworkUpdateMap casts a UpdateOpts struct to a map. func (opts NetworkUpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { base, err := opts.UpdateOptsBuilder.ToNetworkUpdateMap() if err != nil { return nil, err } network := base["network"].(map[string]interface{}) if opts.PortSecurityEnabled != nil { network["port_security_enabled"] = &opts.PortSecurityEnabled } return base, nil } results.go000066400000000000000000000003031335005366400372010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/portsecuritypackage portsecurity type PortSecurityExt struct { // PortSecurityEnabled specifies whether port security is enabled or // disabled. PortSecurityEnabled bool `json:"port_security_enabled"` } provider/000077500000000000000000000000001335005366400342335ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensionsdoc.go000066400000000000000000000043371335005366400353360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/provider/* Package provider gives access to the provider Neutron plugin, allowing network extended attributes. The provider extended attributes for networks enable administrative users to specify how network objects map to the underlying networking infrastructure. These extended attributes also appear when administrative users query networks. For more information about extended attributes, see the NetworkExtAttrs struct. The actual semantics of these attributes depend on the technology back end of the particular plug-in. See the plug-in documentation and the OpenStack Cloud Administrator Guide to understand which values should be specific for each of these attributes when OpenStack Networking is deployed with a particular plug-in. The examples shown in this chapter refer to the Open vSwitch plug-in. The default policy settings enable only users with administrative rights to specify these parameters in requests and to see their values in responses. By default, the provider network extension attributes are completely hidden from regular tenants. As a rule of thumb, if these attributes are not visible in a GET /networks/ operation, this implies the user submitting the request is not authorized to view or manipulate provider network attributes. Example to List Networks with Provider Information type NetworkWithProvider { networks.Network provider.NetworkProviderExt } var allNetworks []NetworkWithProvider allPages, err := networks.List(networkClient, nil).AllPages() if err != nil { panic(err) } err = networks.ExtractNetworksInto(allPages, &allNetworks) if err != nil { panic(err) } for _, network := range allNetworks { fmt.Printf("%+v\n", network) } Example to Create a Provider Network segments := []provider.Segment{ provider.Segment{ NetworkType: "vxlan", PhysicalNetwork: "br-ex", SegmentationID: 615, }, } iTrue := true networkCreateOpts := networks.CreateOpts{ Name: "provider-network", AdminStateUp: &iTrue, Shared: &iTrue, } createOpts : provider.CreateOptsExt{ CreateOptsBuilder: networkCreateOpts, Segments: segments, } network, err := networks.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } */ package provider requests.go000066400000000000000000000012751335005366400364420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/providerpackage provider import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" ) // CreateOptsExt adds a Segments option to the base Network CreateOpts. type CreateOptsExt struct { networks.CreateOptsBuilder Segments []Segment `json:"segments,omitempty"` } // ToNetworkCreateMap adds segments to the base network creation options. func (opts CreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { base, err := opts.CreateOptsBuilder.ToNetworkCreateMap() if err != nil { return nil, err } if opts.Segments == nil { return base, nil } providerMap := base["network"].(map[string]interface{}) providerMap["segments"] = opts.Segments return base, nil } results.go000066400000000000000000000040231335005366400362620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/providerpackage provider import ( "encoding/json" "strconv" ) // NetworkProviderExt represents an extended form of a Network with additional // fields. type NetworkProviderExt struct { // Specifies the nature of the physical network mapped to this network // resource. Examples are flat, vlan, or gre. NetworkType string `json:"provider:network_type"` // Identifies the physical network on top of which this network object is // being implemented. The OpenStack Networking API does not expose any // facility for retrieving the list of available physical networks. As an // example, in the Open vSwitch plug-in this is a symbolic name which is // then mapped to specific bridges on each compute host through the Open // vSwitch plug-in configuration file. PhysicalNetwork string `json:"provider:physical_network"` // Identifies an isolated segment on the physical network; the nature of the // segment depends on the segmentation model defined by network_type. For // instance, if network_type is vlan, then this is a vlan identifier; // otherwise, if network_type is gre, then this will be a gre key. SegmentationID string `json:"-"` // Segments is an array of Segment which defines multiple physical bindings // to logical networks. Segments []Segment `json:"segments"` } // Segment defines a physical binding to a logical network. type Segment struct { PhysicalNetwork string `json:"provider:physical_network"` NetworkType string `json:"provider:network_type"` SegmentationID int `json:"provider:segmentation_id"` } func (r *NetworkProviderExt) UnmarshalJSON(b []byte) error { type tmp NetworkProviderExt var networkProviderExt struct { tmp SegmentationID interface{} `json:"provider:segmentation_id"` } if err := json.Unmarshal(b, &networkProviderExt); err != nil { return err } *r = NetworkProviderExt(networkProviderExt.tmp) switch t := networkProviderExt.SegmentationID.(type) { case float64: r.SegmentationID = strconv.FormatFloat(t, 'f', -1, 64) case string: r.SegmentationID = string(t) } return nil } testing/000077500000000000000000000000001335005366400357105ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/providerdoc.go000066400000000000000000000000471335005366400370050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/provider/testing// provider unit tests package testing results_test.go000066400000000000000000000143311335005366400410010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/provider/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/provider" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" nettest "github.com/gophercloud/gophercloud/openstack/networking/v2/networks/testing" th "github.com/gophercloud/gophercloud/testhelper" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, nettest.ListResponse) }) type NetworkWithExt struct { networks.Network provider.NetworkProviderExt } var actual []NetworkWithExt allPages, err := networks.List(fake.ServiceClient(), networks.ListOpts{}).AllPages() th.AssertNoErr(t, err) err = networks.ExtractNetworksInto(allPages, &actual) th.AssertNoErr(t, err) th.AssertEquals(t, "d32019d3-bc6e-4319-9c1d-6722fc136a22", actual[0].ID) th.AssertEquals(t, "db193ab3-96e3-4cb3-8fc5-05f4296d0324", actual[1].ID) th.AssertEquals(t, "local", actual[1].NetworkType) th.AssertEquals(t, "1234567890", actual[1].SegmentationID) th.AssertEquals(t, actual[0].Subnets[0], "54d6f61d-db07-451c-9ab3-b9609b6b6f0b") th.AssertEquals(t, actual[1].Subnets[0], "08eae331-0402-425a-923c-34f7cfe39c1b") } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, nettest.GetResponse) }) var s struct { networks.Network provider.NetworkProviderExt } err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "d32019d3-bc6e-4319-9c1d-6722fc136a22", s.ID) th.AssertEquals(t, "", s.PhysicalNetwork) th.AssertEquals(t, "local", s.NetworkType) th.AssertEquals(t, "9876543210", s.SegmentationID) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, nettest.CreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, nettest.CreateResponse) }) var s struct { networks.Network provider.NetworkProviderExt } options := networks.CreateOpts{Name: "private", AdminStateUp: gophercloud.Enabled} err := networks.Create(fake.ServiceClient(), options).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "db193ab3-96e3-4cb3-8fc5-05f4296d0324", s.ID) th.AssertEquals(t, "", s.PhysicalNetwork) th.AssertEquals(t, "local", s.NetworkType) th.AssertEquals(t, "9876543210", s.SegmentationID) } func TestCreateWithMultipleProvider(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "network": { "name": "sample_network", "admin_state_up": true, "shared": true, "tenant_id": "12345", "segments": [ { "provider:segmentation_id": 666, "provider:physical_network": "br-ex", "provider:network_type": "vxlan" }, { "provider:segmentation_id": 615, "provider:physical_network": "br-ex", "provider:network_type": "vxlan" } ] } } `) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "network": { "status": "ACTIVE", "name": "sample_network", "admin_state_up": true, "shared": true, "tenant_id": "12345", "segments": [ { "provider:segmentation_id": 666, "provider:physical_network": "br-ex", "provider:network_type": "vlan" }, { "provider:segmentation_id": 615, "provider:physical_network": "br-ex", "provider:network_type": "vlan" } ] } } `) }) iTrue := true segments := []provider.Segment{ provider.Segment{NetworkType: "vxlan", PhysicalNetwork: "br-ex", SegmentationID: 666}, provider.Segment{NetworkType: "vxlan", PhysicalNetwork: "br-ex", SegmentationID: 615}, } networkCreateOpts := networks.CreateOpts{ Name: "sample_network", AdminStateUp: &iTrue, Shared: &iTrue, TenantID: "12345", } providerCreateOpts := provider.CreateOptsExt{ CreateOptsBuilder: networkCreateOpts, Segments: segments, } _, err := networks.Create(fake.ServiceClient(), providerCreateOpts).Extract() th.AssertNoErr(t, err) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, nettest.UpdateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, nettest.UpdateResponse) }) var s struct { networks.Network provider.NetworkProviderExt } iTrue := true options := networks.UpdateOpts{Name: "new_network_name", AdminStateUp: gophercloud.Disabled, Shared: &iTrue} err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "4e8e5957-649f-477b-9e5b-f1f75b21c03c", s.ID) th.AssertEquals(t, "", s.PhysicalNetwork) th.AssertEquals(t, "local", s.NetworkType) th.AssertEquals(t, "1234567890", s.SegmentationID) } qos/000077500000000000000000000000001335005366400332035ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensionsruletypes/000077500000000000000000000000001335005366400352375ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/qosdoc.go000066400000000000000000000006141335005366400363340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/qos/ruletypes/* Package ruletypes contains functionality for working with Neutron 'quality of service' rule-type resources. Example: You can list rule-types in the following way: page, err := ruletypes.ListRuleTypes(client).AllPages() if err != nil { return } rules, err := ruletypes.ExtractRuleTypes(page) if err != nil { return } fmt.Printf("%v <- Rule Types\n", rules) */ package ruletypes requests.go000066400000000000000000000006461335005366400374470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/qos/ruletypespackage ruletypes import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListRuleTypes returns the list of rule types from the server func ListRuleTypes(c *gophercloud.ServiceClient) (result pagination.Pager) { return pagination.NewPager(c, listRuleTypesURL(c), func(r pagination.PageResult) pagination.Page { return ListRuleTypesPage{pagination.SinglePageBase(r)} }) } results.go000066400000000000000000000010431335005366400372650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/qos/ruletypespackage ruletypes import "github.com/gophercloud/gophercloud/pagination" // The result of listing the qos rule types type RuleType struct { Type string `json:"type"` } type ListRuleTypesPage struct { pagination.SinglePageBase } func (r ListRuleTypesPage) IsEmpty() (bool, error) { v, err := ExtractRuleTypes(r) return len(v) == 0, err } func ExtractRuleTypes(r pagination.Page) ([]RuleType, error) { var s struct { RuleTypes []RuleType `json:"rule_types"` } err := (r.(ListRuleTypesPage)).ExtractInto(&s) return s.RuleTypes, err } testing/000077500000000000000000000000001335005366400367145ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/qos/ruletypesdoc.go000066400000000000000000000000421335005366400400040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/qos/ruletypes/testing// qos unit tests package testing fixtures.go000066400000000000000000000004051335005366400411130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/qos/ruletypes/testingpackage testing const ( ListRuleTypesResponse = ` { "rule_types": [ { "type": "bandwidth_limit" }, { "type": "dscp_marking" }, { "type": "minimum_bandwidth" } ] } ` ) requests_test.go000066400000000000000000000020471335005366400421600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/qos/ruletypes/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/ruletypes" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListRuleTypes(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, ListRuleTypesResponse) }) page, err := ruletypes.ListRuleTypes(fake.ServiceClient()).AllPages() if err != nil { t.Errorf("Failed to list rule types pages: %v", err) return } rules, err := ruletypes.ExtractRuleTypes(page) if err != nil { t.Errorf("Failed to list rule types: %v", err) return } expected := []ruletypes.RuleType{{Type: "bandwidth_limit"}, {Type: "dscp_marking"}, {Type: "minimum_bandwidth"}} th.AssertDeepEquals(t, expected, rules) } urls.go000066400000000000000000000002511335005366400365510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/qos/ruletypespackage ruletypes import "github.com/gophercloud/gophercloud" func listRuleTypesURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("qos", "rule-types") } rbacpolicies/000077500000000000000000000000001335005366400350405ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensionsdoc.go000066400000000000000000000043231335005366400361360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/rbacpolicies/* Package rbacpolicies contains functionality for working with Neutron RBAC Policies. Role-Based Access Control (RBAC) policy framework enables both operators and users to grant access to resources for specific projects. Sharing an object with a specific project is accomplished by creating a policy entry that permits the target project the access_as_shared action on that object. To make a network available as an external network for specific projects rather than all projects, use the access_as_external action. If a network is marked as external during creation, it now implicitly creates a wildcard RBAC policy granting everyone access to preserve previous behavior before this feature was added. Example to Create a RBAC Policy createOpts := rbacpolicies.CreateOpts{ Action: rbacpolicies.ActionAccessShared, ObjectType: "network", TargetTenant: "6e547a3bcfe44702889fdeff3c3520c3", ObjectID: "240d22bf-bd17-4238-9758-25f72610ecdc" } rbacPolicy, err := rbacpolicies.Create(rbacClient, createOpts).Extract() if err != nil { panic(err) } Example to List RBAC Policies listOpts := rbacpolicies.ListOpts{ TenantID: "a99e9b4e620e4db09a2dfb6e42a01e66", } allPages, err := rbacpolicies.List(rbacClient, listOpts).AllPages() if err != nil { panic(err) } allRBACPolicies, err := rbacpolicies.ExtractRBACPolicies(allPages) if err != nil { panic(err) } for _, rbacpolicy := range allRBACPolicies { fmt.Printf("%+v", rbacpolicy) } Example to Delete a RBAC Policy rbacPolicyID := "94fe107f-da78-4d92-a9d7-5611b06dad8d" err := rbacpolicies.Delete(rbacClient, rbacPolicyID).ExtractErr() if err != nil { panic(err) } Example to Get RBAC Policy by ID rbacPolicyID := "94fe107f-da78-4d92-a9d7-5611b06dad8d" rbacpolicy, err := rbacpolicies.Get(rbacClient, rbacPolicyID).Extract() if err != nil { panic(err) } fmt.Printf("%+v", rbacpolicy) Example to Update a RBAC Policy rbacPolicyID := "570b0306-afb5-4d3b-ab47-458fdc16baaa" updateOpts := rbacpolicies.UpdateOpts{ TargetTenant: "9d766060b6354c9e8e2da44cab0e8f38", } rbacPolicy, err := rbacpolicies.Update(rbacClient, rbacPolicyID, updateOpts).Extract() if err != nil { panic(err) } */ package rbacpolicies requests.go000066400000000000000000000114151335005366400372440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/rbacpoliciespackage rbacpolicies import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToRBACPolicyListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the rbac attributes you want to see returned. SortKey allows you to sort // by a particular rbac attribute. SortDir sets the direction, and is either // `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { TargetTenant string `q:"target_tenant"` ObjectType string `q:"object_type"` ObjectID string `q:"object_id"` Action PolicyAction `q:"action"` TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` Marker string `q:"marker"` Limit int `q:"limit"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToRBACPolicyListQuery formats a ListOpts into a query string. func (opts ListOpts) ToRBACPolicyListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // rbac policies. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(c) if opts != nil { query, err := opts.ToRBACPolicyListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return RBACPolicyPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves a specific rbac policy based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(getURL(c, id), &r.Body, nil) return } // PolicyAction maps to Action for the RBAC policy. // Which allows access_as_external or access_as_shared. type PolicyAction string const ( // ActionAccessExternal returns Action for the RBAC policy as access_as_external. ActionAccessExternal PolicyAction = "access_as_external" // ActionAccessShared returns Action for the RBAC policy as access_as_shared. ActionAccessShared PolicyAction = "access_as_shared" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToRBACPolicyCreateMap() (map[string]interface{}, error) } // CreateOpts represents options used to create a rbac-policy. type CreateOpts struct { Action PolicyAction `json:"action" required:"true"` ObjectType string `json:"object_type" required:"true"` TargetTenant string `json:"target_tenant" required:"true"` ObjectID string `json:"object_id" required:"true"` } // ToRBACPolicyCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToRBACPolicyCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "rbac_policy") } // Create accepts a CreateOpts struct and creates a new rbac-policy using the values // provided. // // The tenant ID that is contained in the URI is the tenant that creates the // rbac-policy. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToRBACPolicyCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(createURL(c), b, &r.Body, nil) return } // Delete accepts a unique ID and deletes the rbac-policy associated with it. func Delete(c *gophercloud.ServiceClient, rbacPolicyID string) (r DeleteResult) { _, r.Err = c.Delete(deleteURL(c, rbacPolicyID), nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToRBACPolicyUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents options used to update a rbac-policy. type UpdateOpts struct { TargetTenant string `json:"target_tenant" required:"true"` } // ToRBACPolicyUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToRBACPolicyUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "rbac_policy") } // Update accepts a UpdateOpts struct and updates an existing rbac-policy using the // values provided. func Update(c *gophercloud.ServiceClient, rbacPolicyID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToRBACPolicyUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(updateURL(c, rbacPolicyID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) return } results.go000066400000000000000000000056611335005366400371000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/rbacpoliciespackage rbacpolicies import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts RBAC Policy resource. func (r commonResult) Extract() (*RBACPolicy, error) { var s RBACPolicy err := r.ExtractInto(&s) return &s, err } func (r commonResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "rbac_policy") } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a RBAC Policy. type CreateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a RBAC Policy. type GetResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its // ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a RBAC Policy. type UpdateResult struct { commonResult } // RBACPolicy represents a RBAC policy. type RBACPolicy struct { // UUID of the RBAC policy. ID string `json:"id"` // Action for the RBAC policy which is access_as_external or access_as_shared. Action PolicyAction `json:"action"` // ObjectID is the ID of the object_type resource. // An object_type of network returns a network ID and // object_type of qos-policy returns a QoS ID. ObjectID string `json:"object_id"` // ObjectType is the type of the object that the RBAC policy affects. // Types include qos-policy or network. ObjectType string `json:"object_type"` // TenantID is the ID of the project that owns the resource. TenantID string `json:"tenant_id"` // TargetTenant is the ID of the tenant to which the RBAC policy will be enforced. TargetTenant string `json:"target_tenant"` // ProjectID is the ID of the project. ProjectID string `json:"project_id"` } // RBACPolicyPage is the page returned by a pager when traversing over a // collection of rbac policies. type RBACPolicyPage struct { pagination.LinkedPageBase } // IsEmpty checks whether a RBACPolicyPage struct is empty. func (r RBACPolicyPage) IsEmpty() (bool, error) { is, err := ExtractRBACPolicies(r) return len(is) == 0, err } // ExtractRBACPolicies accepts a Page struct, specifically a RBAC Policy struct, // and extracts the elements into a slice of RBAC Policy structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractRBACPolicies(r pagination.Page) ([]RBACPolicy, error) { var s []RBACPolicy err := ExtractRBACPolicesInto(r, &s) return s, err } // ExtractRBACPolicesInto extracts the elements into a slice of RBAC Policy structs. func ExtractRBACPolicesInto(r pagination.Page, v interface{}) error { return r.(RBACPolicyPage).Result.ExtractIntoSlicePtr(v, "rbac_policies") } testing/000077500000000000000000000000001335005366400365155ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/rbacpoliciesdoc.go000066400000000000000000000000741335005366400376120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/rbacpolicies/testing// Package testing includes rbac unit tests package testing fixtures.go000066400000000000000000000073361335005366400407260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/rbacpolicies/testingpackage testing import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/rbacpolicies" ) const ListResponse = ` { "rbac_policies": [ { "target_tenant": "6e547a3bcfe44702889fdeff3c3520c3", "tenant_id": "3de27ce0a2a54cc6ae06dc62dd0ec832", "object_type": "network", "object_id": "240d22bf-bd17-4238-9758-25f72610ecdc", "action": "access_as_shared", "project_id": "3de27ce0a2a54cc6ae06dc62dd0ec832", "id": "2cf7523a-93b5-4e69-9360-6c6bf986bb7c" }, { "target_tenant": "1a547a3bcfe44702889fdeff3c3520c3", "tenant_id": "1ae27ce0a2a54cc6ae06dc62dd0ec832", "object_type": "network", "object_id": "120d22bf-bd17-4238-9758-25f72610ecdc", "action": "access_as_shared", "project_id": "1ae27ce0a2a54cc6ae06dc62dd0ec832", "id":"1ab7523a-93b5-4e69-9360-6c6bf986bb7c" } ] }` // CreateRequest is the structure of request body to create rbac-policy. const CreateRequest = ` { "rbac_policy": { "action": "access_as_shared", "object_type": "network", "target_tenant": "6e547a3bcfe44702889fdeff3c3520c3", "object_id": "240d22bf-bd17-4238-9758-25f72610ecdc" } }` // CreateResponse is the structure of response body of rbac-policy create. const CreateResponse = ` { "rbac_policy": { "target_tenant": "6e547a3bcfe44702889fdeff3c3520c3", "tenant_id": "3de27ce0a2a54cc6ae06dc62dd0ec832", "object_type": "network", "object_id": "240d22bf-bd17-4238-9758-25f72610ecdc", "action": "access_as_shared", "project_id": "3de27ce0a2a54cc6ae06dc62dd0ec832", "id": "2cf7523a-93b5-4e69-9360-6c6bf986bb7c" } }` // GetResponse is the structure of the response body of rbac-policy get operation. const GetResponse = ` { "rbac_policy": { "target_tenant": "6e547a3bcfe44702889fdeff3c3520c3", "tenant_id": "3de27ce0a2a54cc6ae06dc62dd0ec832", "object_type": "network", "object_id": "240d22bf-bd17-4238-9758-25f72610ecdc", "action": "access_as_shared", "project_id": "3de27ce0a2a54cc6ae06dc62dd0ec832", "id": "2cf7523a-93b5-4e69-9360-6c6bf986bb7c" } }` // UpdateRequest is the structure of request body to update rbac-policy. const UpdateRequest = ` { "rbac_policy": { "target_tenant": "9d766060b6354c9e8e2da44cab0e8f38" } }` // UpdateResponse is the structure of response body of rbac-policy update. const UpdateResponse = ` { "rbac_policy": { "target_tenant": "9d766060b6354c9e8e2da44cab0e8f38", "tenant_id": "3de27ce0a2a54cc6ae06dc62dd0ec832", "object_type": "network", "object_id": "240d22bf-bd17-4238-9758-25f72610ecdc", "action": "access_as_shared", "project_id": "3de27ce0a2a54cc6ae06dc62dd0ec832", "id": "2cf7523a-93b5-4e69-9360-6c6bf986bb7c" } }` var rbacPolicy1 = rbacpolicies.RBACPolicy{ ID: "2cf7523a-93b5-4e69-9360-6c6bf986bb7c", Action: rbacpolicies.ActionAccessShared, ObjectID: "240d22bf-bd17-4238-9758-25f72610ecdc", ObjectType: "network", TenantID: "3de27ce0a2a54cc6ae06dc62dd0ec832", TargetTenant: "6e547a3bcfe44702889fdeff3c3520c3", ProjectID: "3de27ce0a2a54cc6ae06dc62dd0ec832", } var rbacPolicy2 = rbacpolicies.RBACPolicy{ ID: "1ab7523a-93b5-4e69-9360-6c6bf986bb7c", Action: rbacpolicies.ActionAccessShared, ObjectID: "120d22bf-bd17-4238-9758-25f72610ecdc", ObjectType: "network", TenantID: "1ae27ce0a2a54cc6ae06dc62dd0ec832", TargetTenant: "1a547a3bcfe44702889fdeff3c3520c3", ProjectID: "1ae27ce0a2a54cc6ae06dc62dd0ec832", } var ExpectedRBACPoliciesSlice = []rbacpolicies.RBACPolicy{rbacPolicy1, rbacPolicy2} requests_test.go000066400000000000000000000117031335005366400417600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/rbacpolicies/testingpackage testing import ( "fmt" "net/http" "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/rbacpolicies" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/rbac-policies", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, CreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, CreateResponse) }) options := rbacpolicies.CreateOpts{ Action: rbacpolicies.ActionAccessShared, ObjectType: "network", TargetTenant: "6e547a3bcfe44702889fdeff3c3520c3", ObjectID: "240d22bf-bd17-4238-9758-25f72610ecdc", } rbacResult, err := rbacpolicies.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &rbacPolicy1, rbacResult) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/rbac-policies/2cf7523a-93b5-4e69-9360-6c6bf986bb7c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetResponse) }) n, err := rbacpolicies.Get(fake.ServiceClient(), "2cf7523a-93b5-4e69-9360-6c6bf986bb7c").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &rbacPolicy1, n) } func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/rbac-policies", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListResponse) }) client := fake.ServiceClient() count := 0 rbacpolicies.List(client, rbacpolicies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := rbacpolicies.ExtractRBACPolicies(page) if err != nil { t.Errorf("Failed to extract rbac policies: %v", err) return false, err } th.CheckDeepEquals(t, ExpectedRBACPoliciesSlice, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestListWithAllPages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/rbac-policies", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListResponse) }) client := fake.ServiceClient() type newRBACPolicy struct { rbacpolicies.RBACPolicy } var allRBACpolicies []newRBACPolicy allPages, err := rbacpolicies.List(client, rbacpolicies.ListOpts{}).AllPages() th.AssertNoErr(t, err) err = rbacpolicies.ExtractRBACPolicesInto(allPages, &allRBACpolicies) th.AssertNoErr(t, err) th.AssertEquals(t, allRBACpolicies[0].ObjectType, "network") th.AssertEquals(t, allRBACpolicies[0].Action, rbacpolicies.ActionAccessShared) th.AssertEquals(t, allRBACpolicies[1].ProjectID, "1ae27ce0a2a54cc6ae06dc62dd0ec832") th.AssertEquals(t, allRBACpolicies[1].TargetTenant, "1a547a3bcfe44702889fdeff3c3520c3") } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/rbac-policies/71d55b18-d2f8-4c76-a5e6-e0a3dd114361", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := rbacpolicies.Delete(fake.ServiceClient(), "71d55b18-d2f8-4c76-a5e6-e0a3dd114361").ExtractErr() th.AssertNoErr(t, res) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/rbac-policies/2cf7523a-93b5-4e69-9360-6c6bf986bb7c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, UpdateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, UpdateResponse) }) options := rbacpolicies.UpdateOpts{TargetTenant: "9d766060b6354c9e8e2da44cab0e8f38"} rbacResult, err := rbacpolicies.Update(fake.ServiceClient(), "2cf7523a-93b5-4e69-9360-6c6bf986bb7c", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, rbacResult.TargetTenant, "9d766060b6354c9e8e2da44cab0e8f38") th.AssertEquals(t, rbacResult.ID, "2cf7523a-93b5-4e69-9360-6c6bf986bb7c") } urls.go000066400000000000000000000012771335005366400363630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/rbacpoliciespackage rbacpolicies import "github.com/gophercloud/gophercloud" func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("rbac-policies", id) } func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("rbac-policies") } func createURL(c *gophercloud.ServiceClient) string { return rootURL(c) } func listURL(c *gophercloud.ServiceClient) string { return rootURL(c) } func getURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } func deleteURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } func updateURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } security/000077500000000000000000000000001335005366400342505ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensionsdoc.go000066400000000000000000000032101335005366400353400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/security// Package security contains functionality to work with security group and // security group rules Neutron resources. // // Security groups and security group rules allows administrators and tenants // the ability to specify the type of traffic and direction (ingress/egress) // that is allowed to pass through a port. A security group is a container for // security group rules. // // When a port is created in Networking it is associated with a security group. // If a security group is not specified the port is associated with a 'default' // security group. By default, this group drops all ingress traffic and allows // all egress. Rules can be added to this group in order to change the behaviour. // // The basic characteristics of Neutron Security Groups are: // // For ingress traffic (to an instance) // - Only traffic matched with security group rules are allowed. // - When there is no rule defined, all traffic is dropped. // // For egress traffic (from an instance) // - Only traffic matched with security group rules are allowed. // - When there is no rule defined, all egress traffic are dropped. // - When a new security group is created, rules to allow all egress traffic // is automatically added. // // "default security group" is defined for each tenant. // - For the default security group a rule which allows intercommunication // among hosts associated with the default security group is defined by default. // - As a result, all egress traffic and intercommunication in the default // group are allowed and all ingress from outside of the default group is // dropped by default (in the default security group). package security groups/000077500000000000000000000000001335005366400355675ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/securitydoc.go000066400000000000000000000022241335005366400366630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/security/groups/* Package groups provides information and interaction with Security Groups for the OpenStack Networking service. Example to List Security Groups listOpts := groups.ListOpts{ TenantID: "966b3c7d36a24facaf20b7e458bf2192", } allPages, err := groups.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allGroups, err := groups.ExtractGroups(allPages) if err != nil { panic(err) } for _, group := range allGroups { fmt.Printf("%+v\n", group) } Example to Create a Security Group createOpts := groups.CreateOpts{ Name: "group_name", Description: "A Security Group", } group, err := groups.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Security Group groupID := "37d94f8a-d136-465c-ae46-144f0d8ef141" updateOpts := groups.UpdateOpts{ Name: "new_name", } group, err := groups.Update(networkClient, groupID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Security Group groupID := "37d94f8a-d136-465c-ae46-144f0d8ef141" err := groups.Delete(networkClient, groupID).ExtractErr() if err != nil { panic(err) } */ package groups requests.go000066400000000000000000000116101335005366400377700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/security/groupspackage groups import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the group attributes you want to see returned. SortKey allows you to // sort by a particular network attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { ID string `q:"id"` Name string `q:"name"` TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // List returns a Pager which allows you to iterate over a collection of // security groups. It accepts a ListOpts struct, which allows you to filter // and sort the returned collection for greater efficiency. func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { q, err := gophercloud.BuildQueryString(&opts) if err != nil { return pagination.Pager{Err: err} } u := rootURL(c) + q.String() return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page { return SecGroupPage{pagination.LinkedPageBase{PageResult: r}} }) } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToSecGroupCreateMap() (map[string]interface{}, error) } // CreateOpts contains all the values needed to create a new security group. type CreateOpts struct { // Human-readable name for the Security Group. Does not have to be unique. Name string `json:"name" required:"true"` // TenantID is the UUID of the project who owns the Group. // Only administrative users can specify a tenant UUID other than their own. TenantID string `json:"tenant_id,omitempty"` // ProjectID is the UUID of the project who owns the Group. // Only administrative users can specify a tenant UUID other than their own. ProjectID string `json:"project_id,omitempty"` // Describes the security group. Description string `json:"description,omitempty"` } // ToSecGroupCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToSecGroupCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "security_group") } // Create is an operation which provisions a new security group with default // security group rules for the IPv4 and IPv6 ether types. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSecGroupCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToSecGroupUpdateMap() (map[string]interface{}, error) } // UpdateOpts contains all the values needed to update an existing security // group. type UpdateOpts struct { // Human-readable name for the Security Group. Does not have to be unique. Name string `json:"name,omitempty"` // Describes the security group. Description string `json:"description,omitempty"` } // ToSecGroupUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "security_group") } // Update is an operation which updates an existing security group. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToSecGroupUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Get retrieves a particular security group based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // Delete will permanently delete a particular security group based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } // IDFromName is a convenience function that returns a security group's ID, // given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" listOpts := ListOpts{ Name: name, } pages, err := List(client, listOpts).AllPages() if err != nil { return "", err } all, err := ExtractGroups(pages) if err != nil { return "", err } for _, s := range all { if s.Name == name { count++ id = s.ID } } switch count { case 0: return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "security group"} case 1: return id, nil default: return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "security group"} } } results.go000066400000000000000000000060301335005366400376160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/security/groupspackage groups import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules" "github.com/gophercloud/gophercloud/pagination" ) // SecGroup represents a container for security group rules. type SecGroup struct { // The UUID for the security group. ID string // Human-readable name for the security group. Might not be unique. // Cannot be named "default" as that is automatically created for a tenant. Name string // The security group description. Description string // A slice of security group rules that dictate the permitted behaviour for // traffic entering and leaving the group. Rules []rules.SecGroupRule `json:"security_group_rules"` // TenantID is the project owner of the security group. TenantID string `json:"tenant_id"` // ProjectID is the project owner of the security group. ProjectID string `json:"project_id"` } // SecGroupPage is the page returned by a pager when traversing over a // collection of security groups. type SecGroupPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of security groups has // reached the end of a page and the pager seeks to traverse over a new one. In // order to do this, it needs to construct the next page's URL. func (r SecGroupPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"security_groups_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a SecGroupPage struct is empty. func (r SecGroupPage) IsEmpty() (bool, error) { is, err := ExtractGroups(r) return len(is) == 0, err } // ExtractGroups accepts a Page struct, specifically a SecGroupPage struct, // and extracts the elements into a slice of SecGroup structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractGroups(r pagination.Page) ([]SecGroup, error) { var s struct { SecGroups []SecGroup `json:"security_groups"` } err := (r.(SecGroupPage)).ExtractInto(&s) return s.SecGroups, err } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a security group. func (r commonResult) Extract() (*SecGroup, error) { var s struct { SecGroup *SecGroup `json:"security_group"` } err := r.ExtractInto(&s) return s.SecGroup, err } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a SecGroup. type CreateResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a SecGroup. type UpdateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a SecGroup. type GetResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its // ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400372445ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/security/groupsdoc.go000066400000000000000000000000451335005366400403370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/security/groups/testing// groups unit tests package testing fixtures.go000066400000000000000000000115741335005366400414540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/security/groups/testingpackage testing import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules" ) const SecurityGroupListResponse = ` { "security_groups": [ { "description": "default", "id": "85cc3048-abc3-43cc-89b3-377341426ac5", "name": "default", "security_group_rules": [], "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" } ] } ` var SecurityGroup1 = groups.SecGroup{ Description: "default", ID: "85cc3048-abc3-43cc-89b3-377341426ac5", Name: "default", Rules: []rules.SecGroupRule{}, TenantID: "e4f50856753b4dc6afee5fa6b9b6c550", } const SecurityGroupCreateRequest = ` { "security_group": { "name": "new-webservers", "description": "security group for webservers" } } ` const SecurityGroupCreateResponse = ` { "security_group": { "description": "security group for webservers", "id": "2076db17-a522-4506-91de-c6dd8e837028", "name": "new-webservers", "security_group_rules": [ { "direction": "egress", "ethertype": "IPv4", "id": "38ce2d8e-e8f1-48bd-83c2-d33cb9f50c3d", "port_range_max": null, "port_range_min": null, "protocol": null, "remote_group_id": null, "remote_ip_prefix": null, "security_group_id": "2076db17-a522-4506-91de-c6dd8e837028", "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" }, { "direction": "egress", "ethertype": "IPv6", "id": "565b9502-12de-4ffd-91e9-68885cff6ae1", "port_range_max": null, "port_range_min": null, "protocol": null, "remote_group_id": null, "remote_ip_prefix": null, "security_group_id": "2076db17-a522-4506-91de-c6dd8e837028", "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" } ], "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" } } ` const SecurityGroupUpdateRequest = ` { "security_group": { "name": "newer-webservers" } } ` const SecurityGroupUpdateResponse = ` { "security_group": { "description": "security group for webservers", "id": "2076db17-a522-4506-91de-c6dd8e837028", "name": "newer-webservers", "security_group_rules": [ { "direction": "egress", "ethertype": "IPv4", "id": "38ce2d8e-e8f1-48bd-83c2-d33cb9f50c3d", "port_range_max": null, "port_range_min": null, "protocol": null, "remote_group_id": null, "remote_ip_prefix": null, "security_group_id": "2076db17-a522-4506-91de-c6dd8e837028", "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" }, { "direction": "egress", "ethertype": "IPv6", "id": "565b9502-12de-4ffd-91e9-68885cff6ae1", "port_range_max": null, "port_range_min": null, "protocol": null, "remote_group_id": null, "remote_ip_prefix": null, "security_group_id": "2076db17-a522-4506-91de-c6dd8e837028", "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" } ], "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" } } ` const SecurityGroupGetResponse = ` { "security_group": { "description": "default", "id": "85cc3048-abc3-43cc-89b3-377341426ac5", "name": "default", "security_group_rules": [ { "direction": "egress", "ethertype": "IPv6", "id": "3c0e45ff-adaf-4124-b083-bf390e5482ff", "port_range_max": null, "port_range_min": null, "protocol": null, "remote_group_id": null, "remote_ip_prefix": null, "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" }, { "direction": "egress", "ethertype": "IPv4", "id": "93aa42e5-80db-4581-9391-3a608bd0e448", "port_range_max": null, "port_range_min": null, "protocol": null, "remote_group_id": null, "remote_ip_prefix": null, "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" } ], "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" } } ` requests_test.go000066400000000000000000000100071335005366400425030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/security/groups/testingpackage testing import ( "fmt" "net/http" "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/security-groups", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, SecurityGroupListResponse) }) count := 0 groups.List(fake.ServiceClient(), groups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := groups.ExtractGroups(page) if err != nil { t.Errorf("Failed to extract secgroups: %v", err) return false, err } expected := []groups.SecGroup{SecurityGroup1} th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/security-groups", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, SecurityGroupCreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, SecurityGroupCreateResponse) }) opts := groups.CreateOpts{Name: "new-webservers", Description: "security group for webservers"} _, err := groups.Create(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/security-groups/2076db17-a522-4506-91de-c6dd8e837028", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, SecurityGroupUpdateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, SecurityGroupUpdateResponse) }) opts := groups.UpdateOpts{Name: "newer-webservers"} sg, err := groups.Update(fake.ServiceClient(), "2076db17-a522-4506-91de-c6dd8e837028", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "newer-webservers", sg.Name) th.AssertEquals(t, "security group for webservers", sg.Description) th.AssertEquals(t, "2076db17-a522-4506-91de-c6dd8e837028", sg.ID) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/security-groups/85cc3048-abc3-43cc-89b3-377341426ac5", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, SecurityGroupGetResponse) }) sg, err := groups.Get(fake.ServiceClient(), "85cc3048-abc3-43cc-89b3-377341426ac5").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "default", sg.Description) th.AssertEquals(t, "85cc3048-abc3-43cc-89b3-377341426ac5", sg.ID) th.AssertEquals(t, "default", sg.Name) th.AssertEquals(t, 2, len(sg.Rules)) th.AssertEquals(t, "e4f50856753b4dc6afee5fa6b9b6c550", sg.TenantID) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/security-groups/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := groups.Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000004371335005366400371070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/security/groupspackage groups import "github.com/gophercloud/gophercloud" const rootPath = "security-groups" func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, id) } rules/000077500000000000000000000000001335005366400354025ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/securitydoc.go000066400000000000000000000020601335005366400364740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/security/rules/* Package rules provides information and interaction with Security Group Rules for the OpenStack Networking service. Example to List Security Groups Rules listOpts := rules.ListOpts{ Protocol: "tcp", } allPages, err := rules.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allRules, err := rules.ExtractRules(allPages) if err != nil { panic(err) } for _, rule := range allRules { fmt.Printf("%+v\n", rule) } Example to Create a Security Group Rule createOpts := rules.CreateOpts{ Direction: "ingress", PortRangeMin: 80, EtherType: rules.EtherType4, PortRangeMax: 80, Protocol: "tcp", RemoteGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5", SecGroupID: "a7734e61-b545-452d-a3cd-0189cbd9747a", } rule, err := rules.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Delete a Security Group Rule ruleID := "37d94f8a-d136-465c-ae46-144f0d8ef141" err := rules.Delete(networkClient, ruleID).ExtractErr() if err != nil { panic(err) } */ package rules requests.go000066400000000000000000000141521335005366400376070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/security/rulespackage rules import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the security group rule attributes you want to see returned. SortKey allows // you to sort by a particular network attribute. SortDir sets the direction, // and is either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { Direction string `q:"direction"` EtherType string `q:"ethertype"` ID string `q:"id"` PortRangeMax int `q:"port_range_max"` PortRangeMin int `q:"port_range_min"` Protocol string `q:"protocol"` RemoteGroupID string `q:"remote_group_id"` RemoteIPPrefix string `q:"remote_ip_prefix"` SecGroupID string `q:"security_group_id"` TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // List returns a Pager which allows you to iterate over a collection of // security group rules. It accepts a ListOpts struct, which allows you to filter // and sort the returned collection for greater efficiency. func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { q, err := gophercloud.BuildQueryString(&opts) if err != nil { return pagination.Pager{Err: err} } u := rootURL(c) + q.String() return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page { return SecGroupRulePage{pagination.LinkedPageBase{PageResult: r}} }) } type RuleDirection string type RuleProtocol string type RuleEtherType string // Constants useful for CreateOpts const ( DirIngress RuleDirection = "ingress" DirEgress RuleDirection = "egress" EtherType4 RuleEtherType = "IPv4" EtherType6 RuleEtherType = "IPv6" ProtocolAH RuleProtocol = "ah" ProtocolDCCP RuleProtocol = "dccp" ProtocolEGP RuleProtocol = "egp" ProtocolESP RuleProtocol = "esp" ProtocolGRE RuleProtocol = "gre" ProtocolICMP RuleProtocol = "icmp" ProtocolIGMP RuleProtocol = "igmp" ProtocolIPv6Encap RuleProtocol = "ipv6-encap" ProtocolIPv6Frag RuleProtocol = "ipv6-frag" ProtocolIPv6ICMP RuleProtocol = "ipv6-icmp" ProtocolIPv6NoNxt RuleProtocol = "ipv6-nonxt" ProtocolIPv6Opts RuleProtocol = "ipv6-opts" ProtocolIPv6Route RuleProtocol = "ipv6-route" ProtocolOSPF RuleProtocol = "ospf" ProtocolPGM RuleProtocol = "pgm" ProtocolRSVP RuleProtocol = "rsvp" ProtocolSCTP RuleProtocol = "sctp" ProtocolTCP RuleProtocol = "tcp" ProtocolUDP RuleProtocol = "udp" ProtocolUDPLite RuleProtocol = "udplite" ProtocolVRRP RuleProtocol = "vrrp" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToSecGroupRuleCreateMap() (map[string]interface{}, error) } // CreateOpts contains all the values needed to create a new security group // rule. type CreateOpts struct { // Must be either "ingress" or "egress": the direction in which the security // group rule is applied. Direction RuleDirection `json:"direction" required:"true"` // String description of each rule, optional Description string `json:"description" required:"false"` // Must be "IPv4" or "IPv6", and addresses represented in CIDR must match the // ingress or egress rules. EtherType RuleEtherType `json:"ethertype" required:"true"` // The security group ID to associate with this security group rule. SecGroupID string `json:"security_group_id" required:"true"` // The maximum port number in the range that is matched by the security group // rule. The PortRangeMin attribute constrains the PortRangeMax attribute. If // the protocol is ICMP, this value must be an ICMP type. PortRangeMax int `json:"port_range_max,omitempty"` // The minimum port number in the range that is matched by the security group // rule. If the protocol is TCP or UDP, this value must be less than or equal // to the value of the PortRangeMax attribute. If the protocol is ICMP, this // value must be an ICMP type. PortRangeMin int `json:"port_range_min,omitempty"` // The protocol that is matched by the security group rule. Valid values are // "tcp", "udp", "icmp" or an empty string. Protocol RuleProtocol `json:"protocol,omitempty"` // The remote group ID to be associated with this security group rule. You can // specify either RemoteGroupID or RemoteIPPrefix. RemoteGroupID string `json:"remote_group_id,omitempty"` // The remote IP prefix to be associated with this security group rule. You can // specify either RemoteGroupID or RemoteIPPrefix. This attribute matches the // specified IP prefix as the source IP address of the IP packet. RemoteIPPrefix string `json:"remote_ip_prefix,omitempty"` // TenantID is the UUID of the project who owns the Rule. // Only administrative users can specify a project UUID other than their own. ProjectID string `json:"project_id,omitempty"` } // ToSecGroupRuleCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToSecGroupRuleCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "security_group_rule") } // Create is an operation which adds a new security group rule and associates it // with an existing security group (whose ID is specified in CreateOpts). func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSecGroupRuleCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular security group rule based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // Delete will permanently delete a particular security group rule based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } results.go000066400000000000000000000105331335005366400374340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/security/rulespackage rules import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // SecGroupRule represents a rule to dictate the behaviour of incoming or // outgoing traffic for a particular security group. type SecGroupRule struct { // The UUID for this security group rule. ID string // The direction in which the security group rule is applied. The only values // allowed are "ingress" or "egress". For a compute instance, an ingress // security group rule is applied to incoming (ingress) traffic for that // instance. An egress rule is applied to traffic leaving the instance. Direction string // Descripton of the rule Description string `json:"description"` // Must be IPv4 or IPv6, and addresses represented in CIDR must match the // ingress or egress rules. EtherType string `json:"ethertype"` // The security group ID to associate with this security group rule. SecGroupID string `json:"security_group_id"` // The minimum port number in the range that is matched by the security group // rule. If the protocol is TCP or UDP, this value must be less than or equal // to the value of the PortRangeMax attribute. If the protocol is ICMP, this // value must be an ICMP type. PortRangeMin int `json:"port_range_min"` // The maximum port number in the range that is matched by the security group // rule. The PortRangeMin attribute constrains the PortRangeMax attribute. If // the protocol is ICMP, this value must be an ICMP type. PortRangeMax int `json:"port_range_max"` // The protocol that is matched by the security group rule. Valid values are // "tcp", "udp", "icmp" or an empty string. Protocol string // The remote group ID to be associated with this security group rule. You // can specify either RemoteGroupID or RemoteIPPrefix. RemoteGroupID string `json:"remote_group_id"` // The remote IP prefix to be associated with this security group rule. You // can specify either RemoteGroupID or RemoteIPPrefix . This attribute // matches the specified IP prefix as the source IP address of the IP packet. RemoteIPPrefix string `json:"remote_ip_prefix"` // TenantID is the project owner of this security group rule. TenantID string `json:"tenant_id"` // ProjectID is the project owner of this security group rule. ProjectID string `json:"project_id"` } // SecGroupRulePage is the page returned by a pager when traversing over a // collection of security group rules. type SecGroupRulePage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of security group rules has // reached the end of a page and the pager seeks to traverse over a new one. In // order to do this, it needs to construct the next page's URL. func (r SecGroupRulePage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"security_group_rules_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a SecGroupRulePage struct is empty. func (r SecGroupRulePage) IsEmpty() (bool, error) { is, err := ExtractRules(r) return len(is) == 0, err } // ExtractRules accepts a Page struct, specifically a SecGroupRulePage struct, // and extracts the elements into a slice of SecGroupRule structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractRules(r pagination.Page) ([]SecGroupRule, error) { var s struct { SecGroupRules []SecGroupRule `json:"security_group_rules"` } err := (r.(SecGroupRulePage)).ExtractInto(&s) return s.SecGroupRules, err } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a security rule. func (r commonResult) Extract() (*SecGroupRule, error) { var s struct { SecGroupRule *SecGroupRule `json:"security_group_rule"` } err := r.ExtractInto(&s) return s.SecGroupRule, err } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a SecGroupRule. type CreateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a SecGroupRule. type GetResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its // ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400370575ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/security/rulesdoc.go000066400000000000000000000000441335005366400401510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/security/rules/testing// rules unit tests package testing requests_test.go000066400000000000000000000162621335005366400423270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/security/rules/testingpackage testing import ( "fmt" "net/http" "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/security-group-rules", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "security_group_rules": [ { "direction": "egress", "ethertype": "IPv6", "id": "3c0e45ff-adaf-4124-b083-bf390e5482ff", "port_range_max": null, "port_range_min": null, "protocol": null, "remote_group_id": null, "remote_ip_prefix": null, "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" }, { "direction": "egress", "ethertype": "IPv4", "id": "93aa42e5-80db-4581-9391-3a608bd0e448", "port_range_max": null, "port_range_min": null, "protocol": null, "remote_group_id": null, "remote_ip_prefix": null, "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" } ] } `) }) count := 0 rules.List(fake.ServiceClient(), rules.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := rules.ExtractRules(page) if err != nil { t.Errorf("Failed to extract secrules: %v", err) return false, err } expected := []rules.SecGroupRule{ { Description: "", Direction: "egress", EtherType: "IPv6", ID: "3c0e45ff-adaf-4124-b083-bf390e5482ff", PortRangeMax: 0, PortRangeMin: 0, Protocol: "", RemoteGroupID: "", RemoteIPPrefix: "", SecGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5", TenantID: "e4f50856753b4dc6afee5fa6b9b6c550", }, { Direction: "egress", EtherType: "IPv4", ID: "93aa42e5-80db-4581-9391-3a608bd0e448", PortRangeMax: 0, PortRangeMin: 0, Protocol: "", RemoteGroupID: "", RemoteIPPrefix: "", SecGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5", TenantID: "e4f50856753b4dc6afee5fa6b9b6c550", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/security-group-rules", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "security_group_rule": { "description": "test description of rule", "direction": "ingress", "port_range_min": 80, "ethertype": "IPv4", "port_range_max": 80, "protocol": "tcp", "remote_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", "security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "security_group_rule": { "description": "test description of rule", "direction": "ingress", "ethertype": "IPv4", "id": "2bc0accf-312e-429a-956e-e4407625eb62", "port_range_max": 80, "port_range_min": 80, "protocol": "tcp", "remote_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", "remote_ip_prefix": null, "security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a", "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" } } `) }) opts := rules.CreateOpts{ Description: "test description of rule", Direction: "ingress", PortRangeMin: 80, EtherType: rules.EtherType4, PortRangeMax: 80, Protocol: "tcp", RemoteGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5", SecGroupID: "a7734e61-b545-452d-a3cd-0189cbd9747a", } _, err := rules.Create(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) } func TestRequiredCreateOpts(t *testing.T) { res := rules.Create(fake.ServiceClient(), rules.CreateOpts{Direction: rules.DirIngress}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = rules.Create(fake.ServiceClient(), rules.CreateOpts{Direction: rules.DirIngress, EtherType: rules.EtherType4}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = rules.Create(fake.ServiceClient(), rules.CreateOpts{Direction: rules.DirIngress, EtherType: rules.EtherType4}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = rules.Create(fake.ServiceClient(), rules.CreateOpts{Direction: rules.DirIngress, EtherType: rules.EtherType4, SecGroupID: "something", Protocol: "foo"}) if res.Err == nil { t.Fatalf("Expected error, got none") } } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/security-group-rules/3c0e45ff-adaf-4124-b083-bf390e5482ff", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "security_group_rule": { "direction": "egress", "ethertype": "IPv6", "id": "3c0e45ff-adaf-4124-b083-bf390e5482ff", "port_range_max": null, "port_range_min": null, "protocol": null, "remote_group_id": null, "remote_ip_prefix": null, "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" } } `) }) sr, err := rules.Get(fake.ServiceClient(), "3c0e45ff-adaf-4124-b083-bf390e5482ff").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "egress", sr.Direction) th.AssertEquals(t, "IPv6", sr.EtherType) th.AssertEquals(t, "3c0e45ff-adaf-4124-b083-bf390e5482ff", sr.ID) th.AssertEquals(t, 0, sr.PortRangeMax) th.AssertEquals(t, 0, sr.PortRangeMin) th.AssertEquals(t, "", sr.Protocol) th.AssertEquals(t, "", sr.RemoteGroupID) th.AssertEquals(t, "", sr.RemoteIPPrefix) th.AssertEquals(t, "85cc3048-abc3-43cc-89b3-377341426ac5", sr.SecGroupID) th.AssertEquals(t, "e4f50856753b4dc6afee5fa6b9b6c550", sr.TenantID) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/security-group-rules/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := rules.Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000004431335005366400367170ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/security/rulespackage rules import "github.com/gophercloud/gophercloud" const rootPath = "security-group-rules" func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, id) } subnetpools/000077500000000000000000000000001335005366400347565ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensionsdoc.go000066400000000000000000000030701335005366400360520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/subnetpools/* Package subnetpools provides the ability to retrieve and manage subnetpools through the Neutron API. Example of Listing Subnetpools listOpts := subnets.ListOpts{ IPVersion: 6, } allPages, err := subnetpools.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allSubnetpools, err := subnetpools.ExtractSubnetPools(allPages) if err != nil { panic(err) } for _, subnetpools := range allSubnetpools { fmt.Printf("%+v\n", subnetpools) } Example to Get a Subnetpool subnetPoolID = "23d5d3f7-9dfa-4f73-b72b-8b0b0063ec55" subnetPool, err := subnetpools.Get(networkClient, subnetPoolID).Extract() if err != nil { panic(err) } Example to Create a new Subnetpool subnetPoolName := "private_pool" subnetPoolPrefixes := []string{ "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", } subnetPoolOpts := subnetpools.CreateOpts{ Name: subnetPoolName, Prefixes: subnetPoolPrefixes, } subnetPool, err := subnetpools.Create(networkClient, subnetPoolOpts).Extract() if err != nil { panic(err) } Example to Update a Subnetpool subnetPoolID := "099546ca-788d-41e5-a76d-17d8cd282d3e" updateOpts := networks.UpdateOpts{ Prefixes: []string{ "fdf7:b13d:dead:beef::/64", }, MaxPrefixLen: 72, } subnetPool, err := subnetpools.Update(networkClient, subnetPoolID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Subnetpool subnetPoolID := "23d5d3f7-9dfa-4f73-b72b-8b0b0063ec55" err := subnetpools.Delete(networkClient, subnetPoolID).ExtractErr() if err != nil { panic(err) } */ package subnetpools requests.go000066400000000000000000000201641335005366400371630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/subnetpoolspackage subnetpools import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToSubnetPoolListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the Neutron API. Filtering is achieved by passing in struct field values // that map to the subnetpool attributes you want to see returned. // SortKey allows you to sort by a particular subnetpool attribute. // SortDir sets the direction, and is either `asc' or `desc'. // Marker and Limit are used for the pagination. type ListOpts struct { ID string `q:"id"` Name string `q:"name"` DefaultQuota int `q:"default_quota"` TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` DefaultPrefixLen int `q:"default_prefixlen"` MinPrefixLen int `q:"min_prefixlen"` MaxPrefixLen int `q:"max_prefixlen"` AddressScopeID string `q:"address_scope_id"` IPVersion int `q:"ip_version"` Shared *bool `q:"shared"` Description string `q:"description"` IsDefault *bool `q:"is_default"` RevisionNumber int `q:"revision_number"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToSubnetPoolListQuery formats a ListOpts into a query string. func (opts ListOpts) ToSubnetPoolListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // subnetpools. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. // // Default policy settings return only the subnetpools owned by the project // of the user submitting the request, unless the user has the administrative role. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(c) if opts != nil { query, err := opts.ToSubnetPoolListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return SubnetPoolPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves a specific subnetpool based on its ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(getURL(c, id), &r.Body, nil) return } // CreateOptsBuilder allows to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToSubnetPoolCreateMap() (map[string]interface{}, error) } // CreateOpts specifies parameters of a new subnetpool. type CreateOpts struct { // Name is the human-readable name of the subnetpool. Name string `json:"name"` // DefaultQuota is the per-project quota on the prefix space // that can be allocated from the subnetpool for project subnets. DefaultQuota int `json:"default_quota,omitempty"` // TenantID is the id of the Identity project. TenantID string `json:"tenant_id,omitempty"` // ProjectID is the id of the Identity project. ProjectID string `json:"project_id,omitempty"` // Prefixes is the list of subnet prefixes to assign to the subnetpool. // Neutron API merges adjacent prefixes and treats them as a single prefix. // Each subnet prefix must be unique among all subnet prefixes in all subnetpools // that are associated with the address scope. Prefixes []string `json:"prefixes"` // DefaultPrefixLen is the size of the prefix to allocate when the cidr // or prefixlen attributes are omitted when you create the subnet. // Defaults to the MinPrefixLen. DefaultPrefixLen int `json:"default_prefixlen,omitempty"` // MinPrefixLen is the smallest prefix that can be allocated from a subnetpool. // For IPv4 subnetpools, default is 8. // For IPv6 subnetpools, default is 64. MinPrefixLen int `json:"min_prefixlen,omitempty"` // MaxPrefixLen is the maximum prefix size that can be allocated from the subnetpool. // For IPv4 subnetpools, default is 32. // For IPv6 subnetpools, default is 128. MaxPrefixLen int `json:"max_prefixlen,omitempty"` // AddressScopeID is the Neutron address scope to assign to the subnetpool. AddressScopeID string `json:"address_scope_id,omitempty"` // Shared indicates whether this network is shared across all projects. Shared bool `json:"shared,omitempty"` // Description is the human-readable description for the resource. Description string `json:"description,omitempty"` // IsDefault indicates if the subnetpool is default pool or not. IsDefault bool `json:"is_default,omitempty"` } // ToSubnetPoolCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToSubnetPoolCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "subnetpool") } // Create requests the creation of a new subnetpool on the server. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSubnetPoolCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToSubnetPoolUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents options used to update a network. type UpdateOpts struct { // Name is the human-readable name of the subnetpool. Name string `json:"name,omitempty"` // DefaultQuota is the per-project quota on the prefix space // that can be allocated from the subnetpool for project subnets. DefaultQuota *int `json:"default_quota,omitempty"` // TenantID is the id of the Identity project. TenantID string `json:"tenant_id,omitempty"` // ProjectID is the id of the Identity project. ProjectID string `json:"project_id,omitempty"` // Prefixes is the list of subnet prefixes to assign to the subnetpool. // Neutron API merges adjacent prefixes and treats them as a single prefix. // Each subnet prefix must be unique among all subnet prefixes in all subnetpools // that are associated with the address scope. Prefixes []string `json:"prefixes,omitempty"` // DefaultPrefixLen is yhe size of the prefix to allocate when the cidr // or prefixlen attributes are omitted when you create the subnet. // Defaults to the MinPrefixLen. DefaultPrefixLen int `json:"default_prefixlen,omitempty"` // MinPrefixLen is the smallest prefix that can be allocated from a subnetpool. // For IPv4 subnetpools, default is 8. // For IPv6 subnetpools, default is 64. MinPrefixLen int `json:"min_prefixlen,omitempty"` // MaxPrefixLen is the maximum prefix size that can be allocated from the subnetpool. // For IPv4 subnetpools, default is 32. // For IPv6 subnetpools, default is 128. MaxPrefixLen int `json:"max_prefixlen,omitempty"` // AddressScopeID is the Neutron address scope to assign to the subnetpool. AddressScopeID *string `json:"address_scope_id,omitempty"` // Description is thehuman-readable description for the resource. Description *string `json:"description,omitempty"` // IsDefault indicates if the subnetpool is default pool or not. IsDefault *bool `json:"is_default,omitempty"` } // ToSubnetPoolUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToSubnetPoolUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "subnetpool") } // Update accepts a UpdateOpts struct and updates an existing subnetpool using the // values provided. func Update(c *gophercloud.ServiceClient, subnetPoolID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToSubnetPoolUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(updateURL(c, subnetPoolID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete accepts a unique ID and deletes the subnetpool associated with it. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(deleteURL(c, id), nil) return } results.go000066400000000000000000000133101335005366400370040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/subnetpoolspackage subnetpools import ( "encoding/json" "fmt" "strconv" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a subnetpool resource. func (r commonResult) Extract() (*SubnetPool, error) { var s struct { SubnetPool *SubnetPool `json:"subnetpool"` } err := r.ExtractInto(&s) return s.SubnetPool, err } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a SubnetPool. type GetResult struct { commonResult } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a SubnetPool. type CreateResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a SubnetPool. type UpdateResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its // ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // SubnetPool represents a Neutron subnetpool. // A subnetpool is a pool of addresses from which subnets can be allocated. type SubnetPool struct { // ID is the id of the subnetpool. ID string `json:"id"` // Name is the human-readable name of the subnetpool. Name string `json:"name"` // DefaultQuota is the per-project quota on the prefix space // that can be allocated from the subnetpool for project subnets. DefaultQuota int `json:"default_quota"` // TenantID is the id of the Identity project. TenantID string `json:"tenant_id"` // ProjectID is the id of the Identity project. ProjectID string `json:"project_id"` // CreatedAt is the time at which subnetpool has been created. CreatedAt time.Time `json:"created_at"` // UpdatedAt is the time at which subnetpool has been created. UpdatedAt time.Time `json:"updated_at"` // Prefixes is the list of subnet prefixes to assign to the subnetpool. // Neutron API merges adjacent prefixes and treats them as a single prefix. // Each subnet prefix must be unique among all subnet prefixes in all subnetpools // that are associated with the address scope. Prefixes []string `json:"prefixes"` // DefaultPrefixLen is yhe size of the prefix to allocate when the cidr // or prefixlen attributes are omitted when you create the subnet. // Defaults to the MinPrefixLen. DefaultPrefixLen int `json:"-"` // MinPrefixLen is the smallest prefix that can be allocated from a subnetpool. // For IPv4 subnetpools, default is 8. // For IPv6 subnetpools, default is 64. MinPrefixLen int `json:"-"` // MaxPrefixLen is the maximum prefix size that can be allocated from the subnetpool. // For IPv4 subnetpools, default is 32. // For IPv6 subnetpools, default is 128. MaxPrefixLen int `json:"-"` // AddressScopeID is the Neutron address scope to assign to the subnetpool. AddressScopeID string `json:"address_scope_id"` // IPversion is the IP protocol version. // Valid value is 4 or 6. Default is 4. IPversion int `json:"ip_version"` // Shared indicates whether this network is shared across all projects. Shared bool `json:"shared"` // Description is thehuman-readable description for the resource. Description string `json:"description"` // IsDefault indicates if the subnetpool is default pool or not. IsDefault bool `json:"is_default"` // RevisionNumber is the revision number of the subnetpool. RevisionNumber int `json:"revision_number"` } func (r *SubnetPool) UnmarshalJSON(b []byte) error { type tmp SubnetPool var s struct { tmp DefaultPrefixLen interface{} `json:"default_prefixlen"` MinPrefixLen interface{} `json:"min_prefixlen"` MaxPrefixLen interface{} `json:"max_prefixlen"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = SubnetPool(s.tmp) switch t := s.DefaultPrefixLen.(type) { case string: if r.DefaultPrefixLen, err = strconv.Atoi(t); err != nil { return err } case float64: r.DefaultPrefixLen = int(t) default: return fmt.Errorf("DefaultPrefixLen has unexpected type: %T", t) } switch t := s.MinPrefixLen.(type) { case string: if r.MinPrefixLen, err = strconv.Atoi(t); err != nil { return err } case float64: r.MinPrefixLen = int(t) default: return fmt.Errorf("MinPrefixLen has unexpected type: %T", t) } switch t := s.MaxPrefixLen.(type) { case string: if r.MaxPrefixLen, err = strconv.Atoi(t); err != nil { return err } case float64: r.MaxPrefixLen = int(t) default: return fmt.Errorf("MaxPrefixLen has unexpected type: %T", t) } return nil } // SubnetPoolPage stores a single page of SubnetPools from a List() API call. type SubnetPoolPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of subnetpools has reached // the end of a page and the pager seeks to traverse over a new one. // In order to do this, it needs to construct the next page's URL. func (r SubnetPoolPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"subnetpools_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty determines whether or not a SubnetPoolPage is empty. func (r SubnetPoolPage) IsEmpty() (bool, error) { subnetpools, err := ExtractSubnetPools(r) return len(subnetpools) == 0, err } // ExtractSubnetPools interprets the results of a single page from a List() API call, // producing a slice of SubnetPools structs. func ExtractSubnetPools(r pagination.Page) ([]SubnetPool, error) { var s struct { SubnetPools []SubnetPool `json:"subnetpools"` } err := (r.(SubnetPoolPage)).ExtractInto(&s) return s.SubnetPools, err } testing/000077500000000000000000000000001335005366400364335ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/subnetpoolsdoc.go000066400000000000000000000000521335005366400375240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/subnetpools/testing// subnetpools unit tests package testing fixtures.go000066400000000000000000000165431335005366400406440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/subnetpools/testingpackage testing import ( "time" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools" ) const SubnetPoolsListResult = ` { "subnetpools": [ { "address_scope_id": null, "created_at": "2017-12-28T07:21:41Z", "default_prefixlen": "8", "default_quota": null, "description": "IPv4", "id": "d43a57fe-3390-4608-b437-b1307b0adb40", "ip_version": 4, "is_default": false, "max_prefixlen": "32", "min_prefixlen": "8", "name": "MyPoolIpv4", "prefixes": [ "10.10.10.0/24", "10.11.11.0/24" ], "project_id": "1e2b9857295a4a3e841809ef492812c5", "revision_number": 1, "shared": false, "tenant_id": "1e2b9857295a4a3e841809ef492812c5", "updated_at": "2017-12-28T07:21:41Z" }, { "address_scope_id": "0bc38e22-be49-4e67-969e-fec3f36508bd", "created_at": "2017-12-28T07:21:34Z", "default_prefixlen": "64", "default_quota": null, "description": "IPv6", "id": "832cb7f3-59fe-40cf-8f64-8350ffc03272", "ip_version": 6, "is_default": true, "max_prefixlen": "128", "min_prefixlen": "64", "name": "MyPoolIpv6", "prefixes": [ "fdf7:b13d:dead:beef::/64", "fd65:86cc:a334:39b7::/64" ], "project_id": "1e2b9857295a4a3e841809ef492812c5", "revision_number": 1, "shared": false, "tenant_id": "1e2b9857295a4a3e841809ef492812c5", "updated_at": "2017-12-28T07:21:34Z" }, { "address_scope_id": null, "created_at": "2017-12-28T07:21:27Z", "default_prefixlen": "64", "default_quota": 4, "description": "PublicPool", "id": "2fe18ae6-58c2-4a85-8bfb-566d6426749b", "ip_version": 6, "is_default": false, "max_prefixlen": "128", "min_prefixlen": "64", "name": "PublicIPv6", "prefixes": [ "2001:db8::a3/64" ], "project_id": "ceb366d50ad54fe39717df3af60f9945", "revision_number": 1, "shared": true, "tenant_id": "ceb366d50ad54fe39717df3af60f9945", "updated_at": "2017-12-28T07:21:27Z" } ] } ` var SubnetPool1 = subnetpools.SubnetPool{ AddressScopeID: "", CreatedAt: time.Date(2017, 12, 28, 7, 21, 41, 0, time.UTC), DefaultPrefixLen: 8, DefaultQuota: 0, Description: "IPv4", ID: "d43a57fe-3390-4608-b437-b1307b0adb40", IPversion: 4, IsDefault: false, MaxPrefixLen: 32, MinPrefixLen: 8, Name: "MyPoolIpv4", Prefixes: []string{ "10.10.10.0/24", "10.11.11.0/24", }, ProjectID: "1e2b9857295a4a3e841809ef492812c5", TenantID: "1e2b9857295a4a3e841809ef492812c5", RevisionNumber: 1, Shared: false, UpdatedAt: time.Date(2017, 12, 28, 7, 21, 41, 0, time.UTC), } var SubnetPool2 = subnetpools.SubnetPool{ AddressScopeID: "0bc38e22-be49-4e67-969e-fec3f36508bd", CreatedAt: time.Date(2017, 12, 28, 7, 21, 34, 0, time.UTC), DefaultPrefixLen: 64, DefaultQuota: 0, Description: "IPv6", ID: "832cb7f3-59fe-40cf-8f64-8350ffc03272", IPversion: 6, IsDefault: true, MaxPrefixLen: 128, MinPrefixLen: 64, Name: "MyPoolIpv6", Prefixes: []string{ "fdf7:b13d:dead:beef::/64", "fd65:86cc:a334:39b7::/64", }, ProjectID: "1e2b9857295a4a3e841809ef492812c5", TenantID: "1e2b9857295a4a3e841809ef492812c5", RevisionNumber: 1, Shared: false, UpdatedAt: time.Date(2017, 12, 28, 7, 21, 34, 0, time.UTC), } var SubnetPool3 = subnetpools.SubnetPool{ AddressScopeID: "", CreatedAt: time.Date(2017, 12, 28, 7, 21, 27, 0, time.UTC), DefaultPrefixLen: 64, DefaultQuota: 4, Description: "PublicPool", ID: "2fe18ae6-58c2-4a85-8bfb-566d6426749b", IPversion: 6, IsDefault: false, MaxPrefixLen: 128, MinPrefixLen: 64, Name: "PublicIPv6", Prefixes: []string{ "2001:db8::a3/64", }, ProjectID: "ceb366d50ad54fe39717df3af60f9945", TenantID: "ceb366d50ad54fe39717df3af60f9945", RevisionNumber: 1, Shared: true, UpdatedAt: time.Date(2017, 12, 28, 7, 21, 27, 0, time.UTC), } const SubnetPoolGetResult = ` { "subnetpool": { "min_prefixlen": "64", "address_scope_id": null, "default_prefixlen": "64", "id": "0a738452-8057-4ad3-89c2-92f6a74afa76", "max_prefixlen": "128", "name": "my-ipv6-pool", "default_quota": 2, "is_default": true, "project_id": "1e2b9857295a4a3e841809ef492812c5", "tenant_id": "1e2b9857295a4a3e841809ef492812c5", "created_at": "2018-01-01T00:00:01Z", "prefixes": [ "2001:db8::a3/64" ], "updated_at": "2018-01-01T00:10:10Z", "ip_version": 6, "shared": false, "description": "ipv6 prefixes", "revision_number": 2 } } ` const SubnetPoolCreateRequest = ` { "subnetpool": { "name": "my_ipv4_pool", "prefixes": [ "10.10.0.0/16", "10.11.11.0/24" ], "address_scope_id": "3d4e2e2a-552b-42ad-a16d-820bbf3edaf3", "min_prefixlen": 25, "max_prefixlen": 30, "description": "ipv4 prefixes" } } ` const SubnetPoolCreateResult = ` { "subnetpool": { "address_scope_id": "3d4e2e2a-552b-42ad-a16d-820bbf3edaf3", "created_at": "2018-01-01T00:00:15Z", "default_prefixlen": "25", "default_quota": null, "description": "ipv4 prefixes", "id": "55b5999c-c2fe-42cd-bce0-961a551b80f5", "ip_version": 4, "is_default": false, "max_prefixlen": "30", "min_prefixlen": "25", "name": "my_ipv4_pool", "prefixes": [ "10.10.0.0/16", "10.11.11.0/24" ], "project_id": "1e2b9857295a4a3e841809ef492812c5", "revision_number": 1, "shared": false, "tenant_id": "1e2b9857295a4a3e841809ef492812c5", "updated_at": "2018-01-01T00:00:15Z" } } ` const SubnetPoolUpdateRequest = ` { "subnetpool": { "name": "new_subnetpool_name", "prefixes": [ "10.11.12.0/24", "10.24.0.0/16" ], "max_prefixlen": 16, "address_scope_id": "", "default_quota": 0, "description": "" } } ` const SubnetPoolUpdateResponse = ` { "subnetpool": { "address_scope_id": null, "created_at": "2018-01-03T07:21:34Z", "default_prefixlen": 8, "default_quota": null, "description": null, "id": "099546ca-788d-41e5-a76d-17d8cd282d3e", "ip_version": 4, "is_default": true, "max_prefixlen": 16, "min_prefixlen": 8, "name": "new_subnetpool_name", "prefixes": [ "10.8.0.0/16", "10.11.12.0/24", "10.24.0.0/16" ], "revision_number": 2, "shared": false, "tenant_id": "1e2b9857295a4a3e841809ef492812c5", "updated_at": "2018-01-05T09:56:56Z" } } ` requests_test.go000066400000000000000000000131071335005366400416760ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/subnetpools/testingpackage testing import ( "fmt" "net/http" "testing" "time" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/subnetpools", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, SubnetPoolsListResult) }) count := 0 subnetpools.List(fake.ServiceClient(), subnetpools.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := subnetpools.ExtractSubnetPools(page) if err != nil { t.Errorf("Failed to extract subnetpools: %v", err) return false, nil } expected := []subnetpools.SubnetPool{ SubnetPool1, SubnetPool2, SubnetPool3, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/subnetpools/0a738452-8057-4ad3-89c2-92f6a74afa76", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, SubnetPoolGetResult) }) s, err := subnetpools.Get(fake.ServiceClient(), "0a738452-8057-4ad3-89c2-92f6a74afa76").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.ID, "0a738452-8057-4ad3-89c2-92f6a74afa76") th.AssertEquals(t, s.Name, "my-ipv6-pool") th.AssertEquals(t, s.DefaultQuota, 2) th.AssertEquals(t, s.TenantID, "1e2b9857295a4a3e841809ef492812c5") th.AssertEquals(t, s.ProjectID, "1e2b9857295a4a3e841809ef492812c5") th.AssertEquals(t, s.CreatedAt, time.Date(2018, 1, 1, 0, 0, 1, 0, time.UTC)) th.AssertEquals(t, s.UpdatedAt, time.Date(2018, 1, 1, 0, 10, 10, 0, time.UTC)) th.AssertDeepEquals(t, s.Prefixes, []string{ "2001:db8::a3/64", }) th.AssertEquals(t, s.DefaultPrefixLen, 64) th.AssertEquals(t, s.MinPrefixLen, 64) th.AssertEquals(t, s.MaxPrefixLen, 128) th.AssertEquals(t, s.AddressScopeID, "") th.AssertEquals(t, s.IPversion, 6) th.AssertEquals(t, s.Shared, false) th.AssertEquals(t, s.Description, "ipv6 prefixes") th.AssertEquals(t, s.IsDefault, true) th.AssertEquals(t, s.RevisionNumber, 2) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/subnetpools", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, SubnetPoolCreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, SubnetPoolCreateResult) }) opts := subnetpools.CreateOpts{ Name: "my_ipv4_pool", Prefixes: []string{ "10.10.0.0/16", "10.11.11.0/24", }, MinPrefixLen: 25, MaxPrefixLen: 30, AddressScopeID: "3d4e2e2a-552b-42ad-a16d-820bbf3edaf3", Description: "ipv4 prefixes", } s, err := subnetpools.Create(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "my_ipv4_pool") th.AssertDeepEquals(t, s.Prefixes, []string{ "10.10.0.0/16", "10.11.11.0/24", }) th.AssertEquals(t, s.MinPrefixLen, 25) th.AssertEquals(t, s.MaxPrefixLen, 30) th.AssertEquals(t, s.AddressScopeID, "3d4e2e2a-552b-42ad-a16d-820bbf3edaf3") th.AssertEquals(t, s.Description, "ipv4 prefixes") } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/subnetpools/099546ca-788d-41e5-a76d-17d8cd282d3e", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, SubnetPoolUpdateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, SubnetPoolUpdateResponse) }) nullString := "" nullInt := 0 updateOpts := subnetpools.UpdateOpts{ Name: "new_subnetpool_name", Prefixes: []string{ "10.11.12.0/24", "10.24.0.0/16", }, MaxPrefixLen: 16, AddressScopeID: &nullString, DefaultQuota: &nullInt, Description: &nullString, } n, err := subnetpools.Update(fake.ServiceClient(), "099546ca-788d-41e5-a76d-17d8cd282d3e", updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, "new_subnetpool_name") th.AssertDeepEquals(t, n.Prefixes, []string{ "10.8.0.0/16", "10.11.12.0/24", "10.24.0.0/16", }) th.AssertEquals(t, n.MaxPrefixLen, 16) th.AssertEquals(t, n.ID, "099546ca-788d-41e5-a76d-17d8cd282d3e") th.AssertEquals(t, n.AddressScopeID, "") th.AssertEquals(t, n.DefaultQuota, 0) th.AssertEquals(t, n.Description, "") } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/subnetpools/099546ca-788d-41e5-a76d-17d8cd282d3e", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := subnetpools.Delete(fake.ServiceClient(), "099546ca-788d-41e5-a76d-17d8cd282d3e") th.AssertNoErr(t, res.Err) } urls.go000066400000000000000000000013341335005366400362730ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/subnetpoolspackage subnetpools import "github.com/gophercloud/gophercloud" const resourcePath = "subnetpools" func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id) } func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } func listURL(c *gophercloud.ServiceClient) string { return rootURL(c) } func getURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } func createURL(c *gophercloud.ServiceClient) string { return rootURL(c) } func updateURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } func deleteURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } testing/000077500000000000000000000000001335005366400340565ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensionsdelegate_test.go000066400000000000000000000056051335005366400372240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/testingpackage testing import ( "fmt" "net/http" "testing" common "github.com/gophercloud/gophercloud/openstack/common/extensions" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/extensions", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ` { "extensions": [ { "updated": "2013-01-20T00:00:00-00:00", "name": "Neutron Service Type Management", "links": [], "namespace": "http://docs.openstack.org/ext/neutron/service-type/api/v1.0", "alias": "service-type", "description": "API for retrieving service providers for Neutron advanced services" } ] } `) }) count := 0 extensions.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := extensions.ExtractExtensions(page) if err != nil { t.Errorf("Failed to extract extensions: %v", err) } expected := []extensions.Extension{ { Extension: common.Extension{ Updated: "2013-01-20T00:00:00-00:00", Name: "Neutron Service Type Management", Links: []interface{}{}, Namespace: "http://docs.openstack.org/ext/neutron/service-type/api/v1.0", Alias: "service-type", Description: "API for retrieving service providers for Neutron advanced services", }, }, } th.AssertDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/extensions/agent", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "extension": { "updated": "2013-02-03T10:00:00-00:00", "name": "agent", "links": [], "namespace": "http://docs.openstack.org/ext/agent/api/v2.0", "alias": "agent", "description": "The agent management extension." } } `) }) ext, err := extensions.Get(fake.ServiceClient(), "agent").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ext.Updated, "2013-02-03T10:00:00-00:00") th.AssertEquals(t, ext.Name, "agent") th.AssertEquals(t, ext.Namespace, "http://docs.openstack.org/ext/agent/api/v2.0") th.AssertEquals(t, ext.Alias, "agent") th.AssertEquals(t, ext.Description, "The agent management extension.") } doc.go000066400000000000000000000000511335005366400351460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/testing// extensions unit tests package testing vpnaas/000077500000000000000000000000001335005366400336715ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensionsendpointgroups/000077500000000000000000000000001335005366400367515ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaasdoc.go000066400000000000000000000024571335005366400400550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/endpointgroups/* Package endpointgroups allows management of endpoint groups in the Openstack Network Service Example to create an Endpoint Group createOpts := endpointgroups.CreateOpts{ Name: groupName, Type: endpointgroups.TypeCIDR, Endpoints: []string{ "10.2.0.0/24", "10.3.0.0/24", }, } group, err := endpointgroups.Create(client, createOpts).Extract() if err != nil { return group, err } Example to retrieve an Endpoint Group group, err := endpointgroups.Get(client, "6ecd9cf3-ca64-46c7-863f-f2eb1b9e838a").Extract() if err != nil { panic(err) } Example to Delete an Endpoint Group err := endpointgroups.Delete(client, "5291b189-fd84-46e5-84bd-78f40c05d69c").ExtractErr() if err != nil { panic(err) } Example to List Endpoint groups allPages, err := endpointgroups.List(client, nil).AllPages() if err != nil { panic(err) } allGroups, err := endpointgroups.ExtractEndpointGroups(allPages) if err != nil { panic(err) } Example to Update an endpoint group name := "updatedname" description := "updated description" updateOpts := endpointgroups.UpdateOpts{ Name: &name, Description: &description, } updatedPolicy, err := endpointgroups.Update(client, "5c561d9d-eaea-45f6-ae3e-08d1a7080828", updateOpts).Extract() if err != nil { panic(err) } */ package endpointgroups requests.go000066400000000000000000000111021335005366400411460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/endpointgroupspackage endpointgroups import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type EndpointType string const ( TypeSubnet EndpointType = "subnet" TypeCIDR EndpointType = "cidr" TypeVLAN EndpointType = "vlan" TypeNetwork EndpointType = "network" TypeRouter EndpointType = "router" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToEndpointGroupCreateMap() (map[string]interface{}, error) } // CreateOpts contains all the values needed to create a new endpoint group type CreateOpts struct { // TenantID specifies a tenant to own the endpoint group. The caller must have // an admin role in order to set this. Otherwise, this field is left unset // and the caller will be the owner. TenantID string `json:"tenant_id,omitempty"` // Description is the human readable description of the endpoint group. Description string `json:"description,omitempty"` // Name is the human readable name of the endpoint group. Name string `json:"name,omitempty"` // The type of the endpoints in the group. // A valid value is subnet, cidr, network, router, or vlan. Type EndpointType `json:"type,omitempty"` // List of endpoints of the same type, for the endpoint group. // The values will depend on the type. Endpoints []string `json:"endpoints"` } // ToEndpointGroupCreateMap casts a CreateOpts struct to a map. func (opts CreateOpts) ToEndpointGroupCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "endpoint_group") } // Create accepts a CreateOpts struct and uses the values to create a new // endpoint group. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToEndpointGroupCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular endpoint group based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToEndpointGroupListQuery() (string, error) } // ListOpts allows the filtering of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the Endpoint group attributes you want to see returned. type ListOpts struct { TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` Description string `q:"description"` Name string `q:"name"` Type string `q:"type"` } // ToEndpointGroupListQuery formats a ListOpts into a query string. func (opts ListOpts) ToEndpointGroupListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // Endpoint groups. It accepts a ListOpts struct, which allows you to filter // the returned collection for greater efficiency. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { query, err := opts.ToEndpointGroupListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return EndpointGroupPage{pagination.LinkedPageBase{PageResult: r}} }) } // Delete will permanently delete a particular endpoint group based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToEndpointGroupUpdateMap() (map[string]interface{}, error) } // UpdateOpts contains the values used when updating an endpoint group. type UpdateOpts struct { Description *string `json:"description,omitempty"` Name *string `json:"name,omitempty"` } // ToEndpointGroupUpdateMap casts an UpdateOpts struct to a map. func (opts UpdateOpts) ToEndpointGroupUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "endpoint_group") } // Update allows endpoint groups to be updated. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToEndpointGroupUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000060261335005366400410050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/endpointgroupspackage endpointgroups import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // EndpointGroup is an endpoint group. type EndpointGroup struct { // TenantID specifies a tenant to own the endpoint group. TenantID string `json:"tenant_id"` // TenantID specifies a tenant to own the endpoint group. ProjectID string `json:"project_id"` // Description is the human readable description of the endpoint group. Description string `json:"description"` // Name is the human readable name of the endpoint group. Name string `json:"name"` // Type is the type of the endpoints in the group. Type string `json:"type"` // Endpoints is a list of endpoints. Endpoints []string `json:"endpoints"` // ID is the id of the endpoint group ID string `json:"id"` } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts an endpoint group. func (r commonResult) Extract() (*EndpointGroup, error) { var s struct { Service *EndpointGroup `json:"endpoint_group"` } err := r.ExtractInto(&s) return s.Service, err } // EndpointGroupPage is the page returned by a pager when traversing over a // collection of Policies. type EndpointGroupPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of Endpoint groups has // reached the end of a page and the pager seeks to traverse over a new one. // In order to do this, it needs to construct the next page's URL. func (r EndpointGroupPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"endpoint_groups_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether an EndpointGroupPage struct is empty. func (r EndpointGroupPage) IsEmpty() (bool, error) { is, err := ExtractEndpointGroups(r) return len(is) == 0, err } // ExtractEndpointGroups accepts a Page struct, specifically an EndpointGroupPage struct, // and extracts the elements into a slice of Endpoint group structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractEndpointGroups(r pagination.Page) ([]EndpointGroup, error) { var s struct { EndpointGroups []EndpointGroup `json:"endpoint_groups"` } err := (r.(EndpointGroupPage)).ExtractInto(&s) return s.EndpointGroups, err } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as an endpoint group. type CreateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as an EndpointGroup. type GetResult struct { commonResult } // DeleteResult represents the results of a Delete operation. Call its ExtractErr method // to determine whether the operation succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // UpdateResult represents the result of an update operation. Call its Extract method // to interpret it as an EndpointGroup. type UpdateResult struct { commonResult } testing/000077500000000000000000000000001335005366400404265ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/endpointgroupsrequests_test.go000066400000000000000000000155751335005366400437040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/endpointgroups/testingpackage testing import ( "fmt" "net/http" "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/endpointgroups" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/endpoint-groups", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "endpoint_group": { "endpoints": [ "10.2.0.0/24", "10.3.0.0/24" ], "type": "cidr", "name": "peers" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "endpoint_group": { "description": "", "tenant_id": "4ad57e7ce0b24fca8f12b9834d91079d", "project_id": "4ad57e7ce0b24fca8f12b9834d91079d", "endpoints": [ "10.2.0.0/24", "10.3.0.0/24" ], "type": "cidr", "id": "6ecd9cf3-ca64-46c7-863f-f2eb1b9e838a", "name": "peers" } } `) }) options := endpointgroups.CreateOpts{ Name: "peers", Type: endpointgroups.TypeCIDR, Endpoints: []string{ "10.2.0.0/24", "10.3.0.0/24", }, } actual, err := endpointgroups.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) expected := endpointgroups.EndpointGroup{ Name: "peers", TenantID: "4ad57e7ce0b24fca8f12b9834d91079d", ProjectID: "4ad57e7ce0b24fca8f12b9834d91079d", ID: "6ecd9cf3-ca64-46c7-863f-f2eb1b9e838a", Description: "", Endpoints: []string{ "10.2.0.0/24", "10.3.0.0/24", }, Type: "cidr", } th.AssertDeepEquals(t, expected, *actual) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/endpoint-groups/5c561d9d-eaea-45f6-ae3e-08d1a7080828", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "endpoint_group": { "description": "", "tenant_id": "4ad57e7ce0b24fca8f12b9834d91079d", "project_id": "4ad57e7ce0b24fca8f12b9834d91079d", "endpoints": [ "10.2.0.0/24", "10.3.0.0/24" ], "type": "cidr", "id": "6ecd9cf3-ca64-46c7-863f-f2eb1b9e838a", "name": "peers" } } `) }) actual, err := endpointgroups.Get(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828").Extract() th.AssertNoErr(t, err) expected := endpointgroups.EndpointGroup{ Name: "peers", TenantID: "4ad57e7ce0b24fca8f12b9834d91079d", ProjectID: "4ad57e7ce0b24fca8f12b9834d91079d", ID: "6ecd9cf3-ca64-46c7-863f-f2eb1b9e838a", Description: "", Endpoints: []string{ "10.2.0.0/24", "10.3.0.0/24", }, Type: "cidr", } th.AssertDeepEquals(t, expected, *actual) } func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/endpoint-groups", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "endpoint_groups": [ { "description": "", "tenant_id": "4ad57e7ce0b24fca8f12b9834d91079d", "project_id": "4ad57e7ce0b24fca8f12b9834d91079d", "endpoints": [ "10.2.0.0/24", "10.3.0.0/24" ], "type": "cidr", "id": "6ecd9cf3-ca64-46c7-863f-f2eb1b9e838a", "name": "peers" } ] } `) }) count := 0 endpointgroups.List(fake.ServiceClient(), endpointgroups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := endpointgroups.ExtractEndpointGroups(page) if err != nil { t.Errorf("Failed to extract members: %v", err) return false, err } expected := []endpointgroups.EndpointGroup{ { Name: "peers", TenantID: "4ad57e7ce0b24fca8f12b9834d91079d", ProjectID: "4ad57e7ce0b24fca8f12b9834d91079d", ID: "6ecd9cf3-ca64-46c7-863f-f2eb1b9e838a", Description: "", Endpoints: []string{ "10.2.0.0/24", "10.3.0.0/24", }, Type: "cidr", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/endpoint-groups/5c561d9d-eaea-45f6-ae3e-08d1a7080828", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := endpointgroups.Delete(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") th.AssertNoErr(t, res.Err) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/endpoint-groups/5c561d9d-eaea-45f6-ae3e-08d1a7080828", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "endpoint_group": { "description": "updated description", "name": "updatedname" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "endpoint_group": { "description": "updated description", "tenant_id": "4ad57e7ce0b24fca8f12b9834d91079d", "project_id": "4ad57e7ce0b24fca8f12b9834d91079d", "endpoints": [ "10.2.0.0/24", "10.3.0.0/24" ], "type": "cidr", "id": "6ecd9cf3-ca64-46c7-863f-f2eb1b9e838a", "name": "updatedname" } } `) }) updatedName := "updatedname" updatedDescription := "updated description" options := endpointgroups.UpdateOpts{ Name: &updatedName, Description: &updatedDescription, } actual, err := endpointgroups.Update(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828", options).Extract() th.AssertNoErr(t, err) expected := endpointgroups.EndpointGroup{ Name: "updatedname", TenantID: "4ad57e7ce0b24fca8f12b9834d91079d", ProjectID: "4ad57e7ce0b24fca8f12b9834d91079d", ID: "6ecd9cf3-ca64-46c7-863f-f2eb1b9e838a", Description: "updated description", Endpoints: []string{ "10.2.0.0/24", "10.3.0.0/24", }, Type: "cidr", } th.AssertDeepEquals(t, expected, *actual) } urls.go000066400000000000000000000005421335005366400402660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/endpointgroupspackage endpointgroups import "github.com/gophercloud/gophercloud" const ( rootPath = "vpn" resourcePath = "endpoint-groups" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } ikepolicies/000077500000000000000000000000001335005366400361715ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaasdoc.go000066400000000000000000000026551335005366400372750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/ikepolicies/* Package ikepolicies allows management and retrieval of IKE policies in the OpenStack Networking Service. Example to Create an IKE policy createOpts := ikepolicies.CreateOpts{ Name: "ikepolicy1", Description: "Description of ikepolicy1", EncryptionAlgorithm: ikepolicies.EncryptionAlgorithm3DES, PFS: ikepolicies.PFSGroup5, } policy, err := ikepolicies.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Show the details of a specific IKE policy by ID policy, err := ikepolicies.Get(client, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() if err != nil { panic(err) } Example to Delete a Policy err := ikepolicies.Delete(client, "5291b189-fd84-46e5-84bd-78f40c05d69c").ExtractErr() if err != nil { panic(err) Example to Update an IKE policy name := "updatedname" description := "updated policy" updateOpts := ikepolicies.UpdateOpts{ Name: &name, Description: &description, Lifetime: &ikepolicies.LifetimeUpdateOpts{ Value: 7000, }, } updatedPolicy, err := ikepolicies.Update(client, "5c561d9d-eaea-45f6-ae3e-08d1a7080828", updateOpts).Extract() if err != nil { panic(err) } Example to List IKE policies allPages, err := ikepolicies.List(client, nil).AllPages() if err != nil { panic(err) } allPolicies, err := ikepolicies.ExtractPolicies(allPages) if err != nil { panic(err) } */ package ikepolicies requests.go000066400000000000000000000164731335005366400404060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/ikepoliciespackage ikepolicies import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type AuthAlgorithm string type EncryptionAlgorithm string type PFS string type Unit string type IKEVersion string type Phase1NegotiationMode string const ( AuthAlgorithmSHA1 AuthAlgorithm = "sha1" AuthAlgorithmSHA256 AuthAlgorithm = "sha256" AuthAlgorithmSHA384 AuthAlgorithm = "sha384" AuthAlgorithmSHA512 AuthAlgorithm = "sha512" EncryptionAlgorithm3DES EncryptionAlgorithm = "3des" EncryptionAlgorithmAES128 EncryptionAlgorithm = "aes-128" EncryptionAlgorithmAES256 EncryptionAlgorithm = "aes-256" EncryptionAlgorithmAES192 EncryptionAlgorithm = "aes-192" UnitSeconds Unit = "seconds" UnitKilobytes Unit = "kilobytes" PFSGroup2 PFS = "group2" PFSGroup5 PFS = "group5" PFSGroup14 PFS = "group14" IKEVersionv1 IKEVersion = "v1" IKEVersionv2 IKEVersion = "v2" Phase1NegotiationModeMain Phase1NegotiationMode = "main" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToPolicyCreateMap() (map[string]interface{}, error) } // CreateOpts contains all the values needed to create a new IKE policy type CreateOpts struct { // TenantID specifies a tenant to own the IKE policy. The caller must have // an admin role in order to set this. Otherwise, this field is left unset // and the caller will be the owner. TenantID string `json:"tenant_id,omitempty"` // Description is the human readable description of the policy. Description string `json:"description,omitempty"` // Name is the human readable name of the policy. // Does not have to be unique. Name string `json:"name,omitempty"` // AuthAlgorithm is the authentication hash algorithm. // Valid values are sha1, sha256, sha384, sha512. // The default is sha1. AuthAlgorithm AuthAlgorithm `json:"auth_algorithm,omitempty"` // EncryptionAlgorithm is the encryption algorithm. // A valid value is 3des, aes-128, aes-192, aes-256, and so on. // Default is aes-128. EncryptionAlgorithm EncryptionAlgorithm `json:"encryption_algorithm,omitempty"` // PFS is the Perfect forward secrecy mode. // A valid value is Group2, Group5, Group14, and so on. // Default is Group5. PFS PFS `json:"pfs,omitempty"` // The IKE mode. // A valid value is main, which is the default. Phase1NegotiationMode Phase1NegotiationMode `json:"phase1_negotiation_mode,omitempty"` // The IKE version. // A valid value is v1 or v2. // Default is v1. IKEVersion IKEVersion `json:"ike_version,omitempty"` //Lifetime is the lifetime of the security association Lifetime *LifetimeCreateOpts `json:"lifetime,omitempty"` } // The lifetime consists of a unit and integer value // You can omit either the unit or value portion of the lifetime type LifetimeCreateOpts struct { // Units is the units for the lifetime of the security association // Default unit is seconds Units Unit `json:"units,omitempty"` // The lifetime value. // Must be a positive integer. // Default value is 3600. Value int `json:"value,omitempty"` } // ToPolicyCreateMap casts a CreateOpts struct to a map. func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "ikepolicy") } // Create accepts a CreateOpts struct and uses the values to create a new // IKE policy func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToPolicyCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular IKE policy based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // Delete will permanently delete a particular IKE policy based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToPolicyListQuery() (string, error) } // ListOpts allows the filtering of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the IKE policy attributes you want to see returned. type ListOpts struct { TenantID string `q:"tenant_id"` Name string `q:"name"` Description string `q:"description"` ProjectID string `q:"project_id"` AuthAlgorithm string `q:"auth_algorithm"` EncapsulationMode string `q:"encapsulation_mode"` EncryptionAlgorithm string `q:"encryption_algorithm"` PFS string `q:"pfs"` Phase1NegotiationMode string `q:"phase_1_negotiation_mode"` IKEVersion string `q:"ike_version"` } // ToPolicyListQuery formats a ListOpts into a query string. func (opts ListOpts) ToPolicyListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // IKE policies. It accepts a ListOpts struct, which allows you to filter // the returned collection for greater efficiency. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { query, err := opts.ToPolicyListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return PolicyPage{pagination.LinkedPageBase{PageResult: r}} }) } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToPolicyUpdateMap() (map[string]interface{}, error) } type LifetimeUpdateOpts struct { Units Unit `json:"units,omitempty"` Value int `json:"value,omitempty"` } // UpdateOpts contains the values used when updating an IKE policy type UpdateOpts struct { Description *string `json:"description,omitempty"` Name *string `json:"name,omitempty"` AuthAlgorithm AuthAlgorithm `json:"auth_algorithm,omitempty"` EncryptionAlgorithm EncryptionAlgorithm `json:"encryption_algorithm,omitempty"` PFS PFS `json:"pfs,omitempty"` Lifetime *LifetimeUpdateOpts `json:"lifetime,omitempty"` Phase1NegotiationMode Phase1NegotiationMode `json:"phase_1_negotiation_mode,omitempty"` IKEVersion IKEVersion `json:"ike_version,omitempty"` } // ToPolicyUpdateMap casts an UpdateOpts struct to a map. func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "ikepolicy") } // Update allows IKE policies to be updated. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToPolicyUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000065521335005366400402310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/ikepoliciespackage ikepolicies import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Policy is an IKE Policy type Policy struct { // TenantID is the ID of the project TenantID string `json:"tenant_id"` // ProjectID is the ID of the project ProjectID string `json:"project_id"` // Description is the human readable description of the policy Description string `json:"description"` // Name is the human readable name of the policy Name string `json:"name"` // AuthAlgorithm is the authentication hash algorithm AuthAlgorithm string `json:"auth_algorithm"` // EncryptionAlgorithm is the encryption algorithm EncryptionAlgorithm string `json:"encryption_algorithm"` // PFS is the Perfect forward secrecy (PFS) mode PFS string `json:"pfs"` // Lifetime is the lifetime of the security association Lifetime Lifetime `json:"lifetime"` // ID is the ID of the policy ID string `json:"id"` // Phase1NegotiationMode is the IKE mode Phase1NegotiationMode string `json:"phase1_negotiation_mode"` // IKEVersion is the IKE version. IKEVersion string `json:"ike_version"` } type commonResult struct { gophercloud.Result } type Lifetime struct { // Units is the unit for the lifetime // Default is seconds Units string `json:"units"` // Value is the lifetime // Default is 3600 Value int `json:"value"` } // Extract is a function that accepts a result and extracts an IKE Policy. func (r commonResult) Extract() (*Policy, error) { var s struct { Policy *Policy `json:"ikepolicy"` } err := r.ExtractInto(&s) return s.Policy, err } // PolicyPage is the page returned by a pager when traversing over a // collection of Policies. type PolicyPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of IKE policies has // reached the end of a page and the pager seeks to traverse over a new one. // In order to do this, it needs to construct the next page's URL. func (r PolicyPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"ikepolicies_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a PolicyPage struct is empty. func (r PolicyPage) IsEmpty() (bool, error) { is, err := ExtractPolicies(r) return len(is) == 0, err } // ExtractPolicies accepts a Page struct, specifically a Policy struct, // and extracts the elements into a slice of Policy structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractPolicies(r pagination.Page) ([]Policy, error) { var s struct { Policies []Policy `json:"ikepolicies"` } err := (r.(PolicyPage)).ExtractInto(&s) return s.Policies, err } // CreateResult represents the result of a Create operation. Call its Extract method to // interpret it as a Policy. type CreateResult struct { commonResult } // GetResult represents the result of a Get operation. Call its Extract method to // interpret it as a Policy. type GetResult struct { commonResult } // DeleteResult represents the results of a Delete operation. Call its ExtractErr method // to determine whether the operation succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // UpdateResult represents the result of an update operation. Call its Extract method // to interpret it as a Policy. type UpdateResult struct { commonResult } testing/000077500000000000000000000000001335005366400376465ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/ikepoliciesrequests_test.go000066400000000000000000000204551335005366400431150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/ikepolicies/testingpackage testing import ( "fmt" "net/http" "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ikepolicies" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/ikepolicies", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "ikepolicy":{ "name": "policy", "description": "IKE policy", "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", "ike_version": "v2" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "ikepolicy":{ "name": "policy", "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", "project_id": "9145d91459d248b1b02fdaca97c6a75d", "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", "description": "IKE policy", "auth_algorithm": "sha1", "encryption_algorithm": "aes-128", "pfs": "Group5", "lifetime": { "value": 3600, "units": "seconds" }, "phase1_negotiation_mode": "main", "ike_version": "v2" } } `) }) options := ikepolicies.CreateOpts{ TenantID: "9145d91459d248b1b02fdaca97c6a75d", Name: "policy", Description: "IKE policy", IKEVersion: ikepolicies.IKEVersionv2, } actual, err := ikepolicies.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) expectedLifetime := ikepolicies.Lifetime{ Units: "seconds", Value: 3600, } expected := ikepolicies.Policy{ AuthAlgorithm: "sha1", IKEVersion: "v2", TenantID: "9145d91459d248b1b02fdaca97c6a75d", Phase1NegotiationMode: "main", PFS: "Group5", EncryptionAlgorithm: "aes-128", Description: "IKE policy", Name: "policy", ID: "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", Lifetime: expectedLifetime, ProjectID: "9145d91459d248b1b02fdaca97c6a75d", } th.AssertDeepEquals(t, expected, *actual) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/ikepolicies/5c561d9d-eaea-45f6-ae3e-08d1a7080828", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "ikepolicy":{ "name": "policy", "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", "project_id": "9145d91459d248b1b02fdaca97c6a75d", "id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", "description": "IKE policy", "auth_algorithm": "sha1", "encryption_algorithm": "aes-128", "pfs": "Group5", "lifetime": { "value": 3600, "units": "seconds" }, "phase1_negotiation_mode": "main", "ike_version": "v2" } } `) }) actual, err := ikepolicies.Get(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828").Extract() th.AssertNoErr(t, err) expectedLifetime := ikepolicies.Lifetime{ Units: "seconds", Value: 3600, } expected := ikepolicies.Policy{ AuthAlgorithm: "sha1", IKEVersion: "v2", TenantID: "9145d91459d248b1b02fdaca97c6a75d", ProjectID: "9145d91459d248b1b02fdaca97c6a75d", Phase1NegotiationMode: "main", PFS: "Group5", EncryptionAlgorithm: "aes-128", Description: "IKE policy", Name: "policy", ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", Lifetime: expectedLifetime, } th.AssertDeepEquals(t, expected, *actual) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/ikepolicies/5c561d9d-eaea-45f6-ae3e-08d1a7080828", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := ikepolicies.Delete(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") th.AssertNoErr(t, res.Err) } func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/ikepolicies", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "ikepolicies": [ { "name": "policy", "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", "project_id": "9145d91459d248b1b02fdaca97c6a75d", "id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", "description": "IKE policy", "auth_algorithm": "sha1", "encryption_algorithm": "aes-128", "pfs": "Group5", "lifetime": { "value": 3600, "units": "seconds" }, "phase1_negotiation_mode": "main", "ike_version": "v2" } ] } `) }) count := 0 ikepolicies.List(fake.ServiceClient(), ikepolicies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := ikepolicies.ExtractPolicies(page) if err != nil { t.Errorf("Failed to extract members: %v", err) return false, err } expectedLifetime := ikepolicies.Lifetime{ Units: "seconds", Value: 3600, } expected := []ikepolicies.Policy{ { AuthAlgorithm: "sha1", IKEVersion: "v2", TenantID: "9145d91459d248b1b02fdaca97c6a75d", ProjectID: "9145d91459d248b1b02fdaca97c6a75d", Phase1NegotiationMode: "main", PFS: "Group5", EncryptionAlgorithm: "aes-128", Description: "IKE policy", Name: "policy", ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", Lifetime: expectedLifetime, }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/ikepolicies/5c561d9d-eaea-45f6-ae3e-08d1a7080828", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "ikepolicy":{ "name": "updatedname", "description": "updated policy", "lifetime": { "value": 7000 } } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "ikepolicy": { "name": "updatedname", "transform_protocol": "esp", "auth_algorithm": "sha1", "encapsulation_mode": "tunnel", "encryption_algorithm": "aes-128", "pfs": "group5", "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "project_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "lifetime": { "units": "seconds", "value": 7000 }, "id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", "description": "updated policy" } } `) }) updatedName := "updatedname" updatedDescription := "updated policy" options := ikepolicies.UpdateOpts{ Name: &updatedName, Description: &updatedDescription, Lifetime: &ikepolicies.LifetimeUpdateOpts{ Value: 7000, }, } actual, err := ikepolicies.Update(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828", options).Extract() th.AssertNoErr(t, err) expectedLifetime := ikepolicies.Lifetime{ Units: "seconds", Value: 7000, } expected := ikepolicies.Policy{ TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", ProjectID: "b4eedccc6fb74fa8a7ad6b08382b852b", Name: "updatedname", AuthAlgorithm: "sha1", EncryptionAlgorithm: "aes-128", PFS: "group5", Description: "updated policy", Lifetime: expectedLifetime, ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", } th.AssertDeepEquals(t, expected, *actual) } urls.go000066400000000000000000000005331335005366400375060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/ikepoliciespackage ikepolicies import "github.com/gophercloud/gophercloud" const ( rootPath = "vpn" resourcePath = "ikepolicies" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } ipsecpolicies/000077500000000000000000000000001335005366400365245ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaasdoc.go000066400000000000000000000023321335005366400376200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/* Package ipsecpolicies allows management and retrieval of IPSec Policies in the OpenStack Networking Service. Example to Create a Policy createOpts := ipsecpolicies.CreateOpts{ Name: "IPSecPolicy_1", } policy, err := policies.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Delete a Policy err := ipsecpolicies.Delete(client, "5291b189-fd84-46e5-84bd-78f40c05d69c").ExtractErr() if err != nil { panic(err) } Example to Show the details of a specific IPSec policy by ID policy, err := ipsecpolicies.Get(client, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() if err != nil { panic(err) } Example to Update an IPSec policy name := "updatedname" description := "updated policy" updateOpts := ipsecpolicies.UpdateOpts{ Name: &name, Description: &description, } updatedPolicy, err := ipsecpolicies.Update(client, "5c561d9d-eaea-45f6-ae3e-08d1a7080828", updateOpts).Extract() if err != nil { panic(err) } Example to List IPSec policies allPages, err := ipsecpolicies.List(client, nil).AllPages() if err != nil { panic(err) } allPolicies, err := ipsecpolicies.ExtractPolicies(allPages) if err != nil { panic(err) } */ package ipsecpolicies requests.go000066400000000000000000000166721335005366400407420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/ipsecpoliciespackage ipsecpolicies import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type TransformProtocol string type AuthAlgorithm string type EncapsulationMode string type EncryptionAlgorithm string type PFS string type Unit string const ( TransformProtocolESP TransformProtocol = "esp" TransformProtocolAH TransformProtocol = "ah" TransformProtocolAHESP TransformProtocol = "ah-esp" AuthAlgorithmSHA1 AuthAlgorithm = "sha1" AuthAlgorithmSHA256 AuthAlgorithm = "sha256" AuthAlgorithmSHA384 AuthAlgorithm = "sha384" AuthAlgorithmSHA512 AuthAlgorithm = "sha512" EncryptionAlgorithm3DES EncryptionAlgorithm = "3des" EncryptionAlgorithmAES128 EncryptionAlgorithm = "aes-128" EncryptionAlgorithmAES256 EncryptionAlgorithm = "aes-256" EncryptionAlgorithmAES192 EncryptionAlgorithm = "aes-192" EncapsulationModeTunnel EncapsulationMode = "tunnel" EncapsulationModeTransport EncapsulationMode = "transport" UnitSeconds Unit = "seconds" UnitKilobytes Unit = "kilobytes" PFSGroup2 PFS = "group2" PFSGroup5 PFS = "group5" PFSGroup14 PFS = "group14" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToPolicyCreateMap() (map[string]interface{}, error) } // CreateOpts contains all the values needed to create a new IPSec policy type CreateOpts struct { // TenantID specifies a tenant to own the IPSec policy. The caller must have // an admin role in order to set this. Otherwise, this field is left unset // and the caller will be the owner. TenantID string `json:"tenant_id,omitempty"` // Description is the human readable description of the policy. Description string `json:"description,omitempty"` // Name is the human readable name of the policy. // Does not have to be unique. Name string `json:"name,omitempty"` // AuthAlgorithm is the authentication hash algorithm. // Valid values are sha1, sha256, sha384, sha512. // The default is sha1. AuthAlgorithm AuthAlgorithm `json:"auth_algorithm,omitempty"` // EncapsulationMode is the encapsulation mode. // A valid value is tunnel or transport. // Default is tunnel. EncapsulationMode EncapsulationMode `json:"encapsulation_mode,omitempty"` // EncryptionAlgorithm is the encryption algorithm. // A valid value is 3des, aes-128, aes-192, aes-256, and so on. // Default is aes-128. EncryptionAlgorithm EncryptionAlgorithm `json:"encryption_algorithm,omitempty"` // PFS is the Perfect forward secrecy mode. // A valid value is Group2, Group5, Group14, and so on. // Default is Group5. PFS PFS `json:"pfs,omitempty"` // TransformProtocol is the transform protocol. // A valid value is ESP, AH, or AH- ESP. // Default is ESP. TransformProtocol TransformProtocol `json:"transform_protocol,omitempty"` //Lifetime is the lifetime of the security association Lifetime *LifetimeCreateOpts `json:"lifetime,omitempty"` } // The lifetime consists of a unit and integer value // You can omit either the unit or value portion of the lifetime type LifetimeCreateOpts struct { // Units is the units for the lifetime of the security association // Default unit is seconds Units Unit `json:"units,omitempty"` // The lifetime value. // Must be a positive integer. // Default value is 3600. Value int `json:"value,omitempty"` } // ToPolicyCreateMap casts a CreateOpts struct to a map. func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "ipsecpolicy") } // Create accepts a CreateOpts struct and uses the values to create a new // IPSec policy func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToPolicyCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Delete will permanently delete a particular IPSec policy based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } // Get retrieves a particular IPSec policy based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToPolicyListQuery() (string, error) } // ListOpts allows the filtering of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the IPSec policy attributes you want to see returned. type ListOpts struct { TenantID string `q:"tenant_id"` Name string `q:"name"` Description string `q:"description"` ProjectID string `q:"project_id"` AuthAlgorithm string `q:"auth_algorithm"` EncapsulationMode string `q:"encapsulation_mode"` EncryptionAlgorithm string `q:"encryption_algorithm"` PFS string `q:"pfs"` TransformProtocol string `q:"transform_protocol"` } // ToPolicyListQuery formats a ListOpts into a query string. func (opts ListOpts) ToPolicyListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // IPSec policies. It accepts a ListOpts struct, which allows you to filter // the returned collection for greater efficiency. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { query, err := opts.ToPolicyListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return PolicyPage{pagination.LinkedPageBase{PageResult: r}} }) } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToPolicyUpdateMap() (map[string]interface{}, error) } type LifetimeUpdateOpts struct { Units Unit `json:"units,omitempty"` Value int `json:"value,omitempty"` } // UpdateOpts contains the values used when updating an IPSec policy type UpdateOpts struct { Description *string `json:"description,omitempty"` Name *string `json:"name,omitempty"` AuthAlgorithm AuthAlgorithm `json:"auth_algorithm,omitempty"` EncapsulationMode EncapsulationMode `json:"encapsulation_mode,omitempty"` EncryptionAlgorithm EncryptionAlgorithm `json:"encryption_algorithm,omitempty"` PFS PFS `json:"pfs,omitempty"` TransformProtocol TransformProtocol `json:"transform_protocol,omitempty"` Lifetime *LifetimeUpdateOpts `json:"lifetime,omitempty"` } // ToPolicyUpdateMap casts an UpdateOpts struct to a map. func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "ipsecpolicy") } // Update allows IPSec policies to be updated. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToPolicyUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000066131335005366400405620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/ipsecpoliciespackage ipsecpolicies import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Policy is an IPSec Policy type Policy struct { // TenantID is the ID of the project TenantID string `json:"tenant_id"` // ProjectID is the ID of the project ProjectID string `json:"project_id"` // Description is the human readable description of the policy Description string `json:"description"` // Name is the human readable name of the policy Name string `json:"name"` // AuthAlgorithm is the authentication hash algorithm AuthAlgorithm string `json:"auth_algorithm"` // EncapsulationMode is the encapsulation mode EncapsulationMode string `json:"encapsulation_mode"` // EncryptionAlgorithm is the encryption algorithm EncryptionAlgorithm string `json:"encryption_algorithm"` // PFS is the Perfect forward secrecy (PFS) mode PFS string `json:"pfs"` // TransformProtocol is the transform protocol TransformProtocol string `json:"transform_protocol"` // Lifetime is the lifetime of the security association Lifetime Lifetime `json:"lifetime"` // ID is the ID of the policy ID string `json:"id"` } type Lifetime struct { // Units is the unit for the lifetime // Default is seconds Units string `json:"units"` // Value is the lifetime // Default is 3600 Value int `json:"value"` } type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts an IPSec Policy. func (r commonResult) Extract() (*Policy, error) { var s struct { Policy *Policy `json:"ipsecpolicy"` } err := r.ExtractInto(&s) return s.Policy, err } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a Policy. type CreateResult struct { commonResult } // CreateResult represents the result of a delete operation. Call its ExtractErr method // to determine if the operation succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a Policy. type GetResult struct { commonResult } // PolicyPage is the page returned by a pager when traversing over a // collection of Policies. type PolicyPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of IPSec policies has // reached the end of a page and the pager seeks to traverse over a new one. // In order to do this, it needs to construct the next page's URL. func (r PolicyPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"ipsecpolicies_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a PolicyPage struct is empty. func (r PolicyPage) IsEmpty() (bool, error) { is, err := ExtractPolicies(r) return len(is) == 0, err } // ExtractPolicies accepts a Page struct, specifically a Policy struct, // and extracts the elements into a slice of Policy structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractPolicies(r pagination.Page) ([]Policy, error) { var s struct { Policies []Policy `json:"ipsecpolicies"` } err := (r.(PolicyPage)).ExtractInto(&s) return s.Policies, err } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a Policy. type UpdateResult struct { commonResult } testing/000077500000000000000000000000001335005366400402015ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/ipsecpoliciesrequests_test.go000066400000000000000000000216471335005366400434540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testingpackage testing import ( "fmt" "net/http" "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ipsecpolicies" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/ipsecpolicies", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "ipsecpolicy": { "name": "ipsecpolicy1", "transform_protocol": "esp", "auth_algorithm": "sha1", "encapsulation_mode": "tunnel", "encryption_algorithm": "aes-128", "pfs": "group5", "lifetime": { "units": "seconds", "value": 7200 }, "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "ipsecpolicy": { "name": "ipsecpolicy1", "transform_protocol": "esp", "auth_algorithm": "sha1", "encapsulation_mode": "tunnel", "encryption_algorithm": "aes-128", "pfs": "group5", "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "project_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "lifetime": { "units": "seconds", "value": 7200 }, "id": "5291b189-fd84-46e5-84bd-78f40c05d69c", "description": "" } } `) }) lifetime := ipsecpolicies.LifetimeCreateOpts{ Units: ipsecpolicies.UnitSeconds, Value: 7200, } options := ipsecpolicies.CreateOpts{ TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", Name: "ipsecpolicy1", TransformProtocol: ipsecpolicies.TransformProtocolESP, AuthAlgorithm: ipsecpolicies.AuthAlgorithmSHA1, EncapsulationMode: ipsecpolicies.EncapsulationModeTunnel, EncryptionAlgorithm: ipsecpolicies.EncryptionAlgorithmAES128, PFS: ipsecpolicies.PFSGroup5, Lifetime: &lifetime, Description: "", } actual, err := ipsecpolicies.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) expectedLifetime := ipsecpolicies.Lifetime{ Units: "seconds", Value: 7200, } expected := ipsecpolicies.Policy{ TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", Name: "ipsecpolicy1", TransformProtocol: "esp", AuthAlgorithm: "sha1", EncapsulationMode: "tunnel", EncryptionAlgorithm: "aes-128", PFS: "group5", Description: "", Lifetime: expectedLifetime, ID: "5291b189-fd84-46e5-84bd-78f40c05d69c", ProjectID: "b4eedccc6fb74fa8a7ad6b08382b852b", } th.AssertDeepEquals(t, expected, *actual) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/ipsecpolicies/5c561d9d-eaea-45f6-ae3e-08d1a7080828", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "ipsecpolicy": { "name": "ipsecpolicy1", "transform_protocol": "esp", "auth_algorithm": "sha1", "encapsulation_mode": "tunnel", "encryption_algorithm": "aes-128", "pfs": "group5", "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "project_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "lifetime": { "units": "seconds", "value": 7200 }, "id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", "description": "" } } `) }) actual, err := ipsecpolicies.Get(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828").Extract() th.AssertNoErr(t, err) expectedLifetime := ipsecpolicies.Lifetime{ Units: "seconds", Value: 7200, } expected := ipsecpolicies.Policy{ Name: "ipsecpolicy1", TransformProtocol: "esp", Description: "", AuthAlgorithm: "sha1", EncapsulationMode: "tunnel", EncryptionAlgorithm: "aes-128", PFS: "group5", Lifetime: expectedLifetime, TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", ProjectID: "b4eedccc6fb74fa8a7ad6b08382b852b", } th.AssertDeepEquals(t, expected, *actual) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/ipsecpolicies/5c561d9d-eaea-45f6-ae3e-08d1a7080828", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := ipsecpolicies.Delete(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") th.AssertNoErr(t, res.Err) } func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/ipsecpolicies", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "ipsecpolicies": [ { "name": "ipsecpolicy1", "transform_protocol": "esp", "auth_algorithm": "sha1", "encapsulation_mode": "tunnel", "encryption_algorithm": "aes-128", "pfs": "group5", "project_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "lifetime": { "units": "seconds", "value": 7200 }, "id": "5291b189-fd84-46e5-84bd-78f40c05d69c", "description": "" } ] } `) }) count := 0 ipsecpolicies.List(fake.ServiceClient(), ipsecpolicies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := ipsecpolicies.ExtractPolicies(page) if err != nil { t.Errorf("Failed to extract members: %v", err) return false, err } expected := []ipsecpolicies.Policy{ { Name: "ipsecpolicy1", TransformProtocol: "esp", TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", ProjectID: "b4eedccc6fb74fa8a7ad6b08382b852b", AuthAlgorithm: "sha1", EncapsulationMode: "tunnel", EncryptionAlgorithm: "aes-128", PFS: "group5", Lifetime: ipsecpolicies.Lifetime{ Value: 7200, Units: "seconds", }, Description: "", ID: "5291b189-fd84-46e5-84bd-78f40c05d69c", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/ipsecpolicies/5c561d9d-eaea-45f6-ae3e-08d1a7080828", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "ipsecpolicy":{ "name": "updatedname", "description": "updated policy", "lifetime": { "value": 7000 } } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "ipsecpolicy": { "name": "updatedname", "transform_protocol": "esp", "auth_algorithm": "sha1", "encapsulation_mode": "tunnel", "encryption_algorithm": "aes-128", "pfs": "group5", "project_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "lifetime": { "units": "seconds", "value": 7000 }, "id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", "description": "updated policy" } } `) }) updatedName := "updatedname" updatedDescription := "updated policy" options := ipsecpolicies.UpdateOpts{ Name: &updatedName, Description: &updatedDescription, Lifetime: &ipsecpolicies.LifetimeUpdateOpts{ Value: 7000, }, } actual, err := ipsecpolicies.Update(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828", options).Extract() th.AssertNoErr(t, err) expectedLifetime := ipsecpolicies.Lifetime{ Units: "seconds", Value: 7000, } expected := ipsecpolicies.Policy{ TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", ProjectID: "b4eedccc6fb74fa8a7ad6b08382b852b", Name: "updatedname", TransformProtocol: "esp", AuthAlgorithm: "sha1", EncapsulationMode: "tunnel", EncryptionAlgorithm: "aes-128", PFS: "group5", Description: "updated policy", Lifetime: expectedLifetime, ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", } th.AssertDeepEquals(t, expected, *actual) } urls.go000066400000000000000000000005371335005366400400450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/ipsecpoliciespackage ipsecpolicies import "github.com/gophercloud/gophercloud" const ( rootPath = "vpn" resourcePath = "ipsecpolicies" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } services/000077500000000000000000000000001335005366400355145ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaasdoc.go000066400000000000000000000026551335005366400366200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/services/* Package services allows management and retrieval of VPN services in the OpenStack Networking Service. Example to List Services listOpts := services.ListOpts{ TenantID: "966b3c7d36a24facaf20b7e458bf2192", } allPages, err := services.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allPolicies, err := services.ExtractServices(allPages) if err != nil { panic(err) } for _, service := range allServices { fmt.Printf("%+v\n", service) } Example to Create a Service createOpts := services.CreateOpts{ Name: "vpnservice1", Description: "A service", RouterID: "2512e759-e8d7-4eea-a0af-4a85927a2e59", AdminStateUp: gophercloud.Enabled, } service, err := services.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Service serviceID := "38aee955-6283-4279-b091-8b9c828000ec" updateOpts := services.UpdateOpts{ Description: "New Description", } service, err := services.Update(networkClient, serviceID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Service serviceID := "38aee955-6283-4279-b091-8b9c828000ec" err := services.Delete(networkClient, serviceID).ExtractErr() if err != nil { panic(err) } Example to Show the details of a specific Service by ID service, err := services.Get(client, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() if err != nil { panic(err) } */ package services requests.go000066400000000000000000000115251335005366400377220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/servicespackage services import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToServiceCreateMap() (map[string]interface{}, error) } // CreateOpts contains all the values needed to create a new VPN service type CreateOpts struct { // TenantID specifies a tenant to own the VPN service. The caller must have // an admin role in order to set this. Otherwise, this field is left unset // and the caller will be the owner. TenantID string `json:"tenant_id,omitempty"` // SubnetID is the ID of the subnet. SubnetID string `json:"subnet_id,omitempty"` // RouterID is the ID of the router. RouterID string `json:"router_id" required:"true"` // Description is the human readable description of the service. Description string `json:"description,omitempty"` // AdminStateUp is the administrative state of the resource, which is up (true) or down (false). AdminStateUp *bool `json:"admin_state_up"` // Name is the human readable name of the service. Name string `json:"name,omitempty"` // The ID of the flavor. FlavorID string `json:"flavor_id,omitempty"` } // ToServiceCreateMap casts a CreateOpts struct to a map. func (opts CreateOpts) ToServiceCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "vpnservice") } // Create accepts a CreateOpts struct and uses the values to create a new // VPN service. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToServiceCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Delete will permanently delete a particular VPN service based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToServiceUpdateMap() (map[string]interface{}, error) } // UpdateOpts contains the values used when updating a VPN service type UpdateOpts struct { // Name is the human readable name of the service. Name *string `json:"name,omitempty"` // Description is the human readable description of the service. Description *string `json:"description,omitempty"` // AdminStateUp is the administrative state of the resource, which is up (true) or down (false). AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToServiceUpdateMap casts aa UodateOpts struct to a map. func (opts UpdateOpts) ToServiceUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "vpnservice") } // Update allows VPN services to be updated. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToServiceUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToServiceListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the VPN service attributes you want to see returned. type ListOpts struct { TenantID string `q:"tenant_id"` Name string `q:"name"` Description string `q:"description"` AdminStateUp *bool `q:"admin_state_up"` Status string `q:"status"` SubnetID string `q:"subnet_id"` RouterID string `q:"router_id"` ProjectID string `q:"project_id"` ExternalV6IP string `q:"external_v6_ip"` ExternalV4IP string `q:"external_v4_ip"` FlavorID string `q:"flavor_id"` } // ToServiceListQuery formats a ListOpts into a query string. func (opts ListOpts) ToServiceListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // VPN services. It accepts a ListOpts struct, which allows you to filter // and sort the returned collection for greater efficiency. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { query, err := opts.ToServiceListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return ServicePage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves a particular VPN service based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } results.go000066400000000000000000000070201335005366400375430ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/servicespackage services import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Service is a VPN Service type Service struct { // TenantID is the ID of the project. TenantID string `json:"tenant_id"` // ProjectID is the ID of the project. ProjectID string `json:"project_id"` // SubnetID is the ID of the subnet. SubnetID string `json:"subnet_id"` // RouterID is the ID of the router. RouterID string `json:"router_id"` // Description is a human-readable description for the resource. // Default is an empty string Description string `json:"description"` // AdminStateUp is the administrative state of the resource, which is up (true) or down (false). AdminStateUp bool `json:"admin_state_up"` // Name is the human readable name of the service. Name string `json:"name"` // Status indicates whether IPsec VPN service is currently operational. // Values are ACTIVE, DOWN, BUILD, ERROR, PENDING_CREATE, PENDING_UPDATE, or PENDING_DELETE. Status string `json:"status"` // ID is the unique ID of the VPN service. ID string `json:"id"` // ExternalV6IP is the read-only external (public) IPv6 address that is used for the VPN service. ExternalV6IP string `json:"external_v6_ip"` // ExternalV4IP is the read-only external (public) IPv4 address that is used for the VPN service. ExternalV4IP string `json:"external_v4_ip"` // FlavorID is the ID of the flavor. FlavorID string `json:"flavor_id"` } type commonResult struct { gophercloud.Result } // ServicePage is the page returned by a pager when traversing over a // collection of VPN services. type ServicePage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of VPN services has // reached the end of a page and the pager seeks to traverse over a new one. // In order to do this, it needs to construct the next page's URL. func (r ServicePage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"vpnservices_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a ServicePage struct is empty. func (r ServicePage) IsEmpty() (bool, error) { is, err := ExtractServices(r) return len(is) == 0, err } // ExtractServices accepts a Page struct, specifically a Service struct, // and extracts the elements into a slice of Service structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractServices(r pagination.Page) ([]Service, error) { var s struct { Services []Service `json:"vpnservices"` } err := (r.(ServicePage)).ExtractInto(&s) return s.Services, err } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a Service. type GetResult struct { commonResult } // Extract is a function that accepts a result and extracts a VPN service. func (r commonResult) Extract() (*Service, error) { var s struct { Service *Service `json:"vpnservice"` } err := r.ExtractInto(&s) return s.Service, err } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a Service. type CreateResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its // ExtractErr method to determine if the operation succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a service. type UpdateResult struct { commonResult } testing/000077500000000000000000000000001335005366400371715ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/servicesrequests_test.go000066400000000000000000000173431335005366400424420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/services/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/services" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/vpnservices", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "vpnservice": { "router_id": "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", "name": "vpn", "admin_state_up": true, "description": "OpenStack VPN service", "tenant_id": "10039663455a446d8ba2cbb058b0f578" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "vpnservice": { "router_id": "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", "status": "PENDING_CREATE", "name": "vpn", "external_v6_ip": "2001:db8::1", "admin_state_up": true, "subnet_id": null, "tenant_id": "10039663455a446d8ba2cbb058b0f578", "external_v4_ip": "172.32.1.11", "id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", "description": "OpenStack VPN service", "project_id": "10039663455a446d8ba2cbb058b0f578" } } `) }) options := services.CreateOpts{ TenantID: "10039663455a446d8ba2cbb058b0f578", Name: "vpn", Description: "OpenStack VPN service", AdminStateUp: gophercloud.Enabled, RouterID: "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", } actual, err := services.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) expected := services.Service{ RouterID: "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", Status: "PENDING_CREATE", Name: "vpn", ExternalV6IP: "2001:db8::1", AdminStateUp: true, SubnetID: "", TenantID: "10039663455a446d8ba2cbb058b0f578", ProjectID: "10039663455a446d8ba2cbb058b0f578", ExternalV4IP: "172.32.1.11", ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", Description: "OpenStack VPN service", } th.AssertDeepEquals(t, expected, *actual) } func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/vpnservices", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "vpnservices":[ { "router_id": "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", "status": "PENDING_CREATE", "name": "vpnservice1", "admin_state_up": true, "subnet_id": null, "project_id": "10039663455a446d8ba2cbb058b0f578", "tenant_id": "10039663455a446d8ba2cbb058b0f578", "description": "Test VPN service" } ] } `) }) count := 0 services.List(fake.ServiceClient(), services.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := services.ExtractServices(page) if err != nil { t.Errorf("Failed to extract members: %v", err) return false, err } expected := []services.Service{ { Status: "PENDING_CREATE", Name: "vpnservice1", AdminStateUp: true, TenantID: "10039663455a446d8ba2cbb058b0f578", ProjectID: "10039663455a446d8ba2cbb058b0f578", Description: "Test VPN service", SubnetID: "", RouterID: "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/vpnservices/5c561d9d-eaea-45f6-ae3e-08d1a7080828", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "vpnservice": { "router_id": "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", "status": "PENDING_CREATE", "name": "vpnservice1", "admin_state_up": true, "subnet_id": null, "project_id": "10039663455a446d8ba2cbb058b0f578", "tenant_id": "10039663455a446d8ba2cbb058b0f578", "id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", "description": "VPN test service" } } `) }) actual, err := services.Get(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828").Extract() th.AssertNoErr(t, err) expected := services.Service{ Status: "PENDING_CREATE", Name: "vpnservice1", Description: "VPN test service", AdminStateUp: true, ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", TenantID: "10039663455a446d8ba2cbb058b0f578", ProjectID: "10039663455a446d8ba2cbb058b0f578", RouterID: "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", SubnetID: "", } th.AssertDeepEquals(t, expected, *actual) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/vpnservices/5c561d9d-eaea-45f6-ae3e-08d1a7080828", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := services.Delete(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") th.AssertNoErr(t, res.Err) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/vpnservices/5c561d9d-eaea-45f6-ae3e-08d1a7080828", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "vpnservice":{ "name": "updatedname", "description": "updated service", "admin_state_up": false } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "vpnservice": { "router_id": "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", "status": "PENDING_CREATE", "name": "updatedname", "admin_state_up": false, "subnet_id": null, "tenant_id": "10039663455a446d8ba2cbb058b0f578", "project_id": "10039663455a446d8ba2cbb058b0f578", "id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", "description": "updated service", "external_v4_ip": "172.32.1.11", "external_v6_ip": "2001:db8::1" } } `) }) updatedName := "updatedname" updatedServiceDescription := "updated service" options := services.UpdateOpts{ Name: &updatedName, Description: &updatedServiceDescription, AdminStateUp: gophercloud.Disabled, } actual, err := services.Update(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828", options).Extract() th.AssertNoErr(t, err) expected := services.Service{ RouterID: "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", Status: "PENDING_CREATE", Name: "updatedname", ExternalV6IP: "2001:db8::1", AdminStateUp: false, SubnetID: "", TenantID: "10039663455a446d8ba2cbb058b0f578", ProjectID: "10039663455a446d8ba2cbb058b0f578", ExternalV4IP: "172.32.1.11", ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", Description: "updated service", } th.AssertDeepEquals(t, expected, *actual) } urls.go000066400000000000000000000005301335005366400370260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/servicespackage services import "github.com/gophercloud/gophercloud" const ( rootPath = "vpn" resourcePath = "vpnservices" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } siteconnections/000077500000000000000000000000001335005366400371005ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaasdoc.go000066400000000000000000000035051335005366400401770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/siteconnections/* Package siteconnections allows management and retrieval of IPSec site connections in the OpenStack Networking Service. Example to create an IPSec site connection createOpts := siteconnections.CreateOpts{ Name: "Connection1", PSK: "secret", Initiator: siteconnections.InitiatorBiDirectional, AdminStateUp: gophercloud.Enabled, IPSecPolicyID: "4ab0a72e-64ef-4809-be43-c3f7e0e5239b", PeerEPGroupID: "5f5801b1-b383-4cf0-bf61-9e85d4044b2d", IKEPolicyID: "47a880f9-1da9-468c-b289-219c9eca78f0", VPNServiceID: "692c1ec8-a7cd-44d9-972b-8ed3fe4cc476", LocalEPGroupID: "498bb96a-1517-47ea-b1eb-c4a53db46a16", PeerAddress: "172.24.4.233", PeerID: "172.24.4.233", MTU: 1500, } connection, err := siteconnections.Create(client, createOpts).Extract() if err != nil { panic(err) } Example to Show the details of a specific IPSec site connection by ID conn, err := siteconnections.Get(client, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() if err != nil { panic(err) } Example to Delete a site connection connID := "38aee955-6283-4279-b091-8b9c828000ec" err := siteconnections.Delete(networkClient, connID).ExtractErr() if err != nil { panic(err) } Example to List site connections allPages, err := siteconnections.List(client, nil).AllPages() if err != nil { panic(err) } allConnections, err := siteconnections.ExtractConnections(allPages) if err != nil { panic(err) } Example to Update an IPSec site connection description := "updated connection" name := "updatedname" updateOpts := siteconnections.UpdateOpts{ Name: &name, Description: &description, } updatedConnection, err := siteconnections.Update(client, "5c561d9d-eaea-45f6-ae3e-08d1a7080828", updateOpts).Extract() if err != nil { panic(err) } */ package siteconnections requests.go000066400000000000000000000217421335005366400413100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/siteconnectionspackage siteconnections import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToConnectionCreateMap() (map[string]interface{}, error) } type Action string type Initiator string const ( ActionHold Action = "hold" ActionClear Action = "clear" ActionRestart Action = "restart" ActionDisabled Action = "disabled" ActionRestartByPeer Action = "restart-by-peer" InitiatorBiDirectional Initiator = "bi-directional" InitiatorResponseOnly Initiator = "response-only" ) // DPDCreateOpts contains all the values needed to create a valid configuration for Dead Peer detection protocols type DPDCreateOpts struct { // The dead peer detection (DPD) action. // A valid value is clear, hold, restart, disabled, or restart-by-peer. // Default value is hold. Action Action `json:"action,omitempty"` // The dead peer detection (DPD) timeout in seconds. // A valid value is a positive integer that is greater than the DPD interval value. // Default is 120. Timeout int `json:"timeout,omitempty"` // The dead peer detection (DPD) interval, in seconds. // A valid value is a positive integer. // Default is 30. Interval int `json:"interval,omitempty"` } // CreateOpts contains all the values needed to create a new IPSec site connection type CreateOpts struct { // The ID of the IKE policy IKEPolicyID string `json:"ikepolicy_id"` // The ID of the VPN Service VPNServiceID string `json:"vpnservice_id"` // The ID for the endpoint group that contains private subnets for the local side of the connection. // You must specify this parameter with the peer_ep_group_id parameter unless // in backward- compatible mode where peer_cidrs is provided with a subnet_id for the VPN service. LocalEPGroupID string `json:"local_ep_group_id,omitempty"` // The ID of the IPsec policy. IPSecPolicyID string `json:"ipsecpolicy_id"` // The peer router identity for authentication. // A valid value is an IPv4 address, IPv6 address, e-mail address, key ID, or FQDN. // Typically, this value matches the peer_address value. PeerID string `json:"peer_id"` // The ID of the project TenantID string `json:"tenant_id,omitempty"` // The ID for the endpoint group that contains private CIDRs in the form < net_address > / < prefix > // for the peer side of the connection. // You must specify this parameter with the local_ep_group_id parameter unless in backward-compatible mode // where peer_cidrs is provided with a subnet_id for the VPN service. PeerEPGroupID string `json:"peer_ep_group_id,omitempty"` // An ID to be used instead of the external IP address for a virtual router used in traffic between instances on different networks in east-west traffic. // Most often, local ID would be domain name, email address, etc. // If this is not configured then the external IP address will be used as the ID. LocalID string `json:"local_id,omitempty"` // The human readable name of the connection. // Does not have to be unique. // Default is an empty string Name string `json:"name,omitempty"` // The human readable description of the connection. // Does not have to be unique. // Default is an empty string Description string `json:"description,omitempty"` // The peer gateway public IPv4 or IPv6 address or FQDN. PeerAddress string `json:"peer_address"` // The pre-shared key. // A valid value is any string. PSK string `json:"psk"` // Indicates whether this VPN can only respond to connections or both respond to and initiate connections. // A valid value is response-only or bi-directional. Default is bi-directional. Initiator Initiator `json:"initiator,omitempty"` // Unique list of valid peer private CIDRs in the form < net_address > / < prefix > . PeerCIDRs []string `json:"peer_cidrs,omitempty"` // The administrative state of the resource, which is up (true) or down (false). // Default is false AdminStateUp *bool `json:"admin_state_up,omitempty"` // A dictionary with dead peer detection (DPD) protocol controls. DPD *DPDCreateOpts `json:"dpd,omitempty"` // The maximum transmission unit (MTU) value to address fragmentation. // Minimum value is 68 for IPv4, and 1280 for IPv6. MTU int `json:"mtu,omitempty"` } // ToConnectionCreateMap casts a CreateOpts struct to a map. func (opts CreateOpts) ToConnectionCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "ipsec_site_connection") } // Create accepts a CreateOpts struct and uses the values to create a new // IPSec site connection. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToConnectionCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } // Delete will permanently delete a particular IPSec site connection based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } // Get retrieves a particular IPSec site connection based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToConnectionListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the IPSec site connection attributes you want to see returned. type ListOpts struct { IKEPolicyID string `q:"ikepolicy_id"` VPNServiceID string `q:"vpnservice_id"` LocalEPGroupID string `q:"local_ep_group_id"` IPSecPolicyID string `q:"ipsecpolicy_id"` PeerID string `q:"peer_id"` TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` PeerEPGroupID string `q:"peer_ep_group_id"` LocalID string `q:"local_id"` Name string `q:"name"` Description string `q:"description"` PeerAddress string `q:"peer_address"` PSK string `q:"psk"` Initiator Initiator `q:"initiator"` AdminStateUp *bool `q:"admin_state_up"` MTU int `q:"mtu"` } // ToConnectionListQuery formats a ListOpts into a query string. func (opts ListOpts) ToConnectionListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // IPSec site connections. It accepts a ListOpts struct, which allows you to filter // and sort the returned collection for greater efficiency. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { query, err := opts.ToConnectionListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return ConnectionPage{pagination.LinkedPageBase{PageResult: r}} }) } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToConnectionUpdateMap() (map[string]interface{}, error) } // UpdateOpts contains the values used when updating the DPD of an IPSec site connection type DPDUpdateOpts struct { Action Action `json:"action,omitempty"` Timeout int `json:"timeout,omitempty"` Interval int `json:"interval,omitempty"` } // UpdateOpts contains the values used when updating an IPSec site connection type UpdateOpts struct { Description *string `json:"description,omitempty"` Name *string `json:"name,omitempty"` LocalID string `json:"local_id,omitempty"` PeerAddress string `json:"peer_address,omitempty"` PeerID string `json:"peer_id,omitempty"` PeerCIDRs []string `json:"peer_cidrs,omitempty"` LocalEPGroupID string `json:"local_ep_group_id,omitempty"` PeerEPGroupID string `json:"peer_ep_group_id,omitempty"` MTU int `json:"mtu,omitempty"` Initiator Initiator `json:"initiator,omitempty"` PSK string `json:"psk,omitempty"` DPD *DPDUpdateOpts `json:"dpd,omitempty"` AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToConnectionUpdateMap casts an UpdateOpts struct to a map. func (opts UpdateOpts) ToConnectionUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "ipsec_site_connection") } // Update allows IPSec site connections to be updated. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToConnectionUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000121611335005366400411310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/siteconnectionspackage siteconnections import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type DPD struct { // Action is the dead peer detection (DPD) action. Action string `json:"action"` // Timeout is the dead peer detection (DPD) timeout in seconds. Timeout int `json:"timeout"` // Interval is the dead peer detection (DPD) interval in seconds. Interval int `json:"interval"` } // Connection is an IPSec site connection type Connection struct { // IKEPolicyID is the ID of the IKE policy. IKEPolicyID string `json:"ikepolicy_id"` // VPNServiceID is the ID of the VPN service. VPNServiceID string `json:"vpnservice_id"` // LocalEPGroupID is the ID for the endpoint group that contains private subnets for the local side of the connection. LocalEPGroupID string `json:"local_ep_group_id"` // IPSecPolicyID is the ID of the IPSec policy IPSecPolicyID string `json:"ipsecpolicy_id"` // PeerID is the peer router identity for authentication. PeerID string `json:"peer_id"` // TenantID is the ID of the project. TenantID string `json:"tenant_id"` // ProjectID is the ID of the project. ProjectID string `json:"project_id"` // PeerEPGroupID is the ID for the endpoint group that contains private CIDRs in the form < net_address > / < prefix > // for the peer side of the connection. PeerEPGroupID string `json:"peer_ep_group_id"` // LocalID is an ID to be used instead of the external IP address for a virtual router used in traffic // between instances on different networks in east-west traffic. LocalID string `json:"local_id"` // Name is the human readable name of the connection. Name string `json:"name"` // Description is the human readable description of the connection. Description string `json:"description"` // PeerAddress is the peer gateway public IPv4 or IPv6 address or FQDN. PeerAddress string `json:"peer_address"` // RouteMode is the route mode. RouteMode string `json:"route_mode"` // PSK is the pre-shared key. PSK string `json:"psk"` // Initiator indicates whether this VPN can only respond to connections or both respond to and initiate connections. Initiator string `json:"initiator"` // PeerCIDRs is a unique list of valid peer private CIDRs in the form < net_address > / < prefix > . PeerCIDRs []string `json:"peer_cidrs"` // AdminStateUp is the administrative state of the connection. AdminStateUp bool `json:"admin_state_up"` // DPD is the dead peer detection (DPD) protocol controls. DPD DPD `json:"dpd"` // AuthMode is the authentication mode. AuthMode string `json:"auth_mode"` // MTU is the maximum transmission unit (MTU) value to address fragmentation. MTU int `json:"mtu"` // Status indicates whether the IPsec connection is currently operational. // Values are ACTIVE, DOWN, BUILD, ERROR, PENDING_CREATE, PENDING_UPDATE, or PENDING_DELETE. Status string `json:"status"` // ID is the id of the connection ID string `json:"id"` } type commonResult struct { gophercloud.Result } // ConnectionPage is the page returned by a pager when traversing over a // collection of IPSec site connections. type ConnectionPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of IPSec site connections has // reached the end of a page and the pager seeks to traverse over a new one. // In order to do this, it needs to construct the next page's URL. func (r ConnectionPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"ipsec_site_connections_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a ConnectionPage struct is empty. func (r ConnectionPage) IsEmpty() (bool, error) { is, err := ExtractConnections(r) return len(is) == 0, err } // ExtractConnections accepts a Page struct, specifically a Connection struct, // and extracts the elements into a slice of Connection structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractConnections(r pagination.Page) ([]Connection, error) { var s struct { Connections []Connection `json:"ipsec_site_connections"` } err := (r.(ConnectionPage)).ExtractInto(&s) return s.Connections, err } // Extract is a function that accepts a result and extracts an IPSec site connection. func (r commonResult) Extract() (*Connection, error) { var s struct { Connection *Connection `json:"ipsec_site_connection"` } err := r.ExtractInto(&s) return s.Connection, err } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a Connection. type CreateResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its // ExtractErr method to determine if the operation succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a Connection. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a connection type UpdateResult struct { commonResult } testing/000077500000000000000000000000001335005366400405555ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/siteconnectionsrequests_test.go000066400000000000000000000321761335005366400440270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/siteconnections/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/siteconnections" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/ipsec-site-connections", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "ipsec_site_connection": { "psk": "secret", "initiator": "bi-directional", "ipsecpolicy_id": "e6e23d0c-9519-4d52-8ea4-5b1f96d857b1", "admin_state_up": true, "mtu": 1500, "peer_ep_group_id": "9ad5a7e0-6dac-41b4-b20d-a7b8645fddf1", "ikepolicy_id": "9b00d6b0-6c93-4ca5-9747-b8ade7bb514f", "vpnservice_id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", "local_ep_group_id": "3e1815dd-e212-43d0-8f13-b494fa553e68", "peer_address": "172.24.4.233", "peer_id": "172.24.4.233", "name": "vpnconnection1" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "ipsec_site_connection": { "status": "PENDING_CREATE", "psk": "secret", "initiator": "bi-directional", "name": "vpnconnection1", "admin_state_up": true, "project_id": "10039663455a446d8ba2cbb058b0f578", "tenant_id": "10039663455a446d8ba2cbb058b0f578", "auth_mode": "psk", "peer_cidrs": [], "mtu": 1500, "peer_ep_group_id": "9ad5a7e0-6dac-41b4-b20d-a7b8645fddf1", "ikepolicy_id": "9b00d6b0-6c93-4ca5-9747-b8ade7bb514f", "vpnservice_id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", "dpd": { "action": "hold", "interval": 30, "timeout": 120 }, "route_mode": "static", "ipsecpolicy_id": "e6e23d0c-9519-4d52-8ea4-5b1f96d857b1", "local_ep_group_id": "3e1815dd-e212-43d0-8f13-b494fa553e68", "peer_address": "172.24.4.233", "peer_id": "172.24.4.233", "id": "851f280f-5639-4ea3-81aa-e298525ab74b", "description": "" } } `) }) options := siteconnections.CreateOpts{ Name: "vpnconnection1", AdminStateUp: gophercloud.Enabled, PSK: "secret", Initiator: siteconnections.InitiatorBiDirectional, IPSecPolicyID: "e6e23d0c-9519-4d52-8ea4-5b1f96d857b1", MTU: 1500, PeerEPGroupID: "9ad5a7e0-6dac-41b4-b20d-a7b8645fddf1", IKEPolicyID: "9b00d6b0-6c93-4ca5-9747-b8ade7bb514f", VPNServiceID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", LocalEPGroupID: "3e1815dd-e212-43d0-8f13-b494fa553e68", PeerAddress: "172.24.4.233", PeerID: "172.24.4.233", } actual, err := siteconnections.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) expectedDPD := siteconnections.DPD{ Action: "hold", Interval: 30, Timeout: 120, } expected := siteconnections.Connection{ TenantID: "10039663455a446d8ba2cbb058b0f578", Name: "vpnconnection1", AdminStateUp: true, PSK: "secret", Initiator: "bi-directional", IPSecPolicyID: "e6e23d0c-9519-4d52-8ea4-5b1f96d857b1", MTU: 1500, PeerEPGroupID: "9ad5a7e0-6dac-41b4-b20d-a7b8645fddf1", IKEPolicyID: "9b00d6b0-6c93-4ca5-9747-b8ade7bb514f", VPNServiceID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", LocalEPGroupID: "3e1815dd-e212-43d0-8f13-b494fa553e68", PeerAddress: "172.24.4.233", PeerID: "172.24.4.233", Status: "PENDING_CREATE", ProjectID: "10039663455a446d8ba2cbb058b0f578", AuthMode: "psk", PeerCIDRs: []string{}, DPD: expectedDPD, RouteMode: "static", ID: "851f280f-5639-4ea3-81aa-e298525ab74b", Description: "", } th.AssertDeepEquals(t, expected, *actual) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/ipsec-site-connections/5c561d9d-eaea-45f6-ae3e-08d1a7080828", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := siteconnections.Delete(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") th.AssertNoErr(t, res.Err) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/ipsec-site-connections/5c561d9d-eaea-45f6-ae3e-08d1a7080828", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "ipsec_site_connection": { "status": "PENDING_CREATE", "psk": "secret", "initiator": "bi-directional", "name": "vpnconnection1", "admin_state_up": true, "project_id": "10039663455a446d8ba2cbb058b0f578", "tenant_id": "10039663455a446d8ba2cbb058b0f578", "auth_mode": "psk", "peer_cidrs": [], "mtu": 1500, "peer_ep_group_id": "9ad5a7e0-6dac-41b4-b20d-a7b8645fddf1", "ikepolicy_id": "9b00d6b0-6c93-4ca5-9747-b8ade7bb514f", "vpnservice_id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", "dpd": { "action": "hold", "interval": 30, "timeout": 120 }, "route_mode": "static", "ipsecpolicy_id": "e6e23d0c-9519-4d52-8ea4-5b1f96d857b1", "local_ep_group_id": "3e1815dd-e212-43d0-8f13-b494fa553e68", "peer_address": "172.24.4.233", "peer_id": "172.24.4.233", "id": "851f280f-5639-4ea3-81aa-e298525ab74b", "description": "" } } `) }) actual, err := siteconnections.Get(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828").Extract() th.AssertNoErr(t, err) expectedDPD := siteconnections.DPD{ Action: "hold", Interval: 30, Timeout: 120, } expected := siteconnections.Connection{ TenantID: "10039663455a446d8ba2cbb058b0f578", Name: "vpnconnection1", AdminStateUp: true, PSK: "secret", Initiator: "bi-directional", IPSecPolicyID: "e6e23d0c-9519-4d52-8ea4-5b1f96d857b1", MTU: 1500, PeerEPGroupID: "9ad5a7e0-6dac-41b4-b20d-a7b8645fddf1", IKEPolicyID: "9b00d6b0-6c93-4ca5-9747-b8ade7bb514f", VPNServiceID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", LocalEPGroupID: "3e1815dd-e212-43d0-8f13-b494fa553e68", PeerAddress: "172.24.4.233", PeerID: "172.24.4.233", Status: "PENDING_CREATE", ProjectID: "10039663455a446d8ba2cbb058b0f578", AuthMode: "psk", PeerCIDRs: []string{}, DPD: expectedDPD, RouteMode: "static", ID: "851f280f-5639-4ea3-81aa-e298525ab74b", Description: "", } th.AssertDeepEquals(t, expected, *actual) } func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/ipsec-site-connections", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "ipsec_site_connections":[ { "status": "PENDING_CREATE", "psk": "secret", "initiator": "bi-directional", "name": "vpnconnection1", "admin_state_up": true, "project_id": "10039663455a446d8ba2cbb058b0f578", "tenant_id": "10039663455a446d8ba2cbb058b0f578", "auth_mode": "psk", "peer_cidrs": [], "mtu": 1500, "peer_ep_group_id": "9ad5a7e0-6dac-41b4-b20d-a7b8645fddf1", "ikepolicy_id": "9b00d6b0-6c93-4ca5-9747-b8ade7bb514f", "vpnservice_id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", "dpd": { "action": "hold", "interval": 30, "timeout": 120 }, "route_mode": "static", "ipsecpolicy_id": "e6e23d0c-9519-4d52-8ea4-5b1f96d857b1", "local_ep_group_id": "3e1815dd-e212-43d0-8f13-b494fa553e68", "peer_address": "172.24.4.233", "peer_id": "172.24.4.233", "id": "851f280f-5639-4ea3-81aa-e298525ab74b", "description": "" }] } `) }) count := 0 siteconnections.List(fake.ServiceClient(), siteconnections.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := siteconnections.ExtractConnections(page) if err != nil { t.Errorf("Failed to extract members: %v", err) return false, err } expectedDPD := siteconnections.DPD{ Action: "hold", Interval: 30, Timeout: 120, } expected := []siteconnections.Connection{ { TenantID: "10039663455a446d8ba2cbb058b0f578", Name: "vpnconnection1", AdminStateUp: true, PSK: "secret", Initiator: "bi-directional", IPSecPolicyID: "e6e23d0c-9519-4d52-8ea4-5b1f96d857b1", MTU: 1500, PeerEPGroupID: "9ad5a7e0-6dac-41b4-b20d-a7b8645fddf1", IKEPolicyID: "9b00d6b0-6c93-4ca5-9747-b8ade7bb514f", VPNServiceID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", LocalEPGroupID: "3e1815dd-e212-43d0-8f13-b494fa553e68", PeerAddress: "172.24.4.233", PeerID: "172.24.4.233", Status: "PENDING_CREATE", ProjectID: "10039663455a446d8ba2cbb058b0f578", AuthMode: "psk", PeerCIDRs: []string{}, DPD: expectedDPD, RouteMode: "static", ID: "851f280f-5639-4ea3-81aa-e298525ab74b", Description: "", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/vpn/ipsec-site-connections/5c561d9d-eaea-45f6-ae3e-08d1a7080828", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "ipsec_site_connection": { "psk": "updatedsecret", "initiator": "response-only", "name": "updatedconnection", "description": "updateddescription" } } `) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "ipsec_site_connection": { "status": "ACTIVE", "psk": "updatedsecret", "initiator": "response-only", "name": "updatedconnection", "admin_state_up": true, "project_id": "10039663455a446d8ba2cbb058b0f578", "tenant_id": "10039663455a446d8ba2cbb058b0f578", "auth_mode": "psk", "peer_cidrs": [], "mtu": 1500, "peer_ep_group_id": "9ad5a7e0-6dac-41b4-b20d-a7b8645fddf1", "ikepolicy_id": "9b00d6b0-6c93-4ca5-9747-b8ade7bb514f", "vpnservice_id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", "dpd": { "action": "hold", "interval": 30, "timeout": 120 }, "route_mode": "static", "ipsecpolicy_id": "e6e23d0c-9519-4d52-8ea4-5b1f96d857b1", "local_ep_group_id": "3e1815dd-e212-43d0-8f13-b494fa553e68", "peer_address": "172.24.4.233", "peer_id": "172.24.4.233", "id": "851f280f-5639-4ea3-81aa-e298525ab74b", "description": "updateddescription" } } } `) }) updatedName := "updatedconnection" updatedDescription := "updateddescription" options := siteconnections.UpdateOpts{ Name: &updatedName, Description: &updatedDescription, Initiator: siteconnections.InitiatorResponseOnly, PSK: "updatedsecret", } actual, err := siteconnections.Update(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828", options).Extract() th.AssertNoErr(t, err) expectedDPD := siteconnections.DPD{ Action: "hold", Interval: 30, Timeout: 120, } expected := siteconnections.Connection{ TenantID: "10039663455a446d8ba2cbb058b0f578", Name: "updatedconnection", AdminStateUp: true, PSK: "updatedsecret", Initiator: "response-only", IPSecPolicyID: "e6e23d0c-9519-4d52-8ea4-5b1f96d857b1", MTU: 1500, PeerEPGroupID: "9ad5a7e0-6dac-41b4-b20d-a7b8645fddf1", IKEPolicyID: "9b00d6b0-6c93-4ca5-9747-b8ade7bb514f", VPNServiceID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", LocalEPGroupID: "3e1815dd-e212-43d0-8f13-b494fa553e68", PeerAddress: "172.24.4.233", PeerID: "172.24.4.233", Status: "ACTIVE", ProjectID: "10039663455a446d8ba2cbb058b0f578", AuthMode: "psk", PeerCIDRs: []string{}, DPD: expectedDPD, RouteMode: "static", ID: "851f280f-5639-4ea3-81aa-e298525ab74b", Description: "updateddescription", } th.AssertDeepEquals(t, expected, *actual) } urls.go000066400000000000000000000005521335005366400404160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/extensions/vpnaas/siteconnectionspackage siteconnections import "github.com/gophercloud/gophercloud" const ( rootPath = "vpn" resourcePath = "ipsec-site-connections" ) func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, resourcePath) } func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/networks/000077500000000000000000000000001335005366400321355ustar00rootroot00000000000000doc.go000066400000000000000000000027671335005366400331660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/networks/* Package networks contains functionality for working with Neutron network resources. A network is an isolated virtual layer-2 broadcast domain that is typically reserved for the tenant who created it (unless you configure the network to be shared). Tenants can create multiple networks until the thresholds per-tenant quota is reached. In the v2.0 Networking API, the network is the main entity. Ports and subnets are always associated with a network. Example to List Networks listOpts := networks.ListOpts{ TenantID: "a99e9b4e620e4db09a2dfb6e42a01e66", } allPages, err := networks.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allNetworks, err := networks.ExtractNetworks(allPages) if err != nil { panic(err) } for _, network := range allNetworks { fmt.Printf("%+v", network) } Example to Create a Network iTrue := true createOpts := networks.CreateOpts{ Name: "network_1", AdminStateUp: &iTrue, } network, err := networks.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Network networkID := "484cda0e-106f-4f4b-bb3f-d413710bbe78" updateOpts := networks.UpdateOpts{ Name: "new_name", } network, err := networks.Update(networkClient, networkID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Network networkID := "484cda0e-106f-4f4b-bb3f-d413710bbe78" err := networks.Delete(networkClient, networkID).ExtractErr() if err != nil { panic(err) } */ package networks requests.go000066400000000000000000000126231335005366400342640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/networkspackage networks import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToNetworkListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the network attributes you want to see returned. SortKey allows you to sort // by a particular network attribute. SortDir sets the direction, and is either // `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { Status string `q:"status"` Name string `q:"name"` AdminStateUp *bool `q:"admin_state_up"` TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` Shared *bool `q:"shared"` ID string `q:"id"` Marker string `q:"marker"` Limit int `q:"limit"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToNetworkListQuery formats a ListOpts into a query string. func (opts ListOpts) ToNetworkListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // networks. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(c) if opts != nil { query, err := opts.ToNetworkListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return NetworkPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves a specific network based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(getURL(c, id), &r.Body, nil) return } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToNetworkCreateMap() (map[string]interface{}, error) } // CreateOpts represents options used to create a network. type CreateOpts struct { AdminStateUp *bool `json:"admin_state_up,omitempty"` Name string `json:"name,omitempty"` Shared *bool `json:"shared,omitempty"` TenantID string `json:"tenant_id,omitempty"` ProjectID string `json:"project_id,omitempty"` AvailabilityZoneHints []string `json:"availability_zone_hints,omitempty"` } // ToNetworkCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "network") } // Create accepts a CreateOpts struct and creates a new network using the values // provided. This operation does not actually require a request body, i.e. the // CreateOpts struct argument can be empty. // // The tenant ID that is contained in the URI is the tenant that creates the // network. An admin user, however, has the option of specifying another tenant // ID in the CreateOpts struct. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToNetworkCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(createURL(c), b, &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToNetworkUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents options used to update a network. type UpdateOpts struct { AdminStateUp *bool `json:"admin_state_up,omitempty"` Name string `json:"name,omitempty"` Shared *bool `json:"shared,omitempty"` } // ToNetworkUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToNetworkUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "network") } // Update accepts a UpdateOpts struct and updates an existing network using the // values provided. For more information, see the Create function. func Update(c *gophercloud.ServiceClient, networkID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToNetworkUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(updateURL(c, networkID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) return } // Delete accepts a unique ID and deletes the network associated with it. func Delete(c *gophercloud.ServiceClient, networkID string) (r DeleteResult) { _, r.Err = c.Delete(deleteURL(c, networkID), nil) return } // IDFromName is a convenience function that returns a network's ID, given // its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" listOpts := ListOpts{ Name: name, } pages, err := List(client, listOpts).AllPages() if err != nil { return "", err } all, err := ExtractNetworks(pages) if err != nil { return "", err } for _, s := range all { if s.Name == name { count++ id = s.ID } } switch count { case 0: return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "network"} case 1: return id, nil default: return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "network"} } } results.go000066400000000000000000000066631335005366400341210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/networkspackage networks import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a network resource. func (r commonResult) Extract() (*Network, error) { var s Network err := r.ExtractInto(&s) return &s, err } func (r commonResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "network") } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a Network. type CreateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a Network. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a Network. type UpdateResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its // ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // Network represents, well, a network. type Network struct { // UUID for the network ID string `json:"id"` // Human-readable name for the network. Might not be unique. Name string `json:"name"` // The administrative state of network. If false (down), the network does not // forward packets. AdminStateUp bool `json:"admin_state_up"` // Indicates whether network is currently operational. Possible values include // `ACTIVE', `DOWN', `BUILD', or `ERROR'. Plug-ins might define additional // values. Status string `json:"status"` // Subnets associated with this network. Subnets []string `json:"subnets"` // TenantID is the project owner of the network. TenantID string `json:"tenant_id"` // ProjectID is the project owner of the network. ProjectID string `json:"project_id"` // Specifies whether the network resource can be accessed by any tenant. Shared bool `json:"shared"` // Availability zone hints groups network nodes that run services like DHCP, L3, FW, and others. // Used to make network resources highly available. AvailabilityZoneHints []string `json:"availability_zone_hints"` } // NetworkPage is the page returned by a pager when traversing over a // collection of networks. type NetworkPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of networks has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r NetworkPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"networks_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a NetworkPage struct is empty. func (r NetworkPage) IsEmpty() (bool, error) { is, err := ExtractNetworks(r) return len(is) == 0, err } // ExtractNetworks accepts a Page struct, specifically a NetworkPage struct, // and extracts the elements into a slice of Network structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractNetworks(r pagination.Page) ([]Network, error) { var s []Network err := ExtractNetworksInto(r, &s) return s, err } func ExtractNetworksInto(r pagination.Page, v interface{}) error { return r.(NetworkPage).Result.ExtractIntoSlicePtr(v, "networks") } testing/000077500000000000000000000000001335005366400335335ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/networksdoc.go000066400000000000000000000000471335005366400346300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/networks/testing// networks unit tests package testing fixtures.go000066400000000000000000000122001335005366400357260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/networks/testingpackage testing import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" ) const ListResponse = ` { "networks": [ { "status": "ACTIVE", "subnets": [ "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" ], "name": "public", "admin_state_up": true, "tenant_id": "4fd44f30292945e481c7b8a0c8908869", "shared": true, "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "provider:segmentation_id": 9876543210, "provider:physical_network": null, "provider:network_type": "local", "router:external": true, "port_security_enabled": true }, { "status": "ACTIVE", "subnets": [ "08eae331-0402-425a-923c-34f7cfe39c1b" ], "name": "private", "admin_state_up": true, "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", "shared": false, "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", "provider:segmentation_id": 1234567890, "provider:physical_network": null, "provider:network_type": "local", "router:external": false, "port_security_enabled": false } ] }` const GetResponse = ` { "network": { "status": "ACTIVE", "subnets": [ "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" ], "name": "public", "admin_state_up": true, "tenant_id": "4fd44f30292945e481c7b8a0c8908869", "shared": true, "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "provider:segmentation_id": 9876543210, "provider:physical_network": null, "provider:network_type": "local", "router:external": true, "port_security_enabled": true } }` const CreateRequest = ` { "network": { "name": "private", "admin_state_up": true } }` const CreateResponse = ` { "network": { "status": "ACTIVE", "subnets": ["08eae331-0402-425a-923c-34f7cfe39c1b"], "name": "private", "admin_state_up": true, "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", "shared": false, "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", "provider:segmentation_id": 9876543210, "provider:physical_network": null, "provider:network_type": "local" } }` const CreatePortSecurityRequest = ` { "network": { "name": "private", "admin_state_up": true, "port_security_enabled": false } }` const CreatePortSecurityResponse = ` { "network": { "status": "ACTIVE", "subnets": ["08eae331-0402-425a-923c-34f7cfe39c1b"], "name": "private", "admin_state_up": true, "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", "shared": false, "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", "provider:segmentation_id": 9876543210, "provider:physical_network": null, "provider:network_type": "local", "port_security_enabled": false } }` const CreateOptionalFieldsRequest = ` { "network": { "name": "public", "admin_state_up": true, "shared": true, "tenant_id": "12345", "availability_zone_hints": ["zone1", "zone2"] } }` const UpdateRequest = ` { "network": { "name": "new_network_name", "admin_state_up": false, "shared": true } }` const UpdateResponse = ` { "network": { "status": "ACTIVE", "subnets": [], "name": "new_network_name", "admin_state_up": false, "tenant_id": "4fd44f30292945e481c7b8a0c8908869", "shared": true, "id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c", "provider:segmentation_id": 1234567890, "provider:physical_network": null, "provider:network_type": "local" } }` const UpdatePortSecurityRequest = ` { "network": { "port_security_enabled": false } }` const UpdatePortSecurityResponse = ` { "network": { "status": "ACTIVE", "subnets": ["08eae331-0402-425a-923c-34f7cfe39c1b"], "name": "private", "admin_state_up": true, "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", "shared": false, "id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c", "provider:segmentation_id": 9876543210, "provider:physical_network": null, "provider:network_type": "local", "port_security_enabled": false } }` var Network1 = networks.Network{ Status: "ACTIVE", Subnets: []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"}, Name: "public", AdminStateUp: true, TenantID: "4fd44f30292945e481c7b8a0c8908869", Shared: true, ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", } var Network2 = networks.Network{ Status: "ACTIVE", Subnets: []string{"08eae331-0402-425a-923c-34f7cfe39c1b"}, Name: "private", AdminStateUp: true, TenantID: "26a7980765d0414dbc1fc1f88cdb7e6e", Shared: false, ID: "db193ab3-96e3-4cb3-8fc5-05f4296d0324", } var ExpectedNetworkSlice = []networks.Network{Network1, Network2} requests_test.go000066400000000000000000000215561335005366400370050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/networks/testingpackage testing import ( "fmt" "net/http" "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsecurity" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListResponse) }) client := fake.ServiceClient() count := 0 networks.List(client, networks.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := networks.ExtractNetworks(page) if err != nil { t.Errorf("Failed to extract networks: %v", err) return false, err } th.CheckDeepEquals(t, ExpectedNetworkSlice, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestListWithExtensions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListResponse) }) client := fake.ServiceClient() type networkWithExt struct { networks.Network portsecurity.PortSecurityExt } var allNetworks []networkWithExt allPages, err := networks.List(client, networks.ListOpts{}).AllPages() th.AssertNoErr(t, err) err = networks.ExtractNetworksInto(allPages, &allNetworks) th.AssertNoErr(t, err) th.AssertEquals(t, allNetworks[0].Status, "ACTIVE") th.AssertEquals(t, allNetworks[0].PortSecurityEnabled, true) th.AssertEquals(t, allNetworks[0].Subnets[0], "54d6f61d-db07-451c-9ab3-b9609b6b6f0b") th.AssertEquals(t, allNetworks[1].Subnets[0], "08eae331-0402-425a-923c-34f7cfe39c1b") } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetResponse) }) n, err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &Network1, n) } func TestGetWithExtensions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetResponse) }) var networkWithExtensions struct { networks.Network portsecurity.PortSecurityExt } err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&networkWithExtensions) th.AssertNoErr(t, err) th.AssertEquals(t, networkWithExtensions.Status, "ACTIVE") th.AssertEquals(t, networkWithExtensions.PortSecurityEnabled, true) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, CreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, CreateResponse) }) iTrue := true options := networks.CreateOpts{Name: "private", AdminStateUp: &iTrue} n, err := networks.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "ACTIVE") th.AssertDeepEquals(t, &Network2, n) } func TestCreateWithOptionalFields(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, CreateOptionalFieldsRequest) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, `{}`) }) iTrue := true options := networks.CreateOpts{ Name: "public", AdminStateUp: &iTrue, Shared: &iTrue, TenantID: "12345", AvailabilityZoneHints: []string{"zone1", "zone2"}, } _, err := networks.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, UpdateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, UpdateResponse) }) iTrue, iFalse := true, false options := networks.UpdateOpts{Name: "new_network_name", AdminStateUp: &iFalse, Shared: &iTrue} n, err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, "new_network_name") th.AssertEquals(t, n.AdminStateUp, false) th.AssertEquals(t, n.Shared, true) th.AssertEquals(t, n.ID, "4e8e5957-649f-477b-9e5b-f1f75b21c03c") } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := networks.Delete(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c") th.AssertNoErr(t, res.Err) } func TestCreatePortSecurity(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, CreatePortSecurityRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, CreatePortSecurityResponse) }) var networkWithExtensions struct { networks.Network portsecurity.PortSecurityExt } iTrue := true iFalse := false networkCreateOpts := networks.CreateOpts{Name: "private", AdminStateUp: &iTrue} createOpts := portsecurity.NetworkCreateOptsExt{ CreateOptsBuilder: networkCreateOpts, PortSecurityEnabled: &iFalse, } err := networks.Create(fake.ServiceClient(), createOpts).ExtractInto(&networkWithExtensions) th.AssertNoErr(t, err) th.AssertEquals(t, networkWithExtensions.Status, "ACTIVE") th.AssertEquals(t, networkWithExtensions.PortSecurityEnabled, false) } func TestUpdatePortSecurity(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, UpdatePortSecurityRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, UpdatePortSecurityResponse) }) var networkWithExtensions struct { networks.Network portsecurity.PortSecurityExt } iFalse := false networkUpdateOpts := networks.UpdateOpts{} updateOpts := portsecurity.NetworkUpdateOptsExt{ UpdateOptsBuilder: networkUpdateOpts, PortSecurityEnabled: &iFalse, } err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", updateOpts).ExtractInto(&networkWithExtensions) th.AssertNoErr(t, err) th.AssertEquals(t, networkWithExtensions.Name, "private") th.AssertEquals(t, networkWithExtensions.AdminStateUp, true) th.AssertEquals(t, networkWithExtensions.Shared, false) th.AssertEquals(t, networkWithExtensions.ID, "4e8e5957-649f-477b-9e5b-f1f75b21c03c") th.AssertEquals(t, networkWithExtensions.PortSecurityEnabled, false) } urls.go000066400000000000000000000012611335005366400333720ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/networkspackage networks import "github.com/gophercloud/gophercloud" func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("networks", id) } func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("networks") } func getURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } func listURL(c *gophercloud.ServiceClient) string { return rootURL(c) } func createURL(c *gophercloud.ServiceClient) string { return rootURL(c) } func updateURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } func deleteURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/ports/000077500000000000000000000000001335005366400314305ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/ports/doc.go000066400000000000000000000034641335005366400325330ustar00rootroot00000000000000/* Package ports contains functionality for working with Neutron port resources. A port represents a virtual switch port on a logical network switch. Virtual instances attach their interfaces into ports. The logical port also defines the MAC address and the IP address(es) to be assigned to the interfaces plugged into them. When IP addresses are associated to a port, this also implies the port is associated with a subnet, as the IP address was taken from the allocation pool for a specific subnet. Example to List Ports listOpts := ports.ListOpts{ DeviceID: "b0b89efe-82f8-461d-958b-adbf80f50c7d", } allPages, err := ports.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allPorts, err := ports.ExtractPorts(allPages) if err != nil { panic(err) } for _, port := range allPorts { fmt.Printf("%+v\n", port) } Example to Create a Port createOtps := ports.CreateOpts{ Name: "private-port", AdminStateUp: &asu, NetworkID: "a87cc70a-3e15-4acf-8205-9b711a3531b7", FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, }, SecurityGroups: &[]string{"foo"}, AllowedAddressPairs: []ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, } port, err := ports.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Port portID := "c34bae2b-7641-49b6-bf6d-d8e473620ed8" updateOpts := ports.UpdateOpts{ Name: "new_name", SecurityGroups: &[]string{}, } port, err := ports.Update(networkClient, portID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Port portID := "c34bae2b-7641-49b6-bf6d-d8e473620ed8" err := ports.Delete(networkClient, portID).ExtractErr() if err != nil { panic(err) } */ package ports requests.go000066400000000000000000000137701335005366400335630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/portspackage ports import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToPortListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the port attributes you want to see returned. SortKey allows you to sort // by a particular port attribute. SortDir sets the direction, and is either // `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { Status string `q:"status"` Name string `q:"name"` AdminStateUp *bool `q:"admin_state_up"` NetworkID string `q:"network_id"` TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` DeviceOwner string `q:"device_owner"` MACAddress string `q:"mac_address"` ID string `q:"id"` DeviceID string `q:"device_id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToPortListQuery formats a ListOpts into a query string. func (opts ListOpts) ToPortListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // ports. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. // // Default policy settings return only those ports that are owned by the tenant // who submits the request, unless the request is submitted by a user with // administrative rights. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(c) if opts != nil { query, err := opts.ToPortListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return PortPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves a specific port based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(getURL(c, id), &r.Body, nil) return } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToPortCreateMap() (map[string]interface{}, error) } // CreateOpts represents the attributes used when creating a new port. type CreateOpts struct { NetworkID string `json:"network_id" required:"true"` Name string `json:"name,omitempty"` AdminStateUp *bool `json:"admin_state_up,omitempty"` MACAddress string `json:"mac_address,omitempty"` FixedIPs interface{} `json:"fixed_ips,omitempty"` DeviceID string `json:"device_id,omitempty"` DeviceOwner string `json:"device_owner,omitempty"` TenantID string `json:"tenant_id,omitempty"` ProjectID string `json:"project_id,omitempty"` SecurityGroups *[]string `json:"security_groups,omitempty"` AllowedAddressPairs []AddressPair `json:"allowed_address_pairs,omitempty"` } // ToPortCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToPortCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "port") } // Create accepts a CreateOpts struct and creates a new network using the values // provided. You must remember to provide a NetworkID value. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToPortCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(createURL(c), b, &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToPortUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents the attributes used when updating an existing port. type UpdateOpts struct { Name string `json:"name,omitempty"` AdminStateUp *bool `json:"admin_state_up,omitempty"` FixedIPs interface{} `json:"fixed_ips,omitempty"` DeviceID string `json:"device_id,omitempty"` DeviceOwner string `json:"device_owner,omitempty"` SecurityGroups *[]string `json:"security_groups,omitempty"` AllowedAddressPairs *[]AddressPair `json:"allowed_address_pairs,omitempty"` } // ToPortUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToPortUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "port") } // Update accepts a UpdateOpts struct and updates an existing port using the // values provided. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToPortUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) return } // Delete accepts a unique ID and deletes the port associated with it. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(deleteURL(c, id), nil) return } // IDFromName is a convenience function that returns a port's ID, // given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" listOpts := ListOpts{ Name: name, } pages, err := List(client, listOpts).AllPages() if err != nil { return "", err } all, err := ExtractPorts(pages) if err != nil { return "", err } for _, s := range all { if s.Name == name { count++ id = s.ID } } switch count { case 0: return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "port"} case 1: return id, nil default: return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "port"} } } results.go000066400000000000000000000101771335005366400334070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/portspackage ports import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a port resource. func (r commonResult) Extract() (*Port, error) { var s Port err := r.ExtractInto(&s) return &s, err } func (r commonResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "port") } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a Port. type CreateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a Port. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a Port. type UpdateResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its // ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // IP is a sub-struct that represents an individual IP. type IP struct { SubnetID string `json:"subnet_id"` IPAddress string `json:"ip_address,omitempty"` } // AddressPair contains the IP Address and the MAC address. type AddressPair struct { IPAddress string `json:"ip_address,omitempty"` MACAddress string `json:"mac_address,omitempty"` } // Port represents a Neutron port. See package documentation for a top-level // description of what this is. type Port struct { // UUID for the port. ID string `json:"id"` // Network that this port is associated with. NetworkID string `json:"network_id"` // Human-readable name for the port. Might not be unique. Name string `json:"name"` // Administrative state of port. If false (down), port does not forward // packets. AdminStateUp bool `json:"admin_state_up"` // Indicates whether network is currently operational. Possible values include // `ACTIVE', `DOWN', `BUILD', or `ERROR'. Plug-ins might define additional // values. Status string `json:"status"` // Mac address to use on this port. MACAddress string `json:"mac_address"` // Specifies IP addresses for the port thus associating the port itself with // the subnets where the IP addresses are picked from FixedIPs []IP `json:"fixed_ips"` // TenantID is the project owner of the port. TenantID string `json:"tenant_id"` // ProjectID is the project owner of the port. ProjectID string `json:"project_id"` // Identifies the entity (e.g.: dhcp agent) using this port. DeviceOwner string `json:"device_owner"` // Specifies the IDs of any security groups associated with a port. SecurityGroups []string `json:"security_groups"` // Identifies the device (e.g., virtual server) using this port. DeviceID string `json:"device_id"` // Identifies the list of IP addresses the port will recognize/accept AllowedAddressPairs []AddressPair `json:"allowed_address_pairs"` } // PortPage is the page returned by a pager when traversing over a collection // of network ports. type PortPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of ports has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r PortPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"ports_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a PortPage struct is empty. func (r PortPage) IsEmpty() (bool, error) { is, err := ExtractPorts(r) return len(is) == 0, err } // ExtractPorts accepts a Page struct, specifically a PortPage struct, // and extracts the elements into a slice of Port structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractPorts(r pagination.Page) ([]Port, error) { var s []Port err := ExtractPortsInto(r, &s) return s, err } func ExtractPortsInto(r pagination.Page, v interface{}) error { return r.(PortPage).Result.ExtractIntoSlicePtr(v, "ports") } testing/000077500000000000000000000000001335005366400330265ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/portsdoc.go000066400000000000000000000000441335005366400341200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/ports/testing// ports unit tests package testing fixtures.go000066400000000000000000000440301335005366400352270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/ports/testingpackage testing const ListResponse = ` { "ports": [ { "status": "ACTIVE", "binding:host_id": "devstack", "name": "", "admin_state_up": true, "network_id": "70c1db1f-b701-45bd-96e0-a313ee3430b3", "tenant_id": "", "device_owner": "network:router_gateway", "mac_address": "fa:16:3e:58:42:ed", "binding:vnic_type": "normal", "fixed_ips": [ { "subnet_id": "008ba151-0b8c-4a67-98b5-0d2b87666062", "ip_address": "172.24.4.2" } ], "id": "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b", "security_groups": [], "device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824", "port_security_enabled": false } ] } ` const GetResponse = ` { "port": { "status": "ACTIVE", "binding:host_id": "devstack", "name": "", "allowed_address_pairs": [], "admin_state_up": true, "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "tenant_id": "7e02058126cc4950b75f9970368ba177", "extra_dhcp_opts": [], "binding:vif_details": { "port_filter": true, "ovs_hybrid_plug": true }, "binding:vif_type": "ovs", "device_owner": "network:router_interface", "port_security_enabled": false, "mac_address": "fa:16:3e:23:fd:d7", "binding:profile": {}, "binding:vnic_type": "normal", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.1" } ], "id": "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", "security_groups": [], "device_id": "5e3898d7-11be-483e-9732-b2f5eccd2b2e" } } ` const CreateRequest = ` { "port": { "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "name": "private-port", "admin_state_up": true, "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.2" } ], "security_groups": ["foo"], "allowed_address_pairs": [ { "ip_address": "10.0.0.4", "mac_address": "fa:16:3e:c9:cb:f0" } ] } } ` const CreateResponse = ` { "port": { "status": "DOWN", "name": "private-port", "admin_state_up": true, "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", "device_owner": "", "mac_address": "fa:16:3e:c9:cb:f0", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.2" } ], "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "security_groups": [ "f0ac4394-7e4a-4409-9701-ba8be283dbc3" ], "allowed_address_pairs": [ { "ip_address": "10.0.0.4", "mac_address": "fa:16:3e:c9:cb:f0" } ], "device_id": "" } } ` const CreateOmitSecurityGroupsRequest = ` { "port": { "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "name": "private-port", "admin_state_up": true, "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.2" } ], "allowed_address_pairs": [ { "ip_address": "10.0.0.4", "mac_address": "fa:16:3e:c9:cb:f0" } ] } } ` const CreateWithNoSecurityGroupsRequest = ` { "port": { "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "name": "private-port", "admin_state_up": true, "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.2" } ], "security_groups": [], "allowed_address_pairs": [ { "ip_address": "10.0.0.4", "mac_address": "fa:16:3e:c9:cb:f0" } ] } } ` const CreateWithNoSecurityGroupsResponse = ` { "port": { "status": "DOWN", "name": "private-port", "admin_state_up": true, "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", "device_owner": "", "mac_address": "fa:16:3e:c9:cb:f0", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.2" } ], "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "allowed_address_pairs": [ { "ip_address": "10.0.0.4", "mac_address": "fa:16:3e:c9:cb:f0" } ], "device_id": "" } } ` const CreateOmitSecurityGroupsResponse = ` { "port": { "status": "DOWN", "name": "private-port", "admin_state_up": true, "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", "device_owner": "", "mac_address": "fa:16:3e:c9:cb:f0", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.2" } ], "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "security_groups": [ "f0ac4394-7e4a-4409-9701-ba8be283dbc3" ], "allowed_address_pairs": [ { "ip_address": "10.0.0.4", "mac_address": "fa:16:3e:c9:cb:f0" } ], "device_id": "" } } ` const CreatePortSecurityRequest = ` { "port": { "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "name": "private-port", "admin_state_up": true, "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.2" } ], "security_groups": ["foo"], "allowed_address_pairs": [ { "ip_address": "10.0.0.4", "mac_address": "fa:16:3e:c9:cb:f0" } ], "port_security_enabled": false } } ` const CreatePortSecurityResponse = ` { "port": { "status": "DOWN", "name": "private-port", "admin_state_up": true, "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", "device_owner": "", "mac_address": "fa:16:3e:c9:cb:f0", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.2" } ], "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "security_groups": [ "f0ac4394-7e4a-4409-9701-ba8be283dbc3" ], "allowed_address_pairs": [ { "ip_address": "10.0.0.4", "mac_address": "fa:16:3e:c9:cb:f0" } ], "device_id": "", "port_security_enabled": false } } ` const UpdateRequest = ` { "port": { "name": "new_port_name", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.3" } ], "allowed_address_pairs": [ { "ip_address": "10.0.0.4", "mac_address": "fa:16:3e:c9:cb:f0" } ], "security_groups": [ "f0ac4394-7e4a-4409-9701-ba8be283dbc3" ] } } ` const UpdateResponse = ` { "port": { "status": "DOWN", "name": "new_port_name", "admin_state_up": true, "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", "device_owner": "", "mac_address": "fa:16:3e:c9:cb:f0", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.3" } ], "allowed_address_pairs": [ { "ip_address": "10.0.0.4", "mac_address": "fa:16:3e:c9:cb:f0" } ], "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "security_groups": [ "f0ac4394-7e4a-4409-9701-ba8be283dbc3" ], "device_id": "" } } ` const UpdateOmitSecurityGroupsRequest = ` { "port": { "name": "new_port_name", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.3" } ], "allowed_address_pairs": [ { "ip_address": "10.0.0.4", "mac_address": "fa:16:3e:c9:cb:f0" } ] } } ` const UpdateOmitSecurityGroupsResponse = ` { "port": { "status": "DOWN", "name": "new_port_name", "admin_state_up": true, "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", "device_owner": "", "mac_address": "fa:16:3e:c9:cb:f0", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.3" } ], "allowed_address_pairs": [ { "ip_address": "10.0.0.4", "mac_address": "fa:16:3e:c9:cb:f0" } ], "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "security_groups": [ "f0ac4394-7e4a-4409-9701-ba8be283dbc3" ], "device_id": "" } } ` const UpdatePortSecurityRequest = ` { "port": { "port_security_enabled": false } } ` const UpdatePortSecurityResponse = ` { "port": { "status": "DOWN", "name": "private-port", "admin_state_up": true, "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", "device_owner": "", "mac_address": "fa:16:3e:c9:cb:f0", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.2" } ], "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "security_groups": [ "f0ac4394-7e4a-4409-9701-ba8be283dbc3" ], "allowed_address_pairs": [ { "ip_address": "10.0.0.4", "mac_address": "fa:16:3e:c9:cb:f0" } ], "device_id": "", "port_security_enabled": false } } ` const RemoveSecurityGroupRequest = ` { "port": { "name": "new_port_name", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.3" } ], "allowed_address_pairs": [ { "ip_address": "10.0.0.4", "mac_address": "fa:16:3e:c9:cb:f0" } ], "security_groups": [] } } ` const RemoveSecurityGroupResponse = ` { "port": { "status": "DOWN", "name": "new_port_name", "admin_state_up": true, "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", "device_owner": "", "mac_address": "fa:16:3e:c9:cb:f0", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.3" } ], "allowed_address_pairs": [ { "ip_address": "10.0.0.4", "mac_address": "fa:16:3e:c9:cb:f0" } ], "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "device_id": "" } } ` const RemoveAllowedAddressPairsRequest = ` { "port": { "name": "new_port_name", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.3" } ], "allowed_address_pairs": [], "security_groups": [ "f0ac4394-7e4a-4409-9701-ba8be283dbc3" ] } } ` const RemoveAllowedAddressPairsResponse = ` { "port": { "status": "DOWN", "name": "new_port_name", "admin_state_up": true, "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", "device_owner": "", "mac_address": "fa:16:3e:c9:cb:f0", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.3" } ], "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "security_groups": [ "f0ac4394-7e4a-4409-9701-ba8be283dbc3" ], "device_id": "" } } ` const DontUpdateAllowedAddressPairsRequest = ` { "port": { "name": "new_port_name", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.3" } ], "security_groups": [ "f0ac4394-7e4a-4409-9701-ba8be283dbc3" ] } } ` const DontUpdateAllowedAddressPairsResponse = ` { "port": { "status": "DOWN", "name": "new_port_name", "admin_state_up": true, "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", "device_owner": "", "mac_address": "fa:16:3e:c9:cb:f0", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.3" } ], "allowed_address_pairs": [ { "ip_address": "10.0.0.4", "mac_address": "fa:16:3e:c9:cb:f0" } ], "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "security_groups": [ "f0ac4394-7e4a-4409-9701-ba8be283dbc3" ], "device_id": "" } } ` // GetWithExtraDHCPOptsResponse represents a raw port response with extra // DHCP options. const GetWithExtraDHCPOptsResponse = ` { "port": { "status": "ACTIVE", "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", "extra_dhcp_opts": [ { "opt_name": "option1", "opt_value": "value1", "ip_version": 4 }, { "opt_name": "option2", "opt_value": "value2", "ip_version": 4 } ], "admin_state_up": true, "name": "port-with-extra-dhcp-opts", "device_owner": "", "mac_address": "fa:16:3e:c9:cb:f0", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.4" } ], "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "device_id": "" } } ` // CreateWithExtraDHCPOptsRequest represents a raw port creation request // with extra DHCP options. const CreateWithExtraDHCPOptsRequest = ` { "port": { "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "name": "port-with-extra-dhcp-opts", "admin_state_up": true, "fixed_ips": [ { "ip_address": "10.0.0.2", "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2" } ], "extra_dhcp_opts": [ { "opt_name": "option1", "opt_value": "value1" } ] } } ` // CreateWithExtraDHCPOptsResponse represents a raw port creation response // with extra DHCP options. const CreateWithExtraDHCPOptsResponse = ` { "port": { "status": "DOWN", "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", "extra_dhcp_opts": [ { "opt_name": "option1", "opt_value": "value1", "ip_version": 4 } ], "admin_state_up": true, "name": "port-with-extra-dhcp-opts", "device_owner": "", "mac_address": "fa:16:3e:c9:cb:f0", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.2" } ], "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "device_id": "" } } ` // UpdateWithExtraDHCPOptsRequest represents a raw port update request with // extra DHCP options. const UpdateWithExtraDHCPOptsRequest = ` { "port": { "name": "updated-port-with-dhcp-opts", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.3" } ], "extra_dhcp_opts": [ { "opt_name": "option1", "opt_value": null }, { "opt_name": "option2", "opt_value": "value2" } ] } } ` // UpdateWithExtraDHCPOptsResponse represents a raw port update response with // extra DHCP options. const UpdateWithExtraDHCPOptsResponse = ` { "port": { "status": "DOWN", "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", "extra_dhcp_opts": [ { "opt_name": "option2", "opt_value": "value2", "ip_version": 4 } ], "admin_state_up": true, "name": "updated-port-with-dhcp-opts", "device_owner": "", "mac_address": "fa:16:3e:c9:cb:f0", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.3" } ], "id": "65c0ee9f-d634-4522-8954-51021b570b0d", "device_id": "" } } ` requests_test.go000066400000000000000000000621461335005366400363000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/ports/testingpackage testing import ( "fmt" "net/http" "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/extradhcpopts" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsecurity" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListResponse) }) count := 0 ports.List(fake.ServiceClient(), ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := ports.ExtractPorts(page) if err != nil { t.Errorf("Failed to extract subnets: %v", err) return false, nil } expected := []ports.Port{ { Status: "ACTIVE", Name: "", AdminStateUp: true, NetworkID: "70c1db1f-b701-45bd-96e0-a313ee3430b3", TenantID: "", DeviceOwner: "network:router_gateway", MACAddress: "fa:16:3e:58:42:ed", FixedIPs: []ports.IP{ { SubnetID: "008ba151-0b8c-4a67-98b5-0d2b87666062", IPAddress: "172.24.4.2", }, }, ID: "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b", SecurityGroups: []string{}, DeviceID: "9ae135f4-b6e0-4dad-9e91-3c223e385824", }, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestListWithExtensions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ListResponse) }) type portWithExt struct { ports.Port portsecurity.PortSecurityExt } var allPorts []portWithExt allPages, err := ports.List(fake.ServiceClient(), ports.ListOpts{}).AllPages() th.AssertNoErr(t, err) err = ports.ExtractPortsInto(allPages, &allPorts) th.AssertEquals(t, allPorts[0].Status, "ACTIVE") th.AssertEquals(t, allPorts[0].PortSecurityEnabled, false) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/ports/46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetResponse) }) n, err := ports.Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "ACTIVE") th.AssertEquals(t, n.Name, "") th.AssertEquals(t, n.AdminStateUp, true) th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") th.AssertEquals(t, n.TenantID, "7e02058126cc4950b75f9970368ba177") th.AssertEquals(t, n.DeviceOwner, "network:router_interface") th.AssertEquals(t, n.MACAddress, "fa:16:3e:23:fd:d7") th.AssertDeepEquals(t, n.FixedIPs, []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.1"}, }) th.AssertEquals(t, n.ID, "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2") th.AssertDeepEquals(t, n.SecurityGroups, []string{}) th.AssertEquals(t, n.Status, "ACTIVE") th.AssertEquals(t, n.DeviceID, "5e3898d7-11be-483e-9732-b2f5eccd2b2e") } func TestGetWithExtensions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/ports/46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetResponse) }) var portWithExtensions struct { ports.Port portsecurity.PortSecurityExt } err := ports.Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").ExtractInto(&portWithExtensions) th.AssertNoErr(t, err) th.AssertEquals(t, portWithExtensions.Status, "ACTIVE") th.AssertEquals(t, portWithExtensions.PortSecurityEnabled, false) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, CreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, CreateResponse) }) asu := true options := ports.CreateOpts{ Name: "private-port", AdminStateUp: &asu, NetworkID: "a87cc70a-3e15-4acf-8205-9b711a3531b7", FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, }, SecurityGroups: &[]string{"foo"}, AllowedAddressPairs: []ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, } n, err := ports.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "DOWN") th.AssertEquals(t, n.Name, "private-port") th.AssertEquals(t, n.AdminStateUp, true) th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") th.AssertEquals(t, n.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") th.AssertEquals(t, n.DeviceOwner, "") th.AssertEquals(t, n.MACAddress, "fa:16:3e:c9:cb:f0") th.AssertDeepEquals(t, n.FixedIPs, []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, }) th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertDeepEquals(t, n.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}) th.AssertDeepEquals(t, n.AllowedAddressPairs, []ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }) } func TestCreateOmitSecurityGroups(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, CreateOmitSecurityGroupsRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, CreateOmitSecurityGroupsResponse) }) asu := true options := ports.CreateOpts{ Name: "private-port", AdminStateUp: &asu, NetworkID: "a87cc70a-3e15-4acf-8205-9b711a3531b7", FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, }, AllowedAddressPairs: []ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, } n, err := ports.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "DOWN") th.AssertEquals(t, n.Name, "private-port") th.AssertEquals(t, n.AdminStateUp, true) th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") th.AssertEquals(t, n.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") th.AssertEquals(t, n.DeviceOwner, "") th.AssertEquals(t, n.MACAddress, "fa:16:3e:c9:cb:f0") th.AssertDeepEquals(t, n.FixedIPs, []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, }) th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertDeepEquals(t, n.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}) th.AssertDeepEquals(t, n.AllowedAddressPairs, []ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }) } func TestCreateWithNoSecurityGroup(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, CreateWithNoSecurityGroupsRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, CreateWithNoSecurityGroupsResponse) }) asu := true options := ports.CreateOpts{ Name: "private-port", AdminStateUp: &asu, NetworkID: "a87cc70a-3e15-4acf-8205-9b711a3531b7", FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, }, SecurityGroups: &[]string{}, AllowedAddressPairs: []ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, } n, err := ports.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "DOWN") th.AssertEquals(t, n.Name, "private-port") th.AssertEquals(t, n.AdminStateUp, true) th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") th.AssertEquals(t, n.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") th.AssertEquals(t, n.DeviceOwner, "") th.AssertEquals(t, n.MACAddress, "fa:16:3e:c9:cb:f0") th.AssertDeepEquals(t, n.FixedIPs, []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, }) th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertDeepEquals(t, n.AllowedAddressPairs, []ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }) } func TestRequiredCreateOpts(t *testing.T) { res := ports.Create(fake.ServiceClient(), ports.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } } func TestCreatePortSecurity(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, CreatePortSecurityRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, CreatePortSecurityResponse) }) var portWithExt struct { ports.Port portsecurity.PortSecurityExt } asu := true iFalse := false portCreateOpts := ports.CreateOpts{ Name: "private-port", AdminStateUp: &asu, NetworkID: "a87cc70a-3e15-4acf-8205-9b711a3531b7", FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, }, SecurityGroups: &[]string{"foo"}, AllowedAddressPairs: []ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, } createOpts := portsecurity.PortCreateOptsExt{ CreateOptsBuilder: portCreateOpts, PortSecurityEnabled: &iFalse, } err := ports.Create(fake.ServiceClient(), createOpts).ExtractInto(&portWithExt) th.AssertNoErr(t, err) th.AssertEquals(t, portWithExt.Status, "DOWN") th.AssertEquals(t, portWithExt.PortSecurityEnabled, false) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, UpdateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, UpdateResponse) }) options := ports.UpdateOpts{ Name: "new_port_name", FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, SecurityGroups: &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}, AllowedAddressPairs: &[]ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, } s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "new_port_name") th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }) th.AssertDeepEquals(t, s.AllowedAddressPairs, []ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }) th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}) } func TestUpdateOmitSecurityGroups(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, UpdateOmitSecurityGroupsRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, UpdateOmitSecurityGroupsResponse) }) options := ports.UpdateOpts{ Name: "new_port_name", FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, AllowedAddressPairs: &[]ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, } s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "new_port_name") th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }) th.AssertDeepEquals(t, s.AllowedAddressPairs, []ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }) th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}) } func TestUpdatePortSecurity(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, UpdatePortSecurityRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, UpdatePortSecurityResponse) }) var portWithExt struct { ports.Port portsecurity.PortSecurityExt } iFalse := false portUpdateOpts := ports.UpdateOpts{} updateOpts := portsecurity.PortUpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, PortSecurityEnabled: &iFalse, } err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&portWithExt) th.AssertNoErr(t, err) th.AssertEquals(t, portWithExt.Status, "DOWN") th.AssertEquals(t, portWithExt.Name, "private-port") th.AssertEquals(t, portWithExt.PortSecurityEnabled, false) } func TestRemoveSecurityGroups(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, RemoveSecurityGroupRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, RemoveSecurityGroupResponse) }) options := ports.UpdateOpts{ Name: "new_port_name", FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, SecurityGroups: &[]string{}, AllowedAddressPairs: &[]ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, } s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "new_port_name") th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }) th.AssertDeepEquals(t, s.AllowedAddressPairs, []ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }) th.AssertDeepEquals(t, s.SecurityGroups, []string(nil)) } func TestRemoveAllowedAddressPairs(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, RemoveAllowedAddressPairsRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, RemoveAllowedAddressPairsResponse) }) options := ports.UpdateOpts{ Name: "new_port_name", FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, SecurityGroups: &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}, AllowedAddressPairs: &[]ports.AddressPair{}, } s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "new_port_name") th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }) th.AssertDeepEquals(t, s.AllowedAddressPairs, []ports.AddressPair(nil)) th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}) } func TestDontUpdateAllowedAddressPairs(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, DontUpdateAllowedAddressPairsRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, DontUpdateAllowedAddressPairsResponse) }) options := ports.UpdateOpts{ Name: "new_port_name", FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, SecurityGroups: &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}, } s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "new_port_name") th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }) th.AssertDeepEquals(t, s.AllowedAddressPairs, []ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }) th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := ports.Delete(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertNoErr(t, res.Err) } func TestGetWithExtraDHCPOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/ports/46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, GetWithExtraDHCPOptsResponse) }) var s struct { ports.Port extradhcpopts.ExtraDHCPOptsExt } err := ports.Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, s.Status, "ACTIVE") th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") th.AssertEquals(t, s.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") th.AssertEquals(t, s.AdminStateUp, true) th.AssertEquals(t, s.Name, "port-with-extra-dhcp-opts") th.AssertEquals(t, s.DeviceOwner, "") th.AssertEquals(t, s.MACAddress, "fa:16:3e:c9:cb:f0") th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.4"}, }) th.AssertEquals(t, s.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertEquals(t, s.DeviceID, "") th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].OptName, "option1") th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].OptValue, "value1") th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].IPVersion, 4) th.AssertDeepEquals(t, s.ExtraDHCPOpts[1].OptName, "option2") th.AssertDeepEquals(t, s.ExtraDHCPOpts[1].OptValue, "value2") th.AssertDeepEquals(t, s.ExtraDHCPOpts[1].IPVersion, 4) } func TestCreateWithExtraDHCPOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, CreateWithExtraDHCPOptsRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, CreateWithExtraDHCPOptsResponse) }) adminStateUp := true portCreateOpts := ports.CreateOpts{ Name: "port-with-extra-dhcp-opts", AdminStateUp: &adminStateUp, NetworkID: "a87cc70a-3e15-4acf-8205-9b711a3531b7", FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, }, } createOpts := extradhcpopts.CreateOptsExt{ CreateOptsBuilder: portCreateOpts, ExtraDHCPOpts: []extradhcpopts.CreateExtraDHCPOpt{ { OptName: "option1", OptValue: "value1", }, }, } var s struct { ports.Port extradhcpopts.ExtraDHCPOptsExt } err := ports.Create(fake.ServiceClient(), createOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, s.Status, "DOWN") th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") th.AssertEquals(t, s.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") th.AssertEquals(t, s.AdminStateUp, true) th.AssertEquals(t, s.Name, "port-with-extra-dhcp-opts") th.AssertEquals(t, s.DeviceOwner, "") th.AssertEquals(t, s.MACAddress, "fa:16:3e:c9:cb:f0") th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, }) th.AssertEquals(t, s.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertEquals(t, s.DeviceID, "") th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].OptName, "option1") th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].OptValue, "value1") th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].IPVersion, 4) } func TestUpdateWithExtraDHCPOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, UpdateWithExtraDHCPOptsRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, UpdateWithExtraDHCPOptsResponse) }) portUpdateOpts := ports.UpdateOpts{ Name: "updated-port-with-dhcp-opts", FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, } edoValue2 := "value2" updateOpts := extradhcpopts.UpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, ExtraDHCPOpts: []extradhcpopts.UpdateExtraDHCPOpt{ { OptName: "option1", }, { OptName: "option2", OptValue: &edoValue2, }, }, } var s struct { ports.Port extradhcpopts.ExtraDHCPOptsExt } err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, s.Status, "DOWN") th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") th.AssertEquals(t, s.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") th.AssertEquals(t, s.AdminStateUp, true) th.AssertEquals(t, s.Name, "updated-port-with-dhcp-opts") th.AssertEquals(t, s.DeviceOwner, "") th.AssertEquals(t, s.MACAddress, "fa:16:3e:c9:cb:f0") th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }) th.AssertEquals(t, s.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertEquals(t, s.DeviceID, "") th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].OptName, "option2") th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].OptValue, "value2") th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].IPVersion, 4) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/ports/urls.go000066400000000000000000000012501335005366400327420ustar00rootroot00000000000000package ports import "github.com/gophercloud/gophercloud" func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("ports", id) } func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("ports") } func listURL(c *gophercloud.ServiceClient) string { return rootURL(c) } func getURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } func createURL(c *gophercloud.ServiceClient) string { return rootURL(c) } func updateURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } func deleteURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/subnets/000077500000000000000000000000001335005366400317445ustar00rootroot00000000000000doc.go000066400000000000000000000060001335005366400327550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/subnets/* Package subnets contains functionality for working with Neutron subnet resources. A subnet represents an IP address block that can be used to assign IP addresses to virtual instances. Each subnet must have a CIDR and must be associated with a network. IPs can either be selected from the whole subnet CIDR or from allocation pools specified by the user. A subnet can also have a gateway, a list of DNS name servers, and host routes. This information is pushed to instances whose interfaces are associated with the subnet. Example to List Subnets listOpts := subnets.ListOpts{ IPVersion: 4, } allPages, err := subnets.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allSubnets, err := subnets.ExtractSubnets(allPages) if err != nil { panic(err) } for _, subnet := range allSubnets { fmt.Printf("%+v\n", subnet) } Example to Create a Subnet With Specified Gateway var gatewayIP = "192.168.199.1" createOpts := subnets.CreateOpts{ NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", IPVersion: 4, CIDR: "192.168.199.0/24", GatewayIP: &gatewayIP, AllocationPools: []subnets.AllocationPool{ { Start: "192.168.199.2", End: "192.168.199.254", }, }, DNSNameservers: []string{"foo"}, } subnet, err := subnets.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Create a Subnet With No Gateway var noGateway = "" createOpts := subnets.CreateOpts{ NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a23", IPVersion: 4, CIDR: "192.168.1.0/24", GatewayIP: &noGateway, AllocationPools: []subnets.AllocationPool{ { Start: "192.168.1.2", End: "192.168.1.254", }, }, DNSNameservers: []string{}, } subnet, err := subnets.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Create a Subnet With a Default Gateway createOpts := subnets.CreateOpts{ NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a23", IPVersion: 4, CIDR: "192.168.1.0/24", AllocationPools: []subnets.AllocationPool{ { Start: "192.168.1.2", End: "192.168.1.254", }, }, DNSNameservers: []string{}, } subnet, err := subnets.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Subnet subnetID := "db77d064-e34f-4d06-b060-f21e28a61c23" updateOpts := subnets.UpdateOpts{ Name: "new_name", DNSNameservers: []string{"8.8.8.8}, } subnet, err := subnets.Update(networkClient, subnetID, updateOpts).Extract() if err != nil { panic(err) } Example to Remove a Gateway From a Subnet var noGateway = "" subnetID := "db77d064-e34f-4d06-b060-f21e28a61c23" updateOpts := subnets.UpdateOpts{ GatewayIP: &noGateway, } subnet, err := subnets.Update(networkClient, subnetID, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Subnet subnetID := "db77d064-e34f-4d06-b060-f21e28a61c23" err := subnets.Delete(networkClient, subnetID).ExtractErr() if err != nil { panic(err) } */ package subnets requests.go000066400000000000000000000203321335005366400340670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/subnetspackage subnets import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToSubnetListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the subnet attributes you want to see returned. SortKey allows you to sort // by a particular subnet attribute. SortDir sets the direction, and is either // `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { Name string `q:"name"` EnableDHCP *bool `q:"enable_dhcp"` NetworkID string `q:"network_id"` TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` IPVersion int `q:"ip_version"` GatewayIP string `q:"gateway_ip"` CIDR string `q:"cidr"` IPv6AddressMode string `q:"ipv6_address_mode"` IPv6RAMode string `q:"ipv6_ra_mode"` ID string `q:"id"` SubnetPoolID string `q:"subnetpool_id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` SortDir string `q:"sort_dir"` } // ToSubnetListQuery formats a ListOpts into a query string. func (opts ListOpts) ToSubnetListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns a Pager which allows you to iterate over a collection of // subnets. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. // // Default policy settings return only those subnets that are owned by the tenant // who submits the request, unless the request is submitted by a user with // administrative rights. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(c) if opts != nil { query, err := opts.ToSubnetListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return SubnetPage{pagination.LinkedPageBase{PageResult: r}} }) } // Get retrieves a specific subnet based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(getURL(c, id), &r.Body, nil) return } // CreateOptsBuilder allows extensions to add additional parameters to the // List request. type CreateOptsBuilder interface { ToSubnetCreateMap() (map[string]interface{}, error) } // CreateOpts represents the attributes used when creating a new subnet. type CreateOpts struct { // NetworkID is the UUID of the network the subnet will be associated with. NetworkID string `json:"network_id" required:"true"` // CIDR is the address CIDR of the subnet. CIDR string `json:"cidr,omitempty"` // Name is a human-readable name of the subnet. Name string `json:"name,omitempty"` // The UUID of the project who owns the Subnet. Only administrative users // can specify a project UUID other than their own. TenantID string `json:"tenant_id,omitempty"` // The UUID of the project who owns the Subnet. Only administrative users // can specify a project UUID other than their own. ProjectID string `json:"project_id,omitempty"` // AllocationPools are IP Address pools that will be available for DHCP. AllocationPools []AllocationPool `json:"allocation_pools,omitempty"` // GatewayIP sets gateway information for the subnet. Setting to nil will // cause a default gateway to automatically be created. Setting to an empty // string will cause the subnet to be created with no gateway. Setting to // an explicit address will set that address as the gateway. GatewayIP *string `json:"gateway_ip,omitempty"` // IPVersion is the IP version for the subnet. IPVersion gophercloud.IPVersion `json:"ip_version,omitempty"` // EnableDHCP will either enable to disable the DHCP service. EnableDHCP *bool `json:"enable_dhcp,omitempty"` // DNSNameservers are the nameservers to be set via DHCP. DNSNameservers []string `json:"dns_nameservers,omitempty"` // HostRoutes are any static host routes to be set via DHCP. HostRoutes []HostRoute `json:"host_routes,omitempty"` // The IPv6 address modes specifies mechanisms for assigning IPv6 IP addresses. IPv6AddressMode string `json:"ipv6_address_mode,omitempty"` // The IPv6 router advertisement specifies whether the networking service // should transmit ICMPv6 packets. IPv6RAMode string `json:"ipv6_ra_mode,omitempty"` // SubnetPoolID is the id of the subnet pool that subnet should be associated to. SubnetPoolID string `json:"subnetpool_id,omitempty"` } // ToSubnetCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToSubnetCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "subnet") if err != nil { return nil, err } if m := b["subnet"].(map[string]interface{}); m["gateway_ip"] == "" { m["gateway_ip"] = nil } return b, nil } // Create accepts a CreateOpts struct and creates a new subnet using the values // provided. You must remember to provide a valid NetworkID, CIDR and IP // version. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSubnetCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(createURL(c), b, &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToSubnetUpdateMap() (map[string]interface{}, error) } // UpdateOpts represents the attributes used when updating an existing subnet. type UpdateOpts struct { // Name is a human-readable name of the subnet. Name string `json:"name,omitempty"` // AllocationPools are IP Address pools that will be available for DHCP. AllocationPools []AllocationPool `json:"allocation_pools,omitempty"` // GatewayIP sets gateway information for the subnet. Setting to nil will // cause a default gateway to automatically be created. Setting to an empty // string will cause the subnet to be created with no gateway. Setting to // an explicit address will set that address as the gateway. GatewayIP *string `json:"gateway_ip,omitempty"` // DNSNameservers are the nameservers to be set via DHCP. DNSNameservers []string `json:"dns_nameservers,omitempty"` // HostRoutes are any static host routes to be set via DHCP. HostRoutes *[]HostRoute `json:"host_routes,omitempty"` // EnableDHCP will either enable to disable the DHCP service. EnableDHCP *bool `json:"enable_dhcp,omitempty"` } // ToSubnetUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToSubnetUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "subnet") if err != nil { return nil, err } if m := b["subnet"].(map[string]interface{}); m["gateway_ip"] == "" { m["gateway_ip"] = nil } return b, nil } // Update accepts a UpdateOpts struct and updates an existing subnet using the // values provided. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToSubnetUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) return } // Delete accepts a unique ID and deletes the subnet associated with it. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(deleteURL(c, id), nil) return } // IDFromName is a convenience function that returns a subnet's ID, // given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" listOpts := ListOpts{ Name: name, } pages, err := List(client, listOpts).AllPages() if err != nil { return "", err } all, err := ExtractSubnets(pages) if err != nil { return "", err } for _, s := range all { if s.Name == name { count++ id = s.ID } } switch count { case 0: return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "subnet"} case 1: return id, nil default: return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "subnet"} } } results.go000066400000000000000000000104211335005366400337130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/subnetspackage subnets import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts a subnet resource. func (r commonResult) Extract() (*Subnet, error) { var s struct { Subnet *Subnet `json:"subnet"` } err := r.ExtractInto(&s) return s.Subnet, err } // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a Subnet. type CreateResult struct { commonResult } // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a Subnet. type GetResult struct { commonResult } // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a Subnet. type UpdateResult struct { commonResult } // DeleteResult represents the result of a delete operation. Call its // ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } // AllocationPool represents a sub-range of cidr available for dynamic // allocation to ports, e.g. {Start: "10.0.0.2", End: "10.0.0.254"} type AllocationPool struct { Start string `json:"start"` End string `json:"end"` } // HostRoute represents a route that should be used by devices with IPs from // a subnet (not including local subnet route). type HostRoute struct { DestinationCIDR string `json:"destination"` NextHop string `json:"nexthop"` } // Subnet represents a subnet. See package documentation for a top-level // description of what this is. type Subnet struct { // UUID representing the subnet. ID string `json:"id"` // UUID of the parent network. NetworkID string `json:"network_id"` // Human-readable name for the subnet. Might not be unique. Name string `json:"name"` // IP version, either `4' or `6'. IPVersion int `json:"ip_version"` // CIDR representing IP range for this subnet, based on IP version. CIDR string `json:"cidr"` // Default gateway used by devices in this subnet. GatewayIP string `json:"gateway_ip"` // DNS name servers used by hosts in this subnet. DNSNameservers []string `json:"dns_nameservers"` // Sub-ranges of CIDR available for dynamic allocation to ports. // See AllocationPool. AllocationPools []AllocationPool `json:"allocation_pools"` // Routes that should be used by devices with IPs from this subnet // (not including local subnet route). HostRoutes []HostRoute `json:"host_routes"` // Specifies whether DHCP is enabled for this subnet or not. EnableDHCP bool `json:"enable_dhcp"` // TenantID is the project owner of the subnet. TenantID string `json:"tenant_id"` // ProjectID is the project owner of the subnet. ProjectID string `json:"project_id"` // The IPv6 address modes specifies mechanisms for assigning IPv6 IP addresses. IPv6AddressMode string `json:"ipv6_address_mode"` // The IPv6 router advertisement specifies whether the networking service // should transmit ICMPv6 packets. IPv6RAMode string `json:"ipv6_ra_mode"` // SubnetPoolID is the id of the subnet pool associated with the subnet. SubnetPoolID string `json:"subnetpool_id"` } // SubnetPage is the page returned by a pager when traversing over a collection // of subnets. type SubnetPage struct { pagination.LinkedPageBase } // NextPageURL is invoked when a paginated collection of subnets has reached // the end of a page and the pager seeks to traverse over a new one. In order // to do this, it needs to construct the next page's URL. func (r SubnetPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"subnets_links"` } err := r.ExtractInto(&s) if err != nil { return "", err } return gophercloud.ExtractNextURL(s.Links) } // IsEmpty checks whether a SubnetPage struct is empty. func (r SubnetPage) IsEmpty() (bool, error) { is, err := ExtractSubnets(r) return len(is) == 0, err } // ExtractSubnets accepts a Page struct, specifically a SubnetPage struct, // and extracts the elements into a slice of Subnet structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractSubnets(r pagination.Page) ([]Subnet, error) { var s struct { Subnets []Subnet `json:"subnets"` } err := (r.(SubnetPage)).ExtractInto(&s) return s.Subnets, err } testing/000077500000000000000000000000001335005366400333425ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/subnetsdoc.go000066400000000000000000000000461335005366400344360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/subnets/testing// subnets unit tests package testing fixtures.go000066400000000000000000000337131335005366400355510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/subnets/testingpackage testing import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" ) const SubnetListResult = ` { "subnets": [ { "name": "private-subnet", "enable_dhcp": true, "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", "dns_nameservers": [], "allocation_pools": [ { "start": "10.0.0.2", "end": "10.0.0.254" } ], "host_routes": [], "ip_version": 4, "gateway_ip": "10.0.0.1", "cidr": "10.0.0.0/24", "id": "08eae331-0402-425a-923c-34f7cfe39c1b" }, { "name": "my_subnet", "enable_dhcp": true, "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "tenant_id": "4fd44f30292945e481c7b8a0c8908869", "dns_nameservers": [], "allocation_pools": [ { "start": "192.0.0.2", "end": "192.255.255.254" } ], "host_routes": [], "ip_version": 4, "gateway_ip": "192.0.0.1", "cidr": "192.0.0.0/8", "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" }, { "name": "my_gatewayless_subnet", "enable_dhcp": true, "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23", "tenant_id": "4fd44f30292945e481c7b8a0c8908869", "dns_nameservers": [], "allocation_pools": [ { "start": "192.168.1.2", "end": "192.168.1.254" } ], "host_routes": [], "ip_version": 4, "gateway_ip": null, "cidr": "192.168.1.0/24", "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0c" }, { "name": "my_subnet_with_subnetpool", "enable_dhcp": false, "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23", "tenant_id": "4fd44f30292945e481c7b8a0c8908869", "dns_nameservers": [], "allocation_pools": [ { "start": "10.11.12.2", "end": "10.11.12.254" } ], "host_routes": [], "ip_version": 4, "gateway_ip": null, "cidr": "10.11.12.0/24", "id": "38186a51-f373-4bbc-838b-6eaa1aa13eac", "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b" } ] } ` var Subnet1 = subnets.Subnet{ Name: "private-subnet", EnableDHCP: true, NetworkID: "db193ab3-96e3-4cb3-8fc5-05f4296d0324", TenantID: "26a7980765d0414dbc1fc1f88cdb7e6e", DNSNameservers: []string{}, AllocationPools: []subnets.AllocationPool{ { Start: "10.0.0.2", End: "10.0.0.254", }, }, HostRoutes: []subnets.HostRoute{}, IPVersion: 4, GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24", ID: "08eae331-0402-425a-923c-34f7cfe39c1b", } var Subnet2 = subnets.Subnet{ Name: "my_subnet", EnableDHCP: true, NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", TenantID: "4fd44f30292945e481c7b8a0c8908869", DNSNameservers: []string{}, AllocationPools: []subnets.AllocationPool{ { Start: "192.0.0.2", End: "192.255.255.254", }, }, HostRoutes: []subnets.HostRoute{}, IPVersion: 4, GatewayIP: "192.0.0.1", CIDR: "192.0.0.0/8", ID: "54d6f61d-db07-451c-9ab3-b9609b6b6f0b", } var Subnet3 = subnets.Subnet{ Name: "my_gatewayless_subnet", EnableDHCP: true, NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a23", TenantID: "4fd44f30292945e481c7b8a0c8908869", DNSNameservers: []string{}, AllocationPools: []subnets.AllocationPool{ { Start: "192.168.1.2", End: "192.168.1.254", }, }, HostRoutes: []subnets.HostRoute{}, IPVersion: 4, GatewayIP: "", CIDR: "192.168.1.0/24", ID: "54d6f61d-db07-451c-9ab3-b9609b6b6f0c", } var Subnet4 = subnets.Subnet{ Name: "my_subnet_with_subnetpool", EnableDHCP: false, NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a23", TenantID: "4fd44f30292945e481c7b8a0c8908869", DNSNameservers: []string{}, AllocationPools: []subnets.AllocationPool{ { Start: "10.11.12.2", End: "10.11.12.254", }, }, HostRoutes: []subnets.HostRoute{}, IPVersion: 4, GatewayIP: "", CIDR: "10.11.12.0/24", ID: "38186a51-f373-4bbc-838b-6eaa1aa13eac", SubnetPoolID: "b80340c7-9960-4f67-a99c-02501656284b", } const SubnetGetResult = ` { "subnet": { "name": "my_subnet", "enable_dhcp": true, "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "tenant_id": "4fd44f30292945e481c7b8a0c8908869", "dns_nameservers": [], "allocation_pools": [ { "start": "192.0.0.2", "end": "192.255.255.254" } ], "host_routes": [], "ip_version": 4, "gateway_ip": "192.0.0.1", "cidr": "192.0.0.0/8", "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b", "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b" } } ` const SubnetCreateRequest = ` { "subnet": { "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "ip_version": 4, "gateway_ip": "192.168.199.1", "cidr": "192.168.199.0/24", "dns_nameservers": ["foo"], "allocation_pools": [ { "start": "192.168.199.2", "end": "192.168.199.254" } ], "host_routes": [{"destination":"","nexthop": "bar"}], "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b" } } ` const SubnetCreateResult = ` { "subnet": { "name": "", "enable_dhcp": true, "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "tenant_id": "4fd44f30292945e481c7b8a0c8908869", "dns_nameservers": [], "allocation_pools": [ { "start": "192.168.199.2", "end": "192.168.199.254" } ], "host_routes": [], "ip_version": 4, "gateway_ip": "192.168.199.1", "cidr": "192.168.199.0/24", "id": "3b80198d-4f7b-4f77-9ef5-774d54e17126", "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b" } } ` const SubnetCreateWithNoGatewayRequest = ` { "subnet": { "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23", "ip_version": 4, "cidr": "192.168.1.0/24", "gateway_ip": null, "allocation_pools": [ { "start": "192.168.1.2", "end": "192.168.1.254" } ] } } ` const SubnetCreateWithNoGatewayResponse = ` { "subnet": { "name": "", "enable_dhcp": true, "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23", "tenant_id": "4fd44f30292945e481c7b8a0c8908869", "allocation_pools": [ { "start": "192.168.1.2", "end": "192.168.1.254" } ], "host_routes": [], "ip_version": 4, "gateway_ip": null, "cidr": "192.168.1.0/24", "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0c" } } ` const SubnetCreateWithDefaultGatewayRequest = ` { "subnet": { "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23", "ip_version": 4, "cidr": "192.168.1.0/24", "allocation_pools": [ { "start": "192.168.1.2", "end": "192.168.1.254" } ] } } ` const SubnetCreateWithDefaultGatewayResponse = ` { "subnet": { "name": "", "enable_dhcp": true, "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23", "tenant_id": "4fd44f30292945e481c7b8a0c8908869", "allocation_pools": [ { "start": "192.168.1.2", "end": "192.168.1.254" } ], "host_routes": [], "ip_version": 4, "gateway_ip": "192.168.1.1", "cidr": "192.168.1.0/24", "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0c" } } ` const SubnetCreateWithIPv6RaAddressModeRequest = ` { "subnet": { "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "ip_version": 6, "gateway_ip": "2001:db8:0:a::1", "cidr": "2001:db8:0:a:0:0:0:0/64", "ipv6_address_mode": "slaac", "ipv6_ra_mode": "slaac" } } ` const SubnetCreateWithIPv6RaAddressModeResponse = ` { "subnet": { "name": "", "enable_dhcp": true, "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "tenant_id": "4fd44f30292945e481c7b8a0c8908869", "dns_nameservers": [], "host_routes": [], "ip_version": 6, "gateway_ip": "2001:db8:0:a::1", "cidr": "2001:db8:0:a:0:0:0:0/64", "id": "3b80198d-4f7b-4f77-9ef5-774d54e17126", "ipv6_address_mode": "slaac", "ipv6_ra_mode": "slaac" } } ` const SubnetCreateRequestWithNoCIDR = ` { "subnet": { "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "ip_version": 4, "dns_nameservers": ["foo"], "host_routes": [{"destination":"","nexthop": "bar"}], "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b" } } ` const SubnetUpdateRequest = ` { "subnet": { "name": "my_new_subnet", "dns_nameservers": ["foo"], "host_routes": [{"destination":"","nexthop": "bar"}] } } ` const SubnetUpdateResponse = ` { "subnet": { "name": "my_new_subnet", "enable_dhcp": true, "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", "dns_nameservers": [], "allocation_pools": [ { "start": "10.0.0.2", "end": "10.0.0.254" } ], "host_routes": [], "ip_version": 4, "gateway_ip": "10.0.0.1", "cidr": "10.0.0.0/24", "id": "08eae331-0402-425a-923c-34f7cfe39c1b" } } ` const SubnetUpdateGatewayRequest = ` { "subnet": { "name": "my_new_subnet", "gateway_ip": "10.0.0.1" } } ` const SubnetUpdateGatewayResponse = ` { "subnet": { "name": "my_new_subnet", "enable_dhcp": true, "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", "dns_nameservers": [], "allocation_pools": [ { "start": "10.0.0.2", "end": "10.0.0.254" } ], "host_routes": [], "ip_version": 4, "gateway_ip": "10.0.0.1", "cidr": "10.0.0.0/24", "id": "08eae331-0402-425a-923c-34f7cfe39c1b" } } ` const SubnetUpdateRemoveGatewayRequest = ` { "subnet": { "name": "my_new_subnet", "gateway_ip": null } } ` const SubnetUpdateRemoveGatewayResponse = ` { "subnet": { "name": "my_new_subnet", "enable_dhcp": true, "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", "dns_nameservers": [], "allocation_pools": [ { "start": "10.0.0.2", "end": "10.0.0.254" } ], "host_routes": [], "ip_version": 4, "gateway_ip": null, "cidr": "10.0.0.0/24", "id": "08eae331-0402-425a-923c-34f7cfe39c1b" } } ` const SubnetUpdateHostRoutesRequest = ` { "subnet": { "name": "my_new_subnet", "host_routes": [ { "destination": "192.168.1.1/24", "nexthop": "bar" } ] } } ` const SubnetUpdateHostRoutesResponse = ` { "subnet": { "name": "my_new_subnet", "enable_dhcp": true, "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", "dns_nameservers": [], "allocation_pools": [ { "start": "10.0.0.2", "end": "10.0.0.254" } ], "ip_version": 4, "gateway_ip": "10.0.0.1", "host_routes": [ { "destination": "192.168.1.1/24", "nexthop": "bar" } ], "cidr": "10.0.0.0/24", "id": "08eae331-0402-425a-923c-34f7cfe39c1b" } } ` const SubnetUpdateRemoveHostRoutesRequest = ` { "subnet": { "host_routes": [] } } ` const SubnetUpdateRemoveHostRoutesResponse = ` { "subnet": { "name": "my_new_subnet", "enable_dhcp": true, "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", "dns_nameservers": [], "allocation_pools": [ { "start": "10.0.0.2", "end": "10.0.0.254" } ], "host_routes": [], "ip_version": 4, "gateway_ip": null, "cidr": "10.0.0.0/24", "id": "08eae331-0402-425a-923c-34f7cfe39c1b" } } ` const SubnetUpdateAllocationPoolRequest = ` { "subnet": { "name": "my_new_subnet", "allocation_pools": [ { "start": "10.1.0.2", "end": "10.1.0.254" } ] } } ` const SubnetUpdateAllocationPoolResponse = ` { "subnet": { "name": "my_new_subnet", "enable_dhcp": true, "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", "dns_nameservers": [], "allocation_pools": [ { "start": "10.1.0.2", "end": "10.1.0.254" } ], "host_routes": [], "ip_version": 4, "gateway_ip": "10.0.0.1", "cidr": "10.0.0.0/24", "id": "08eae331-0402-425a-923c-34f7cfe39c1b" } } ` requests_test.go000066400000000000000000000423531335005366400366120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/subnets/testingpackage testing import ( "fmt" "net/http" "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/subnets", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, SubnetListResult) }) count := 0 subnets.List(fake.ServiceClient(), subnets.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := subnets.ExtractSubnets(page) if err != nil { t.Errorf("Failed to extract subnets: %v", err) return false, nil } expected := []subnets.Subnet{ Subnet1, Subnet2, Subnet3, Subnet4, } th.CheckDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/subnets/54d6f61d-db07-451c-9ab3-b9609b6b6f0b", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, SubnetGetResult) }) s, err := subnets.Get(fake.ServiceClient(), "54d6f61d-db07-451c-9ab3-b9609b6b6f0b").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "my_subnet") th.AssertEquals(t, s.EnableDHCP, true) th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869") th.AssertDeepEquals(t, s.DNSNameservers, []string{}) th.AssertDeepEquals(t, s.AllocationPools, []subnets.AllocationPool{ { Start: "192.0.0.2", End: "192.255.255.254", }, }) th.AssertDeepEquals(t, s.HostRoutes, []subnets.HostRoute{}) th.AssertEquals(t, s.IPVersion, 4) th.AssertEquals(t, s.GatewayIP, "192.0.0.1") th.AssertEquals(t, s.CIDR, "192.0.0.0/8") th.AssertEquals(t, s.ID, "54d6f61d-db07-451c-9ab3-b9609b6b6f0b") th.AssertEquals(t, s.SubnetPoolID, "b80340c7-9960-4f67-a99c-02501656284b") } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/subnets", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, SubnetCreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, SubnetCreateResult) }) var gatewayIP = "192.168.199.1" opts := subnets.CreateOpts{ NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", IPVersion: 4, CIDR: "192.168.199.0/24", GatewayIP: &gatewayIP, AllocationPools: []subnets.AllocationPool{ { Start: "192.168.199.2", End: "192.168.199.254", }, }, DNSNameservers: []string{"foo"}, HostRoutes: []subnets.HostRoute{ {NextHop: "bar"}, }, SubnetPoolID: "b80340c7-9960-4f67-a99c-02501656284b", } s, err := subnets.Create(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "") th.AssertEquals(t, s.EnableDHCP, true) th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869") th.AssertDeepEquals(t, s.DNSNameservers, []string{}) th.AssertDeepEquals(t, s.AllocationPools, []subnets.AllocationPool{ { Start: "192.168.199.2", End: "192.168.199.254", }, }) th.AssertDeepEquals(t, s.HostRoutes, []subnets.HostRoute{}) th.AssertEquals(t, s.IPVersion, 4) th.AssertEquals(t, s.GatewayIP, "192.168.199.1") th.AssertEquals(t, s.CIDR, "192.168.199.0/24") th.AssertEquals(t, s.ID, "3b80198d-4f7b-4f77-9ef5-774d54e17126") th.AssertEquals(t, s.SubnetPoolID, "b80340c7-9960-4f67-a99c-02501656284b") } func TestCreateNoGateway(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/subnets", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, SubnetCreateWithNoGatewayRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, SubnetCreateWithNoGatewayResponse) }) var noGateway = "" opts := subnets.CreateOpts{ NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a23", IPVersion: 4, CIDR: "192.168.1.0/24", GatewayIP: &noGateway, AllocationPools: []subnets.AllocationPool{ { Start: "192.168.1.2", End: "192.168.1.254", }, }, DNSNameservers: []string{}, } s, err := subnets.Create(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "") th.AssertEquals(t, s.EnableDHCP, true) th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a23") th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869") th.AssertDeepEquals(t, s.AllocationPools, []subnets.AllocationPool{ { Start: "192.168.1.2", End: "192.168.1.254", }, }) th.AssertDeepEquals(t, s.HostRoutes, []subnets.HostRoute{}) th.AssertEquals(t, s.IPVersion, 4) th.AssertEquals(t, s.GatewayIP, "") th.AssertEquals(t, s.CIDR, "192.168.1.0/24") th.AssertEquals(t, s.ID, "54d6f61d-db07-451c-9ab3-b9609b6b6f0c") } func TestCreateDefaultGateway(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/subnets", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, SubnetCreateWithDefaultGatewayRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, SubnetCreateWithDefaultGatewayResponse) }) opts := subnets.CreateOpts{ NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a23", IPVersion: 4, CIDR: "192.168.1.0/24", AllocationPools: []subnets.AllocationPool{ { Start: "192.168.1.2", End: "192.168.1.254", }, }, DNSNameservers: []string{}, } s, err := subnets.Create(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "") th.AssertEquals(t, s.EnableDHCP, true) th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a23") th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869") th.AssertDeepEquals(t, s.AllocationPools, []subnets.AllocationPool{ { Start: "192.168.1.2", End: "192.168.1.254", }, }) th.AssertDeepEquals(t, s.HostRoutes, []subnets.HostRoute{}) th.AssertEquals(t, s.IPVersion, 4) th.AssertEquals(t, s.GatewayIP, "192.168.1.1") th.AssertEquals(t, s.CIDR, "192.168.1.0/24") th.AssertEquals(t, s.ID, "54d6f61d-db07-451c-9ab3-b9609b6b6f0c") } func TestCreateIPv6RaAddressMode(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/subnets", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, SubnetCreateWithIPv6RaAddressModeRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, SubnetCreateWithIPv6RaAddressModeResponse) }) var gatewayIP = "2001:db8:0:a::1" opts := subnets.CreateOpts{ NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", IPVersion: 6, CIDR: "2001:db8:0:a:0:0:0:0/64", GatewayIP: &gatewayIP, IPv6AddressMode: "slaac", IPv6RAMode: "slaac", } s, err := subnets.Create(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "") th.AssertEquals(t, s.EnableDHCP, true) th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869") th.AssertEquals(t, s.IPVersion, 6) th.AssertEquals(t, s.GatewayIP, "2001:db8:0:a::1") th.AssertEquals(t, s.CIDR, "2001:db8:0:a:0:0:0:0/64") th.AssertEquals(t, s.ID, "3b80198d-4f7b-4f77-9ef5-774d54e17126") th.AssertEquals(t, s.IPv6AddressMode, "slaac") th.AssertEquals(t, s.IPv6RAMode, "slaac") } func TestCreateWithNoCIDR(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/subnets", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, SubnetCreateRequestWithNoCIDR) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, SubnetCreateResult) }) opts := subnets.CreateOpts{ NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", IPVersion: 4, DNSNameservers: []string{"foo"}, HostRoutes: []subnets.HostRoute{ {NextHop: "bar"}, }, SubnetPoolID: "b80340c7-9960-4f67-a99c-02501656284b", } s, err := subnets.Create(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "") th.AssertEquals(t, s.EnableDHCP, true) th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869") th.AssertDeepEquals(t, s.DNSNameservers, []string{}) th.AssertDeepEquals(t, s.AllocationPools, []subnets.AllocationPool{ { Start: "192.168.199.2", End: "192.168.199.254", }, }) th.AssertDeepEquals(t, s.HostRoutes, []subnets.HostRoute{}) th.AssertEquals(t, s.IPVersion, 4) th.AssertEquals(t, s.GatewayIP, "192.168.199.1") th.AssertEquals(t, s.CIDR, "192.168.199.0/24") th.AssertEquals(t, s.ID, "3b80198d-4f7b-4f77-9ef5-774d54e17126") th.AssertEquals(t, s.SubnetPoolID, "b80340c7-9960-4f67-a99c-02501656284b") } func TestRequiredCreateOpts(t *testing.T) { res := subnets.Create(fake.ServiceClient(), subnets.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = subnets.Create(fake.ServiceClient(), subnets.CreateOpts{NetworkID: "foo"}) if res.Err == nil { t.Fatalf("Expected error, got none") } res = subnets.Create(fake.ServiceClient(), subnets.CreateOpts{NetworkID: "foo", CIDR: "bar", IPVersion: 40}) if res.Err == nil { t.Fatalf("Expected error, got none") } } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/subnets/08eae331-0402-425a-923c-34f7cfe39c1b", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, SubnetUpdateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, SubnetUpdateResponse) }) opts := subnets.UpdateOpts{ Name: "my_new_subnet", DNSNameservers: []string{"foo"}, HostRoutes: &[]subnets.HostRoute{ {NextHop: "bar"}, }, } s, err := subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "my_new_subnet") th.AssertEquals(t, s.ID, "08eae331-0402-425a-923c-34f7cfe39c1b") } func TestUpdateGateway(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/subnets/08eae331-0402-425a-923c-34f7cfe39c1b", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, SubnetUpdateGatewayRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, SubnetUpdateGatewayResponse) }) var gatewayIP = "10.0.0.1" opts := subnets.UpdateOpts{ Name: "my_new_subnet", GatewayIP: &gatewayIP, } s, err := subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "my_new_subnet") th.AssertEquals(t, s.ID, "08eae331-0402-425a-923c-34f7cfe39c1b") th.AssertEquals(t, s.GatewayIP, "10.0.0.1") } func TestUpdateRemoveGateway(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/subnets/08eae331-0402-425a-923c-34f7cfe39c1b", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, SubnetUpdateRemoveGatewayRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, SubnetUpdateRemoveGatewayResponse) }) var noGateway = "" opts := subnets.UpdateOpts{ Name: "my_new_subnet", GatewayIP: &noGateway, } s, err := subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "my_new_subnet") th.AssertEquals(t, s.ID, "08eae331-0402-425a-923c-34f7cfe39c1b") th.AssertEquals(t, s.GatewayIP, "") } func TestUpdateHostRoutes(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/subnets/08eae331-0402-425a-923c-34f7cfe39c1b", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, SubnetUpdateHostRoutesRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, SubnetUpdateHostRoutesResponse) }) HostRoutes := []subnets.HostRoute{ { DestinationCIDR: "192.168.1.1/24", NextHop: "bar", }, } opts := subnets.UpdateOpts{ Name: "my_new_subnet", HostRoutes: &HostRoutes, } s, err := subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "my_new_subnet") th.AssertEquals(t, s.ID, "08eae331-0402-425a-923c-34f7cfe39c1b") th.AssertDeepEquals(t, s.HostRoutes, HostRoutes) } func TestUpdateRemoveHostRoutes(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/subnets/08eae331-0402-425a-923c-34f7cfe39c1b", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, SubnetUpdateRemoveHostRoutesRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, SubnetUpdateRemoveHostRoutesResponse) }) noHostRoutes := []subnets.HostRoute{} opts := subnets.UpdateOpts{ HostRoutes: &noHostRoutes, } s, err := subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "my_new_subnet") th.AssertEquals(t, s.ID, "08eae331-0402-425a-923c-34f7cfe39c1b") th.AssertDeepEquals(t, s.HostRoutes, noHostRoutes) } func TestUpdateAllocationPool(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/subnets/08eae331-0402-425a-923c-34f7cfe39c1b", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, SubnetUpdateAllocationPoolRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, SubnetUpdateAllocationPoolResponse) }) opts := subnets.UpdateOpts{ Name: "my_new_subnet", AllocationPools: []subnets.AllocationPool{ { Start: "10.1.0.2", End: "10.1.0.254", }, }, } s, err := subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "my_new_subnet") th.AssertEquals(t, s.ID, "08eae331-0402-425a-923c-34f7cfe39c1b") th.AssertDeepEquals(t, s.AllocationPools, []subnets.AllocationPool{ { Start: "10.1.0.2", End: "10.1.0.254", }, }) } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/v2.0/subnets/08eae331-0402-425a-923c-34f7cfe39c1b", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) res := subnets.Delete(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b") th.AssertNoErr(t, res.Err) } results_test.go000066400000000000000000000026331335005366400364350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/subnets/testingpackage testing import ( "encoding/json" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" th "github.com/gophercloud/gophercloud/testhelper" ) func TestHostRoute(t *testing.T) { sejson := []byte(` {"subnet": { "name": "test-subnet", "enable_dhcp": false, "network_id": "3e66c41e-cbbd-4019-9aab-740b7e4150a0", "tenant_id": "f86e123198cf42d19c8854c5f80c2f06", "dns_nameservers": [], "gateway_ip": "172.16.0.1", "ipv6_ra_mode": null, "allocation_pools": [ { "start": "172.16.0.2", "end": "172.16.255.254" } ], "host_routes": [ { "destination": "172.20.1.0/24", "nexthop": "172.16.0.2" } ], "ip_version": 4, "ipv6_address_mode": null, "cidr": "172.16.0.0/16", "id": "6dcaa873-7115-41af-9ef5-915f73636e43", "subnetpool_id": null }} `) var dejson interface{} err := json.Unmarshal(sejson, &dejson) if err != nil { t.Fatalf("%s", err) } resp := gophercloud.Result{Body: dejson} var subnetWrapper struct { Subnet subnets.Subnet `json:"subnet"` } err = resp.ExtractInto(&subnetWrapper) if err != nil { t.Fatalf("%s", err) } route := subnetWrapper.Subnet.HostRoutes[0] th.AssertEquals(t, route.NextHop, "172.16.0.2") th.AssertEquals(t, route.DestinationCIDR, "172.20.1.0/24") } urls.go000066400000000000000000000012561335005366400332050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/networking/v2/subnetspackage subnets import "github.com/gophercloud/gophercloud" func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("subnets", id) } func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("subnets") } func listURL(c *gophercloud.ServiceClient) string { return rootURL(c) } func getURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } func createURL(c *gophercloud.ServiceClient) string { return rootURL(c) } func updateURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } func deleteURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/000077500000000000000000000000001335005366400303765ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/000077500000000000000000000000001335005366400307245ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/accounts/000077500000000000000000000000001335005366400325435ustar00rootroot00000000000000doc.go000066400000000000000000000014241335005366400335610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/accounts/* Package accounts contains functionality for working with Object Storage account resources. An account is the top-level resource the object storage hierarchy: containers belong to accounts, objects belong to containers. Another way of thinking of an account is like a namespace for all your resources. It is synonymous with a project or tenant in other OpenStack services. Example to Get an Account account, err := accounts.Get(objectStorageClient, nil).Extract() fmt.Printf("%+v\n", account) Example to Update an Account metadata := map[string]string{ "some": "metadata", } updateOpts := accounts.UpdateOpts{ Metadata: metadata, } updateResult, err := accounts.Update(objectStorageClient, updateOpts).Extract() fmt.Printf("%+v\n", updateResult) */ package accounts requests.go000066400000000000000000000053741335005366400346770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/accountspackage accounts import "github.com/gophercloud/gophercloud" // GetOptsBuilder allows extensions to add additional headers to the Get // request. type GetOptsBuilder interface { ToAccountGetMap() (map[string]string, error) } // GetOpts is a structure that contains parameters for getting an account's // metadata. type GetOpts struct { Newest bool `h:"X-Newest"` } // ToAccountGetMap formats a GetOpts into a map[string]string of headers. func (opts GetOpts) ToAccountGetMap() (map[string]string, error) { return gophercloud.BuildHeaders(opts) } // Get is a function that retrieves an account's metadata. To extract just the // custom metadata, call the ExtractMetadata method on the GetResult. To extract // all the headers that are returned (including the metadata), call the // Extract method on the GetResult. func Get(c *gophercloud.ServiceClient, opts GetOptsBuilder) (r GetResult) { h := make(map[string]string) if opts != nil { headers, err := opts.ToAccountGetMap() if err != nil { r.Err = err return } for k, v := range headers { h[k] = v } } resp, err := c.Head(getURL(c), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{204}, }) if resp != nil { r.Header = resp.Header } r.Err = err return } // UpdateOptsBuilder allows extensions to add additional headers to the Update // request. type UpdateOptsBuilder interface { ToAccountUpdateMap() (map[string]string, error) } // UpdateOpts is a structure that contains parameters for updating, creating, or // deleting an account's metadata. type UpdateOpts struct { Metadata map[string]string ContentType string `h:"Content-Type"` DetectContentType bool `h:"X-Detect-Content-Type"` TempURLKey string `h:"X-Account-Meta-Temp-URL-Key"` TempURLKey2 string `h:"X-Account-Meta-Temp-URL-Key-2"` } // ToAccountUpdateMap formats an UpdateOpts into a map[string]string of headers. func (opts UpdateOpts) ToAccountUpdateMap() (map[string]string, error) { headers, err := gophercloud.BuildHeaders(opts) if err != nil { return nil, err } for k, v := range opts.Metadata { headers["X-Account-Meta-"+k] = v } return headers, err } // Update is a function that creates, updates, or deletes an account's metadata. // To extract the headers returned, call the Extract method on the UpdateResult. func Update(c *gophercloud.ServiceClient, opts UpdateOptsBuilder) (r UpdateResult) { h := make(map[string]string) if opts != nil { headers, err := opts.ToAccountUpdateMap() if err != nil { r.Err = err return } for k, v := range headers { h[k] = v } } resp, err := c.Request("POST", updateURL(c), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201, 202, 204}, }) if resp != nil { r.Header = resp.Header } r.Err = err return } results.go000066400000000000000000000077641335005366400345320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/accountspackage accounts import ( "encoding/json" "strconv" "strings" "time" "github.com/gophercloud/gophercloud" ) // UpdateResult is returned from a call to the Update function. type UpdateResult struct { gophercloud.HeaderResult } // UpdateHeader represents the headers returned in the response from an Update // request. type UpdateHeader struct { ContentLength int64 `json:"-"` ContentType string `json:"Content-Type"` TransID string `json:"X-Trans-Id"` Date time.Time `json:"-"` } func (r *UpdateHeader) UnmarshalJSON(b []byte) error { type tmp UpdateHeader var s struct { tmp ContentLength string `json:"Content-Length"` Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = UpdateHeader(s.tmp) switch s.ContentLength { case "": r.ContentLength = 0 default: r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) if err != nil { return err } } r.Date = time.Time(s.Date) return err } // Extract will return a struct of headers returned from a call to Get. To // obtain a map of headers, call the Extract method on the GetResult. func (r UpdateResult) Extract() (*UpdateHeader, error) { var s *UpdateHeader err := r.ExtractInto(&s) return s, err } // GetHeader represents the headers returned in the response from a Get request. type GetHeader struct { BytesUsed int64 `json:"-"` QuotaBytes *int64 `json:"-"` ContainerCount int64 `json:"-"` ContentLength int64 `json:"-"` ObjectCount int64 `json:"-"` ContentType string `json:"Content-Type"` TransID string `json:"X-Trans-Id"` TempURLKey string `json:"X-Account-Meta-Temp-URL-Key"` TempURLKey2 string `json:"X-Account-Meta-Temp-URL-Key-2"` Date time.Time `json:"-"` } func (r *GetHeader) UnmarshalJSON(b []byte) error { type tmp GetHeader var s struct { tmp BytesUsed string `json:"X-Account-Bytes-Used"` QuotaBytes string `json:"X-Account-Meta-Quota-Bytes"` ContentLength string `json:"Content-Length"` ContainerCount string `json:"X-Account-Container-Count"` ObjectCount string `json:"X-Account-Object-Count"` Date string `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = GetHeader(s.tmp) switch s.BytesUsed { case "": r.BytesUsed = 0 default: r.BytesUsed, err = strconv.ParseInt(s.BytesUsed, 10, 64) if err != nil { return err } } switch s.QuotaBytes { case "": r.QuotaBytes = nil default: v, err := strconv.ParseInt(s.QuotaBytes, 10, 64) if err != nil { return err } r.QuotaBytes = &v } switch s.ContentLength { case "": r.ContentLength = 0 default: r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) if err != nil { return err } } switch s.ObjectCount { case "": r.ObjectCount = 0 default: r.ObjectCount, err = strconv.ParseInt(s.ObjectCount, 10, 64) if err != nil { return err } } switch s.ContainerCount { case "": r.ContainerCount = 0 default: r.ContainerCount, err = strconv.ParseInt(s.ContainerCount, 10, 64) if err != nil { return err } } if s.Date != "" { r.Date, err = time.Parse(time.RFC1123, s.Date) } return err } // GetResult is returned from a call to the Get function. type GetResult struct { gophercloud.HeaderResult } // Extract will return a struct of headers returned from a call to Get. func (r GetResult) Extract() (*GetHeader, error) { var s *GetHeader err := r.ExtractInto(&s) return s, err } // ExtractMetadata is a function that takes a GetResult (of type *http.Response) // and returns the custom metatdata associated with the account. func (r GetResult) ExtractMetadata() (map[string]string, error) { if r.Err != nil { return nil, r.Err } metadata := make(map[string]string) for k, v := range r.Header { if strings.HasPrefix(k, "X-Account-Meta-") { key := strings.TrimPrefix(k, "X-Account-Meta-") metadata[key] = v[0] } } return metadata, nil } testing/000077500000000000000000000000001335005366400341415ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/accountsdoc.go000066400000000000000000000000471335005366400352360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/accounts/testing// accounts unit tests package testing fixtures.go000066400000000000000000000037321335005366400363460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/accounts/testingpackage testing import ( "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) // HandleGetAccountSuccessfully creates an HTTP handler at `/` on the test handler mux that // responds with a `Get` response. func HandleGetAccountSuccessfully(t *testing.T) { th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "HEAD") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Set("X-Account-Container-Count", "2") w.Header().Set("X-Account-Object-Count", "5") w.Header().Set("X-Account-Meta-Quota-Bytes", "42") w.Header().Set("X-Account-Bytes-Used", "14") w.Header().Set("X-Account-Meta-Subject", "books") w.Header().Set("Date", "Fri, 17 Jan 2014 16:09:56 GMT") w.WriteHeader(http.StatusNoContent) }) } // HandleGetAccountNoQuotaSuccessfully creates an HTTP handler at `/` on the // test handler mux that responds with a `Get` response. func HandleGetAccountNoQuotaSuccessfully(t *testing.T) { th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "HEAD") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Set("X-Account-Container-Count", "2") w.Header().Set("X-Account-Object-Count", "5") w.Header().Set("X-Account-Bytes-Used", "14") w.Header().Set("X-Account-Meta-Subject", "books") w.Header().Set("Date", "Fri, 17 Jan 2014 16:09:56 GMT") w.WriteHeader(http.StatusNoContent) }) } // HandleUpdateAccountSuccessfully creates an HTTP handler at `/` on the test handler mux that // responds with a `Update` response. func HandleUpdateAccountSuccessfully(t *testing.T) { th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "X-Account-Meta-Gophercloud-Test", "accounts") w.Header().Set("Date", "Fri, 17 Jan 2014 16:09:56 GMT") w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000044651335005366400374130ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/accounts/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) var ( loc, _ = time.LoadLocation("GMT") ) func TestUpdateAccount(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateAccountSuccessfully(t) options := &accounts.UpdateOpts{Metadata: map[string]string{"gophercloud-test": "accounts"}} res := accounts.Update(fake.ServiceClient(), options) th.AssertNoErr(t, res.Err) expected := &accounts.UpdateHeader{ Date: time.Date(2014, time.January, 17, 16, 9, 56, 0, loc), // Fri, 17 Jan 2014 16:09:56 GMT } actual, err := res.Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } func TestGetAccount(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetAccountSuccessfully(t) expectedMetadata := map[string]string{"Subject": "books", "Quota-Bytes": "42"} res := accounts.Get(fake.ServiceClient(), &accounts.GetOpts{}) th.AssertNoErr(t, res.Err) actualMetadata, _ := res.ExtractMetadata() th.CheckDeepEquals(t, expectedMetadata, actualMetadata) _, err := res.Extract() th.AssertNoErr(t, err) var quotaBytes int64 = 42 expected := &accounts.GetHeader{ QuotaBytes: "aBytes, ContainerCount: 2, ObjectCount: 5, BytesUsed: 14, Date: time.Date(2014, time.January, 17, 16, 9, 56, 0, loc), // Fri, 17 Jan 2014 16:09:56 GMT } actual, err := res.Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } func TestGetAccountNoQuota(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetAccountNoQuotaSuccessfully(t) expectedMetadata := map[string]string{"Subject": "books"} res := accounts.Get(fake.ServiceClient(), &accounts.GetOpts{}) th.AssertNoErr(t, res.Err) actualMetadata, _ := res.ExtractMetadata() th.CheckDeepEquals(t, expectedMetadata, actualMetadata) _, err := res.Extract() th.AssertNoErr(t, err) expected := &accounts.GetHeader{ QuotaBytes: nil, ContainerCount: 2, ObjectCount: 5, BytesUsed: 14, Date: time.Date(2014, time.January, 17, 16, 9, 56, 0, loc), // Fri, 17 Jan 2014 16:09:56 GMT } actual, err := res.Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } urls.go000066400000000000000000000003221335005366400337750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/accountspackage accounts import "github.com/gophercloud/gophercloud" func getURL(c *gophercloud.ServiceClient) string { return c.Endpoint } func updateURL(c *gophercloud.ServiceClient) string { return getURL(c) } containers/000077500000000000000000000000001335005366400330125ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1doc.go000066400000000000000000000041251335005366400341100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/containers/* Package containers contains functionality for working with Object Storage container resources. A container serves as a logical namespace for objects that are placed inside it - an object with the same name in two different containers represents two different objects. In addition to containing objects, you can also use the container to control access to objects by using an access control list (ACL). Note: When referencing the Object Storage API docs, some of the API actions are listed under "accounts" rather than "containers". This was an intentional design in Gophercloud to make some container actions feel more natural. Example to List Containers listOpts := containers.ListOpts{ Full: true, } allPages, err := containers.List(objectStorageClient, listOpts).AllPages() if err != nil { panic(err) } allContainers, err := containers.ExtractInfo(allPages) if err != nil { panic(err) } for _, container := range allContainers { fmt.Printf("%+v\n", container) } Example to List Only Container Names listOpts := containers.ListOpts{ Full: false, } allPages, err := containers.List(objectStorageClient, listOpts).AllPages() if err != nil { panic(err) } allContainers, err := containers.ExtractNames(allPages) if err != nil { panic(err) } for _, container := range allContainers { fmt.Printf("%+v\n", container) } Example to Create a Container createOpts := containers.CreateOpts{ ContentType: "application/json", Metadata: map[string]string{ "foo": "bar", }, } container, err := containers.Create(objectStorageClient, createOpts).Extract() if err != nil { panic(err) } Example to Update a Container containerName := "my_container" updateOpts := containers.UpdateOpts{ Metadata: map[string]string{ "bar": "baz", }, } container, err := containers.Update(objectStorageClient, containerName, updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Container containerName := "my_container" container, err := containers.Delete(objectStorageClient, containerName).Extract() if err != nil { panic(err) } */ package containers requests.go000066400000000000000000000144331335005366400352210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/containerspackage containers import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { ToContainerListParams() (bool, string, error) } // ListOpts is a structure that holds options for listing containers. type ListOpts struct { Full bool Limit int `q:"limit"` Marker string `q:"marker"` EndMarker string `q:"end_marker"` Format string `q:"format"` Prefix string `q:"prefix"` Delimiter string `q:"delimiter"` } // ToContainerListParams formats a ListOpts into a query string and boolean // representing whether to list complete information for each container. func (opts ListOpts) ToContainerListParams() (bool, string, error) { q, err := gophercloud.BuildQueryString(opts) return opts.Full, q.String(), err } // List is a function that retrieves containers associated with the account as // well as account metadata. It returns a pager which can be iterated with the // EachPage function. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { headers := map[string]string{"Accept": "text/plain", "Content-Type": "text/plain"} url := listURL(c) if opts != nil { full, query, err := opts.ToContainerListParams() if err != nil { return pagination.Pager{Err: err} } url += query if full { headers = map[string]string{"Accept": "application/json", "Content-Type": "application/json"} } } pager := pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { p := ContainerPage{pagination.MarkerPageBase{PageResult: r}} p.MarkerPageBase.Owner = p return p }) pager.Headers = headers return pager } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToContainerCreateMap() (map[string]string, error) } // CreateOpts is a structure that holds parameters for creating a container. type CreateOpts struct { Metadata map[string]string ContainerRead string `h:"X-Container-Read"` ContainerSyncTo string `h:"X-Container-Sync-To"` ContainerSyncKey string `h:"X-Container-Sync-Key"` ContainerWrite string `h:"X-Container-Write"` ContentType string `h:"Content-Type"` DetectContentType bool `h:"X-Detect-Content-Type"` IfNoneMatch string `h:"If-None-Match"` VersionsLocation string `h:"X-Versions-Location"` } // ToContainerCreateMap formats a CreateOpts into a map of headers. func (opts CreateOpts) ToContainerCreateMap() (map[string]string, error) { h, err := gophercloud.BuildHeaders(opts) if err != nil { return nil, err } for k, v := range opts.Metadata { h["X-Container-Meta-"+k] = v } return h, nil } // Create is a function that creates a new container. func Create(c *gophercloud.ServiceClient, containerName string, opts CreateOptsBuilder) (r CreateResult) { h := make(map[string]string) if opts != nil { headers, err := opts.ToContainerCreateMap() if err != nil { r.Err = err return } for k, v := range headers { h[k] = v } } resp, err := c.Request("PUT", createURL(c, containerName), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201, 202, 204}, }) if resp != nil { r.Header = resp.Header resp.Body.Close() } r.Err = err return } // Delete is a function that deletes a container. func Delete(c *gophercloud.ServiceClient, containerName string) (r DeleteResult) { _, r.Err = c.Delete(deleteURL(c, containerName), nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToContainerUpdateMap() (map[string]string, error) } // UpdateOpts is a structure that holds parameters for updating, creating, or // deleting a container's metadata. type UpdateOpts struct { Metadata map[string]string ContainerRead string `h:"X-Container-Read"` ContainerSyncTo string `h:"X-Container-Sync-To"` ContainerSyncKey string `h:"X-Container-Sync-Key"` ContainerWrite string `h:"X-Container-Write"` ContentType string `h:"Content-Type"` DetectContentType bool `h:"X-Detect-Content-Type"` RemoveVersionsLocation string `h:"X-Remove-Versions-Location"` VersionsLocation string `h:"X-Versions-Location"` } // ToContainerUpdateMap formats a UpdateOpts into a map of headers. func (opts UpdateOpts) ToContainerUpdateMap() (map[string]string, error) { h, err := gophercloud.BuildHeaders(opts) if err != nil { return nil, err } for k, v := range opts.Metadata { h["X-Container-Meta-"+k] = v } return h, nil } // Update is a function that creates, updates, or deletes a container's // metadata. func Update(c *gophercloud.ServiceClient, containerName string, opts UpdateOptsBuilder) (r UpdateResult) { h := make(map[string]string) if opts != nil { headers, err := opts.ToContainerUpdateMap() if err != nil { r.Err = err return } for k, v := range headers { h[k] = v } } resp, err := c.Request("POST", updateURL(c, containerName), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201, 202, 204}, }) if resp != nil { r.Header = resp.Header } r.Err = err return } // GetOptsBuilder allows extensions to add additional parameters to the Get // request. type GetOptsBuilder interface { ToContainerGetMap() (map[string]string, error) } // GetOpts is a structure that holds options for listing containers. type GetOpts struct { Newest bool `h:"X-Newest"` } // ToContainerGetMap formats a GetOpts into a map of headers. func (opts GetOpts) ToContainerGetMap() (map[string]string, error) { return gophercloud.BuildHeaders(opts) } // Get is a function that retrieves the metadata of a container. To extract just // the custom metadata, pass the GetResult response to the ExtractMetadata // function. func Get(c *gophercloud.ServiceClient, containerName string, opts GetOptsBuilder) (r GetResult) { h := make(map[string]string) if opts != nil { headers, err := opts.ToContainerGetMap() if err != nil { r.Err = err return } for k, v := range headers { h[k] = v } } resp, err := c.Head(getURL(c, containerName), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{200, 204}, }) if resp != nil { r.Header = resp.Header } r.Err = err return } results.go000066400000000000000000000204271335005366400350470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/containerspackage containers import ( "encoding/json" "fmt" "strconv" "strings" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Container represents a container resource. type Container struct { // The total number of bytes stored in the container. Bytes int64 `json:"bytes"` // The total number of objects stored in the container. Count int64 `json:"count"` // The name of the container. Name string `json:"name"` } // ContainerPage is the page returned by a pager when traversing over a // collection of containers. type ContainerPage struct { pagination.MarkerPageBase } //IsEmpty returns true if a ListResult contains no container names. func (r ContainerPage) IsEmpty() (bool, error) { names, err := ExtractNames(r) return len(names) == 0, err } // LastMarker returns the last container name in a ListResult. func (r ContainerPage) LastMarker() (string, error) { names, err := ExtractNames(r) if err != nil { return "", err } if len(names) == 0 { return "", nil } return names[len(names)-1], nil } // ExtractInfo is a function that takes a ListResult and returns the // containers' information. func ExtractInfo(r pagination.Page) ([]Container, error) { var s []Container err := (r.(ContainerPage)).ExtractInto(&s) return s, err } // ExtractNames is a function that takes a ListResult and returns the // containers' names. func ExtractNames(page pagination.Page) ([]string, error) { casted := page.(ContainerPage) ct := casted.Header.Get("Content-Type") switch { case strings.HasPrefix(ct, "application/json"): parsed, err := ExtractInfo(page) if err != nil { return nil, err } names := make([]string, 0, len(parsed)) for _, container := range parsed { names = append(names, container.Name) } return names, nil case strings.HasPrefix(ct, "text/plain"): names := make([]string, 0, 50) body := string(page.(ContainerPage).Body.([]uint8)) for _, name := range strings.Split(body, "\n") { if len(name) > 0 { names = append(names, name) } } return names, nil default: return nil, fmt.Errorf("Cannot extract names from response with content-type: [%s]", ct) } } // GetHeader represents the headers returned in the response from a Get request. type GetHeader struct { AcceptRanges string `json:"Accept-Ranges"` BytesUsed int64 `json:"-"` ContentLength int64 `json:"-"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` ObjectCount int64 `json:"-"` Read []string `json:"-"` TransID string `json:"X-Trans-Id"` VersionsLocation string `json:"X-Versions-Location"` Write []string `json:"-"` StoragePolicy string `json:"X-Storage-Policy"` } func (r *GetHeader) UnmarshalJSON(b []byte) error { type tmp GetHeader var s struct { tmp BytesUsed string `json:"X-Container-Bytes-Used"` ContentLength string `json:"Content-Length"` ObjectCount string `json:"X-Container-Object-Count"` Write string `json:"X-Container-Write"` Read string `json:"X-Container-Read"` Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = GetHeader(s.tmp) switch s.BytesUsed { case "": r.BytesUsed = 0 default: r.BytesUsed, err = strconv.ParseInt(s.BytesUsed, 10, 64) if err != nil { return err } } switch s.ContentLength { case "": r.ContentLength = 0 default: r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) if err != nil { return err } } switch s.ObjectCount { case "": r.ObjectCount = 0 default: r.ObjectCount, err = strconv.ParseInt(s.ObjectCount, 10, 64) if err != nil { return err } } r.Read = strings.Split(s.Read, ",") r.Write = strings.Split(s.Write, ",") r.Date = time.Time(s.Date) return err } // GetResult represents the result of a get operation. type GetResult struct { gophercloud.HeaderResult } // Extract will return a struct of headers returned from a call to Get. func (r GetResult) Extract() (*GetHeader, error) { var s *GetHeader err := r.ExtractInto(&s) return s, err } // ExtractMetadata is a function that takes a GetResult (of type *http.Response) // and returns the custom metadata associated with the container. func (r GetResult) ExtractMetadata() (map[string]string, error) { if r.Err != nil { return nil, r.Err } metadata := make(map[string]string) for k, v := range r.Header { if strings.HasPrefix(k, "X-Container-Meta-") { key := strings.TrimPrefix(k, "X-Container-Meta-") metadata[key] = v[0] } } return metadata, nil } // CreateHeader represents the headers returned in the response from a Create // request. type CreateHeader struct { ContentLength int64 `json:"-"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` TransID string `json:"X-Trans-Id"` } func (r *CreateHeader) UnmarshalJSON(b []byte) error { type tmp CreateHeader var s struct { tmp ContentLength string `json:"Content-Length"` Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = CreateHeader(s.tmp) switch s.ContentLength { case "": r.ContentLength = 0 default: r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) if err != nil { return err } } r.Date = time.Time(s.Date) return err } // CreateResult represents the result of a create operation. To extract the // the headers from the HTTP response, call its Extract method. type CreateResult struct { gophercloud.HeaderResult } // Extract will return a struct of headers returned from a call to Create. // To extract the headers from the HTTP response, call its Extract method. func (r CreateResult) Extract() (*CreateHeader, error) { var s *CreateHeader err := r.ExtractInto(&s) return s, err } // UpdateHeader represents the headers returned in the response from a Update // request. type UpdateHeader struct { ContentLength int64 `json:"-"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` TransID string `json:"X-Trans-Id"` } func (r *UpdateHeader) UnmarshalJSON(b []byte) error { type tmp UpdateHeader var s struct { tmp ContentLength string `json:"Content-Length"` Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = UpdateHeader(s.tmp) switch s.ContentLength { case "": r.ContentLength = 0 default: r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) if err != nil { return err } } r.Date = time.Time(s.Date) return err } // UpdateResult represents the result of an update operation. To extract the // the headers from the HTTP response, call its Extract method. type UpdateResult struct { gophercloud.HeaderResult } // Extract will return a struct of headers returned from a call to Update. func (r UpdateResult) Extract() (*UpdateHeader, error) { var s *UpdateHeader err := r.ExtractInto(&s) return s, err } // DeleteHeader represents the headers returned in the response from a Delete // request. type DeleteHeader struct { ContentLength int64 `json:"-"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` TransID string `json:"X-Trans-Id"` } func (r *DeleteHeader) UnmarshalJSON(b []byte) error { type tmp DeleteHeader var s struct { tmp ContentLength string `json:"Content-Length"` Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = DeleteHeader(s.tmp) switch s.ContentLength { case "": r.ContentLength = 0 default: r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) if err != nil { return err } } r.Date = time.Time(s.Date) return err } // DeleteResult represents the result of a delete operation. To extract the // the headers from the HTTP response, call its Extract method. type DeleteResult struct { gophercloud.HeaderResult } // Extract will return a struct of headers returned from a call to Delete. func (r DeleteResult) Extract() (*DeleteHeader, error) { var s *DeleteHeader err := r.ExtractInto(&s) return s, err } testing/000077500000000000000000000000001335005366400344675ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/containersdoc.go000066400000000000000000000000511335005366400355570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/containers/testing// containers unit tests package testing fixtures.go000066400000000000000000000120141335005366400366650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/containers/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) // ExpectedListInfo is the result expected from a call to `List` when full // info is requested. var ExpectedListInfo = []containers.Container{ { Count: 0, Bytes: 0, Name: "janeausten", }, { Count: 1, Bytes: 14, Name: "marktwain", }, } // ExpectedListNames is the result expected from a call to `List` when just // container names are requested. var ExpectedListNames = []string{"janeausten", "marktwain"} // HandleListContainerInfoSuccessfully creates an HTTP handler at `/` on the test handler mux that // responds with a `List` response when full info is requested. func HandleListContainerInfoSuccessfully(t *testing.T) { th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, `[ { "count": 0, "bytes": 0, "name": "janeausten" }, { "count": 1, "bytes": 14, "name": "marktwain" } ]`) case "janeausten": fmt.Fprintf(w, `[ { "count": 1, "bytes": 14, "name": "marktwain" } ]`) case "marktwain": fmt.Fprintf(w, `[]`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) } // HandleListContainerNamesSuccessfully creates an HTTP handler at `/` on the test handler mux that // responds with a `ListNames` response when only container names are requested. func HandleListContainerNamesSuccessfully(t *testing.T) { th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "text/plain") w.Header().Set("Content-Type", "text/plain") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, "janeausten\nmarktwain\n") case "janeausten": fmt.Fprintf(w, "marktwain\n") case "marktwain": fmt.Fprintf(w, ``) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) } // HandleCreateContainerSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that // responds with a `Create` response. func HandleCreateContainerSuccessfully(t *testing.T) { th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Add("X-Container-Meta-Foo", "bar") w.Header().Set("Content-Length", "0") w.Header().Set("Content-Type", "text/html; charset=UTF-8") w.Header().Set("Date", "Wed, 17 Aug 2016 19:25:43 GMT") w.Header().Set("X-Trans-Id", "tx554ed59667a64c61866f1-0058b4ba37") w.WriteHeader(http.StatusNoContent) }) } // HandleDeleteContainerSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that // responds with a `Delete` response. func HandleDeleteContainerSuccessfully(t *testing.T) { th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.WriteHeader(http.StatusNoContent) }) } // HandleUpdateContainerSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that // responds with a `Update` response. func HandleUpdateContainerSuccessfully(t *testing.T) { th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.WriteHeader(http.StatusNoContent) }) } // HandleGetContainerSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that // responds with a `Get` response. func HandleGetContainerSuccessfully(t *testing.T) { th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "HEAD") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Accept-Ranges", "bytes") w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("Date", "Wed, 17 Aug 2016 19:25:43 GMT") w.Header().Set("X-Container-Bytes-Used", "100") w.Header().Set("X-Container-Object-Count", "4") w.Header().Set("X-Container-Read", "test") w.Header().Set("X-Container-Write", "test2,user4") w.Header().Set("X-Timestamp", "1471298837.95721") w.Header().Set("X-Trans-Id", "tx554ed59667a64c61866f1-0057b4ba37") w.Header().Set("X-Storage-Policy", "test_policy") w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000102621335005366400377310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/containers/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) var ( metadata = map[string]string{"gophercloud-test": "containers"} loc, _ = time.LoadLocation("GMT") ) func TestListContainerInfo(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListContainerInfoSuccessfully(t) count := 0 err := containers.List(fake.ServiceClient(), &containers.ListOpts{Full: true}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := containers.ExtractInfo(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedListInfo, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListAllContainerInfo(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListContainerInfoSuccessfully(t) allPages, err := containers.List(fake.ServiceClient(), &containers.ListOpts{Full: true}).AllPages() th.AssertNoErr(t, err) actual, err := containers.ExtractInfo(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedListInfo, actual) } func TestListContainerNames(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListContainerNamesSuccessfully(t) count := 0 err := containers.List(fake.ServiceClient(), &containers.ListOpts{Full: false}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := containers.ExtractNames(page) if err != nil { t.Errorf("Failed to extract container names: %v", err) return false, err } th.CheckDeepEquals(t, ExpectedListNames, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListAllContainerNames(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListContainerNamesSuccessfully(t) allPages, err := containers.List(fake.ServiceClient(), &containers.ListOpts{Full: false}).AllPages() th.AssertNoErr(t, err) actual, err := containers.ExtractNames(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedListNames, actual) } func TestCreateContainer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateContainerSuccessfully(t) options := containers.CreateOpts{ContentType: "application/json", Metadata: map[string]string{"foo": "bar"}} res := containers.Create(fake.ServiceClient(), "testContainer", options) th.CheckEquals(t, "bar", res.Header["X-Container-Meta-Foo"][0]) expected := &containers.CreateHeader{ ContentLength: 0, ContentType: "text/html; charset=UTF-8", Date: time.Date(2016, time.August, 17, 19, 25, 43, 0, loc), //Wed, 17 Aug 2016 19:25:43 GMT TransID: "tx554ed59667a64c61866f1-0058b4ba37", } actual, err := res.Extract() th.CheckNoErr(t, err) th.AssertDeepEquals(t, expected, actual) } func TestDeleteContainer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteContainerSuccessfully(t) res := containers.Delete(fake.ServiceClient(), "testContainer") th.CheckNoErr(t, res.Err) } func TestUpdateContainer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateContainerSuccessfully(t) options := &containers.UpdateOpts{Metadata: map[string]string{"foo": "bar"}} res := containers.Update(fake.ServiceClient(), "testContainer", options) th.CheckNoErr(t, res.Err) } func TestGetContainer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetContainerSuccessfully(t) getOpts := containers.GetOpts{ Newest: true, } res := containers.Get(fake.ServiceClient(), "testContainer", getOpts) _, err := res.ExtractMetadata() th.CheckNoErr(t, err) expected := &containers.GetHeader{ AcceptRanges: "bytes", BytesUsed: 100, ContentType: "application/json; charset=utf-8", Date: time.Date(2016, time.August, 17, 19, 25, 43, 0, loc), //Wed, 17 Aug 2016 19:25:43 GMT ObjectCount: 4, Read: []string{"test"}, TransID: "tx554ed59667a64c61866f1-0057b4ba37", Write: []string{"test2", "user4"}, StoragePolicy: "test_policy", } actual, err := res.Extract() th.CheckNoErr(t, err) th.AssertDeepEquals(t, expected, actual) } urls.go000066400000000000000000000010631335005366400343260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/containerspackage containers import "github.com/gophercloud/gophercloud" func listURL(c *gophercloud.ServiceClient) string { return c.Endpoint } func createURL(c *gophercloud.ServiceClient, container string) string { return c.ServiceURL(container) } func getURL(c *gophercloud.ServiceClient, container string) string { return createURL(c, container) } func deleteURL(c *gophercloud.ServiceClient, container string) string { return createURL(c, container) } func updateURL(c *gophercloud.ServiceClient, container string) string { return createURL(c, container) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/objects/000077500000000000000000000000001335005366400323555ustar00rootroot00000000000000doc.go000066400000000000000000000045311335005366400333750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/objects/* Package objects contains functionality for working with Object Storage object resources. An object is a resource that represents and contains data - such as documents, images, and so on. You can also store custom metadata with an object. Note: When referencing the Object Storage API docs, some of the API actions are listed under "containers" rather than "objects". This was an intentional design in Gophercloud to make some object actions feel more natural. Example to List Objects containerName := "my_container" listOpts := objects.ListOpts{ Full: true, } allPages, err := objects.List(objectStorageClient, containerName, listOpts).AllPages() if err != nil { panic(err) } allObjects, err := objects.ExtractInfo(allPages) if err != nil { panic(err) } for _, object := range allObjects { fmt.Printf("%+v\n", object) } Example to List Object Names containerName := "my_container" listOpts := objects.ListOpts{ Full: false, } allPages, err := objects.List(objectStorageClient, containerName, listOpts).AllPages() if err != nil { panic(err) } allObjects, err := objects.ExtractNames(allPages) if err != nil { panic(err) } for _, object := range allObjects { fmt.Printf("%+v\n", object) } Example to Create an Object content := "some object content" objectName := "my_object" containerName := "my_container" createOpts := objects.CreateOpts{ ContentType: "text/plain" Content: strings.NewReader(content), } object, err := objects.Create(objectStorageClient, containerName, objectName, createOpts).Extract() if err != nil { panic(err) } Example to Copy an Object objectName := "my_object" containerName := "my_container" copyOpts := objects.CopyOpts{ Destination: "/newContainer/newObject", } object, err := objects.Copy(objectStorageClient, containerName, objectName, copyOpts).Extract() if err != nil { panic(err) } Example to Delete an Object objectName := "my_object" containerName := "my_container" object, err := objects.Delete(objectStorageClient, containerName, objectName).Extract() if err != nil { panic(err) } Example to Download an Object's Data objectName := "my_object" containerName := "my_container" object := objects.Download(objectStorageClient, containerName, objectName, nil) content, err := object.ExtractContent() if err != nil { panic(err) } */ package objects errors.go000066400000000000000000000005111335005366400341360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/objectspackage objects import "github.com/gophercloud/gophercloud" // ErrWrongChecksum is the error when the checksum generated for an object // doesn't match the ETAG header. type ErrWrongChecksum struct { gophercloud.BaseError } func (e ErrWrongChecksum) Error() string { return "Local checksum does not match API ETag header" } requests.go000066400000000000000000000346431335005366400345120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/objectspackage objects import ( "bytes" "crypto/hmac" "crypto/md5" "crypto/sha1" "fmt" "io" "io/ioutil" "strings" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { ToObjectListParams() (bool, string, error) } // ListOpts is a structure that holds parameters for listing objects. type ListOpts struct { // Full is a true/false value that represents the amount of object information // returned. If Full is set to true, then the content-type, number of bytes, // hash date last modified, and name are returned. If set to false or not set, // then only the object names are returned. Full bool Limit int `q:"limit"` Marker string `q:"marker"` EndMarker string `q:"end_marker"` Format string `q:"format"` Prefix string `q:"prefix"` Delimiter string `q:"delimiter"` Path string `q:"path"` } // ToObjectListParams formats a ListOpts into a query string and boolean // representing whether to list complete information for each object. func (opts ListOpts) ToObjectListParams() (bool, string, error) { q, err := gophercloud.BuildQueryString(opts) return opts.Full, q.String(), err } // List is a function that retrieves all objects in a container. It also returns // the details for the container. To extract only the object information or names, // pass the ListResult response to the ExtractInfo or ExtractNames function, // respectively. func List(c *gophercloud.ServiceClient, containerName string, opts ListOptsBuilder) pagination.Pager { headers := map[string]string{"Accept": "text/plain", "Content-Type": "text/plain"} url := listURL(c, containerName) if opts != nil { full, query, err := opts.ToObjectListParams() if err != nil { return pagination.Pager{Err: err} } url += query if full { headers = map[string]string{"Accept": "application/json", "Content-Type": "application/json"} } } pager := pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { p := ObjectPage{pagination.MarkerPageBase{PageResult: r}} p.MarkerPageBase.Owner = p return p }) pager.Headers = headers return pager } // DownloadOptsBuilder allows extensions to add additional parameters to the // Download request. type DownloadOptsBuilder interface { ToObjectDownloadParams() (map[string]string, string, error) } // DownloadOpts is a structure that holds parameters for downloading an object. type DownloadOpts struct { IfMatch string `h:"If-Match"` IfModifiedSince time.Time `h:"If-Modified-Since"` IfNoneMatch string `h:"If-None-Match"` IfUnmodifiedSince time.Time `h:"If-Unmodified-Since"` Newest bool `h:"X-Newest"` Range string `h:"Range"` Expires string `q:"expires"` MultipartManifest string `q:"multipart-manifest"` Signature string `q:"signature"` } // ToObjectDownloadParams formats a DownloadOpts into a query string and map of // headers. func (opts DownloadOpts) ToObjectDownloadParams() (map[string]string, string, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return nil, "", err } h, err := gophercloud.BuildHeaders(opts) if err != nil { return nil, q.String(), err } return h, q.String(), nil } // Download is a function that retrieves the content and metadata for an object. // To extract just the content, pass the DownloadResult response to the // ExtractContent function. func Download(c *gophercloud.ServiceClient, containerName, objectName string, opts DownloadOptsBuilder) (r DownloadResult) { url := downloadURL(c, containerName, objectName) h := make(map[string]string) if opts != nil { headers, query, err := opts.ToObjectDownloadParams() if err != nil { r.Err = err return } for k, v := range headers { h[k] = v } url += query } resp, err := c.Get(url, nil, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{200, 206, 304}, }) if resp != nil { r.Header = resp.Header r.Body = resp.Body } r.Err = err return } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToObjectCreateParams() (io.Reader, map[string]string, string, error) } // CreateOpts is a structure that holds parameters for creating an object. type CreateOpts struct { Content io.Reader Metadata map[string]string NoETag bool CacheControl string `h:"Cache-Control"` ContentDisposition string `h:"Content-Disposition"` ContentEncoding string `h:"Content-Encoding"` ContentLength int64 `h:"Content-Length"` ContentType string `h:"Content-Type"` CopyFrom string `h:"X-Copy-From"` DeleteAfter int `h:"X-Delete-After"` DeleteAt int `h:"X-Delete-At"` DetectContentType string `h:"X-Detect-Content-Type"` ETag string `h:"ETag"` IfNoneMatch string `h:"If-None-Match"` ObjectManifest string `h:"X-Object-Manifest"` TransferEncoding string `h:"Transfer-Encoding"` Expires string `q:"expires"` MultipartManifest string `q:"multipart-manifest"` Signature string `q:"signature"` } // ToObjectCreateParams formats a CreateOpts into a query string and map of // headers. func (opts CreateOpts) ToObjectCreateParams() (io.Reader, map[string]string, string, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return nil, nil, "", err } h, err := gophercloud.BuildHeaders(opts) if err != nil { return nil, nil, "", err } for k, v := range opts.Metadata { h["X-Object-Meta-"+k] = v } if opts.NoETag { delete(h, "etag") return opts.Content, h, q.String(), nil } if h["ETag"] != "" { return opts.Content, h, q.String(), nil } // When we're dealing with big files an io.ReadSeeker allows us to efficiently calculate // the md5 sum. An io.Reader is only readable once which means we have to copy the entire // file content into memory first. readSeeker, isReadSeeker := opts.Content.(io.ReadSeeker) if !isReadSeeker { data, err := ioutil.ReadAll(opts.Content) if err != nil { return nil, nil, "", err } readSeeker = bytes.NewReader(data) } hash := md5.New() // io.Copy into md5 is very efficient as it's done in small chunks. if _, err := io.Copy(hash, readSeeker); err != nil { return nil, nil, "", err } readSeeker.Seek(0, io.SeekStart) h["ETag"] = fmt.Sprintf("%x", hash.Sum(nil)) return readSeeker, h, q.String(), nil } // Create is a function that creates a new object or replaces an existing // object. If the returned response's ETag header fails to match the local // checksum, the failed request will automatically be retried up to a maximum // of 3 times. func Create(c *gophercloud.ServiceClient, containerName, objectName string, opts CreateOptsBuilder) (r CreateResult) { url := createURL(c, containerName, objectName) h := make(map[string]string) var b io.Reader if opts != nil { tmpB, headers, query, err := opts.ToObjectCreateParams() if err != nil { r.Err = err return } for k, v := range headers { h[k] = v } url += query b = tmpB } resp, err := c.Put(url, nil, nil, &gophercloud.RequestOpts{ RawBody: b, MoreHeaders: h, }) r.Err = err if resp != nil { r.Header = resp.Header } return } // CopyOptsBuilder allows extensions to add additional parameters to the // Copy request. type CopyOptsBuilder interface { ToObjectCopyMap() (map[string]string, error) } // CopyOpts is a structure that holds parameters for copying one object to // another. type CopyOpts struct { Metadata map[string]string ContentDisposition string `h:"Content-Disposition"` ContentEncoding string `h:"Content-Encoding"` ContentType string `h:"Content-Type"` Destination string `h:"Destination" required:"true"` } // ToObjectCopyMap formats a CopyOpts into a map of headers. func (opts CopyOpts) ToObjectCopyMap() (map[string]string, error) { h, err := gophercloud.BuildHeaders(opts) if err != nil { return nil, err } for k, v := range opts.Metadata { h["X-Object-Meta-"+k] = v } return h, nil } // Copy is a function that copies one object to another. func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts CopyOptsBuilder) (r CopyResult) { h := make(map[string]string) headers, err := opts.ToObjectCopyMap() if err != nil { r.Err = err return } for k, v := range headers { h[k] = v } url := copyURL(c, containerName, objectName) resp, err := c.Request("COPY", url, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, }) if resp != nil { r.Header = resp.Header } r.Err = err return } // DeleteOptsBuilder allows extensions to add additional parameters to the // Delete request. type DeleteOptsBuilder interface { ToObjectDeleteQuery() (string, error) } // DeleteOpts is a structure that holds parameters for deleting an object. type DeleteOpts struct { MultipartManifest string `q:"multipart-manifest"` } // ToObjectDeleteQuery formats a DeleteOpts into a query string. func (opts DeleteOpts) ToObjectDeleteQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // Delete is a function that deletes an object. func Delete(c *gophercloud.ServiceClient, containerName, objectName string, opts DeleteOptsBuilder) (r DeleteResult) { url := deleteURL(c, containerName, objectName) if opts != nil { query, err := opts.ToObjectDeleteQuery() if err != nil { r.Err = err return } url += query } resp, err := c.Delete(url, nil) if resp != nil { r.Header = resp.Header } r.Err = err return } // GetOptsBuilder allows extensions to add additional parameters to the // Get request. type GetOptsBuilder interface { ToObjectGetParams() (map[string]string, string, error) } // GetOpts is a structure that holds parameters for getting an object's // metadata. type GetOpts struct { Newest bool `h:"X-Newest"` Expires string `q:"expires"` Signature string `q:"signature"` } // ToObjectGetParams formats a GetOpts into a query string and a map of headers. func (opts GetOpts) ToObjectGetParams() (map[string]string, string, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return nil, "", err } h, err := gophercloud.BuildHeaders(opts) if err != nil { return nil, q.String(), err } return h, q.String(), nil } // Get is a function that retrieves the metadata of an object. To extract just // the custom metadata, pass the GetResult response to the ExtractMetadata // function. func Get(c *gophercloud.ServiceClient, containerName, objectName string, opts GetOptsBuilder) (r GetResult) { url := getURL(c, containerName, objectName) h := make(map[string]string) if opts != nil { headers, query, err := opts.ToObjectGetParams() if err != nil { r.Err = err return } for k, v := range headers { h[k] = v } url += query } resp, err := c.Head(url, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{200, 204}, }) if resp != nil { r.Header = resp.Header } r.Err = err return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToObjectUpdateMap() (map[string]string, error) } // UpdateOpts is a structure that holds parameters for updating, creating, or // deleting an object's metadata. type UpdateOpts struct { Metadata map[string]string ContentDisposition string `h:"Content-Disposition"` ContentEncoding string `h:"Content-Encoding"` ContentType string `h:"Content-Type"` DeleteAfter int `h:"X-Delete-After"` DeleteAt int `h:"X-Delete-At"` DetectContentType bool `h:"X-Detect-Content-Type"` } // ToObjectUpdateMap formats a UpdateOpts into a map of headers. func (opts UpdateOpts) ToObjectUpdateMap() (map[string]string, error) { h, err := gophercloud.BuildHeaders(opts) if err != nil { return nil, err } for k, v := range opts.Metadata { h["X-Object-Meta-"+k] = v } return h, nil } // Update is a function that creates, updates, or deletes an object's metadata. func Update(c *gophercloud.ServiceClient, containerName, objectName string, opts UpdateOptsBuilder) (r UpdateResult) { h := make(map[string]string) if opts != nil { headers, err := opts.ToObjectUpdateMap() if err != nil { r.Err = err return } for k, v := range headers { h[k] = v } } url := updateURL(c, containerName, objectName) resp, err := c.Post(url, nil, nil, &gophercloud.RequestOpts{ MoreHeaders: h, }) if resp != nil { r.Header = resp.Header } r.Err = err return } // HTTPMethod represents an HTTP method string (e.g. "GET"). type HTTPMethod string var ( // GET represents an HTTP "GET" method. GET HTTPMethod = "GET" // POST represents an HTTP "POST" method. POST HTTPMethod = "POST" ) // CreateTempURLOpts are options for creating a temporary URL for an object. type CreateTempURLOpts struct { // (REQUIRED) Method is the HTTP method to allow for users of the temp URL. // Valid values are "GET" and "POST". Method HTTPMethod // (REQUIRED) TTL is the number of seconds the temp URL should be active. TTL int // (Optional) Split is the string on which to split the object URL. Since only // the object path is used in the hash, the object URL needs to be parsed. If // empty, the default OpenStack URL split point will be used ("/v1/"). Split string } // CreateTempURL is a function for creating a temporary URL for an object. It // allows users to have "GET" or "POST" access to a particular tenant's object // for a limited amount of time. func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName string, opts CreateTempURLOpts) (string, error) { if opts.Split == "" { opts.Split = "/v1/" } duration := time.Duration(opts.TTL) * time.Second expiry := time.Now().Add(duration).Unix() getHeader, err := accounts.Get(c, nil).Extract() if err != nil { return "", err } secretKey := []byte(getHeader.TempURLKey) url := getURL(c, containerName, objectName) splitPath := strings.Split(url, opts.Split) baseURL, objectPath := splitPath[0], splitPath[1] objectPath = opts.Split + objectPath body := fmt.Sprintf("%s\n%d\n%s", opts.Method, expiry, objectPath) hash := hmac.New(sha1.New, secretKey) hash.Write([]byte(body)) hexsum := fmt.Sprintf("%x", hash.Sum(nil)) return fmt.Sprintf("%s%s?temp_url_sig=%s&temp_url_expires=%d", baseURL, objectPath, hexsum, expiry), nil } results.go000066400000000000000000000346701335005366400343400ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/objectspackage objects import ( "encoding/json" "fmt" "io" "io/ioutil" "net/url" "strconv" "strings" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Object is a structure that holds information related to a storage object. type Object struct { // Bytes is the total number of bytes that comprise the object. Bytes int64 `json:"bytes"` // ContentType is the content type of the object. ContentType string `json:"content_type"` // Hash represents the MD5 checksum value of the object's content. Hash string `json:"hash"` // LastModified is the time the object was last modified. LastModified time.Time `json:"-"` // Name is the unique name for the object. Name string `json:"name"` // Subdir denotes if the result contains a subdir. Subdir string `json:"subdir"` } func (r *Object) UnmarshalJSON(b []byte) error { type tmp Object var s *struct { tmp LastModified string `json:"last_modified"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Object(s.tmp) if s.LastModified != "" { t, err := time.Parse(gophercloud.RFC3339MilliNoZ, s.LastModified) if err != nil { t, err = time.Parse(gophercloud.RFC3339Milli, s.LastModified) if err != nil { return err } } r.LastModified = t } return nil } // ObjectPage is a single page of objects that is returned from a call to the // List function. type ObjectPage struct { pagination.MarkerPageBase } // IsEmpty returns true if a ListResult contains no object names. func (r ObjectPage) IsEmpty() (bool, error) { names, err := ExtractNames(r) return len(names) == 0, err } // LastMarker returns the last object name in a ListResult. func (r ObjectPage) LastMarker() (string, error) { return extractLastMarker(r) } // ExtractInfo is a function that takes a page of objects and returns their // full information. func ExtractInfo(r pagination.Page) ([]Object, error) { var s []Object err := (r.(ObjectPage)).ExtractInto(&s) return s, err } // ExtractNames is a function that takes a page of objects and returns only // their names. func ExtractNames(r pagination.Page) ([]string, error) { casted := r.(ObjectPage) ct := casted.Header.Get("Content-Type") switch { case strings.HasPrefix(ct, "application/json"): parsed, err := ExtractInfo(r) if err != nil { return nil, err } names := make([]string, 0, len(parsed)) for _, object := range parsed { if object.Subdir != "" { names = append(names, object.Subdir) } else { names = append(names, object.Name) } } return names, nil case strings.HasPrefix(ct, "text/plain"): names := make([]string, 0, 50) body := string(r.(ObjectPage).Body.([]uint8)) for _, name := range strings.Split(body, "\n") { if len(name) > 0 { names = append(names, name) } } return names, nil case strings.HasPrefix(ct, "text/html"): return []string{}, nil default: return nil, fmt.Errorf("Cannot extract names from response with content-type: [%s]", ct) } } // DownloadHeader represents the headers returned in the response from a // Download request. type DownloadHeader struct { AcceptRanges string `json:"Accept-Ranges"` ContentDisposition string `json:"Content-Disposition"` ContentEncoding string `json:"Content-Encoding"` ContentLength int64 `json:"-"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` DeleteAt time.Time `json:"-"` ETag string `json:"Etag"` LastModified time.Time `json:"-"` ObjectManifest string `json:"X-Object-Manifest"` StaticLargeObject bool `json:"-"` TransID string `json:"X-Trans-Id"` } func (r *DownloadHeader) UnmarshalJSON(b []byte) error { type tmp DownloadHeader var s struct { tmp ContentLength string `json:"Content-Length"` Date gophercloud.JSONRFC1123 `json:"Date"` DeleteAt gophercloud.JSONUnix `json:"X-Delete-At"` LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"` StaticLargeObject interface{} `json:"X-Static-Large-Object"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = DownloadHeader(s.tmp) switch s.ContentLength { case "": r.ContentLength = 0 default: r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) if err != nil { return err } } switch t := s.StaticLargeObject.(type) { case string: if t == "True" || t == "true" { r.StaticLargeObject = true } case bool: r.StaticLargeObject = t } r.Date = time.Time(s.Date) r.DeleteAt = time.Time(s.DeleteAt) r.LastModified = time.Time(s.LastModified) return nil } // DownloadResult is a *http.Response that is returned from a call to the // Download function. type DownloadResult struct { gophercloud.HeaderResult Body io.ReadCloser } // Extract will return a struct of headers returned from a call to Download. func (r DownloadResult) Extract() (*DownloadHeader, error) { var s *DownloadHeader err := r.ExtractInto(&s) return s, err } // ExtractContent is a function that takes a DownloadResult's io.Reader body // and reads all available data into a slice of bytes. Please be aware that due // the nature of io.Reader is forward-only - meaning that it can only be read // once and not rewound. You can recreate a reader from the output of this // function by using bytes.NewReader(downloadBytes) func (r *DownloadResult) ExtractContent() ([]byte, error) { if r.Err != nil { return nil, r.Err } defer r.Body.Close() body, err := ioutil.ReadAll(r.Body) if err != nil { return nil, err } r.Body.Close() return body, nil } // GetHeader represents the headers returned in the response from a Get request. type GetHeader struct { ContentDisposition string `json:"Content-Disposition"` ContentEncoding string `json:"Content-Encoding"` ContentLength int64 `json:"-"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` DeleteAt time.Time `json:"-"` ETag string `json:"Etag"` LastModified time.Time `json:"-"` ObjectManifest string `json:"X-Object-Manifest"` StaticLargeObject bool `json:"-"` TransID string `json:"X-Trans-Id"` } func (r *GetHeader) UnmarshalJSON(b []byte) error { type tmp GetHeader var s struct { tmp ContentLength string `json:"Content-Length"` Date gophercloud.JSONRFC1123 `json:"Date"` DeleteAt gophercloud.JSONUnix `json:"X-Delete-At"` LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"` StaticLargeObject interface{} `json:"X-Static-Large-Object"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = GetHeader(s.tmp) switch s.ContentLength { case "": r.ContentLength = 0 default: r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) if err != nil { return err } } switch t := s.StaticLargeObject.(type) { case string: if t == "True" || t == "true" { r.StaticLargeObject = true } case bool: r.StaticLargeObject = t } r.Date = time.Time(s.Date) r.DeleteAt = time.Time(s.DeleteAt) r.LastModified = time.Time(s.LastModified) return nil } // GetResult is a *http.Response that is returned from a call to the Get // function. type GetResult struct { gophercloud.HeaderResult } // Extract will return a struct of headers returned from a call to Get. func (r GetResult) Extract() (*GetHeader, error) { var s *GetHeader err := r.ExtractInto(&s) return s, err } // ExtractMetadata is a function that takes a GetResult (of type *http.Response) // and returns the custom metadata associated with the object. func (r GetResult) ExtractMetadata() (map[string]string, error) { if r.Err != nil { return nil, r.Err } metadata := make(map[string]string) for k, v := range r.Header { if strings.HasPrefix(k, "X-Object-Meta-") { key := strings.TrimPrefix(k, "X-Object-Meta-") metadata[key] = v[0] } } return metadata, nil } // CreateHeader represents the headers returned in the response from a // Create request. type CreateHeader struct { ContentLength int64 `json:"-"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` ETag string `json:"Etag"` LastModified time.Time `json:"-"` TransID string `json:"X-Trans-Id"` } func (r *CreateHeader) UnmarshalJSON(b []byte) error { type tmp CreateHeader var s struct { tmp ContentLength string `json:"Content-Length"` Date gophercloud.JSONRFC1123 `json:"Date"` LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = CreateHeader(s.tmp) switch s.ContentLength { case "": r.ContentLength = 0 default: r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) if err != nil { return err } } r.Date = time.Time(s.Date) r.LastModified = time.Time(s.LastModified) return nil } // CreateResult represents the result of a create operation. type CreateResult struct { checksum string gophercloud.HeaderResult } // Extract will return a struct of headers returned from a call to Create. func (r CreateResult) Extract() (*CreateHeader, error) { //if r.Header.Get("ETag") != fmt.Sprintf("%x", localChecksum) { // return nil, ErrWrongChecksum{} //} var s *CreateHeader err := r.ExtractInto(&s) return s, err } // UpdateHeader represents the headers returned in the response from a // Update request. type UpdateHeader struct { ContentLength int64 `json:"-"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` TransID string `json:"X-Trans-Id"` } func (r *UpdateHeader) UnmarshalJSON(b []byte) error { type tmp UpdateHeader var s struct { tmp ContentLength string `json:"Content-Length"` Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = UpdateHeader(s.tmp) switch s.ContentLength { case "": r.ContentLength = 0 default: r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) if err != nil { return err } } r.Date = time.Time(s.Date) return nil } // UpdateResult represents the result of an update operation. type UpdateResult struct { gophercloud.HeaderResult } // Extract will return a struct of headers returned from a call to Update. func (r UpdateResult) Extract() (*UpdateHeader, error) { var s *UpdateHeader err := r.ExtractInto(&s) return s, err } // DeleteHeader represents the headers returned in the response from a // Delete request. type DeleteHeader struct { ContentLength int64 `json:"-"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` TransID string `json:"X-Trans-Id"` } func (r *DeleteHeader) UnmarshalJSON(b []byte) error { type tmp DeleteHeader var s struct { tmp ContentLength string `json:"Content-Length"` Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = DeleteHeader(s.tmp) switch s.ContentLength { case "": r.ContentLength = 0 default: r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) if err != nil { return err } } r.Date = time.Time(s.Date) return nil } // DeleteResult represents the result of a delete operation. type DeleteResult struct { gophercloud.HeaderResult } // Extract will return a struct of headers returned from a call to Delete. func (r DeleteResult) Extract() (*DeleteHeader, error) { var s *DeleteHeader err := r.ExtractInto(&s) return s, err } // CopyHeader represents the headers returned in the response from a // Copy request. type CopyHeader struct { ContentLength int64 `json:"-"` ContentType string `json:"Content-Type"` CopiedFrom string `json:"X-Copied-From"` CopiedFromLastModified time.Time `json:"-"` Date time.Time `json:"-"` ETag string `json:"Etag"` LastModified time.Time `json:"-"` TransID string `json:"X-Trans-Id"` } func (r *CopyHeader) UnmarshalJSON(b []byte) error { type tmp CopyHeader var s struct { tmp ContentLength string `json:"Content-Length"` CopiedFromLastModified gophercloud.JSONRFC1123 `json:"X-Copied-From-Last-Modified"` Date gophercloud.JSONRFC1123 `json:"Date"` LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = CopyHeader(s.tmp) switch s.ContentLength { case "": r.ContentLength = 0 default: r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) if err != nil { return err } } r.Date = time.Time(s.Date) r.CopiedFromLastModified = time.Time(s.CopiedFromLastModified) r.LastModified = time.Time(s.LastModified) return nil } // CopyResult represents the result of a copy operation. type CopyResult struct { gophercloud.HeaderResult } // Extract will return a struct of headers returned from a call to Copy. func (r CopyResult) Extract() (*CopyHeader, error) { var s *CopyHeader err := r.ExtractInto(&s) return s, err } // extractLastMarker is a function that takes a page of objects and returns the // marker for the page. This can either be a subdir or the last object's name. func extractLastMarker(r pagination.Page) (string, error) { casted := r.(ObjectPage) // If a delimiter was requested, check if a subdir exists. queryParams, err := url.ParseQuery(casted.URL.RawQuery) if err != nil { return "", err } var delimeter bool if v, ok := queryParams["delimiter"]; ok && len(v) > 0 { delimeter = true } ct := casted.Header.Get("Content-Type") switch { case strings.HasPrefix(ct, "application/json"): parsed, err := ExtractInfo(r) if err != nil { return "", err } var lastObject Object if len(parsed) > 0 { lastObject = parsed[len(parsed)-1] } if !delimeter { return lastObject.Name, nil } if lastObject.Name != "" { return lastObject.Name, nil } return lastObject.Subdir, nil case strings.HasPrefix(ct, "text/plain"): names := make([]string, 0, 50) body := string(r.(ObjectPage).Body.([]uint8)) for _, name := range strings.Split(body, "\n") { if len(name) > 0 { names = append(names, name) } } return names[len(names)-1], err case strings.HasPrefix(ct, "text/html"): return "", nil default: return "", fmt.Errorf("Cannot extract names from response with content-type: [%s]", ct) } } testing/000077500000000000000000000000001335005366400337535ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/objectsdoc.go000066400000000000000000000000461335005366400350470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/objects/testing// objects unit tests package testing fixtures.go000066400000000000000000000217211335005366400361560ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/objects/testingpackage testing import ( "crypto/md5" "fmt" "io" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) // HandleDownloadObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that // responds with a `Download` response. func HandleDownloadObjectSuccessfully(t *testing.T) { th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Date", "Wed, 10 Nov 2009 23:00:00 GMT") w.Header().Set("X-Static-Large-Object", "True") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "Successful download with Gophercloud") }) } // ExpectedListInfo is the result expected from a call to `List` when full // info is requested. var ExpectedListInfo = []objects.Object{ { Hash: "451e372e48e0f6b1114fa0724aa79fa1", LastModified: time.Date(2016, time.August, 17, 22, 11, 58, 602650000, time.UTC), //"2016-08-17T22:11:58.602650" Bytes: 14, Name: "goodbye", ContentType: "application/octet-stream", }, { Hash: "451e372e48e0f6b1114fa0724aa79fa1", LastModified: time.Date(2016, time.August, 17, 22, 11, 58, 602650000, time.UTC), Bytes: 14, Name: "hello", ContentType: "application/octet-stream", }, } // ExpectedListSubdir is the result expected from a call to `List` when full // info is requested. var ExpectedListSubdir = []objects.Object{ { Subdir: "directory/", }, } // ExpectedListNames is the result expected from a call to `List` when just // object names are requested. var ExpectedListNames = []string{"hello", "goodbye"} // HandleListObjectsInfoSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that // responds with a `List` response when full info is requested. func HandleListObjectsInfoSuccessfully(t *testing.T) { th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, `[ { "hash": "451e372e48e0f6b1114fa0724aa79fa1", "last_modified": "2016-08-17T22:11:58.602650", "bytes": 14, "name": "goodbye", "content_type": "application/octet-stream" }, { "hash": "451e372e48e0f6b1114fa0724aa79fa1", "last_modified": "2016-08-17T22:11:58.602650", "bytes": 14, "name": "hello", "content_type": "application/octet-stream" } ]`) case "hello": fmt.Fprintf(w, `[]`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) } // HandleListSubdirSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that // responds with a `List` response when full info is requested. func HandleListSubdirSuccessfully(t *testing.T) { th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, `[ { "subdir": "directory/" } ]`) case "directory/": fmt.Fprintf(w, `[]`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) } // HandleListObjectNamesSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that // responds with a `List` response when only object names are requested. func HandleListObjectNamesSuccessfully(t *testing.T) { th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "text/plain") w.Header().Set("Content-Type", "text/plain") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, "hello\ngoodbye\n") case "goodbye": fmt.Fprintf(w, "") default: t.Fatalf("Unexpected marker: [%s]", marker) } }) } // HandleCreateTextObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux // that responds with a `Create` response. A Content-Type of "text/plain" is expected. func HandleCreateTextObjectSuccessfully(t *testing.T, content string) { th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "text/plain") th.TestHeader(t, r, "Accept", "application/json") hash := md5.New() io.WriteString(hash, content) localChecksum := hash.Sum(nil) w.Header().Set("ETag", fmt.Sprintf("%x", localChecksum)) w.WriteHeader(http.StatusCreated) }) } // HandleCreateTextWithCacheControlSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler // mux that responds with a `Create` response. A Cache-Control of `max-age="3600", public` is expected. func HandleCreateTextWithCacheControlSuccessfully(t *testing.T, content string) { th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Cache-Control", `max-age="3600", public`) th.TestHeader(t, r, "Accept", "application/json") hash := md5.New() io.WriteString(hash, content) localChecksum := hash.Sum(nil) w.Header().Set("ETag", fmt.Sprintf("%x", localChecksum)) w.WriteHeader(http.StatusCreated) }) } // HandleCreateTypelessObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler // mux that responds with a `Create` response. No Content-Type header may be present in the request, so that server- // side content-type detection will be triggered properly. func HandleCreateTypelessObjectSuccessfully(t *testing.T, content string) { th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") if contentType, present := r.Header["Content-Type"]; present { t.Errorf("Expected Content-Type header to be omitted, but was %#v", contentType) } hash := md5.New() io.WriteString(hash, content) localChecksum := hash.Sum(nil) w.Header().Set("ETag", fmt.Sprintf("%x", localChecksum)) w.WriteHeader(http.StatusCreated) }) } // HandleCopyObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that // responds with a `Copy` response. func HandleCopyObjectSuccessfully(t *testing.T) { th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "COPY") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "Destination", "/newTestContainer/newTestObject") w.WriteHeader(http.StatusCreated) }) } // HandleDeleteObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that // responds with a `Delete` response. func HandleDeleteObjectSuccessfully(t *testing.T) { th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.WriteHeader(http.StatusNoContent) }) } // HandleUpdateObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that // responds with a `Update` response. func HandleUpdateObjectSuccessfully(t *testing.T) { th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Object-Meta-Gophercloud-Test", "objects") w.WriteHeader(http.StatusAccepted) }) } // HandleGetObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that // responds with a `Get` response. func HandleGetObjectSuccessfully(t *testing.T) { th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "HEAD") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Add("X-Object-Meta-Gophercloud-Test", "objects") w.Header().Add("X-Static-Large-Object", "true") w.WriteHeader(http.StatusNoContent) }) } requests_test.go000066400000000000000000000204501335005366400372150ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/objects/testingpackage testing import ( "bytes" "crypto/md5" "fmt" "io" "io/ioutil" "strings" "testing" "time" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) var ( loc, _ = time.LoadLocation("GMT") ) func TestDownloadReader(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDownloadObjectSuccessfully(t) response := objects.Download(fake.ServiceClient(), "testContainer", "testObject", nil) defer response.Body.Close() // Check reader buf := bytes.NewBuffer(make([]byte, 0)) io.CopyN(buf, response.Body, 10) th.CheckEquals(t, "Successful", string(buf.Bytes())) } func TestDownloadExtraction(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDownloadObjectSuccessfully(t) response := objects.Download(fake.ServiceClient(), "testContainer", "testObject", nil) // Check []byte extraction bytes, err := response.ExtractContent() th.AssertNoErr(t, err) th.CheckEquals(t, "Successful download with Gophercloud", string(bytes)) expected := &objects.DownloadHeader{ ContentLength: 36, ContentType: "text/plain; charset=utf-8", Date: time.Date(2009, time.November, 10, 23, 0, 0, 0, loc), StaticLargeObject: true, } actual, err := response.Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } func TestListObjectInfo(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListObjectsInfoSuccessfully(t) count := 0 options := &objects.ListOpts{Full: true} err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := objects.ExtractInfo(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedListInfo, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListObjectSubdir(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSubdirSuccessfully(t) count := 0 options := &objects.ListOpts{Full: true, Prefix: "", Delimiter: "/"} err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := objects.ExtractInfo(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedListSubdir, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListObjectNames(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListObjectNamesSuccessfully(t) // Check without delimiter. count := 0 options := &objects.ListOpts{Full: false} err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := objects.ExtractNames(page) if err != nil { t.Errorf("Failed to extract container names: %v", err) return false, err } th.CheckDeepEquals(t, ExpectedListNames, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) // Check with delimiter. count = 0 options = &objects.ListOpts{Full: false, Delimiter: "/"} err = objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := objects.ExtractNames(page) if err != nil { t.Errorf("Failed to extract container names: %v", err) return false, err } th.CheckDeepEquals(t, ExpectedListNames, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestCreateObject(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() content := "Did gyre and gimble in the wabe" HandleCreateTextObjectSuccessfully(t, content) options := &objects.CreateOpts{ContentType: "text/plain", Content: strings.NewReader(content)} res := objects.Create(fake.ServiceClient(), "testContainer", "testObject", options) th.AssertNoErr(t, res.Err) } func TestCreateObjectWithCacheControl(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() content := "All mimsy were the borogoves" HandleCreateTextWithCacheControlSuccessfully(t, content) options := &objects.CreateOpts{ CacheControl: `max-age="3600", public`, Content: strings.NewReader(content), } res := objects.Create(fake.ServiceClient(), "testContainer", "testObject", options) th.AssertNoErr(t, res.Err) } func TestCreateObjectWithoutContentType(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() content := "The sky was the color of television, tuned to a dead channel." HandleCreateTypelessObjectSuccessfully(t, content) res := objects.Create(fake.ServiceClient(), "testContainer", "testObject", &objects.CreateOpts{Content: strings.NewReader(content)}) th.AssertNoErr(t, res.Err) } /* func TestErrorIsRaisedForChecksumMismatch(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("ETag", "acbd18db4cc2f85cedef654fccc4a4d8") w.WriteHeader(http.StatusCreated) }) content := strings.NewReader("The sky was the color of television, tuned to a dead channel.") res := Create(fake.ServiceClient(), "testContainer", "testObject", &CreateOpts{Content: content}) err := fmt.Errorf("Local checksum does not match API ETag header") th.AssertDeepEquals(t, err, res.Err) } */ func TestCopyObject(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCopyObjectSuccessfully(t) options := &objects.CopyOpts{Destination: "/newTestContainer/newTestObject"} res := objects.Copy(fake.ServiceClient(), "testContainer", "testObject", options) th.AssertNoErr(t, res.Err) } func TestDeleteObject(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteObjectSuccessfully(t) res := objects.Delete(fake.ServiceClient(), "testContainer", "testObject", nil) th.AssertNoErr(t, res.Err) } func TestUpateObjectMetadata(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateObjectSuccessfully(t) options := &objects.UpdateOpts{Metadata: map[string]string{"Gophercloud-Test": "objects"}} res := objects.Update(fake.ServiceClient(), "testContainer", "testObject", options) th.AssertNoErr(t, res.Err) } func TestGetObject(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetObjectSuccessfully(t) expected := map[string]string{"Gophercloud-Test": "objects"} actual, err := objects.Get(fake.ServiceClient(), "testContainer", "testObject", nil).ExtractMetadata() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) getOpts := objects.GetOpts{ Newest: true, } actualHeaders, err := objects.Get(fake.ServiceClient(), "testContainer", "testObject", getOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, actualHeaders.StaticLargeObject, true) } func TestETag(t *testing.T) { content := "some example object" createOpts := objects.CreateOpts{ Content: strings.NewReader(content), NoETag: true, } _, headers, _, err := createOpts.ToObjectCreateParams() th.AssertNoErr(t, err) _, ok := headers["ETag"] th.AssertEquals(t, ok, false) hash := md5.New() io.WriteString(hash, content) localChecksum := fmt.Sprintf("%x", hash.Sum(nil)) createOpts = objects.CreateOpts{ Content: strings.NewReader(content), ETag: localChecksum, } _, headers, _, err = createOpts.ToObjectCreateParams() th.AssertNoErr(t, err) th.AssertEquals(t, headers["ETag"], localChecksum) } func TestObjectCreateParamsWithoutSeek(t *testing.T) { content := "I do not implement Seek()" buf := bytes.NewBuffer([]byte(content)) createOpts := objects.CreateOpts{Content: buf} reader, headers, _, err := createOpts.ToObjectCreateParams() th.AssertNoErr(t, err) _, ok := reader.(io.ReadSeeker) th.AssertEquals(t, ok, true) c, err := ioutil.ReadAll(reader) th.AssertNoErr(t, err) th.AssertEquals(t, content, string(c)) _, ok = headers["ETag"] th.AssertEquals(t, true, ok) } func TestObjectCreateParamsWithSeek(t *testing.T) { content := "I implement Seek()" createOpts := objects.CreateOpts{Content: strings.NewReader(content)} reader, headers, _, err := createOpts.ToObjectCreateParams() th.AssertNoErr(t, err) _, ok := reader.(io.ReadSeeker) th.AssertEquals(t, ok, true) c, err := ioutil.ReadAll(reader) th.AssertNoErr(t, err) th.AssertEquals(t, content, string(c)) _, ok = headers["ETag"] th.AssertEquals(t, true, ok) } urls.go000066400000000000000000000016001335005366400336070ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/objectspackage objects import ( "github.com/gophercloud/gophercloud" ) func listURL(c *gophercloud.ServiceClient, container string) string { return c.ServiceURL(container) } func copyURL(c *gophercloud.ServiceClient, container, object string) string { return c.ServiceURL(container, object) } func createURL(c *gophercloud.ServiceClient, container, object string) string { return copyURL(c, container, object) } func getURL(c *gophercloud.ServiceClient, container, object string) string { return copyURL(c, container, object) } func deleteURL(c *gophercloud.ServiceClient, container, object string) string { return copyURL(c, container, object) } func downloadURL(c *gophercloud.ServiceClient, container, object string) string { return copyURL(c, container, object) } func updateURL(c *gophercloud.ServiceClient, container, object string) string { return copyURL(c, container, object) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/swauth/000077500000000000000000000000001335005366400322375ustar00rootroot00000000000000doc.go000066400000000000000000000004561335005366400332610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/swauth/* Package swauth implements Swift's built-in authentication. Example to Authenticate with swauth authOpts := swauth.AuthOpts{ User: "project:user", Key: "password", } swiftClient, err := swauth.NewObjectStorageV1(providerClient, authOpts) if err != nil { panic(err) } */ package swauth requests.go000066400000000000000000000033171335005366400343660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/swauthpackage swauth import "github.com/gophercloud/gophercloud" // AuthOptsBuilder describes struct types that can be accepted by the Auth call. type AuthOptsBuilder interface { ToAuthOptsMap() (map[string]string, error) } // AuthOpts specifies an authentication request. type AuthOpts struct { // User is an Swauth-based username in username:tenant format. User string `h:"X-Auth-User" required:"true"` // Key is a secret/password to authenticate the User with. Key string `h:"X-Auth-Key" required:"true"` } // ToAuthOptsMap formats an AuthOpts structure into a request body. func (opts AuthOpts) ToAuthOptsMap() (map[string]string, error) { return gophercloud.BuildHeaders(opts) } // Auth performs an authentication request for a Swauth-based user. func Auth(c *gophercloud.ProviderClient, opts AuthOptsBuilder) (r GetAuthResult) { h := make(map[string]string) if opts != nil { headers, err := opts.ToAuthOptsMap() if err != nil { r.Err = err return } for k, v := range headers { h[k] = v } } resp, err := c.Request("GET", getURL(c), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{200}, }) if resp != nil { r.Header = resp.Header } r.Err = err return r } // NewObjectStorageV1 creates a Swauth-authenticated *gophercloud.ServiceClient // client that can issue ObjectStorage-based API calls. func NewObjectStorageV1(pc *gophercloud.ProviderClient, authOpts AuthOpts) (*gophercloud.ServiceClient, error) { auth, err := Auth(pc, authOpts).Extract() if err != nil { return nil, err } swiftClient := &gophercloud.ServiceClient{ ProviderClient: pc, Endpoint: gophercloud.NormalizeURL(auth.StorageURL), } swiftClient.TokenID = auth.Token return swiftClient, nil } results.go000066400000000000000000000013331335005366400342100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/swauthpackage swauth import ( "github.com/gophercloud/gophercloud" ) // GetAuthResult contains the response from the Auth request. Call its Extract // method to interpret it as an AuthResult. type GetAuthResult struct { gophercloud.HeaderResult } // AuthResult contains the authentication information from a Swauth // authentication request. type AuthResult struct { Token string `json:"X-Auth-Token"` StorageURL string `json:"X-Storage-Url"` CDNURL string `json:"X-CDN-Management-Url"` } // Extract is a method that attempts to interpret any Swauth authentication // response as a AuthResult struct. func (r GetAuthResult) Extract() (*AuthResult, error) { var s *AuthResult err := r.ExtractInto(&s) return s, err } testing/000077500000000000000000000000001335005366400336355ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/swauthdoc.go000066400000000000000000000000451335005366400347300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/swauth/testing// swauth unit tests package testing fixtures.go000066400000000000000000000015631335005366400360420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/swauth/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/swauth" th "github.com/gophercloud/gophercloud/testhelper" ) // AuthResult is the expected result of AuthOutput var AuthResult = swauth.AuthResult{ Token: "AUTH_tk6223e6071f8f4299aa334b48015484a1", StorageURL: "http://127.0.0.1:8080/v1/AUTH_test/", } // HandleAuthSuccessfully configures the test server to respond to an Auth request. func HandleAuthSuccessfully(t *testing.T, authOpts swauth.AuthOpts) { th.Mux.HandleFunc("/auth/v1.0", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-User", authOpts.User) th.TestHeader(t, r, "X-Auth-Key", authOpts.Key) w.Header().Add("X-Auth-Token", AuthResult.Token) w.Header().Add("X-Storage-Url", AuthResult.StorageURL) fmt.Fprintf(w, "") }) } requests_test.go000066400000000000000000000014671335005366400371060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/swauth/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/swauth" th "github.com/gophercloud/gophercloud/testhelper" ) func TestAuth(t *testing.T) { authOpts := swauth.AuthOpts{ User: "test:tester", Key: "testing", } th.SetupHTTP() defer th.TeardownHTTP() HandleAuthSuccessfully(t, authOpts) providerClient, err := openstack.NewClient(th.Endpoint()) th.AssertNoErr(t, err) swiftClient, err := swauth.NewObjectStorageV1(providerClient, authOpts) th.AssertNoErr(t, err) th.AssertEquals(t, swiftClient.TokenID, AuthResult.Token) } func TestBadAuth(t *testing.T) { authOpts := swauth.AuthOpts{} _, err := authOpts.ToAuthOptsMap() if err == nil { t.Fatalf("Expected an error due to missing auth options") } } urls.go000066400000000000000000000002301335005366400334670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/objectstorage/v1/swauthpackage swauth import "github.com/gophercloud/gophercloud" func getURL(c *gophercloud.ProviderClient) string { return c.IdentityBase + "auth/v1.0" } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/000077500000000000000000000000001335005366400304275ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/000077500000000000000000000000001335005366400307555ustar00rootroot00000000000000apiversions/000077500000000000000000000000001335005366400332405ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1doc.go000066400000000000000000000003271335005366400343360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/apiversions// Package apiversions provides information and interaction with the different // API versions for the OpenStack Heat service. This functionality is not // restricted to this particular version. package apiversions requests.go000066400000000000000000000006431335005366400354450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/apiversionspackage apiversions import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ListVersions lists all the Neutron API versions available to end-users func ListVersions(c *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(c, apiVersionsURL(c), func(r pagination.PageResult) pagination.Page { return APIVersionPage{pagination.SinglePageBase(r)} }) } results.go000066400000000000000000000020661335005366400352740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/apiversionspackage apiversions import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // APIVersion represents an API version for Neutron. It contains the status of // the API, and its unique ID. type APIVersion struct { Status string `json:"status"` ID string `json:"id"` Links []gophercloud.Link `json:"links"` } // APIVersionPage is the page returned by a pager when traversing over a // collection of API versions. type APIVersionPage struct { pagination.SinglePageBase } // IsEmpty checks whether an APIVersionPage struct is empty. func (r APIVersionPage) IsEmpty() (bool, error) { is, err := ExtractAPIVersions(r) return len(is) == 0, err } // ExtractAPIVersions takes a collection page, extracts all of the elements, // and returns them a slice of APIVersion structs. It is effectively a cast. func ExtractAPIVersions(r pagination.Page) ([]APIVersion, error) { var s struct { APIVersions []APIVersion `json:"versions"` } err := (r.(APIVersionPage)).ExtractInto(&s) return s.APIVersions, err } testing/000077500000000000000000000000001335005366400347155ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/apiversionsdoc.go000066400000000000000000000000601335005366400360050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/apiversions/testing// orchestration_apiversions_v1 package testing requests_test.go000066400000000000000000000040011335005366400401510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/apiversions/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/apiversions" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListVersions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "versions": [ { "status": "CURRENT", "id": "v1.0", "links": [ { "href": "http://23.253.228.211:8000/v1", "rel": "self" } ] } ] }`) }) count := 0 apiversions.ListVersions(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := apiversions.ExtractAPIVersions(page) if err != nil { t.Errorf("Failed to extract API versions: %v", err) return false, err } expected := []apiversions.APIVersion{ { Status: "CURRENT", ID: "v1.0", Links: []gophercloud.Link{ gophercloud.Link{ Href: "http://23.253.228.211:8000/v1", Rel: "self", }, }, }, } th.AssertDeepEquals(t, expected, actual) return true, nil }) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } } func TestNonJSONCannotBeExtractedIntoAPIVersions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }) apiversions.ListVersions(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { if _, err := apiversions.ExtractAPIVersions(page); err == nil { t.Fatalf("Expected error, got nil") } return true, nil }) } urls.go000066400000000000000000000002221335005366400345500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/apiversionspackage apiversions import "github.com/gophercloud/gophercloud" func apiVersionsURL(c *gophercloud.ServiceClient) string { return c.Endpoint } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/buildinfo/000077500000000000000000000000001335005366400327305ustar00rootroot00000000000000doc.go000066400000000000000000000001321335005366400337410ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/buildinfo// Package buildinfo provides build information about heat deployments. package buildinfo requests.go000066400000000000000000000003401335005366400350500ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/buildinfopackage buildinfo import "github.com/gophercloud/gophercloud" // Get retreives data for the given stack template. func Get(c *gophercloud.ServiceClient) (r GetResult) { _, r.Err = c.Get(getURL(c), &r.Body, nil) return } results.go000066400000000000000000000012271335005366400347030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/buildinfopackage buildinfo import ( "github.com/gophercloud/gophercloud" ) // Revision represents the API/Engine revision of a Heat deployment. type Revision struct { Revision string `json:"revision"` } // BuildInfo represents the build information for a Heat deployment. type BuildInfo struct { API Revision `json:"api"` Engine Revision `json:"engine"` } // GetResult represents the result of a Get operation. type GetResult struct { gophercloud.Result } // Extract returns a pointer to a BuildInfo object and is called after a // Get operation. func (r GetResult) Extract() (*BuildInfo, error) { var s *BuildInfo err := r.ExtractInto(&s) return s, err } testing/000077500000000000000000000000001335005366400343265ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/buildinfodoc.go000066400000000000000000000000561335005366400354230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/buildinfo/testing// orchestration_buildinfo_v1 package testing fixtures.go000066400000000000000000000021761335005366400365340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/buildinfo/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/buildinfo" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) // GetExpected represents the expected object from a Get request. var GetExpected = &buildinfo.BuildInfo{ API: buildinfo.Revision{ Revision: "2.4.5", }, Engine: buildinfo.Revision{ Revision: "1.2.1", }, } // GetOutput represents the response body from a Get request. const GetOutput = ` { "api": { "revision": "2.4.5" }, "engine": { "revision": "1.2.1" } }` // HandleGetSuccessfully creates an HTTP handler at `/build_info` // on the test handler mux that responds with a `Get` response. func HandleGetSuccessfully(t *testing.T, output string) { th.Mux.HandleFunc("/build_info", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, output) }) } requests_test.go000066400000000000000000000007651335005366400375770ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/buildinfo/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/buildinfo" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestGetTemplate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t, GetOutput) actual, err := buildinfo.Get(fake.ServiceClient()).Extract() th.AssertNoErr(t, err) expected := GetExpected th.AssertDeepEquals(t, expected, actual) } urls.go000066400000000000000000000002301335005366400341600ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/buildinfopackage buildinfo import "github.com/gophercloud/gophercloud" func getURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("build_info") } stackevents/000077500000000000000000000000001335005366400332305ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1doc.go000066400000000000000000000010071335005366400343220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackevents/* Package stackevents provides operations for finding, listing, and retrieving stack events. Stack events are events that take place on stacks such as updating and abandoning. Example for list events for a stack pages, err := stackevents.List(client, stack.Name, stack.ID, nil).AllPages() if err != nil { panic(err) } events, err := stackevents.ExtractEvents(pages) if err != nil { panic(err) } fmt.Println("Get Event List") fmt.Println(events) */ package stackevents requests.go000066400000000000000000000172351335005366400354420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackeventspackage stackevents import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Find retrieves stack events for the given stack name. func Find(c *gophercloud.ServiceClient, stackName string) (r FindResult) { _, r.Err = c.Get(findURL(c, stackName), &r.Body, nil) return } // SortDir is a type for specifying in which direction to sort a list of events. type SortDir string // SortKey is a type for specifying by which key to sort a list of events. type SortKey string // ResourceStatus is a type for specifying by which resource status to filter a // list of events. type ResourceStatus string // ResourceAction is a type for specifying by which resource action to filter a // list of events. type ResourceAction string var ( // ResourceStatusInProgress is used to filter a List request by the 'IN_PROGRESS' status. ResourceStatusInProgress ResourceStatus = "IN_PROGRESS" // ResourceStatusComplete is used to filter a List request by the 'COMPLETE' status. ResourceStatusComplete ResourceStatus = "COMPLETE" // ResourceStatusFailed is used to filter a List request by the 'FAILED' status. ResourceStatusFailed ResourceStatus = "FAILED" // ResourceActionCreate is used to filter a List request by the 'CREATE' action. ResourceActionCreate ResourceAction = "CREATE" // ResourceActionDelete is used to filter a List request by the 'DELETE' action. ResourceActionDelete ResourceAction = "DELETE" // ResourceActionUpdate is used to filter a List request by the 'UPDATE' action. ResourceActionUpdate ResourceAction = "UPDATE" // ResourceActionRollback is used to filter a List request by the 'ROLLBACK' action. ResourceActionRollback ResourceAction = "ROLLBACK" // ResourceActionSuspend is used to filter a List request by the 'SUSPEND' action. ResourceActionSuspend ResourceAction = "SUSPEND" // ResourceActionResume is used to filter a List request by the 'RESUME' action. ResourceActionResume ResourceAction = "RESUME" // ResourceActionAbandon is used to filter a List request by the 'ABANDON' action. ResourceActionAbandon ResourceAction = "ABANDON" // SortAsc is used to sort a list of stacks in ascending order. SortAsc SortDir = "asc" // SortDesc is used to sort a list of stacks in descending order. SortDesc SortDir = "desc" // SortName is used to sort a list of stacks by name. SortName SortKey = "name" // SortResourceType is used to sort a list of stacks by resource type. SortResourceType SortKey = "resource_type" // SortCreatedAt is used to sort a list of stacks by date created. SortCreatedAt SortKey = "created_at" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToStackEventListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Marker and Limit are used for pagination. type ListOpts struct { // The stack resource ID with which to start the listing. Marker string `q:"marker"` // Integer value for the limit of values to return. Limit int `q:"limit"` // Filters the event list by the specified ResourceAction. You can use this // filter multiple times to filter by multiple resource actions: CREATE, DELETE, // UPDATE, ROLLBACK, SUSPEND, RESUME or ADOPT. ResourceActions []ResourceAction `q:"resource_action"` // Filters the event list by the specified resource_status. You can use this // filter multiple times to filter by multiple resource statuses: IN_PROGRESS, // COMPLETE or FAILED. ResourceStatuses []ResourceStatus `q:"resource_status"` // Filters the event list by the specified resource_name. You can use this // filter multiple times to filter by multiple resource names. ResourceNames []string `q:"resource_name"` // Filters the event list by the specified resource_type. You can use this // filter multiple times to filter by multiple resource types: OS::Nova::Server, // OS::Cinder::Volume, and so on. ResourceTypes []string `q:"resource_type"` // Sorts the event list by: resource_type or created_at. SortKey SortKey `q:"sort_keys"` // The sort direction of the event list. Which is asc (ascending) or desc (descending). SortDir SortDir `q:"sort_dir"` } // ToStackEventListQuery formats a ListOpts into a query string. func (opts ListOpts) ToStackEventListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List makes a request against the API to list resources for the given stack. func List(client *gophercloud.ServiceClient, stackName, stackID string, opts ListOptsBuilder) pagination.Pager { url := listURL(client, stackName, stackID) if opts != nil { query, err := opts.ToStackEventListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { p := EventPage{pagination.MarkerPageBase{PageResult: r}} p.MarkerPageBase.Owner = p return p }) } // ListResourceEventsOptsBuilder allows extensions to add additional parameters to the // ListResourceEvents request. type ListResourceEventsOptsBuilder interface { ToResourceEventListQuery() (string, error) } // ListResourceEventsOpts allows the filtering and sorting of paginated resource events through // the API. Marker and Limit are used for pagination. type ListResourceEventsOpts struct { // The stack resource ID with which to start the listing. Marker string `q:"marker"` // Integer value for the limit of values to return. Limit int `q:"limit"` // Filters the event list by the specified ResourceAction. You can use this // filter multiple times to filter by multiple resource actions: CREATE, DELETE, // UPDATE, ROLLBACK, SUSPEND, RESUME or ADOPT. ResourceActions []string `q:"resource_action"` // Filters the event list by the specified resource_status. You can use this // filter multiple times to filter by multiple resource statuses: IN_PROGRESS, // COMPLETE or FAILED. ResourceStatuses []string `q:"resource_status"` // Filters the event list by the specified resource_name. You can use this // filter multiple times to filter by multiple resource names. ResourceNames []string `q:"resource_name"` // Filters the event list by the specified resource_type. You can use this // filter multiple times to filter by multiple resource types: OS::Nova::Server, // OS::Cinder::Volume, and so on. ResourceTypes []string `q:"resource_type"` // Sorts the event list by: resource_type or created_at. SortKey SortKey `q:"sort_keys"` // The sort direction of the event list. Which is asc (ascending) or desc (descending). SortDir SortDir `q:"sort_dir"` } // ToResourceEventListQuery formats a ListResourceEventsOpts into a query string. func (opts ListResourceEventsOpts) ToResourceEventListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // ListResourceEvents makes a request against the API to list resources for the given stack. func ListResourceEvents(client *gophercloud.ServiceClient, stackName, stackID, resourceName string, opts ListResourceEventsOptsBuilder) pagination.Pager { url := listResourceEventsURL(client, stackName, stackID, resourceName) if opts != nil { query, err := opts.ToResourceEventListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { p := EventPage{pagination.MarkerPageBase{PageResult: r}} p.MarkerPageBase.Owner = p return p }) } // Get retreives data for the given stack resource. func Get(c *gophercloud.ServiceClient, stackName, stackID, resourceName, eventID string) (r GetResult) { _, r.Err = c.Get(getURL(c, stackName, stackID, resourceName, eventID), &r.Body, nil) return } results.go000066400000000000000000000064171335005366400352700ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackeventspackage stackevents import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Event represents a stack event. type Event struct { // The name of the resource for which the event occurred. ResourceName string `json:"resource_name"` // The time the event occurred. Time time.Time `json:"-"` // The URLs to the event. Links []gophercloud.Link `json:"links"` // The logical ID of the stack resource. LogicalResourceID string `json:"logical_resource_id"` // The reason of the status of the event. ResourceStatusReason string `json:"resource_status_reason"` // The status of the event. ResourceStatus string `json:"resource_status"` // The physical ID of the stack resource. PhysicalResourceID string `json:"physical_resource_id"` // The event ID. ID string `json:"id"` // Properties of the stack resource. ResourceProperties map[string]interface{} `json:"resource_properties"` } func (r *Event) UnmarshalJSON(b []byte) error { type tmp Event var s struct { tmp Time string `json:"event_time"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Event(s.tmp) if s.Time != "" { t, err := time.Parse(time.RFC3339, s.Time) if err != nil { t, err = time.Parse(gophercloud.RFC3339NoZ, s.Time) if err != nil { return err } } r.Time = t } return nil } // FindResult represents the result of a Find operation. type FindResult struct { gophercloud.Result } // Extract returns a slice of Event objects and is called after a // Find operation. func (r FindResult) Extract() ([]Event, error) { var s struct { Events []Event `json:"events"` } err := r.ExtractInto(&s) return s.Events, err } // EventPage abstracts the raw results of making a List() request against the API. // As OpenStack extensions may freely alter the response bodies of structures returned to the client, you may only safely access the // data provided through the ExtractResources call. type EventPage struct { pagination.MarkerPageBase } // IsEmpty returns true if a page contains no Server results. func (r EventPage) IsEmpty() (bool, error) { events, err := ExtractEvents(r) return len(events) == 0, err } // LastMarker returns the last stack ID in a ListResult. func (r EventPage) LastMarker() (string, error) { events, err := ExtractEvents(r) if err != nil { return "", err } if len(events) == 0 { return "", nil } return events[len(events)-1].ID, nil } // ExtractEvents interprets the results of a single page from a List() call, producing a slice of Event entities. func ExtractEvents(r pagination.Page) ([]Event, error) { var s struct { Events []Event `json:"events"` } err := (r.(EventPage)).ExtractInto(&s) return s.Events, err } // ExtractResourceEvents interprets the results of a single page from a // ListResourceEvents() call, producing a slice of Event entities. func ExtractResourceEvents(page pagination.Page) ([]Event, error) { return ExtractEvents(page) } // GetResult represents the result of a Get operation. type GetResult struct { gophercloud.Result } // Extract returns a pointer to an Event object and is called after a // Get operation. func (r GetResult) Extract() (*Event, error) { var s struct { Event *Event `json:"event"` } err := r.ExtractInto(&s) return s.Event, err } testing/000077500000000000000000000000001335005366400347055ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackeventsdoc.go000066400000000000000000000000601335005366400357750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackevents/testing// orchestration_stackevents_v1 package testing fixtures.go000066400000000000000000000422611335005366400371120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackevents/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackevents" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) var Timestamp1, _ = time.Parse(time.RFC3339, "2018-06-26T07:58:17Z") var Timestamp2, _ = time.Parse(time.RFC3339, "2018-06-26T07:59:17Z") // FindExpected represents the expected object from a Find request. var FindExpected = []stackevents.Event{ { ResourceName: "hello_world", Time: Timestamp1, Links: []gophercloud.Link{ { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a", Rel: "self", }, { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world", Rel: "resource", }, { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b", Rel: "stack", }, }, LogicalResourceID: "hello_world", ResourceStatusReason: "state changed", ResourceStatus: "CREATE_IN_PROGRESS", PhysicalResourceID: "", ID: "06feb26f-9298-4a9b-8749-9d770e5d577a", }, { ResourceName: "hello_world", Time: Timestamp2, Links: []gophercloud.Link{ { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18", Rel: "self", }, { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world", Rel: "resource", }, { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b", Rel: "stack", }, }, LogicalResourceID: "hello_world", ResourceStatusReason: "state changed", ResourceStatus: "CREATE_COMPLETE", PhysicalResourceID: "49181cd6-169a-4130-9455-31185bbfc5bf", ID: "93940999-7d40-44ae-8de4-19624e7b8d18", }, } // FindOutput represents the response body from a Find request. const FindOutput = ` { "events": [ { "resource_name": "hello_world", "event_time": "2018-06-26T07:58:17Z", "links": [ { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a", "rel": "self" }, { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world", "rel": "resource" }, { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b", "rel": "stack" } ], "logical_resource_id": "hello_world", "resource_status_reason": "state changed", "resource_status": "CREATE_IN_PROGRESS", "physical_resource_id": null, "id": "06feb26f-9298-4a9b-8749-9d770e5d577a" }, { "resource_name": "hello_world", "event_time": "2018-06-26T07:59:17Z", "links": [ { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18", "rel": "self" }, { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world", "rel": "resource" }, { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b", "rel": "stack" } ], "logical_resource_id": "hello_world", "resource_status_reason": "state changed", "resource_status": "CREATE_COMPLETE", "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf", "id": "93940999-7d40-44ae-8de4-19624e7b8d18" } ] }` // HandleFindSuccessfully creates an HTTP handler at `/stacks/postman_stack/events` // on the test handler mux that responds with a `Find` response. func HandleFindSuccessfully(t *testing.T, output string) { th.Mux.HandleFunc("/stacks/postman_stack/events", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, output) }) } // ListExpected represents the expected object from a List request. var ListExpected = []stackevents.Event{ { ResourceName: "hello_world", Time: Timestamp1, Links: []gophercloud.Link{ { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a", Rel: "self", }, { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world", Rel: "resource", }, { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b", Rel: "stack", }, }, LogicalResourceID: "hello_world", ResourceStatusReason: "state changed", ResourceStatus: "CREATE_IN_PROGRESS", PhysicalResourceID: "", ID: "06feb26f-9298-4a9b-8749-9d770e5d577a", }, { ResourceName: "hello_world", Time: Timestamp2, Links: []gophercloud.Link{ { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18", Rel: "self", }, { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world", Rel: "resource", }, { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b", Rel: "stack", }, }, LogicalResourceID: "hello_world", ResourceStatusReason: "state changed", ResourceStatus: "CREATE_COMPLETE", PhysicalResourceID: "49181cd6-169a-4130-9455-31185bbfc5bf", ID: "93940999-7d40-44ae-8de4-19624e7b8d18", }, } // ListOutput represents the response body from a List request. const ListOutput = ` { "events": [ { "resource_name": "hello_world", "event_time": "2018-06-26T07:58:17Z", "links": [ { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a", "rel": "self" }, { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world", "rel": "resource" }, { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b", "rel": "stack" } ], "logical_resource_id": "hello_world", "resource_status_reason": "state changed", "resource_status": "CREATE_IN_PROGRESS", "physical_resource_id": null, "id": "06feb26f-9298-4a9b-8749-9d770e5d577a" }, { "resource_name": "hello_world", "event_time": "2018-06-26T07:59:17Z", "links": [ { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18", "rel": "self" }, { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world", "rel": "resource" }, { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b", "rel": "stack" } ], "logical_resource_id": "hello_world", "resource_status_reason": "state changed", "resource_status": "CREATE_COMPLETE", "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf", "id": "93940999-7d40-44ae-8de4-19624e7b8d18" } ] }` // HandleListSuccessfully creates an HTTP handler at `/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/events` // on the test handler mux that responds with a `List` response. func HandleListSuccessfully(t *testing.T, output string) { th.Mux.HandleFunc("/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/events", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, output) case "93940999-7d40-44ae-8de4-19624e7b8d18": fmt.Fprintf(w, `{"events":[]}`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) } // ListResourceEventsExpected represents the expected object from a ListResourceEvents request. var ListResourceEventsExpected = []stackevents.Event{ { ResourceName: "hello_world", Time: Timestamp1, Links: []gophercloud.Link{ { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a", Rel: "self", }, { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world", Rel: "resource", }, { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b", Rel: "stack", }, }, LogicalResourceID: "hello_world", ResourceStatusReason: "state changed", ResourceStatus: "CREATE_IN_PROGRESS", PhysicalResourceID: "", ID: "06feb26f-9298-4a9b-8749-9d770e5d577a", }, { ResourceName: "hello_world", Time: Timestamp2, Links: []gophercloud.Link{ { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18", Rel: "self", }, { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world", Rel: "resource", }, { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b", Rel: "stack", }, }, LogicalResourceID: "hello_world", ResourceStatusReason: "state changed", ResourceStatus: "CREATE_COMPLETE", PhysicalResourceID: "49181cd6-169a-4130-9455-31185bbfc5bf", ID: "93940999-7d40-44ae-8de4-19624e7b8d18", }, } // ListResourceEventsOutput represents the response body from a ListResourceEvents request. const ListResourceEventsOutput = ` { "events": [ { "resource_name": "hello_world", "event_time": "2018-06-26T07:58:17Z", "links": [ { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a", "rel": "self" }, { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world", "rel": "resource" }, { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b", "rel": "stack" } ], "logical_resource_id": "hello_world", "resource_status_reason": "state changed", "resource_status": "CREATE_IN_PROGRESS", "physical_resource_id": null, "id": "06feb26f-9298-4a9b-8749-9d770e5d577a" }, { "resource_name": "hello_world", "event_time": "2018-06-26T07:59:17Z", "links": [ { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18", "rel": "self" }, { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world", "rel": "resource" }, { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b", "rel": "stack" } ], "logical_resource_id": "hello_world", "resource_status_reason": "state changed", "resource_status": "CREATE_COMPLETE", "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf", "id": "93940999-7d40-44ae-8de4-19624e7b8d18" } ] }` // HandleListResourceEventsSuccessfully creates an HTTP handler at `/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/resources/my_resource/events` // on the test handler mux that responds with a `ListResourceEvents` response. func HandleListResourceEventsSuccessfully(t *testing.T, output string) { th.Mux.HandleFunc("/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/resources/my_resource/events", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, output) case "93940999-7d40-44ae-8de4-19624e7b8d18": fmt.Fprintf(w, `{"events":[]}`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) } // GetExpected represents the expected object from a Get request. var GetExpected = &stackevents.Event{ ResourceName: "hello_world", Time: Timestamp2, Links: []gophercloud.Link{ { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18", Rel: "self", }, { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world", Rel: "resource", }, { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b", Rel: "stack", }, }, LogicalResourceID: "hello_world", ResourceStatusReason: "state changed", ResourceStatus: "CREATE_COMPLETE", PhysicalResourceID: "49181cd6-169a-4130-9455-31185bbfc5bf", ID: "93940999-7d40-44ae-8de4-19624e7b8d18", } // GetOutput represents the response body from a Get request. const GetOutput = ` { "event":{ "resource_name": "hello_world", "event_time": "2018-06-26T07:59:17Z", "links": [ { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18", "rel": "self" }, { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world", "rel": "resource" }, { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b", "rel": "stack" } ], "logical_resource_id": "hello_world", "resource_status_reason": "state changed", "resource_status": "CREATE_COMPLETE", "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf", "id": "93940999-7d40-44ae-8de4-19624e7b8d18" } }` // HandleGetSuccessfully creates an HTTP handler at `/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/resources/my_resource/events/93940999-7d40-44ae-8de4-19624e7b8d18` // on the test handler mux that responds with a `Get` response. func HandleGetSuccessfully(t *testing.T, output string) { th.Mux.HandleFunc("/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/resources/my_resource/events/93940999-7d40-44ae-8de4-19624e7b8d18", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, output) }) } requests_test.go000066400000000000000000000037631335005366400401570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackevents/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackevents" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestFindEvents(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleFindSuccessfully(t, FindOutput) actual, err := stackevents.Find(fake.ServiceClient(), "postman_stack").Extract() th.AssertNoErr(t, err) expected := FindExpected th.AssertDeepEquals(t, expected, actual) } func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t, ListOutput) count := 0 err := stackevents.List(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := stackevents.ExtractEvents(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ListExpected, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestListResourceEvents(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListResourceEventsSuccessfully(t, ListResourceEventsOutput) count := 0 err := stackevents.ListResourceEvents(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", "my_resource", nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := stackevents.ExtractResourceEvents(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ListResourceEventsExpected, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestGetEvent(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t, GetOutput) actual, err := stackevents.Get(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", "my_resource", "93940999-7d40-44ae-8de4-19624e7b8d18").Extract() th.AssertNoErr(t, err) expected := GetExpected th.AssertDeepEquals(t, expected, actual) } urls.go000066400000000000000000000013341335005366400345450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackeventspackage stackevents import "github.com/gophercloud/gophercloud" func findURL(c *gophercloud.ServiceClient, stackName string) string { return c.ServiceURL("stacks", stackName, "events") } func listURL(c *gophercloud.ServiceClient, stackName, stackID string) string { return c.ServiceURL("stacks", stackName, stackID, "events") } func listResourceEventsURL(c *gophercloud.ServiceClient, stackName, stackID, resourceName string) string { return c.ServiceURL("stacks", stackName, stackID, "resources", resourceName, "events") } func getURL(c *gophercloud.ServiceClient, stackName, stackID, resourceName, eventID string) string { return c.ServiceURL("stacks", stackName, stackID, "resources", resourceName, "events", eventID) } stackresources/000077500000000000000000000000001335005366400337365ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1doc.go000066400000000000000000000041301335005366400350300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackresources/* Package stackresources provides operations for working with stack resources. A resource is a template artifact that represents some component of your desired architecture (a Cloud Server, a group of scaled Cloud Servers, a load balancer, some configuration management system, and so forth). Example of get resource information in stack rsrc_result := stackresources.Get(client, stack.Name, stack.ID, rsrc.Name) if rsrc_result.Err != nil { panic(rsrc_result.Err) } rsrc, err := rsrc_result.Extract() if err != nil { panic(err) } Example for list stack resources all_stack_rsrc_pages, err := stackresources.List(client, stack.Name, stack.ID, nil).AllPages() if err != nil { panic(err) } all_stack_rsrcs, err := stackresources.ExtractResources(all_stack_rsrc_pages) if err != nil { panic(err) } fmt.Println("Resource List:") for _, rsrc := range all_stack_rsrcs { // Get information of a resource in stack rsrc_result := stackresources.Get(client, stack.Name, stack.ID, rsrc.Name) if rsrc_result.Err != nil { panic(rsrc_result.Err) } rsrc, err := rsrc_result.Extract() if err != nil { panic(err) } fmt.Println("Resource Name: ", rsrc.Name, ", Physical ID: ", rsrc.PhysicalID, ", Status: ", rsrc.Status) } Example for get resource type schema schema_result := stackresources.Schema(client, "OS::Heat::Stack") if schema_result.Err != nil { panic(schema_result.Err) } schema, err := schema_result.Extract() if err != nil { panic(err) } fmt.Println("Schema for resource type OS::Heat::Stack") fmt.Println(schema.SupportStatus) Example for get resource type Template tmp_result := stackresources.Template(client, "OS::Heat::Stack") if tmp_result.Err != nil { panic(tmp_result.Err) } tmp, err := tmp_result.Extract() if err != nil { panic(err) } fmt.Println("Template for resource type OS::Heat::Stack") fmt.Println(string(tmp)) */ package stackresources requests.go000066400000000000000000000052711335005366400361450ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackresourcespackage stackresources import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Find retrieves stack resources for the given stack name. func Find(c *gophercloud.ServiceClient, stackName string) (r FindResult) { _, r.Err = c.Get(findURL(c, stackName), &r.Body, nil) return } // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToStackResourceListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Marker and Limit are used for pagination. type ListOpts struct { // Include resources from nest stacks up to Depth levels of recursion. Depth int `q:"nested_depth"` } // ToStackResourceListQuery formats a ListOpts into a query string. func (opts ListOpts) ToStackResourceListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List makes a request against the API to list resources for the given stack. func List(client *gophercloud.ServiceClient, stackName, stackID string, opts ListOptsBuilder) pagination.Pager { url := listURL(client, stackName, stackID) if opts != nil { query, err := opts.ToStackResourceListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ResourcePage{pagination.SinglePageBase(r)} }) } // Get retreives data for the given stack resource. func Get(c *gophercloud.ServiceClient, stackName, stackID, resourceName string) (r GetResult) { _, r.Err = c.Get(getURL(c, stackName, stackID, resourceName), &r.Body, nil) return } // Metadata retreives the metadata for the given stack resource. func Metadata(c *gophercloud.ServiceClient, stackName, stackID, resourceName string) (r MetadataResult) { _, r.Err = c.Get(metadataURL(c, stackName, stackID, resourceName), &r.Body, nil) return } // ListTypes makes a request against the API to list resource types. func ListTypes(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, listTypesURL(client), func(r pagination.PageResult) pagination.Page { return ResourceTypePage{pagination.SinglePageBase(r)} }) } // Schema retreives the schema for the given resource type. func Schema(c *gophercloud.ServiceClient, resourceType string) (r SchemaResult) { _, r.Err = c.Get(schemaURL(c, resourceType), &r.Body, nil) return } // Template retreives the template representation for the given resource type. func Template(c *gophercloud.ServiceClient, resourceType string) (r TemplateResult) { _, r.Err = c.Get(templateURL(c, resourceType), &r.Body, nil) return } results.go000066400000000000000000000132011335005366400357630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackresourcespackage stackresources import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // Resource represents a stack resource. type Resource struct { Attributes map[string]interface{} `json:"attributes"` CreationTime time.Time `json:"-"` Description string `json:"description"` Links []gophercloud.Link `json:"links"` LogicalID string `json:"logical_resource_id"` Name string `json:"resource_name"` PhysicalID string `json:"physical_resource_id"` RequiredBy []interface{} `json:"required_by"` Status string `json:"resource_status"` StatusReason string `json:"resource_status_reason"` Type string `json:"resource_type"` UpdatedTime time.Time `json:"-"` } func (r *Resource) UnmarshalJSON(b []byte) error { type tmp Resource var s struct { tmp CreationTime string `json:"creation_time"` UpdatedTime string `json:"updated_time"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Resource(s.tmp) if s.CreationTime != "" { t, err := time.Parse(time.RFC3339, s.CreationTime) if err != nil { t, err = time.Parse(gophercloud.RFC3339NoZ, s.CreationTime) if err != nil { return err } } r.CreationTime = t } if s.UpdatedTime != "" { t, err := time.Parse(time.RFC3339, s.UpdatedTime) if err != nil { t, err = time.Parse(gophercloud.RFC3339NoZ, s.UpdatedTime) if err != nil { return err } } r.UpdatedTime = t } return nil } // FindResult represents the result of a Find operation. type FindResult struct { gophercloud.Result } // Extract returns a slice of Resource objects and is called after a // Find operation. func (r FindResult) Extract() ([]Resource, error) { var s struct { Resources []Resource `json:"resources"` } err := r.ExtractInto(&s) return s.Resources, err } // ResourcePage abstracts the raw results of making a List() request against the API. // As OpenStack extensions may freely alter the response bodies of structures returned to the client, you may only safely access the // data provided through the ExtractResources call. type ResourcePage struct { pagination.SinglePageBase } // IsEmpty returns true if a page contains no Server results. func (r ResourcePage) IsEmpty() (bool, error) { resources, err := ExtractResources(r) return len(resources) == 0, err } // ExtractResources interprets the results of a single page from a List() call, producing a slice of Resource entities. func ExtractResources(r pagination.Page) ([]Resource, error) { var s struct { Resources []Resource `json:"resources"` } err := (r.(ResourcePage)).ExtractInto(&s) return s.Resources, err } // GetResult represents the result of a Get operation. type GetResult struct { gophercloud.Result } // Extract returns a pointer to a Resource object and is called after a // Get operation. func (r GetResult) Extract() (*Resource, error) { var s struct { Resource *Resource `json:"resource"` } err := r.ExtractInto(&s) return s.Resource, err } // MetadataResult represents the result of a Metadata operation. type MetadataResult struct { gophercloud.Result } // Extract returns a map object and is called after a // Metadata operation. func (r MetadataResult) Extract() (map[string]string, error) { var s struct { Meta map[string]string `json:"metadata"` } err := r.ExtractInto(&s) return s.Meta, err } // ResourceTypePage abstracts the raw results of making a ListTypes() request against the API. // As OpenStack extensions may freely alter the response bodies of structures returned to the client, you may only safely access the // data provided through the ExtractResourceTypes call. type ResourceTypePage struct { pagination.SinglePageBase } // IsEmpty returns true if a ResourceTypePage contains no resource types. func (r ResourceTypePage) IsEmpty() (bool, error) { rts, err := ExtractResourceTypes(r) return len(rts) == 0, err } // ResourceTypes represents the type that holds the result of ExtractResourceTypes. // We define methods on this type to sort it before output type ResourceTypes []string func (r ResourceTypes) Len() int { return len(r) } func (r ResourceTypes) Swap(i, j int) { r[i], r[j] = r[j], r[i] } func (r ResourceTypes) Less(i, j int) bool { return r[i] < r[j] } // ExtractResourceTypes extracts and returns resource types. func ExtractResourceTypes(r pagination.Page) (ResourceTypes, error) { var s struct { ResourceTypes ResourceTypes `json:"resource_types"` } err := (r.(ResourceTypePage)).ExtractInto(&s) return s.ResourceTypes, err } // TypeSchema represents a stack resource schema. type TypeSchema struct { Attributes map[string]interface{} `json:"attributes"` Properties map[string]interface{} `json:"properties"` ResourceType string `json:"resource_type"` SupportStatus map[string]interface{} `json:"support_status"` } // SchemaResult represents the result of a Schema operation. type SchemaResult struct { gophercloud.Result } // Extract returns a pointer to a TypeSchema object and is called after a // Schema operation. func (r SchemaResult) Extract() (*TypeSchema, error) { var s *TypeSchema err := r.ExtractInto(&s) return s, err } // TemplateResult represents the result of a Template operation. type TemplateResult struct { gophercloud.Result } // Extract returns the template and is called after a // Template operation. func (r TemplateResult) Extract() ([]byte, error) { if r.Err != nil { return nil, r.Err } template, err := json.MarshalIndent(r.Body, "", " ") return template, err } testing/000077500000000000000000000000001335005366400354135ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackresourcesdoc.go000066400000000000000000000000631335005366400365060ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackresources/testing// orchestration_stackresources_v1 package testing fixtures.go000066400000000000000000000370321335005366400376200ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackresources/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackresources" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) var Create_time, _ = time.Parse(time.RFC3339, "2018-06-26T07:57:17Z") var Updated_time, _ = time.Parse(time.RFC3339, "2018-06-26T07:58:17Z") // FindExpected represents the expected object from a Find request. var FindExpected = []stackresources.Resource{ { Name: "hello_world", Links: []gophercloud.Link{ { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world", Rel: "self", }, { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b", Rel: "stack", }, }, LogicalID: "hello_world", StatusReason: "state changed", UpdatedTime: Updated_time, CreationTime: Create_time, RequiredBy: []interface{}{}, Status: "CREATE_IN_PROGRESS", PhysicalID: "49181cd6-169a-4130-9455-31185bbfc5bf", Type: "OS::Nova::Server", Attributes: map[string]interface{}{"SXSW": "atx"}, Description: "Some resource", }, } // FindOutput represents the response body from a Find request. const FindOutput = ` { "resources": [ { "description": "Some resource", "attributes": {"SXSW": "atx"}, "resource_name": "hello_world", "links": [ { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world", "rel": "self" }, { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b", "rel": "stack" } ], "logical_resource_id": "hello_world", "resource_status_reason": "state changed", "updated_time": "2018-06-26T07:58:17Z", "creation_time": "2018-06-26T07:57:17Z", "required_by": [], "resource_status": "CREATE_IN_PROGRESS", "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf", "resource_type": "OS::Nova::Server" } ] }` // HandleFindSuccessfully creates an HTTP handler at `/stacks/hello_world/resources` // on the test handler mux that responds with a `Find` response. func HandleFindSuccessfully(t *testing.T, output string) { th.Mux.HandleFunc("/stacks/hello_world/resources", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, output) }) } // ListExpected represents the expected object from a List request. var ListExpected = []stackresources.Resource{ { Name: "hello_world", Links: []gophercloud.Link{ { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world", Rel: "self", }, { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b", Rel: "stack", }, }, LogicalID: "hello_world", StatusReason: "state changed", UpdatedTime: Updated_time, CreationTime: Create_time, RequiredBy: []interface{}{}, Status: "CREATE_IN_PROGRESS", PhysicalID: "49181cd6-169a-4130-9455-31185bbfc5bf", Type: "OS::Nova::Server", Attributes: map[string]interface{}{"SXSW": "atx"}, Description: "Some resource", }, } // ListOutput represents the response body from a List request. const ListOutput = `{ "resources": [ { "resource_name": "hello_world", "links": [ { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world", "rel": "self" }, { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b", "rel": "stack" } ], "logical_resource_id": "hello_world", "resource_status_reason": "state changed", "updated_time": "2018-06-26T07:58:17Z", "creation_time": "2018-06-26T07:57:17Z", "required_by": [], "resource_status": "CREATE_IN_PROGRESS", "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf", "resource_type": "OS::Nova::Server", "attributes": {"SXSW": "atx"}, "description": "Some resource" } ] }` // HandleListSuccessfully creates an HTTP handler at `/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/resources` // on the test handler mux that responds with a `List` response. func HandleListSuccessfully(t *testing.T, output string) { th.Mux.HandleFunc("/stacks/hello_world/49181cd6-169a-4130-9455-31185bbfc5bf/resources", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, output) case "49181cd6-169a-4130-9455-31185bbfc5bf": fmt.Fprintf(w, `{"resources":[]}`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) } // GetExpected represents the expected object from a Get request. var GetExpected = &stackresources.Resource{ Name: "wordpress_instance", Links: []gophercloud.Link{ { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance", Rel: "self", }, { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e", Rel: "stack", }, }, LogicalID: "wordpress_instance", Attributes: map[string]interface{}{"SXSW": "atx"}, StatusReason: "state changed", UpdatedTime: Updated_time, RequiredBy: []interface{}{}, Status: "CREATE_COMPLETE", PhysicalID: "00e3a2fe-c65d-403c-9483-4db9930dd194", Type: "OS::Nova::Server", } // GetOutput represents the response body from a Get request. const GetOutput = ` { "resource": { "description": "Some resource", "attributes": {"SXSW": "atx"}, "resource_name": "wordpress_instance", "description": "", "links": [ { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance", "rel": "self" }, { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e", "rel": "stack" } ], "logical_resource_id": "wordpress_instance", "resource_status": "CREATE_COMPLETE", "updated_time": "2018-06-26T07:58:17Z", "required_by": [], "resource_status_reason": "state changed", "physical_resource_id": "00e3a2fe-c65d-403c-9483-4db9930dd194", "resource_type": "OS::Nova::Server" } }` // HandleGetSuccessfully creates an HTTP handler at `/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance` // on the test handler mux that responds with a `Get` response. func HandleGetSuccessfully(t *testing.T, output string) { th.Mux.HandleFunc("/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, output) }) } // MetadataExpected represents the expected object from a Metadata request. var MetadataExpected = map[string]string{ "number": "7", "animal": "auk", } // MetadataOutput represents the response body from a Metadata request. const MetadataOutput = ` { "metadata": { "number": "7", "animal": "auk" } }` // HandleMetadataSuccessfully creates an HTTP handler at `/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance/metadata` // on the test handler mux that responds with a `Metadata` response. func HandleMetadataSuccessfully(t *testing.T, output string) { th.Mux.HandleFunc("/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance/metadata", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, output) }) } // ListTypesExpected represents the expected object from a ListTypes request. var ListTypesExpected = stackresources.ResourceTypes{ "OS::Nova::Server", "OS::Heat::RandomString", "OS::Swift::Container", "OS::Trove::Instance", "OS::Nova::FloatingIPAssociation", "OS::Cinder::VolumeAttachment", "OS::Nova::FloatingIP", "OS::Nova::KeyPair", } // same as above, but sorted var SortedListTypesExpected = stackresources.ResourceTypes{ "OS::Cinder::VolumeAttachment", "OS::Heat::RandomString", "OS::Nova::FloatingIP", "OS::Nova::FloatingIPAssociation", "OS::Nova::KeyPair", "OS::Nova::Server", "OS::Swift::Container", "OS::Trove::Instance", } // ListTypesOutput represents the response body from a ListTypes request. const ListTypesOutput = ` { "resource_types": [ "OS::Nova::Server", "OS::Heat::RandomString", "OS::Swift::Container", "OS::Trove::Instance", "OS::Nova::FloatingIPAssociation", "OS::Cinder::VolumeAttachment", "OS::Nova::FloatingIP", "OS::Nova::KeyPair" ] }` // HandleListTypesSuccessfully creates an HTTP handler at `/resource_types` // on the test handler mux that responds with a `ListTypes` response. func HandleListTypesSuccessfully(t *testing.T, output string) { th.Mux.HandleFunc("/resource_types", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, output) }) } // GetSchemaExpected represents the expected object from a Schema request. var GetSchemaExpected = &stackresources.TypeSchema{ Attributes: map[string]interface{}{ "an_attribute": map[string]interface{}{ "description": "An attribute description .", }, }, Properties: map[string]interface{}{ "a_property": map[string]interface{}{ "update_allowed": false, "required": true, "type": "string", "description": "A resource description.", }, }, ResourceType: "OS::Heat::AResourceName", SupportStatus: map[string]interface{}{ "message": "A status message", "status": "SUPPORTED", "version": "2014.1", }, } // GetSchemaOutput represents the response body from a Schema request. const GetSchemaOutput = ` { "attributes": { "an_attribute": { "description": "An attribute description ." } }, "properties": { "a_property": { "update_allowed": false, "required": true, "type": "string", "description": "A resource description." } }, "resource_type": "OS::Heat::AResourceName", "support_status": { "message": "A status message", "status": "SUPPORTED", "version": "2014.1" } }` // HandleGetSchemaSuccessfully creates an HTTP handler at `/resource_types/OS::Heat::AResourceName` // on the test handler mux that responds with a `Schema` response. func HandleGetSchemaSuccessfully(t *testing.T, output string) { th.Mux.HandleFunc("/resource_types/OS::Heat::AResourceName", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, output) }) } // GetTemplateExpected represents the expected object from a Template request. var GetTemplateExpected = "{\n \"HeatTemplateFormatVersion\": \"2012-12-12\",\n \"Outputs\": {\n \"private_key\": {\n \"Description\": \"The private key if it has been saved.\",\n \"Value\": \"{\\\"Fn::GetAtt\\\": [\\\"KeyPair\\\", \\\"private_key\\\"]}\"\n },\n \"public_key\": {\n \"Description\": \"The public key.\",\n \"Value\": \"{\\\"Fn::GetAtt\\\": [\\\"KeyPair\\\", \\\"public_key\\\"]}\"\n }\n },\n \"Parameters\": {\n \"name\": {\n \"Description\": \"The name of the key pair.\",\n \"Type\": \"String\"\n },\n \"public_key\": {\n \"Description\": \"The optional public key. This allows users to supply the public key from a pre-existing key pair. If not supplied, a new key pair will be generated.\",\n \"Type\": \"String\"\n },\n \"save_private_key\": {\n \"AllowedValues\": [\n \"True\",\n \"true\",\n \"False\",\n \"false\"\n ],\n \"Default\": false,\n \"Description\": \"True if the system should remember a generated private key; False otherwise.\",\n \"Type\": \"String\"\n }\n },\n \"Resources\": {\n \"KeyPair\": {\n \"Properties\": {\n \"name\": {\n \"Ref\": \"name\"\n },\n \"public_key\": {\n \"Ref\": \"public_key\"\n },\n \"save_private_key\": {\n \"Ref\": \"save_private_key\"\n }\n },\n \"Type\": \"OS::Nova::KeyPair\"\n }\n }\n}" // GetTemplateOutput represents the response body from a Template request. const GetTemplateOutput = ` { "HeatTemplateFormatVersion": "2012-12-12", "Outputs": { "private_key": { "Description": "The private key if it has been saved.", "Value": "{\"Fn::GetAtt\": [\"KeyPair\", \"private_key\"]}" }, "public_key": { "Description": "The public key.", "Value": "{\"Fn::GetAtt\": [\"KeyPair\", \"public_key\"]}" } }, "Parameters": { "name": { "Description": "The name of the key pair.", "Type": "String" }, "public_key": { "Description": "The optional public key. This allows users to supply the public key from a pre-existing key pair. If not supplied, a new key pair will be generated.", "Type": "String" }, "save_private_key": { "AllowedValues": [ "True", "true", "False", "false" ], "Default": false, "Description": "True if the system should remember a generated private key; False otherwise.", "Type": "String" } }, "Resources": { "KeyPair": { "Properties": { "name": { "Ref": "name" }, "public_key": { "Ref": "public_key" }, "save_private_key": { "Ref": "save_private_key" } }, "Type": "OS::Nova::KeyPair" } } }` // HandleGetTemplateSuccessfully creates an HTTP handler at `/resource_types/OS::Heat::AResourceName/template` // on the test handler mux that responds with a `Template` response. func HandleGetTemplateSuccessfully(t *testing.T, output string) { th.Mux.HandleFunc("/resource_types/OS::Heat::AResourceName/template", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, output) }) } requests_test.go000066400000000000000000000060301335005366400406530ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackresources/testingpackage testing import ( "sort" "testing" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackresources" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestFindResources(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleFindSuccessfully(t, FindOutput) actual, err := stackresources.Find(fake.ServiceClient(), "hello_world").Extract() th.AssertNoErr(t, err) expected := FindExpected th.AssertDeepEquals(t, expected, actual) } func TestListResources(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t, ListOutput) count := 0 err := stackresources.List(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := stackresources.ExtractResources(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ListExpected, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestGetResource(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t, GetOutput) actual, err := stackresources.Get(fake.ServiceClient(), "teststack", "0b1771bd-9336-4f2b-ae86-a80f971faf1e", "wordpress_instance").Extract() th.AssertNoErr(t, err) expected := GetExpected th.AssertDeepEquals(t, expected, actual) } func TestResourceMetadata(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleMetadataSuccessfully(t, MetadataOutput) actual, err := stackresources.Metadata(fake.ServiceClient(), "teststack", "0b1771bd-9336-4f2b-ae86-a80f971faf1e", "wordpress_instance").Extract() th.AssertNoErr(t, err) expected := MetadataExpected th.AssertDeepEquals(t, expected, actual) } func TestListResourceTypes(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListTypesSuccessfully(t, ListTypesOutput) count := 0 err := stackresources.ListTypes(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := stackresources.ExtractResourceTypes(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ListTypesExpected, actual) // test if sorting works sort.Sort(actual) th.CheckDeepEquals(t, SortedListTypesExpected, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, 1, count) } func TestGetResourceSchema(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSchemaSuccessfully(t, GetSchemaOutput) actual, err := stackresources.Schema(fake.ServiceClient(), "OS::Heat::AResourceName").Extract() th.AssertNoErr(t, err) expected := GetSchemaExpected th.AssertDeepEquals(t, expected, actual) } func TestGetResourceTemplate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetTemplateSuccessfully(t, GetTemplateOutput) actual, err := stackresources.Template(fake.ServiceClient(), "OS::Heat::AResourceName").Extract() th.AssertNoErr(t, err) expected := GetTemplateExpected th.AssertDeepEquals(t, expected, string(actual)) } urls.go000066400000000000000000000020501335005366400352470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackresourcespackage stackresources import "github.com/gophercloud/gophercloud" func findURL(c *gophercloud.ServiceClient, stackName string) string { return c.ServiceURL("stacks", stackName, "resources") } func listURL(c *gophercloud.ServiceClient, stackName, stackID string) string { return c.ServiceURL("stacks", stackName, stackID, "resources") } func getURL(c *gophercloud.ServiceClient, stackName, stackID, resourceName string) string { return c.ServiceURL("stacks", stackName, stackID, "resources", resourceName) } func metadataURL(c *gophercloud.ServiceClient, stackName, stackID, resourceName string) string { return c.ServiceURL("stacks", stackName, stackID, "resources", resourceName, "metadata") } func listTypesURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("resource_types") } func schemaURL(c *gophercloud.ServiceClient, typeName string) string { return c.ServiceURL("resource_types", typeName) } func templateURL(c *gophercloud.ServiceClient, typeName string) string { return c.ServiceURL("resource_types", typeName, "template") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stacks/000077500000000000000000000000001335005366400322455ustar00rootroot00000000000000doc.go000066400000000000000000000151001335005366400332570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stacks/* Package stacks provides operation for working with Heat stacks. A stack is a group of resources (servers, load balancers, databases, and so forth) combined to fulfill a useful purpose. Based on a template, Heat orchestration engine creates an instantiated set of resources (a stack) to run the application framework or component specified (in the template). A stack is a running instance of a template. The result of creating a stack is a deployment of the application framework or component. Prepare required import packages import ( "fmt" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks" ) Example of Preparing Orchestration client: client, err := openstack.NewOrchestrationV1(provider, gophercloud.EndpointOpts{Region: "RegionOne"}) Example of List Stack: all_stack_pages, err := stacks.List(client, nil).AllPages() if err != nil { panic(err) } all_stacks, err := stacks.ExtractStacks(all_stack_pages) if err != nil { panic(err) } for _, stack := range all_stacks { fmt.Printf("%+v\n", stack) } Example to Create an Stack // Create Template t := make(map[string]interface{}) f, err := ioutil.ReadFile("template.yaml") if err != nil { panic(err) } err = yaml.Unmarshal(f, t) if err != nil { panic(err) } template := &stacks.Template{} template.TE = stacks.TE{ Bin: f, } // Create Environment if needed t_env := make(map[string]interface{}) f_env, err := ioutil.ReadFile("env.yaml") if err != nil { panic(err) } err = yaml.Unmarshal(f_env, t_env) if err != nil { panic(err) } env := &stacks.Environment{} env.TE = stacks.TE{ Bin: f_env, } // Remember, the priority of parameters you given through // Parameters is higher than the parameters you provided in EnvironmentOpts. params := make(map[string]string) params["number_of_nodes"] = 1 tags := []string{"example-stack"} createOpts := &stacks.CreateOpts{ // The name of the stack. It must start with an alphabetic character. Name: "testing_group", // A structure that contains either the template file or url. Call the // associated methods to extract the information relevant to send in a create request. TemplateOpts: template, // A structure that contains details for the environment of the stack. EnvironmentOpts: env, // User-defined parameters to pass to the template. Parameters: params, // A list of tags to assosciate with the Stack Tags: tags, } r := stacks.Create(client, createOpts) //dcreated_stack := stacks.CreatedStack() if r.Err != nil { panic(r.Err) } created_stack, err := r.Extract() if err != nil { panic(err) } fmt.Printf("Created Stack: %v", created_stack.ID) Example for Get Stack get_result := stacks.Get(client, stackName, created_stack.ID) if get_result.Err != nil { panic(get_result.Err) } stack, err := get_result.Extract() if err != nil { panic(err) } fmt.Println("Get Stack: Name: ", stack.Name, ", ID: ", stack.ID, ", Status: ", stack.Status) Example for Delete Stack del_r := stacks.Delete(client, stackName, created_stack.ID) if del_r.Err != nil { panic(del_r.Err) } fmt.Println("Deleted Stack: ", stackName) Summary of Behavior Between Stack Update and UpdatePatch Methods : Function | Test Case | Result Update() | Template AND Parameters WITH Conflict | Parameter takes priority, parameters are set in raw_template.environment overlay Update() | Template ONLY | Template updates, raw_template.environment overlay is removed Update() | Parameters ONLY | No update, template is required UpdatePatch() | Template AND Parameters WITH Conflict | Parameter takes priority, parameters are set in raw_template.environment overlay UpdatePatch() | Template ONLY | Template updates, but raw_template.environment overlay is not removed, existing parameter values will remain UpdatePatch() | Parameters ONLY | Parameters (raw_template.environment) is updated, excluded values are unchanged The PUT Update() function will remove parameters from the raw_template.environment overlay if they are excluded from the operation, whereas PATCH Update() will never be destructive to the raw_template.environment overlay. It is not possible to expose the raw_template values with a patch update once they have been added to the environment overlay with the PATCH verb, but newly added values that do not have a corresponding key in the overlay will display the raw_template value. Example to Update a Stack Using the Update (PUT) Method t := make(map[string]interface{}) f, err := ioutil.ReadFile("template.yaml") if err != nil { panic(err) } err = yaml.Unmarshal(f, t) if err != nil { panic(err) } template := stacks.Template{} template.TE = stacks.TE{ Bin: f, } var params = make(map[string]interface{}) params["number_of_nodes"] = 2 stackName := "my_stack" stackId := "d68cc349-ccc5-4b44-a17d-07f068c01e5a" stackOpts := &stacks.UpdateOpts{ Parameters: params, TemplateOpts: &template, } res := stacks.Update(orchestrationClient, stackName, stackId, stackOpts) if res.Err != nil { panic(res.Err) } Example to Update a Stack Using the UpdatePatch (PATCH) Method var params = make(map[string]interface{}) params["number_of_nodes"] = 2 stackName := "my_stack" stackId := "d68cc349-ccc5-4b44-a17d-07f068c01e5a" stackOpts := &stacks.UpdateOpts{ Parameters: params, } res := stacks.UpdatePatch(orchestrationClient, stackName, stackId, stackOpts) if res.Err != nil { panic(res.Err) } Example YAML Template Containing a Heat::ResourceGroup With Three Nodes heat_template_version: 2016-04-08 parameters: number_of_nodes: type: number default: 3 description: the number of nodes node_flavor: type: string default: m1.small description: node flavor node_image: type: string default: centos7.5-latest description: node os image node_network: type: string default: my-node-network description: node network name resources: resource_group: type: OS::Heat::ResourceGroup properties: count: { get_param: number_of_nodes } resource_def: type: OS::Nova::Server properties: name: my_nova_server_%index% image: { get_param: node_image } flavor: { get_param: node_flavor } networks: - network: {get_param: node_network} */ package stacks environment.go000066400000000000000000000075521335005366400350720ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackspackage stacks import "strings" // Environment is a structure that represents stack environments type Environment struct { TE } // EnvironmentSections is a map containing allowed sections in a stack environment file var EnvironmentSections = map[string]bool{ "parameters": true, "parameter_defaults": true, "resource_registry": true, } // Validate validates the contents of the Environment func (e *Environment) Validate() error { if e.Parsed == nil { if err := e.Parse(); err != nil { return err } } for key := range e.Parsed { if _, ok := EnvironmentSections[key]; !ok { return ErrInvalidEnvironment{Section: key} } } return nil } // Parse environment file to resolve the URL's of the resources. This is done by // reading from the `Resource Registry` section, which is why the function is // named GetRRFileContents. func (e *Environment) getRRFileContents(ignoreIf igFunc) error { // initialize environment if empty if e.Files == nil { e.Files = make(map[string]string) } if e.fileMaps == nil { e.fileMaps = make(map[string]string) } // get the resource registry rr := e.Parsed["resource_registry"] // search the resource registry for URLs switch rr.(type) { // process further only if the resource registry is a map case map[string]interface{}, map[interface{}]interface{}: rrMap, err := toStringKeys(rr) if err != nil { return err } // the resource registry might contain a base URL for the resource. If // such a field is present, use it. Otherwise, use the default base URL. var baseURL string if val, ok := rrMap["base_url"]; ok { baseURL = val.(string) } else { baseURL = e.baseURL } // The contents of the resource may be located in a remote file, which // will be a template. Instantiate a temporary template to manage the // contents. tempTemplate := new(Template) tempTemplate.baseURL = baseURL tempTemplate.client = e.client // Fetch the contents of remote resource URL's if err = tempTemplate.getFileContents(rr, ignoreIf, false); err != nil { return err } // check the `resources` section (if it exists) for more URL's. Note that // the previous call to GetFileContents was (deliberately) not recursive // as we want more control over where to look for URL's if val, ok := rrMap["resources"]; ok { switch val.(type) { // process further only if the contents are a map case map[string]interface{}, map[interface{}]interface{}: resourcesMap, err := toStringKeys(val) if err != nil { return err } for _, v := range resourcesMap { switch v.(type) { case map[string]interface{}, map[interface{}]interface{}: resourceMap, err := toStringKeys(v) if err != nil { return err } var resourceBaseURL string // if base_url for the resource type is defined, use it if val, ok := resourceMap["base_url"]; ok { resourceBaseURL = val.(string) } else { resourceBaseURL = baseURL } tempTemplate.baseURL = resourceBaseURL if err := tempTemplate.getFileContents(v, ignoreIf, false); err != nil { return err } } } } } // if the resource registry contained any URL's, store them. This can // then be passed as parameter to api calls to Heat api. e.Files = tempTemplate.Files return nil default: return nil } } // function to choose keys whose values are other environment files func ignoreIfEnvironment(key string, value interface{}) bool { // base_url and hooks refer to components which cannot have urls if key == "base_url" || key == "hooks" { return true } // if value is not string, it cannot be a URL valueString, ok := value.(string) if !ok { return true } // if value contains `::`, it must be a reference to another resource type // e.g. OS::Nova::Server : Rackspace::Cloud::Server if strings.Contains(valueString, "::") { return true } return false } environment_test.go000066400000000000000000000146771335005366400361370ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackspackage stacks import ( "fmt" "net/http" "net/url" "strings" "testing" th "github.com/gophercloud/gophercloud/testhelper" ) func TestEnvironmentValidation(t *testing.T) { environmentJSON := new(Environment) environmentJSON.Bin = []byte(ValidJSONEnvironment) err := environmentJSON.Validate() th.AssertNoErr(t, err) environmentYAML := new(Environment) environmentYAML.Bin = []byte(ValidYAMLEnvironment) err = environmentYAML.Validate() th.AssertNoErr(t, err) environmentInvalid := new(Environment) environmentInvalid.Bin = []byte(InvalidEnvironment) if err = environmentInvalid.Validate(); err == nil { t.Error("environment validation did not catch invalid environment") } } func TestEnvironmentParsing(t *testing.T) { environmentJSON := new(Environment) environmentJSON.Bin = []byte(ValidJSONEnvironment) err := environmentJSON.Parse() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ValidJSONEnvironmentParsed, environmentJSON.Parsed) environmentYAML := new(Environment) environmentYAML.Bin = []byte(ValidJSONEnvironment) err = environmentYAML.Parse() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ValidJSONEnvironmentParsed, environmentYAML.Parsed) environmentInvalid := new(Environment) environmentInvalid.Bin = []byte("Keep Austin Weird") err = environmentInvalid.Parse() if err == nil { t.Error("environment parsing did not catch invalid environment") } } func TestIgnoreIfEnvironment(t *testing.T) { var keyValueTests = []struct { key string value interface{} out bool }{ {"base_url", "afksdf", true}, {"not_type", "hooks", false}, {"get_file", "::", true}, {"hooks", "dfsdfsd", true}, {"type", "sdfubsduf.yaml", false}, {"type", "sdfsdufs.environment", false}, {"type", "sdfsdf.file", false}, {"type", map[string]string{"key": "value"}, true}, } var result bool for _, kv := range keyValueTests { result = ignoreIfEnvironment(kv.key, kv.value) if result != kv.out { t.Errorf("key: %v, value: %v expected: %v, actual: %v", kv.key, kv.value, kv.out, result) } } } func TestGetRRFileContents(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() environmentContent := ` heat_template_version: 2013-05-23 description: Heat WordPress template to support F18, using only Heat OpenStack-native resource types, and without the requirement for heat-cfntools in the image. WordPress is web software you can use to create a beautiful website or blog. This template installs a single-instance WordPress deployment using a local MySQL database to store the data. parameters: key_name: type: string description : Name of a KeyPair to enable SSH access to the instance resources: wordpress_instance: type: OS::Nova::Server properties: image: { get_param: image_id } flavor: { get_param: instance_type } key_name: { get_param: key_name }` dbContent := ` heat_template_version: 2014-10-16 description: Test template for Trove resource capabilities parameters: db_pass: type: string hidden: true description: Database access password default: secrete resources: service_db: type: OS::Trove::Instance properties: name: trove_test_db datastore_type: mariadb flavor: 1GB Instance size: 10 databases: - name: test_data users: - name: kitchen_sink password: { get_param: db_pass } databases: [ test_data ]` baseurl, err := getBasePath() th.AssertNoErr(t, err) fakeEnvURL := strings.Join([]string{baseurl, "my_env.yaml"}, "/") urlparsed, err := url.Parse(fakeEnvURL) th.AssertNoErr(t, err) // handler for my_env.yaml th.Mux.HandleFunc(urlparsed.Path, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, environmentContent) }) fakeDBURL := strings.Join([]string{baseurl, "my_db.yaml"}, "/") urlparsed, err = url.Parse(fakeDBURL) th.AssertNoErr(t, err) // handler for my_db.yaml th.Mux.HandleFunc(urlparsed.Path, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, dbContent) }) client := fakeClient{BaseClient: getHTTPClient()} env := new(Environment) env.Bin = []byte(`{"resource_registry": {"My::WP::Server": "my_env.yaml", "resources": {"my_db_server": {"OS::DBInstance": "my_db.yaml"}}}}`) env.client = client err = env.Parse() th.AssertNoErr(t, err) err = env.getRRFileContents(ignoreIfEnvironment) th.AssertNoErr(t, err) expectedEnvFilesContent := "\nheat_template_version: 2013-05-23\n\ndescription:\n Heat WordPress template to support F18, using only Heat OpenStack-native\n resource types, and without the requirement for heat-cfntools in the image.\n WordPress is web software you can use to create a beautiful website or blog.\n This template installs a single-instance WordPress deployment using a local\n MySQL database to store the data.\n\nparameters:\n\n key_name:\n type: string\n description : Name of a KeyPair to enable SSH access to the instance\n\nresources:\n wordpress_instance:\n type: OS::Nova::Server\n properties:\n image: { get_param: image_id }\n flavor: { get_param: instance_type }\n key_name: { get_param: key_name }" expectedDBFilesContent := "\nheat_template_version: 2014-10-16\n\ndescription:\n Test template for Trove resource capabilities\n\nparameters:\n db_pass:\n type: string\n hidden: true\n description: Database access password\n default: secrete\n\nresources:\n\nservice_db:\n type: OS::Trove::Instance\n properties:\n name: trove_test_db\n datastore_type: mariadb\n flavor: 1GB Instance\n size: 10\n databases:\n - name: test_data\n users:\n - name: kitchen_sink\n password: { get_param: db_pass }\n databases: [ test_data ]" th.AssertEquals(t, expectedEnvFilesContent, env.Files[fakeEnvURL]) th.AssertEquals(t, expectedDBFilesContent, env.Files[fakeDBURL]) // Update env's fileMaps to replace relative filenames by absolute URLs. env.fileMaps = map[string]string{ "my_env.yaml": fakeEnvURL, "my_db.yaml": fakeDBURL, } env.fixFileRefs() expectedParsed := map[string]interface{}{ "resource_registry": map[string]interface{}{ "My::WP::Server": fakeEnvURL, "resources": map[string]interface{}{ "my_db_server": map[string]interface{}{ "OS::DBInstance": fakeDBURL, }, }, }, } env.Parse() th.AssertDeepEquals(t, expectedParsed, env.Parsed) } errors.go000066400000000000000000000014621335005366400340340ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackspackage stacks import ( "fmt" "github.com/gophercloud/gophercloud" ) type ErrInvalidEnvironment struct { gophercloud.BaseError Section string } func (e ErrInvalidEnvironment) Error() string { return fmt.Sprintf("Environment has wrong section: %s", e.Section) } type ErrInvalidDataFormat struct { gophercloud.BaseError } func (e ErrInvalidDataFormat) Error() string { return fmt.Sprintf("Data in neither json nor yaml format.") } type ErrInvalidTemplateFormatVersion struct { gophercloud.BaseError Version string } func (e ErrInvalidTemplateFormatVersion) Error() string { return fmt.Sprintf("Template format version not found.") } type ErrTemplateRequired struct { gophercloud.BaseError } func (e ErrTemplateRequired) Error() string { return fmt.Sprintf("Template required for this function.") } fixtures.go000066400000000000000000000131141335005366400343660ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackspackage stacks // ValidJSONTemplate is a valid OpenStack Heat template in JSON format const ValidJSONTemplate = ` { "heat_template_version": "2014-10-16", "parameters": { "flavor": { "default": "debian2G", "description": "Flavor for the server to be created", "hidden": true, "type": "string" } }, "resources": { "test_server": { "properties": { "flavor": "2 GB General Purpose v1", "image": "Debian 7 (Wheezy) (PVHVM)", "name": "test-server" }, "type": "OS::Nova::Server" } } } ` // ValidYAMLTemplate is a valid OpenStack Heat template in YAML format const ValidYAMLTemplate = ` heat_template_version: 2014-10-16 parameters: flavor: type: string description: Flavor for the server to be created default: debian2G hidden: true resources: test_server: type: "OS::Nova::Server" properties: name: test-server flavor: 2 GB General Purpose v1 image: Debian 7 (Wheezy) (PVHVM) ` // InvalidTemplateNoVersion is an invalid template as it has no `version` section const InvalidTemplateNoVersion = ` parameters: flavor: type: string description: Flavor for the server to be created default: debian2G hidden: true resources: test_server: type: "OS::Nova::Server" properties: name: test-server flavor: 2 GB General Purpose v1 image: Debian 7 (Wheezy) (PVHVM) ` // ValidJSONEnvironment is a valid environment for a stack in JSON format const ValidJSONEnvironment = ` { "parameters": { "user_key": "userkey" }, "resource_registry": { "My::WP::Server": "file:///home/shardy/git/heat-templates/hot/F18/WordPress_Native.yaml", "OS::Quantum*": "OS::Neutron*", "AWS::CloudWatch::Alarm": "file:///etc/heat/templates/AWS_CloudWatch_Alarm.yaml", "OS::Metering::Alarm": "OS::Ceilometer::Alarm", "AWS::RDS::DBInstance": "file:///etc/heat/templates/AWS_RDS_DBInstance.yaml", "resources": { "my_db_server": { "OS::DBInstance": "file:///home/mine/all_my_cool_templates/db.yaml" }, "my_server": { "OS::DBInstance": "file:///home/mine/all_my_cool_templates/db.yaml", "hooks": "pre-create" }, "nested_stack": { "nested_resource": { "hooks": "pre-update" }, "another_resource": { "hooks": [ "pre-create", "pre-update" ] } } } } } ` // ValidYAMLEnvironment is a valid environment for a stack in YAML format const ValidYAMLEnvironment = ` parameters: user_key: userkey resource_registry: My::WP::Server: file:///home/shardy/git/heat-templates/hot/F18/WordPress_Native.yaml # allow older templates with Quantum in them. "OS::Quantum*": "OS::Neutron*" # Choose your implementation of AWS::CloudWatch::Alarm "AWS::CloudWatch::Alarm": "file:///etc/heat/templates/AWS_CloudWatch_Alarm.yaml" #"AWS::CloudWatch::Alarm": "OS::Heat::CWLiteAlarm" "OS::Metering::Alarm": "OS::Ceilometer::Alarm" "AWS::RDS::DBInstance": "file:///etc/heat/templates/AWS_RDS_DBInstance.yaml" resources: my_db_server: "OS::DBInstance": file:///home/mine/all_my_cool_templates/db.yaml my_server: "OS::DBInstance": file:///home/mine/all_my_cool_templates/db.yaml hooks: pre-create nested_stack: nested_resource: hooks: pre-update another_resource: hooks: [pre-create, pre-update] ` // InvalidEnvironment is an invalid environment as it has an extra section called `resources` const InvalidEnvironment = ` parameters: flavor: type: string description: Flavor for the server to be created default: debian2G hidden: true resources: test_server: type: "OS::Nova::Server" properties: name: test-server flavor: 2 GB General Purpose v1 image: Debian 7 (Wheezy) (PVHVM) parameter_defaults: KeyName: heat_key ` // ValidJSONEnvironmentParsed is the expected parsed version of ValidJSONEnvironment var ValidJSONEnvironmentParsed = map[string]interface{}{ "parameters": map[string]interface{}{ "user_key": "userkey", }, "resource_registry": map[string]interface{}{ "My::WP::Server": "file:///home/shardy/git/heat-templates/hot/F18/WordPress_Native.yaml", "OS::Quantum*": "OS::Neutron*", "AWS::CloudWatch::Alarm": "file:///etc/heat/templates/AWS_CloudWatch_Alarm.yaml", "OS::Metering::Alarm": "OS::Ceilometer::Alarm", "AWS::RDS::DBInstance": "file:///etc/heat/templates/AWS_RDS_DBInstance.yaml", "resources": map[string]interface{}{ "my_db_server": map[string]interface{}{ "OS::DBInstance": "file:///home/mine/all_my_cool_templates/db.yaml", }, "my_server": map[string]interface{}{ "OS::DBInstance": "file:///home/mine/all_my_cool_templates/db.yaml", "hooks": "pre-create", }, "nested_stack": map[string]interface{}{ "nested_resource": map[string]interface{}{ "hooks": "pre-update", }, "another_resource": map[string]interface{}{ "hooks": []interface{}{ "pre-create", "pre-update", }, }, }, }, }, } // ValidJSONTemplateParsed is the expected parsed version of ValidJSONTemplate var ValidJSONTemplateParsed = map[string]interface{}{ "heat_template_version": "2014-10-16", "parameters": map[string]interface{}{ "flavor": map[string]interface{}{ "default": "debian2G", "description": "Flavor for the server to be created", "hidden": true, "type": "string", }, }, "resources": map[string]interface{}{ "test_server": map[string]interface{}{ "properties": map[string]interface{}{ "flavor": "2 GB General Purpose v1", "image": "Debian 7 (Wheezy) (PVHVM)", "name": "test-server", }, "type": "OS::Nova::Server", }, }, } requests.go000066400000000000000000000366421335005366400344030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackspackage stacks import ( "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder is the interface options structs have to satisfy in order // to be used in the main Create operation in this package. Since many // extensions decorate or modify the common logic, it is useful for them to // satisfy a basic interface in order for them to be used. type CreateOptsBuilder interface { ToStackCreateMap() (map[string]interface{}, error) } // CreateOpts is the common options struct used in this package's Create // operation. type CreateOpts struct { // The name of the stack. It must start with an alphabetic character. Name string `json:"stack_name" required:"true"` // A structure that contains either the template file or url. Call the // associated methods to extract the information relevant to send in a create request. TemplateOpts *Template `json:"-" required:"true"` // Enables or disables deletion of all stack resources when a stack // creation fails. Default is true, meaning all resources are not deleted when // stack creation fails. DisableRollback *bool `json:"disable_rollback,omitempty"` // A structure that contains details for the environment of the stack. EnvironmentOpts *Environment `json:"-"` // User-defined parameters to pass to the template. Parameters map[string]interface{} `json:"parameters,omitempty"` // The timeout for stack creation in minutes. Timeout int `json:"timeout_mins,omitempty"` // A list of tags to assosciate with the Stack Tags []string `json:"-"` } // ToStackCreateMap casts a CreateOpts struct to a map. func (opts CreateOpts) ToStackCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } if err := opts.TemplateOpts.Parse(); err != nil { return nil, err } if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil { return nil, err } opts.TemplateOpts.fixFileRefs() b["template"] = string(opts.TemplateOpts.Bin) files := make(map[string]string) for k, v := range opts.TemplateOpts.Files { files[k] = v } if opts.EnvironmentOpts != nil { if err := opts.EnvironmentOpts.Parse(); err != nil { return nil, err } if err := opts.EnvironmentOpts.getRRFileContents(ignoreIfEnvironment); err != nil { return nil, err } opts.EnvironmentOpts.fixFileRefs() for k, v := range opts.EnvironmentOpts.Files { files[k] = v } b["environment"] = string(opts.EnvironmentOpts.Bin) } if len(files) > 0 { b["files"] = files } if opts.Tags != nil { b["tags"] = strings.Join(opts.Tags, ",") } return b, nil } // Create accepts a CreateOpts struct and creates a new stack using the values // provided. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToStackCreateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(createURL(c), b, &r.Body, nil) return } // AdoptOptsBuilder is the interface options structs have to satisfy in order // to be used in the Adopt function in this package. Since many // extensions decorate or modify the common logic, it is useful for them to // satisfy a basic interface in order for them to be used. type AdoptOptsBuilder interface { ToStackAdoptMap() (map[string]interface{}, error) } // AdoptOpts is the common options struct used in this package's Adopt // operation. type AdoptOpts struct { // Existing resources data represented as a string to add to the // new stack. Data returned by Abandon could be provided as AdoptsStackData. AdoptStackData string `json:"adopt_stack_data" required:"true"` // The name of the stack. It must start with an alphabetic character. Name string `json:"stack_name" required:"true"` // A structure that contains either the template file or url. Call the // associated methods to extract the information relevant to send in a create request. TemplateOpts *Template `json:"-" required:"true"` // The timeout for stack creation in minutes. Timeout int `json:"timeout_mins,omitempty"` // A structure that contains either the template file or url. Call the // associated methods to extract the information relevant to send in a create request. //TemplateOpts *Template `json:"-" required:"true"` // Enables or disables deletion of all stack resources when a stack // creation fails. Default is true, meaning all resources are not deleted when // stack creation fails. DisableRollback *bool `json:"disable_rollback,omitempty"` // A structure that contains details for the environment of the stack. EnvironmentOpts *Environment `json:"-"` // User-defined parameters to pass to the template. Parameters map[string]interface{} `json:"parameters,omitempty"` } // ToStackAdoptMap casts a CreateOpts struct to a map. func (opts AdoptOpts) ToStackAdoptMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } if err := opts.TemplateOpts.Parse(); err != nil { return nil, err } if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil { return nil, err } opts.TemplateOpts.fixFileRefs() b["template"] = string(opts.TemplateOpts.Bin) files := make(map[string]string) for k, v := range opts.TemplateOpts.Files { files[k] = v } if opts.EnvironmentOpts != nil { if err := opts.EnvironmentOpts.Parse(); err != nil { return nil, err } if err := opts.EnvironmentOpts.getRRFileContents(ignoreIfEnvironment); err != nil { return nil, err } opts.EnvironmentOpts.fixFileRefs() for k, v := range opts.EnvironmentOpts.Files { files[k] = v } b["environment"] = string(opts.EnvironmentOpts.Bin) } if len(files) > 0 { b["files"] = files } return b, nil } // Adopt accepts an AdoptOpts struct and creates a new stack using the resources // from another stack. func Adopt(c *gophercloud.ServiceClient, opts AdoptOptsBuilder) (r AdoptResult) { b, err := opts.ToStackAdoptMap() if err != nil { r.Err = err return } _, r.Err = c.Post(adoptURL(c), b, &r.Body, nil) return } // SortDir is a type for specifying in which direction to sort a list of stacks. type SortDir string // SortKey is a type for specifying by which key to sort a list of stacks. type SortKey string var ( // SortAsc is used to sort a list of stacks in ascending order. SortAsc SortDir = "asc" // SortDesc is used to sort a list of stacks in descending order. SortDesc SortDir = "desc" // SortName is used to sort a list of stacks by name. SortName SortKey = "name" // SortStatus is used to sort a list of stacks by status. SortStatus SortKey = "status" // SortCreatedAt is used to sort a list of stacks by date created. SortCreatedAt SortKey = "created_at" // SortUpdatedAt is used to sort a list of stacks by date updated. SortUpdatedAt SortKey = "updated_at" ) // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { ToStackListQuery() (string, error) } // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the network attributes you want to see returned. SortKey allows you to sort // by a particular network attribute. SortDir sets the direction, and is either // `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { Status string `q:"status"` Name string `q:"name"` Marker string `q:"marker"` Limit int `q:"limit"` SortKey SortKey `q:"sort_keys"` SortDir SortDir `q:"sort_dir"` } // ToStackListQuery formats a ListOpts into a query string. func (opts ListOpts) ToStackListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return "", err } return q.String(), nil } // List returns a Pager which allows you to iterate over a collection of // stacks. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(c) if opts != nil { query, err := opts.ToStackListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } createPage := func(r pagination.PageResult) pagination.Page { return StackPage{pagination.SinglePageBase(r)} } return pagination.NewPager(c, url, createPage) } // Get retreives a stack based on the stack name and stack ID. func Get(c *gophercloud.ServiceClient, stackName, stackID string) (r GetResult) { _, r.Err = c.Get(getURL(c, stackName, stackID), &r.Body, nil) return } // UpdateOptsBuilder is the interface options structs have to satisfy in order // to be used in the Update operation in this package. type UpdateOptsBuilder interface { ToStackUpdateMap() (map[string]interface{}, error) } // UpdatePatchOptsBuilder is the interface options structs have to satisfy in order // to be used in the UpdatePatch operation in this package type UpdatePatchOptsBuilder interface { ToStackUpdatePatchMap() (map[string]interface{}, error) } // UpdateOpts contains the common options struct used in this package's Update // and UpdatePatch operations. type UpdateOpts struct { // A structure that contains either the template file or url. Call the // associated methods to extract the information relevant to send in a create request. TemplateOpts *Template `json:"-"` // A structure that contains details for the environment of the stack. EnvironmentOpts *Environment `json:"-"` // User-defined parameters to pass to the template. Parameters map[string]interface{} `json:"parameters,omitempty"` // The timeout for stack creation in minutes. Timeout int `json:"timeout_mins,omitempty"` // A list of tags to associate with the Stack Tags []string `json:"-"` } // ToStackUpdateMap validates that a template was supplied and calls // the toStackUpdateMap private function. func (opts UpdateOpts) ToStackUpdateMap() (map[string]interface{}, error) { if opts.TemplateOpts == nil { return nil, ErrTemplateRequired{} } return toStackUpdateMap(opts) } // ToStackUpdatePatchMap calls the private function toStackUpdateMap // directly. func (opts UpdateOpts) ToStackUpdatePatchMap() (map[string]interface{}, error) { return toStackUpdateMap(opts) } // ToStackUpdateMap casts a CreateOpts struct to a map. func toStackUpdateMap(opts UpdateOpts) (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } files := make(map[string]string) if opts.TemplateOpts != nil { if err := opts.TemplateOpts.Parse(); err != nil { return nil, err } if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil { return nil, err } opts.TemplateOpts.fixFileRefs() b["template"] = string(opts.TemplateOpts.Bin) for k, v := range opts.TemplateOpts.Files { files[k] = v } } if opts.EnvironmentOpts != nil { if err := opts.EnvironmentOpts.Parse(); err != nil { return nil, err } if err := opts.EnvironmentOpts.getRRFileContents(ignoreIfEnvironment); err != nil { return nil, err } opts.EnvironmentOpts.fixFileRefs() for k, v := range opts.EnvironmentOpts.Files { files[k] = v } b["environment"] = string(opts.EnvironmentOpts.Bin) } if len(files) > 0 { b["files"] = files } if opts.Tags != nil { b["tags"] = strings.Join(opts.Tags, ",") } return b, nil } // Update accepts an UpdateOpts struct and updates an existing stack using the // http PUT verb with the values provided. opts.TemplateOpts is required. func Update(c *gophercloud.ServiceClient, stackName, stackID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToStackUpdateMap() if err != nil { r.Err = err return } _, r.Err = c.Put(updateURL(c, stackName, stackID), b, nil, nil) return } // Update accepts an UpdateOpts struct and updates an existing stack using the // http PATCH verb with the values provided. opts.TemplateOpts is not required. func UpdatePatch(c *gophercloud.ServiceClient, stackName, stackID string, opts UpdatePatchOptsBuilder) (r UpdateResult) { b, err := opts.ToStackUpdatePatchMap() if err != nil { r.Err = err return } _, r.Err = c.Patch(updateURL(c, stackName, stackID), b, nil, nil) return } // Delete deletes a stack based on the stack name and stack ID. func Delete(c *gophercloud.ServiceClient, stackName, stackID string) (r DeleteResult) { _, r.Err = c.Delete(deleteURL(c, stackName, stackID), nil) return } // PreviewOptsBuilder is the interface options structs have to satisfy in order // to be used in the Preview operation in this package. type PreviewOptsBuilder interface { ToStackPreviewMap() (map[string]interface{}, error) } // PreviewOpts contains the common options struct used in this package's Preview // operation. type PreviewOpts struct { // The name of the stack. It must start with an alphabetic character. Name string `json:"stack_name" required:"true"` // The timeout for stack creation in minutes. Timeout int `json:"timeout_mins" required:"true"` // A structure that contains either the template file or url. Call the // associated methods to extract the information relevant to send in a create request. TemplateOpts *Template `json:"-" required:"true"` // Enables or disables deletion of all stack resources when a stack // creation fails. Default is true, meaning all resources are not deleted when // stack creation fails. DisableRollback *bool `json:"disable_rollback,omitempty"` // A structure that contains details for the environment of the stack. EnvironmentOpts *Environment `json:"-"` // User-defined parameters to pass to the template. Parameters map[string]interface{} `json:"parameters,omitempty"` } // ToStackPreviewMap casts a PreviewOpts struct to a map. func (opts PreviewOpts) ToStackPreviewMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } if err := opts.TemplateOpts.Parse(); err != nil { return nil, err } if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil { return nil, err } opts.TemplateOpts.fixFileRefs() b["template"] = string(opts.TemplateOpts.Bin) files := make(map[string]string) for k, v := range opts.TemplateOpts.Files { files[k] = v } if opts.EnvironmentOpts != nil { if err := opts.EnvironmentOpts.Parse(); err != nil { return nil, err } if err := opts.EnvironmentOpts.getRRFileContents(ignoreIfEnvironment); err != nil { return nil, err } opts.EnvironmentOpts.fixFileRefs() for k, v := range opts.EnvironmentOpts.Files { files[k] = v } b["environment"] = string(opts.EnvironmentOpts.Bin) } if len(files) > 0 { b["files"] = files } return b, nil } // Preview accepts a PreviewOptsBuilder interface and creates a preview of a stack using the values // provided. func Preview(c *gophercloud.ServiceClient, opts PreviewOptsBuilder) (r PreviewResult) { b, err := opts.ToStackPreviewMap() if err != nil { r.Err = err return } _, r.Err = c.Post(previewURL(c), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Abandon deletes the stack with the provided stackName and stackID, but leaves its // resources intact, and returns data describing the stack and its resources. func Abandon(c *gophercloud.ServiceClient, stackName, stackID string) (r AbandonResult) { _, r.Err = c.Delete(abandonURL(c, stackName, stackID), &gophercloud.RequestOpts{ JSONResponse: &r.Body, OkCodes: []int{200}, }) return } results.go000066400000000000000000000207231335005366400342220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackspackage stacks import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreatedStack represents the object extracted from a Create operation. type CreatedStack struct { ID string `json:"id"` Links []gophercloud.Link `json:"links"` } // CreateResult represents the result of a Create operation. type CreateResult struct { gophercloud.Result } // Extract returns a pointer to a CreatedStack object and is called after a // Create operation. func (r CreateResult) Extract() (*CreatedStack, error) { var s struct { CreatedStack *CreatedStack `json:"stack"` } err := r.ExtractInto(&s) return s.CreatedStack, err } // AdoptResult represents the result of an Adopt operation. AdoptResult has the // same form as CreateResult. type AdoptResult struct { CreateResult } // StackPage is a pagination.Pager that is returned from a call to the List function. type StackPage struct { pagination.SinglePageBase } // IsEmpty returns true if a ListResult contains no Stacks. func (r StackPage) IsEmpty() (bool, error) { stacks, err := ExtractStacks(r) return len(stacks) == 0, err } // ListedStack represents an element in the slice extracted from a List operation. type ListedStack struct { CreationTime time.Time `json:"-"` Description string `json:"description"` ID string `json:"id"` Links []gophercloud.Link `json:"links"` Name string `json:"stack_name"` Status string `json:"stack_status"` StatusReason string `json:"stack_status_reason"` Tags []string `json:"tags"` UpdatedTime time.Time `json:"-"` } func (r *ListedStack) UnmarshalJSON(b []byte) error { type tmp ListedStack var s struct { tmp CreationTime string `json:"creation_time"` UpdatedTime string `json:"updated_time"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = ListedStack(s.tmp) if s.CreationTime != "" { t, err := time.Parse(time.RFC3339, s.CreationTime) if err != nil { t, err = time.Parse(gophercloud.RFC3339NoZ, s.CreationTime) if err != nil { return err } } r.CreationTime = t } if s.UpdatedTime != "" { t, err := time.Parse(time.RFC3339, s.UpdatedTime) if err != nil { t, err = time.Parse(gophercloud.RFC3339NoZ, s.UpdatedTime) if err != nil { return err } } r.UpdatedTime = t } return nil } // ExtractStacks extracts and returns a slice of ListedStack. It is used while iterating // over a stacks.List call. func ExtractStacks(r pagination.Page) ([]ListedStack, error) { var s struct { ListedStacks []ListedStack `json:"stacks"` } err := (r.(StackPage)).ExtractInto(&s) return s.ListedStacks, err } // RetrievedStack represents the object extracted from a Get operation. type RetrievedStack struct { Capabilities []interface{} `json:"capabilities"` CreationTime time.Time `json:"-"` Description string `json:"description"` DisableRollback bool `json:"disable_rollback"` ID string `json:"id"` Links []gophercloud.Link `json:"links"` NotificationTopics []interface{} `json:"notification_topics"` Outputs []map[string]interface{} `json:"outputs"` Parameters map[string]string `json:"parameters"` Name string `json:"stack_name"` Status string `json:"stack_status"` StatusReason string `json:"stack_status_reason"` Tags []string `json:"tags"` TemplateDescription string `json:"template_description"` Timeout int `json:"timeout_mins"` UpdatedTime time.Time `json:"-"` } func (r *RetrievedStack) UnmarshalJSON(b []byte) error { type tmp RetrievedStack var s struct { tmp CreationTime string `json:"creation_time"` UpdatedTime string `json:"updated_time"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = RetrievedStack(s.tmp) if s.CreationTime != "" { t, err := time.Parse(time.RFC3339, s.CreationTime) if err != nil { t, err = time.Parse(gophercloud.RFC3339NoZ, s.CreationTime) if err != nil { return err } } r.CreationTime = t } if s.UpdatedTime != "" { t, err := time.Parse(time.RFC3339, s.UpdatedTime) if err != nil { t, err = time.Parse(gophercloud.RFC3339NoZ, s.UpdatedTime) if err != nil { return err } } r.UpdatedTime = t } return nil } // GetResult represents the result of a Get operation. type GetResult struct { gophercloud.Result } // Extract returns a pointer to a RetrievedStack object and is called after a // Get operation. func (r GetResult) Extract() (*RetrievedStack, error) { var s struct { Stack *RetrievedStack `json:"stack"` } err := r.ExtractInto(&s) return s.Stack, err } // UpdateResult represents the result of a Update operation. type UpdateResult struct { gophercloud.ErrResult } // DeleteResult represents the result of a Delete operation. type DeleteResult struct { gophercloud.ErrResult } // PreviewedStack represents the result of a Preview operation. type PreviewedStack struct { Capabilities []interface{} `json:"capabilities"` CreationTime time.Time `json:"-"` Description string `json:"description"` DisableRollback bool `json:"disable_rollback"` ID string `json:"id"` Links []gophercloud.Link `json:"links"` Name string `json:"stack_name"` NotificationTopics []interface{} `json:"notification_topics"` Parameters map[string]string `json:"parameters"` Resources []interface{} `json:"resources"` TemplateDescription string `json:"template_description"` Timeout int `json:"timeout_mins"` UpdatedTime time.Time `json:"-"` } func (r *PreviewedStack) UnmarshalJSON(b []byte) error { type tmp PreviewedStack var s struct { tmp CreationTime string `json:"creation_time"` UpdatedTime string `json:"updated_time"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = PreviewedStack(s.tmp) if s.CreationTime != "" { t, err := time.Parse(time.RFC3339, s.CreationTime) if err != nil { t, err = time.Parse(gophercloud.RFC3339NoZ, s.CreationTime) if err != nil { return err } } r.CreationTime = t } if s.UpdatedTime != "" { t, err := time.Parse(time.RFC3339, s.UpdatedTime) if err != nil { t, err = time.Parse(gophercloud.RFC3339NoZ, s.UpdatedTime) if err != nil { return err } } r.UpdatedTime = t } return nil } // PreviewResult represents the result of a Preview operation. type PreviewResult struct { gophercloud.Result } // Extract returns a pointer to a PreviewedStack object and is called after a // Preview operation. func (r PreviewResult) Extract() (*PreviewedStack, error) { var s struct { PreviewedStack *PreviewedStack `json:"stack"` } err := r.ExtractInto(&s) return s.PreviewedStack, err } // AbandonedStack represents the result of an Abandon operation. type AbandonedStack struct { Status string `json:"status"` Name string `json:"name"` Template map[string]interface{} `json:"template"` Action string `json:"action"` ID string `json:"id"` Resources map[string]interface{} `json:"resources"` Files map[string]string `json:"files"` StackUserProjectID string `json:"stack_user_project_id"` ProjectID string `json:"project_id"` Environment map[string]interface{} `json:"environment"` } // AbandonResult represents the result of an Abandon operation. type AbandonResult struct { gophercloud.Result } // Extract returns a pointer to an AbandonedStack object and is called after an // Abandon operation. func (r AbandonResult) Extract() (*AbandonedStack, error) { var s *AbandonedStack err := r.ExtractInto(&s) return s, err } // String converts an AbandonResult to a string. This is useful to when passing // the result of an Abandon operation to an AdoptOpts AdoptStackData field. func (r AbandonResult) String() (string, error) { out, err := json.Marshal(r) return string(out), err } template.go000066400000000000000000000102611335005366400343300ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackspackage stacks import ( "fmt" "reflect" "strings" "github.com/gophercloud/gophercloud" ) // Template is a structure that represents OpenStack Heat templates type Template struct { TE } // TemplateFormatVersions is a map containing allowed variations of the template format version // Note that this contains the permitted variations of the _keys_ not the values. var TemplateFormatVersions = map[string]bool{ "HeatTemplateFormatVersion": true, "heat_template_version": true, "AWSTemplateFormatVersion": true, } // Validate validates the contents of the Template func (t *Template) Validate() error { if t.Parsed == nil { if err := t.Parse(); err != nil { return err } } var invalid string for key := range t.Parsed { if _, ok := TemplateFormatVersions[key]; ok { return nil } invalid = key } return ErrInvalidTemplateFormatVersion{Version: invalid} } // GetFileContents recursively parses a template to search for urls. These urls // are assumed to point to other templates (known in OpenStack Heat as child // templates). The contents of these urls are fetched and stored in the `Files` // parameter of the template structure. This is the only way that a user can // use child templates that are located in their filesystem; urls located on the // web (e.g. on github or swift) can be fetched directly by Heat engine. func (t *Template) getFileContents(te interface{}, ignoreIf igFunc, recurse bool) error { // initialize template if empty if t.Files == nil { t.Files = make(map[string]string) } if t.fileMaps == nil { t.fileMaps = make(map[string]string) } switch te.(type) { // if te is a map case map[string]interface{}, map[interface{}]interface{}: teMap, err := toStringKeys(te) if err != nil { return err } for k, v := range teMap { value, ok := v.(string) if !ok { // if the value is not a string, recursively parse that value if err := t.getFileContents(v, ignoreIf, recurse); err != nil { return err } } else if !ignoreIf(k, value) { // at this point, the k, v pair has a reference to an external template. // The assumption of heatclient is that value v is a reference // to a file in the users environment // create a new child template childTemplate := new(Template) // initialize child template // get the base location of the child template baseURL, err := gophercloud.NormalizePathURL(t.baseURL, value) if err != nil { return err } childTemplate.baseURL = baseURL childTemplate.client = t.client // fetch the contents of the child template if err := childTemplate.Parse(); err != nil { return err } // process child template recursively if required. This is // required if the child template itself contains references to // other templates if recurse { if err := childTemplate.getFileContents(childTemplate.Parsed, ignoreIf, recurse); err != nil { return err } } // update parent template with current child templates' content. // At this point, the child template has been parsed recursively. t.fileMaps[value] = childTemplate.URL t.Files[childTemplate.URL] = string(childTemplate.Bin) } } return nil // if te is a slice, call the function on each element of the slice. case []interface{}: teSlice := te.([]interface{}) for i := range teSlice { if err := t.getFileContents(teSlice[i], ignoreIf, recurse); err != nil { return err } } // if te is anything else, return case string, bool, float64, nil, int: return nil default: return gophercloud.ErrUnexpectedType{Actual: fmt.Sprintf("%v", reflect.TypeOf(te))} } return nil } // function to choose keys whose values are other template files func ignoreIfTemplate(key string, value interface{}) bool { // key must be either `get_file` or `type` for value to be a URL if key != "get_file" && key != "type" { return true } // value must be a string valueString, ok := value.(string) if !ok { return true } // `.template` and `.yaml` are allowed suffixes for template URLs when referred to by `type` if key == "type" && !(strings.HasSuffix(valueString, ".template") || strings.HasSuffix(valueString, ".yaml")) { return true } return false } template_test.go000066400000000000000000000076331335005366400354000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackspackage stacks import ( "fmt" "net/http" "net/url" "strings" "testing" th "github.com/gophercloud/gophercloud/testhelper" ) func TestTemplateValidation(t *testing.T) { templateJSON := new(Template) templateJSON.Bin = []byte(ValidJSONTemplate) err := templateJSON.Validate() th.AssertNoErr(t, err) templateYAML := new(Template) templateYAML.Bin = []byte(ValidYAMLTemplate) err = templateYAML.Validate() th.AssertNoErr(t, err) templateInvalid := new(Template) templateInvalid.Bin = []byte(InvalidTemplateNoVersion) if err = templateInvalid.Validate(); err == nil { t.Error("Template validation did not catch invalid template") } } func TestTemplateParsing(t *testing.T) { templateJSON := new(Template) templateJSON.Bin = []byte(ValidJSONTemplate) err := templateJSON.Parse() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ValidJSONTemplateParsed, templateJSON.Parsed) templateYAML := new(Template) templateYAML.Bin = []byte(ValidJSONTemplate) err = templateYAML.Parse() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ValidJSONTemplateParsed, templateYAML.Parsed) templateInvalid := new(Template) templateInvalid.Bin = []byte("Keep Austin Weird") err = templateInvalid.Parse() if err == nil { t.Error("Template parsing did not catch invalid template") } } func TestIgnoreIfTemplate(t *testing.T) { var keyValueTests = []struct { key string value interface{} out bool }{ {"not_get_file", "afksdf", true}, {"not_type", "sdfd", true}, {"get_file", "shdfuisd", false}, {"type", "dfsdfsd", true}, {"type", "sdfubsduf.yaml", false}, {"type", "sdfsdufs.template", false}, {"type", "sdfsdf.file", true}, {"type", map[string]string{"key": "value"}, true}, } var result bool for _, kv := range keyValueTests { result = ignoreIfTemplate(kv.key, kv.value) if result != kv.out { t.Errorf("key: %v, value: %v expected: %v, actual: %v", kv.key, kv.value, result, kv.out) } } } func TestGetFileContents(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() baseurl, err := getBasePath() th.AssertNoErr(t, err) fakeURL := strings.Join([]string{baseurl, "my_nova.yaml"}, "/") urlparsed, err := url.Parse(fakeURL) th.AssertNoErr(t, err) myNovaContent := `heat_template_version: 2014-10-16 parameters: flavor: type: string description: Flavor for the server to be created default: 4353 hidden: true resources: test_server: type: "OS::Nova::Server" properties: name: test-server flavor: 2 GB General Purpose v1 image: Debian 7 (Wheezy) (PVHVM) networks: - {uuid: 11111111-1111-1111-1111-111111111111}` th.Mux.HandleFunc(urlparsed.Path, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, myNovaContent) }) client := fakeClient{BaseClient: getHTTPClient()} te := new(Template) te.Bin = []byte(`heat_template_version: 2015-04-30 resources: my_server: type: my_nova.yaml`) te.client = client err = te.Parse() th.AssertNoErr(t, err) err = te.getFileContents(te.Parsed, ignoreIfTemplate, true) th.AssertNoErr(t, err) expectedFiles := map[string]string{ "my_nova.yaml": `heat_template_version: 2014-10-16 parameters: flavor: type: string description: Flavor for the server to be created default: 4353 hidden: true resources: test_server: type: "OS::Nova::Server" properties: name: test-server flavor: 2 GB General Purpose v1 image: Debian 7 (Wheezy) (PVHVM) networks: - {uuid: 11111111-1111-1111-1111-111111111111}`} th.AssertEquals(t, expectedFiles["my_nova.yaml"], te.Files[fakeURL]) te.fixFileRefs() expectedParsed := map[string]interface{}{ "heat_template_version": "2015-04-30", "resources": map[string]interface{}{ "my_server": map[string]interface{}{ "type": fakeURL, }, }, } te.Parse() th.AssertDeepEquals(t, expectedParsed, te.Parsed) } testing/000077500000000000000000000000001335005366400336435ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stacksdoc.go000066400000000000000000000000531335005366400347350ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stacks/testing// orchestration_stacks_v1 package testing fixtures.go000066400000000000000000000356031335005366400360520ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stacks/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) var Create_time, _ = time.Parse(time.RFC3339, "2018-06-26T07:58:17Z") var Updated_time, _ = time.Parse(time.RFC3339, "2018-06-26T07:59:17Z") // CreateExpected represents the expected object from a Create request. var CreateExpected = &stacks.CreatedStack{ ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87", Links: []gophercloud.Link{ { Href: "http://168.28.170.117:8004/v1/98606384f58drad0bhdb7d02779549ac/stacks/stackcreated/16ef0584-4458-41eb-87c8-0dc8d5f66c87", Rel: "self", }, }, } // CreateOutput represents the response body from a Create request. const CreateOutput = ` { "stack": { "id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87", "links": [ { "href": "http://168.28.170.117:8004/v1/98606384f58drad0bhdb7d02779549ac/stacks/stackcreated/16ef0584-4458-41eb-87c8-0dc8d5f66c87", "rel": "self" } ] } }` // HandleCreateSuccessfully creates an HTTP handler at `/stacks` on the test handler mux // that responds with a `Create` response. func HandleCreateSuccessfully(t *testing.T, output string) { th.Mux.HandleFunc("/stacks", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, output) }) } // ListExpected represents the expected object from a List request. var ListExpected = []stacks.ListedStack{ { Description: "Simple template to test heat commands", Links: []gophercloud.Link{ { Href: "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87", Rel: "self", }, }, StatusReason: "Stack CREATE completed successfully", Name: "postman_stack", CreationTime: Create_time, Status: "CREATE_COMPLETE", ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87", Tags: []string{"rackspace", "atx"}, }, { Description: "Simple template to test heat commands", Links: []gophercloud.Link{ { Href: "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/gophercloud-test-stack-2/db6977b2-27aa-4775-9ae7-6213212d4ada", Rel: "self", }, }, StatusReason: "Stack successfully updated", Name: "gophercloud-test-stack-2", CreationTime: Create_time, UpdatedTime: Updated_time, Status: "UPDATE_COMPLETE", ID: "db6977b2-27aa-4775-9ae7-6213212d4ada", Tags: []string{"sfo", "satx"}, }, } // FullListOutput represents the response body from a List request without a marker. const FullListOutput = ` { "stacks": [ { "description": "Simple template to test heat commands", "links": [ { "href": "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87", "rel": "self" } ], "stack_status_reason": "Stack CREATE completed successfully", "stack_name": "postman_stack", "creation_time": "2018-06-26T07:58:17Z", "updated_time": null, "stack_status": "CREATE_COMPLETE", "id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87", "tags": ["rackspace", "atx"] }, { "description": "Simple template to test heat commands", "links": [ { "href": "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/gophercloud-test-stack-2/db6977b2-27aa-4775-9ae7-6213212d4ada", "rel": "self" } ], "stack_status_reason": "Stack successfully updated", "stack_name": "gophercloud-test-stack-2", "creation_time": "2018-06-26T07:58:17Z", "updated_time": "2018-06-26T07:59:17Z", "stack_status": "UPDATE_COMPLETE", "id": "db6977b2-27aa-4775-9ae7-6213212d4ada", "tags": ["sfo", "satx"] } ] } ` // HandleListSuccessfully creates an HTTP handler at `/stacks` on the test handler mux // that responds with a `List` response. func HandleListSuccessfully(t *testing.T, output string) { th.Mux.HandleFunc("/stacks", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, output) case "db6977b2-27aa-4775-9ae7-6213212d4ada": fmt.Fprintf(w, `[]`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) } // GetExpected represents the expected object from a Get request. var GetExpected = &stacks.RetrievedStack{ DisableRollback: true, Description: "Simple template to test heat commands", Parameters: map[string]string{ "flavor": "m1.tiny", "OS::stack_name": "postman_stack", "OS::stack_id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87", }, StatusReason: "Stack CREATE completed successfully", Name: "postman_stack", Outputs: []map[string]interface{}{}, CreationTime: Create_time, Links: []gophercloud.Link{ { Href: "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87", Rel: "self", }, }, Capabilities: []interface{}{}, NotificationTopics: []interface{}{}, Status: "CREATE_COMPLETE", ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87", TemplateDescription: "Simple template to test heat commands", Tags: []string{"rackspace", "atx"}, } // GetOutput represents the response body from a Get request. const GetOutput = ` { "stack": { "disable_rollback": true, "description": "Simple template to test heat commands", "parameters": { "flavor": "m1.tiny", "OS::stack_name": "postman_stack", "OS::stack_id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87" }, "stack_status_reason": "Stack CREATE completed successfully", "stack_name": "postman_stack", "outputs": [], "creation_time": "2018-06-26T07:58:17Z", "links": [ { "href": "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87", "rel": "self" } ], "capabilities": [], "notification_topics": [], "timeout_mins": null, "stack_status": "CREATE_COMPLETE", "updated_time": null, "id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87", "template_description": "Simple template to test heat commands", "tags": ["rackspace", "atx"] } } ` // HandleGetSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87` // on the test handler mux that responds with a `Get` response. func HandleGetSuccessfully(t *testing.T, output string) { th.Mux.HandleFunc("/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, output) }) } // HandleUpdateSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87` // on the test handler mux that responds with an `Update` response. func HandleUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/stacks/gophercloud-test-stack-2/db6977b2-27aa-4775-9ae7-6213212d4ada", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) }) } // HandleUpdatePatchSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87` // on the test handler mux that responds with an `Update` response. func HandleUpdatePatchSuccessfully(t *testing.T) { th.Mux.HandleFunc("/stacks/gophercloud-test-stack-2/db6977b2-27aa-4775-9ae7-6213212d4ada", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) }) } // HandleDeleteSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87` // on the test handler mux that responds with a `Delete` response. func HandleDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc("/stacks/gophercloud-test-stack-2/db6977b2-27aa-4775-9ae7-6213212d4ada", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusNoContent) }) } // GetExpected represents the expected object from a Get request. var PreviewExpected = &stacks.PreviewedStack{ DisableRollback: true, Description: "Simple template to test heat commands", Parameters: map[string]string{ "flavor": "m1.tiny", "OS::stack_name": "postman_stack", "OS::stack_id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87", }, Name: "postman_stack", CreationTime: Create_time, Links: []gophercloud.Link{ { Href: "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87", Rel: "self", }, }, Capabilities: []interface{}{}, NotificationTopics: []interface{}{}, ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87", TemplateDescription: "Simple template to test heat commands", } // HandlePreviewSuccessfully creates an HTTP handler at `/stacks/preview` // on the test handler mux that responds with a `Preview` response. func HandlePreviewSuccessfully(t *testing.T, output string) { th.Mux.HandleFunc("/stacks/preview", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, output) }) } // AbandonExpected represents the expected object from an Abandon request. var AbandonExpected = &stacks.AbandonedStack{ Status: "COMPLETE", Name: "postman_stack", Template: map[string]interface{}{ "heat_template_version": "2013-05-23", "description": "Simple template to test heat commands", "parameters": map[string]interface{}{ "flavor": map[string]interface{}{ "default": "m1.tiny", "type": "string", }, }, "resources": map[string]interface{}{ "hello_world": map[string]interface{}{ "type": "OS::Nova::Server", "properties": map[string]interface{}{ "key_name": "heat_key", "flavor": map[string]interface{}{ "get_param": "flavor", }, "image": "ad091b52-742f-469e-8f3c-fd81cadf0743", "user_data": "#!/bin/bash -xv\necho \"hello world\" > /root/hello-world.txt\n", }, }, }, }, Action: "CREATE", ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87", Resources: map[string]interface{}{ "hello_world": map[string]interface{}{ "status": "COMPLETE", "name": "hello_world", "resource_id": "8a310d36-46fc-436f-8be4-37a696b8ac63", "action": "CREATE", "type": "OS::Nova::Server", }, }, Files: map[string]string{ "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "heat_template_version: 2014-10-16\nparameters:\n flavor:\n type: string\n description: Flavor for the server to be created\n default: 4353\n hidden: true\nresources:\n test_server:\n type: \"OS::Nova::Server\"\n properties:\n name: test-server\n flavor: 2 GB General Purpose v1\n image: Debian 7 (Wheezy) (PVHVM)\n", }, StackUserProjectID: "897686", ProjectID: "897686", Environment: map[string]interface{}{ "encrypted_param_names": make([]map[string]interface{}, 0), "parameter_defaults": make(map[string]interface{}), "parameters": make(map[string]interface{}), "resource_registry": map[string]interface{}{ "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml", "resources": make(map[string]interface{}), }, }, } // AbandonOutput represents the response body from an Abandon request. const AbandonOutput = ` { "status": "COMPLETE", "name": "postman_stack", "template": { "heat_template_version": "2013-05-23", "description": "Simple template to test heat commands", "parameters": { "flavor": { "default": "m1.tiny", "type": "string" } }, "resources": { "hello_world": { "type": "OS::Nova::Server", "properties": { "key_name": "heat_key", "flavor": { "get_param": "flavor" }, "image": "ad091b52-742f-469e-8f3c-fd81cadf0743", "user_data": "#!/bin/bash -xv\necho \"hello world\" > /root/hello-world.txt\n" } } } }, "action": "CREATE", "id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87", "resources": { "hello_world": { "status": "COMPLETE", "name": "hello_world", "resource_id": "8a310d36-46fc-436f-8be4-37a696b8ac63", "action": "CREATE", "type": "OS::Nova::Server" } }, "files": { "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "heat_template_version: 2014-10-16\nparameters:\n flavor:\n type: string\n description: Flavor for the server to be created\n default: 4353\n hidden: true\nresources:\n test_server:\n type: \"OS::Nova::Server\"\n properties:\n name: test-server\n flavor: 2 GB General Purpose v1\n image: Debian 7 (Wheezy) (PVHVM)\n" }, "environment": { "encrypted_param_names": [], "parameter_defaults": {}, "parameters": {}, "resource_registry": { "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml", "resources": {} } }, "stack_user_project_id": "897686", "project_id": "897686" }` // HandleAbandonSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87/abandon` // on the test handler mux that responds with an `Abandon` response. func HandleAbandonSuccessfully(t *testing.T, output string) { th.Mux.HandleFunc("/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c8/abandon", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, output) }) } requests_test.go000066400000000000000000000143331335005366400371100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stacks/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreateStack(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateSuccessfully(t, CreateOutput) template := new(stacks.Template) template.Bin = []byte(` { "heat_template_version": "2013-05-23", "description": "Simple template to test heat commands", "parameters": { "flavor": { "default": "m1.tiny", "type": "string" } } }`) createOpts := stacks.CreateOpts{ Name: "stackcreated", Timeout: 60, TemplateOpts: template, DisableRollback: gophercloud.Disabled, } actual, err := stacks.Create(fake.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) expected := CreateExpected th.AssertDeepEquals(t, expected, actual) } func TestCreateStackMissingRequiredInOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateSuccessfully(t, CreateOutput) template := new(stacks.Template) template.Bin = []byte(` { "heat_template_version": "2013-05-23", "description": "Simple template to test heat commands", "parameters": { "flavor": { "default": "m1.tiny", "type": "string" } } }`) createOpts := stacks.CreateOpts{ DisableRollback: gophercloud.Disabled, } r := stacks.Create(fake.ServiceClient(), createOpts) th.AssertEquals(t, "Missing input for argument [Name]", r.Err.Error()) } func TestAdoptStack(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateSuccessfully(t, CreateOutput) template := new(stacks.Template) template.Bin = []byte(` { "stack_name": "postman_stack", "template": { "heat_template_version": "2013-05-23", "description": "Simple template to test heat commands", "parameters": { "flavor": { "default": "m1.tiny", "type": "string" } }, "resources": { "hello_world": { "type":"OS::Nova::Server", "properties": { "key_name": "heat_key", "flavor": { "get_param": "flavor" }, "image": "ad091b52-742f-469e-8f3c-fd81cadf0743", "user_data": "#!/bin/bash -xv\necho \"hello world\" > /root/hello-world.txt\n" } } } } }`) adoptOpts := stacks.AdoptOpts{ AdoptStackData: `{environment{parameters{}}}`, Name: "stackcreated", Timeout: 60, TemplateOpts: template, DisableRollback: gophercloud.Disabled, } actual, err := stacks.Adopt(fake.ServiceClient(), adoptOpts).Extract() th.AssertNoErr(t, err) expected := CreateExpected th.AssertDeepEquals(t, expected, actual) } func TestListStack(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListSuccessfully(t, FullListOutput) count := 0 err := stacks.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := stacks.ExtractStacks(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ListExpected, actual) return true, nil }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) } func TestGetStack(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t, GetOutput) actual, err := stacks.Get(fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c87").Extract() th.AssertNoErr(t, err) expected := GetExpected th.AssertDeepEquals(t, expected, actual) } func TestUpdateStack(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateSuccessfully(t) template := new(stacks.Template) template.Bin = []byte(` { "heat_template_version": "2013-05-23", "description": "Simple template to test heat commands", "parameters": { "flavor": { "default": "m1.tiny", "type": "string" } } }`) updateOpts := &stacks.UpdateOpts{ TemplateOpts: template, } err := stacks.Update(fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada", updateOpts).ExtractErr() th.AssertNoErr(t, err) } func TestUpdateStackNoTemplate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateSuccessfully(t) parameters := make(map[string]interface{}) parameters["flavor"] = "m1.tiny" updateOpts := &stacks.UpdateOpts{ Parameters: parameters, } expected := stacks.ErrTemplateRequired{} err := stacks.Update(fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada", updateOpts).ExtractErr() th.AssertEquals(t, expected, err) } func TestUpdatePatchStack(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdatePatchSuccessfully(t) parameters := make(map[string]interface{}) parameters["flavor"] = "m1.tiny" updateOpts := &stacks.UpdateOpts{ Parameters: parameters, } err := stacks.UpdatePatch(fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada", updateOpts).ExtractErr() th.AssertNoErr(t, err) } func TestDeleteStack(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSuccessfully(t) err := stacks.Delete(fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada").ExtractErr() th.AssertNoErr(t, err) } func TestPreviewStack(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePreviewSuccessfully(t, GetOutput) template := new(stacks.Template) template.Bin = []byte(` { "heat_template_version": "2013-05-23", "description": "Simple template to test heat commands", "parameters": { "flavor": { "default": "m1.tiny", "type": "string" } } }`) previewOpts := stacks.PreviewOpts{ Name: "stackcreated", Timeout: 60, TemplateOpts: template, DisableRollback: gophercloud.Disabled, } actual, err := stacks.Preview(fake.ServiceClient(), previewOpts).Extract() th.AssertNoErr(t, err) expected := PreviewExpected th.AssertDeepEquals(t, expected, actual) } func TestAbandonStack(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAbandonSuccessfully(t, AbandonOutput) actual, err := stacks.Abandon(fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c8").Extract() th.AssertNoErr(t, err) expected := AbandonExpected th.AssertDeepEquals(t, expected, actual) } urls.go000066400000000000000000000015141335005366400335030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackspackage stacks import "github.com/gophercloud/gophercloud" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("stacks") } func adoptURL(c *gophercloud.ServiceClient) string { return createURL(c) } func listURL(c *gophercloud.ServiceClient) string { return createURL(c) } func getURL(c *gophercloud.ServiceClient, name, id string) string { return c.ServiceURL("stacks", name, id) } func updateURL(c *gophercloud.ServiceClient, name, id string) string { return getURL(c, name, id) } func deleteURL(c *gophercloud.ServiceClient, name, id string) string { return getURL(c, name, id) } func previewURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("stacks", "preview") } func abandonURL(c *gophercloud.ServiceClient, name, id string) string { return c.ServiceURL("stacks", name, id, "abandon") } utils.go000066400000000000000000000103701335005366400336560ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackspackage stacks import ( "encoding/json" "fmt" "io/ioutil" "net/http" "path/filepath" "reflect" "strings" "github.com/gophercloud/gophercloud" "gopkg.in/yaml.v2" ) // Client is an interface that expects a Get method similar to http.Get. This // is needed for unit testing, since we can mock an http client. Thus, the // client will usually be an http.Client EXCEPT in unit tests. type Client interface { Get(string) (*http.Response, error) } // TE is a base structure for both Template and Environment type TE struct { // Bin stores the contents of the template or environment. Bin []byte // URL stores the URL of the template. This is allowed to be a 'file://' // for local files. URL string // Parsed contains a parsed version of Bin. Since there are 2 different // fields referring to the same value, you must be careful when accessing // this filed. Parsed map[string]interface{} // Files contains a mapping between the urls in templates to their contents. Files map[string]string // fileMaps is a map used internally when determining Files. fileMaps map[string]string // baseURL represents the location of the template or environment file. baseURL string // client is an interface which allows TE to fetch contents from URLS client Client } // Fetch fetches the contents of a TE from its URL. Once a TE structure has a // URL, call the fetch method to fetch the contents. func (t *TE) Fetch() error { // if the baseURL is not provided, use the current directors as the base URL if t.baseURL == "" { u, err := getBasePath() if err != nil { return err } t.baseURL = u } // if the contents are already present, do nothing. if t.Bin != nil { return nil } // get a fqdn from the URL using the baseURL of the TE. For local files, // the URL's will have the `file` scheme. u, err := gophercloud.NormalizePathURL(t.baseURL, t.URL) if err != nil { return err } t.URL = u // get an HTTP client if none present if t.client == nil { t.client = getHTTPClient() } // use the client to fetch the contents of the TE resp, err := t.client.Get(t.URL) if err != nil { return err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return err } t.Bin = body return nil } // get the basepath of the TE func getBasePath() (string, error) { basePath, err := filepath.Abs(".") if err != nil { return "", err } u, err := gophercloud.NormalizePathURL("", basePath) if err != nil { return "", err } return u, nil } // get a an HTTP client to retrieve URL's. This client allows the use of `file` // scheme since we may need to fetch files from users filesystem func getHTTPClient() Client { transport := &http.Transport{} transport.RegisterProtocol("file", http.NewFileTransport(http.Dir("/"))) return &http.Client{Transport: transport} } // Parse will parse the contents and then validate. The contents MUST be either JSON or YAML. func (t *TE) Parse() error { if err := t.Fetch(); err != nil { return err } if jerr := json.Unmarshal(t.Bin, &t.Parsed); jerr != nil { if yerr := yaml.Unmarshal(t.Bin, &t.Parsed); yerr != nil { return ErrInvalidDataFormat{} } } return t.Validate() } // Validate validates the contents of TE func (t *TE) Validate() error { return nil } // igfunc is a parameter used by GetFileContents and GetRRFileContents to check // for valid URL's. type igFunc func(string, interface{}) bool // convert map[interface{}]interface{} to map[string]interface{} func toStringKeys(m interface{}) (map[string]interface{}, error) { switch m.(type) { case map[string]interface{}, map[interface{}]interface{}: typedMap := make(map[string]interface{}) if _, ok := m.(map[interface{}]interface{}); ok { for k, v := range m.(map[interface{}]interface{}) { typedMap[k.(string)] = v } } else { typedMap = m.(map[string]interface{}) } return typedMap, nil default: return nil, gophercloud.ErrUnexpectedType{Expected: "map[string]interface{}/map[interface{}]interface{}", Actual: fmt.Sprintf("%v", reflect.TypeOf(m))} } } // fix the reference to files by replacing relative URL's by absolute // URL's func (t *TE) fixFileRefs() { tStr := string(t.Bin) if t.fileMaps == nil { return } for k, v := range t.fileMaps { tStr = strings.Replace(tStr, k, v, -1) } t.Bin = []byte(tStr) } utils_test.go000066400000000000000000000046021335005366400347160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stackspackage stacks import ( "fmt" "net/http" "net/url" "strings" "testing" th "github.com/gophercloud/gophercloud/testhelper" ) func TestTEFixFileRefs(t *testing.T) { te := TE{ Bin: []byte(`string_to_replace: my fair lady`), fileMaps: map[string]string{ "string_to_replace": "london bridge is falling down", }, } te.fixFileRefs() th.AssertEquals(t, string(te.Bin), `london bridge is falling down: my fair lady`) } func TestToStringKeys(t *testing.T) { var test1 interface{} = map[interface{}]interface{}{ "Adam": "Smith", "Isaac": "Newton", } result1, err := toStringKeys(test1) th.AssertNoErr(t, err) expected := map[string]interface{}{ "Adam": "Smith", "Isaac": "Newton", } th.AssertDeepEquals(t, result1, expected) } func TestGetBasePath(t *testing.T) { _, err := getBasePath() th.AssertNoErr(t, err) } // test if HTTP client can read file type URLS. Read the URL of this file // because if this test is running, it means this file _must_ exist func TestGetHTTPClient(t *testing.T) { client := getHTTPClient() baseurl, err := getBasePath() th.AssertNoErr(t, err) resp, err := client.Get(baseurl) th.AssertNoErr(t, err) th.AssertEquals(t, resp.StatusCode, 200) } // Implement a fakeclient that can be used to mock out HTTP requests type fakeClient struct { BaseClient Client } // this client's Get method first changes the URL given to point to // testhelper's (th) endpoints. This is done because the http Mux does not seem // to work for fqdns with the `file` scheme func (c fakeClient) Get(url string) (*http.Response, error) { newurl := strings.Replace(url, "file://", th.Endpoint(), 1) return c.BaseClient.Get(newurl) } // test the fetch function func TestFetch(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() baseurl, err := getBasePath() th.AssertNoErr(t, err) fakeURL := strings.Join([]string{baseurl, "file.yaml"}, "/") urlparsed, err := url.Parse(fakeURL) th.AssertNoErr(t, err) th.Mux.HandleFunc(urlparsed.Path, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") w.Header().Set("Content-Type", "application/jason") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "Fee-fi-fo-fum") }) client := fakeClient{BaseClient: getHTTPClient()} te := TE{ URL: "file.yaml", client: client, } err = te.Fetch() th.AssertNoErr(t, err) th.AssertEquals(t, fakeURL, te.URL) th.AssertEquals(t, "Fee-fi-fo-fum", string(te.Bin)) } stacktemplates/000077500000000000000000000000001335005366400337225ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1doc.go000066400000000000000000000024261335005366400350220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stacktemplates/* Package stacktemplates provides operations for working with Heat templates. A Cloud Orchestration template is a portable file, written in a user-readable language, that describes how a set of resources should be assembled and what software should be installed in order to produce a working stack. The template specifies what resources should be used, what attributes can be set, and other parameters that are critical to the successful, repeatable automation of a specific application stack. Example to get stack template temp, err := stacktemplates.Get(client, stack.Name, stack.ID).Extract() if err != nil { panic(err) } fmt.Println("Get Stack Template for Stack ", stack.Name) fmt.Println(string(temp)) Example to validate stack template f2, err := ioutil.ReadFile("template.err.yaml") if err != nil { panic(err) } fmt.Println(string(f2)) validateOpts := &stacktemplates.ValidateOpts{ Template: string(f2), } validate_result, err := stacktemplates.Validate(client, validateOpts).Extract() if err != nil { // If validate failed, you will get error message here fmt.Println("Validate failed: ", err.Error()) } else { fmt.Println(validate_result.Parameters) } */ package stacktemplates requests.go000066400000000000000000000024131335005366400361240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stacktemplatespackage stacktemplates import "github.com/gophercloud/gophercloud" // Get retreives data for the given stack template. func Get(c *gophercloud.ServiceClient, stackName, stackID string) (r GetResult) { _, r.Err = c.Get(getURL(c, stackName, stackID), &r.Body, nil) return } // ValidateOptsBuilder describes struct types that can be accepted by the Validate call. // The ValidateOpts struct in this package does. type ValidateOptsBuilder interface { ToStackTemplateValidateMap() (map[string]interface{}, error) } // ValidateOpts specifies the template validation parameters. type ValidateOpts struct { Template string `json:"template" or:"TemplateURL"` TemplateURL string `json:"template_url" or:"Template"` } // ToStackTemplateValidateMap assembles a request body based on the contents of a ValidateOpts. func (opts ValidateOpts) ToStackTemplateValidateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // Validate validates the given stack template. func Validate(c *gophercloud.ServiceClient, opts ValidateOptsBuilder) (r ValidateResult) { b, err := opts.ToStackTemplateValidateMap() if err != nil { r.Err = err return } _, r.Err = c.Post(validateURL(c), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000021561335005366400357560ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stacktemplatespackage stacktemplates import ( "encoding/json" "github.com/gophercloud/gophercloud" ) // GetResult represents the result of a Get operation. type GetResult struct { gophercloud.Result } // Extract returns the JSON template and is called after a Get operation. func (r GetResult) Extract() ([]byte, error) { if r.Err != nil { return nil, r.Err } template, err := json.MarshalIndent(r.Body, "", " ") if err != nil { return nil, err } return template, nil } // ValidatedTemplate represents the parsed object returned from a Validate request. type ValidatedTemplate struct { Description string `json:"Description"` Parameters map[string]interface{} `json:"Parameters"` ParameterGroups map[string]interface{} `json:"ParameterGroups"` } // ValidateResult represents the result of a Validate operation. type ValidateResult struct { gophercloud.Result } // Extract returns a pointer to a ValidatedTemplate object and is called after a // Validate operation. func (r ValidateResult) Extract() (*ValidatedTemplate, error) { var s *ValidatedTemplate err := r.ExtractInto(&s) return s, err } testing/000077500000000000000000000000001335005366400353775ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stacktemplatesdoc.go000066400000000000000000000000631335005366400364720ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stacktemplates/testing// orchestration_stacktemplates_v1 package testing fixtures.go000066400000000000000000000063331335005366400376040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stacktemplates/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacktemplates" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) // GetExpected represents the expected object from a Get request. var GetExpected = "{\n \"description\": \"Simple template to test heat commands\",\n \"heat_template_version\": \"2013-05-23\",\n \"parameters\": {\n \"flavor\": {\n \"default\": \"m1.tiny\",\n \"type\": \"string\"\n }\n },\n \"resources\": {\n \"hello_world\": {\n \"properties\": {\n \"flavor\": {\n \"get_param\": \"flavor\"\n },\n \"image\": \"ad091b52-742f-469e-8f3c-fd81cadf0743\",\n \"key_name\": \"heat_key\"\n },\n \"type\": \"OS::Nova::Server\"\n }\n }\n}" // GetOutput represents the response body from a Get request. const GetOutput = ` { "heat_template_version": "2013-05-23", "description": "Simple template to test heat commands", "parameters": { "flavor": { "default": "m1.tiny", "type": "string" } }, "resources": { "hello_world": { "type": "OS::Nova::Server", "properties": { "key_name": "heat_key", "flavor": { "get_param": "flavor" }, "image": "ad091b52-742f-469e-8f3c-fd81cadf0743" } } } }` // HandleGetSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87/template` // on the test handler mux that responds with a `Get` response. func HandleGetSuccessfully(t *testing.T, output string) { th.Mux.HandleFunc("/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87/template", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, output) }) } // ValidateExpected represents the expected object from a Validate request. var ValidateExpected = &stacktemplates.ValidatedTemplate{ Description: "Simple template to test heat commands", Parameters: map[string]interface{}{ "flavor": map[string]interface{}{ "Default": "m1.tiny", "Type": "String", "NoEcho": "false", "Description": "", "Label": "flavor", }, }, } // ValidateOutput represents the response body from a Validate request. const ValidateOutput = ` { "Description": "Simple template to test heat commands", "Parameters": { "flavor": { "Default": "m1.tiny", "Type": "String", "NoEcho": "false", "Description": "", "Label": "flavor" } } }` // HandleValidateSuccessfully creates an HTTP handler at `/validate` // on the test handler mux that responds with a `Validate` response. func HandleValidateSuccessfully(t *testing.T, output string) { th.Mux.HandleFunc("/validate", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, output) }) } requests_test.go000066400000000000000000000030021335005366400406330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stacktemplates/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacktemplates" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestGetTemplate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t, GetOutput) actual, err := stacktemplates.Get(fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c87").Extract() th.AssertNoErr(t, err) expected := GetExpected th.AssertDeepEquals(t, expected, string(actual)) } func TestValidateTemplate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleValidateSuccessfully(t, ValidateOutput) opts := stacktemplates.ValidateOpts{ Template: `{ "heat_template_version": "2013-05-23", "description": "Simple template to test heat commands", "parameters": { "flavor": { "default": "m1.tiny", "type": "string" } }, "resources": { "hello_world": { "type": "OS::Nova::Server", "properties": { "key_name": "heat_key", "flavor": { "get_param": "flavor" }, "image": "ad091b52-742f-469e-8f3c-fd81cadf0743", "user_data": "#!/bin/bash -xv\necho \"hello world\" > /root/hello-world.txt\n" } } } }`, } actual, err := stacktemplates.Validate(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) expected := ValidateExpected th.AssertDeepEquals(t, expected, actual) } urls.go000066400000000000000000000004601335005366400352360ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/orchestration/v1/stacktemplatespackage stacktemplates import "github.com/gophercloud/gophercloud" func getURL(c *gophercloud.ServiceClient, stackName, stackID string) string { return c.ServiceURL("stacks", stackName, stackID, "template") } func validateURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("validate") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/000077500000000000000000000000001335005366400313015ustar00rootroot00000000000000apiversions/000077500000000000000000000000001335005366400335645ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystemsdoc.go000066400000000000000000000002521335005366400346570ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/apiversions// Package apiversions provides information and interaction with the different // API versions for the Shared File System service, code-named Manila. package apiversions errors.go000066400000000000000000000010271335005366400354270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/apiversionspackage apiversions import ( "fmt" ) // ErrVersionNotFound is the error when the requested API version // could not be found. type ErrVersionNotFound struct{} func (e ErrVersionNotFound) Error() string { return fmt.Sprintf("Unable to find requested API version") } // ErrMultipleVersionsFound is the error when a request for an API // version returns multiple results. type ErrMultipleVersionsFound struct { Count int } func (e ErrMultipleVersionsFound) Error() string { return fmt.Sprintf("Found %d API versions", e.Count) } requests.go000066400000000000000000000011151335005366400357640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/apiversionspackage apiversions import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List lists all the API versions available to end-users. func List(c *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(c, listURL(c), func(r pagination.PageResult) pagination.Page { return APIVersionPage{pagination.SinglePageBase(r)} }) } // Get will get a specific API version, specified by major ID. func Get(client *gophercloud.ServiceClient, v string) (r GetResult) { _, r.Err = client.Get(getURL(client, v), &r.Body, nil) return } results.go000066400000000000000000000035451335005366400356230ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/apiversionspackage apiversions import ( "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // APIVersion represents an API version for the Shared File System service. type APIVersion struct { // ID is the unique identifier of the API version. ID string `json:"id"` // MinVersion is the minimum microversion supported. MinVersion string `json:"min_version"` // Status is the API versions status. Status string `json:"status"` // Updated is the date when the API was last updated. Updated time.Time `json:"updated"` // Version is the maximum microversion supported. Version string `json:"version"` } // APIVersionPage is the page returned by a pager when traversing over a // collection of API versions. type APIVersionPage struct { pagination.SinglePageBase } // IsEmpty checks whether an APIVersionPage struct is empty. func (r APIVersionPage) IsEmpty() (bool, error) { is, err := ExtractAPIVersions(r) return len(is) == 0, err } // ExtractAPIVersions takes a collection page, extracts all of the elements, // and returns them a slice of APIVersion structs. It is effectively a cast. func ExtractAPIVersions(r pagination.Page) ([]APIVersion, error) { var s struct { Versions []APIVersion `json:"versions"` } err := (r.(APIVersionPage)).ExtractInto(&s) return s.Versions, err } // GetResult represents the result of a get operation. type GetResult struct { gophercloud.Result } // Extract is a function that accepts a result and extracts an API version resource. func (r GetResult) Extract() (*APIVersion, error) { var s struct { Versions []APIVersion `json:"versions"` } err := r.ExtractInto(&s) if err != nil { return nil, err } switch len(s.Versions) { case 0: return nil, ErrVersionNotFound{} case 1: return &s.Versions[0], nil default: return nil, ErrMultipleVersionsFound{Count: len(s.Versions)} } } testing/000077500000000000000000000000001335005366400352415ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/apiversionsdoc.go000066400000000000000000000000421335005366400363310ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/apiversions/testing// apiversions_v1 package testing fixtures.go000066400000000000000000000136421335005366400374470ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/apiversions/testingpackage testing import ( "fmt" "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/apiversions" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) const ManilaAPIVersionResponse = ` { "versions": [ { "id": "v2.0", "links": [ { "href": "http://docs.openstack.org/", "rel": "describedby", "type": "text/html" }, { "href": "http://localhost:8786/v2/", "rel": "self" } ], "media-types": [ { "base": "application/json", "type": "application/vnd.openstack.share+json;version=1" } ], "min_version": "2.0", "status": "CURRENT", "updated": "2015-08-27T11:33:21Z", "version": "2.32" } ] } ` const ManilaAPIInvalidVersionResponse_1 = ` { "versions": [ ] } ` const ManilaAPIInvalidVersionResponse_2 = ` { "versions": [ { "id": "v2.0", "links": [ { "href": "http://docs.openstack.org/", "rel": "describedby", "type": "text/html" }, { "href": "http://localhost:8786/v2/", "rel": "self" } ], "media-types": [ { "base": "application/json", "type": "application/vnd.openstack.share+json;version=1" } ], "min_version": "2.0", "status": "CURRENT", "updated": "2015-08-27T11:33:21Z", "version": "2.32" }, { "id": "v2.9", "links": [ { "href": "http://docs.openstack.org/", "rel": "describedby", "type": "text/html" }, { "href": "http://localhost:8786/v2/", "rel": "self" } ], "media-types": [ { "base": "application/json", "type": "application/vnd.openstack.share+json;version=1" } ], "min_version": "2.9", "status": "CURRENT", "updated": "2015-08-27T11:33:21Z", "version": "2.99" } ] } ` const ManilaAllAPIVersionsResponse = ` { "versions": [ { "id": "v1.0", "links": [ { "href": "http://docs.openstack.org/", "rel": "describedby", "type": "text/html" }, { "href": "http://localhost:8786/v1/", "rel": "self" } ], "media-types": [ { "base": "application/json", "type": "application/vnd.openstack.share+json;version=1" } ], "min_version": "", "status": "DEPRECATED", "updated": "2015-08-27T11:33:21Z", "version": "" }, { "id": "v2.0", "links": [ { "href": "http://docs.openstack.org/", "rel": "describedby", "type": "text/html" }, { "href": "http://localhost:8786/v2/", "rel": "self" } ], "media-types": [ { "base": "application/json", "type": "application/vnd.openstack.share+json;version=1" } ], "min_version": "2.0", "status": "CURRENT", "updated": "2015-08-27T11:33:21Z", "version": "2.32" } ] } ` var ManilaAPIVersion1Result = apiversions.APIVersion{ ID: "v1.0", Status: "DEPRECATED", Updated: time.Date(2015, 8, 27, 11, 33, 21, 0, time.UTC), } var ManilaAPIVersion2Result = apiversions.APIVersion{ ID: "v2.0", Status: "CURRENT", Updated: time.Date(2015, 8, 27, 11, 33, 21, 0, time.UTC), MinVersion: "2.0", Version: "2.32", } var ManilaAllAPIVersionResults = []apiversions.APIVersion{ ManilaAPIVersion1Result, ManilaAPIVersion2Result, } func MockListResponse(t *testing.T) { th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ManilaAllAPIVersionsResponse) }) } func MockGetResponse(t *testing.T) { th.Mux.HandleFunc("/v2/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ManilaAPIVersionResponse) }) } func MockGetNoResponse(t *testing.T) { th.Mux.HandleFunc("/v2/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ManilaAPIInvalidVersionResponse_1) }) } func MockGetMultipleResponses(t *testing.T) { th.Mux.HandleFunc("/v2/", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ManilaAPIInvalidVersionResponse_2) }) } requests_test.go000066400000000000000000000024641335005366400405100ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/apiversions/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/apiversions" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListAPIVersions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) allVersions, err := apiversions.List(client.ServiceClient()).AllPages() th.AssertNoErr(t, err) actual, err := apiversions.ExtractAPIVersions(allVersions) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ManilaAllAPIVersionResults, actual) } func TestGetAPIVersion(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetResponse(t) actual, err := apiversions.Get(client.ServiceClient(), "v2").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ManilaAPIVersion2Result, *actual) } func TestGetNoAPIVersion(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetNoResponse(t) _, err := apiversions.Get(client.ServiceClient(), "v2").Extract() th.AssertEquals(t, err.Error(), "Unable to find requested API version") } func TestGetMultipleAPIVersion(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetMultipleResponses(t) _, err := apiversions.Get(client.ServiceClient(), "v2").Extract() th.AssertEquals(t, err.Error(), "Found 2 API versions") } urls.go000066400000000000000000000006161335005366400351030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/apiversionspackage apiversions import ( "net/url" "strings" "github.com/gophercloud/gophercloud" ) func getURL(c *gophercloud.ServiceClient, version string) string { u, _ := url.Parse(c.ServiceURL("")) u.Path = "/" + strings.TrimRight(version, "/") + "/" return u.String() } func listURL(c *gophercloud.ServiceClient) string { u, _ := url.Parse(c.ServiceURL("")) u.Path = "/" return u.String() } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/000077500000000000000000000000001335005366400316305ustar00rootroot00000000000000availabilityzones/000077500000000000000000000000001335005366400353025ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2requests.go000066400000000000000000000006321335005366400375050ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/availabilityzonespackage availabilityzones import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // List will return the existing availability zones. func List(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { return AvailabilityZonePage{pagination.SinglePageBase(r)} }) } results.go000066400000000000000000000030441335005366400373330ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/availabilityzonespackage availabilityzones import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // AvailabilityZone contains all the information associated with an OpenStack // AvailabilityZone. type AvailabilityZone struct { // The availability zone ID. ID string `json:"id"` // The name of the availability zone. Name string `json:"name"` // The date and time stamp when the availability zone was created. CreatedAt time.Time `json:"-"` // The date and time stamp when the availability zone was updated. UpdatedAt time.Time `json:"-"` } type commonResult struct { gophercloud.Result } // ListResult contains the response body and error from a List request. type AvailabilityZonePage struct { pagination.SinglePageBase } // ExtractAvailabilityZones will get the AvailabilityZone objects out of the shareTypeAccessResult object. func ExtractAvailabilityZones(r pagination.Page) ([]AvailabilityZone, error) { var a struct { AvailabilityZone []AvailabilityZone `json:"availability_zones"` } err := (r.(AvailabilityZonePage)).ExtractInto(&a) return a.AvailabilityZone, err } func (r *AvailabilityZone) UnmarshalJSON(b []byte) error { type tmp AvailabilityZone var s struct { tmp CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = AvailabilityZone(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) r.UpdatedAt = time.Time(s.UpdatedAt) return nil } testing/000077500000000000000000000000001335005366400367575ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/availabilityzonesfixtures.go000066400000000000000000000014471335005366400411650ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/availabilityzones/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func MockListResponse(t *testing.T) { th.Mux.HandleFunc("/os-availability-zone", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "availability_zones": [ { "name": "nova", "created_at": "2015-09-18T09:50:55.000000", "updated_at": null, "id": "388c983d-258e-4a0e-b1ba-10da37d766db" } ] }`) }) } requests_test.go000066400000000000000000000015621335005366400422240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/availabilityzones/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/availabilityzones" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // Verifies that availability zones can be listed correctly func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) allPages, err := availabilityzones.List(client.ServiceClient()).AllPages() th.AssertNoErr(t, err) actual, err := availabilityzones.ExtractAvailabilityZones(allPages) th.AssertNoErr(t, err) var nilTime time.Time expected := []availabilityzones.AvailabilityZone{ { Name: "nova", CreatedAt: time.Date(2015, 9, 18, 9, 50, 55, 0, time.UTC), UpdatedAt: nilTime, ID: "388c983d-258e-4a0e-b1ba-10da37d766db", }, } th.CheckDeepEquals(t, expected, actual) } urls.go000066400000000000000000000002531335005366400366160ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/availabilityzonespackage availabilityzones import "github.com/gophercloud/gophercloud" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-availability-zone") } securityservices/000077500000000000000000000000001335005366400351645ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2requests.go000066400000000000000000000145121335005366400373710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/securityservicespackage securityservices import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type SecurityServiceType string // Valid security service types const ( LDAP SecurityServiceType = "ldap" Kerberos SecurityServiceType = "kerberos" ActiveDirectory SecurityServiceType = "active_directory" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToSecurityServiceCreateMap() (map[string]interface{}, error) } // CreateOpts contains options for creating a SecurityService. This object is // passed to the securityservices.Create function. For more information about // these parameters, see the SecurityService object. type CreateOpts struct { // The security service type. A valid value is ldap, kerberos, or active_directory Type SecurityServiceType `json:"type" required:"true"` // The security service name Name string `json:"name,omitempty"` // The security service description Description string `json:"description,omitempty"` // The DNS IP address that is used inside the tenant network DNSIP string `json:"dns_ip,omitempty"` // The security service user or group name that is used by the tenant User string `json:"user,omitempty"` // The user password, if you specify a user Password string `json:"password,omitempty"` // The security service domain Domain string `json:"domain,omitempty"` // The security service host name or IP address Server string `json:"server,omitempty"` } // ToSecurityServicesCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToSecurityServiceCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "security_service") } // Create will create a new SecurityService based on the values in CreateOpts. To // extract the SecurityService object from the response, call the Extract method // on the CreateResult. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSecurityServiceCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete will delete the existing SecurityService with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { ToSecurityServiceListQuery() (string, error) } // ListOpts holds options for listing SecurityServices. It is passed to the // securityservices.List function. type ListOpts struct { // admin-only option. Set it to true to see all tenant security services. AllTenants bool `q:"all_tenants"` // The security service ID ID string `q:"id"` // The security service domain Domain string `q:"domain"` // The security service type. A valid value is ldap, kerberos, or active_directory Type SecurityServiceType `q:"type"` // The security service name Name string `q:"name"` // The DNS IP address that is used inside the tenant network DNSIP string `q:"dns_ip"` // The security service user or group name that is used by the tenant User string `q:"user"` // The security service host name or IP address Server string `q:"server"` // The ID of the share network using security services ShareNetworkID string `q:"share_network_id"` } // ToSecurityServiceListQuery formats a ListOpts into a query string. func (opts ListOpts) ToSecurityServiceListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns SecurityServices optionally limited by the conditions provided in ListOpts. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToSecurityServiceListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return SecurityServicePage{pagination.SinglePageBase(r)} }) } // Get retrieves the SecurityService with the provided ID. To extract the SecurityService // object from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToSecurityServiceUpdateMap() (map[string]interface{}, error) } // UpdateOpts contain options for updating an existing SecurityService. This object is passed // to the securityservices.Update function. For more information about the parameters, see // the SecurityService object. type UpdateOpts struct { // The security service name Name string `json:"name"` // The security service description Description string `json:"description,omitempty"` // The security service type. A valid value is ldap, kerberos, or active_directory Type string `json:"type,omitempty"` // The DNS IP address that is used inside the tenant network DNSIP string `json:"dns_ip,omitempty"` // The security service user or group name that is used by the tenant User string `json:"user,omitempty"` // The user password, if you specify a user Password string `json:"password,omitempty"` // The security service domain Domain string `json:"domain,omitempty"` // The security service host name or IP address Server string `json:"server,omitempty"` } // ToSecurityServiceUpdateMap assembles a request body based on the contents of an // UpdateOpts. func (opts UpdateOpts) ToSecurityServiceUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "security_service") } // Update will update the SecurityService with provided information. To extract the updated // SecurityService from the response, call the Extract method on the UpdateResult. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToSecurityServiceUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000064171335005366400372240ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/securityservicespackage securityservices import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // SecurityService contains all the information associated with an OpenStack // SecurityService. type SecurityService struct { // The security service ID ID string `json:"id"` // The UUID of the project where the security service was created ProjectID string `json:"project_id"` // The security service domain Domain string `json:"domain"` // The security service status Status string `json:"status"` // The security service type. A valid value is ldap, kerberos, or active_directory Type string `json:"type"` // The security service name Name string `json:"name"` // The security service description Description string `json:"description"` // The DNS IP address that is used inside the tenant network DNSIP string `json:"dns_ip"` // The security service user or group name that is used by the tenant User string `json:"user"` // The user password, if you specify a user Password string `json:"password"` // The security service host name or IP address Server string `json:"server"` // The date and time stamp when the security service was created CreatedAt time.Time `json:"-"` // The date and time stamp when the security service was updated UpdatedAt time.Time `json:"-"` } func (r *SecurityService) UnmarshalJSON(b []byte) error { type tmp SecurityService var s struct { tmp CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = SecurityService(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) r.UpdatedAt = time.Time(s.UpdatedAt) return nil } type commonResult struct { gophercloud.Result } // SecurityServicePage is a pagination.pager that is returned from a call to the List function. type SecurityServicePage struct { pagination.SinglePageBase } // IsEmpty returns true if a ListResult contains no SecurityServices. func (r SecurityServicePage) IsEmpty() (bool, error) { securityServices, err := ExtractSecurityServices(r) return len(securityServices) == 0, err } // ExtractSecurityServices extracts and returns SecurityServices. It is used while // iterating over a securityservices.List call. func ExtractSecurityServices(r pagination.Page) ([]SecurityService, error) { var s struct { SecurityServices []SecurityService `json:"security_services"` } err := (r.(SecurityServicePage)).ExtractInto(&s) return s.SecurityServices, err } // Extract will get the SecurityService object out of the commonResult object. func (r commonResult) Extract() (*SecurityService, error) { var s struct { SecurityService *SecurityService `json:"security_service"` } err := r.ExtractInto(&s) return s.SecurityService, err } // CreateResult contains the response body and error from a Create request. type CreateResult struct { commonResult } // DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { gophercloud.ErrResult } // GetResult contains the response body and error from a Get request. type GetResult struct { commonResult } // UpdateResult contains the response body and error from an Update request. type UpdateResult struct { commonResult } testing/000077500000000000000000000000001335005366400366415ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/securityservicesfixtures.go000066400000000000000000000150161335005366400410440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/securityservices/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func MockCreateResponse(t *testing.T) { th.Mux.HandleFunc("/security-services", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "security_service": { "description": "Creating my first Security Service", "dns_ip": "10.0.0.0/24", "user": "demo", "password": "***", "type": "kerberos", "name": "SecServ1" } }`) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "security_service": { "status": "new", "domain": null, "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "name": "SecServ1", "created_at": "2015-09-07T12:19:10.695211", "updated_at": null, "server": null, "dns_ip": "10.0.0.0/24", "user": "demo", "password": "supersecret", "type": "kerberos", "id": "3c829734-0679-4c17-9637-801da48c0d5f", "description": "Creating my first Security Service" } }`) }) } func MockDeleteResponse(t *testing.T) { th.Mux.HandleFunc("/security-services/securityServiceID", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusAccepted) }) } func MockListResponse(t *testing.T) { th.Mux.HandleFunc("/security-services/detail", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "security_services": [ { "status": "new", "domain": null, "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "name": "SecServ1", "created_at": "2015-09-07T12:19:10.000000", "description": "Creating my first Security Service", "updated_at": null, "server": null, "dns_ip": "10.0.0.0/24", "user": "demo", "password": "supersecret", "type": "kerberos", "id": "3c829734-0679-4c17-9637-801da48c0d5f" }, { "status": "new", "domain": null, "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "name": "SecServ2", "created_at": "2015-09-07T12:25:03.000000", "description": "Creating my second Security Service", "updated_at": null, "server": null, "dns_ip": "10.0.0.0/24", "user": null, "password": null, "type": "ldap", "id": "5a1d3a12-34a7-4087-8983-50e9ed03509a" } ] }`) }) } func MockFilteredListResponse(t *testing.T) { th.Mux.HandleFunc("/security-services/detail", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "security_services": [ { "status": "new", "domain": null, "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "name": "SecServ1", "created_at": "2015-09-07T12:19:10.000000", "description": "Creating my first Security Service", "updated_at": null, "server": null, "dns_ip": "10.0.0.0/24", "user": "demo", "password": "supersecret", "type": "kerberos", "id": "3c829734-0679-4c17-9637-801da48c0d5f" } ] }`) }) } func MockGetResponse(t *testing.T) { th.Mux.HandleFunc("/security-services/3c829734-0679-4c17-9637-801da48c0d5f", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "security_service": { "status": "new", "domain": null, "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "name": "SecServ1", "created_at": "2015-09-07T12:19:10.000000", "updated_at": null, "server": null, "dns_ip": "10.0.0.0/24", "user": "demo", "password": "supersecret", "type": "kerberos", "id": "3c829734-0679-4c17-9637-801da48c0d5f", "description": "Creating my first Security Service" } }`) }) } func MockUpdateResponse(t *testing.T) { th.Mux.HandleFunc("/security-services/securityServiceID", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "security_service": { "status": "new", "domain": null, "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "name": "SecServ2", "created_at": "2015-09-07T12:19:10.000000", "updated_at": "2015-09-07T12:20:10.000000", "server": null, "dns_ip": "10.0.0.0/24", "user": "demo", "password": "supersecret", "type": "kerberos", "id": "securityServiceID", "description": "Updating my first Security Service" } } `) }) } requests_test.go000066400000000000000000000135261335005366400421110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/securityservices/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/securityservices" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // Verifies that a security service can be created correctly func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockCreateResponse(t) options := &securityservices.CreateOpts{ Name: "SecServ1", Description: "Creating my first Security Service", DNSIP: "10.0.0.0/24", User: "demo", Password: "***", Type: "kerberos", } s, err := securityservices.Create(client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "SecServ1") th.AssertEquals(t, s.Description, "Creating my first Security Service") th.AssertEquals(t, s.User, "demo") th.AssertEquals(t, s.DNSIP, "10.0.0.0/24") th.AssertEquals(t, s.Password, "supersecret") th.AssertEquals(t, s.Type, "kerberos") } // Verifies that a security service cannot be created without a type func TestCreateFails(t *testing.T) { options := &securityservices.CreateOpts{ Name: "SecServ1", Description: "Creating my first Security Service", DNSIP: "10.0.0.0/24", User: "demo", Password: "***", } _, err := securityservices.Create(client.ServiceClient(), options).Extract() if _, ok := err.(gophercloud.ErrMissingInput); !ok { t.Fatal("ErrMissingInput was expected to occur") } } // Verifies that security service deletion works func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockDeleteResponse(t) res := securityservices.Delete(client.ServiceClient(), "securityServiceID") th.AssertNoErr(t, res.Err) } // Verifies that security services can be listed correctly func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) allPages, err := securityservices.List(client.ServiceClient(), &securityservices.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := securityservices.ExtractSecurityServices(allPages) th.AssertNoErr(t, err) var nilTime time.Time expected := []securityservices.SecurityService{ { Status: "new", Domain: "", ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", Name: "SecServ1", CreatedAt: time.Date(2015, 9, 7, 12, 19, 10, 0, time.UTC), Description: "Creating my first Security Service", UpdatedAt: nilTime, Server: "", DNSIP: "10.0.0.0/24", User: "demo", Password: "supersecret", Type: "kerberos", ID: "3c829734-0679-4c17-9637-801da48c0d5f", }, { Status: "new", Domain: "", ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", Name: "SecServ2", CreatedAt: time.Date(2015, 9, 7, 12, 25, 03, 0, time.UTC), Description: "Creating my second Security Service", UpdatedAt: nilTime, Server: "", DNSIP: "10.0.0.0/24", User: "", Password: "", Type: "ldap", ID: "5a1d3a12-34a7-4087-8983-50e9ed03509a", }, } th.CheckDeepEquals(t, expected, actual) } // Verifies that security services list can be called with query parameters func TestFilteredList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockFilteredListResponse(t) options := &securityservices.ListOpts{ Type: "kerberos", } allPages, err := securityservices.List(client.ServiceClient(), options).AllPages() th.AssertNoErr(t, err) actual, err := securityservices.ExtractSecurityServices(allPages) th.AssertNoErr(t, err) var nilTime time.Time expected := []securityservices.SecurityService{ { Status: "new", Domain: "", ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", Name: "SecServ1", CreatedAt: time.Date(2015, 9, 7, 12, 19, 10, 0, time.UTC), Description: "Creating my first Security Service", UpdatedAt: nilTime, Server: "", DNSIP: "10.0.0.0/24", User: "demo", Password: "supersecret", Type: "kerberos", ID: "3c829734-0679-4c17-9637-801da48c0d5f", }, } th.CheckDeepEquals(t, expected, actual) } // Verifies that it is possible to get a security service func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetResponse(t) var nilTime time.Time expected := securityservices.SecurityService{ ID: "3c829734-0679-4c17-9637-801da48c0d5f", Name: "SecServ1", CreatedAt: time.Date(2015, 9, 7, 12, 19, 10, 0, time.UTC), Description: "Creating my first Security Service", Type: "kerberos", UpdatedAt: nilTime, ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", Status: "new", Domain: "", Server: "", DNSIP: "10.0.0.0/24", User: "demo", Password: "supersecret", } n, err := securityservices.Get(client.ServiceClient(), "3c829734-0679-4c17-9637-801da48c0d5f").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, n) } // Verifies that it is possible to update a security service func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUpdateResponse(t) expected := securityservices.SecurityService{ ID: "securityServiceID", Name: "SecServ2", CreatedAt: time.Date(2015, 9, 7, 12, 19, 10, 0, time.UTC), Description: "Updating my first Security Service", Type: "kerberos", UpdatedAt: time.Date(2015, 9, 7, 12, 20, 10, 0, time.UTC), ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", Status: "new", Domain: "", Server: "", DNSIP: "10.0.0.0/24", User: "demo", Password: "supersecret", } options := securityservices.UpdateOpts{Name: "SecServ2"} s, err := securityservices.Update(client.ServiceClient(), "securityServiceID", options).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, s) } urls.go000066400000000000000000000010751335005366400365030ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/securityservicespackage securityservices import "github.com/gophercloud/gophercloud" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("security-services") } func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("security-services", id) } func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("security-services", "detail") } func getURL(c *gophercloud.ServiceClient, id string) string { return deleteURL(c, id) } func updateURL(c *gophercloud.ServiceClient, id string) string { return deleteURL(c, id) } sharenetworks/000077500000000000000000000000001335005366400344505ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2requests.go000066400000000000000000000214021335005366400366510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/sharenetworkspackage sharenetworks import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToShareNetworkCreateMap() (map[string]interface{}, error) } // CreateOpts contains options for creating a ShareNetwork. This object is // passed to the sharenetworks.Create function. For more information about // these parameters, see the ShareNetwork object. type CreateOpts struct { // The UUID of the Neutron network to set up for share servers NeutronNetID string `json:"neutron_net_id,omitempty"` // The UUID of the Neutron subnet to set up for share servers NeutronSubnetID string `json:"neutron_subnet_id,omitempty"` // The UUID of the nova network to set up for share servers NovaNetID string `json:"nova_net_id,omitempty"` // The share network name Name string `json:"name"` // The share network description Description string `json:"description"` } // ToShareNetworkCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToShareNetworkCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "share_network") } // Create will create a new ShareNetwork based on the values in CreateOpts. To // extract the ShareNetwork object from the response, call the Extract method // on the CreateResult. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToShareNetworkCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) return } // Delete will delete the existing ShareNetwork with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { ToShareNetworkListQuery() (string, error) } // ListOpts holds options for listing ShareNetworks. It is passed to the // sharenetworks.List function. type ListOpts struct { // admin-only option. Set it to true to see all tenant share networks. AllTenants bool `q:"all_tenants"` // The UUID of the project where the share network was created ProjectID string `q:"project_id"` // The neutron network ID NeutronNetID string `q:"neutron_net_id"` // The neutron subnet ID NeutronSubnetID string `q:"neutron_subnet_id"` // The nova network ID NovaNetID string `q:"nova_net_id"` // The network type. A valid value is VLAN, VXLAN, GRE or flat NetworkType string `q:"network_type"` // The Share Network name Name string `q:"name"` // The Share Network description Description string `q:"description"` // The Share Network IP version IPVersion gophercloud.IPVersion `q:"ip_version"` // The Share Network segmentation ID SegmentationID int `q:"segmentation_id"` // List all share networks created after the given date CreatedSince string `q:"created_since"` // List all share networks created before the given date CreatedBefore string `q:"created_before"` // Limit specifies the page size. Limit int `q:"limit"` // Limit specifies the page number. Offset int `q:"offset"` } // ToShareNetworkListQuery formats a ListOpts into a query string. func (opts ListOpts) ToShareNetworkListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // ListDetail returns ShareNetworks optionally limited by the conditions provided in ListOpts. func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listDetailURL(client) if opts != nil { query, err := opts.ToShareNetworkListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { p := ShareNetworkPage{pagination.MarkerPageBase{PageResult: r}} p.MarkerPageBase.Owner = p return p }) } // Get retrieves the ShareNetwork with the provided ID. To extract the ShareNetwork // object from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { ToShareNetworkUpdateMap() (map[string]interface{}, error) } // UpdateOpts contain options for updating an existing ShareNetwork. This object is passed // to the sharenetworks.Update function. For more information about the parameters, see // the ShareNetwork object. type UpdateOpts struct { // The share network name Name string `json:"name,omitempty"` // The share network description Description string `json:"description,omitempty"` // The UUID of the Neutron network to set up for share servers NeutronNetID string `json:"neutron_net_id,omitempty"` // The UUID of the Neutron subnet to set up for share servers NeutronSubnetID string `json:"neutron_subnet_id,omitempty"` // The UUID of the nova network to set up for share servers NovaNetID string `json:"nova_net_id,omitempty"` } // ToShareNetworkUpdateMap assembles a request body based on the contents of an // UpdateOpts. func (opts UpdateOpts) ToShareNetworkUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "share_network") } // Update will update the ShareNetwork with provided information. To extract the updated // ShareNetwork from the response, call the Extract method on the UpdateResult. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToShareNetworkUpdateMap() if err != nil { r.Err = err return } _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // AddSecurityServiceOptsBuilder allows extensions to add additional parameters to the // AddSecurityService request. type AddSecurityServiceOptsBuilder interface { ToShareNetworkAddSecurityServiceMap() (map[string]interface{}, error) } // AddSecurityServiceOpts contain options for adding a security service to an // existing ShareNetwork. This object is passed to the sharenetworks.AddSecurityService // function. For more information about the parameters, see the ShareNetwork object. type AddSecurityServiceOpts struct { SecurityServiceID string `json:"security_service_id"` } // ToShareNetworkAddSecurityServiceMap assembles a request body based on the contents of an // AddSecurityServiceOpts. func (opts AddSecurityServiceOpts) ToShareNetworkAddSecurityServiceMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "add_security_service") } // AddSecurityService will add the security service to a ShareNetwork. To extract the updated // ShareNetwork from the response, call the Extract method on the UpdateResult. func AddSecurityService(client *gophercloud.ServiceClient, id string, opts AddSecurityServiceOptsBuilder) (r UpdateResult) { b, err := opts.ToShareNetworkAddSecurityServiceMap() if err != nil { r.Err = err return } _, r.Err = client.Post(addSecurityServiceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // RemoveSecurityServiceOptsBuilder allows extensions to add additional parameters to the // RemoveSecurityService request. type RemoveSecurityServiceOptsBuilder interface { ToShareNetworkRemoveSecurityServiceMap() (map[string]interface{}, error) } // RemoveSecurityServiceOpts contain options for removing a security service from an // existing ShareNetwork. This object is passed to the sharenetworks.RemoveSecurityService // function. For more information about the parameters, see the ShareNetwork object. type RemoveSecurityServiceOpts struct { SecurityServiceID string `json:"security_service_id"` } // ToShareNetworkRemoveSecurityServiceMap assembles a request body based on the contents of an // RemoveSecurityServiceOpts. func (opts RemoveSecurityServiceOpts) ToShareNetworkRemoveSecurityServiceMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "remove_security_service") } // RemoveSecurityService will remove the security service from a ShareNetwork. To extract the updated // ShareNetwork from the response, call the Extract method on the UpdateResult. func RemoveSecurityService(client *gophercloud.ServiceClient, id string, opts RemoveSecurityServiceOptsBuilder) (r UpdateResult) { b, err := opts.ToShareNetworkRemoveSecurityServiceMap() if err != nil { r.Err = err return } _, r.Err = client.Post(removeSecurityServiceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } results.go000066400000000000000000000112431335005366400365010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/sharenetworkspackage sharenetworks import ( "encoding/json" "net/url" "strconv" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ShareNetwork contains all the information associated with an OpenStack // ShareNetwork. type ShareNetwork struct { // The Share Network ID ID string `json:"id"` // The UUID of the project where the share network was created ProjectID string `json:"project_id"` // The neutron network ID NeutronNetID string `json:"neutron_net_id"` // The neutron subnet ID NeutronSubnetID string `json:"neutron_subnet_id"` // The nova network ID NovaNetID string `json:"nova_net_id"` // The network type. A valid value is VLAN, VXLAN, GRE or flat NetworkType string `json:"network_type"` // The segmentation ID SegmentationID int `json:"segmentation_id"` // The IP block from which to allocate the network, in CIDR notation CIDR string `json:"cidr"` // The IP version of the network. A valid value is 4 or 6 IPVersion int `json:"ip_version"` // The Share Network name Name string `json:"name"` // The Share Network description Description string `json:"description"` // The date and time stamp when the Share Network was created CreatedAt time.Time `json:"-"` // The date and time stamp when the Share Network was updated UpdatedAt time.Time `json:"-"` } func (r *ShareNetwork) UnmarshalJSON(b []byte) error { type tmp ShareNetwork var s struct { tmp CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = ShareNetwork(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) r.UpdatedAt = time.Time(s.UpdatedAt) return nil } type commonResult struct { gophercloud.Result } // ShareNetworkPage is a pagination.pager that is returned from a call to the List function. type ShareNetworkPage struct { pagination.MarkerPageBase } // NextPageURL generates the URL for the page of results after this one. func (r ShareNetworkPage) NextPageURL() (string, error) { currentURL := r.URL mark, err := r.Owner.LastMarker() if err != nil { return "", err } q := currentURL.Query() q.Set("offset", mark) currentURL.RawQuery = q.Encode() return currentURL.String(), nil } // LastMarker returns the last offset in a ListResult. func (r ShareNetworkPage) LastMarker() (string, error) { maxInt := strconv.Itoa(int(^uint(0) >> 1)) shareNetworks, err := ExtractShareNetworks(r) if err != nil { return maxInt, err } if len(shareNetworks) == 0 { return maxInt, nil } u, err := url.Parse(r.URL.String()) if err != nil { return maxInt, err } queryParams := u.Query() offset := queryParams.Get("offset") limit := queryParams.Get("limit") // Limit is not present, only one page required if limit == "" { return maxInt, nil } iOffset := 0 if offset != "" { iOffset, err = strconv.Atoi(offset) if err != nil { return maxInt, err } } iLimit, err := strconv.Atoi(limit) if err != nil { return maxInt, err } iOffset = iOffset + iLimit offset = strconv.Itoa(iOffset) return offset, nil } // IsEmpty satisifies the IsEmpty method of the Page interface func (r ShareNetworkPage) IsEmpty() (bool, error) { shareNetworks, err := ExtractShareNetworks(r) return len(shareNetworks) == 0, err } // ExtractShareNetworks extracts and returns ShareNetworks. It is used while // iterating over a sharenetworks.List call. func ExtractShareNetworks(r pagination.Page) ([]ShareNetwork, error) { var s struct { ShareNetworks []ShareNetwork `json:"share_networks"` } err := (r.(ShareNetworkPage)).ExtractInto(&s) return s.ShareNetworks, err } // Extract will get the ShareNetwork object out of the commonResult object. func (r commonResult) Extract() (*ShareNetwork, error) { var s struct { ShareNetwork *ShareNetwork `json:"share_network"` } err := r.ExtractInto(&s) return s.ShareNetwork, err } // CreateResult contains the response body and error from a Create request. type CreateResult struct { commonResult } // DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { gophercloud.ErrResult } // GetResult contains the response body and error from a Get request. type GetResult struct { commonResult } // UpdateResult contains the response body and error from an Update request. type UpdateResult struct { commonResult } // AddSecurityServiceResult contains the response body and error from a security // service addition request. type AddSecurityServiceResult struct { commonResult } // RemoveSecurityServiceResult contains the response body and error from a security // service removal request. type RemoveSecurityServiceResult struct { commonResult } testing/000077500000000000000000000000001335005366400361255ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/sharenetworksfixtures.go000066400000000000000000000276461335005366400403440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/sharenetworks/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func createReq(name, description, network, subnetwork string) string { return fmt.Sprintf(`{ "share_network": { "name": "%s", "description": "%s", "neutron_net_id": "%s", "neutron_subnet_id": "%s" } }`, name, description, network, subnetwork) } func createResp(name, description, network, subnetwork string) string { return fmt.Sprintf(` { "share_network": { "name": "%s", "description": "%s", "segmentation_id": null, "created_at": "2015-09-07T14:37:00.583656", "updated_at": null, "id": "77eb3421-4549-4789-ac39-0d5185d68c29", "neutron_net_id": "%s", "neutron_subnet_id": "%s", "ip_version": null, "nova_net_id": null, "cidr": null, "project_id": "e10a683c20da41248cfd5e1ab3d88c62", "network_type": null } }`, name, description, network, subnetwork) } func MockCreateResponse(t *testing.T) { th.Mux.HandleFunc("/share-networks", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, createReq("my_network", "This is my share network", "998b42ee-2cee-4d36-8b95-67b5ca1f2109", "53482b62-2c84-4a53-b6ab-30d9d9800d06")) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, createResp("my_network", "This is my share network", "998b42ee-2cee-4d36-8b95-67b5ca1f2109", "53482b62-2c84-4a53-b6ab-30d9d9800d06")) }) } func MockDeleteResponse(t *testing.T) { th.Mux.HandleFunc("/share-networks/fa158a3d-6d9f-4187-9ca5-abbb82646eb2", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusAccepted) }) } func MockListResponse(t *testing.T) { th.Mux.HandleFunc("/share-networks/detail", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) r.ParseForm() marker := r.Form.Get("offset") switch marker { case "": fmt.Fprintf(w, `{ "share_networks": [ { "name": "net_my1", "segmentation_id": null, "created_at": "2015-09-04T14:57:13.000000", "neutron_subnet_id": "53482b62-2c84-4a53-b6ab-30d9d9800d06", "updated_at": null, "id": "32763294-e3d4-456a-998d-60047677c2fb", "neutron_net_id": "998b42ee-2cee-4d36-8b95-67b5ca1f2109", "ip_version": null, "nova_net_id": null, "cidr": null, "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "network_type": null, "description": "descr" }, { "name": "net_my", "segmentation_id": null, "created_at": "2015-09-04T14:54:25.000000", "neutron_subnet_id": "53482b62-2c84-4a53-b6ab-30d9d9800d06", "updated_at": null, "id": "713df749-aac0-4a54-af52-10f6c991e80c", "neutron_net_id": "998b42ee-2cee-4d36-8b95-67b5ca1f2109", "ip_version": null, "nova_net_id": null, "cidr": null, "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "network_type": null, "description": "desecr" }, { "name": null, "segmentation_id": null, "created_at": "2015-09-04T14:51:41.000000", "neutron_subnet_id": null, "updated_at": null, "id": "fa158a3d-6d9f-4187-9ca5-abbb82646eb2", "neutron_net_id": null, "ip_version": null, "nova_net_id": null, "cidr": null, "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "network_type": null, "description": null } ] }`) default: fmt.Fprintf(w, ` { "share_networks": [] }`) } }) } func MockFilteredListResponse(t *testing.T) { th.Mux.HandleFunc("/share-networks/detail", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) r.ParseForm() marker := r.Form.Get("offset") switch marker { case "": fmt.Fprintf(w, ` { "share_networks": [ { "name": "net_my1", "segmentation_id": null, "created_at": "2015-09-04T14:57:13.000000", "neutron_subnet_id": "53482b62-2c84-4a53-b6ab-30d9d9800d06", "updated_at": null, "id": "32763294-e3d4-456a-998d-60047677c2fb", "neutron_net_id": "998b42ee-2cee-4d36-8b95-67b5ca1f2109", "ip_version": null, "nova_net_id": null, "cidr": null, "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "network_type": null, "description": "descr" } ] }`) case "1": fmt.Fprintf(w, ` { "share_networks": [ { "name": "net_my1", "segmentation_id": null, "created_at": "2015-09-04T14:57:13.000000", "neutron_subnet_id": "53482b62-2c84-4a53-b6ab-30d9d9800d06", "updated_at": null, "id": "32763294-e3d4-456a-998d-60047677c2fb", "neutron_net_id": "998b42ee-2cee-4d36-8b95-67b5ca1f2109", "ip_version": null, "nova_net_id": null, "cidr": null, "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "network_type": null, "description": "descr" } ] }`) case "2": fmt.Fprintf(w, ` { "share_networks": [ { "name": "net_my1", "segmentation_id": null, "created_at": "2015-09-04T14:57:13.000000", "neutron_subnet_id": "53482b62-2c84-4a53-b6ab-30d9d9800d06", "updated_at": null, "id": "32763294-e3d4-456a-998d-60047677c2fb", "neutron_net_id": "998b42ee-2cee-4d36-8b95-67b5ca1f2109", "ip_version": null, "nova_net_id": null, "cidr": null, "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "network_type": null, "description": "descr" } ] }`) default: fmt.Fprintf(w, ` { "share_networks": [] }`) } }) } func MockGetResponse(t *testing.T) { th.Mux.HandleFunc("/share-networks/7f950b52-6141-4a08-bbb5-bb7ffa3ea5fd", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "share_network": { "name": "net_my1", "segmentation_id": null, "created_at": "2015-09-04T14:56:45.000000", "neutron_subnet_id": "53482b62-2c84-4a53-b6ab-30d9d9800d06", "updated_at": null, "id": "7f950b52-6141-4a08-bbb5-bb7ffa3ea5fd", "neutron_net_id": "998b42ee-2cee-4d36-8b95-67b5ca1f2109", "ip_version": null, "nova_net_id": null, "cidr": null, "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "network_type": null, "description": "descr" } }`) }) } func MockUpdateNeutronResponse(t *testing.T) { th.Mux.HandleFunc("/share-networks/713df749-aac0-4a54-af52-10f6c991e80c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "share_network": { "name": "net_my2", "segmentation_id": null, "created_at": "2015-09-04T14:54:25.000000", "neutron_subnet_id": "new-neutron-subnet-id", "updated_at": "2015-09-07T08:02:53.512184", "id": "713df749-aac0-4a54-af52-10f6c991e80c", "neutron_net_id": "new-neutron-id", "ip_version": 4, "nova_net_id": null, "cidr": null, "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "network_type": null, "description": "new description" } } `) }) } func MockUpdateNovaResponse(t *testing.T) { th.Mux.HandleFunc("/share-networks/713df749-aac0-4a54-af52-10f6c991e80c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "share_network": { "name": "net_my2", "segmentation_id": null, "created_at": "2015-09-04T14:54:25.000000", "neutron_subnet_id": null, "updated_at": "2015-09-07T08:02:53.512184", "id": "713df749-aac0-4a54-af52-10f6c991e80c", "neutron_net_id": null, "ip_version": 4, "nova_net_id": "new-nova-id", "cidr": null, "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "network_type": null, "description": "new description" } } `) }) } func MockAddSecurityServiceResponse(t *testing.T) { th.Mux.HandleFunc("/share-networks/shareNetworkID/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "share_network": { "name": "net2", "segmentation_id": null, "created_at": "2015-09-07T12:31:12.000000", "neutron_subnet_id": null, "updated_at": null, "id": "d8ae6799-2567-4a89-aafb-fa4424350d2b", "neutron_net_id": null, "ip_version": 4, "nova_net_id": "998b42ee-2cee-4d36-8b95-67b5ca1f2109", "cidr": null, "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "network_type": null, "description": null } }`) }) } func MockRemoveSecurityServiceResponse(t *testing.T) { th.Mux.HandleFunc("/share-networks/shareNetworkID/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "share_network": { "name": "net2", "segmentation_id": null, "created_at": "2015-09-07T12:31:12.000000", "neutron_subnet_id": null, "updated_at": null, "id": "d8ae6799-2567-4a89-aafb-fa4424350d2b", "neutron_net_id": null, "ip_version": null, "nova_net_id": "998b42ee-2cee-4d36-8b95-67b5ca1f2109", "cidr": null, "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "network_type": null, "description": null } }`) }) } requests_test.go000066400000000000000000000202521335005366400413670ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/sharenetworks/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharenetworks" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // Verifies that a share network can be created correctly func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockCreateResponse(t) options := &sharenetworks.CreateOpts{ Name: "my_network", Description: "This is my share network", NeutronNetID: "998b42ee-2cee-4d36-8b95-67b5ca1f2109", NeutronSubnetID: "53482b62-2c84-4a53-b6ab-30d9d9800d06", } n, err := sharenetworks.Create(client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, "my_network") th.AssertEquals(t, n.Description, "This is my share network") th.AssertEquals(t, n.NeutronNetID, "998b42ee-2cee-4d36-8b95-67b5ca1f2109") th.AssertEquals(t, n.NeutronSubnetID, "53482b62-2c84-4a53-b6ab-30d9d9800d06") } // Verifies that share network deletion works func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockDeleteResponse(t) res := sharenetworks.Delete(client.ServiceClient(), "fa158a3d-6d9f-4187-9ca5-abbb82646eb2") th.AssertNoErr(t, res.Err) } // Verifies that share networks can be listed correctly func TestListDetail(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) allPages, err := sharenetworks.ListDetail(client.ServiceClient(), &sharenetworks.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := sharenetworks.ExtractShareNetworks(allPages) th.AssertNoErr(t, err) var nilTime time.Time expected := []sharenetworks.ShareNetwork{ { ID: "32763294-e3d4-456a-998d-60047677c2fb", Name: "net_my1", CreatedAt: time.Date(2015, 9, 4, 14, 57, 13, 0, time.UTC), Description: "descr", NetworkType: "", CIDR: "", NovaNetID: "", NeutronNetID: "998b42ee-2cee-4d36-8b95-67b5ca1f2109", NeutronSubnetID: "53482b62-2c84-4a53-b6ab-30d9d9800d06", IPVersion: 0, SegmentationID: 0, UpdatedAt: nilTime, ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", }, { ID: "713df749-aac0-4a54-af52-10f6c991e80c", Name: "net_my", CreatedAt: time.Date(2015, 9, 4, 14, 54, 25, 0, time.UTC), Description: "desecr", NetworkType: "", CIDR: "", NovaNetID: "", NeutronNetID: "998b42ee-2cee-4d36-8b95-67b5ca1f2109", NeutronSubnetID: "53482b62-2c84-4a53-b6ab-30d9d9800d06", IPVersion: 0, SegmentationID: 0, UpdatedAt: nilTime, ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", }, { ID: "fa158a3d-6d9f-4187-9ca5-abbb82646eb2", Name: "", CreatedAt: time.Date(2015, 9, 4, 14, 51, 41, 0, time.UTC), Description: "", NetworkType: "", CIDR: "", NovaNetID: "", NeutronNetID: "", NeutronSubnetID: "", IPVersion: 0, SegmentationID: 0, UpdatedAt: nilTime, ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", }, } th.CheckDeepEquals(t, expected, actual) } // Verifies that share networks list can be called with query parameters func TestPaginatedListDetail(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockFilteredListResponse(t) options := &sharenetworks.ListOpts{ Offset: 0, Limit: 1, } count := 0 err := sharenetworks.ListDetail(client.ServiceClient(), options).EachPage(func(page pagination.Page) (bool, error) { count++ _, err := sharenetworks.ExtractShareNetworks(page) if err != nil { t.Errorf("Failed to extract share networks: %v", err) return false, err } return true, nil }) th.AssertNoErr(t, err) th.AssertEquals(t, count, 3) } // Verifies that it is possible to get a share network func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetResponse(t) var nilTime time.Time expected := sharenetworks.ShareNetwork{ ID: "7f950b52-6141-4a08-bbb5-bb7ffa3ea5fd", Name: "net_my1", CreatedAt: time.Date(2015, 9, 4, 14, 56, 45, 0, time.UTC), Description: "descr", NetworkType: "", CIDR: "", NovaNetID: "", NeutronNetID: "998b42ee-2cee-4d36-8b95-67b5ca1f2109", NeutronSubnetID: "53482b62-2c84-4a53-b6ab-30d9d9800d06", IPVersion: 0, SegmentationID: 0, UpdatedAt: nilTime, ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", } n, err := sharenetworks.Get(client.ServiceClient(), "7f950b52-6141-4a08-bbb5-bb7ffa3ea5fd").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, n) } // Verifies that it is possible to update a share network using neutron network func TestUpdateNeutron(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUpdateNeutronResponse(t) expected := sharenetworks.ShareNetwork{ ID: "713df749-aac0-4a54-af52-10f6c991e80c", Name: "net_my2", CreatedAt: time.Date(2015, 9, 4, 14, 54, 25, 0, time.UTC), Description: "new description", NetworkType: "", CIDR: "", NovaNetID: "", NeutronNetID: "new-neutron-id", NeutronSubnetID: "new-neutron-subnet-id", IPVersion: 4, SegmentationID: 0, UpdatedAt: time.Date(2015, 9, 7, 8, 2, 53, 512184000, time.UTC), ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", } options := sharenetworks.UpdateOpts{ Name: "net_my2", Description: "new description", NeutronNetID: "new-neutron-id", NeutronSubnetID: "new-neutron-subnet-id", } v, err := sharenetworks.Update(client.ServiceClient(), "713df749-aac0-4a54-af52-10f6c991e80c", options).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, v) } // Verifies that it is possible to update a share network using nova network func TestUpdateNova(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUpdateNovaResponse(t) expected := sharenetworks.ShareNetwork{ ID: "713df749-aac0-4a54-af52-10f6c991e80c", Name: "net_my2", CreatedAt: time.Date(2015, 9, 4, 14, 54, 25, 0, time.UTC), Description: "new description", NetworkType: "", CIDR: "", NovaNetID: "new-nova-id", NeutronNetID: "", NeutronSubnetID: "", IPVersion: 4, SegmentationID: 0, UpdatedAt: time.Date(2015, 9, 7, 8, 2, 53, 512184000, time.UTC), ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", } options := sharenetworks.UpdateOpts{ Name: "net_my2", Description: "new description", NovaNetID: "new-nova-id", } v, err := sharenetworks.Update(client.ServiceClient(), "713df749-aac0-4a54-af52-10f6c991e80c", options).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, v) } // Verifies that it is possible to add a security service to a share network func TestAddSecurityService(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockAddSecurityServiceResponse(t) var nilTime time.Time expected := sharenetworks.ShareNetwork{ ID: "d8ae6799-2567-4a89-aafb-fa4424350d2b", Name: "net2", CreatedAt: time.Date(2015, 9, 7, 12, 31, 12, 0, time.UTC), Description: "", NetworkType: "", CIDR: "", NovaNetID: "998b42ee-2cee-4d36-8b95-67b5ca1f2109", NeutronNetID: "", NeutronSubnetID: "", IPVersion: 4, SegmentationID: 0, UpdatedAt: nilTime, ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", } options := sharenetworks.AddSecurityServiceOpts{SecurityServiceID: "securityServiceID"} s, err := sharenetworks.AddSecurityService(client.ServiceClient(), "shareNetworkID", options).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, s) } // Verifies that it is possible to remove a security service from a share network func TestRemoveSecurityService(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockRemoveSecurityServiceResponse(t) options := sharenetworks.RemoveSecurityServiceOpts{SecurityServiceID: "securityServiceID"} _, err := sharenetworks.RemoveSecurityService(client.ServiceClient(), "shareNetworkID", options).Extract() th.AssertNoErr(t, err) } urls.go000066400000000000000000000015041335005366400357640ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/sharenetworkspackage sharenetworks import "github.com/gophercloud/gophercloud" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("share-networks") } func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("share-networks", id) } func listDetailURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("share-networks", "detail") } func getURL(c *gophercloud.ServiceClient, id string) string { return deleteURL(c, id) } func updateURL(c *gophercloud.ServiceClient, id string) string { return deleteURL(c, id) } func addSecurityServiceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("share-networks", id, "action") } func removeSecurityServiceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("share-networks", id, "action") } shares/000077500000000000000000000000001335005366400330365ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2requests.go000066400000000000000000000322341335005366400352440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/sharespackage shares import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToShareCreateMap() (map[string]interface{}, error) } // CreateOpts contains the options for create a Share. This object is // passed to shares.Create(). For more information about these parameters, // please refer to the Share object, or the shared file systems API v2 // documentation type CreateOpts struct { // Defines the share protocol to use ShareProto string `json:"share_proto" required:"true"` // Size in GB Size int `json:"size" required:"true"` // Defines the share name Name string `json:"name,omitempty"` // Share description Description string `json:"description,omitempty"` // DisplayName is equivalent to Name. The API supports using both // This is an inherited attribute from the block storage API DisplayName string `json:"display_name,omitempty"` // DisplayDescription is equivalent to Description. The API supports using both // This is an inherited attribute from the block storage API DisplayDescription string `json:"display_description,omitempty"` // ShareType defines the sharetype. If omitted, a default share type is used ShareType string `json:"share_type,omitempty"` // VolumeType is deprecated but supported. Either ShareType or VolumeType can be used VolumeType string `json:"volume_type,omitempty"` // The UUID from which to create a share SnapshotID string `json:"snapshot_id,omitempty"` // Determines whether or not the share is public IsPublic *bool `json:"is_public,omitempty"` // Key value pairs of user defined metadata Metadata map[string]string `json:"metadata,omitempty"` // The UUID of the share network to which the share belongs to ShareNetworkID string `json:"share_network_id,omitempty"` // The UUID of the consistency group to which the share belongs to ConsistencyGroupID string `json:"consistency_group_id,omitempty"` // The availability zone of the share AvailabilityZone string `json:"availability_zone,omitempty"` } // ToShareCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToShareCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "share") } // Create will create a new Share based on the values in CreateOpts. To extract // the Share object from the response, call the Extract method on the // CreateResult. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToShareCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) return } // ListOpts holds options for listing Shares. It is passed to the // shares.List function. type ListOpts struct { // (Admin only). Defines whether to list the requested resources for all projects. AllTenants bool `q:"all_tenants"` // The share name. Name string `q:"name"` // Filters by a share status. Status string `q:"status"` // The UUID of the share server. ShareServerID string `q:"share_server_id"` // One or more metadata key and value pairs as a dictionary of strings. Metadata map[string]string `q:"metadata"` // The extra specifications for the share type. ExtraSpecs map[string]string `q:"extra_specs"` // The UUID of the share type. ShareTypeID string `q:"share_type_id"` // The maximum number of shares to return. Limit int `q:"limit"` // The offset to define start point of share or share group listing. Offset int `q:"offset"` // The key to sort a list of shares. SortKey string `q:"sort_key"` // The direction to sort a list of shares. SortDir string `q:"sort_dir"` // The UUID of the share’s base snapshot to filter the request based on. SnapshotID string `q:"snapshot_id"` // The share host name. Host string `q:"host"` // The share network ID. ShareNetworkID string `q:"share_network_id"` // The UUID of the project in which the share was created. Useful with all_tenants parameter. ProjectID string `q:"project_id"` // The level of visibility for the share. IsPublic *bool `q:"is_public"` // The UUID of a share group to filter resource. ShareGroupID string `q:"share_group_id"` // The export location UUID that can be used to filter shares or share instances. ExportLocationID string `q:"export_location_id"` // The export location path that can be used to filter shares or share instances. ExportLocationPath string `q:"export_location_path"` // The name pattern that can be used to filter shares, share snapshots, share networks or share groups. NamePattern string `q:"name~"` // The description pattern that can be used to filter shares, share snapshots, share networks or share groups. DescriptionPattern string `q:"description~"` // Whether to show count in API response or not, default is False. WithCount bool `q:"with_count"` // DisplayName is equivalent to Name. The API supports using both // This is an inherited attribute from the block storage API DisplayName string `q:"display_name"` // Equivalent to NamePattern. DisplayNamePattern string `q:"display_name~"` // VolumeTypeID is deprecated but supported. Either ShareTypeID or VolumeTypeID can be used VolumeTypeID string `q:"volume_type_id"` // The UUID of the share group snapshot. ShareGroupSnapshotID string `q:"share_group_snapshot_id"` // DisplayDescription is equivalent to Description. The API supports using both // This is an inherited attribute from the block storage API DisplayDescription string `q:"display_description"` // Equivalent to DescriptionPattern DisplayDescriptionPattern string `q:"display_description~"` } // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { ToShareListQuery() (string, error) } // ToShareListQuery formats a ListOpts into a query string. func (opts ListOpts) ToShareListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // ListDetail returns []Share optionally limited by the conditions provided in ListOpts. func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listDetailURL(client) if opts != nil { query, err := opts.ToShareListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { p := SharePage{pagination.MarkerPageBase{PageResult: r}} p.MarkerPageBase.Owner = p return p }) } // Delete will delete an existing Share with the given UUID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // Get will get a single share with given UUID func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // GetExportLocations will get shareID's export locations. // Client must have Microversion set; minimum supported microversion for GetExportLocations is 2.14. func GetExportLocations(client *gophercloud.ServiceClient, id string) (r GetExportLocationsResult) { _, r.Err = client.Get(getExportLocationsURL(client, id), &r.Body, nil) return } // GrantAccessOptsBuilder allows extensions to add additional parameters to the // GrantAccess request. type GrantAccessOptsBuilder interface { ToGrantAccessMap() (map[string]interface{}, error) } // GrantAccessOpts contains the options for creation of an GrantAccess request. // For more information about these parameters, please, refer to the shared file systems API v2, // Share Actions, Grant Access documentation type GrantAccessOpts struct { // The access rule type that can be "ip", "cert" or "user". AccessType string `json:"access_type"` // The value that defines the access that can be a valid format of IP, cert or user. AccessTo string `json:"access_to"` // The access level to the share is either "rw" or "ro". AccessLevel string `json:"access_level"` } // ToGrantAccessMap assembles a request body based on the contents of a // GrantAccessOpts. func (opts GrantAccessOpts) ToGrantAccessMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "allow_access") } // GrantAccess will grant access to a Share based on the values in GrantAccessOpts. To extract // the GrantAccess object from the response, call the Extract method on the GrantAccessResult. // Client must have Microversion set; minimum supported microversion for GrantAccess is 2.7. func GrantAccess(client *gophercloud.ServiceClient, id string, opts GrantAccessOptsBuilder) (r GrantAccessResult) { b, err := opts.ToGrantAccessMap() if err != nil { r.Err = err return } _, r.Err = client.Post(grantAccessURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // RevokeAccessOptsBuilder allows extensions to add additional parameters to the // RevokeAccess request. type RevokeAccessOptsBuilder interface { ToRevokeAccessMap() (map[string]interface{}, error) } // RevokeAccessOpts contains the options for creation of a RevokeAccess request. // For more information about these parameters, please, refer to the shared file systems API v2, // Share Actions, Revoke Access documentation type RevokeAccessOpts struct { AccessID string `json:"access_id"` } // ToRevokeAccessMap assembles a request body based on the contents of a // RevokeAccessOpts. func (opts RevokeAccessOpts) ToRevokeAccessMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "deny_access") } // RevokeAccess will revoke an existing access to a Share based on the values in RevokeAccessOpts. // RevokeAccessResult contains only the error. To extract it, call the ExtractErr method on // the RevokeAccessResult. Client must have Microversion set; minimum supported microversion // for RevokeAccess is 2.7. func RevokeAccess(client *gophercloud.ServiceClient, id string, opts RevokeAccessOptsBuilder) (r RevokeAccessResult) { b, err := opts.ToRevokeAccessMap() if err != nil { r.Err = err return } _, r.Err = client.Post(revokeAccessURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) return } // ListAccessRights lists all access rules assigned to a Share based on its id. To extract // the AccessRight slice from the response, call the Extract method on the ListAccessRightsResult. // Client must have Microversion set; minimum supported microversion for ListAccessRights is 2.7. func ListAccessRights(client *gophercloud.ServiceClient, id string) (r ListAccessRightsResult) { requestBody := map[string]interface{}{"access_list": nil} _, r.Err = client.Post(listAccessRightsURL(client, id), requestBody, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // ExtendOptsBuilder allows extensions to add additional parameters to the // Extend request. type ExtendOptsBuilder interface { ToShareExtendMap() (map[string]interface{}, error) } // ExtendOpts contains options for extending a Share. // For more information about these parameters, please, refer to the shared file systems API v2, // Share Actions, Extend share documentation type ExtendOpts struct { // New size in GBs. NewSize int `json:"new_size"` } // ToShareExtendMap assembles a request body based on the contents of a // ExtendOpts. func (opts ExtendOpts) ToShareExtendMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "extend") } // Extend will extend the capacity of an existing share. ExtendResult contains only the error. // To extract it, call the ExtractErr method on the ExtendResult. // Client must have Microversion set; minimum supported microversion for Extend is 2.7. func Extend(client *gophercloud.ServiceClient, id string, opts ExtendOptsBuilder) (r ExtendResult) { b, err := opts.ToShareExtendMap() if err != nil { r.Err = err return } _, r.Err = client.Post(extendURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return } // ShrinkOptsBuilder allows extensions to add additional parameters to the // Shrink request. type ShrinkOptsBuilder interface { ToShareShrinkMap() (map[string]interface{}, error) } // ShrinkOpts contains options for shrinking a Share. // For more information about these parameters, please, refer to the shared file systems API v2, // Share Actions, Shrink share documentation type ShrinkOpts struct { // New size in GBs. NewSize int `json:"new_size"` } // ToShareShrinkMap assembles a request body based on the contents of a // ShrinkOpts. func (opts ShrinkOpts) ToShareShrinkMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "shrink") } // Shrink will shrink the capacity of an existing share. ShrinkResult contains only the error. // To extract it, call the ExtractErr method on the ShrinkResult. // Client must have Microversion set; minimum supported microversion for Shrink is 2.7. func Shrink(client *gophercloud.ServiceClient, id string, opts ShrinkOptsBuilder) (r ShrinkResult) { b, err := opts.ToShareShrinkMap() if err != nil { r.Err = err return } _, r.Err = client.Post(shrinkURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return } results.go000066400000000000000000000221201335005366400350630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/sharespackage shares import ( "encoding/json" "net/url" "strconv" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) const ( invalidMarker = "-1" ) // Share contains all information associated with an OpenStack Share type Share struct { // The availability zone of the share AvailabilityZone string `json:"availability_zone"` // A description of the share Description string `json:"description,omitempty"` // DisplayDescription is inherited from BlockStorage API. // Both Description and DisplayDescription can be used DisplayDescription string `json:"display_description,omitempty"` // DisplayName is inherited from BlockStorage API // Both DisplayName and Name can be used DisplayName string `json:"display_name,omitempty"` // Indicates whether a share has replicas or not. HasReplicas bool `json:"has_replicas"` // The host name of the share Host string `json:"host"` // The UUID of the share ID string `json:"id"` // Indicates the visibility of the share IsPublic bool `json:"is_public,omitempty"` // Share links for pagination Links []map[string]string `json:"links"` // Key, value -pairs of custom metadata Metadata map[string]string `json:"metadata,omitempty"` // The name of the share Name string `json:"name,omitempty"` // The UUID of the project to which this share belongs to ProjectID string `json:"project_id"` // The share replication type ReplicationType string `json:"replication_type,omitempty"` // The UUID of the share network ShareNetworkID string `json:"share_network_id"` // The shared file system protocol ShareProto string `json:"share_proto"` // The UUID of the share server ShareServerID string `json:"share_server_id"` // The UUID of the share type. ShareType string `json:"share_type"` // The name of the share type. ShareTypeName string `json:"share_type_name"` // Size of the share in GB Size int `json:"size"` // UUID of the snapshot from which to create the share SnapshotID string `json:"snapshot_id"` // The share status Status string `json:"status"` // The task state, used for share migration TaskState string `json:"task_state"` // The type of the volume VolumeType string `json:"volume_type,omitempty"` // The UUID of the consistency group this share belongs to ConsistencyGroupID string `json:"consistency_group_id"` // Used for filtering backends which either support or do not support share snapshots SnapshotSupport bool `json:"snapshot_support"` SourceCgsnapshotMemberID string `json:"source_cgsnapshot_member_id"` // Timestamp when the share was created CreatedAt time.Time `json:"-"` } func (r *Share) UnmarshalJSON(b []byte) error { type tmp Share var s struct { tmp CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Share(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) return nil } type commonResult struct { gophercloud.Result } // Extract will get the Share object from the commonResult func (r commonResult) Extract() (*Share, error) { var s struct { Share *Share `json:"share"` } err := r.ExtractInto(&s) return s.Share, err } // CreateResult contains the response body and error from a Create request. type CreateResult struct { commonResult } // SharePage is a pagination.pager that is returned from a call to the List function. type SharePage struct { pagination.MarkerPageBase } // NextPageURL generates the URL for the page of results after this one. func (r SharePage) NextPageURL() (string, error) { currentURL := r.URL mark, err := r.Owner.LastMarker() if err != nil { return "", err } if mark == invalidMarker { return "", nil } q := currentURL.Query() q.Set("offset", mark) currentURL.RawQuery = q.Encode() return currentURL.String(), nil } // LastMarker returns the last offset in a ListResult. func (r SharePage) LastMarker() (string, error) { shares, err := ExtractShares(r) if err != nil { return invalidMarker, err } if len(shares) == 0 { return invalidMarker, nil } u, err := url.Parse(r.URL.String()) if err != nil { return invalidMarker, err } queryParams := u.Query() offset := queryParams.Get("offset") limit := queryParams.Get("limit") // Limit is not present, only one page required if limit == "" { return invalidMarker, nil } iOffset := 0 if offset != "" { iOffset, err = strconv.Atoi(offset) if err != nil { return invalidMarker, err } } iLimit, err := strconv.Atoi(limit) if err != nil { return invalidMarker, err } iOffset = iOffset + iLimit offset = strconv.Itoa(iOffset) return offset, nil } // IsEmpty satisifies the IsEmpty method of the Page interface func (r SharePage) IsEmpty() (bool, error) { shares, err := ExtractShares(r) return len(shares) == 0, err } // ExtractShares extracts and returns a Share slice. It is used while // iterating over a shares.List call. func ExtractShares(r pagination.Page) ([]Share, error) { var s struct { Shares []Share `json:"shares"` } err := (r.(SharePage)).ExtractInto(&s) return s.Shares, err } // DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { gophercloud.ErrResult } // GetResult contains the response body and error from a Get request. type GetResult struct { commonResult } // IDFromName is a convenience function that returns a share's ID given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { r, err := ListDetail(client, &ListOpts{Name: name}).AllPages() if err != nil { return "", err } ss, err := ExtractShares(r) if err != nil { return "", err } switch len(ss) { case 0: return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "share"} case 1: return ss[0].ID, nil default: return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: len(ss), ResourceType: "share"} } } // GetExportLocationsResult contains the result body and error from an // GetExportLocations request. type GetExportLocationsResult struct { gophercloud.Result } // ExportLocation contains all information associated with a share export location type ExportLocation struct { // The export location path that should be used for mount operation. Path string `json:"path"` // The UUID of the share instance that this export location belongs to. ShareInstanceID string `json:"share_instance_id"` // Defines purpose of an export location. // If set to true, then it is expected to be used for service needs // and by administrators only. // If it is set to false, then this export location can be used by end users. IsAdminOnly bool `json:"is_admin_only"` // The share export location UUID. ID string `json:"id"` // Drivers may use this field to identify which export locations are // most efficient and should be used preferentially by clients. // By default it is set to false value. New in version 2.14 Preferred bool `json:"preferred"` } // Extract will get the Export Locations from the commonResult func (r GetExportLocationsResult) Extract() ([]ExportLocation, error) { var s struct { ExportLocations []ExportLocation `json:"export_locations"` } err := r.ExtractInto(&s) return s.ExportLocations, err } // AccessRight contains all information associated with an OpenStack share // Grant Access Response type AccessRight struct { // The UUID of the share to which you are granted or denied access. ShareID string `json:"share_id"` // The access rule type that can be "ip", "cert" or "user". AccessType string `json:"access_type,omitempty"` // The value that defines the access that can be a valid format of IP, cert or user. AccessTo string `json:"access_to,omitempty"` // The access credential of the entity granted share access. AccessKey string `json:"access_key,omitempty"` // The access level to the share is either "rw" or "ro". AccessLevel string `json:"access_level,omitempty"` // The state of the access rule State string `json:"state,omitempty"` // The access rule ID. ID string `json:"id"` } // Extract will get the GrantAccess object from the commonResult func (r GrantAccessResult) Extract() (*AccessRight, error) { var s struct { AccessRight *AccessRight `json:"access"` } err := r.ExtractInto(&s) return s.AccessRight, err } // GrantAccessResult contains the result body and error from an GrantAccess request. type GrantAccessResult struct { gophercloud.Result } // RevokeAccessResult contains the response body and error from a Revoke access request. type RevokeAccessResult struct { gophercloud.ErrResult } // Extract will get a slice of AccessRight objects from the commonResult func (r ListAccessRightsResult) Extract() ([]AccessRight, error) { var s struct { AccessRights []AccessRight `json:"access_list"` } err := r.ExtractInto(&s) return s.AccessRights, err } // ListAccessRightsResult contains the result body and error from a ListAccessRights request. type ListAccessRightsResult struct { gophercloud.Result } // ExtendResult contains the response body and error from an Extend request. type ExtendResult struct { gophercloud.ErrResult } // ShrinkResult contains the response body and error from a Shrink request. type ShrinkResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400345135ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/sharesfixtures.go000066400000000000000000000256341335005366400367250ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/shares/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) const ( shareEndpoint = "/shares" shareID = "011d21e2-fbc3-4e4a-9993-9ea223f73264" ) var createRequest = `{ "share": { "name": "my_test_share", "size": 1, "share_proto": "NFS" } }` var createResponse = `{ "share": { "name": "my_test_share", "share_proto": "NFS", "size": 1, "status": null, "share_server_id": null, "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "share_type": "25747776-08e5-494f-ab40-a64b9d20d8f7", "share_type_name": "default", "availability_zone": null, "created_at": "2015-09-18T10:25:24.533287", "export_location": null, "links": [ { "href": "http://172.18.198.54:8786/v1/16e1ab15c35a457e9c2b2aa189f544e1/shares/011d21e2-fbc3-4e4a-9993-9ea223f73264", "rel": "self" }, { "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/shares/011d21e2-fbc3-4e4a-9993-9ea223f73264", "rel": "bookmark" } ], "share_network_id": null, "export_locations": [], "host": null, "access_rules_status": "active", "has_replicas": false, "replication_type": null, "task_state": null, "snapshot_support": true, "consistency_group_id": "9397c191-8427-4661-a2e8-b23820dc01d4", "source_cgsnapshot_member_id": null, "volume_type": "default", "snapshot_id": null, "is_public": true, "metadata": { "project": "my_app", "aim": "doc" }, "id": "011d21e2-fbc3-4e4a-9993-9ea223f73264", "description": "My custom share London" } }` // MockCreateResponse creates a mock response func MockCreateResponse(t *testing.T) { th.Mux.HandleFunc(shareEndpoint, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, createRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, createResponse) }) } // MockDeleteResponse creates a mock delete response func MockDeleteResponse(t *testing.T) { th.Mux.HandleFunc(shareEndpoint+"/"+shareID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusAccepted) }) } var getResponse = `{ "share": { "links": [ { "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/shares/011d21e2-fbc3-4e4a-9993-9ea223f73264", "rel": "self" }, { "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/shares/011d21e2-fbc3-4e4a-9993-9ea223f73264", "rel": "bookmark" } ], "availability_zone": "nova", "share_network_id": "713df749-aac0-4a54-af52-10f6c991e80c", "share_server_id": "e268f4aa-d571-43dd-9ab3-f49ad06ffaef", "snapshot_id": null, "id": "011d21e2-fbc3-4e4a-9993-9ea223f73264", "size": 1, "share_type": "25747776-08e5-494f-ab40-a64b9d20d8f7", "share_type_name": "default", "consistency_group_id": "9397c191-8427-4661-a2e8-b23820dc01d4", "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "metadata": { "project": "my_app", "aim": "doc" }, "status": "available", "description": "My custom share London", "host": "manila2@generic1#GENERIC1", "has_replicas": false, "replication_type": null, "task_state": null, "is_public": true, "snapshot_support": true, "name": "my_test_share", "created_at": "2015-09-18T10:25:24.000000", "share_proto": "NFS", "volume_type": "default", "source_cgsnapshot_member_id": null } }` // MockGetResponse creates a mock get response func MockGetResponse(t *testing.T) { th.Mux.HandleFunc(shareEndpoint+"/"+shareID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, getResponse) }) } var listDetailResponse = `{ "shares": [ { "links": [ { "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/shares/011d21e2-fbc3-4e4a-9993-9ea223f73264", "rel": "self" }, { "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/shares/011d21e2-fbc3-4e4a-9993-9ea223f73264", "rel": "bookmark" } ], "availability_zone": "nova", "share_network_id": "713df749-aac0-4a54-af52-10f6c991e80c", "share_server_id": "e268f4aa-d571-43dd-9ab3-f49ad06ffaef", "snapshot_id": null, "id": "011d21e2-fbc3-4e4a-9993-9ea223f73264", "size": 1, "share_type": "25747776-08e5-494f-ab40-a64b9d20d8f7", "share_type_name": "default", "consistency_group_id": "9397c191-8427-4661-a2e8-b23820dc01d4", "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", "metadata": { "project": "my_app", "aim": "doc" }, "status": "available", "description": "My custom share London", "host": "manila2@generic1#GENERIC1", "has_replicas": false, "replication_type": null, "task_state": null, "is_public": true, "snapshot_support": true, "name": "my_test_share", "created_at": "2015-09-18T10:25:24.000000", "share_proto": "NFS", "volume_type": "default", "source_cgsnapshot_member_id": null } ] }` var listDetailEmptyResponse = `{"shares": []}` // MockListDetailResponse creates a mock detailed-list response func MockListDetailResponse(t *testing.T) { th.Mux.HandleFunc(shareEndpoint+"/detail", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) r.ParseForm() marker := r.Form.Get("offset") switch marker { case "": fmt.Fprint(w, listDetailResponse) default: fmt.Fprint(w, listDetailEmptyResponse) } }) } var getExportLocationsResponse = `{ "export_locations": [ { "path": "127.0.0.1:/var/lib/manila/mnt/share-9a922036-ad26-4d27-b955-7a1e285fa74d", "share_instance_id": "011d21e2-fbc3-4e4a-9993-9ea223f73264", "is_admin_only": false, "id": "80ed63fc-83bc-4afc-b881-da4a345ac83d", "preferred": false } ] }` // MockGetExportLocationsResponse creates a mock get export locations response func MockGetExportLocationsResponse(t *testing.T) { th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/export_locations", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, getExportLocationsResponse) }) } var grantAccessRequest = `{ "allow_access": { "access_type": "ip", "access_to": "0.0.0.0/0", "access_level": "rw" } }` var grantAccessResponse = `{ "access": { "share_id": "011d21e2-fbc3-4e4a-9993-9ea223f73264", "access_type": "ip", "access_to": "0.0.0.0/0", "access_key": "", "access_level": "rw", "state": "new", "id": "a2f226a5-cee8-430b-8a03-78a59bd84ee8" } }` // MockGrantAccessResponse creates a mock grant access response func MockGrantAccessResponse(t *testing.T) { th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, grantAccessRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, grantAccessResponse) }) } var revokeAccessRequest = `{ "deny_access": { "access_id": "a2f226a5-cee8-430b-8a03-78a59bd84ee8" } }` // MockRevokeAccessResponse creates a mock revoke access response func MockRevokeAccessResponse(t *testing.T) { th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, revokeAccessRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) }) } var listAccessRightsRequest = `{ "access_list": null }` var listAccessRightsResponse = `{ "access_list": [ { "share_id": "011d21e2-fbc3-4e4a-9993-9ea223f73264", "access_type": "ip", "access_to": "0.0.0.0/0", "access_key": "", "access_level": "rw", "state": "new", "id": "a2f226a5-cee8-430b-8a03-78a59bd84ee8" } ] }` // MockListAccessRightsResponse creates a mock list access response func MockListAccessRightsResponse(t *testing.T) { th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, listAccessRightsRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, listAccessRightsResponse) }) } var extendRequest = `{ "extend": { "new_size": 2 } }` // MockExtendResponse creates a mock extend share response func MockExtendResponse(t *testing.T) { th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, extendRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) }) } var shrinkRequest = `{ "shrink": { "new_size": 1 } }` // MockShrinkResponse creates a mock shrink share response func MockShrinkResponse(t *testing.T) { th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, shrinkRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) }) } request_test.go000066400000000000000000000167161335005366400376040ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/shares/testingpackage testing import ( "testing" "time" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockCreateResponse(t) options := &shares.CreateOpts{Size: 1, Name: "my_test_share", ShareProto: "NFS"} n, err := shares.Create(client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, "my_test_share") th.AssertEquals(t, n.Size, 1) th.AssertEquals(t, n.ShareProto, "NFS") } func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockDeleteResponse(t) result := shares.Delete(client.ServiceClient(), shareID) th.AssertNoErr(t, result.Err) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetResponse(t) s, err := shares.Get(client.ServiceClient(), shareID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, s, &shares.Share{ AvailabilityZone: "nova", ShareNetworkID: "713df749-aac0-4a54-af52-10f6c991e80c", ShareServerID: "e268f4aa-d571-43dd-9ab3-f49ad06ffaef", SnapshotID: "", ID: shareID, Size: 1, ShareType: "25747776-08e5-494f-ab40-a64b9d20d8f7", ShareTypeName: "default", ConsistencyGroupID: "9397c191-8427-4661-a2e8-b23820dc01d4", ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", Metadata: map[string]string{ "project": "my_app", "aim": "doc", }, Status: "available", Description: "My custom share London", Host: "manila2@generic1#GENERIC1", HasReplicas: false, ReplicationType: "", TaskState: "", SnapshotSupport: true, Name: "my_test_share", CreatedAt: time.Date(2015, time.September, 18, 10, 25, 24, 0, time.UTC), ShareProto: "NFS", VolumeType: "default", SourceCgsnapshotMemberID: "", IsPublic: true, Links: []map[string]string{ { "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/shares/011d21e2-fbc3-4e4a-9993-9ea223f73264", "rel": "self", }, { "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/shares/011d21e2-fbc3-4e4a-9993-9ea223f73264", "rel": "bookmark", }, }, }) } func TestListDetail(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListDetailResponse(t) allPages, err := shares.ListDetail(client.ServiceClient(), &shares.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := shares.ExtractShares(allPages) th.AssertNoErr(t, err) th.AssertDeepEquals(t, actual, []shares.Share{ shares.Share{ AvailabilityZone: "nova", ShareNetworkID: "713df749-aac0-4a54-af52-10f6c991e80c", ShareServerID: "e268f4aa-d571-43dd-9ab3-f49ad06ffaef", SnapshotID: "", ID: shareID, Size: 1, ShareType: "25747776-08e5-494f-ab40-a64b9d20d8f7", ShareTypeName: "default", ConsistencyGroupID: "9397c191-8427-4661-a2e8-b23820dc01d4", ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", Metadata: map[string]string{ "project": "my_app", "aim": "doc", }, Status: "available", Description: "My custom share London", Host: "manila2@generic1#GENERIC1", HasReplicas: false, ReplicationType: "", TaskState: "", SnapshotSupport: true, Name: "my_test_share", CreatedAt: time.Date(2015, time.September, 18, 10, 25, 24, 0, time.UTC), ShareProto: "NFS", VolumeType: "default", SourceCgsnapshotMemberID: "", IsPublic: true, Links: []map[string]string{ { "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/shares/011d21e2-fbc3-4e4a-9993-9ea223f73264", "rel": "self", }, { "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/shares/011d21e2-fbc3-4e4a-9993-9ea223f73264", "rel": "bookmark", }, }, }, }) } func TestGetExportLocationsSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetExportLocationsResponse(t) c := client.ServiceClient() // Client c must have Microversion set; minimum supported microversion for Get Export Locations is 2.14 c.Microversion = "2.14" s, err := shares.GetExportLocations(c, shareID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, s, []shares.ExportLocation{ { Path: "127.0.0.1:/var/lib/manila/mnt/share-9a922036-ad26-4d27-b955-7a1e285fa74d", ShareInstanceID: "011d21e2-fbc3-4e4a-9993-9ea223f73264", IsAdminOnly: false, ID: "80ed63fc-83bc-4afc-b881-da4a345ac83d", Preferred: false, }, }) } func TestGrantAcessSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGrantAccessResponse(t) c := client.ServiceClient() // Client c must have Microversion set; minimum supported microversion for Grant Access is 2.7 c.Microversion = "2.7" var grantAccessReq shares.GrantAccessOpts grantAccessReq.AccessType = "ip" grantAccessReq.AccessTo = "0.0.0.0/0" grantAccessReq.AccessLevel = "rw" s, err := shares.GrantAccess(c, shareID, grantAccessReq).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, s, &shares.AccessRight{ ShareID: "011d21e2-fbc3-4e4a-9993-9ea223f73264", AccessType: "ip", AccessTo: "0.0.0.0/0", AccessKey: "", AccessLevel: "rw", State: "new", ID: "a2f226a5-cee8-430b-8a03-78a59bd84ee8", }) } func TestRevokeAccessSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockRevokeAccessResponse(t) c := client.ServiceClient() // Client c must have Microversion set; minimum supported microversion for Revoke Access is 2.7 c.Microversion = "2.7" options := &shares.RevokeAccessOpts{AccessID: "a2f226a5-cee8-430b-8a03-78a59bd84ee8"} err := shares.RevokeAccess(c, shareID, options).ExtractErr() th.AssertNoErr(t, err) } func TestListAccessRightsSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListAccessRightsResponse(t) c := client.ServiceClient() // Client c must have Microversion set; minimum supported microversion for Grant Access is 2.7 c.Microversion = "2.7" s, err := shares.ListAccessRights(c, shareID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, s, []shares.AccessRight{ { ShareID: "011d21e2-fbc3-4e4a-9993-9ea223f73264", AccessType: "ip", AccessTo: "0.0.0.0/0", AccessKey: "", AccessLevel: "rw", State: "new", ID: "a2f226a5-cee8-430b-8a03-78a59bd84ee8", }, }) } func TestExtendSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockExtendResponse(t) c := client.ServiceClient() // Client c must have Microversion set; minimum supported microversion for Grant Access is 2.7 c.Microversion = "2.7" err := shares.Extend(c, shareID, &shares.ExtendOpts{NewSize: 2}).ExtractErr() th.AssertNoErr(t, err) } func TestShrinkSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockShrinkResponse(t) c := client.ServiceClient() // Client c must have Microversion set; minimum supported microversion for Grant Access is 2.7 c.Microversion = "2.7" err := shares.Shrink(c, shareID, &shares.ShrinkOpts{NewSize: 1}).ExtractErr() th.AssertNoErr(t, err) } urls.go000066400000000000000000000022261335005366400343540ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/sharespackage shares import "github.com/gophercloud/gophercloud" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("shares") } func listDetailURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("shares", "detail") } func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id) } func getURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id) } func getExportLocationsURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "export_locations") } func grantAccessURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "action") } func revokeAccessURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "action") } func listAccessRightsURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "action") } func extendURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "action") } func shrinkURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "action") } sharetypes/000077500000000000000000000000001335005366400337405ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2requests.go000066400000000000000000000160521335005366400361460ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/sharetypespackage sharetypes import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { ToShareTypeCreateMap() (map[string]interface{}, error) } // CreateOpts contains options for creating a ShareType. This object is // passed to the sharetypes.Create function. For more information about // these parameters, see the ShareType object. type CreateOpts struct { // The share type name Name string `json:"name" required:"true"` // Indicates whether a share type is publicly accessible IsPublic bool `json:"os-share-type-access:is_public"` // The extra specifications for the share type ExtraSpecs ExtraSpecsOpts `json:"extra_specs" required:"true"` } // ExtraSpecsOpts represent the extra specifications that can be selected for a share type type ExtraSpecsOpts struct { // An extra specification that defines the driver mode for share server, or storage, life cycle management DriverHandlesShareServers bool `json:"driver_handles_share_servers" required:"true"` // An extra specification that filters back ends by whether they do or do not support share snapshots SnapshotSupport *bool `json:"snapshot_support,omitempty"` } // ToShareTypeCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToShareTypeCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "share_type") } // Create will create a new ShareType based on the values in CreateOpts. To // extract the ShareType object from the response, call the Extract method // on the CreateResult. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToShareTypeCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) return } // Delete will delete the existing ShareType with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { ToShareTypeListQuery() (string, error) } // ListOpts holds options for listing ShareTypes. It is passed to the // sharetypes.List function. type ListOpts struct { // Select if public types, private types, or both should be listed IsPublic string `q:"is_public"` } // ToShareTypeListQuery formats a ListOpts into a query string. func (opts ListOpts) ToShareTypeListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List returns ShareTypes optionally limited by the conditions provided in ListOpts. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToShareTypeListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ShareTypePage{pagination.SinglePageBase(r)} }) } // GetDefault will retrieve the default ShareType. func GetDefault(client *gophercloud.ServiceClient) (r GetDefaultResult) { _, r.Err = client.Get(getDefaultURL(client), &r.Body, nil) return } // GetExtraSpecs will retrieve the extra specifications for a given ShareType. func GetExtraSpecs(client *gophercloud.ServiceClient, id string) (r GetExtraSpecsResult) { _, r.Err = client.Get(getExtraSpecsURL(client, id), &r.Body, nil) return } // SetExtraSpecsOptsBuilder allows extensions to add additional parameters to the // SetExtraSpecs request. type SetExtraSpecsOptsBuilder interface { ToShareTypeSetExtraSpecsMap() (map[string]interface{}, error) } type SetExtraSpecsOpts struct { // A list of all extra specifications to be added to a ShareType ExtraSpecs map[string]interface{} `json:"extra_specs" required:"true"` } // ToShareTypeSetExtraSpecsMap assembles a request body based on the contents of a // SetExtraSpecsOpts. func (opts SetExtraSpecsOpts) ToShareTypeSetExtraSpecsMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // SetExtraSpecs will set new specifications for a ShareType based on the values // in SetExtraSpecsOpts. To extract the extra specifications object from the response, // call the Extract method on the SetExtraSpecsResult. func SetExtraSpecs(client *gophercloud.ServiceClient, id string, opts SetExtraSpecsOptsBuilder) (r SetExtraSpecsResult) { b, err := opts.ToShareTypeSetExtraSpecsMap() if err != nil { r.Err = err return } _, r.Err = client.Post(setExtraSpecsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) return } // UnsetExtraSpecs will unset an extra specification for an existing ShareType. func UnsetExtraSpecs(client *gophercloud.ServiceClient, id string, key string) (r UnsetExtraSpecsResult) { _, r.Err = client.Delete(unsetExtraSpecsURL(client, id, key), nil) return } // ShowAccess will show access details for an existing ShareType. func ShowAccess(client *gophercloud.ServiceClient, id string) (r ShowAccessResult) { _, r.Err = client.Get(showAccessURL(client, id), &r.Body, nil) return } // AddAccessOptsBuilder allows extensions to add additional parameters to the // AddAccess type AddAccessOptsBuilder interface { ToAddAccessMap() (map[string]interface{}, error) } type AccessOpts struct { // The UUID of the project to which access to the share type is granted. Project string `json:"project"` } // ToAddAccessMap assembles a request body based on the contents of a // AccessOpts. func (opts AccessOpts) ToAddAccessMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "addProjectAccess") } // AddAccess will add access to a ShareType based on the values // in AccessOpts. func AddAccess(client *gophercloud.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) { b, err := opts.ToAddAccessMap() if err != nil { r.Err = err return } _, r.Err = client.Post(addAccessURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) return } // RemoveAccessOptsBuilder allows extensions to add additional parameters to the // RemoveAccess type RemoveAccessOptsBuilder interface { ToRemoveAccessMap() (map[string]interface{}, error) } // ToRemoveAccessMap assembles a request body based on the contents of a // AccessOpts. func (opts AccessOpts) ToRemoveAccessMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "removeProjectAccess") } // RemoveAccess will remove access to a ShareType based on the values // in AccessOpts. func RemoveAccess(client *gophercloud.ServiceClient, id string, opts RemoveAccessOptsBuilder) (r RemoveAccessResult) { b, err := opts.ToRemoveAccessMap() if err != nil { r.Err = err return } _, r.Err = client.Post(removeAccessURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) return } results.go000066400000000000000000000076501335005366400360000ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/sharetypespackage sharetypes import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // ShareType contains all the information associated with an OpenStack // ShareType. type ShareType struct { // The Share Type ID ID string `json:"id"` // The Share Type name Name string `json:"name"` // Indicates whether a share type is publicly accessible IsPublic bool `json:"os-share-type-access:is_public"` // The required extra specifications for the share type RequiredExtraSpecs map[string]interface{} `json:"required_extra_specs"` // The extra specifications for the share type ExtraSpecs map[string]interface{} `json:"extra_specs"` } type commonResult struct { gophercloud.Result } // Extract will get the ShareType object out of the commonResult object. func (r commonResult) Extract() (*ShareType, error) { var s struct { ShareType *ShareType `json:"share_type"` } err := r.ExtractInto(&s) return s.ShareType, err } // CreateResult contains the response body and error from a Create request. type CreateResult struct { commonResult } // DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { gophercloud.ErrResult } // ShareTypePage is a pagination.pager that is returned from a call to the List function. type ShareTypePage struct { pagination.SinglePageBase } // IsEmpty returns true if a ListResult contains no ShareTypes. func (r ShareTypePage) IsEmpty() (bool, error) { shareTypes, err := ExtractShareTypes(r) return len(shareTypes) == 0, err } // ExtractShareTypes extracts and returns ShareTypes. It is used while // iterating over a sharetypes.List call. func ExtractShareTypes(r pagination.Page) ([]ShareType, error) { var s struct { ShareTypes []ShareType `json:"share_types"` } err := (r.(ShareTypePage)).ExtractInto(&s) return s.ShareTypes, err } // GetDefaultResult contains the response body and error from a Get Default request. type GetDefaultResult struct { commonResult } // ExtraSpecs contains all the information associated with extra specifications // for an Openstack ShareType. type ExtraSpecs map[string]interface{} type extraSpecsResult struct { gophercloud.Result } // Extract will get the ExtraSpecs object out of the commonResult object. func (r extraSpecsResult) Extract() (ExtraSpecs, error) { var s struct { Specs ExtraSpecs `json:"extra_specs"` } err := r.ExtractInto(&s) return s.Specs, err } // GetExtraSpecsResult contains the response body and error from a Get Extra Specs request. type GetExtraSpecsResult struct { extraSpecsResult } // SetExtraSpecsResult contains the response body and error from a Set Extra Specs request. type SetExtraSpecsResult struct { extraSpecsResult } // UnsetExtraSpecsResult contains the response body and error from a Unset Extra Specs request. type UnsetExtraSpecsResult struct { gophercloud.ErrResult } // ShareTypeAccess contains all the information associated with an OpenStack // ShareTypeAccess. type ShareTypeAccess struct { // The share type ID of the member. ShareTypeID string `json:"share_type_id"` // The UUID of the project for which access to the share type is granted. ProjectID string `json:"project_id"` } type shareTypeAccessResult struct { gophercloud.Result } // ShowAccessResult contains the response body and error from a Show access request. type ShowAccessResult struct { shareTypeAccessResult } // Extract will get the ShareTypeAccess objects out of the shareTypeAccessResult object. func (r ShowAccessResult) Extract() ([]ShareTypeAccess, error) { var s struct { ShareTypeAccess []ShareTypeAccess `json:"share_type_access"` } err := r.ExtractInto(&s) return s.ShareTypeAccess, err } // AddAccessResult contains the response body and error from a Add Access request. type AddAccessResult struct { gophercloud.ErrResult } // RemoveAccessResult contains the response body and error from a Remove Access request. type RemoveAccessResult struct { gophercloud.ErrResult } testing/000077500000000000000000000000001335005366400354155ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/sharetypesfixtures.go000066400000000000000000000215151335005366400376210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/sharetypes/testingpackage testing import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func MockCreateResponse(t *testing.T) { th.Mux.HandleFunc("/types", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "share_type": { "os-share-type-access:is_public": true, "extra_specs": { "driver_handles_share_servers": true, "snapshot_support": true }, "name": "my_new_share_type" } }`) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, ` { "volume_type": { "os-share-type-access:is_public": true, "required_extra_specs": { "driver_handles_share_servers": true }, "extra_specs": { "snapshot_support": "True", "driver_handles_share_servers": "True" }, "name": "my_new_share_type", "id": "1d600d02-26a7-4b23-af3d-7d51860fe858" }, "share_type": { "os-share-type-access:is_public": true, "required_extra_specs": { "driver_handles_share_servers": true }, "extra_specs": { "snapshot_support": "True", "driver_handles_share_servers": "True" }, "name": "my_new_share_type", "id": "1d600d02-26a7-4b23-af3d-7d51860fe858" } }`) }) } func MockDeleteResponse(t *testing.T) { th.Mux.HandleFunc("/types/shareTypeID", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusAccepted) }) } func MockListResponse(t *testing.T) { th.Mux.HandleFunc("/types", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "volume_types": [ { "os-share-type-access:is_public": true, "required_extra_specs": { "driver_handles_share_servers": "True" }, "extra_specs": { "snapshot_support": "True", "driver_handles_share_servers": "True" }, "name": "default", "id": "be27425c-f807-4500-a056-d00721db45cf" }, { "os-share-type-access:is_public": true, "required_extra_specs": { "driver_handles_share_servers": "false" }, "extra_specs": { "snapshot_support": "True", "driver_handles_share_servers": "false" }, "name": "d", "id": "f015bebe-c38b-4c49-8832-00143b10253b" } ], "share_types": [ { "os-share-type-access:is_public": true, "required_extra_specs": { "driver_handles_share_servers": "True" }, "extra_specs": { "snapshot_support": "True", "driver_handles_share_servers": "True" }, "name": "default", "id": "be27425c-f807-4500-a056-d00721db45cf" }, { "os-share-type-access:is_public": true, "required_extra_specs": { "driver_handles_share_servers": "false" }, "extra_specs": { "snapshot_support": "True", "driver_handles_share_servers": "false" }, "name": "d", "id": "f015bebe-c38b-4c49-8832-00143b10253b" } ] }`) }) } func MockGetDefaultResponse(t *testing.T) { th.Mux.HandleFunc("/types/default", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "volume_type": { "required_extra_specs": null, "extra_specs": { "snapshot_support": "True", "driver_handles_share_servers": "True" }, "name": "default", "id": "be27425c-f807-4500-a056-d00721db45cf" }, "share_type": { "required_extra_specs": null, "extra_specs": { "snapshot_support": "True", "driver_handles_share_servers": "True" }, "name": "default", "id": "be27425c-f807-4500-a056-d00721db45cf" } }`) }) } func MockGetExtraSpecsResponse(t *testing.T) { th.Mux.HandleFunc("/types/shareTypeID/extra_specs", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "extra_specs": { "snapshot_support": "True", "driver_handles_share_servers": "True", "my_custom_extra_spec": "False" } }`) }) } func MockSetExtraSpecsResponse(t *testing.T) { th.Mux.HandleFunc("/types/shareTypeID/extra_specs", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "extra_specs": { "my_key": "my_value" } }`) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) fmt.Fprintf(w, ` { "extra_specs": { "my_key": "my_value" } }`) }) } func MockUnsetExtraSpecsResponse(t *testing.T) { th.Mux.HandleFunc("/types/shareTypeID/extra_specs/my_key", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusAccepted) }) } func MockShowAccessResponse(t *testing.T) { th.Mux.HandleFunc("/types/shareTypeID/share_type_access", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { "share_type_access": [ { "share_type_id": "1732f284-401d-41d9-a494-425451e8b4b8", "project_id": "818a3f48dcd644909b3fa2e45a399a27" }, { "share_type_id": "1732f284-401d-41d9-a494-425451e8b4b8", "project_id": "e1284adea3ee4d2482af5ed214f3ad90" } ] }`) }) } func MockAddAccessResponse(t *testing.T) { th.Mux.HandleFunc("/types/shareTypeID/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "addProjectAccess": { "project": "e1284adea3ee4d2482af5ed214f3ad90" } }`) w.WriteHeader(http.StatusAccepted) }) } func MockRemoveAccessResponse(t *testing.T) { th.Mux.HandleFunc("/types/shareTypeID/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") th.TestJSONRequest(t, r, ` { "removeProjectAccess": { "project": "e1284adea3ee4d2482af5ed214f3ad90" } }`) w.WriteHeader(http.StatusAccepted) }) } requests_test.go000066400000000000000000000137401335005366400406630ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/sharetypes/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharetypes" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) // Verifies that a share type can be created correctly func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockCreateResponse(t) snapshotSupport := true extraSpecs := sharetypes.ExtraSpecsOpts{ DriverHandlesShareServers: true, SnapshotSupport: &snapshotSupport, } options := &sharetypes.CreateOpts{ Name: "my_new_share_type", IsPublic: true, ExtraSpecs: extraSpecs, } st, err := sharetypes.Create(client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, st.Name, "my_new_share_type") th.AssertEquals(t, st.IsPublic, true) } // Verifies that a share type can't be created if the required parameters are missing func TestCreateFails(t *testing.T) { options := &sharetypes.CreateOpts{ Name: "my_new_share_type", } _, err := sharetypes.Create(client.ServiceClient(), options).Extract() if _, ok := err.(gophercloud.ErrMissingInput); !ok { t.Fatal("ErrMissingInput was expected to occur") } extraSpecs := sharetypes.ExtraSpecsOpts{ DriverHandlesShareServers: true, } options = &sharetypes.CreateOpts{ ExtraSpecs: extraSpecs, } _, err = sharetypes.Create(client.ServiceClient(), options).Extract() if _, ok := err.(gophercloud.ErrMissingInput); !ok { t.Fatal("ErrMissingInput was expected to occur") } } // Verifies that share type deletion works func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockDeleteResponse(t) res := sharetypes.Delete(client.ServiceClient(), "shareTypeID") th.AssertNoErr(t, res.Err) } // Verifies that share types can be listed correctly func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockListResponse(t) allPages, err := sharetypes.List(client.ServiceClient(), &sharetypes.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := sharetypes.ExtractShareTypes(allPages) th.AssertNoErr(t, err) expected := []sharetypes.ShareType{ { ID: "be27425c-f807-4500-a056-d00721db45cf", Name: "default", IsPublic: true, ExtraSpecs: map[string]interface{}{"snapshot_support": "True", "driver_handles_share_servers": "True"}, RequiredExtraSpecs: map[string]interface{}{"driver_handles_share_servers": "True"}, }, { ID: "f015bebe-c38b-4c49-8832-00143b10253b", Name: "d", IsPublic: true, ExtraSpecs: map[string]interface{}{"driver_handles_share_servers": "false", "snapshot_support": "True"}, RequiredExtraSpecs: map[string]interface{}{"driver_handles_share_servers": "false"}, }, } th.CheckDeepEquals(t, expected, actual) } // Verifies that it is possible to get the default share type func TestGetDefault(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetDefaultResponse(t) expected := sharetypes.ShareType{ ID: "be27425c-f807-4500-a056-d00721db45cf", Name: "default", ExtraSpecs: map[string]interface{}{"snapshot_support": "True", "driver_handles_share_servers": "True"}, RequiredExtraSpecs: map[string]interface{}(nil), } actual, err := sharetypes.GetDefault(client.ServiceClient()).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, actual) } // Verifies that it is possible to get the extra specifications for a share type func TestGetExtraSpecs(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockGetExtraSpecsResponse(t) st, err := sharetypes.GetExtraSpecs(client.ServiceClient(), "shareTypeID").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, st["snapshot_support"], "True") th.AssertEquals(t, st["driver_handles_share_servers"], "True") th.AssertEquals(t, st["my_custom_extra_spec"], "False") } // Verifies that an extra specs can be added to a share type func TestSetExtraSpecs(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockSetExtraSpecsResponse(t) options := &sharetypes.SetExtraSpecsOpts{ ExtraSpecs: map[string]interface{}{"my_key": "my_value"}, } es, err := sharetypes.SetExtraSpecs(client.ServiceClient(), "shareTypeID", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, es["my_key"], "my_value") } // Verifies that an extra specification can be unset for a share type func TestUnsetExtraSpecs(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockUnsetExtraSpecsResponse(t) res := sharetypes.UnsetExtraSpecs(client.ServiceClient(), "shareTypeID", "my_key") th.AssertNoErr(t, res.Err) } // Verifies that it is possible to see the access for a share type func TestShowAccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockShowAccessResponse(t) expected := []sharetypes.ShareTypeAccess{ { ShareTypeID: "1732f284-401d-41d9-a494-425451e8b4b8", ProjectID: "818a3f48dcd644909b3fa2e45a399a27", }, { ShareTypeID: "1732f284-401d-41d9-a494-425451e8b4b8", ProjectID: "e1284adea3ee4d2482af5ed214f3ad90", }, } shareType, err := sharetypes.ShowAccess(client.ServiceClient(), "shareTypeID").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, shareType) } // Verifies that an access can be added to a share type func TestAddAccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockAddAccessResponse(t) options := &sharetypes.AccessOpts{ Project: "e1284adea3ee4d2482af5ed214f3ad90", } err := sharetypes.AddAccess(client.ServiceClient(), "shareTypeID", options).ExtractErr() th.AssertNoErr(t, err) } // Verifies that an access can be removed from a share type func TestRemoveAccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() MockRemoveAccessResponse(t) options := &sharetypes.AccessOpts{ Project: "e1284adea3ee4d2482af5ed214f3ad90", } err := sharetypes.RemoveAccess(client.ServiceClient(), "shareTypeID", options).ExtractErr() th.AssertNoErr(t, err) } urls.go000066400000000000000000000021751335005366400352610ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/sharedfilesystems/v2/sharetypespackage sharetypes import "github.com/gophercloud/gophercloud" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("types") } func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("types", id) } func listURL(c *gophercloud.ServiceClient) string { return createURL(c) } func getDefaultURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("types", "default") } func getExtraSpecsURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("types", id, "extra_specs") } func setExtraSpecsURL(c *gophercloud.ServiceClient, id string) string { return getExtraSpecsURL(c, id) } func unsetExtraSpecsURL(c *gophercloud.ServiceClient, id string, key string) string { return c.ServiceURL("types", id, "extra_specs", key) } func showAccessURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("types", id, "share_type_access") } func addAccessURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("types", id, "action") } func removeAccessURL(c *gophercloud.ServiceClient, id string) string { return addAccessURL(c, id) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/testing/000077500000000000000000000000001335005366400272205ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/testing/client_test.go000066400000000000000000000204251335005366400320670ustar00rootroot00000000000000package testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack" th "github.com/gophercloud/gophercloud/testhelper" ) const ID = "0123456789" func TestAuthenticatedClientV3(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, ` { "versions": { "values": [ { "status": "stable", "id": "v3.0", "links": [ { "href": "%s", "rel": "self" } ] }, { "status": "stable", "id": "v2.0", "links": [ { "href": "%s", "rel": "self" } ] } ] } } `, th.Endpoint()+"v3/", th.Endpoint()+"v2.0/") }) th.Mux.HandleFunc("/v3/auth/tokens", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("X-Subject-Token", ID) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, `{ "token": { "expires_at": "2013-02-02T18:30:59.000000Z" } }`) }) options := gophercloud.AuthOptions{ Username: "me", Password: "secret", DomainName: "default", TenantName: "project", IdentityEndpoint: th.Endpoint(), } client, err := openstack.AuthenticatedClient(options) th.AssertNoErr(t, err) th.CheckEquals(t, ID, client.TokenID) } func TestAuthenticatedClientV2(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, ` { "versions": { "values": [ { "status": "experimental", "id": "v3.0", "links": [ { "href": "%s", "rel": "self" } ] }, { "status": "stable", "id": "v2.0", "links": [ { "href": "%s", "rel": "self" } ] } ] } } `, th.Endpoint()+"v3/", th.Endpoint()+"v2.0/") }) th.Mux.HandleFunc("/v2.0/tokens", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, ` { "access": { "token": { "id": "01234567890", "expires": "2014-10-01T10:00:00.000000Z" }, "serviceCatalog": [ { "name": "Cloud Servers", "type": "compute", "endpoints": [ { "tenantId": "t1000", "publicURL": "https://compute.north.host.com/v1/t1000", "internalURL": "https://compute.north.internal/v1/t1000", "region": "North", "versionId": "1", "versionInfo": "https://compute.north.host.com/v1/", "versionList": "https://compute.north.host.com/" }, { "tenantId": "t1000", "publicURL": "https://compute.north.host.com/v1.1/t1000", "internalURL": "https://compute.north.internal/v1.1/t1000", "region": "North", "versionId": "1.1", "versionInfo": "https://compute.north.host.com/v1.1/", "versionList": "https://compute.north.host.com/" } ], "endpoints_links": [] }, { "name": "Cloud Files", "type": "object-store", "endpoints": [ { "tenantId": "t1000", "publicURL": "https://storage.north.host.com/v1/t1000", "internalURL": "https://storage.north.internal/v1/t1000", "region": "North", "versionId": "1", "versionInfo": "https://storage.north.host.com/v1/", "versionList": "https://storage.north.host.com/" }, { "tenantId": "t1000", "publicURL": "https://storage.south.host.com/v1/t1000", "internalURL": "https://storage.south.internal/v1/t1000", "region": "South", "versionId": "1", "versionInfo": "https://storage.south.host.com/v1/", "versionList": "https://storage.south.host.com/" } ] } ] } } `) }) options := gophercloud.AuthOptions{ Username: "me", Password: "secret", IdentityEndpoint: th.Endpoint(), } client, err := openstack.AuthenticatedClient(options) th.AssertNoErr(t, err) th.CheckEquals(t, "01234567890", client.TokenID) } func TestIdentityAdminV3Client(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, ` { "versions": { "values": [ { "status": "stable", "id": "v3.0", "links": [ { "href": "%s", "rel": "self" } ] }, { "status": "stable", "id": "v2.0", "links": [ { "href": "%s", "rel": "self" } ] } ] } } `, th.Endpoint()+"v3/", th.Endpoint()+"v2.0/") }) th.Mux.HandleFunc("/v3/auth/tokens", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("X-Subject-Token", ID) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, ` { "token": { "audit_ids": ["VcxU2JYqT8OzfUVvrjEITQ", "qNUTIJntTzO1-XUk5STybw"], "catalog": [ { "endpoints": [ { "id": "39dc322ce86c4111b4f06c2eeae0841b", "interface": "public", "region": "RegionOne", "url": "http://localhost:5000" }, { "id": "ec642f27474842e78bf059f6c48f4e99", "interface": "internal", "region": "RegionOne", "url": "http://localhost:5000" }, { "id": "c609fc430175452290b62a4242e8a7e8", "interface": "admin", "region": "RegionOne", "url": "http://localhost:35357" } ], "id": "4363ae44bdf34a3981fde3b823cb9aa2", "type": "identity", "name": "keystone" } ], "expires_at": "2013-02-27T18:30:59.999999Z", "is_domain": false, "issued_at": "2013-02-27T16:30:59.999999Z", "methods": [ "password" ], "project": { "domain": { "id": "1789d1", "name": "example.com" }, "id": "263fd9", "name": "project-x" }, "roles": [ { "id": "76e72a", "name": "admin" }, { "id": "f4f392", "name": "member" } ], "service_providers": [ { "auth_url":"https://example.com:5000/v3/OS-FEDERATION/identity_providers/acme/protocols/saml2/auth", "id": "sp1", "sp_url": "https://example.com:5000/Shibboleth.sso/SAML2/ECP" }, { "auth_url":"https://other.example.com:5000/v3/OS-FEDERATION/identity_providers/acme/protocols/saml2/auth", "id": "sp2", "sp_url": "https://other.example.com:5000/Shibboleth.sso/SAML2/ECP" } ], "user": { "domain": { "id": "1789d1", "name": "example.com" }, "id": "0ca8f6", "name": "Joe", "password_expires_at": "2016-11-06T15:32:17.000000" } } } `) }) options := gophercloud.AuthOptions{ Username: "me", Password: "secret", DomainID: "12345", IdentityEndpoint: th.Endpoint(), } pc, err := openstack.AuthenticatedClient(options) th.AssertNoErr(t, err) sc, err := openstack.NewIdentityV3(pc, gophercloud.EndpointOpts{ Availability: gophercloud.AvailabilityAdmin, }) th.AssertNoErr(t, err) th.CheckEquals(t, "http://localhost:35357/v3/", sc.Endpoint) } func testAuthenticatedClientFails(t *testing.T, endpoint string) { options := gophercloud.AuthOptions{ Username: "me", Password: "secret", DomainName: "default", TenantName: "project", IdentityEndpoint: endpoint, } _, err := openstack.AuthenticatedClient(options) if err == nil { t.Fatal("expected error but call succeeded") } } func TestAuthenticatedClientV3Fails(t *testing.T) { testAuthenticatedClientFails(t, "http://bad-address.example.com/v3") } func TestAuthenticatedClientV2Fails(t *testing.T) { testAuthenticatedClientFails(t, "http://bad-address.example.com/v2.0") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/testing/doc.go000066400000000000000000000000351335005366400303120ustar00rootroot00000000000000// openstack package testing endpoint_location_test.go000066400000000000000000000164221335005366400342440ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/testingpackage testing import ( "strings" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack" tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" th "github.com/gophercloud/gophercloud/testhelper" ) // Service catalog fixtures take too much vertical space! var catalog2 = tokens2.ServiceCatalog{ Entries: []tokens2.CatalogEntry{ tokens2.CatalogEntry{ Type: "same", Name: "same", Endpoints: []tokens2.Endpoint{ tokens2.Endpoint{ Region: "same", PublicURL: "https://public.correct.com/", InternalURL: "https://internal.correct.com/", AdminURL: "https://admin.correct.com/", }, tokens2.Endpoint{ Region: "different", PublicURL: "https://badregion.com/", }, }, }, tokens2.CatalogEntry{ Type: "same", Name: "different", Endpoints: []tokens2.Endpoint{ tokens2.Endpoint{ Region: "same", PublicURL: "https://badname.com/", }, tokens2.Endpoint{ Region: "different", PublicURL: "https://badname.com/+badregion", }, }, }, tokens2.CatalogEntry{ Type: "different", Name: "different", Endpoints: []tokens2.Endpoint{ tokens2.Endpoint{ Region: "same", PublicURL: "https://badtype.com/+badname", }, tokens2.Endpoint{ Region: "different", PublicURL: "https://badtype.com/+badregion+badname", }, }, }, }, } func TestV2EndpointExact(t *testing.T) { expectedURLs := map[gophercloud.Availability]string{ gophercloud.AvailabilityPublic: "https://public.correct.com/", gophercloud.AvailabilityAdmin: "https://admin.correct.com/", gophercloud.AvailabilityInternal: "https://internal.correct.com/", } for availability, expected := range expectedURLs { actual, err := openstack.V2EndpointURL(&catalog2, gophercloud.EndpointOpts{ Type: "same", Name: "same", Region: "same", Availability: availability, }) th.AssertNoErr(t, err) th.CheckEquals(t, expected, actual) } } func TestV2EndpointNone(t *testing.T) { _, actual := openstack.V2EndpointURL(&catalog2, gophercloud.EndpointOpts{ Type: "nope", Availability: gophercloud.AvailabilityPublic, }) expected := &gophercloud.ErrEndpointNotFound{} th.CheckEquals(t, expected.Error(), actual.Error()) } func TestV2EndpointMultiple(t *testing.T) { _, err := openstack.V2EndpointURL(&catalog2, gophercloud.EndpointOpts{ Type: "same", Region: "same", Availability: gophercloud.AvailabilityPublic, }) if !strings.HasPrefix(err.Error(), "Discovered 2 matching endpoints:") { t.Errorf("Received unexpected error: %v", err) } } func TestV2EndpointBadAvailability(t *testing.T) { _, err := openstack.V2EndpointURL(&catalog2, gophercloud.EndpointOpts{ Type: "same", Name: "same", Region: "same", Availability: "wat", }) th.CheckEquals(t, "Unexpected availability in endpoint query: wat", err.Error()) } var catalog3 = tokens3.ServiceCatalog{ Entries: []tokens3.CatalogEntry{ tokens3.CatalogEntry{ Type: "same", Name: "same", Endpoints: []tokens3.Endpoint{ tokens3.Endpoint{ ID: "1", Region: "same", Interface: "public", URL: "https://public.correct.com/", }, tokens3.Endpoint{ ID: "2", Region: "same", Interface: "admin", URL: "https://admin.correct.com/", }, tokens3.Endpoint{ ID: "3", Region: "same", Interface: "internal", URL: "https://internal.correct.com/", }, tokens3.Endpoint{ ID: "4", Region: "different", Interface: "public", URL: "https://badregion.com/", }, }, }, tokens3.CatalogEntry{ Type: "same", Name: "different", Endpoints: []tokens3.Endpoint{ tokens3.Endpoint{ ID: "5", Region: "same", Interface: "public", URL: "https://badname.com/", }, tokens3.Endpoint{ ID: "6", Region: "different", Interface: "public", URL: "https://badname.com/+badregion", }, }, }, tokens3.CatalogEntry{ Type: "different", Name: "different", Endpoints: []tokens3.Endpoint{ tokens3.Endpoint{ ID: "7", Region: "same", Interface: "public", URL: "https://badtype.com/+badname", }, tokens3.Endpoint{ ID: "8", Region: "different", Interface: "public", URL: "https://badtype.com/+badregion+badname", }, }, }, tokens3.CatalogEntry{ Type: "someother", Name: "someother", Endpoints: []tokens3.Endpoint{ tokens3.Endpoint{ ID: "1", Region: "someother", Interface: "public", URL: "https://public.correct.com/", }, tokens3.Endpoint{ ID: "2", RegionID: "someother", Interface: "admin", URL: "https://admin.correct.com/", }, tokens3.Endpoint{ ID: "3", RegionID: "someother", Interface: "internal", URL: "https://internal.correct.com/", }, }, }, }, } func TestV3EndpointExact(t *testing.T) { expectedURLs := map[gophercloud.Availability]string{ gophercloud.AvailabilityPublic: "https://public.correct.com/", gophercloud.AvailabilityAdmin: "https://admin.correct.com/", gophercloud.AvailabilityInternal: "https://internal.correct.com/", } for availability, expected := range expectedURLs { actual, err := openstack.V3EndpointURL(&catalog3, gophercloud.EndpointOpts{ Type: "same", Name: "same", Region: "same", Availability: availability, }) th.AssertNoErr(t, err) th.CheckEquals(t, expected, actual) } } func TestV3EndpointNone(t *testing.T) { _, actual := openstack.V3EndpointURL(&catalog3, gophercloud.EndpointOpts{ Type: "nope", Availability: gophercloud.AvailabilityPublic, }) expected := &gophercloud.ErrEndpointNotFound{} th.CheckEquals(t, expected.Error(), actual.Error()) } func TestV3EndpointMultiple(t *testing.T) { _, err := openstack.V3EndpointURL(&catalog3, gophercloud.EndpointOpts{ Type: "same", Region: "same", Availability: gophercloud.AvailabilityPublic, }) if !strings.HasPrefix(err.Error(), "Discovered 2 matching endpoints:") { t.Errorf("Received unexpected error: %v", err) } } func TestV3EndpointBadAvailability(t *testing.T) { _, err := openstack.V3EndpointURL(&catalog3, gophercloud.EndpointOpts{ Type: "same", Name: "same", Region: "same", Availability: "wat", }) th.CheckEquals(t, "Unexpected availability in endpoint query: wat", err.Error()) } func TestV3EndpointWithRegionID(t *testing.T) { expectedURLs := map[gophercloud.Availability]string{ gophercloud.AvailabilityPublic: "https://public.correct.com/", gophercloud.AvailabilityAdmin: "https://admin.correct.com/", gophercloud.AvailabilityInternal: "https://internal.correct.com/", } for availability, expected := range expectedURLs { actual, err := openstack.V3EndpointURL(&catalog3, gophercloud.EndpointOpts{ Type: "someother", Name: "someother", Region: "someother", Availability: availability, }) th.AssertNoErr(t, err) th.CheckEquals(t, expected, actual) } } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/utils/000077500000000000000000000000001335005366400267035ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/utils/base_endpoint.go000066400000000000000000000010111335005366400320350ustar00rootroot00000000000000package utils import ( "net/url" "regexp" "strings" ) // BaseEndpoint will return a URL without the /vX.Y // portion of the URL. func BaseEndpoint(endpoint string) (string, error) { u, err := url.Parse(endpoint) if err != nil { return "", err } u.RawQuery, u.Fragment = "", "" path := u.Path versionRe := regexp.MustCompile("v[0-9.]+/?") if version := versionRe.FindString(path); version != "" { versionIndex := strings.Index(path, version) u.Path = path[:versionIndex] } return u.String(), nil } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/utils/choose_version.go000066400000000000000000000054161335005366400322650ustar00rootroot00000000000000package utils import ( "fmt" "strings" "github.com/gophercloud/gophercloud" ) // Version is a supported API version, corresponding to a vN package within the appropriate service. type Version struct { ID string Suffix string Priority int } var goodStatus = map[string]bool{ "current": true, "supported": true, "stable": true, } // ChooseVersion queries the base endpoint of an API to choose the most recent non-experimental alternative from a service's // published versions. // It returns the highest-Priority Version among the alternatives that are provided, as well as its corresponding endpoint. func ChooseVersion(client *gophercloud.ProviderClient, recognized []*Version) (*Version, string, error) { type linkResp struct { Href string `json:"href"` Rel string `json:"rel"` } type valueResp struct { ID string `json:"id"` Status string `json:"status"` Links []linkResp `json:"links"` } type versionsResp struct { Values []valueResp `json:"values"` } type response struct { Versions versionsResp `json:"versions"` } normalize := func(endpoint string) string { if !strings.HasSuffix(endpoint, "/") { return endpoint + "/" } return endpoint } identityEndpoint := normalize(client.IdentityEndpoint) // If a full endpoint is specified, check version suffixes for a match first. for _, v := range recognized { if strings.HasSuffix(identityEndpoint, v.Suffix) { return v, identityEndpoint, nil } } var resp response _, err := client.Request("GET", client.IdentityBase, &gophercloud.RequestOpts{ JSONResponse: &resp, OkCodes: []int{200, 300}, }) if err != nil { return nil, "", err } var highest *Version var endpoint string for _, value := range resp.Versions.Values { href := "" for _, link := range value.Links { if link.Rel == "self" { href = normalize(link.Href) } } for _, version := range recognized { if strings.Contains(value.ID, version.ID) { // Prefer a version that exactly matches the provided endpoint. if href == identityEndpoint { if href == "" { return nil, "", fmt.Errorf("Endpoint missing in version %s response from %s", value.ID, client.IdentityBase) } return version, href, nil } // Otherwise, find the highest-priority version with a whitelisted status. if goodStatus[strings.ToLower(value.Status)] { if highest == nil || version.Priority > highest.Priority { highest = version endpoint = href } } } } } if highest == nil { return nil, "", fmt.Errorf("No supported version available from endpoint %s", client.IdentityBase) } if endpoint == "" { return nil, "", fmt.Errorf("Endpoint missing in version %s response from %s", highest.ID, client.IdentityBase) } return highest, endpoint, nil } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/utils/testing/000077500000000000000000000000001335005366400303605ustar00rootroot00000000000000base_endpoint_test.go000066400000000000000000000042311335005366400345010ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/utils/testingpackage testing import ( "testing" "github.com/gophercloud/gophercloud/openstack/utils" th "github.com/gophercloud/gophercloud/testhelper" ) type endpointTestCases struct { Endpoint string BaseEndpoint string } func TestBaseEndpoint(t *testing.T) { tests := []endpointTestCases{ { Endpoint: "http://example.com:5000/v3", BaseEndpoint: "http://example.com:5000/", }, { Endpoint: "http://example.com:5000/v3.6", BaseEndpoint: "http://example.com:5000/", }, { Endpoint: "http://example.com:5000/v2.0", BaseEndpoint: "http://example.com:5000/", }, { Endpoint: "http://example.com:5000/", BaseEndpoint: "http://example.com:5000/", }, { Endpoint: "http://example.com:5000", BaseEndpoint: "http://example.com:5000", }, { Endpoint: "http://example.com/identity/v3", BaseEndpoint: "http://example.com/identity/", }, { Endpoint: "http://example.com/identity/v3.6", BaseEndpoint: "http://example.com/identity/", }, { Endpoint: "http://example.com/identity/v2.0", BaseEndpoint: "http://example.com/identity/", }, { Endpoint: "http://example.com/identity/v2.0/projects", BaseEndpoint: "http://example.com/identity/", }, { Endpoint: "http://example.com/v2.0/projects", BaseEndpoint: "http://example.com/", }, { Endpoint: "http://example.com/identity/", BaseEndpoint: "http://example.com/identity/", }, { Endpoint: "http://dev.example.com:5000/v3", BaseEndpoint: "http://dev.example.com:5000/", }, { Endpoint: "http://dev.example.com:5000/v3.6", BaseEndpoint: "http://dev.example.com:5000/", }, { Endpoint: "http://dev.example.com/identity/", BaseEndpoint: "http://dev.example.com/identity/", }, { Endpoint: "http://dev.example.com/identity/v2.0/projects", BaseEndpoint: "http://dev.example.com/identity/", }, { Endpoint: "http://dev.example.com/identity/v3.6", BaseEndpoint: "http://dev.example.com/identity/", }, } for _, test := range tests { actual, err := utils.BaseEndpoint(test.Endpoint) th.AssertNoErr(t, err) th.AssertEquals(t, test.BaseEndpoint, actual) } } choose_version_test.go000066400000000000000000000056731335005366400347270ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/utils/testingpackage testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/utils" "github.com/gophercloud/gophercloud/testhelper" ) func setupVersionHandler() { testhelper.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, ` { "versions": { "values": [ { "status": "stable", "id": "v3.0", "links": [ { "href": "%s/v3.0", "rel": "self" } ] }, { "status": "stable", "id": "v2.0", "links": [ { "href": "%s/v2.0", "rel": "self" } ] } ] } } `, testhelper.Server.URL, testhelper.Server.URL) }) } func TestChooseVersion(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() setupVersionHandler() v2 := &utils.Version{ID: "v2.0", Priority: 2, Suffix: "blarg"} v3 := &utils.Version{ID: "v3.0", Priority: 3, Suffix: "hargl"} c := &gophercloud.ProviderClient{ IdentityBase: testhelper.Endpoint(), IdentityEndpoint: "", } v, endpoint, err := utils.ChooseVersion(c, []*utils.Version{v2, v3}) if err != nil { t.Fatalf("Unexpected error from ChooseVersion: %v", err) } if v != v3 { t.Errorf("Expected %#v to win, but %#v did instead", v3, v) } expected := testhelper.Endpoint() + "v3.0/" if endpoint != expected { t.Errorf("Expected endpoint [%s], but was [%s] instead", expected, endpoint) } } func TestChooseVersionOpinionatedLink(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() setupVersionHandler() v2 := &utils.Version{ID: "v2.0", Priority: 2, Suffix: "nope"} v3 := &utils.Version{ID: "v3.0", Priority: 3, Suffix: "northis"} c := &gophercloud.ProviderClient{ IdentityBase: testhelper.Endpoint(), IdentityEndpoint: testhelper.Endpoint() + "v2.0/", } v, endpoint, err := utils.ChooseVersion(c, []*utils.Version{v2, v3}) if err != nil { t.Fatalf("Unexpected error from ChooseVersion: %v", err) } if v != v2 { t.Errorf("Expected %#v to win, but %#v did instead", v2, v) } expected := testhelper.Endpoint() + "v2.0/" if endpoint != expected { t.Errorf("Expected endpoint [%s], but was [%s] instead", expected, endpoint) } } func TestChooseVersionFromSuffix(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() v2 := &utils.Version{ID: "v2.0", Priority: 2, Suffix: "/v2.0/"} v3 := &utils.Version{ID: "v3.0", Priority: 3, Suffix: "/v3.0/"} c := &gophercloud.ProviderClient{ IdentityBase: testhelper.Endpoint(), IdentityEndpoint: testhelper.Endpoint() + "v2.0/", } v, endpoint, err := utils.ChooseVersion(c, []*utils.Version{v2, v3}) if err != nil { t.Fatalf("Unexpected error from ChooseVersion: %v", err) } if v != v2 { t.Errorf("Expected %#v to win, but %#v did instead", v2, v) } expected := testhelper.Endpoint() + "v2.0/" if endpoint != expected { t.Errorf("Expected endpoint [%s], but was [%s] instead", expected, endpoint) } } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/utils/testing/doc.go000066400000000000000000000000301335005366400314450ustar00rootroot00000000000000//utils package testing golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/000077500000000000000000000000001335005366400274155ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/000077500000000000000000000000001335005366400277445ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/crontriggers/000077500000000000000000000000001335005366400324545ustar00rootroot00000000000000doc.go000066400000000000000000000021521335005366400334710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/crontriggers/* Package crontriggers provides interaction with the cron triggers API in the OpenStack Mistral service. Cron trigger is an object that allows to run Mistral workflows according to a time pattern (Unix crontab patterns format). Once a trigger is created it will run a specified workflow according to its properties: pattern, first_execution_time and remaining_executions. Example to list cron triggers listOpts := crontriggers.ListOpts{ WorkflowID: "w1", } allPages, err := crontriggers.List(mistralClient, listOpts).AllPages() if err != nil { panic(err) } allCrontriggers, err := crontriggers.ExtractCronTriggers(allPages) if err != nil { panic(err) } for _, ex := range allCrontriggers { fmt.Printf("%+v\n", ex) } Example to create a cron trigger createOpts := &crontriggers.CreateOpts{ WorkflowID: "w1", WorkflowParams: map[string]interface{}{ "msg": "hello", }, WorkflowInput: map[string]interface{}{ "msg": "world", }, Name: "trigger", } crontrigger, err := crontriggers.Create(mistralClient, opts).Extract() if err != nil { panic(err) } */ package crontriggers requests.go000066400000000000000000000040031335005366400345740ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/crontriggerspackage crontriggers import ( "time" "github.com/gophercloud/gophercloud" ) // CreateOptsBuilder allows extension to add additional parameters to the Create request. type CreateOptsBuilder interface { ToCronTriggerCreateMap() (map[string]interface{}, error) } // CreateOpts specifies parameters used to create a cron trigger. type CreateOpts struct { // Name is the cron trigger name. Name string `json:"name"` // Pattern is a Unix crontab patterns format to execute the workflow. Pattern string `json:"pattern"` // RemainingExecutions sets the number of executions for the trigger. RemainingExecutions int `json:"remaining_executions,omitempty"` // WorkflowID is the unique id of the workflow. WorkflowID string `json:"workflow_id,omitempty" or:"WorkflowName"` // WorkflowName is the name of the workflow. // It is recommended to refer to workflow by the WorkflowID parameter instead of WorkflowName. WorkflowName string `json:"workflow_name,omitempty" or:"WorkflowID"` // WorkflowParams defines workflow type specific parameters. WorkflowParams map[string]interface{} `json:"workflow_params,omitempty"` // WorkflowInput defines workflow input values. WorkflowInput map[string]interface{} `json:"workflow_input,omitempty"` // FirstExecutionTime defines the first execution time of the trigger. FirstExecutionTime *time.Time `json:"-"` } // ToCronTriggerCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToCronTriggerCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } if opts.FirstExecutionTime != nil { b["first_execution_time"] = opts.FirstExecutionTime.Format("2006-01-02 15:04") } return b, nil } // Create requests the creation of a new cron trigger. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToCronTriggerCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, nil) return } results.go000066400000000000000000000060051335005366400344260ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/crontriggerspackage crontriggers import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" ) type commonResult struct { gophercloud.Result } // CreateResult is the response of a Post operations. Call its Extract method to interpret it as a CronTrigger. type CreateResult struct { commonResult } // Extract helps to get a CronTrigger struct from a Get or a Create function. func (r commonResult) Extract() (*CronTrigger, error) { var s CronTrigger err := r.ExtractInto(&s) return &s, err } // CronTrigger represents a workflow cron trigger on OpenStack mistral API. type CronTrigger struct { // ID is the cron trigger's unique ID. ID string `json:"id"` // Name is the name of the cron trigger. Name string `json:"name"` // Pattern is the cron-like style pattern to execute the workflow. // Example of value: "* * * * *" Pattern string `json:"pattern"` // ProjectID is the project id owner of the cron trigger. ProjectID string `json:"project_id"` // RemainingExecutions is the number of remaining executions of this trigger. RemainingExecutions int `json:"remaining_executions"` // Scope is the scope of the trigger. // Values can be "private" or "public". Scope string `json:"scope"` // WorkflowID is the ID of the workflow linked to the trigger. WorkflowID string `json:"workflow_id"` // WorkflowName is the name of the workflow linked to the trigger. WorkflowName string `json:"workflow_name"` // WorkflowInput contains the workflow input values. WorkflowInput map[string]interface{} `json:"-"` // WorkflowParams contains workflow type specific parameters. WorkflowParams map[string]interface{} `json:"-"` // CreatedAt contains the cron trigger creation date. CreatedAt time.Time `json:"-"` // FirstExecutionTime is the date of the first execution of the trigger. FirstExecutionTime *time.Time `json:"-"` // NextExecutionTime is the date of the next execution of the trigger. NextExecutionTime *time.Time `json:"-"` } // UnmarshalJSON implements unmarshalling custom types func (r *CronTrigger) UnmarshalJSON(b []byte) error { type tmp CronTrigger var s struct { tmp CreatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"created_at"` FirstExecutionTime *gophercloud.JSONRFC3339ZNoTNoZ `json:"first_execution_time"` NextExecutionTime *gophercloud.JSONRFC3339ZNoTNoZ `json:"next_execution_time"` WorkflowInput string `json:"workflow_input"` WorkflowParams string `json:"workflow_params"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = CronTrigger(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) if s.FirstExecutionTime != nil { t := time.Time(*s.FirstExecutionTime) r.FirstExecutionTime = &t } if s.NextExecutionTime != nil { t := time.Time(*s.NextExecutionTime) r.NextExecutionTime = &t } if err := json.Unmarshal([]byte(s.WorkflowInput), &r.WorkflowInput); err != nil { return err } if err := json.Unmarshal([]byte(s.WorkflowParams), &r.WorkflowParams); err != nil { return err } return nil } testing/000077500000000000000000000000001335005366400340525ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/crontriggersrequests_test.go000066400000000000000000000043731335005366400373220ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/crontriggers/testingpackage testing import ( "fmt" "net/http" "reflect" "testing" "time" "github.com/gophercloud/gophercloud/openstack/workflow/v2/crontriggers" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreateCronTrigger(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/cron_triggers", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusCreated) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ` { "created_at": "1970-01-01 00:00:00", "id": "1", "name": "trigger", "pattern": "* * * * *", "project_id": "p1", "remaining_executions": 42, "scope": "private", "updated_at": "1970-01-01 00:00:00", "first_execution_time": "1970-01-01 00:00:00", "next_execution_time": "1970-01-01 00:00:00", "workflow_id": "w1", "workflow_input": "{\"msg\": \"hello\"}", "workflow_name": "my_wf", "workflow_params": "{\"msg\": \"world\"}" } `) }) firstExecution := time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) opts := &crontriggers.CreateOpts{ WorkflowID: "w1", Name: "trigger", FirstExecutionTime: &firstExecution, WorkflowParams: map[string]interface{}{ "msg": "world", }, WorkflowInput: map[string]interface{}{ "msg": "hello", }, } actual, err := crontriggers.Create(fake.ServiceClient(), opts).Extract() if err != nil { t.Fatalf("Unable to create cron trigger: %v", err) } expected := &crontriggers.CronTrigger{ ID: "1", Name: "trigger", Pattern: "* * * * *", ProjectID: "p1", RemainingExecutions: 42, Scope: "private", WorkflowID: "w1", WorkflowName: "my_wf", WorkflowParams: map[string]interface{}{ "msg": "world", }, WorkflowInput: map[string]interface{}{ "msg": "hello", }, CreatedAt: time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC), FirstExecutionTime: &firstExecution, NextExecutionTime: &firstExecution, } if !reflect.DeepEqual(expected, actual) { t.Errorf("Expected %#v, but was %#v", expected, actual) } } urls.go000066400000000000000000000002531335005366400337110ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/crontriggerspackage crontriggers import "github.com/gophercloud/gophercloud" func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("cron_triggers") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/executions/000077500000000000000000000000001335005366400321325ustar00rootroot00000000000000doc.go000066400000000000000000000017311335005366400331510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/executions/* Package executions provides interaction with the execution API in the OpenStack Mistral service. An execution is a particular execution of a specific workflow. Each execution contains all information about workflow itself, about execution process, state, input and output data. Example to list executions listOpts := executions.ListOpts{ WorkflowID: "w1", } allPages, err := executions.List(mistralClient, listOpts).AllPages() if err != nil { panic(err) } allExecutions, err := executions.ExtractExecutions(allPages) if err != nil { panic(err) } for _, ex := range allExecutions { fmt.Printf("%+v\n", ex) } Example to create an execution createOpts := &executions.CreateOpts{ WorkflowID: "6656c143-a009-4bcb-9814-cc100a20bbfa", Input: map[string]interface{}{ "msg": "Hello", }, Description: "this is a description", } execution, err := executions.Create(mistralClient, opts).Extract() if err != nil { panic(err) } */ package executions requests.go000066400000000000000000000034121335005366400342550ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/executionspackage executions import "github.com/gophercloud/gophercloud" // CreateOptsBuilder allows extension to add additional parameters to the Create request. type CreateOptsBuilder interface { ToExecutionCreateMap() (map[string]interface{}, error) } // CreateOpts specifies parameters used to create an execution. type CreateOpts struct { // ID is the unique ID of the execution. ID string `json:"id,omitempty"` // SourceExecutionID can be set to create an execution based on another existing execution. SourceExecutionID string `json:"source_execution_id,omitempty"` // WorkflowID is the unique id of the workflow. WorkflowID string `json:"workflow_id,omitempty" or:"WorkflowName"` // WorkflowName is the name identifier of the workflow. WorkflowName string `json:"workflow_name,omitempty" or:"WorkflowID"` // WorkflowNamespace is the namespace of the workflow. WorkflowNamespace string `json:"workflow_namespace,omitempty"` // Input is a JSON structure containing workflow input values, serialized as string. Input map[string]interface{} `json:"input,omitempty"` // Params define workflow type specific parameters. Params map[string]interface{} `json:"params,omitempty"` // Description is the description of the workflow execution. Description string `json:"description,omitempty"` } // ToExecutionCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToExecutionCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } // Create requests the creation of a new execution. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToExecutionCreateMap() if err != nil { r.Err = err return } _, r.Err = client.Post(createURL(client), b, &r.Body, nil) return } results.go000066400000000000000000000056511335005366400341120ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/executionspackage executions import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" ) type commonResult struct { gophercloud.Result } // CreateResult is the response of a Post operations. Call its Extract method to interpret it as an Execution. type CreateResult struct { commonResult } // Extract helps to get an Execution struct from a Get or a Create function. func (r commonResult) Extract() (*Execution, error) { var s Execution err := r.ExtractInto(&s) return &s, err } // Execution represents a workflow execution on OpenStack mistral API. type Execution struct { // ID is the execution's unique ID. ID string `json:"id"` // CreatedAt contains the execution creation date. CreatedAt time.Time `json:"-"` // UpdatedAt is the last update of the execution. UpdatedAt time.Time `json:"-"` // RootExecutionID is the parent execution ID. RootExecutionID *string `json:"root_execution_id"` // TaskExecutionID is the task execution ID. TaskExecutionID *string `json:"task_execution_id"` // Description is the description of the execution. Description string `json:"description"` // Input contains the workflow input values. Input map[string]interface{} `json:"-"` // Ouput contains the workflow output values. Output map[string]interface{} `json:"-"` // Params contains workflow type specific parameters. Params map[string]interface{} `json:"-"` // ProjectID is the project id owner of the execution. ProjectID string `json:"project_id"` // State is the current state of the execution. State can be one of: IDLE, RUNNING, SUCCESS, ERROR, PAUSED, CANCELLED. State string `json:"state"` // StateInfo contains an optional state information string. StateInfo *string `json:"state_info"` // WorkflowID is the ID of the workflow linked to the execution. WorkflowID string `json:"workflow_id"` // WorkflowName is the name of the workflow linked to the execution. WorkflowName string `json:"workflow_name"` // WorkflowNamespace is the namespace of the workflow linked to the execution. WorkflowNamespace string `json:"workflow_namespace"` } // UnmarshalJSON implements unmarshalling custom types func (r *Execution) UnmarshalJSON(b []byte) error { type tmp Execution var s struct { tmp CreatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"updated_at"` Input string `json:"input"` Output string `json:"output"` Params string `json:"params"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Execution(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) r.UpdatedAt = time.Time(s.UpdatedAt) if err := json.Unmarshal([]byte(s.Input), &r.Input); err != nil { return err } if err := json.Unmarshal([]byte(s.Output), &r.Output); err != nil { return err } if err := json.Unmarshal([]byte(s.Params), &r.Params); err != nil { return err } return nil } testing/000077500000000000000000000000001335005366400335305ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/executionsrequests_test.go000066400000000000000000000043251335005366400367750ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/executions/testingpackage testing import ( "fmt" "net/http" "reflect" "testing" "time" "github.com/gophercloud/gophercloud/openstack/workflow/v2/executions" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreateExecution(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/executions", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusCreated) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ` { "created_at": "2018-09-12 14:48:49", "description": "description", "id": "50bb59f1-eb77-4017-a77f-6d575b002667", "input": "{\"msg\": \"Hello\"}", "output": "{}", "params": "{\"namespace\": \"\", \"env\": {}}", "project_id": "778c0f25df0d492a9a868ee9e2fbb513", "root_execution_id": null, "state": "SUCCESS", "state_info": null, "task_execution_id": null, "updated_at": "2018-09-12 14:48:49", "workflow_id": "6656c143-a009-4bcb-9814-cc100a20bbfa", "workflow_name": "echo", "workflow_namespace": "" } `) }) opts := &executions.CreateOpts{ WorkflowID: "6656c143-a009-4bcb-9814-cc100a20bbfa", Input: map[string]interface{}{ "msg": "Hello", }, Description: "description", } actual, err := executions.Create(fake.ServiceClient(), opts).Extract() if err != nil { t.Fatalf("Unable to create execution: %v", err) } expected := &executions.Execution{ ID: "50bb59f1-eb77-4017-a77f-6d575b002667", Description: "description", Input: map[string]interface{}{ "msg": "Hello", }, Params: map[string]interface{}{ "namespace": "", "env": map[string]interface{}{}, }, Output: map[string]interface{}{}, ProjectID: "778c0f25df0d492a9a868ee9e2fbb513", State: "SUCCESS", WorkflowID: "6656c143-a009-4bcb-9814-cc100a20bbfa", WorkflowName: "echo", CreatedAt: time.Date(2018, time.September, 12, 14, 48, 49, 0, time.UTC), UpdatedAt: time.Date(2018, time.September, 12, 14, 48, 49, 0, time.UTC), } if !reflect.DeepEqual(expected, actual) { t.Errorf("Expected %#v, but was %#v", expected, actual) } } urls.go000066400000000000000000000002461335005366400333710ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/executionspackage executions import "github.com/gophercloud/gophercloud" func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("executions") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/workflows/000077500000000000000000000000001335005366400320015ustar00rootroot00000000000000doc.go000066400000000000000000000032711335005366400330210ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/workflows/* Package workflows provides interaction with the workflows API in the OpenStack Mistral service. Workflow represents a process that can be described in a various number of ways and that can do some job interesting to the end user. Each workflow consists of tasks (at least one) describing what exact steps should be made during workflow execution. Example to list workflows listOpts := workflows.ListOpts{ Namespace: "some-namespace", } allPages, err := workflows.List(mistralClient, listOpts).AllPages() if err != nil { panic(err) } allWorkflows, err := workflows.ExtractWorkflows(allPages) if err != nil { panic(err) } for _, workflow := range allWorkflows { fmt.Printf("%+v\n", workflow) } Example to get a workflow by its ID workflow, err := workflows.Get(mistralClient, "workflow-id").Extract() if err != nil { t.Fatalf("Unable to get workflow %s: %v", id, err) } fmt.Printf("%+v\n", workflow) Example to create a workflow workflowDefinition := `--- version: '2.0' create_vm: description: Simple workflow example type: direct input: - vm_name - image_ref - flavor_ref output: vm_id: <% $.vm_id %> tasks: create_server: action: nova.servers_create name=<% $.vm_name %> image=<% $.image_ref %> flavor=<% $.flavor_ref %> publish: vm_id: <% task(create_server).result.id %> on-success: - wait_for_instance wait_for_instance: action: nova.servers_find id=<% $.vm_id %> status='ACTIVE' retry: delay: 5 count: 15` createOpts := &workflows.CreateOpts{ Definition: strings.NewReader(workflowDefinition), Namespace: "some-namespace", } execution, err := workflows.Create(mistralClient, opts).Extract() if err != nil { panic(err) } */ package workflows requests.go000066400000000000000000000071711335005366400341320ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/workflowspackage workflows import ( "io" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extension to add additional parameters to the Create request. type CreateOptsBuilder interface { ToWorkflowCreateParams() (io.Reader, string, error) } // CreateOpts specifies parameters used to create a cron trigger. type CreateOpts struct { // Scope is the scope of the workflow. // Allowed values are "private" and "public". Scope string `q:"scope"` // Namespace will define the namespace of the workflow. Namespace string `q:"namespace"` // Definition is the workflow definition written in Mistral Workflow Language v2. Definition io.Reader } // ToWorkflowCreateParams constructs a request query string from CreateOpts. func (opts CreateOpts) ToWorkflowCreateParams() (io.Reader, string, error) { q, err := gophercloud.BuildQueryString(opts) return opts.Definition, q.String(), err } // Create requests the creation of a new execution. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { url := createURL(client) var b io.Reader if opts != nil { tmpB, query, err := opts.ToWorkflowCreateParams() if err != nil { r.Err = err return } url += query b = tmpB } _, r.Err = client.Post(url, nil, &r.Body, &gophercloud.RequestOpts{ RawBody: b, MoreHeaders: map[string]string{ "Content-Type": "text/plain", "Accept": "", // Drop default JSON Accept header }, }) return } // Delete deletes the specified execution. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } // Get retrieves details of a single execution. // Use Extract to convert its result into an Workflow. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } // ListOptsBuilder allows extension to add additional parameters to the List request. type ListOptsBuilder interface { ToWorkflowListQuery() (string, error) } // ListOpts filters the result returned by the List() function. type ListOpts struct { // Name allows to filter by workflow name. Name string `q:"name"` // Namespace allows to filter by workflow namespace. Namespace string `q:"namespace"` // Definition allows to filter by workflow definition. Definition string `q:"definition"` // Scope filters by the workflow's scope. // Values can be "private" or "public". Scope string `q:"scope"` // SortDir allows to select sort direction. // It can be "asc" or "desc" (default). SortDir string `q:"sort_dir"` // SortKey allows to sort by one of the cron trigger attributes. SortKey string `q:"sort_key"` // Marker and Limit control paging. // Marker instructs List where to start listing from. Marker string `q:"marker"` // Limit instructs List to refrain from sending excessively large lists of // cron triggers. Limit int `q:"limit"` } // ToWorkflowListQuery formats a ListOpts into a query string. func (opts ListOpts) ToWorkflowListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } // List performs a call to list cron triggers. // You may provide options to filter the results. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToWorkflowListQuery() if err != nil { return pagination.Pager{Err: err} } url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return WorkflowPage{pagination.LinkedPageBase{PageResult: r}} }) } results.go000066400000000000000000000065701335005366400337620ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/workflowspackage workflows import ( "encoding/json" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) // CreateResult is the response of a Post operations. Call its Extract method to interpret it as a list of Workflows. type CreateResult struct { gophercloud.Result } // DeleteResult is the result from a Delete operation. Call its ExtractErr method to determine the success of the call. type DeleteResult struct { gophercloud.ErrResult } // Extract helps to get created Workflow struct from a Create function. func (r CreateResult) Extract() ([]Workflow, error) { var s struct { Workflows []Workflow `json:"workflows"` } err := r.ExtractInto(&s) return s.Workflows, err } // GetResult is the response of Get operations. Call its Extract method to interpret it as a Workflow. type GetResult struct { gophercloud.Result } // Extract helps to get a Workflow struct from a Get function. func (r GetResult) Extract() (*Workflow, error) { var s Workflow err := r.ExtractInto(&s) return &s, err } // Workflow represents a workflow execution on OpenStack mistral API. type Workflow struct { // ID is the workflow's unique ID. ID string `json:"id"` // Definition is the workflow definition in Mistral v2 DSL. Definition string `json:"definition"` // Name is the name of the workflow. Name string `json:"name"` // Namespace is the namespace of the workflow. Namespace string `json:"namespace"` // Input represents the needed input to execute the workflow. // This parameter is a list of each input, comma separated. Input string `json:"input"` // ProjectID is the project id owner of the workflow. ProjectID string `json:"project_id"` // Scope is the scope of the workflow. // Values can be "private" or "public". Scope string `json:"scope"` // Tags is a list of tags associated to the workflow. Tags []string `json:"tags"` // CreatedAt is the creation date of the workflow. CreatedAt time.Time `json:"-"` // UpdatedAt is the last update date of the workflow. UpdatedAt *time.Time `json:"-"` } // UnmarshalJSON implements unmarshalling custom types func (r *Workflow) UnmarshalJSON(b []byte) error { type tmp Workflow var s struct { tmp CreatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"created_at"` UpdatedAt *gophercloud.JSONRFC3339ZNoTNoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = Workflow(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) if s.UpdatedAt != nil { t := time.Time(*s.UpdatedAt) r.UpdatedAt = &t } return nil } // WorkflowPage contains a single page of all workflows from a List call. type WorkflowPage struct { pagination.LinkedPageBase } // IsEmpty checks if an WorkflowPage contains any results. func (r WorkflowPage) IsEmpty() (bool, error) { exec, err := ExtractWorkflows(r) return len(exec) == 0, err } // NextPageURL finds the next page URL in a page in order to navigate to the next page of results. func (r WorkflowPage) NextPageURL() (string, error) { var s struct { Next string `json:"next"` } err := r.ExtractInto(&s) if err != nil { return "", err } return s.Next, nil } // ExtractWorkflows get the list of cron triggers from a page acquired from the List call. func ExtractWorkflows(r pagination.Page) ([]Workflow, error) { var s struct { Workflows []Workflow `json:"workflows"` } err := (r.(WorkflowPage)).ExtractInto(&s) return s.Workflows, err } testing/000077500000000000000000000000001335005366400333775ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/workflowsrequests_test.go000066400000000000000000000127361335005366400366510ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/workflows/testingpackage testing import ( "fmt" "net/http" "reflect" "strings" "testing" "time" "github.com/gophercloud/gophercloud/openstack/workflow/v2/workflows" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestCreateWorkflow(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() definition := `--- version: '2.0' simple_echo: description: Simple workflow example type: direct tasks: test: action: std.echo output="Hello World!"` th.Mux.HandleFunc("/workflows", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "text/plain") th.TestFormValues(t, r, map[string]string{ "namespace": "some-namespace", "scope": "private", }) th.TestBody(t, r, definition) w.WriteHeader(http.StatusCreated) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "workflows": [ { "created_at": "1970-01-01 00:00:00", "definition": "Workflow Definition in Mistral DSL v2", "id": "1", "input": "param1, param2", "name": "flow", "namespace": "some-namespace", "project_id": "p1", "scope": "private", "updated_at": "1970-01-01 00:00:00" } ] }`) }) opts := &workflows.CreateOpts{ Namespace: "some-namespace", Scope: "private", Definition: strings.NewReader(definition), } actual, err := workflows.Create(fake.ServiceClient(), opts).Extract() if err != nil { t.Fatalf("Unable to create workflow: %v", err) } updated := time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) expected := []workflows.Workflow{ workflows.Workflow{ ID: "1", Definition: "Workflow Definition in Mistral DSL v2", Name: "flow", Namespace: "some-namespace", Input: "param1, param2", ProjectID: "p1", Scope: "private", CreatedAt: time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC), UpdatedAt: &updated, }, } if !reflect.DeepEqual(expected, actual) { t.Errorf("Expected %#v, but was %#v", expected, actual) } } func TestDeleteWorkflow(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/workflows/1", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusAccepted) }) res := workflows.Delete(fake.ServiceClient(), "1") th.AssertNoErr(t, res.Err) } func TestGetWorkflow(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/workflows/1", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-token", fake.TokenID) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ` { "created_at": "1970-01-01 00:00:00", "definition": "Workflow Definition in Mistral DSL v2", "id": "1", "input": "param1, param2", "name": "flow", "namespace": "some-namespace", "project_id": "p1", "scope": "private", "updated_at": "1970-01-01 00:00:00" } `) }) actual, err := workflows.Get(fake.ServiceClient(), "1").Extract() if err != nil { t.Fatalf("Unable to get workflow: %v", err) } updated := time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) expected := &workflows.Workflow{ ID: "1", Definition: "Workflow Definition in Mistral DSL v2", Name: "flow", Namespace: "some-namespace", Input: "param1, param2", ProjectID: "p1", Scope: "private", CreatedAt: time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC), UpdatedAt: &updated, } if !reflect.DeepEqual(expected, actual) { t.Errorf("Expected %#v, but was %#v", expected, actual) } } func TestListWorkflows(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/workflows", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, `{ "next": "%s/workflows?marker=1", "workflows": [ { "created_at": "1970-01-01 00:00:00", "definition": "Workflow Definition in Mistral DSL v2", "id": "1", "input": "param1, param2", "name": "flow", "namespace": "some-namespace", "project_id": "p1", "scope": "private", "updated_at": "1970-01-01 00:00:00" } ] }`, th.Server.URL) case "1": fmt.Fprintf(w, `{ "workflows": [] }`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) pages := 0 // Get all workflows err := workflows.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := workflows.ExtractWorkflows(page) if err != nil { return false, err } updated := time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) expected := []workflows.Workflow{ {ID: "1", Definition: "Workflow Definition in Mistral DSL v2", Name: "flow", Namespace: "some-namespace", Input: "param1, param2", ProjectID: "p1", Scope: "private", CreatedAt: time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC), UpdatedAt: &updated}, } if !reflect.DeepEqual(expected, actual) { t.Errorf("Expected %#v, but was %#v", expected, actual) } return true, nil }) if err != nil { t.Fatal(err) } if pages != 1 { t.Errorf("Expected one page, got %d", pages) } } urls.go000066400000000000000000000007611335005366400332420ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/openstack/workflow/v2/workflowspackage workflows import ( "github.com/gophercloud/gophercloud" ) func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("workflows") } func deleteURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("workflows", id) } func getURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("workflows", id) } func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("workflows") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/pagination/000077500000000000000000000000001335005366400257055ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/pagination/http.go000066400000000000000000000030301335005366400272070ustar00rootroot00000000000000package pagination import ( "encoding/json" "io/ioutil" "net/http" "net/url" "strings" "github.com/gophercloud/gophercloud" ) // PageResult stores the HTTP response that returned the current page of results. type PageResult struct { gophercloud.Result url.URL } // PageResultFrom parses an HTTP response as JSON and returns a PageResult containing the // results, interpreting it as JSON if the content type indicates. func PageResultFrom(resp *http.Response) (PageResult, error) { var parsedBody interface{} defer resp.Body.Close() rawBody, err := ioutil.ReadAll(resp.Body) if err != nil { return PageResult{}, err } if strings.HasPrefix(resp.Header.Get("Content-Type"), "application/json") { err = json.Unmarshal(rawBody, &parsedBody) if err != nil { return PageResult{}, err } } else { parsedBody = rawBody } return PageResultFromParsed(resp, parsedBody), err } // PageResultFromParsed constructs a PageResult from an HTTP response that has already had its // body parsed as JSON (and closed). func PageResultFromParsed(resp *http.Response, body interface{}) PageResult { return PageResult{ Result: gophercloud.Result{ Body: body, Header: resp.Header, }, URL: *resp.Request.URL, } } // Request performs an HTTP request and extracts the http.Response from the result. func Request(client *gophercloud.ServiceClient, headers map[string]string, url string) (*http.Response, error) { return client.Get(url, nil, &gophercloud.RequestOpts{ MoreHeaders: headers, OkCodes: []int{200, 204, 300}, }) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/pagination/linked.go000066400000000000000000000047421335005366400275110ustar00rootroot00000000000000package pagination import ( "fmt" "reflect" "github.com/gophercloud/gophercloud" ) // LinkedPageBase may be embedded to implement a page that provides navigational "Next" and "Previous" links within its result. type LinkedPageBase struct { PageResult // LinkPath lists the keys that should be traversed within a response to arrive at the "next" pointer. // If any link along the path is missing, an empty URL will be returned. // If any link results in an unexpected value type, an error will be returned. // When left as "nil", []string{"links", "next"} will be used as a default. LinkPath []string } // NextPageURL extracts the pagination structure from a JSON response and returns the "next" link, if one is present. // It assumes that the links are available in a "links" element of the top-level response object. // If this is not the case, override NextPageURL on your result type. func (current LinkedPageBase) NextPageURL() (string, error) { var path []string var key string if current.LinkPath == nil { path = []string{"links", "next"} } else { path = current.LinkPath } submap, ok := current.Body.(map[string]interface{}) if !ok { err := gophercloud.ErrUnexpectedType{} err.Expected = "map[string]interface{}" err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) return "", err } for { key, path = path[0], path[1:len(path)] value, ok := submap[key] if !ok { return "", nil } if len(path) > 0 { submap, ok = value.(map[string]interface{}) if !ok { err := gophercloud.ErrUnexpectedType{} err.Expected = "map[string]interface{}" err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value)) return "", err } } else { if value == nil { // Actual null element. return "", nil } url, ok := value.(string) if !ok { err := gophercloud.ErrUnexpectedType{} err.Expected = "string" err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value)) return "", err } return url, nil } } } // IsEmpty satisifies the IsEmpty method of the Page interface func (current LinkedPageBase) IsEmpty() (bool, error) { if b, ok := current.Body.([]interface{}); ok { return len(b) == 0, nil } err := gophercloud.ErrUnexpectedType{} err.Expected = "[]interface{}" err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) return true, err } // GetBody returns the linked page's body. This method is needed to satisfy the // Page interface. func (current LinkedPageBase) GetBody() interface{} { return current.Body } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/pagination/marker.go000066400000000000000000000027301335005366400275170ustar00rootroot00000000000000package pagination import ( "fmt" "reflect" "github.com/gophercloud/gophercloud" ) // MarkerPage is a stricter Page interface that describes additional functionality required for use with NewMarkerPager. // For convenience, embed the MarkedPageBase struct. type MarkerPage interface { Page // LastMarker returns the last "marker" value on this page. LastMarker() (string, error) } // MarkerPageBase is a page in a collection that's paginated by "limit" and "marker" query parameters. type MarkerPageBase struct { PageResult // Owner is a reference to the embedding struct. Owner MarkerPage } // NextPageURL generates the URL for the page of results after this one. func (current MarkerPageBase) NextPageURL() (string, error) { currentURL := current.URL mark, err := current.Owner.LastMarker() if err != nil { return "", err } q := currentURL.Query() q.Set("marker", mark) currentURL.RawQuery = q.Encode() return currentURL.String(), nil } // IsEmpty satisifies the IsEmpty method of the Page interface func (current MarkerPageBase) IsEmpty() (bool, error) { if b, ok := current.Body.([]interface{}); ok { return len(b) == 0, nil } err := gophercloud.ErrUnexpectedType{} err.Expected = "[]interface{}" err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) return true, err } // GetBody returns the linked page's body. This method is needed to satisfy the // Page interface. func (current MarkerPageBase) GetBody() interface{} { return current.Body } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/pagination/pager.go000066400000000000000000000167171335005366400273460ustar00rootroot00000000000000package pagination import ( "errors" "fmt" "net/http" "reflect" "strings" "github.com/gophercloud/gophercloud" ) var ( // ErrPageNotAvailable is returned from a Pager when a next or previous page is requested, but does not exist. ErrPageNotAvailable = errors.New("The requested page does not exist.") ) // Page must be satisfied by the result type of any resource collection. // It allows clients to interact with the resource uniformly, regardless of whether or not or how it's paginated. // Generally, rather than implementing this interface directly, implementors should embed one of the concrete PageBase structs, // instead. // Depending on the pagination strategy of a particular resource, there may be an additional subinterface that the result type // will need to implement. type Page interface { // NextPageURL generates the URL for the page of data that follows this collection. // Return "" if no such page exists. NextPageURL() (string, error) // IsEmpty returns true if this Page has no items in it. IsEmpty() (bool, error) // GetBody returns the Page Body. This is used in the `AllPages` method. GetBody() interface{} } // Pager knows how to advance through a specific resource collection, one page at a time. type Pager struct { client *gophercloud.ServiceClient initialURL string createPage func(r PageResult) Page firstPage Page Err error // Headers supplies additional HTTP headers to populate on each paged request. Headers map[string]string } // NewPager constructs a manually-configured pager. // Supply the URL for the first page, a function that requests a specific page given a URL, and a function that counts a page. func NewPager(client *gophercloud.ServiceClient, initialURL string, createPage func(r PageResult) Page) Pager { return Pager{ client: client, initialURL: initialURL, createPage: createPage, } } // WithPageCreator returns a new Pager that substitutes a different page creation function. This is // useful for overriding List functions in delegation. func (p Pager) WithPageCreator(createPage func(r PageResult) Page) Pager { return Pager{ client: p.client, initialURL: p.initialURL, createPage: createPage, } } func (p Pager) fetchNextPage(url string) (Page, error) { resp, err := Request(p.client, p.Headers, url) if err != nil { return nil, err } remembered, err := PageResultFrom(resp) if err != nil { return nil, err } return p.createPage(remembered), nil } // EachPage iterates over each page returned by a Pager, yielding one at a time to a handler function. // Return "false" from the handler to prematurely stop iterating. func (p Pager) EachPage(handler func(Page) (bool, error)) error { if p.Err != nil { return p.Err } currentURL := p.initialURL for { var currentPage Page // if first page has already been fetched, no need to fetch it again if p.firstPage != nil { currentPage = p.firstPage p.firstPage = nil } else { var err error currentPage, err = p.fetchNextPage(currentURL) if err != nil { return err } } empty, err := currentPage.IsEmpty() if err != nil { return err } if empty { return nil } ok, err := handler(currentPage) if err != nil { return err } if !ok { return nil } currentURL, err = currentPage.NextPageURL() if err != nil { return err } if currentURL == "" { return nil } } } // AllPages returns all the pages from a `List` operation in a single page, // allowing the user to retrieve all the pages at once. func (p Pager) AllPages() (Page, error) { // pagesSlice holds all the pages until they get converted into as Page Body. var pagesSlice []interface{} // body will contain the final concatenated Page body. var body reflect.Value // Grab a first page to ascertain the page body type. firstPage, err := p.fetchNextPage(p.initialURL) if err != nil { return nil, err } // Store the page type so we can use reflection to create a new mega-page of // that type. pageType := reflect.TypeOf(firstPage) // if it's a single page, just return the firstPage (first page) if _, found := pageType.FieldByName("SinglePageBase"); found { return firstPage, nil } // store the first page to avoid getting it twice p.firstPage = firstPage // Switch on the page body type. Recognized types are `map[string]interface{}`, // `[]byte`, and `[]interface{}`. switch pb := firstPage.GetBody().(type) { case map[string]interface{}: // key is the map key for the page body if the body type is `map[string]interface{}`. var key string // Iterate over the pages to concatenate the bodies. err = p.EachPage(func(page Page) (bool, error) { b := page.GetBody().(map[string]interface{}) for k, v := range b { // If it's a linked page, we don't want the `links`, we want the other one. if !strings.HasSuffix(k, "links") { // check the field's type. we only want []interface{} (which is really []map[string]interface{}) switch vt := v.(type) { case []interface{}: key = k pagesSlice = append(pagesSlice, vt...) } } } return true, nil }) if err != nil { return nil, err } // Set body to value of type `map[string]interface{}` body = reflect.MakeMap(reflect.MapOf(reflect.TypeOf(key), reflect.TypeOf(pagesSlice))) body.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(pagesSlice)) case []byte: // Iterate over the pages to concatenate the bodies. err = p.EachPage(func(page Page) (bool, error) { b := page.GetBody().([]byte) pagesSlice = append(pagesSlice, b) // seperate pages with a comma pagesSlice = append(pagesSlice, []byte{10}) return true, nil }) if err != nil { return nil, err } if len(pagesSlice) > 0 { // Remove the trailing comma. pagesSlice = pagesSlice[:len(pagesSlice)-1] } var b []byte // Combine the slice of slices in to a single slice. for _, slice := range pagesSlice { b = append(b, slice.([]byte)...) } // Set body to value of type `bytes`. body = reflect.New(reflect.TypeOf(b)).Elem() body.SetBytes(b) case []interface{}: // Iterate over the pages to concatenate the bodies. err = p.EachPage(func(page Page) (bool, error) { b := page.GetBody().([]interface{}) pagesSlice = append(pagesSlice, b...) return true, nil }) if err != nil { return nil, err } // Set body to value of type `[]interface{}` body = reflect.MakeSlice(reflect.TypeOf(pagesSlice), len(pagesSlice), len(pagesSlice)) for i, s := range pagesSlice { body.Index(i).Set(reflect.ValueOf(s)) } default: err := gophercloud.ErrUnexpectedType{} err.Expected = "map[string]interface{}/[]byte/[]interface{}" err.Actual = fmt.Sprintf("%T", pb) return nil, err } // Each `Extract*` function is expecting a specific type of page coming back, // otherwise the type assertion in those functions will fail. pageType is needed // to create a type in this method that has the same type that the `Extract*` // function is expecting and set the Body of that object to the concatenated // pages. page := reflect.New(pageType) // Set the page body to be the concatenated pages. page.Elem().FieldByName("Body").Set(body) // Set any additional headers that were pass along. The `objectstorage` pacakge, // for example, passes a Content-Type header. h := make(http.Header) for k, v := range p.Headers { h.Add(k, v) } page.Elem().FieldByName("Header").Set(reflect.ValueOf(h)) // Type assert the page to a Page interface so that the type assertion in the // `Extract*` methods will work. return page.Elem().Interface().(Page), err } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/pagination/pkg.go000066400000000000000000000002261335005366400270150ustar00rootroot00000000000000/* Package pagination contains utilities and convenience structs that implement common pagination idioms within OpenStack APIs. */ package pagination golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/pagination/single.go000066400000000000000000000016261335005366400275220ustar00rootroot00000000000000package pagination import ( "fmt" "reflect" "github.com/gophercloud/gophercloud" ) // SinglePageBase may be embedded in a Page that contains all of the results from an operation at once. type SinglePageBase PageResult // NextPageURL always returns "" to indicate that there are no more pages to return. func (current SinglePageBase) NextPageURL() (string, error) { return "", nil } // IsEmpty satisifies the IsEmpty method of the Page interface func (current SinglePageBase) IsEmpty() (bool, error) { if b, ok := current.Body.([]interface{}); ok { return len(b) == 0, nil } err := gophercloud.ErrUnexpectedType{} err.Expected = "[]interface{}" err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) return true, err } // GetBody returns the single page's body. This method is needed to satisfy the // Page interface. func (current SinglePageBase) GetBody() interface{} { return current.Body } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/pagination/testing/000077500000000000000000000000001335005366400273625ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/pagination/testing/doc.go000066400000000000000000000000361335005366400304550ustar00rootroot00000000000000// pagination package testing golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/pagination/testing/linked_test.go000066400000000000000000000053461335005366400322260ustar00rootroot00000000000000package testing import ( "fmt" "net/http" "reflect" "testing" "github.com/gophercloud/gophercloud/pagination" "github.com/gophercloud/gophercloud/testhelper" ) // LinkedPager sample and test cases. type LinkedPageResult struct { pagination.LinkedPageBase } func (r LinkedPageResult) IsEmpty() (bool, error) { is, err := ExtractLinkedInts(r) return len(is) == 0, err } func ExtractLinkedInts(r pagination.Page) ([]int, error) { var s struct { Ints []int `json:"ints"` } err := (r.(LinkedPageResult)).ExtractInto(&s) return s.Ints, err } func createLinked(t *testing.T) pagination.Pager { testhelper.SetupHTTP() testhelper.Mux.HandleFunc("/page1", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "ints": [1, 2, 3], "links": { "next": "%s/page2" } }`, testhelper.Server.URL) }) testhelper.Mux.HandleFunc("/page2", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "ints": [4, 5, 6], "links": { "next": "%s/page3" } }`, testhelper.Server.URL) }) testhelper.Mux.HandleFunc("/page3", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "ints": [7, 8, 9], "links": { "next": null } }`) }) client := createClient() createPage := func(r pagination.PageResult) pagination.Page { return LinkedPageResult{pagination.LinkedPageBase{PageResult: r}} } return pagination.NewPager(client, testhelper.Server.URL+"/page1", createPage) } func TestEnumerateLinked(t *testing.T) { pager := createLinked(t) defer testhelper.TeardownHTTP() callCount := 0 err := pager.EachPage(func(page pagination.Page) (bool, error) { actual, err := ExtractLinkedInts(page) if err != nil { return false, err } t.Logf("Handler invoked with %v", actual) var expected []int switch callCount { case 0: expected = []int{1, 2, 3} case 1: expected = []int{4, 5, 6} case 2: expected = []int{7, 8, 9} default: t.Fatalf("Unexpected call count: %d", callCount) return false, nil } if !reflect.DeepEqual(expected, actual) { t.Errorf("Call %d: Expected %#v, but was %#v", callCount, expected, actual) } callCount++ return true, nil }) if err != nil { t.Errorf("Unexpected error for page iteration: %v", err) } if callCount != 3 { t.Errorf("Expected 3 calls, but was %d", callCount) } } func TestAllPagesLinked(t *testing.T) { pager := createLinked(t) defer testhelper.TeardownHTTP() page, err := pager.AllPages() testhelper.AssertNoErr(t, err) expected := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} actual, err := ExtractLinkedInts(page) testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, expected, actual) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/pagination/testing/marker_test.go000066400000000000000000000056651335005366400322450ustar00rootroot00000000000000package testing import ( "fmt" "net/http" "strings" "testing" "github.com/gophercloud/gophercloud/pagination" "github.com/gophercloud/gophercloud/testhelper" ) // MarkerPager sample and test cases. type MarkerPageResult struct { pagination.MarkerPageBase } func (r MarkerPageResult) IsEmpty() (bool, error) { results, err := ExtractMarkerStrings(r) if err != nil { return true, err } return len(results) == 0, err } func (r MarkerPageResult) LastMarker() (string, error) { results, err := ExtractMarkerStrings(r) if err != nil { return "", err } if len(results) == 0 { return "", nil } return results[len(results)-1], nil } func createMarkerPaged(t *testing.T) pagination.Pager { testhelper.SetupHTTP() testhelper.Mux.HandleFunc("/page", func(w http.ResponseWriter, r *http.Request) { r.ParseForm() ms := r.Form["marker"] switch { case len(ms) == 0: fmt.Fprintf(w, "aaa\nbbb\nccc") case len(ms) == 1 && ms[0] == "ccc": fmt.Fprintf(w, "ddd\neee\nfff") case len(ms) == 1 && ms[0] == "fff": fmt.Fprintf(w, "ggg\nhhh\niii") case len(ms) == 1 && ms[0] == "iii": w.WriteHeader(http.StatusNoContent) default: t.Errorf("Request with unexpected marker: [%v]", ms) } }) client := createClient() createPage := func(r pagination.PageResult) pagination.Page { p := MarkerPageResult{pagination.MarkerPageBase{PageResult: r}} p.MarkerPageBase.Owner = p return p } return pagination.NewPager(client, testhelper.Server.URL+"/page", createPage) } func ExtractMarkerStrings(page pagination.Page) ([]string, error) { content := page.(MarkerPageResult).Body.([]uint8) parts := strings.Split(string(content), "\n") results := make([]string, 0, len(parts)) for _, part := range parts { if len(part) > 0 { results = append(results, part) } } return results, nil } func TestEnumerateMarker(t *testing.T) { pager := createMarkerPaged(t) defer testhelper.TeardownHTTP() callCount := 0 err := pager.EachPage(func(page pagination.Page) (bool, error) { actual, err := ExtractMarkerStrings(page) if err != nil { return false, err } t.Logf("Handler invoked with %v", actual) var expected []string switch callCount { case 0: expected = []string{"aaa", "bbb", "ccc"} case 1: expected = []string{"ddd", "eee", "fff"} case 2: expected = []string{"ggg", "hhh", "iii"} default: t.Fatalf("Unexpected call count: %d", callCount) return false, nil } testhelper.CheckDeepEquals(t, expected, actual) callCount++ return true, nil }) testhelper.AssertNoErr(t, err) testhelper.AssertEquals(t, callCount, 3) } func TestAllPagesMarker(t *testing.T) { pager := createMarkerPaged(t) defer testhelper.TeardownHTTP() page, err := pager.AllPages() testhelper.AssertNoErr(t, err) expected := []string{"aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii"} actual, err := ExtractMarkerStrings(page) testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, expected, actual) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/pagination/testing/pagination_test.go000066400000000000000000000004711335005366400331030ustar00rootroot00000000000000package testing import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/testhelper" ) func createClient() *gophercloud.ServiceClient { return &gophercloud.ServiceClient{ ProviderClient: &gophercloud.ProviderClient{TokenID: "abc123"}, Endpoint: testhelper.Endpoint(), } } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/pagination/testing/single_test.go000066400000000000000000000034621335005366400322360ustar00rootroot00000000000000package testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud/pagination" "github.com/gophercloud/gophercloud/testhelper" ) // SinglePage sample and test cases. type SinglePageResult struct { pagination.SinglePageBase } func (r SinglePageResult) IsEmpty() (bool, error) { is, err := ExtractSingleInts(r) if err != nil { return true, err } return len(is) == 0, nil } func ExtractSingleInts(r pagination.Page) ([]int, error) { var s struct { Ints []int `json:"ints"` } err := (r.(SinglePageResult)).ExtractInto(&s) return s.Ints, err } func setupSinglePaged() pagination.Pager { testhelper.SetupHTTP() client := createClient() testhelper.Mux.HandleFunc("/only", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "ints": [1, 2, 3] }`) }) createPage := func(r pagination.PageResult) pagination.Page { return SinglePageResult{pagination.SinglePageBase(r)} } return pagination.NewPager(client, testhelper.Server.URL+"/only", createPage) } func TestEnumerateSinglePaged(t *testing.T) { callCount := 0 pager := setupSinglePaged() defer testhelper.TeardownHTTP() err := pager.EachPage(func(page pagination.Page) (bool, error) { callCount++ expected := []int{1, 2, 3} actual, err := ExtractSingleInts(page) testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, expected, actual) return true, nil }) testhelper.CheckNoErr(t, err) testhelper.CheckEquals(t, 1, callCount) } func TestAllPagesSingle(t *testing.T) { pager := setupSinglePaged() defer testhelper.TeardownHTTP() page, err := pager.AllPages() testhelper.AssertNoErr(t, err) expected := []int{1, 2, 3} actual, err := ExtractSingleInts(page) testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, expected, actual) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/params.go000066400000000000000000000312431335005366400253710ustar00rootroot00000000000000package gophercloud import ( "encoding/json" "fmt" "net/url" "reflect" "strconv" "strings" "time" ) /* BuildRequestBody builds a map[string]interface from the given `struct`. If parent is not an empty string, the final map[string]interface returned will encapsulate the built one. For example: disk := 1 createOpts := flavors.CreateOpts{ ID: "1", Name: "m1.tiny", Disk: &disk, RAM: 512, VCPUs: 1, RxTxFactor: 1.0, } body, err := gophercloud.BuildRequestBody(createOpts, "flavor") The above example can be run as-is, however it is recommended to look at how BuildRequestBody is used within Gophercloud to more fully understand how it fits within the request process as a whole rather than use it directly as shown above. */ func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, error) { optsValue := reflect.ValueOf(opts) if optsValue.Kind() == reflect.Ptr { optsValue = optsValue.Elem() } optsType := reflect.TypeOf(opts) if optsType.Kind() == reflect.Ptr { optsType = optsType.Elem() } optsMap := make(map[string]interface{}) if optsValue.Kind() == reflect.Struct { //fmt.Printf("optsValue.Kind() is a reflect.Struct: %+v\n", optsValue.Kind()) for i := 0; i < optsValue.NumField(); i++ { v := optsValue.Field(i) f := optsType.Field(i) if f.Name != strings.Title(f.Name) { //fmt.Printf("Skipping field: %s...\n", f.Name) continue } //fmt.Printf("Starting on field: %s...\n", f.Name) zero := isZero(v) //fmt.Printf("v is zero?: %v\n", zero) // if the field has a required tag that's set to "true" if requiredTag := f.Tag.Get("required"); requiredTag == "true" { //fmt.Printf("Checking required field [%s]:\n\tv: %+v\n\tisZero:%v\n", f.Name, v.Interface(), zero) // if the field's value is zero, return a missing-argument error if zero { // if the field has a 'required' tag, it can't have a zero-value err := ErrMissingInput{} err.Argument = f.Name return nil, err } } if xorTag := f.Tag.Get("xor"); xorTag != "" { //fmt.Printf("Checking `xor` tag for field [%s] with value %+v:\n\txorTag: %s\n", f.Name, v, xorTag) xorField := optsValue.FieldByName(xorTag) var xorFieldIsZero bool if reflect.ValueOf(xorField.Interface()) == reflect.Zero(xorField.Type()) { xorFieldIsZero = true } else { if xorField.Kind() == reflect.Ptr { xorField = xorField.Elem() } xorFieldIsZero = isZero(xorField) } if !(zero != xorFieldIsZero) { err := ErrMissingInput{} err.Argument = fmt.Sprintf("%s/%s", f.Name, xorTag) err.Info = fmt.Sprintf("Exactly one of %s and %s must be provided", f.Name, xorTag) return nil, err } } if orTag := f.Tag.Get("or"); orTag != "" { //fmt.Printf("Checking `or` tag for field with:\n\tname: %+v\n\torTag:%s\n", f.Name, orTag) //fmt.Printf("field is zero?: %v\n", zero) if zero { orField := optsValue.FieldByName(orTag) var orFieldIsZero bool if reflect.ValueOf(orField.Interface()) == reflect.Zero(orField.Type()) { orFieldIsZero = true } else { if orField.Kind() == reflect.Ptr { orField = orField.Elem() } orFieldIsZero = isZero(orField) } if orFieldIsZero { err := ErrMissingInput{} err.Argument = fmt.Sprintf("%s/%s", f.Name, orTag) err.Info = fmt.Sprintf("At least one of %s and %s must be provided", f.Name, orTag) return nil, err } } } jsonTag := f.Tag.Get("json") if jsonTag == "-" { continue } if v.Kind() == reflect.Struct || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct) { if zero { //fmt.Printf("value before change: %+v\n", optsValue.Field(i)) if jsonTag != "" { jsonTagPieces := strings.Split(jsonTag, ",") if len(jsonTagPieces) > 1 && jsonTagPieces[1] == "omitempty" { if v.CanSet() { if !v.IsNil() { if v.Kind() == reflect.Ptr { v.Set(reflect.Zero(v.Type())) } } //fmt.Printf("value after change: %+v\n", optsValue.Field(i)) } } } continue } //fmt.Printf("Calling BuildRequestBody with:\n\tv: %+v\n\tf.Name:%s\n", v.Interface(), f.Name) _, err := BuildRequestBody(v.Interface(), f.Name) if err != nil { return nil, err } } } //fmt.Printf("opts: %+v \n", opts) b, err := json.Marshal(opts) if err != nil { return nil, err } //fmt.Printf("string(b): %s\n", string(b)) err = json.Unmarshal(b, &optsMap) if err != nil { return nil, err } //fmt.Printf("optsMap: %+v\n", optsMap) if parent != "" { optsMap = map[string]interface{}{parent: optsMap} } //fmt.Printf("optsMap after parent added: %+v\n", optsMap) return optsMap, nil } // Return an error if the underlying type of 'opts' isn't a struct. return nil, fmt.Errorf("Options type is not a struct.") } // EnabledState is a convenience type, mostly used in Create and Update // operations. Because the zero value of a bool is FALSE, we need to use a // pointer instead to indicate zero-ness. type EnabledState *bool // Convenience vars for EnabledState values. var ( iTrue = true iFalse = false Enabled EnabledState = &iTrue Disabled EnabledState = &iFalse ) // IPVersion is a type for the possible IP address versions. Valid instances // are IPv4 and IPv6 type IPVersion int const ( // IPv4 is used for IP version 4 addresses IPv4 IPVersion = 4 // IPv6 is used for IP version 6 addresses IPv6 IPVersion = 6 ) // IntToPointer is a function for converting integers into integer pointers. // This is useful when passing in options to operations. func IntToPointer(i int) *int { return &i } /* MaybeString is an internal function to be used by request methods in individual resource packages. It takes a string that might be a zero value and returns either a pointer to its address or nil. This is useful for allowing users to conveniently omit values from an options struct by leaving them zeroed, but still pass nil to the JSON serializer so they'll be omitted from the request body. */ func MaybeString(original string) *string { if original != "" { return &original } return nil } /* MaybeInt is an internal function to be used by request methods in individual resource packages. Like MaybeString, it accepts an int that may or may not be a zero value, and returns either a pointer to its address or nil. It's intended to hint that the JSON serializer should omit its field. */ func MaybeInt(original int) *int { if original != 0 { return &original } return nil } /* func isUnderlyingStructZero(v reflect.Value) bool { switch v.Kind() { case reflect.Ptr: return isUnderlyingStructZero(v.Elem()) default: return isZero(v) } } */ var t time.Time func isZero(v reflect.Value) bool { //fmt.Printf("\n\nchecking isZero for value: %+v\n", v) switch v.Kind() { case reflect.Ptr: if v.IsNil() { return true } return false case reflect.Func, reflect.Map, reflect.Slice: return v.IsNil() case reflect.Array: z := true for i := 0; i < v.Len(); i++ { z = z && isZero(v.Index(i)) } return z case reflect.Struct: if v.Type() == reflect.TypeOf(t) { if v.Interface().(time.Time).IsZero() { return true } return false } z := true for i := 0; i < v.NumField(); i++ { z = z && isZero(v.Field(i)) } return z } // Compare other types directly: z := reflect.Zero(v.Type()) //fmt.Printf("zero type for value: %+v\n\n\n", z) return v.Interface() == z.Interface() } /* BuildQueryString is an internal function to be used by request methods in individual resource packages. It accepts a tagged structure and expands it into a URL struct. Field names are converted into query parameters based on a "q" tag. For example: type struct Something { Bar string `q:"x_bar"` Baz int `q:"lorem_ipsum"` } instance := Something{ Bar: "AAA", Baz: "BBB", } will be converted into "?x_bar=AAA&lorem_ipsum=BBB". The struct's fields may be strings, integers, or boolean values. Fields left at their type's zero value will be omitted from the query. */ func BuildQueryString(opts interface{}) (*url.URL, error) { optsValue := reflect.ValueOf(opts) if optsValue.Kind() == reflect.Ptr { optsValue = optsValue.Elem() } optsType := reflect.TypeOf(opts) if optsType.Kind() == reflect.Ptr { optsType = optsType.Elem() } params := url.Values{} if optsValue.Kind() == reflect.Struct { for i := 0; i < optsValue.NumField(); i++ { v := optsValue.Field(i) f := optsType.Field(i) qTag := f.Tag.Get("q") // if the field has a 'q' tag, it goes in the query string if qTag != "" { tags := strings.Split(qTag, ",") // if the field is set, add it to the slice of query pieces if !isZero(v) { loop: switch v.Kind() { case reflect.Ptr: v = v.Elem() goto loop case reflect.String: params.Add(tags[0], v.String()) case reflect.Int: params.Add(tags[0], strconv.FormatInt(v.Int(), 10)) case reflect.Bool: params.Add(tags[0], strconv.FormatBool(v.Bool())) case reflect.Slice: switch v.Type().Elem() { case reflect.TypeOf(0): for i := 0; i < v.Len(); i++ { params.Add(tags[0], strconv.FormatInt(v.Index(i).Int(), 10)) } default: for i := 0; i < v.Len(); i++ { params.Add(tags[0], v.Index(i).String()) } } case reflect.Map: if v.Type().Key().Kind() == reflect.String && v.Type().Elem().Kind() == reflect.String { var s []string for _, k := range v.MapKeys() { value := v.MapIndex(k).String() s = append(s, fmt.Sprintf("'%s':'%s'", k.String(), value)) } params.Add(tags[0], fmt.Sprintf("{%s}", strings.Join(s, ", "))) } } } else { // if the field has a 'required' tag, it can't have a zero-value if requiredTag := f.Tag.Get("required"); requiredTag == "true" { return &url.URL{}, fmt.Errorf("Required query parameter [%s] not set.", f.Name) } } } } return &url.URL{RawQuery: params.Encode()}, nil } // Return an error if the underlying type of 'opts' isn't a struct. return nil, fmt.Errorf("Options type is not a struct.") } /* BuildHeaders is an internal function to be used by request methods in individual resource packages. It accepts an arbitrary tagged structure and produces a string map that's suitable for use as the HTTP headers of an outgoing request. Field names are mapped to header names based in "h" tags. type struct Something { Bar string `h:"x_bar"` Baz int `h:"lorem_ipsum"` } instance := Something{ Bar: "AAA", Baz: "BBB", } will be converted into: map[string]string{ "x_bar": "AAA", "lorem_ipsum": "BBB", } Untagged fields and fields left at their zero values are skipped. Integers, booleans and string values are supported. */ func BuildHeaders(opts interface{}) (map[string]string, error) { optsValue := reflect.ValueOf(opts) if optsValue.Kind() == reflect.Ptr { optsValue = optsValue.Elem() } optsType := reflect.TypeOf(opts) if optsType.Kind() == reflect.Ptr { optsType = optsType.Elem() } optsMap := make(map[string]string) if optsValue.Kind() == reflect.Struct { for i := 0; i < optsValue.NumField(); i++ { v := optsValue.Field(i) f := optsType.Field(i) hTag := f.Tag.Get("h") // if the field has a 'h' tag, it goes in the header if hTag != "" { tags := strings.Split(hTag, ",") // if the field is set, add it to the slice of query pieces if !isZero(v) { switch v.Kind() { case reflect.String: optsMap[tags[0]] = v.String() case reflect.Int: optsMap[tags[0]] = strconv.FormatInt(v.Int(), 10) case reflect.Bool: optsMap[tags[0]] = strconv.FormatBool(v.Bool()) } } else { // if the field has a 'required' tag, it can't have a zero-value if requiredTag := f.Tag.Get("required"); requiredTag == "true" { return optsMap, fmt.Errorf("Required header [%s] not set.", f.Name) } } } } return optsMap, nil } // Return an error if the underlying type of 'opts' isn't a struct. return optsMap, fmt.Errorf("Options type is not a struct.") } // IDSliceToQueryString takes a slice of elements and converts them into a query // string. For example, if name=foo and slice=[]int{20, 40, 60}, then the // result would be `?name=20&name=40&name=60' func IDSliceToQueryString(name string, ids []int) string { str := "" for k, v := range ids { if k == 0 { str += "?" } else { str += "&" } str += fmt.Sprintf("%s=%s", name, strconv.Itoa(v)) } return str } // IntWithinRange returns TRUE if an integer falls within a defined range, and // FALSE if not. func IntWithinRange(val, min, max int) bool { return val > min && val < max } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/provider_client.go000066400000000000000000000267301335005366400273030ustar00rootroot00000000000000package gophercloud import ( "bytes" "encoding/json" "io" "io/ioutil" "net/http" "strings" "sync" ) // DefaultUserAgent is the default User-Agent string set in the request header. const DefaultUserAgent = "gophercloud/2.0.0" // UserAgent represents a User-Agent header. type UserAgent struct { // prepend is the slice of User-Agent strings to prepend to DefaultUserAgent. // All the strings to prepend are accumulated and prepended in the Join method. prepend []string } // Prepend prepends a user-defined string to the default User-Agent string. Users // may pass in one or more strings to prepend. func (ua *UserAgent) Prepend(s ...string) { ua.prepend = append(s, ua.prepend...) } // Join concatenates all the user-defined User-Agend strings with the default // Gophercloud User-Agent string. func (ua *UserAgent) Join() string { uaSlice := append(ua.prepend, DefaultUserAgent) return strings.Join(uaSlice, " ") } // ProviderClient stores details that are required to interact with any // services within a specific provider's API. // // Generally, you acquire a ProviderClient by calling the NewClient method in // the appropriate provider's child package, providing whatever authentication // credentials are required. type ProviderClient struct { // IdentityBase is the base URL used for a particular provider's identity // service - it will be used when issuing authenticatation requests. It // should point to the root resource of the identity service, not a specific // identity version. IdentityBase string // IdentityEndpoint is the identity endpoint. This may be a specific version // of the identity service. If this is the case, this endpoint is used rather // than querying versions first. IdentityEndpoint string // TokenID is the ID of the most recently issued valid token. // NOTE: Aside from within a custom ReauthFunc, this field shouldn't be set by an application. // To safely read or write this value, call `Token` or `SetToken`, respectively TokenID string // EndpointLocator describes how this provider discovers the endpoints for // its constituent services. EndpointLocator EndpointLocator // HTTPClient allows users to interject arbitrary http, https, or other transit behaviors. HTTPClient http.Client // UserAgent represents the User-Agent header in the HTTP request. UserAgent UserAgent // ReauthFunc is the function used to re-authenticate the user if the request // fails with a 401 HTTP response code. This a needed because there may be multiple // authentication functions for different Identity service versions. ReauthFunc func() error mut *sync.RWMutex reauthmut *reauthlock } type reauthlock struct { sync.RWMutex reauthing bool } // AuthenticatedHeaders returns a map of HTTP headers that are common for all // authenticated service requests. func (client *ProviderClient) AuthenticatedHeaders() (m map[string]string) { if client.reauthmut != nil { client.reauthmut.RLock() if client.reauthmut.reauthing { client.reauthmut.RUnlock() return } client.reauthmut.RUnlock() } t := client.Token() if t == "" { return } return map[string]string{"X-Auth-Token": t} } // UseTokenLock creates a mutex that is used to allow safe concurrent access to the auth token. // If the application's ProviderClient is not used concurrently, this doesn't need to be called. func (client *ProviderClient) UseTokenLock() { client.mut = new(sync.RWMutex) client.reauthmut = new(reauthlock) } // Token safely reads the value of the auth token from the ProviderClient. Applications should // call this method to access the token instead of the TokenID field func (client *ProviderClient) Token() string { if client.mut != nil { client.mut.RLock() defer client.mut.RUnlock() } return client.TokenID } // SetToken safely sets the value of the auth token in the ProviderClient. Applications may // use this method in a custom ReauthFunc func (client *ProviderClient) SetToken(t string) { if client.mut != nil { client.mut.Lock() defer client.mut.Unlock() } client.TokenID = t } //Reauthenticate calls client.ReauthFunc in a thread-safe way. If this is //called because of a 401 response, the caller may pass the previous token. In //this case, the reauthentication can be skipped if another thread has already //reauthenticated in the meantime. If no previous token is known, an empty //string should be passed instead to force unconditional reauthentication. func (client *ProviderClient) Reauthenticate(previousToken string) (err error) { if client.ReauthFunc == nil { return nil } if client.mut == nil { return client.ReauthFunc() } client.mut.Lock() defer client.mut.Unlock() client.reauthmut.Lock() client.reauthmut.reauthing = true client.reauthmut.Unlock() if previousToken == "" || client.TokenID == previousToken { err = client.ReauthFunc() } client.reauthmut.Lock() client.reauthmut.reauthing = false client.reauthmut.Unlock() return } // RequestOpts customizes the behavior of the provider.Request() method. type RequestOpts struct { // JSONBody, if provided, will be encoded as JSON and used as the body of the HTTP request. The // content type of the request will default to "application/json" unless overridden by MoreHeaders. // It's an error to specify both a JSONBody and a RawBody. JSONBody interface{} // RawBody contains an io.Reader that will be consumed by the request directly. No content-type // will be set unless one is provided explicitly by MoreHeaders. RawBody io.Reader // JSONResponse, if provided, will be populated with the contents of the response body parsed as // JSON. JSONResponse interface{} // OkCodes contains a list of numeric HTTP status codes that should be interpreted as success. If // the response has a different code, an error will be returned. OkCodes []int // MoreHeaders specifies additional HTTP headers to be provide on the request. If a header is // provided with a blank value (""), that header will be *omitted* instead: use this to suppress // the default Accept header or an inferred Content-Type, for example. MoreHeaders map[string]string // ErrorContext specifies the resource error type to return if an error is encountered. // This lets resources override default error messages based on the response status code. ErrorContext error } var applicationJSON = "application/json" // Request performs an HTTP request using the ProviderClient's current HTTPClient. An authentication // header will automatically be provided. func (client *ProviderClient) Request(method, url string, options *RequestOpts) (*http.Response, error) { var body io.Reader var contentType *string // Derive the content body by either encoding an arbitrary object as JSON, or by taking a provided // io.ReadSeeker as-is. Default the content-type to application/json. if options.JSONBody != nil { if options.RawBody != nil { panic("Please provide only one of JSONBody or RawBody to gophercloud.Request().") } rendered, err := json.Marshal(options.JSONBody) if err != nil { return nil, err } body = bytes.NewReader(rendered) contentType = &applicationJSON } if options.RawBody != nil { body = options.RawBody } // Construct the http.Request. req, err := http.NewRequest(method, url, body) if err != nil { return nil, err } // Populate the request headers. Apply options.MoreHeaders last, to give the caller the chance to // modify or omit any header. if contentType != nil { req.Header.Set("Content-Type", *contentType) } req.Header.Set("Accept", applicationJSON) // Set the User-Agent header req.Header.Set("User-Agent", client.UserAgent.Join()) if options.MoreHeaders != nil { for k, v := range options.MoreHeaders { if v != "" { req.Header.Set(k, v) } else { req.Header.Del(k) } } } // get latest token from client for k, v := range client.AuthenticatedHeaders() { req.Header.Set(k, v) } // Set connection parameter to close the connection immediately when we've got the response req.Close = true prereqtok := req.Header.Get("X-Auth-Token") // Issue the request. resp, err := client.HTTPClient.Do(req) if err != nil { return nil, err } // Allow default OkCodes if none explicitly set if options.OkCodes == nil { options.OkCodes = defaultOkCodes(method) } // Validate the HTTP response status. var ok bool for _, code := range options.OkCodes { if resp.StatusCode == code { ok = true break } } if !ok { body, _ := ioutil.ReadAll(resp.Body) resp.Body.Close() respErr := ErrUnexpectedResponseCode{ URL: url, Method: method, Expected: options.OkCodes, Actual: resp.StatusCode, Body: body, } errType := options.ErrorContext switch resp.StatusCode { case http.StatusBadRequest: err = ErrDefault400{respErr} if error400er, ok := errType.(Err400er); ok { err = error400er.Error400(respErr) } case http.StatusUnauthorized: if client.ReauthFunc != nil { err = client.Reauthenticate(prereqtok) if err != nil { e := &ErrUnableToReauthenticate{} e.ErrOriginal = respErr return nil, e } if options.RawBody != nil { if seeker, ok := options.RawBody.(io.Seeker); ok { seeker.Seek(0, 0) } } // make a new call to request with a nil reauth func in order to avoid infinite loop reauthFunc := client.ReauthFunc client.ReauthFunc = nil resp, err = client.Request(method, url, options) client.ReauthFunc = reauthFunc if err != nil { switch err.(type) { case *ErrUnexpectedResponseCode: e := &ErrErrorAfterReauthentication{} e.ErrOriginal = err.(*ErrUnexpectedResponseCode) return nil, e default: e := &ErrErrorAfterReauthentication{} e.ErrOriginal = err return nil, e } } return resp, nil } err = ErrDefault401{respErr} if error401er, ok := errType.(Err401er); ok { err = error401er.Error401(respErr) } case http.StatusForbidden: err = ErrDefault403{respErr} if error403er, ok := errType.(Err403er); ok { err = error403er.Error403(respErr) } case http.StatusNotFound: err = ErrDefault404{respErr} if error404er, ok := errType.(Err404er); ok { err = error404er.Error404(respErr) } case http.StatusMethodNotAllowed: err = ErrDefault405{respErr} if error405er, ok := errType.(Err405er); ok { err = error405er.Error405(respErr) } case http.StatusRequestTimeout: err = ErrDefault408{respErr} if error408er, ok := errType.(Err408er); ok { err = error408er.Error408(respErr) } case 429: err = ErrDefault429{respErr} if error429er, ok := errType.(Err429er); ok { err = error429er.Error429(respErr) } case http.StatusInternalServerError: err = ErrDefault500{respErr} if error500er, ok := errType.(Err500er); ok { err = error500er.Error500(respErr) } case http.StatusServiceUnavailable: err = ErrDefault503{respErr} if error503er, ok := errType.(Err503er); ok { err = error503er.Error503(respErr) } } if err == nil { err = respErr } return resp, err } // Parse the response body as JSON, if requested to do so. if options.JSONResponse != nil { defer resp.Body.Close() if err := json.NewDecoder(resp.Body).Decode(options.JSONResponse); err != nil { return nil, err } } return resp, nil } func defaultOkCodes(method string) []int { switch { case method == "GET": return []int{200} case method == "POST": return []int{201, 202} case method == "PUT": return []int{201, 202} case method == "PATCH": return []int{200, 202, 204} case method == "DELETE": return []int{202, 204} } return []int{} } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/results.go000066400000000000000000000256751335005366400256230ustar00rootroot00000000000000package gophercloud import ( "bytes" "encoding/json" "fmt" "io" "net/http" "reflect" "strconv" "time" ) /* Result is an internal type to be used by individual resource packages, but its methods will be available on a wide variety of user-facing embedding types. It acts as a base struct that other Result types, returned from request functions, can embed for convenience. All Results capture basic information from the HTTP transaction that was performed, including the response body, HTTP headers, and any errors that happened. Generally, each Result type will have an Extract method that can be used to further interpret the result's payload in a specific context. Extensions or providers can then provide additional extraction functions to pull out provider- or extension-specific information as well. */ type Result struct { // Body is the payload of the HTTP response from the server. In most cases, // this will be the deserialized JSON structure. Body interface{} // Header contains the HTTP header structure from the original response. Header http.Header // Err is an error that occurred during the operation. It's deferred until // extraction to make it easier to chain the Extract call. Err error } // ExtractInto allows users to provide an object into which `Extract` will extract // the `Result.Body`. This would be useful for OpenStack providers that have // different fields in the response object than OpenStack proper. func (r Result) ExtractInto(to interface{}) error { if r.Err != nil { return r.Err } if reader, ok := r.Body.(io.Reader); ok { if readCloser, ok := reader.(io.Closer); ok { defer readCloser.Close() } return json.NewDecoder(reader).Decode(to) } b, err := json.Marshal(r.Body) if err != nil { return err } err = json.Unmarshal(b, to) return err } func (r Result) extractIntoPtr(to interface{}, label string) error { if label == "" { return r.ExtractInto(&to) } var m map[string]interface{} err := r.ExtractInto(&m) if err != nil { return err } b, err := json.Marshal(m[label]) if err != nil { return err } toValue := reflect.ValueOf(to) if toValue.Kind() == reflect.Ptr { toValue = toValue.Elem() } switch toValue.Kind() { case reflect.Slice: typeOfV := toValue.Type().Elem() if typeOfV.Kind() == reflect.Struct { if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous { newSlice := reflect.MakeSlice(reflect.SliceOf(typeOfV), 0, 0) for _, v := range m[label].([]interface{}) { // For each iteration of the slice, we create a new struct. // This is to work around a bug where elements of a slice // are reused and not overwritten when the same copy of the // struct is used: // // https://github.com/golang/go/issues/21092 // https://github.com/golang/go/issues/24155 // https://play.golang.org/p/NHo3ywlPZli newType := reflect.New(typeOfV).Elem() b, err := json.Marshal(v) if err != nil { return err } // This is needed for structs with an UnmarshalJSON method. // Technically this is just unmarshalling the response into // a struct that is never used, but it's good enough to // trigger the UnmarshalJSON method. for i := 0; i < newType.NumField(); i++ { s := newType.Field(i).Addr().Interface() // Unmarshal is used rather than NewDecoder to also work // around the above-mentioned bug. err = json.Unmarshal(b, s) if err != nil { return err } } newSlice = reflect.Append(newSlice, newType) } // "to" should now be properly modeled to receive the // JSON response body and unmarshal into all the correct // fields of the struct or composed extension struct // at the end of this method. toValue.Set(newSlice) } } case reflect.Struct: typeOfV := toValue.Type() if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous { for i := 0; i < toValue.NumField(); i++ { toField := toValue.Field(i) if toField.Kind() == reflect.Struct { s := toField.Addr().Interface() err = json.NewDecoder(bytes.NewReader(b)).Decode(s) if err != nil { return err } } } } } err = json.Unmarshal(b, &to) return err } // ExtractIntoStructPtr will unmarshal the Result (r) into the provided // interface{} (to). // // NOTE: For internal use only // // `to` must be a pointer to an underlying struct type // // If provided, `label` will be filtered out of the response // body prior to `r` being unmarshalled into `to`. func (r Result) ExtractIntoStructPtr(to interface{}, label string) error { if r.Err != nil { return r.Err } t := reflect.TypeOf(to) if k := t.Kind(); k != reflect.Ptr { return fmt.Errorf("Expected pointer, got %v", k) } switch t.Elem().Kind() { case reflect.Struct: return r.extractIntoPtr(to, label) default: return fmt.Errorf("Expected pointer to struct, got: %v", t) } } // ExtractIntoSlicePtr will unmarshal the Result (r) into the provided // interface{} (to). // // NOTE: For internal use only // // `to` must be a pointer to an underlying slice type // // If provided, `label` will be filtered out of the response // body prior to `r` being unmarshalled into `to`. func (r Result) ExtractIntoSlicePtr(to interface{}, label string) error { if r.Err != nil { return r.Err } t := reflect.TypeOf(to) if k := t.Kind(); k != reflect.Ptr { return fmt.Errorf("Expected pointer, got %v", k) } switch t.Elem().Kind() { case reflect.Slice: return r.extractIntoPtr(to, label) default: return fmt.Errorf("Expected pointer to slice, got: %v", t) } } // PrettyPrintJSON creates a string containing the full response body as // pretty-printed JSON. It's useful for capturing test fixtures and for // debugging extraction bugs. If you include its output in an issue related to // a buggy extraction function, we will all love you forever. func (r Result) PrettyPrintJSON() string { pretty, err := json.MarshalIndent(r.Body, "", " ") if err != nil { panic(err.Error()) } return string(pretty) } // ErrResult is an internal type to be used by individual resource packages, but // its methods will be available on a wide variety of user-facing embedding // types. // // It represents results that only contain a potential error and // nothing else. Usually, if the operation executed successfully, the Err field // will be nil; otherwise it will be stocked with a relevant error. Use the // ExtractErr method // to cleanly pull it out. type ErrResult struct { Result } // ExtractErr is a function that extracts error information, or nil, from a result. func (r ErrResult) ExtractErr() error { return r.Err } /* HeaderResult is an internal type to be used by individual resource packages, but its methods will be available on a wide variety of user-facing embedding types. It represents a result that only contains an error (possibly nil) and an http.Header. This is used, for example, by the objectstorage packages in openstack, because most of the operations don't return response bodies, but do have relevant information in headers. */ type HeaderResult struct { Result } // ExtractInto allows users to provide an object into which `Extract` will // extract the http.Header headers of the result. func (r HeaderResult) ExtractInto(to interface{}) error { if r.Err != nil { return r.Err } tmpHeaderMap := map[string]string{} for k, v := range r.Header { if len(v) > 0 { tmpHeaderMap[k] = v[0] } } b, err := json.Marshal(tmpHeaderMap) if err != nil { return err } err = json.Unmarshal(b, to) return err } // RFC3339Milli describes a common time format used by some API responses. const RFC3339Milli = "2006-01-02T15:04:05.999999Z" type JSONRFC3339Milli time.Time func (jt *JSONRFC3339Milli) UnmarshalJSON(data []byte) error { b := bytes.NewBuffer(data) dec := json.NewDecoder(b) var s string if err := dec.Decode(&s); err != nil { return err } t, err := time.Parse(RFC3339Milli, s) if err != nil { return err } *jt = JSONRFC3339Milli(t) return nil } const RFC3339MilliNoZ = "2006-01-02T15:04:05.999999" type JSONRFC3339MilliNoZ time.Time func (jt *JSONRFC3339MilliNoZ) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return err } if s == "" { return nil } t, err := time.Parse(RFC3339MilliNoZ, s) if err != nil { return err } *jt = JSONRFC3339MilliNoZ(t) return nil } type JSONRFC1123 time.Time func (jt *JSONRFC1123) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return err } if s == "" { return nil } t, err := time.Parse(time.RFC1123, s) if err != nil { return err } *jt = JSONRFC1123(t) return nil } type JSONUnix time.Time func (jt *JSONUnix) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return err } if s == "" { return nil } unix, err := strconv.ParseInt(s, 10, 64) if err != nil { return err } t = time.Unix(unix, 0) *jt = JSONUnix(t) return nil } // RFC3339NoZ is the time format used in Heat (Orchestration). const RFC3339NoZ = "2006-01-02T15:04:05" type JSONRFC3339NoZ time.Time func (jt *JSONRFC3339NoZ) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return err } if s == "" { return nil } t, err := time.Parse(RFC3339NoZ, s) if err != nil { return err } *jt = JSONRFC3339NoZ(t) return nil } // RFC3339ZNoT is the time format used in Zun (Containers Service). const RFC3339ZNoT = "2006-01-02 15:04:05-07:00" type JSONRFC3339ZNoT time.Time func (jt *JSONRFC3339ZNoT) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return err } if s == "" { return nil } t, err := time.Parse(RFC3339ZNoT, s) if err != nil { return err } *jt = JSONRFC3339ZNoT(t) return nil } // RFC3339ZNoTNoZ is another time format used in Zun (Containers Service). const RFC3339ZNoTNoZ = "2006-01-02 15:04:05" type JSONRFC3339ZNoTNoZ time.Time func (jt *JSONRFC3339ZNoTNoZ) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return err } if s == "" { return nil } t, err := time.Parse(RFC3339ZNoTNoZ, s) if err != nil { return err } *jt = JSONRFC3339ZNoTNoZ(t) return nil } /* Link is an internal type to be used in packages of collection resources that are paginated in a certain way. It's a response substructure common to many paginated collection results that is used to point to related pages. Usually, the one we care about is the one with Rel field set to "next". */ type Link struct { Href string `json:"href"` Rel string `json:"rel"` } /* ExtractNextURL is an internal function useful for packages of collection resources that are paginated in a certain way. It attempts to extract the "next" URL from slice of Link structs, or "" if no such URL is present. */ func ExtractNextURL(links []Link) (string, error) { var url string for _, l := range links { if l.Rel == "next" { url = l.Href } } if url == "" { return "", nil } return url, nil } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/script/000077500000000000000000000000001335005366400250605ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/script/acceptancetest000077500000000000000000000027051335005366400300000ustar00rootroot00000000000000#!/bin/bash # set -x source `dirname $0`/stackenv timeout="60m" failed= # Run the acceptance tests. # Check the error code after each suite, but do not exit early if a suite failed. # Identity v3 go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/identity/v3/ if [[ $? != 0 ]]; then failed=1 fi # Networking v2 go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/ if [[ $? != 0 ]]; then failed=1 fi # Block Storage v2 go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/blockstorage/v2/ if [[ $? != 0 ]]; then failed=1 fi # Block Storage v3 go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/blockstorage/v3/ if [[ $? != 0 ]]; then failed=1 fi # Compute v2 go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/compute/v2/ if [[ $? != 0 ]]; then failed=1 fi # Container v1 go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/container/v1/ if [[ $? != 0 ]]; then failed=1 fi # Image Service v2 go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/imageservice/v2/ if [[ $? != 0 ]]; then failed=1 fi # Orchestration v1 go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/orchestration/v1/ if [[ $? != 0 ]]; then failed=1 fi # If any of the test suites failed, exit 1 if [[ -n $failed ]]; then exit 1 fi exit 0 golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/script/bootstrap000077500000000000000000000011611335005366400270220ustar00rootroot00000000000000#!/bin/bash # # This script helps new contributors set up their local workstation for # gophercloud development and contributions. # Create the environment export GOPATH=$HOME/go/gophercloud mkdir -p $GOPATH # Download gophercloud into that environment go get github.com/gophercloud/gophercloud cd $GOPATH/src/github.com/gophercloud/gophercloud git checkout master # Write out the env.sh convenience file. cd $GOPATH cat <env.sh #!/bin/bash export GOPATH=$(pwd) export GOPHERCLOUD=$GOPATH/src/github.com/gophercloud/gophercloud EOF chmod a+x env.sh # Make changes immediately available as a convenience. . ./env.sh golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/script/cibuild000077500000000000000000000001161335005366400264170ustar00rootroot00000000000000#!/bin/bash # # Test script to be invoked by Travis. exec script/unittest -v golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/script/coverage000077500000000000000000000004521335005366400266020ustar00rootroot00000000000000#!/bin/bash set -e n=1 for testpkg in $(go list ./testing ./.../testing); do covpkg="${testpkg/"/testing"/}" go test -covermode count -coverprofile "testing_"$n.coverprofile -coverpkg $covpkg $testpkg 2>/dev/null n=$((n+1)) done gocovmerge `ls *.coverprofile` > cover.out rm *.coverprofile golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/script/format000077500000000000000000000017621335005366400263040ustar00rootroot00000000000000#!/usr/bin/env bash goimports="goimports" find_files() { find . -not \( \ \( \ -wholename './output' \ -o -wholename './_output' \ -o -wholename './_gopath' \ -o -wholename './release' \ -o -wholename './target' \ -o -wholename '*/third_party/*' \ -o -wholename '*/vendor/*' \ \) -prune \ \) -name '*.go' } ignore_files=( "./openstack/compute/v2/extensions/quotasets/testing/fixtures.go" "./openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go" ) bad_files=$(find_files | xargs ${goimports} -l) final_files=() for bad_file in $bad_files; do found= for ignore_file in "${ignore_files[@]}"; do [[ "${bad_file}" == "${ignore_file}" ]] && { found=1; break; } done [[ -n $found ]] || final_files+=("$bad_file") done if [[ "${#final_files[@]}" -gt 0 ]]; then diff=$(echo "${final_files[@]}" | xargs ${goimports} -d -e 2>&1) if [[ -n "${diff}" ]]; then echo "${diff}" exit 1 fi fi golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/script/stackenv000066400000000000000000000022241335005366400266210ustar00rootroot00000000000000# Prep the testing environment by creating the required testing resources and # environment variables. This env is for theopenlab CI jobs, you might need # to modify this according to your setup pushd /opt/stack/new/devstack source openrc admin admin openstack flavor create m1.acctest --id 99 --ram 512 --disk 5 --vcpu 1 --ephemeral 10 openstack flavor create m1.resize --id 98 --ram 512 --disk 6 --vcpu 1 --ephemeral 10 _NETWORK_ID=$(openstack network show private -c id -f value) _EXTGW_ID=$(openstack network show public -c id -f value) _IMAGE=$(openstack image list | grep -i cirros | head -n 1) _IMAGE_ID=$(echo $_IMAGE | awk -F\| '{print $2}' | tr -d ' ') _IMAGE_NAME=$(echo $_IMAGE | awk -F\| '{print $3}' | tr -d ' ') echo export OS_IMAGE_NAME="$_IMAGE_NAME" >> openrc echo export OS_IMAGE_ID="$_IMAGE_ID" >> openrc echo export OS_NETWORK_ID=$_NETWORK_ID >> openrc echo export OS_EXTGW_ID=$_EXTGW_ID >> openrc echo export OS_POOL_NAME="public" >> openrc echo export OS_FLAVOR_ID=99 >> openrc echo export OS_FLAVOR_ID_RESIZE=98 >> openrc echo export OS_SHARE_NETWORK_ID=foobar >> openrc echo export OS_DOMAIN_ID=default >> openrc source openrc admin admin popd golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/script/test000077500000000000000000000001261335005366400257640ustar00rootroot00000000000000#!/bin/bash # # Run all the tests. exec go test -tags 'acceptance fixtures' ./... $@ golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/script/unittest000077500000000000000000000001151335005366400266620ustar00rootroot00000000000000#!/bin/bash # # Run the unit tests. exec go test ./testing ./.../testing $@ golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/service_client.go000066400000000000000000000115751335005366400271120ustar00rootroot00000000000000package gophercloud import ( "io" "net/http" "strings" ) // ServiceClient stores details required to interact with a specific service API implemented by a provider. // Generally, you'll acquire these by calling the appropriate `New` method on a ProviderClient. type ServiceClient struct { // ProviderClient is a reference to the provider that implements this service. *ProviderClient // Endpoint is the base URL of the service's API, acquired from a service catalog. // It MUST end with a /. Endpoint string // ResourceBase is the base URL shared by the resources within a service's API. It should include // the API version and, like Endpoint, MUST end with a / if set. If not set, the Endpoint is used // as-is, instead. ResourceBase string // This is the service client type (e.g. compute, sharev2). // NOTE: FOR INTERNAL USE ONLY. DO NOT SET. GOPHERCLOUD WILL SET THIS. // It is only exported because it gets set in a different package. Type string // The microversion of the service to use. Set this to use a particular microversion. Microversion string // MoreHeaders allows users (or Gophercloud) to set service-wide headers on requests. Put another way, // values set in this field will be set on all the HTTP requests the service client sends. MoreHeaders map[string]string } // ResourceBaseURL returns the base URL of any resources used by this service. It MUST end with a /. func (client *ServiceClient) ResourceBaseURL() string { if client.ResourceBase != "" { return client.ResourceBase } return client.Endpoint } // ServiceURL constructs a URL for a resource belonging to this provider. func (client *ServiceClient) ServiceURL(parts ...string) string { return client.ResourceBaseURL() + strings.Join(parts, "/") } func (client *ServiceClient) initReqOpts(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) { if v, ok := (JSONBody).(io.Reader); ok { opts.RawBody = v } else if JSONBody != nil { opts.JSONBody = JSONBody } if JSONResponse != nil { opts.JSONResponse = JSONResponse } if opts.MoreHeaders == nil { opts.MoreHeaders = make(map[string]string) } if client.Microversion != "" { client.setMicroversionHeader(opts) } } // Get calls `Request` with the "GET" HTTP verb. func (client *ServiceClient) Get(url string, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(url, nil, JSONResponse, opts) return client.Request("GET", url, opts) } // Post calls `Request` with the "POST" HTTP verb. func (client *ServiceClient) Post(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(url, JSONBody, JSONResponse, opts) return client.Request("POST", url, opts) } // Put calls `Request` with the "PUT" HTTP verb. func (client *ServiceClient) Put(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(url, JSONBody, JSONResponse, opts) return client.Request("PUT", url, opts) } // Patch calls `Request` with the "PATCH" HTTP verb. func (client *ServiceClient) Patch(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(url, JSONBody, JSONResponse, opts) return client.Request("PATCH", url, opts) } // Delete calls `Request` with the "DELETE" HTTP verb. func (client *ServiceClient) Delete(url string, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(url, nil, nil, opts) return client.Request("DELETE", url, opts) } // Head calls `Request` with the "HEAD" HTTP verb. func (client *ServiceClient) Head(url string, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(url, nil, nil, opts) return client.Request("HEAD", url, opts) } func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) { switch client.Type { case "compute": opts.MoreHeaders["X-OpenStack-Nova-API-Version"] = client.Microversion case "sharev2": opts.MoreHeaders["X-OpenStack-Manila-API-Version"] = client.Microversion case "volume": opts.MoreHeaders["X-OpenStack-Volume-API-Version"] = client.Microversion } if client.Type != "" { opts.MoreHeaders["OpenStack-API-Version"] = client.Type + " " + client.Microversion } } // Request carries out the HTTP operation for the service client func (client *ServiceClient) Request(method, url string, options *RequestOpts) (*http.Response, error) { if len(client.MoreHeaders) > 0 { if options == nil { options = new(RequestOpts) } for k, v := range client.MoreHeaders { options.MoreHeaders[k] = v } } return client.ProviderClient.Request(method, url, options) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/testhelper/000077500000000000000000000000001335005366400257335ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/testhelper/client/000077500000000000000000000000001335005366400272115ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/testhelper/client/fake.go000066400000000000000000000007061335005366400304510ustar00rootroot00000000000000package client import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/testhelper" ) // Fake token to use. const TokenID = "cbc36478b0bd8e67e89469c7749d4127" // ServiceClient returns a generic service client for use in tests. func ServiceClient() *gophercloud.ServiceClient { return &gophercloud.ServiceClient{ ProviderClient: &gophercloud.ProviderClient{TokenID: TokenID}, Endpoint: testhelper.Endpoint(), } } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/testhelper/convenience.go000066400000000000000000000234631335005366400305660ustar00rootroot00000000000000package testhelper import ( "bytes" "encoding/json" "fmt" "path/filepath" "reflect" "runtime" "strings" "testing" ) const ( logBodyFmt = "\033[1;31m%s %s\033[0m" greenCode = "\033[0m\033[1;32m" yellowCode = "\033[0m\033[1;33m" resetCode = "\033[0m\033[1;31m" ) func prefix(depth int) string { _, file, line, _ := runtime.Caller(depth) return fmt.Sprintf("Failure in %s, line %d:", filepath.Base(file), line) } func green(str interface{}) string { return fmt.Sprintf("%s%#v%s", greenCode, str, resetCode) } func yellow(str interface{}) string { return fmt.Sprintf("%s%#v%s", yellowCode, str, resetCode) } func logFatal(t *testing.T, str string) { t.Fatalf(logBodyFmt, prefix(3), str) } func logError(t *testing.T, str string) { t.Errorf(logBodyFmt, prefix(3), str) } type diffLogger func([]string, interface{}, interface{}) type visit struct { a1 uintptr a2 uintptr typ reflect.Type } // Recursively visits the structures of "expected" and "actual". The diffLogger function will be // invoked with each different value encountered, including the reference path that was followed // to get there. func deepDiffEqual(expected, actual reflect.Value, visited map[visit]bool, path []string, logDifference diffLogger) { defer func() { // Fall back to the regular reflect.DeepEquals function. if r := recover(); r != nil { var e, a interface{} if expected.IsValid() { e = expected.Interface() } if actual.IsValid() { a = actual.Interface() } if !reflect.DeepEqual(e, a) { logDifference(path, e, a) } } }() if !expected.IsValid() && actual.IsValid() { logDifference(path, nil, actual.Interface()) return } if expected.IsValid() && !actual.IsValid() { logDifference(path, expected.Interface(), nil) return } if !expected.IsValid() && !actual.IsValid() { return } hard := func(k reflect.Kind) bool { switch k { case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct: return true } return false } if expected.CanAddr() && actual.CanAddr() && hard(expected.Kind()) { addr1 := expected.UnsafeAddr() addr2 := actual.UnsafeAddr() if addr1 > addr2 { addr1, addr2 = addr2, addr1 } if addr1 == addr2 { // References are identical. We can short-circuit return } typ := expected.Type() v := visit{addr1, addr2, typ} if visited[v] { // Already visited. return } // Remember this visit for later. visited[v] = true } switch expected.Kind() { case reflect.Array: for i := 0; i < expected.Len(); i++ { hop := append(path, fmt.Sprintf("[%d]", i)) deepDiffEqual(expected.Index(i), actual.Index(i), visited, hop, logDifference) } return case reflect.Slice: if expected.IsNil() != actual.IsNil() { logDifference(path, expected.Interface(), actual.Interface()) return } if expected.Len() == actual.Len() && expected.Pointer() == actual.Pointer() { return } for i := 0; i < expected.Len(); i++ { hop := append(path, fmt.Sprintf("[%d]", i)) deepDiffEqual(expected.Index(i), actual.Index(i), visited, hop, logDifference) } return case reflect.Interface: if expected.IsNil() != actual.IsNil() { logDifference(path, expected.Interface(), actual.Interface()) return } deepDiffEqual(expected.Elem(), actual.Elem(), visited, path, logDifference) return case reflect.Ptr: deepDiffEqual(expected.Elem(), actual.Elem(), visited, path, logDifference) return case reflect.Struct: for i, n := 0, expected.NumField(); i < n; i++ { field := expected.Type().Field(i) hop := append(path, "."+field.Name) deepDiffEqual(expected.Field(i), actual.Field(i), visited, hop, logDifference) } return case reflect.Map: if expected.IsNil() != actual.IsNil() { logDifference(path, expected.Interface(), actual.Interface()) return } if expected.Len() == actual.Len() && expected.Pointer() == actual.Pointer() { return } var keys []reflect.Value if expected.Len() >= actual.Len() { keys = expected.MapKeys() } else { keys = actual.MapKeys() } for _, k := range keys { expectedValue := expected.MapIndex(k) actualValue := actual.MapIndex(k) if !expectedValue.IsValid() { logDifference(path, nil, actual.Interface()) return } if !actualValue.IsValid() { logDifference(path, expected.Interface(), nil) return } hop := append(path, fmt.Sprintf("[%v]", k)) deepDiffEqual(expectedValue, actualValue, visited, hop, logDifference) } return case reflect.Func: if expected.IsNil() != actual.IsNil() { logDifference(path, expected.Interface(), actual.Interface()) } return default: if expected.Interface() != actual.Interface() { logDifference(path, expected.Interface(), actual.Interface()) } } } func deepDiff(expected, actual interface{}, logDifference diffLogger) { if expected == nil || actual == nil { logDifference([]string{}, expected, actual) return } expectedValue := reflect.ValueOf(expected) actualValue := reflect.ValueOf(actual) if expectedValue.Type() != actualValue.Type() { logDifference([]string{}, expected, actual) return } deepDiffEqual(expectedValue, actualValue, map[visit]bool{}, []string{}, logDifference) } // AssertEquals compares two arbitrary values and performs a comparison. If the // comparison fails, a fatal error is raised that will fail the test func AssertEquals(t *testing.T, expected, actual interface{}) { if expected != actual { logFatal(t, fmt.Sprintf("expected %s but got %s", green(expected), yellow(actual))) } } // CheckEquals is similar to AssertEquals, except with a non-fatal error func CheckEquals(t *testing.T, expected, actual interface{}) { if expected != actual { logError(t, fmt.Sprintf("expected %s but got %s", green(expected), yellow(actual))) } } // AssertDeepEquals - like Equals - performs a comparison - but on more complex // structures that requires deeper inspection func AssertDeepEquals(t *testing.T, expected, actual interface{}) { pre := prefix(2) differed := false deepDiff(expected, actual, func(path []string, expected, actual interface{}) { differed = true t.Errorf("\033[1;31m%sat %s expected %s, but got %s\033[0m", pre, strings.Join(path, ""), green(expected), yellow(actual)) }) if differed { logFatal(t, "The structures were different.") } } // CheckDeepEquals is similar to AssertDeepEquals, except with a non-fatal error func CheckDeepEquals(t *testing.T, expected, actual interface{}) { pre := prefix(2) deepDiff(expected, actual, func(path []string, expected, actual interface{}) { t.Errorf("\033[1;31m%s at %s expected %s, but got %s\033[0m", pre, strings.Join(path, ""), green(expected), yellow(actual)) }) } func isByteArrayEquals(t *testing.T, expectedBytes []byte, actualBytes []byte) bool { return bytes.Equal(expectedBytes, actualBytes) } // AssertByteArrayEquals a convenience function for checking whether two byte arrays are equal func AssertByteArrayEquals(t *testing.T, expectedBytes []byte, actualBytes []byte) { if !isByteArrayEquals(t, expectedBytes, actualBytes) { logFatal(t, "The bytes differed.") } } // CheckByteArrayEquals a convenience function for silent checking whether two byte arrays are equal func CheckByteArrayEquals(t *testing.T, expectedBytes []byte, actualBytes []byte) { if !isByteArrayEquals(t, expectedBytes, actualBytes) { logError(t, "The bytes differed.") } } // isJSONEquals is a utility function that implements JSON comparison for AssertJSONEquals and // CheckJSONEquals. func isJSONEquals(t *testing.T, expectedJSON string, actual interface{}) bool { var parsedExpected, parsedActual interface{} err := json.Unmarshal([]byte(expectedJSON), &parsedExpected) if err != nil { t.Errorf("Unable to parse expected value as JSON: %v", err) return false } jsonActual, err := json.Marshal(actual) AssertNoErr(t, err) err = json.Unmarshal(jsonActual, &parsedActual) AssertNoErr(t, err) if !reflect.DeepEqual(parsedExpected, parsedActual) { prettyExpected, err := json.MarshalIndent(parsedExpected, "", " ") if err != nil { t.Logf("Unable to pretty-print expected JSON: %v\n%s", err, expectedJSON) } else { // We can't use green() here because %#v prints prettyExpected as a byte array literal, which // is... unhelpful. Converting it to a string first leaves "\n" uninterpreted for some reason. t.Logf("Expected JSON:\n%s%s%s", greenCode, prettyExpected, resetCode) } prettyActual, err := json.MarshalIndent(actual, "", " ") if err != nil { t.Logf("Unable to pretty-print actual JSON: %v\n%#v", err, actual) } else { // We can't use yellow() for the same reason. t.Logf("Actual JSON:\n%s%s%s", yellowCode, prettyActual, resetCode) } return false } return true } // AssertJSONEquals serializes a value as JSON, parses an expected string as JSON, and ensures that // both are consistent. If they aren't, the expected and actual structures are pretty-printed and // shown for comparison. // // This is useful for comparing structures that are built as nested map[string]interface{} values, // which are a pain to construct as literals. func AssertJSONEquals(t *testing.T, expectedJSON string, actual interface{}) { if !isJSONEquals(t, expectedJSON, actual) { logFatal(t, "The generated JSON structure differed.") } } // CheckJSONEquals is similar to AssertJSONEquals, but nonfatal. func CheckJSONEquals(t *testing.T, expectedJSON string, actual interface{}) { if !isJSONEquals(t, expectedJSON, actual) { logError(t, "The generated JSON structure differed.") } } // AssertNoErr is a convenience function for checking whether an error value is // an actual error func AssertNoErr(t *testing.T, e error) { if e != nil { logFatal(t, fmt.Sprintf("unexpected error %s", yellow(e.Error()))) } } // CheckNoErr is similar to AssertNoErr, except with a non-fatal error func CheckNoErr(t *testing.T, e error) { if e != nil { logError(t, fmt.Sprintf("unexpected error %s", yellow(e.Error()))) } } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/testhelper/doc.go000066400000000000000000000001461335005366400270300ustar00rootroot00000000000000/* Package testhelper container methods that are useful for writing unit tests. */ package testhelper golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/testhelper/fixture/000077500000000000000000000000001335005366400274215ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/testhelper/fixture/helper.go000066400000000000000000000012321335005366400312250ustar00rootroot00000000000000package fixture import ( "fmt" "net/http" "testing" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func SetupHandler(t *testing.T, url, method, requestBody, responseBody string, status int) { th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, method) th.TestHeader(t, r, "X-Auth-Token", client.TokenID) if requestBody != "" { th.TestJSONRequest(t, r, requestBody) } if responseBody != "" { w.Header().Add("Content-Type", "application/json") } w.WriteHeader(status) if responseBody != "" { fmt.Fprintf(w, responseBody) } }) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/testhelper/http_responses.go000066400000000000000000000045431335005366400313500ustar00rootroot00000000000000package testhelper import ( "encoding/json" "io/ioutil" "net/http" "net/http/httptest" "net/url" "reflect" "testing" ) var ( // Mux is a multiplexer that can be used to register handlers. Mux *http.ServeMux // Server is an in-memory HTTP server for testing. Server *httptest.Server ) // SetupHTTP prepares the Mux and Server. func SetupHTTP() { Mux = http.NewServeMux() Server = httptest.NewServer(Mux) } // TeardownHTTP releases HTTP-related resources. func TeardownHTTP() { Server.Close() } // Endpoint returns a fake endpoint that will actually target the Mux. func Endpoint() string { return Server.URL + "/" } // TestFormValues ensures that all the URL parameters given to the http.Request are the same as values. func TestFormValues(t *testing.T, r *http.Request, values map[string]string) { want := url.Values{} for k, v := range values { want.Add(k, v) } r.ParseForm() if !reflect.DeepEqual(want, r.Form) { t.Errorf("Request parameters = %v, want %v", r.Form, want) } } // TestMethod checks that the Request has the expected method (e.g. GET, POST). func TestMethod(t *testing.T, r *http.Request, expected string) { if expected != r.Method { t.Errorf("Request method = %v, expected %v", r.Method, expected) } } // TestHeader checks that the header on the http.Request matches the expected value. func TestHeader(t *testing.T, r *http.Request, header string, expected string) { if actual := r.Header.Get(header); expected != actual { t.Errorf("Header %s = %s, expected %s", header, actual, expected) } } // TestBody verifies that the request body matches an expected body. func TestBody(t *testing.T, r *http.Request, expected string) { b, err := ioutil.ReadAll(r.Body) if err != nil { t.Errorf("Unable to read body: %v", err) } str := string(b) if expected != str { t.Errorf("Body = %s, expected %s", str, expected) } } // TestJSONRequest verifies that the JSON payload of a request matches an expected structure, without asserting things about // whitespace or ordering. func TestJSONRequest(t *testing.T, r *http.Request, expected string) { b, err := ioutil.ReadAll(r.Body) if err != nil { t.Errorf("Unable to read request body: %v", err) } var actualJSON interface{} err = json.Unmarshal(b, &actualJSON) if err != nil { t.Errorf("Unable to parse request body as JSON: %v", err) } CheckJSONEquals(t, expected, actualJSON) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/testing/000077500000000000000000000000001335005366400252315ustar00rootroot00000000000000golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/testing/doc.go000066400000000000000000000000371335005366400263250ustar00rootroot00000000000000// gophercloud package testing golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/testing/endpoint_search_test.go000066400000000000000000000012131335005366400317610ustar00rootroot00000000000000package testing import ( "testing" "github.com/gophercloud/gophercloud" th "github.com/gophercloud/gophercloud/testhelper" ) func TestApplyDefaultsToEndpointOpts(t *testing.T) { eo := gophercloud.EndpointOpts{Availability: gophercloud.AvailabilityPublic} eo.ApplyDefaults("compute") expected := gophercloud.EndpointOpts{Availability: gophercloud.AvailabilityPublic, Type: "compute"} th.CheckDeepEquals(t, expected, eo) eo = gophercloud.EndpointOpts{Type: "compute"} eo.ApplyDefaults("object-store") expected = gophercloud.EndpointOpts{Availability: gophercloud.AvailabilityPublic, Type: "compute"} th.CheckDeepEquals(t, expected, eo) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/testing/params_test.go000066400000000000000000000154161335005366400301110ustar00rootroot00000000000000package testing import ( "net/url" "reflect" "testing" "time" "github.com/gophercloud/gophercloud" th "github.com/gophercloud/gophercloud/testhelper" ) func TestMaybeString(t *testing.T) { testString := "" var expected *string actual := gophercloud.MaybeString(testString) th.CheckDeepEquals(t, expected, actual) testString = "carol" expected = &testString actual = gophercloud.MaybeString(testString) th.CheckDeepEquals(t, expected, actual) } func TestMaybeInt(t *testing.T) { testInt := 0 var expected *int actual := gophercloud.MaybeInt(testInt) th.CheckDeepEquals(t, expected, actual) testInt = 4 expected = &testInt actual = gophercloud.MaybeInt(testInt) th.CheckDeepEquals(t, expected, actual) } func TestBuildQueryString(t *testing.T) { type testVar string iFalse := false opts := struct { J int `q:"j"` R string `q:"r" required:"true"` C bool `q:"c"` S []string `q:"s"` TS []testVar `q:"ts"` TI []int `q:"ti"` F *bool `q:"f"` M map[string]string `q:"m"` }{ J: 2, R: "red", C: true, S: []string{"one", "two", "three"}, TS: []testVar{"a", "b"}, TI: []int{1, 2}, F: &iFalse, M: map[string]string{"k1": "success1"}, } expected := &url.URL{RawQuery: "c=true&f=false&j=2&m=%7B%27k1%27%3A%27success1%27%7D&r=red&s=one&s=two&s=three&ti=1&ti=2&ts=a&ts=b"} actual, err := gophercloud.BuildQueryString(&opts) if err != nil { t.Errorf("Error building query string: %v", err) } th.CheckDeepEquals(t, expected, actual) opts = struct { J int `q:"j"` R string `q:"r" required:"true"` C bool `q:"c"` S []string `q:"s"` TS []testVar `q:"ts"` TI []int `q:"ti"` F *bool `q:"f"` M map[string]string `q:"m"` }{ J: 2, C: true, } _, err = gophercloud.BuildQueryString(&opts) if err == nil { t.Errorf("Expected error: 'Required field not set'") } th.CheckDeepEquals(t, expected, actual) _, err = gophercloud.BuildQueryString(map[string]interface{}{"Number": 4}) if err == nil { t.Errorf("Expected error: 'Options type is not a struct'") } } func TestBuildHeaders(t *testing.T) { testStruct := struct { Accept string `h:"Accept"` Num int `h:"Number" required:"true"` Style bool `h:"Style"` }{ Accept: "application/json", Num: 4, Style: true, } expected := map[string]string{"Accept": "application/json", "Number": "4", "Style": "true"} actual, err := gophercloud.BuildHeaders(&testStruct) th.CheckNoErr(t, err) th.CheckDeepEquals(t, expected, actual) testStruct.Num = 0 _, err = gophercloud.BuildHeaders(&testStruct) if err == nil { t.Errorf("Expected error: 'Required header not set'") } _, err = gophercloud.BuildHeaders(map[string]interface{}{"Number": 4}) if err == nil { t.Errorf("Expected error: 'Options type is not a struct'") } } func TestQueriesAreEscaped(t *testing.T) { type foo struct { Name string `q:"something"` Shape string `q:"else"` } expected := &url.URL{RawQuery: "else=Triangl+e&something=blah%2B%3F%21%21foo"} actual, err := gophercloud.BuildQueryString(foo{Name: "blah+?!!foo", Shape: "Triangl e"}) th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, actual) } func TestBuildRequestBody(t *testing.T) { type PasswordCredentials struct { Username string `json:"username" required:"true"` Password string `json:"password" required:"true"` } type TokenCredentials struct { ID string `json:"id,omitempty" required:"true"` } type orFields struct { Filler int `json:"filler,omitempty"` F1 int `json:"f1,omitempty" or:"F2"` F2 int `json:"f2,omitempty" or:"F1"` } // AuthOptions wraps a gophercloud AuthOptions in order to adhere to the AuthOptionsBuilder // interface. type AuthOptions struct { PasswordCredentials *PasswordCredentials `json:"passwordCredentials,omitempty" xor:"TokenCredentials"` // The TenantID and TenantName fields are optional for the Identity V2 API. // Some providers allow you to specify a TenantName instead of the TenantId. // Some require both. Your provider's authentication policies will determine // how these fields influence authentication. TenantID string `json:"tenantId,omitempty"` TenantName string `json:"tenantName,omitempty"` // TokenCredentials allows users to authenticate (possibly as another user) with an // authentication token ID. TokenCredentials *TokenCredentials `json:"token,omitempty" xor:"PasswordCredentials"` OrFields *orFields `json:"or_fields,omitempty"` } var successCases = []struct { opts AuthOptions expected map[string]interface{} }{ { AuthOptions{ PasswordCredentials: &PasswordCredentials{ Username: "me", Password: "swordfish", }, }, map[string]interface{}{ "auth": map[string]interface{}{ "passwordCredentials": map[string]interface{}{ "password": "swordfish", "username": "me", }, }, }, }, { AuthOptions{ TokenCredentials: &TokenCredentials{ ID: "1234567", }, }, map[string]interface{}{ "auth": map[string]interface{}{ "token": map[string]interface{}{ "id": "1234567", }, }, }, }, } for _, successCase := range successCases { actual, err := gophercloud.BuildRequestBody(successCase.opts, "auth") th.AssertNoErr(t, err) th.AssertDeepEquals(t, successCase.expected, actual) } var failCases = []struct { opts AuthOptions expected error }{ { AuthOptions{ TenantID: "987654321", TenantName: "me", }, gophercloud.ErrMissingInput{}, }, { AuthOptions{ TokenCredentials: &TokenCredentials{ ID: "1234567", }, PasswordCredentials: &PasswordCredentials{ Username: "me", Password: "swordfish", }, }, gophercloud.ErrMissingInput{}, }, { AuthOptions{ PasswordCredentials: &PasswordCredentials{ Password: "swordfish", }, }, gophercloud.ErrMissingInput{}, }, { AuthOptions{ PasswordCredentials: &PasswordCredentials{ Username: "me", Password: "swordfish", }, OrFields: &orFields{ Filler: 2, }, }, gophercloud.ErrMissingInput{}, }, } for _, failCase := range failCases { _, err := gophercloud.BuildRequestBody(failCase.opts, "auth") th.AssertDeepEquals(t, reflect.TypeOf(failCase.expected), reflect.TypeOf(err)) } createdAt := time.Date(2018, 1, 4, 10, 00, 12, 0, time.UTC) var complexFields = struct { Username string `json:"username" required:"true"` CreatedAt *time.Time `json:"-"` }{ Username: "jdoe", CreatedAt: &createdAt, } expectedComplexFields := map[string]interface{}{ "username": "jdoe", } actual, err := gophercloud.BuildRequestBody(complexFields, "") th.AssertNoErr(t, err) th.AssertDeepEquals(t, expectedComplexFields, actual) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/testing/provider_client_test.go000066400000000000000000000070111335005366400320060ustar00rootroot00000000000000package testing import ( "fmt" "io/ioutil" "net/http" "reflect" "sync" "testing" "time" "github.com/gophercloud/gophercloud" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestAuthenticatedHeaders(t *testing.T) { p := &gophercloud.ProviderClient{ TokenID: "1234", } expected := map[string]string{"X-Auth-Token": "1234"} actual := p.AuthenticatedHeaders() th.CheckDeepEquals(t, expected, actual) } func TestUserAgent(t *testing.T) { p := &gophercloud.ProviderClient{} p.UserAgent.Prepend("custom-user-agent/2.4.0") expected := "custom-user-agent/2.4.0 gophercloud/2.0.0" actual := p.UserAgent.Join() th.CheckEquals(t, expected, actual) p.UserAgent.Prepend("another-custom-user-agent/0.3.0", "a-third-ua/5.9.0") expected = "another-custom-user-agent/0.3.0 a-third-ua/5.9.0 custom-user-agent/2.4.0 gophercloud/2.0.0" actual = p.UserAgent.Join() th.CheckEquals(t, expected, actual) p.UserAgent = gophercloud.UserAgent{} expected = "gophercloud/2.0.0" actual = p.UserAgent.Join() th.CheckEquals(t, expected, actual) } func TestConcurrentReauth(t *testing.T) { var info = struct { numreauths int mut *sync.RWMutex }{ 0, new(sync.RWMutex), } numconc := 20 prereauthTok := client.TokenID postreauthTok := "12345678" p := new(gophercloud.ProviderClient) p.UseTokenLock() p.SetToken(prereauthTok) p.ReauthFunc = func() error { time.Sleep(1 * time.Second) p.AuthenticatedHeaders() info.mut.Lock() info.numreauths++ info.mut.Unlock() p.TokenID = postreauthTok return nil } th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/route", func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("X-Auth-Token") != postreauthTok { w.WriteHeader(http.StatusUnauthorized) return } info.mut.RLock() hasReauthed := info.numreauths != 0 info.mut.RUnlock() if hasReauthed { th.CheckEquals(t, p.Token(), postreauthTok) } w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{}`) }) wg := new(sync.WaitGroup) reqopts := new(gophercloud.RequestOpts) reqopts.MoreHeaders = map[string]string{ "X-Auth-Token": prereauthTok, } for i := 0; i < numconc; i++ { wg.Add(1) go func() { defer wg.Done() resp, err := p.Request("GET", fmt.Sprintf("%s/route", th.Endpoint()), reqopts) th.CheckNoErr(t, err) if resp == nil { t.Errorf("got a nil response") return } if resp.Body == nil { t.Errorf("response body was nil") return } defer resp.Body.Close() actual, err := ioutil.ReadAll(resp.Body) if err != nil { t.Errorf("error reading response body: %s", err) return } th.CheckByteArrayEquals(t, []byte(`{}`), actual) }() } wg.Wait() th.AssertEquals(t, 1, info.numreauths) } func TestReauthEndLoop(t *testing.T) { p := new(gophercloud.ProviderClient) p.UseTokenLock() p.SetToken(client.TokenID) p.ReauthFunc = func() error { // Reauth func is working and returns no error return nil } th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/route", func(w http.ResponseWriter, r *http.Request) { // route always return 401 w.WriteHeader(http.StatusUnauthorized) return }) reqopts := new(gophercloud.RequestOpts) _, err := p.Request("GET", fmt.Sprintf("%s/route", th.Endpoint()), reqopts) if err == nil { t.Errorf("request ends with a nil error") return } if reflect.TypeOf(err) != reflect.TypeOf(&gophercloud.ErrErrorAfterReauthentication{}) { t.Errorf("error is not an ErrErrorAfterReauthentication") } } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/testing/results_test.go000066400000000000000000000106241335005366400303230ustar00rootroot00000000000000package testing import ( "encoding/json" "testing" "github.com/gophercloud/gophercloud" th "github.com/gophercloud/gophercloud/testhelper" ) var singleResponse = ` { "person": { "name": "Bill", "email": "bill@example.com", "location": "Canada" } } ` var multiResponse = ` { "people": [ { "name": "Bill", "email": "bill@example.com", "location": "Canada" }, { "name": "Ted", "email": "ted@example.com", "location": "Mexico" } ] } ` type TestPerson struct { Name string `json:"-"` Email string `json:"email"` } func (r *TestPerson) UnmarshalJSON(b []byte) error { type tmp TestPerson var s struct { tmp Name string `json:"name"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = TestPerson(s.tmp) r.Name = s.Name + " unmarshalled" return nil } type TestPersonExt struct { Location string `json:"-"` } func (r *TestPersonExt) UnmarshalJSON(b []byte) error { type tmp TestPersonExt var s struct { tmp Location string `json:"location"` } err := json.Unmarshal(b, &s) if err != nil { return err } *r = TestPersonExt(s.tmp) r.Location = s.Location + " unmarshalled" return nil } type TestPersonWithExtensions struct { TestPerson TestPersonExt } type TestPersonWithExtensionsNamed struct { TestPerson TestPerson TestPersonExt TestPersonExt } // TestUnmarshalAnonymousStruct tests if UnmarshalJSON is called on each // of the anonymous structs contained in an overarching struct. func TestUnmarshalAnonymousStructs(t *testing.T) { var actual TestPersonWithExtensions var dejson interface{} sejson := []byte(singleResponse) err := json.Unmarshal(sejson, &dejson) if err != nil { t.Fatal(err) } var singleResult = gophercloud.Result{ Body: dejson, } err = singleResult.ExtractIntoStructPtr(&actual, "person") th.AssertNoErr(t, err) th.AssertEquals(t, "Bill unmarshalled", actual.Name) th.AssertEquals(t, "Canada unmarshalled", actual.Location) } // TestUnmarshalSliceofAnonymousStructs tests if UnmarshalJSON is called on each // of the anonymous structs contained in an overarching struct slice. func TestUnmarshalSliceOfAnonymousStructs(t *testing.T) { var actual []TestPersonWithExtensions var dejson interface{} sejson := []byte(multiResponse) err := json.Unmarshal(sejson, &dejson) if err != nil { t.Fatal(err) } var multiResult = gophercloud.Result{ Body: dejson, } err = multiResult.ExtractIntoSlicePtr(&actual, "people") th.AssertNoErr(t, err) th.AssertEquals(t, "Bill unmarshalled", actual[0].Name) th.AssertEquals(t, "Canada unmarshalled", actual[0].Location) th.AssertEquals(t, "Ted unmarshalled", actual[1].Name) th.AssertEquals(t, "Mexico unmarshalled", actual[1].Location) } // TestUnmarshalSliceOfStruct tests if extracting results from a "normal" // struct still works correctly. func TestUnmarshalSliceofStruct(t *testing.T) { var actual []TestPerson var dejson interface{} sejson := []byte(multiResponse) err := json.Unmarshal(sejson, &dejson) if err != nil { t.Fatal(err) } var multiResult = gophercloud.Result{ Body: dejson, } err = multiResult.ExtractIntoSlicePtr(&actual, "people") th.AssertNoErr(t, err) th.AssertEquals(t, "Bill unmarshalled", actual[0].Name) th.AssertEquals(t, "Ted unmarshalled", actual[1].Name) } // TestUnmarshalNamedStruct tests if the result is empty. func TestUnmarshalNamedStructs(t *testing.T) { var actual TestPersonWithExtensionsNamed var dejson interface{} sejson := []byte(singleResponse) err := json.Unmarshal(sejson, &dejson) if err != nil { t.Fatal(err) } var singleResult = gophercloud.Result{ Body: dejson, } err = singleResult.ExtractIntoStructPtr(&actual, "person") th.AssertNoErr(t, err) th.AssertEquals(t, "", actual.TestPerson.Name) th.AssertEquals(t, "", actual.TestPersonExt.Location) } // TestUnmarshalSliceofNamedStructs tests if the result is empty. func TestUnmarshalSliceOfNamedStructs(t *testing.T) { var actual []TestPersonWithExtensionsNamed var dejson interface{} sejson := []byte(multiResponse) err := json.Unmarshal(sejson, &dejson) if err != nil { t.Fatal(err) } var multiResult = gophercloud.Result{ Body: dejson, } err = multiResult.ExtractIntoSlicePtr(&actual, "people") th.AssertNoErr(t, err) th.AssertEquals(t, "", actual[0].TestPerson.Name) th.AssertEquals(t, "", actual[0].TestPersonExt.Location) th.AssertEquals(t, "", actual[1].TestPerson.Name) th.AssertEquals(t, "", actual[1].TestPersonExt.Location) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/testing/service_client_test.go000066400000000000000000000015741335005366400316240ustar00rootroot00000000000000package testing import ( "fmt" "net/http" "testing" "github.com/gophercloud/gophercloud" th "github.com/gophercloud/gophercloud/testhelper" ) func TestServiceURL(t *testing.T) { c := &gophercloud.ServiceClient{Endpoint: "http://123.45.67.8/"} expected := "http://123.45.67.8/more/parts/here" actual := c.ServiceURL("more", "parts", "here") th.CheckEquals(t, expected, actual) } func TestMoreHeaders(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/route", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }) c := new(gophercloud.ServiceClient) c.MoreHeaders = map[string]string{ "custom": "header", } c.ProviderClient = new(gophercloud.ProviderClient) resp, err := c.Get(fmt.Sprintf("%s/route", th.Endpoint()), nil, nil) th.AssertNoErr(t, err) th.AssertEquals(t, resp.Request.Header.Get("custom"), "header") } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/testing/util_test.go000066400000000000000000000070561335005366400276040ustar00rootroot00000000000000package testing import ( "errors" "os" "path/filepath" "strings" "testing" "time" "github.com/gophercloud/gophercloud" th "github.com/gophercloud/gophercloud/testhelper" ) func TestWaitFor(t *testing.T) { err := gophercloud.WaitFor(2, func() (bool, error) { return true, nil }) th.CheckNoErr(t, err) } func TestWaitForTimeout(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") } err := gophercloud.WaitFor(1, func() (bool, error) { return false, nil }) th.AssertEquals(t, "A timeout occurred", err.Error()) } func TestWaitForError(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") } err := gophercloud.WaitFor(2, func() (bool, error) { return false, errors.New("Error has occurred") }) th.AssertEquals(t, "Error has occurred", err.Error()) } func TestWaitForPredicateExceed(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") } err := gophercloud.WaitFor(1, func() (bool, error) { time.Sleep(4 * time.Second) return false, errors.New("Just wasting time") }) th.AssertEquals(t, "A timeout occurred", err.Error()) } func TestNormalizeURL(t *testing.T) { urls := []string{ "NoSlashAtEnd", "SlashAtEnd/", } expected := []string{ "NoSlashAtEnd/", "SlashAtEnd/", } for i := 0; i < len(expected); i++ { th.CheckEquals(t, expected[i], gophercloud.NormalizeURL(urls[i])) } } func TestNormalizePathURL(t *testing.T) { baseDir, _ := os.Getwd() rawPath := "template.yaml" basePath, _ := filepath.Abs(".") result, _ := gophercloud.NormalizePathURL(basePath, rawPath) expected := strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "template.yaml"}, "/") th.CheckEquals(t, expected, result) rawPath = "http://www.google.com" basePath, _ = filepath.Abs(".") result, _ = gophercloud.NormalizePathURL(basePath, rawPath) expected = "http://www.google.com" th.CheckEquals(t, expected, result) rawPath = "very/nested/file.yaml" basePath, _ = filepath.Abs(".") result, _ = gophercloud.NormalizePathURL(basePath, rawPath) expected = strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "very/nested/file.yaml"}, "/") th.CheckEquals(t, expected, result) rawPath = "very/nested/file.yaml" basePath = "http://www.google.com" result, _ = gophercloud.NormalizePathURL(basePath, rawPath) expected = "http://www.google.com/very/nested/file.yaml" th.CheckEquals(t, expected, result) rawPath = "very/nested/file.yaml/" basePath = "http://www.google.com/" result, _ = gophercloud.NormalizePathURL(basePath, rawPath) expected = "http://www.google.com/very/nested/file.yaml" th.CheckEquals(t, expected, result) rawPath = "very/nested/file.yaml" basePath = "http://www.google.com/even/more" result, _ = gophercloud.NormalizePathURL(basePath, rawPath) expected = "http://www.google.com/even/more/very/nested/file.yaml" th.CheckEquals(t, expected, result) rawPath = "very/nested/file.yaml" basePath = strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "only/file/even/more"}, "/") result, _ = gophercloud.NormalizePathURL(basePath, rawPath) expected = strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "only/file/even/more/very/nested/file.yaml"}, "/") th.CheckEquals(t, expected, result) rawPath = "very/nested/file.yaml/" basePath = strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "only/file/even/more"}, "/") result, _ = gophercloud.NormalizePathURL(basePath, rawPath) expected = strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "only/file/even/more/very/nested/file.yaml"}, "/") th.CheckEquals(t, expected, result) } golang-github-gophercloud-gophercloud-0.0~git20180917.45f1c769/util.go000066400000000000000000000052601335005366400250630ustar00rootroot00000000000000package gophercloud import ( "fmt" "net/url" "path/filepath" "strings" "time" ) // WaitFor polls a predicate function, once per second, up to a timeout limit. // This is useful to wait for a resource to transition to a certain state. // To handle situations when the predicate might hang indefinitely, the // predicate will be prematurely cancelled after the timeout. // Resource packages will wrap this in a more convenient function that's // specific to a certain resource, but it can also be useful on its own. func WaitFor(timeout int, predicate func() (bool, error)) error { type WaitForResult struct { Success bool Error error } start := time.Now().Unix() for { // If a timeout is set, and that's been exceeded, shut it down. if timeout >= 0 && time.Now().Unix()-start >= int64(timeout) { return fmt.Errorf("A timeout occurred") } time.Sleep(1 * time.Second) var result WaitForResult ch := make(chan bool, 1) go func() { defer close(ch) satisfied, err := predicate() result.Success = satisfied result.Error = err }() select { case <-ch: if result.Error != nil { return result.Error } if result.Success { return nil } // If the predicate has not finished by the timeout, cancel it. case <-time.After(time.Duration(timeout) * time.Second): return fmt.Errorf("A timeout occurred") } } } // NormalizeURL is an internal function to be used by provider clients. // // It ensures that each endpoint URL has a closing `/`, as expected by // ServiceClient's methods. func NormalizeURL(url string) string { if !strings.HasSuffix(url, "/") { return url + "/" } return url } // NormalizePathURL is used to convert rawPath to a fqdn, using basePath as // a reference in the filesystem, if necessary. basePath is assumed to contain // either '.' when first used, or the file:// type fqdn of the parent resource. // e.g. myFavScript.yaml => file://opt/lib/myFavScript.yaml func NormalizePathURL(basePath, rawPath string) (string, error) { u, err := url.Parse(rawPath) if err != nil { return "", err } // if a scheme is defined, it must be a fqdn already if u.Scheme != "" { return u.String(), nil } // if basePath is a url, then child resources are assumed to be relative to it bu, err := url.Parse(basePath) if err != nil { return "", err } var basePathSys, absPathSys string if bu.Scheme != "" { basePathSys = filepath.FromSlash(bu.Path) absPathSys = filepath.Join(basePathSys, rawPath) bu.Path = filepath.ToSlash(absPathSys) return bu.String(), nil } absPathSys = filepath.Join(basePath, rawPath) u.Path = filepath.ToSlash(absPathSys) if err != nil { return "", err } u.Scheme = "file" return u.String(), nil }